// // MaaWpfGui - A part of the MaaCoreArknights project // Copyright (C) 2021 MistEO and Contributors // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License v3.0 only as published by // the Free Software Foundation, either version 3 of the License, or // any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY // #nullable enable using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Threading.Tasks; using System.Windows; using MaaWpfGui.Constants; using MaaWpfGui.Extensions; using MaaWpfGui.Helper; using MaaWpfGui.Main; using MaaWpfGui.Models; using MaaWpfGui.Properties; using MaaWpfGui.Services; using MaaWpfGui.Utilities.ValueType; using MaaWpfGui.ViewModels.UI; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Stylet; namespace MaaWpfGui.ViewModels.UserControl.Settings; /// /// 软件更新设置 /// public class VersionUpdateSettingsUserControlModel : PropertyChangedBase { static VersionUpdateSettingsUserControlModel() { Instance = new(); } public static VersionUpdateSettingsUserControlModel Instance { get; } public enum UpdateVersionType { /// /// 测试版 /// Nightly, /// /// 开发版 /// Beta, /// /// 稳定版 /// Stable, } /// /// Gets the core version. /// public static string CoreVersion { get; } = Marshal.PtrToStringAnsi(MaaService.AsstGetVersion()) ?? "0.0.1"; public static string CoreVersionDisplay => string.Join("\u200B", CoreVersion.ToCharArray()); private static readonly string _uiVersion = Assembly.GetExecutingAssembly().GetCustomAttribute()?.InformationalVersion.Split('+')[0] ?? "0.0.1"; /// /// Gets the UI version. /// public static string UiVersion { get; } = _uiVersion == "0.0.1" ? "DEBUG VERSION" : _uiVersion; public static string UiVersionDisplay => string.Join("\u200B", UiVersion.ToCharArray()); public static DateTime BuildDateTime { get; } = Assembly.GetExecutingAssembly().GetCustomAttribute()?.BuildDateTime ?? DateTime.MinValue; public static string BuildDateTimeCurrentCultureString => BuildDateTime.ToLocalTimeString(); private static (DateTime DateTime, string VersionName) _resourceInfo = GetResourceVersionByClientType(ConfigurationHelper.GetValue(ConfigurationKeys.ClientType, string.Empty)); public (DateTime DateTime, string VersionName) ResourceInfo { get => _resourceInfo; set => SetAndNotify(ref _resourceInfo, value); } private static string _resourceVersion = _resourceInfo.VersionName; /// /// Gets or sets the resource version. /// public string ResourceVersion { get => _resourceVersion; set => SetAndNotify(ref _resourceVersion, value); } private static DateTime _resourceDateTime = _resourceInfo.DateTime; public DateTime ResourceDateTime { get => _resourceDateTime; set => SetAndNotify(ref _resourceDateTime, value); } public string ResourceDateTimeCurrentCultureString => ResourceDateTime.ToLocalTimeString(); public static (DateTime DateTime, string VersionName) GetResourceVersionByClientType(string clientType) { const string OfficialClientType = "Official"; const string BilibiliClientType = "Bilibili"; var jsonPath = "resource/version.json"; if (clientType is not ("" or OfficialClientType or BilibiliClientType)) { jsonPath = $"resource/global/{clientType}/resource/version.json"; } string versionName; if (!File.Exists(jsonPath)) { return (DateTime.MinValue, string.Empty); } var versionJson = (JObject?)JsonConvert.DeserializeObject(File.ReadAllText(jsonPath)); var currentTime = (ulong)DateTimeOffset.UtcNow.ToUnixTimeSeconds(); var poolTime = (ulong?)versionJson?["gacha"]?["time"]; // 卡池的开始时间 var activityTime = (ulong?)versionJson?["activity"]?["time"]; // 活动的开始时间 var lastUpdated = (string?)versionJson?["last_updated"]; // 最后更新时间 var dateTime = lastUpdated == null ? DateTime.MinValue : DateTime.ParseExact(lastUpdated, "yyyy-MM-dd HH:mm:ss.fff", null); if (currentTime < poolTime && currentTime < activityTime) { versionName = string.Empty; } else if (currentTime >= poolTime && currentTime < activityTime) { versionName = versionJson?["gacha"]?["pool"]?.ToString() ?? string.Empty; } else if (currentTime < poolTime && currentTime >= activityTime) { versionName = versionJson?["activity"]?["name"]?.ToString() ?? string.Empty; } else if (poolTime > activityTime) { versionName = versionJson?["gacha"]?["pool"]?.ToString() ?? string.Empty; } else { versionName = versionJson?["activity"]?["name"]?.ToString() ?? string.Empty; } return (dateTime, versionName); } private UpdateVersionType _versionType = (UpdateVersionType)Enum.Parse( typeof(UpdateVersionType), ConfigurationHelper.GetGlobalValue(ConfigurationKeys.VersionType, UpdateVersionType.Stable.ToString())); /// /// Gets or sets the type of version to update. /// public UpdateVersionType VersionType { get => _versionType; set { SetAndNotify(ref _versionType, value); ConfigurationHelper.SetGlobalValue(ConfigurationKeys.VersionType, value.ToString()); } } /// /// Gets the list of the version type. /// public List> AllVersionTypeList { get; } = [ new() { Display = LocalizationHelper.GetString("UpdateCheckNightly"), Value = UpdateVersionType.Nightly }, new() { Display = LocalizationHelper.GetString("UpdateCheckBeta"), Value = UpdateVersionType.Beta }, new() { Display = LocalizationHelper.GetString("UpdateCheckStable"), Value = UpdateVersionType.Stable }, ]; public List> VersionTypeList { get => AllVersionTypeList.Where(v => AllowNightlyUpdates || v.Value != UpdateVersionType.Nightly).ToList(); } public bool AllowNightlyUpdates { get; set; } = Convert.ToBoolean(ConfigurationHelper.GetGlobalValue(ConfigurationKeys.AllowNightlyUpdates, bool.FalseString)); private bool _hasAcknowledgedNightlyWarning = Convert.ToBoolean(ConfigurationHelper.GetGlobalValue(ConfigurationKeys.HasAcknowledgedNightlyWarning, bool.FalseString)); public bool HasAcknowledgedNightlyWarning { get => _hasAcknowledgedNightlyWarning; set { SetAndNotify(ref _hasAcknowledgedNightlyWarning, value); ConfigurationHelper.SetGlobalValue(ConfigurationKeys.HasAcknowledgedNightlyWarning, value.ToString()); } } /// /// Gets a value indicating whether to update nightly. /// public bool UpdateNightly { get => _versionType == UpdateVersionType.Nightly; } /// /// Gets a value indicating whether to update beta version. /// public bool UpdateBeta { get => _versionType == UpdateVersionType.Beta; } private bool _updateCheck = Convert.ToBoolean(ConfigurationHelper.GetGlobalValue(ConfigurationKeys.UpdateCheck, bool.TrueString)); public List> ResourceUpdateSourceList { get; } = [ new() { Display = "Github", Value = "Github" }, new() { Display = LocalizationHelper.GetString("MirrorChyan"), Value = "MirrorChyan" }, ]; private string _resourceUpdateSource = ConfigurationHelper.GetGlobalValue(ConfigurationKeys.ResourceUpdateSource, "Github"); /// /// Gets or sets the type of version to update. /// public string ResourceUpdateSource { get => _resourceUpdateSource; set { SetAndNotify(ref _resourceUpdateSource, value); ConfigurationHelper.SetGlobalValue(ConfigurationKeys.ResourceUpdateSource, value.ToString()); } } private string _mirrorChyanCdk = SimpleEncryptionHelper.Decrypt(ConfigurationHelper.GetGlobalValue(ConfigurationKeys.MirrorChyanCdk, string.Empty)); public string MirrorChyanCdk { get => _mirrorChyanCdk; set { SetAndNotify(ref _mirrorChyanCdk, value); value = SimpleEncryptionHelper.Encrypt(value); ConfigurationHelper.SetGlobalValue(ConfigurationKeys.MirrorChyanCdk, value); } } /// /// Gets or sets a value indicating whether to check update. /// public bool UpdateCheck { get => _updateCheck; set { SetAndNotify(ref _updateCheck, value); ConfigurationHelper.SetGlobalValue(ConfigurationKeys.UpdateCheck, value.ToString()); } } private bool _updateAutoCheck = Convert.ToBoolean(ConfigurationHelper.GetGlobalValue(ConfigurationKeys.UpdateAutoCheck, bool.FalseString)); /// /// Gets or sets a value indicating whether to check update. /// public bool UpdateAutoCheck { get => _updateAutoCheck; set { SetAndNotify(ref _updateAutoCheck, value); ConfigurationHelper.SetGlobalValue(ConfigurationKeys.UpdateAutoCheck, value.ToString()); } } private string _proxy = ConfigurationHelper.GetGlobalValue(ConfigurationKeys.UpdateProxy, string.Empty); /// /// Gets or sets the proxy settings. /// public string Proxy { get => _proxy; set { SetAndNotify(ref _proxy, value); ConfigurationHelper.SetGlobalValue(ConfigurationKeys.UpdateProxy, value); } } public List ProxyTypeList { get; } = [ new() { Display = "HTTP Proxy", Value = "http" }, new() { Display = "Socks5 Proxy", Value = "socks5" }, ]; private string _proxyType = ConfigurationHelper.GetGlobalValue(ConfigurationKeys.ProxyType, "http"); public string ProxyType { get => _proxyType; set { SetAndNotify(ref _proxyType, value); ConfigurationHelper.SetGlobalValue(ConfigurationKeys.ProxyType, value); } } private bool _isCheckingForUpdates; /// /// Gets or sets a value indicating whether the update is being checked. /// public bool IsCheckingForUpdates { get => _isCheckingForUpdates; set { SetAndNotify(ref _isCheckingForUpdates, value); } } private bool _autoDownloadUpdatePackage = Convert.ToBoolean(ConfigurationHelper.GetGlobalValue(ConfigurationKeys.AutoDownloadUpdatePackage, bool.TrueString)); /// /// Gets or sets a value indicating whether to auto download update package. /// public bool AutoDownloadUpdatePackage { get => _autoDownloadUpdatePackage; set { SetAndNotify(ref _autoDownloadUpdatePackage, value); ConfigurationHelper.SetGlobalValue(ConfigurationKeys.AutoDownloadUpdatePackage, value.ToString()); } } private bool _autoInstallUpdatePackage = Convert.ToBoolean(ConfigurationHelper.GetGlobalValue(ConfigurationKeys.AutoInstallUpdatePackage, bool.FalseString)); /// /// Gets or sets a value indicating whether to auto install update package. /// public bool AutoInstallUpdatePackage { get => _autoInstallUpdatePackage; set { SetAndNotify(ref _autoInstallUpdatePackage, value); ConfigurationHelper.SetGlobalValue(ConfigurationKeys.AutoInstallUpdatePackage, value.ToString()); } } /// /// Updates manually. /// /// A representing the asynchronous operation. public async Task ManualUpdate() { var ret = await Instances.VersionUpdateViewModel.CheckAndDownloadUpdate(); var toastMessage = string.Empty; switch (ret) { case VersionUpdateViewModel.CheckUpdateRetT.NoNeedToUpdate: break; case VersionUpdateViewModel.CheckUpdateRetT.NoNeedToUpdateDebugVersion: toastMessage = LocalizationHelper.GetString("NoNeedToUpdateDebugVersion"); break; case VersionUpdateViewModel.CheckUpdateRetT.AlreadyLatest: toastMessage = LocalizationHelper.GetString("AlreadyLatest"); break; case VersionUpdateViewModel.CheckUpdateRetT.UnknownError: toastMessage = LocalizationHelper.GetString("NewVersionDetectFailedTitle"); break; case VersionUpdateViewModel.CheckUpdateRetT.NetworkError: toastMessage = LocalizationHelper.GetString("CheckNetworking"); break; case VersionUpdateViewModel.CheckUpdateRetT.FailedToGetInfo: toastMessage = LocalizationHelper.GetString("GetReleaseNoteFailed"); break; case VersionUpdateViewModel.CheckUpdateRetT.OK: Instances.VersionUpdateViewModel.AskToRestart(); break; case VersionUpdateViewModel.CheckUpdateRetT.NewVersionIsBeingBuilt: toastMessage = LocalizationHelper.GetString("NewVersionIsBeingBuilt"); break; case VersionUpdateViewModel.CheckUpdateRetT.OnlyGameResourceUpdated: toastMessage = LocalizationHelper.GetString("GameResourceUpdated"); break; default: throw new ArgumentOutOfRangeException(); } if (toastMessage != string.Empty) { ToastNotification.ShowDirect(toastMessage); } } public async Task ManualUpdateResource() { IsCheckingForUpdates = true; var success = false; switch (ResourceUpdateSource) { case "Github": if (await ResourceUpdater.UpdateFromGithubAsync()) { success = true; } break; case "MirrorChyan": if (await ResourceUpdater.UpdateFromMirrorChyanAsync()) { success = true; } break; } if (success) { if (AutoInstallUpdatePackage) { await Bootstrapper.RestartAfterIdleAsync(); } else { var result = MessageBoxHelper.Show( LocalizationHelper.GetString("GameResourceUpdated"), LocalizationHelper.GetString("Tip"), MessageBoxButton.OKCancel, MessageBoxImage.Question, ok: LocalizationHelper.GetString("Ok"), cancel: LocalizationHelper.GetString("ManualRestart")); if (result == MessageBoxResult.OK) { Bootstrapper.ShutdownAndRestartWithoutArgs(); } } } IsCheckingForUpdates = false; } // UI 绑定的方法 // ReSharper disable once UnusedMember.Global public void ShowChangelog() { Instances.WindowManager.ShowWindow(Instances.VersionUpdateViewModel); } }