From 429cb92cc032964b65505f97e03f52f676b9647b Mon Sep 17 00:00:00 2001 From: Bagus Nur Listiyono Date: Wed, 3 Jul 2024 18:49:49 +0700 Subject: [PATCH 01/44] Initial implementation for ZZZ GSP Backend NOTE: - Reverse Encode needs checking - GeneralData is still F'd - I'm eepy --- CollapseLauncher.sln.DotSettings | 1 + .../GameSettings/Zenless/Context.cs | 7 + .../GameSettings/Zenless/Decode.cs | 55 ++++++ .../Zenless/FileClass/GeneralData.cs | 61 +++++++ .../Zenless/JsonProperties/Properties.cs | 15 ++ .../Zenless/RegistryClass/ScreenManager.cs | 172 ++++++++++++++++++ .../GameSettings/Zenless/Settings.cs | 60 ++++++ 7 files changed, 371 insertions(+) create mode 100644 CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Context.cs create mode 100644 CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Decode.cs create mode 100644 CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs create mode 100644 CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/JsonProperties/Properties.cs create mode 100644 CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/RegistryClass/ScreenManager.cs create mode 100644 CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Settings.cs diff --git a/CollapseLauncher.sln.DotSettings b/CollapseLauncher.sln.DotSettings index ab0b3bcd2..54eafe745 100644 --- a/CollapseLauncher.sln.DotSettings +++ b/CollapseLauncher.sln.DotSettings @@ -69,4 +69,5 @@ True True True + True \ No newline at end of file diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Context.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Context.cs new file mode 100644 index 000000000..43b3d3d97 --- /dev/null +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Context.cs @@ -0,0 +1,7 @@ +using System.Text.Json.Serialization; + +namespace CollapseLauncher.GameSettings.Zenless.Context; + +[JsonSourceGenerationOptions(IncludeFields = false, GenerationMode = JsonSourceGenerationMode.Metadata, IgnoreReadOnlyFields = true)] +[JsonSerializable(typeof(GeneralData))] +internal sealed partial class ZenlessSettingsJSONContext : JsonSerializerContext {} \ No newline at end of file diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Decode.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Decode.cs new file mode 100644 index 000000000..13895b403 --- /dev/null +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Decode.cs @@ -0,0 +1,55 @@ +using System; +using System.IO; +using System.Linq; +using static System.Text.Encoding; + +namespace CollapseLauncher.GameSettings.Zenless; + +public static class Decode +{ + public static byte[] RunDecode(string inputFile, byte[] magic) + { + if (!File.Exists(inputFile)) throw new FileNotFoundException(); + byte[] raw = File.ReadAllBytes(inputFile).Skip(24).ToArray(); + + byte[] result = new byte[raw.Length]; + for (int i = 0; i < raw.Length; i++) + { + result[i] = (byte)(raw[i] ^ magic[i % magic.Length]); + } + + for (int i = 0; i < result.Length; i++) + { + if (result[i] == (byte)'!' && i + 1 < result.Length) + { + result[i] = 0; + result[i + 1] += 0x40; + } + } + + result = result.Where(b => b != 0x0 && b != 0x20).ToArray(); + return result; + } + + public static byte[] RunEncode(byte[] input, byte[] magic) + { + if (input == null || input.Length == 0) throw new NullReferenceException(); + + byte[] result = new byte[input.Length]; + for (int i = 0; i < input.Length; i++) + { + result[i] = (byte)(input[i] ^ magic[i % magic.Length]); + } + + return result; + } + + public static string DecodeToString(string inputFile, byte[] magic) + { + var a = RunDecode(inputFile, magic); + var d = UTF8.GetString(a); + + // trim last char + return d.Length > 0 ? d.Substring(0, d.Length - 1) : d; + } +} \ No newline at end of file diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs new file mode 100644 index 000000000..4640e57e9 --- /dev/null +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs @@ -0,0 +1,61 @@ +using CollapseLauncher.GameSettings.Zenless.Context; +using CollapseLauncher.Helper.Metadata; +using CollapseLauncher.Statics; +using Hi3Helper; +using System; +using System.IO; +using static Hi3Helper.Logger; + +namespace CollapseLauncher.GameSettings.Zenless; + +internal class GeneralData +{ + #region Fields +#nullable enable + private static GamePresetProperty? _gamePresetProperty; + private static GamePresetProperty ZenlessGameProperty + { + get + { + if (_gamePresetProperty != null) return _gamePresetProperty; + _gamePresetProperty = GamePropertyVault.GetCurrentGameProperty(); + if (ZenlessGameProperty._GamePreset.GameType == GameNameType.Zenless) + throw new InvalidDataException("[ZenlessSettings] GameProperty value is not Zenless!"); + return _gamePresetProperty; + } + } + + private static string gameFolder = ZenlessGameProperty._GameVersion.GameDirPath; + private static string gameExec = ZenlessGameProperty._GamePreset.GameExecutableName!; + private static string configFileLoc = $@"{gameExec}_Data\Persistent\LocalStorage\GENERAL_DATA.bin"; + + private static string configFile = Path.Join(gameFolder, configFileLoc); + #endregion + + #region Methods + + public static GeneralData Load(byte[] magic) + { + try + { + if (!File.Exists(configFile)) throw new FileNotFoundException("Zenless settings not found!"); + + string raw = Decode.DecodeToString(configFile, magic); + + #if DEBUG + LogWriteLine($"RAW Zenless Settings: {configFile}\r\n" + + $"{raw}", LogType.Debug, true); + #endif + GeneralData data = raw.Deserialize(ZenlessSettingsJSONContext.Default) ?? new GeneralData(); + return data; + } + catch (Exception ex) + { + LogWriteLine($"Failed to parse Zenless settings\r\n{ex}", LogType.Error, true); + return new GeneralData(); + } + } + + + #endregion +} \ No newline at end of file diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/JsonProperties/Properties.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/JsonProperties/Properties.cs new file mode 100644 index 000000000..175dc43c0 --- /dev/null +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/JsonProperties/Properties.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace CollapseLauncher.GameSettings.Zenless.JsonProperties; + +public class SystemSettingDataMap +{ + [JsonPropertyName("$Type")] + public string Type { get; set; } + + [JsonPropertyName("Version")] + public int Version { get; set; } + + [JsonPropertyName("Data")] + public int Data { get; set; } +} \ No newline at end of file diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/RegistryClass/ScreenManager.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/RegistryClass/ScreenManager.cs new file mode 100644 index 000000000..6c965d3d8 --- /dev/null +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/RegistryClass/ScreenManager.cs @@ -0,0 +1,172 @@ +using CollapseLauncher.GameSettings.Base; +using CollapseLauncher.Interfaces; +using Hi3Helper; +using Hi3Helper.EncTool; +using Hi3Helper.Screen; +using Microsoft.Win32; +using System; +using System.Drawing; +using static CollapseLauncher.GameSettings.Base.SettingsBase; +using static Hi3Helper.Logger; + +namespace CollapseLauncher.GameSettings.Zenless +{ + internal class ScreenManager : BaseScreenSettingData, IGameSettingsValue + { + #region Fields + private const string _ValueNameScreenManagerWidth = "Screenmanager Resolution Width_h182942802"; + private const string _ValueNameScreenManagerHeight = "Screenmanager Resolution Height_h2627697771"; + private const string _ValueNameScreenManagerFullscreen = "Screenmanager Fullscreen mode_h3630240806"; + private static Size currentRes = ScreenProp.currentResolution; + #endregion + + #region Enums + public enum FullScreenMode : int + { + Fullscreen = 1, + Something, + SomethingElse + } + + #endregion + + #region Properties + /// + /// This value references values from inner width and height.

+ /// Range: 64 x 64 - int.MaxValue x int.MaxValue
+ /// Default: Your screen resolution + ///
+ public override Size sizeRes + { + get => new Size(width, height); + set + { + width = value.Width < 64 ? currentRes.Width : value.Width; + height = value.Height < 64 ? currentRes.Height : value.Height; + } + } + + /// + /// This value references values from inner width and height as string.

+ /// Range: 64 x 64 - int.MaxValue x int.MaxValue
+ /// Default: Your screen resolution + ///
+ public override string sizeResString + { + get => $"{width}x{height}"; + set + { + string[] size = value.Split('x'); + int w; + int h; + if (!int.TryParse(size[0], out w) || !int.TryParse(size[1], out h)) + { + width = currentRes.Width; + height = currentRes.Height; + } + else + { + width = w; + height = h; + } + } + } + + /// + /// This defines "Game Resolution's Width" combobox In-game settings -> Video.

+ /// Range: 0 - int.MaxValue
+ /// Default: Your screen width + ///
+ public override int width { get; set; } = currentRes.Width; + + /// + /// This defines "Game Resolution's Height" combobox In-game settings -> Video.

+ /// Range: 0 - int.MaxValue
+ /// Default: Your screen Height + ///
+ public override int height { get; set; } = currentRes.Height; + + /// + /// This defines "Fullscreen" checkbox In-game settings -> Video.

+ /// Range: 0 - 1 + /// Default: 1 + ///
+ public FullScreenMode fullscreen { get; set; } + + public override bool isfullScreen + { + get => fullscreen switch + { + FullScreenMode.Fullscreen => true, + _ => false, + }; + set => fullscreen = value switch + { + true => FullScreenMode.Fullscreen, + _ => 0, + }; + } + #endregion + + #region Methods +#nullable enable + public static ScreenManager Load() + { + try + { + if (RegistryRoot == null) throw new NullReferenceException($"Cannot load Genshin Screen Manager settings as RegistryKey is unexpectedly not initialized!"); + + object? valueWidth = RegistryRoot.GetValue(_ValueNameScreenManagerWidth, null); + object? valueHeight = RegistryRoot.GetValue(_ValueNameScreenManagerHeight, null); + object? valueFullscreen = RegistryRoot.GetValue(_ValueNameScreenManagerFullscreen, null); + if (valueWidth != null && valueHeight != null && valueFullscreen != null) + { + int width = (int)valueWidth; + int height = (int)valueHeight; + FullScreenMode fullscreen = (FullScreenMode)valueFullscreen; +#if DEBUG + LogWriteLine($"Loaded Genshin Settings: {_ValueNameScreenManagerWidth} : {width}", LogType.Debug, true); + LogWriteLine($"Loaded Genshin Settings: {_ValueNameScreenManagerHeight} : {height}", LogType.Debug, true); + LogWriteLine($"Loaded Genshin Settings: {_ValueNameScreenManagerFullscreen} : {fullscreen}", LogType.Debug, true); +#endif + return new ScreenManager { width = width, height = height, fullscreen = fullscreen }; + } + } + catch (Exception ex) + { + LogWriteLine($"Failed while reading Genshin ScreenManager Data" + + $"\r\n Please open the game and change any Graphics Settings, then close normally. After that you can use this feature." + + $"\r\n If the issue persist, please report it on GitHub" + + $"\r\n{ex}", LogType.Error, true); + ErrorSender.SendException(new Exception( + $"Failed when reading Genshin ScreenManager Data\r\n" + + $"Please open the game and change any graphics settings, then safely close the game. If the problem persist, report the issue on our GitHub\r\n" + + $"{ex}", ex)); + } + + return new ScreenManager(); + } + + public override void Save() + { + try + { + RegistryRoot?.SetValue(_ValueNameScreenManagerFullscreen, fullscreen, RegistryValueKind.DWord); + RegistryRoot?.SetValue(_ValueNameScreenManagerWidth, width, RegistryValueKind.DWord); + RegistryRoot?.SetValue(_ValueNameScreenManagerHeight, height, RegistryValueKind.DWord); +#if DEBUG + LogWriteLine($"Saved Genshin Settings: {_ValueNameScreenManagerFullscreen} : {RegistryRoot?.GetValue(_ValueNameScreenManagerFullscreen, null)}", LogType.Debug, true); + LogWriteLine($"Saved Genshin Settings: {_ValueNameScreenManagerWidth} : {RegistryRoot?.GetValue(_ValueNameScreenManagerWidth, null)}", LogType.Debug, true); + LogWriteLine($"Saved Genshin Settings: {_ValueNameScreenManagerHeight} : {RegistryRoot?.GetValue(_ValueNameScreenManagerHeight, null)}", LogType.Debug, true); +#endif + } + catch (Exception ex) + { + LogWriteLine($"Failed to save Genshin Impact ScreenManager Values!\r\n{ex}", LogType.Error, true); + } + } + + public bool Equals(ScreenManager? comparedTo) => TypeExtensions.IsInstancePropertyEqual(this, comparedTo); + } + #endregion +} \ No newline at end of file diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Settings.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Settings.cs new file mode 100644 index 000000000..c9550e6a5 --- /dev/null +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Settings.cs @@ -0,0 +1,60 @@ +using CollapseLauncher.GameSettings.Base; +using CollapseLauncher.GameVersioning; +using CollapseLauncher.Interfaces; +using System; +using System.Diagnostics; + +// ReSharper disable CheckNamespace + +namespace CollapseLauncher.GameSettings.Zenless +{ + internal class ZenlessSettings : SettingsBase + { + #region Variables + #nullable enable + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private byte[]? _magicReDo; + #nullable restore + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private byte[] MagicReDo + { + get + { + if (_magicReDo != null) return _magicReDo; + _magicReDo = (_gameVersionManager as GameTypeZenlessVersion)?.GamePreset + .GetGameDataTemplate("MagicReDo", new byte[] {1, 0, 0, 0}); + if (_magicReDo == null || _magicReDo.Length == 0) + throw new NullReferenceException("MagicReDo value for ZZZ settings is empty!"); + return _magicReDo; + } + } + #endregion + + public ZenlessSettings(IGameVersionCheck GameVersionManager) : base(GameVersionManager) + { + // Initialize magic + _ = MagicReDo; + + InitializeSettings(); + } + + public sealed override void InitializeSettings() + { + base.InitializeSettings(); + SettingsScreen = ScreenManager.Load(); + } + + public override void ReloadSettings() + { + InitializeSettings(); + } + + public override void SaveSettings() + { + base.SaveSettings(); + SettingsScreen.Save(); + } + + public override IGameSettingsUniversal AsIGameSettingsUniversal() => this; + } +} \ No newline at end of file From a7016ac0631c95686f8de8483db91501041a136a Mon Sep 17 00:00:00 2001 From: Bagus Nur Listiyono Date: Wed, 3 Jul 2024 19:36:12 +0700 Subject: [PATCH 02/44] Preliminary frontend for Zenless Settings WARNING: WILL CRASH WHEN OPENING THE PAGE --- .../GameSettings/Zenless/Settings.cs | 5 + .../XAMLs/MainApp/MainPage.xaml.cs | 18 +- .../ZenlessGameSettingsPage.Ext.cs | 312 ++++++ .../ZenlessGameSettingsPage.xaml | 920 ++++++++++++++++++ .../ZenlessGameSettingsPage.xaml.cs | 244 +++++ 5 files changed, 1493 insertions(+), 6 deletions(-) create mode 100644 CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs create mode 100644 CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml create mode 100644 CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Settings.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Settings.cs index c9550e6a5..c27237dde 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Settings.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Settings.cs @@ -29,6 +29,10 @@ private byte[] MagicReDo } } #endregion + + #region Properties + public GeneralData GeneralData {get;set;} + #endregion public ZenlessSettings(IGameVersionCheck GameVersionManager) : base(GameVersionManager) { @@ -42,6 +46,7 @@ public sealed override void InitializeSettings() { base.InitializeSettings(); SettingsScreen = ScreenManager.Load(); + GeneralData = GeneralData.Load(MagicReDo); } public override void ReloadSettings() diff --git a/CollapseLauncher/XAMLs/MainApp/MainPage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/MainPage.xaml.cs index 02c1dcb7f..91bee3d1c 100644 --- a/CollapseLauncher/XAMLs/MainApp/MainPage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/MainPage.xaml.cs @@ -1195,12 +1195,14 @@ private void InitializeNavigationItems(bool ResetSelection = true) NavigationViewControl.MenuItems.Add(new NavigationViewItem() { Content = Lang._StarRailGameSettingsPage.PageTitle, Icon = IconGameSettings, Tag = "starrailgamesettings" }); break; - } - - if (CurrentGameVersionCheck.GameType == GameNameType.Genshin) - { - NavigationViewControl.MenuItems.Add(new NavigationViewItem() - { Content = Lang._GenshinGameSettingsPage.PageTitle, Icon = IconGameSettings, Tag = "genshingamesettings" }); + case GameNameType.Genshin: + NavigationViewControl.MenuItems.Add(new NavigationViewItem() + { Content = Lang._GenshinGameSettingsPage.PageTitle, Icon = IconGameSettings, Tag = "genshingamesettings" }); + break; + case GameNameType.Zenless: + NavigationViewControl.MenuItems.Add(new NavigationViewItem() + {Content = Lang._GameSettingsPage.PageTitle, Icon = IconGameSettings, Tag = "zenlessgamesettings"}); + break; } if (ResetSelection) @@ -1310,6 +1312,10 @@ void NavigateInnerSwitch(string itemTag) case "genshingamesettings": Navigate(IsGameInstalled() ? typeof(GenshinGameSettingsPage) : typeof(NotInstalledPage), itemTag); break; + + case "zenlessgamesettings": + Navigate(IsGameInstalled() ? typeof(ZenlessGameSettingsPage) : typeof(NotificationInvoker), itemTag); + break; } } diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs new file mode 100644 index 000000000..3a6b5b05e --- /dev/null +++ b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs @@ -0,0 +1,312 @@ +using CollapseLauncher.GameSettings.Zenless; +using Hi3Helper; +using Hi3Helper.Screen; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using System; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; +using System.Runtime.CompilerServices; + +namespace CollapseLauncher.Pages +{ + [SuppressMessage("ReSharper", "PossibleNullReferenceException")] + public partial class ZenlessGameSettingsPage : INotifyPropertyChanged + { + #region Methods + public event PropertyChangedEventHandler PropertyChanged = delegate { }; + // ReSharper disable once UnusedMember.Local + private void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + // Raise the PropertyChanged event, passing the name of the property whose value has changed. + this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + #endregion + + #region GameResolution + public bool IsFullscreenEnabled + { + get => Settings.SettingsScreen.isfullScreen; + set + { + Settings.SettingsScreen.isfullScreen = value; + if (value) + { + GameWindowResizable.IsEnabled = false; + GameWindowResizable.IsChecked = false; + GameResolutionFullscreenExclusive.IsEnabled = !IsCustomResolutionEnabled; + GameResolutionBorderless.IsChecked = false; + return; + } + GameWindowResizable.IsEnabled = true; + GameResolutionFullscreenExclusive.IsEnabled = false; + GameResolutionFullscreenExclusive.IsChecked = false; + GameResolutionBorderless.IsEnabled = true; + } + } + + public bool IsBorderlessEnabled + { + get => Settings.SettingsCollapseScreen.UseBorderlessScreen; + set + { + Settings.SettingsCollapseScreen.UseBorderlessScreen = value; + if (value) + { + GameWindowResizable.IsEnabled = false; + GameWindowResizable.IsChecked = false; + GameResolutionFullscreen.IsEnabled = false; + GameResolutionFullscreen.IsChecked = false; + } + else + { + GameWindowResizable.IsEnabled = true; + GameResolutionFullscreen.IsEnabled = true; + } + } + } + + public bool IsCustomResolutionEnabled + { + get => Settings.SettingsCollapseScreen.UseCustomResolution; + set + { + Settings.SettingsCollapseScreen.UseCustomResolution = value; + if (value) + { + GameResolutionFullscreenExclusive.IsEnabled = false; + GameResolutionSelector.IsEnabled = false; + GameCustomResolutionWidth.IsEnabled = true; + GameCustomResolutionHeight.IsEnabled = true; + + string[] _size = ResolutionSelected.Split('x'); + GameCustomResolutionWidth.Value = int.Parse(_size[0]); + GameCustomResolutionHeight.Value = int.Parse(_size[1]); + + return; + } + GameCustomResolutionWidth.IsEnabled = false; + GameCustomResolutionHeight.IsEnabled = false; + GameResolutionFullscreenExclusive.IsEnabled = IsFullscreenEnabled; + GameResolutionSelector.IsEnabled = true; + + Size size = ScreenProp.GetScreenSize(); + GameResolutionSelector.SelectedItem = $"{size.Width}x{size.Height}"; + } + } + + public bool IsCanExclusiveFullscreen + { + get => !(!IsFullscreenEnabled || IsCustomResolutionEnabled); + } + + public bool IsExclusiveFullscreenEnabled + { + get + { + if (!IsFullscreenEnabled) + { + return false; + } + return Settings.SettingsCollapseScreen.UseExclusiveFullscreen; + } + set + { + Settings.SettingsCollapseScreen.UseExclusiveFullscreen = value; + if (value) + { + GameCustomResolutionCheckbox.IsEnabled = false; + GameCustomResolutionCheckbox.IsChecked = false; + GameCustomResolutionWidth.IsEnabled = false; + GameCustomResolutionHeight.IsEnabled = false; + return; + } + GameCustomResolutionCheckbox.IsEnabled = true; + } + } + + public bool IsCanResizableWindow + { + get => !Settings.SettingsScreen.isfullScreen && !IsExclusiveFullscreenEnabled; + } + + public bool IsResizableWindow + { + get => Settings.SettingsCollapseScreen.UseResizableWindow; + set + { + Settings.SettingsCollapseScreen.UseResizableWindow = value; + if (value) + { + GameCustomResolutionCheckbox.IsEnabled = true; + } + else + { + GameCustomResolutionCheckbox.IsEnabled = false; + GameCustomResolutionCheckbox.IsChecked = false; + } + } + } + + public bool IsCanCustomResolution + { + get => Settings.SettingsCollapseScreen.UseResizableWindow && !IsExclusiveFullscreenEnabled; + } + + public int ResolutionW + { + get => Settings.SettingsScreen.sizeRes.Width; + set => Settings.SettingsScreen.sizeRes = new Size(value, ResolutionH); + } + + public int ResolutionH + { + get => Settings.SettingsScreen.sizeRes.Height; + set => Settings.SettingsScreen.sizeRes = new Size(ResolutionW, value); + } + + public bool IsCanResolutionWH + { + get => IsCustomResolutionEnabled; + } + + public string ResolutionSelected + { + get + { + string res = Settings.SettingsScreen.sizeResString; + if (string.IsNullOrEmpty(res)) + { + Size size = ScreenProp.GetScreenSize(); + return $"{size.Width}x{size.Height}"; + } + return res; + } + set => Settings.SettingsScreen.sizeResString = value; + } + #endregion + + #region Misc + public bool IsGameBoost + { + get => Settings.SettingsCollapseMisc.UseGameBoost; + set => Settings.SettingsCollapseMisc.UseGameBoost = value; + } + + public bool IsMobileMode + { + get => Settings.SettingsCollapseMisc.LaunchMobileMode; + set => Settings.SettingsCollapseMisc.LaunchMobileMode = value; + } + #endregion + + #region Advanced Settings + public bool IsUseAdvancedSettings + { + get + { + bool value = Settings.SettingsCollapseMisc.UseAdvancedGameSettings; + if (value){AdvancedSettingsPanel.Visibility = Visibility.Visible;} + else AdvancedSettingsPanel.Visibility = Visibility.Collapsed; + return value; + } + set + { + Settings.SettingsCollapseMisc.UseAdvancedGameSettings = value; + if (value) AdvancedSettingsPanel.Visibility = Visibility.Visible; + else AdvancedSettingsPanel.Visibility = Visibility.Collapsed; + } + } + + public bool IsUsePreLaunchCommand + { + get + { + bool value = Settings.SettingsCollapseMisc.UseGamePreLaunchCommand; + + if (value) + { + PreLaunchCommandTextBox.IsEnabled = true; + PreLaunchForceCloseToggle.IsEnabled = true; + } + else + { + PreLaunchCommandTextBox.IsEnabled = false; + PreLaunchForceCloseToggle.IsEnabled = false; + } + + return value; + } + set + { + if (value) + { + PreLaunchCommandTextBox.IsEnabled = true; + PreLaunchForceCloseToggle.IsEnabled = true; + GameLaunchDelay.IsEnabled = true; + } + else + { + PreLaunchCommandTextBox.IsEnabled = false; + PreLaunchForceCloseToggle.IsEnabled = false; + GameLaunchDelay.IsEnabled = false; + } + + Settings.SettingsCollapseMisc.UseGamePreLaunchCommand = value; + } + } + + public string PreLaunchCommand + { + get => Settings.SettingsCollapseMisc.GamePreLaunchCommand; + set => Settings.SettingsCollapseMisc.GamePreLaunchCommand = value; + } + + public bool IsPreLaunchCommandExitOnGameClose + { + get => Settings.SettingsCollapseMisc.GamePreLaunchExitOnGameStop; + set => Settings.SettingsCollapseMisc.GamePreLaunchExitOnGameStop = value; + } + + public int LaunchDelay + { + get => Settings.SettingsCollapseMisc.GameLaunchDelay; + set => Settings.SettingsCollapseMisc.GameLaunchDelay = value; + } + + public bool IsUsePostExitCommand + { + get + { + bool value = Settings.SettingsCollapseMisc.UseGamePostExitCommand; + + if (value) PostExitCommandTextBox.IsEnabled = true; + else PostExitCommandTextBox.IsEnabled = false; + + return value; + } + set + { + if (value) PostExitCommandTextBox.IsEnabled = true; + else PostExitCommandTextBox.IsEnabled = false; + + Settings.SettingsCollapseMisc.UseGamePostExitCommand = value; + } + } + + public string PostExitCommand + { + get => Settings.SettingsCollapseMisc.GamePostExitCommand; + set => Settings.SettingsCollapseMisc.GamePostExitCommand = value; + } + + private void GameLaunchDelay_OnValueChanged(NumberBox sender, NumberBoxValueChangedEventArgs args) + { + // clamp for negative value when clearing the number box + if ((int)sender.Value < 0) + sender.Value = 0; + } + #endregion + } +} diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml new file mode 100644 index 000000000..e3f67e1b6 --- /dev/null +++ b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml @@ -0,0 +1,920 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs new file mode 100644 index 000000000..7d070e425 --- /dev/null +++ b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs @@ -0,0 +1,244 @@ +#if !DISABLEDISCORD + using CollapseLauncher.DiscordPresence; +#endif + using CollapseLauncher.Dialogs; + using CollapseLauncher.GameSettings.Zenless; + using CollapseLauncher.Helper.Animation; + using CollapseLauncher.Interfaces; + using CollapseLauncher.Statics; + using Hi3Helper; + using Hi3Helper.Shared.ClassStruct; + using Microsoft.UI.Xaml; + using Microsoft.UI.Xaml.Media; + using Microsoft.UI.Xaml.Navigation; + using Microsoft.Win32; + using RegistryUtils; + using System; + using System.Diagnostics.CodeAnalysis; + using System.IO; + using System.Numerics; + using Windows.UI; + using static Hi3Helper.Locale; + using static Hi3Helper.Logger; + using static Hi3Helper.Shared.Region.LauncherConfig; + using static CollapseLauncher.Statics.GamePropertyVault; + + namespace CollapseLauncher.Pages +{ + [SuppressMessage("ReSharper", "PossibleNullReferenceException")] + public partial class ZenlessGameSettingsPage + { + private GamePresetProperty CurrentGameProperty { get; set; } + private ZenlessSettings Settings { get; set; } + private Brush InheritApplyTextColor { get; set; } + private RegistryMonitor RegistryWatcher { get; set; } + + private bool IsNoReload = false; + + public ZenlessGameSettingsPage() + { + try + { + CurrentGameProperty = GetCurrentGameProperty(); + Settings = CurrentGameProperty._GameSettings as ZenlessSettings; + + DispatcherQueue?.TryEnqueue(() => + { + RegistryWatcher = new RegistryMonitor(RegistryHive.CurrentUser, Path.Combine($"Software\\{CurrentGameProperty._GameVersion.VendorTypeProp.VendorType}", CurrentGameProperty._GameVersion.GamePreset.InternalGameNameInConfig!)); + ToggleRegistrySubscribe(true); + }); + + LoadPage(); + } + catch (Exception ex) + { + LogWriteLine($"{ex}", LogType.Error, true); + ErrorSender.SendException(ex); + } + } + + private void ToggleRegistrySubscribe(bool doSubscribe) + { + if (doSubscribe) + RegistryWatcher.RegChanged += RegistryListener; + else + RegistryWatcher.RegChanged -= RegistryListener; + } + + private void RegistryListener(object sender, EventArgs e) + { + if (!IsNoReload) + { + LogWriteLine("[HSR GSP Module] RegistryMonitor has detected registry change outside of the launcher! Reloading the page...", LogType.Warning, true); + DispatcherQueue?.TryEnqueue(MainFrameChanger.ReloadCurrentMainFrame); + } + } + + private void LoadPage() + { + BackgroundImgChanger.ToggleBackground(true); + Settings.ReloadSettings(); + + this.InitializeComponent(); + this.NavigationCacheMode = NavigationCacheMode.Disabled; + ApplyButton.Translation = Shadow32; + GameSettingsApplyGrid.Translation = new Vector3(0, 0, 64); + SettingsScrollViewer.EnableImplicitAnimation(true); + + InheritApplyTextColor = ApplyText.Foreground!; + } + + private void RegistryExportClick(object sender, RoutedEventArgs e) + { + try + { + ToggleRegistrySubscribe(false); + Exception exc = Settings.ExportSettings(); + + if (exc != null) throw exc; + + ApplyText.Foreground = InheritApplyTextColor; + ApplyText.Text = Lang._GameSettingsPage.SettingsRegExported; + ApplyText.Visibility = Visibility.Visible; + } + catch (Exception ex) + { + LogWriteLine($"Error has occured while exporting registry!\r\n{ex}", LogType.Error, true); + ApplyText.Foreground = new SolidColorBrush(new Color { A = 255, R = 255, B = 0, G = 0 }); + ApplyText.Text = ex.Message; + ApplyText.Visibility = Visibility.Visible; + } + finally + { + ToggleRegistrySubscribe(true); + } + } + + private void RegistryImportClick(object sender, RoutedEventArgs e) + { + try + { + ToggleRegistrySubscribe(false); + Exception exc = Settings.ImportSettings(); + + if (exc != null) throw exc; + + ApplyText.Foreground = InheritApplyTextColor; + ApplyText.Text = Lang._GameSettingsPage.SettingsRegImported; + ApplyText.Visibility = Visibility.Visible; + } + catch (Exception ex) + { + LogWriteLine($"Error has occured while importing registry!\r\n{ex}", LogType.Error, true); + ApplyText.Foreground = new SolidColorBrush(new Color { A = 255, R = 255, B = 0, G = 0 }); + ApplyText.Text = ex.Message; + ApplyText.Visibility = Visibility.Visible; + } + finally + { + ToggleRegistrySubscribe(true); + } + } + + private void InitializeSettings(object sender, RoutedEventArgs e) + { + try + { + GameResolutionSelector.ItemsSource = ScreenResolutionsList; + + if (CurrentGameProperty.IsGameRunning) + { + #if !GSPBYPASSGAMERUNNING + Overlay.Visibility = Visibility.Visible; + PageContent.Visibility = Visibility.Collapsed; + OverlayTitle.Text = Lang._ZenlessGameSettingsPage.OverlayGameRunningTitle; + OverlaySubtitle.Text = Lang._ZenlessGameSettingsPage.OverlayGameRunningSubtitle; + #endif + } + else if (GameInstallationState == GameInstallStateEnum.NotInstalled + || GameInstallationState == GameInstallStateEnum.NeedsUpdate + || GameInstallationState == GameInstallStateEnum.InstalledHavePlugin + || GameInstallationState == GameInstallStateEnum.GameBroken) + { + Overlay.Visibility = Visibility.Visible; + PageContent.Visibility = Visibility.Collapsed; + OverlayTitle.Text = Lang._GameSettingsPage.OverlayNotInstalledTitle; + OverlaySubtitle.Text = Lang._GameSettingsPage.OverlayNotInstalledSubtitle; + } + else + { +#if !DISABLEDISCORD + InnerLauncherConfig.AppDiscordPresence.SetActivity(ActivityType.GameSettings); +#endif + } + } + catch (Exception ex) + { + LogWriteLine($"FATAL ERROR!!!\r\n{ex}", LogType.Error, true); + ErrorSender.SendException(ex); + } + } + + private void ApplyButton_Click(object sender, RoutedEventArgs e) + { + try + { + ApplyText.Foreground = InheritApplyTextColor; + ApplyText.Text = Lang._GameSettingsPage.SettingsApplied; + ApplyText.Visibility = Visibility.Visible; + + ToggleRegistrySubscribe(false); + Settings.SaveSettings(); + } + catch (Exception ex) + { + LogWriteLine($"{ex}", LogType.Error, true); + ErrorSender.SendException(ex); + } + finally + { + ToggleRegistrySubscribe(true); + } + } + + public string CustomArgsValue + { + get => ((IGameSettingsUniversal)CurrentGameProperty._GameSettings).SettingsCustomArgument.CustomArgumentValue; + set + { + ToggleRegistrySubscribe(false); + ((IGameSettingsUniversal)CurrentGameProperty._GameSettings).SettingsCustomArgument.CustomArgumentValue = value; + ToggleRegistrySubscribe(true); + } + } + + public bool IsUseCustomArgs + { + get + { + bool value = ((IGameSettingsUniversal)CurrentGameProperty._GameSettings).SettingsCollapseMisc.UseCustomArguments; + + if (value) CustomArgsTextBox.IsEnabled = true; + else CustomArgsTextBox.IsEnabled = false; + + return value; + } + set + { + ((IGameSettingsUniversal)CurrentGameProperty._GameSettings).SettingsCollapseMisc.UseCustomArguments = value; + + if (value) CustomArgsTextBox.IsEnabled = true; + else CustomArgsTextBox.IsEnabled = false; + } + } + + private void OnUnload(object sender, RoutedEventArgs e) + { + DispatcherQueue?.TryEnqueue(() => + { + ToggleRegistrySubscribe(false); + RegistryWatcher?.Dispose(); + }); + } + } +} From ca169b657f39bccc42564413f390b394a0726e5c Mon Sep 17 00:00:00 2001 From: Kemal Setya Adhi Date: Wed, 3 Jul 2024 21:56:08 +0700 Subject: [PATCH 03/44] Fix crashes on the initial ZZZ settings implementation --- .../GameSettings/Zenless/Settings.cs | 2 +- CollapseLauncher/Classes/GamePropertyVault.cs | 4 ++-- .../Classes/Helper/Metadata/PresetConfig.cs | 10 +++++++++- .../XAMLs/MainApp/MainPage.xaml.cs | 2 +- .../ZenlessGameSettingsPage.Ext.cs | 20 +++++++++---------- .../ZenlessGameSettingsPage.xaml.cs | 4 ++-- 6 files changed, 25 insertions(+), 17 deletions(-) diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Settings.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Settings.cs index c27237dde..8d01571b3 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Settings.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Settings.cs @@ -22,7 +22,7 @@ private byte[] MagicReDo { if (_magicReDo != null) return _magicReDo; _magicReDo = (_gameVersionManager as GameTypeZenlessVersion)?.GamePreset - .GetGameDataTemplate("MagicReDo", new byte[] {1, 0, 0, 0}); + .GetGameDataTemplate("ImSleepin", new byte[] {1, 0, 0, 0}); if (_magicReDo == null || _magicReDo.Length == 0) throw new NullReferenceException("MagicReDo value for ZZZ settings is empty!"); return _magicReDo; diff --git a/CollapseLauncher/Classes/GamePropertyVault.cs b/CollapseLauncher/Classes/GamePropertyVault.cs index 098e036b1..3500cc014 100644 --- a/CollapseLauncher/Classes/GamePropertyVault.cs +++ b/CollapseLauncher/Classes/GamePropertyVault.cs @@ -2,6 +2,7 @@ using CollapseLauncher.GameSettings.Genshin; using CollapseLauncher.GameSettings.Honkai; using CollapseLauncher.GameSettings.StarRail; +using CollapseLauncher.GameSettings.Zenless; using CollapseLauncher.GameVersioning; using CollapseLauncher.Helper.Metadata; using CollapseLauncher.InstallManager.Genshin; @@ -54,8 +55,7 @@ internal GamePresetProperty(UIElement UIElementParent, RegionResourceProp APIRes break; case GameNameType.Zenless: _GameVersion = new GameTypeZenlessVersion(UIElementParent, _APIResouceProp, GameName, GameRegion); - _GameSettings = new SettingsBase(_GameVersion); - _GameSettings.InitializeSettings(); // TODO: Remove this call if we already find a way to do game settings for ZZZ + _GameSettings = new ZenlessSettings(_GameVersion); _GameCache = null; _GameRepair = null; _GameInstall = new ZenlessInstall(UIElementParent, _GameVersion); diff --git a/CollapseLauncher/Classes/Helper/Metadata/PresetConfig.cs b/CollapseLauncher/Classes/Helper/Metadata/PresetConfig.cs index 131f22101..15e7b77f4 100644 --- a/CollapseLauncher/Classes/Helper/Metadata/PresetConfig.cs +++ b/CollapseLauncher/Classes/Helper/Metadata/PresetConfig.cs @@ -468,9 +468,17 @@ private void SetVoiceLanguageID_StarRail(int langID) int verInt = GameDataVersion.GetBytesToIntVersion(gameVersion); if (!value.DataVersion.TryGetValue(verInt, out GameDataVersion? verData)) { - return null; + // Fallback to find the default value anyway + KeyValuePair? kvpTemp = value.DataVersion.FirstOrDefault(); + if (kvpTemp == null) + return null; + + verData = kvpTemp?.Value; } + if (verData == null) + return null; + if (!DataCooker.IsServeV3Data(verData.Data)) { return verData.Data; diff --git a/CollapseLauncher/XAMLs/MainApp/MainPage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/MainPage.xaml.cs index 91bee3d1c..80d4710c2 100644 --- a/CollapseLauncher/XAMLs/MainApp/MainPage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/MainPage.xaml.cs @@ -1314,7 +1314,7 @@ void NavigateInnerSwitch(string itemTag) break; case "zenlessgamesettings": - Navigate(IsGameInstalled() ? typeof(ZenlessGameSettingsPage) : typeof(NotificationInvoker), itemTag); + Navigate(IsGameInstalled() ? typeof(ZenlessGameSettingsPage) : typeof(NotInstalledPage), itemTag); break; } } diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs index 3a6b5b05e..69a35cfb6 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs @@ -190,13 +190,13 @@ public string ResolutionSelected #region Misc public bool IsGameBoost { - get => Settings.SettingsCollapseMisc.UseGameBoost; + get => Settings?.SettingsCollapseMisc?.UseGameBoost ?? false; set => Settings.SettingsCollapseMisc.UseGameBoost = value; } public bool IsMobileMode { - get => Settings.SettingsCollapseMisc.LaunchMobileMode; + get => Settings?.SettingsCollapseMisc?.LaunchMobileMode ?? false; set => Settings.SettingsCollapseMisc.LaunchMobileMode = value; } #endregion @@ -206,7 +206,7 @@ public bool IsUseAdvancedSettings { get { - bool value = Settings.SettingsCollapseMisc.UseAdvancedGameSettings; + bool value = Settings?.SettingsCollapseMisc?.UseAdvancedGameSettings ?? false; if (value){AdvancedSettingsPanel.Visibility = Visibility.Visible;} else AdvancedSettingsPanel.Visibility = Visibility.Collapsed; return value; @@ -222,8 +222,8 @@ public bool IsUseAdvancedSettings public bool IsUsePreLaunchCommand { get - { - bool value = Settings.SettingsCollapseMisc.UseGamePreLaunchCommand; + { + bool value = Settings?.SettingsCollapseMisc?.UseGamePreLaunchCommand ?? false; if (value) { @@ -259,19 +259,19 @@ public bool IsUsePreLaunchCommand public string PreLaunchCommand { - get => Settings.SettingsCollapseMisc.GamePreLaunchCommand; + get => Settings?.SettingsCollapseMisc?.GamePreLaunchCommand; set => Settings.SettingsCollapseMisc.GamePreLaunchCommand = value; } public bool IsPreLaunchCommandExitOnGameClose { - get => Settings.SettingsCollapseMisc.GamePreLaunchExitOnGameStop; + get => Settings?.SettingsCollapseMisc?.GamePreLaunchExitOnGameStop ?? false; set => Settings.SettingsCollapseMisc.GamePreLaunchExitOnGameStop = value; } public int LaunchDelay { - get => Settings.SettingsCollapseMisc.GameLaunchDelay; + get => Settings?.SettingsCollapseMisc?.GameLaunchDelay ?? 0; set => Settings.SettingsCollapseMisc.GameLaunchDelay = value; } @@ -279,7 +279,7 @@ public bool IsUsePostExitCommand { get { - bool value = Settings.SettingsCollapseMisc.UseGamePostExitCommand; + bool value = Settings?.SettingsCollapseMisc?.UseGamePostExitCommand ?? false; if (value) PostExitCommandTextBox.IsEnabled = true; else PostExitCommandTextBox.IsEnabled = false; @@ -297,7 +297,7 @@ public bool IsUsePostExitCommand public string PostExitCommand { - get => Settings.SettingsCollapseMisc.GamePostExitCommand; + get => Settings?.SettingsCollapseMisc?.GamePostExitCommand; set => Settings.SettingsCollapseMisc.GamePostExitCommand = value; } diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs index 7d070e425..b6ba24734 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs @@ -29,7 +29,7 @@ namespace CollapseLauncher.Pages public partial class ZenlessGameSettingsPage { private GamePresetProperty CurrentGameProperty { get; set; } - private ZenlessSettings Settings { get; set; } + private ZenlessSettings Settings { get; set; } private Brush InheritApplyTextColor { get; set; } private RegistryMonitor RegistryWatcher { get; set; } @@ -77,13 +77,13 @@ private void RegistryListener(object sender, EventArgs e) private void LoadPage() { BackgroundImgChanger.ToggleBackground(true); - Settings.ReloadSettings(); this.InitializeComponent(); this.NavigationCacheMode = NavigationCacheMode.Disabled; ApplyButton.Translation = Shadow32; GameSettingsApplyGrid.Translation = new Vector3(0, 0, 64); SettingsScrollViewer.EnableImplicitAnimation(true); + Settings?.ReloadSettings(); InheritApplyTextColor = ApplyText.Foreground!; } From c6c7b69edab004b7ff1a2b5330e16eec30b5092c Mon Sep 17 00:00:00 2001 From: Bagus Nur Listiyono Date: Thu, 4 Jul 2024 02:09:05 +0700 Subject: [PATCH 04/44] Some fixes - Initialize General Data settings later due to certain properties are not initialized sooner - Fix path for general data config file - Fix frontend format --- .../Zenless/FileClass/GeneralData.cs | 2 +- .../GameSettings/Zenless/Settings.cs | 3 +- .../ZenlessGameSettingsPage.xaml | 546 ++---------------- 3 files changed, 40 insertions(+), 511 deletions(-) diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs index 4640e57e9..d4d796c7a 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs @@ -26,7 +26,7 @@ private static GamePresetProperty ZenlessGameProperty } private static string gameFolder = ZenlessGameProperty._GameVersion.GameDirPath; - private static string gameExec = ZenlessGameProperty._GamePreset.GameExecutableName!; + private static string gameExec = Path.GetFileNameWithoutExtension(ZenlessGameProperty._GamePreset.GameExecutableName!); private static string configFileLoc = $@"{gameExec}_Data\Persistent\LocalStorage\GENERAL_DATA.bin"; private static string configFile = Path.Join(gameFolder, configFileLoc); diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Settings.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Settings.cs index 8d01571b3..c66d0225d 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Settings.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Settings.cs @@ -46,11 +46,12 @@ public sealed override void InitializeSettings() { base.InitializeSettings(); SettingsScreen = ScreenManager.Load(); - GeneralData = GeneralData.Load(MagicReDo); } public override void ReloadSettings() { + GeneralData = GeneralData.Load(MagicReDo); + InitializeSettings(); } diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml index e3f67e1b6..c2a29b00d 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml +++ b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml @@ -219,500 +219,31 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Grid.Column="1" + Margin="0,16"> + + + + - - - - - - - - - - - - + + + + + + + - - - + - - - + - - - - - - - - - - - - - + Date: Thu, 4 Jul 2024 02:18:30 +0700 Subject: [PATCH 05/44] Fixed recursive check me dum --- .../GameSettings/Zenless/FileClass/GeneralData.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs index d4d796c7a..e769672bc 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs @@ -19,7 +19,7 @@ private static GamePresetProperty ZenlessGameProperty { if (_gamePresetProperty != null) return _gamePresetProperty; _gamePresetProperty = GamePropertyVault.GetCurrentGameProperty(); - if (ZenlessGameProperty._GamePreset.GameType == GameNameType.Zenless) + if (_gamePresetProperty._GamePreset.GameType != GameNameType.Zenless) throw new InvalidDataException("[ZenlessSettings] GameProperty value is not Zenless!"); return _gamePresetProperty; } From 965cd991b9bbd9d447129af1993182cd832da0c4 Mon Sep 17 00:00:00 2001 From: Shatyuka Date: Fri, 5 Jul 2024 01:14:16 +0800 Subject: [PATCH 06/44] Fix decode/encode --- .../GameSettings/Zenless/Decode.cs | 101 ++++++++++++------ .../Zenless/FileClass/GeneralData.cs | 6 +- 2 files changed, 72 insertions(+), 35 deletions(-) diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Decode.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Decode.cs index 13895b403..7dcec3033 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Decode.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Decode.cs @@ -1,4 +1,4 @@ -using System; +using System.Collections.Generic; using System.IO; using System.Linq; using static System.Text.Encoding; @@ -7,49 +7,86 @@ namespace CollapseLauncher.GameSettings.Zenless; public static class Decode { - public static byte[] RunDecode(string inputFile, byte[] magic) + public static string RunDecode(byte[] input, byte[] magic) { - if (!File.Exists(inputFile)) throw new FileNotFoundException(); - byte[] raw = File.ReadAllBytes(inputFile).Skip(24).ToArray(); + using var mem = new MemoryStream(input); + using var reader = new BinaryReader(mem); + reader.ReadByte(); + reader.ReadInt32(); + reader.ReadInt32(); + reader.ReadInt32(); + reader.ReadInt32(); + reader.ReadByte(); + reader.ReadInt32(); + byte[] body = reader.ReadBytes(reader.Read7BitEncodedInt()); - byte[] result = new byte[raw.Length]; - for (int i = 0; i < raw.Length; i++) + bool[] evil = magic.ToList().ConvertAll(ch => (ch & 0xC0) == 0xC0).ToArray(); + var result = new List(body.Length); + var sleepy = false; + for (int i = 0; i < body.Length; i++) { - result[i] = (byte)(raw[i] ^ magic[i % magic.Length]); - } - - for (int i = 0; i < result.Length; i++) - { - if (result[i] == (byte)'!' && i + 1 < result.Length) + int n = i % magic.Length; + byte ch = (byte)(body[i] ^ magic[n]); + if (evil[n]) + { + if (ch != 0) + { + sleepy = true; + } + } + else { - result[i] = 0; - result[i + 1] += 0x40; + if (sleepy) + { + ch += 0x40; + sleepy = false; + } + result.Add(ch); } } - result = result.Where(b => b != 0x0 && b != 0x20).ToArray(); - return result; + return UTF8.GetString(result.ToArray()); } - public static byte[] RunEncode(byte[] input, byte[] magic) + public static byte[] RunEncode(string input, byte[] magic) { - if (input == null || input.Length == 0) throw new NullReferenceException(); + byte[] plain = UTF8.GetBytes(input); + bool[] evil = magic.ToList().ConvertAll(ch => (ch & 0xC0) == 0xC0).ToArray(); - byte[] result = new byte[input.Length]; - for (int i = 0; i < input.Length; i++) + var body = new List(plain.Length * 2); + for (int i = 0, j = 0; j < plain.Length; i++, j++) { - result[i] = (byte)(input[i] ^ magic[i % magic.Length]); + int n = i % magic.Length; + if (evil[n]) + { + byte ch = plain[j]; + byte sleepy = 0; + if (plain[j] > 0x40) + { + ch -= 0x40; + sleepy = 1; + } + body.Add((byte)(sleepy ^ magic[n])); + i++; + n = i % magic.Length; + body.Add((byte)(ch ^ magic[n])); + continue; + } + body.Add((byte)(plain[j] ^ magic[n])); } - return result; - } - - public static string DecodeToString(string inputFile, byte[] magic) - { - var a = RunDecode(inputFile, magic); - var d = UTF8.GetString(a); - - // trim last char - return d.Length > 0 ? d.Substring(0, d.Length - 1) : d; + using var mem = new MemoryStream(); + using var writer = new BinaryWriter(mem); + writer.Write((byte)0); + writer.Write(1); + writer.Write(-1); + writer.Write(1); + writer.Write(0); + writer.Write((byte)6); + writer.Write(1); + writer.Write7BitEncodedInt(body.Count); + writer.Write(body.ToArray()); + writer.Write((byte)0x0B); + return mem.ToArray(); } -} \ No newline at end of file +} diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs index e769672bc..05e41a760 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs @@ -39,8 +39,8 @@ public static GeneralData Load(byte[] magic) try { if (!File.Exists(configFile)) throw new FileNotFoundException("Zenless settings not found!"); - - string raw = Decode.DecodeToString(configFile, magic); + byte[] input = File.ReadAllBytes(configFile); + string raw = Decode.RunDecode(input, magic); #if DEBUG LogWriteLine($"RAW Zenless Settings: {configFile}\r\n" + @@ -58,4 +58,4 @@ public static GeneralData Load(byte[] magic) #endregion -} \ No newline at end of file +} From 1f03aa8405c1aaa943757c9bde648e3a61f34403 Mon Sep 17 00:00:00 2001 From: Kemal Setya Adhi Date: Mon, 8 Jul 2024 17:29:15 +0700 Subject: [PATCH 07/44] Adjust proper deserialization on ZZZ's GeneralData As the file is based on the old and obsolete BinaryFormatter serializer and no longer available for future .NET runtime releases, emulating the read/write routine should do the trick. Thanks @shatyuka for the help on the initial implementation - Adding basic props for ZZZ ``GeneralData`` class - Remove unused Encoder method - Clean-up namespaces - Implement JsonNode De/Serializer API - Implement JsonNode's Getter and Setter This is the difficult one :pepeHands: - Remove testing setter Co-authored-by: Shatyuka Co-authored-by: Bagus Nur Listiyono --- .../Classes/EventsManagement/EventsHandler.cs | 1 - .../BaseClass/MagicNodeBaseValues.cs | 166 ++++++++ .../BaseClass/SettingsGameVersionManager.cs | 39 ++ .../GameSettings/Zenless/Decode.cs | 92 ----- .../Zenless/FileClass/GeneralData.cs | 106 ++--- .../Zenless/JsonProperties/Properties.cs | 12 +- .../Zenless/RegistryClass/ScreenManager.cs | 20 +- .../GameSettings/Zenless/Settings.cs | 27 +- .../GameSettings/Zenless/Sleepy.cs | 363 ++++++++++++++++++ .../Interfaces/Class/JSONSerializerHelper.cs | 122 +++++- .../Class/JSONSerializerHelperAsync.cs | 13 +- .../Classes/Interfaces/IGameSettingsValue.cs | 13 +- .../ZenlessGameSettingsPage.xaml.cs | 4 +- CollapseLauncher/packages.lock.json | 6 - 14 files changed, 806 insertions(+), 178 deletions(-) create mode 100644 CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs create mode 100644 CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/SettingsGameVersionManager.cs delete mode 100644 CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Decode.cs create mode 100644 CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Sleepy.cs diff --git a/CollapseLauncher/Classes/EventsManagement/EventsHandler.cs b/CollapseLauncher/Classes/EventsManagement/EventsHandler.cs index d6dcf8c44..624d2f770 100644 --- a/CollapseLauncher/Classes/EventsManagement/EventsHandler.cs +++ b/CollapseLauncher/Classes/EventsManagement/EventsHandler.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Text.Json.Serialization; -using System.Threading.Tasks; using Windows.Foundation; using Windows.Networking.Connectivity; using static CollapseLauncher.InnerLauncherConfig; diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs new file mode 100644 index 000000000..de914003e --- /dev/null +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs @@ -0,0 +1,166 @@ +using CollapseLauncher.GameSettings.Zenless; +using Hi3Helper; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; + +#nullable enable +namespace CollapseLauncher.GameSettings.Base +{ + internal class MagicNodeBaseValues + where T : MagicNodeBaseValues, new() + { + [JsonIgnore] + [DebuggerBrowsable(DebuggerBrowsableState.Never)] +#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. + public byte[] Magic { get; protected set; } + + [JsonIgnore] + protected SettingsGameVersionManager GameVersionManager { get; set; } + + [JsonIgnore] + public JsonNode? SettingsJsonNode { get; protected set; } + + [JsonIgnore] + public JsonSerializerContext Context { get; protected set; } +#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. + + + [Obsolete("Loading settings with Load() is not supported for IGameSettingsValueMagic member. Use LoadWithMagic() instead!", true)] + public static T Load() => throw new NotSupportedException("Loading settings with Load() is not supported for IGameSettingsValueMagic member. Use LoadWithMagic() instead!"); + + public static T LoadWithMagic(byte[] magic, SettingsGameVersionManager versionManager, JsonSerializerContext context) + { + if (magic == null || magic.Length == 0) + throw new NullReferenceException($"Magic cannot be an empty array!"); + + try + { + string? filePath = versionManager.ConfigFilePath; + + if (!File.Exists(filePath)) throw new FileNotFoundException("MagicNodeBaseValues config file not found!"); + string raw = Sleepy.ReadString(filePath, magic); + +#if DEBUG + Logger.LogWriteLine($"RAW MagicNodeBaseValues Settings: {filePath}\r\n" + + $"{raw}", LogType.Debug, true); +#endif + JsonNode? node = raw.DeserializeAsJsonNode(); + T data = new T(); + data.InjectNodeAndMagic(node, magic, versionManager, context); + return data; + } + catch (Exception ex) + { + Logger.LogWriteLine($"Failed to parse MagicNodeBaseValues settings\r\n{ex}", LogType.Error, true); + return new T().DefaultValue(magic, versionManager, context); + } + } + + public T DefaultValue(byte[] magic, SettingsGameVersionManager versionManager, JsonSerializerContext context) + { + // Generate dummy data + T data = new T(); + + // Generate raw JSON string + string rawJson = data.Serialize(Context, false, false); + + // Deserialize it back to JSON Node and inject + // the node and magic + JsonNode? defaultJsonNode = rawJson.DeserializeAsJsonNode(); + data.InjectNodeAndMagic(defaultJsonNode, magic, versionManager, context); + + // Return + return data; + } + + public void Save() + { + // Get the file and dir path + string? filePath = GameVersionManager.ConfigFilePath; + string? fileDirPath = Path.GetDirectoryName(filePath); + + // Create the dir if not exist + if (string.IsNullOrEmpty(fileDirPath) && !Directory.Exists(fileDirPath)) + Directory.CreateDirectory(fileDirPath!); + + // Write into the file + string jsonString = SettingsJsonNode.SerializeJsonNode(Context, false, false); + Sleepy.WriteString(filePath!, jsonString, Magic); + } + + public bool Equals(GeneralData? other) + { + return true; + } + + protected virtual void InjectNodeAndMagic(JsonNode? jsonNode, byte[] magic, SettingsGameVersionManager versionManager, JsonSerializerContext context) + { + SettingsJsonNode = jsonNode; + Magic = magic; + Context = context; + } + + protected virtual string GetValue(JsonNode? node, string keyName, string defaultValue) + { + // Get node as object + JsonObject? jsonObject = node?.AsObject(); + + // If node is null, return the default value + if (jsonObject == null) return defaultValue; + + // Try get node as struct value + if (jsonObject.TryGetPropertyValue(keyName, out JsonNode? jsonNodeValue) && jsonNodeValue != null) + { + string returnValue = jsonNodeValue.AsValue().GetValue(); + return returnValue; + } + + return defaultValue; + } + + protected virtual TValue GetValue(JsonNode? node, string keyName, TValue defaultValue) + where TValue : struct + { + // Get node as object + JsonObject? jsonObject = node?.AsObject(); + + // If node is null, return the default value + if (jsonObject == null) return defaultValue; + + // Try get node as struct value + if (jsonObject.TryGetPropertyValue(keyName, out JsonNode? jsonNodeValue) && jsonNodeValue != null) + { + TValue returnValue = jsonNodeValue.AsValue().GetValue(); + return returnValue; + } + + return defaultValue; + } + + protected virtual void SetValue(JsonNode? node, string keyName, TValue value) + { + // If node is null, return and ignore + if (node == null) return; + + // Get node as object + JsonObject? jsonObject = node.AsObject(); + + // If node is null, return and ignore + if (jsonObject == null) return; + + // Create an instance of the JSON node value + JsonValue? jsonValue = JsonValue.Create(value); + + // If the node has object, then assign the new value + if (jsonObject.ContainsKey(keyName)) + node[keyName] = jsonValue; + // Otherwise, add it + else + jsonObject.Add(new KeyValuePair(keyName, jsonValue)); + } + } +} diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/SettingsGameVersionManager.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/SettingsGameVersionManager.cs new file mode 100644 index 000000000..96d395c79 --- /dev/null +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/SettingsGameVersionManager.cs @@ -0,0 +1,39 @@ +using CollapseLauncher.Interfaces; +using System.IO; + +#nullable enable +namespace CollapseLauncher.GameSettings.Base +{ + internal sealed class SettingsGameVersionManager + { + /// + /// Create an instance of the IGameVersionCheck adapter for members.
+ /// Note: Use the formatting string with index 0 for executable and 1 for the config filename.
+ ///
+ /// For example:
+ /// "{0}_Data\Persistent\LocalStorage\{1}" + ///
+ /// The game version manager to be injected + /// The formatted string for the file location + /// Name of the config file + /// to be used for members. + internal static SettingsGameVersionManager Create(IGameVersionCheck? gameVersionManager, string fileDirPathFormat, string fileName) + { + return new SettingsGameVersionManager + { + VersionManager = gameVersionManager, + ConfigFileLocationFormat = fileDirPathFormat, + ConfigFileName = fileName + }; + } + + internal IGameVersionCheck? VersionManager { get; set; } + + internal string? GameFolder => VersionManager?.GameDirPath; + internal string? GameExecutable => Path.GetFileNameWithoutExtension(VersionManager?.GamePreset.GameExecutableName!); + internal string? ConfigFileLocationFormat { get; set; } + internal string? ConfigFileName { get; set; } + internal string? ConfigFilePath { get => Path.Combine(GameFolder ?? string.Empty, string.Format(ConfigFileLocationFormat ?? string.Empty, GameExecutable, ConfigFileName)); } + } + +} diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Decode.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Decode.cs deleted file mode 100644 index 7dcec3033..000000000 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Decode.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using static System.Text.Encoding; - -namespace CollapseLauncher.GameSettings.Zenless; - -public static class Decode -{ - public static string RunDecode(byte[] input, byte[] magic) - { - using var mem = new MemoryStream(input); - using var reader = new BinaryReader(mem); - reader.ReadByte(); - reader.ReadInt32(); - reader.ReadInt32(); - reader.ReadInt32(); - reader.ReadInt32(); - reader.ReadByte(); - reader.ReadInt32(); - byte[] body = reader.ReadBytes(reader.Read7BitEncodedInt()); - - bool[] evil = magic.ToList().ConvertAll(ch => (ch & 0xC0) == 0xC0).ToArray(); - var result = new List(body.Length); - var sleepy = false; - for (int i = 0; i < body.Length; i++) - { - int n = i % magic.Length; - byte ch = (byte)(body[i] ^ magic[n]); - if (evil[n]) - { - if (ch != 0) - { - sleepy = true; - } - } - else - { - if (sleepy) - { - ch += 0x40; - sleepy = false; - } - result.Add(ch); - } - } - - return UTF8.GetString(result.ToArray()); - } - - public static byte[] RunEncode(string input, byte[] magic) - { - byte[] plain = UTF8.GetBytes(input); - bool[] evil = magic.ToList().ConvertAll(ch => (ch & 0xC0) == 0xC0).ToArray(); - - var body = new List(plain.Length * 2); - for (int i = 0, j = 0; j < plain.Length; i++, j++) - { - int n = i % magic.Length; - if (evil[n]) - { - byte ch = plain[j]; - byte sleepy = 0; - if (plain[j] > 0x40) - { - ch -= 0x40; - sleepy = 1; - } - body.Add((byte)(sleepy ^ magic[n])); - i++; - n = i % magic.Length; - body.Add((byte)(ch ^ magic[n])); - continue; - } - body.Add((byte)(plain[j] ^ magic[n])); - } - - using var mem = new MemoryStream(); - using var writer = new BinaryWriter(mem); - writer.Write((byte)0); - writer.Write(1); - writer.Write(-1); - writer.Write(1); - writer.Write(0); - writer.Write((byte)6); - writer.Write(1); - writer.Write7BitEncodedInt(body.Count); - writer.Write(body.ToArray()); - writer.Write((byte)0x0B); - return mem.ToArray(); - } -} diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs index 05e41a760..4d2aee390 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs @@ -1,61 +1,75 @@ -using CollapseLauncher.GameSettings.Zenless.Context; -using CollapseLauncher.Helper.Metadata; -using CollapseLauncher.Statics; -using Hi3Helper; -using System; -using System.IO; -using static Hi3Helper.Logger; +using CollapseLauncher.GameSettings.Base; +using CollapseLauncher.GameSettings.Zenless.JsonProperties; +using CollapseLauncher.Interfaces; +using System.Collections.Generic; +using System.Text.Json.Serialization; namespace CollapseLauncher.GameSettings.Zenless; -internal class GeneralData +internal class GeneralData : MagicNodeBaseValues, IGameSettingsValueMagic { #region Fields #nullable enable - private static GamePresetProperty? _gamePresetProperty; - private static GamePresetProperty ZenlessGameProperty - { - get - { - if (_gamePresetProperty != null) return _gamePresetProperty; - _gamePresetProperty = GamePropertyVault.GetCurrentGameProperty(); - if (_gamePresetProperty._GamePreset.GameType != GameNameType.Zenless) - throw new InvalidDataException("[ZenlessSettings] GameProperty value is not Zenless!"); - return _gamePresetProperty; - } - } + #endregion - private static string gameFolder = ZenlessGameProperty._GameVersion.GameDirPath; - private static string gameExec = Path.GetFileNameWithoutExtension(ZenlessGameProperty._GamePreset.GameExecutableName!); - private static string configFileLoc = $@"{gameExec}_Data\Persistent\LocalStorage\GENERAL_DATA.bin"; + #region Properties + [JsonPropertyName("$Type")] + public string? TypeString { get; set; } = "MoleMole.GeneralLocalDataItem"; - private static string configFile = Path.Join(gameFolder, configFileLoc); - #endregion + [JsonPropertyName("deviceUUID")] + public string? DeviceUUID { get; set; } + + [JsonPropertyName("userLocalDataVersionId")] + public string? UserLocalDataVersionId { get; set; } = "0.0.1"; - #region Methods + [JsonPropertyName("curAccountName")] + public string? CurrentAccountName { get; set; } + + [JsonPropertyName("selectedServerIndex")] + public int SelectedServerIndex { get; set; } = 0; + + [JsonPropertyName("DeviceLanguageType")] + public int DeviceLanguageType { get; set; } = -1; + + [JsonPropertyName("DeviceLanguageVoiceType")] + public int DeviceLanguageVoiceType + { + get => GetValue(SettingsJsonNode, "DeviceLanguageVoiceType", -1); // Set the default value here + set => SetValue(SettingsJsonNode, "DeviceLanguageVoiceType", value); + } - public static GeneralData Load(byte[] magic) + [JsonPropertyName("selectServerName")] + public string? SelectedServerName { - try - { - if (!File.Exists(configFile)) throw new FileNotFoundException("Zenless settings not found!"); - byte[] input = File.ReadAllBytes(configFile); - string raw = Decode.RunDecode(input, magic); - - #if DEBUG - LogWriteLine($"RAW Zenless Settings: {configFile}\r\n" + - $"{raw}", LogType.Debug, true); - #endif - GeneralData data = raw.Deserialize(ZenlessSettingsJSONContext.Default) ?? new GeneralData(); - return data; - } - catch (Exception ex) - { - LogWriteLine($"Failed to parse Zenless settings\r\n{ex}", LogType.Error, true); - return new GeneralData(); - } + get => GetValue(SettingsJsonNode, "selectServerName", "prod_gf_jp"); // Set the default value here + set => SetValue(SettingsJsonNode, "selectServerName", value); } - + [JsonPropertyName("SystemSettingDataMap")] + public Dictionary SystemSettingDataMap { get; set; } = new Dictionary(); + + [JsonPropertyName("KeyboardBindingMap")] + public Dictionary KeyboardBindingMap { get; set; } = new Dictionary(); + + [JsonPropertyName("MouseBindingMap")] + public Dictionary MouseBindingMap { get; set; } = new Dictionary(); + + [JsonPropertyName("GamepadBindingMap")] + public Dictionary GamepadBindingMap { get; set; } = new Dictionary(); + + [JsonPropertyName("HDRSettingRecordState")] + public int HDRSettingRecordState { get; set; } = 0; + + [JsonPropertyName("HDRMaxLuminosityLevel")] + public int HDRMaxLuminosityLevel { get; set; } = -1; + + [JsonPropertyName("HDRUIPaperWhiteLevel")] + public int HDRUIPaperWhiteLevel { get; set; } = -1; + + [JsonPropertyName("LastVHSStoreOpenTime")] + public string? LastVHSStoreOpenTime { get; set; } = "01/01/0001 00:00:00"; + + [JsonPropertyName("DisableBattleUIOptimization")] + public bool DisableBattleUIOptimization { get; set; } = false; #endregion } diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/JsonProperties/Properties.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/JsonProperties/Properties.cs index 175dc43c0..2f22e72bf 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/JsonProperties/Properties.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/JsonProperties/Properties.cs @@ -4,12 +4,12 @@ namespace CollapseLauncher.GameSettings.Zenless.JsonProperties; public class SystemSettingDataMap { - [JsonPropertyName("$Type")] - public string Type { get; set; } - + [JsonPropertyName("$Type")] + public string TypeString { get; set; } = "MoleMole.SystemSettingLocalData"; + [JsonPropertyName("Version")] - public int Version { get; set; } - + public int Version { get; set; } = 1; + [JsonPropertyName("Data")] - public int Data { get; set; } + public int Data { get; set; } = 0; } \ No newline at end of file diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/RegistryClass/ScreenManager.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/RegistryClass/ScreenManager.cs index 6c965d3d8..db6175969 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/RegistryClass/ScreenManager.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/RegistryClass/ScreenManager.cs @@ -25,7 +25,7 @@ public enum FullScreenMode : int { Fullscreen = 1, Something, - SomethingElse + Windowed } #endregion @@ -91,7 +91,7 @@ public override string sizeResString /// Range: 0 - 1 /// Default: 1 /// - public FullScreenMode fullscreen { get; set; } + public FullScreenMode fullscreen { get; set; } = FullScreenMode.Fullscreen; public override bool isfullScreen { @@ -103,7 +103,7 @@ public override bool isfullScreen set => fullscreen = value switch { true => FullScreenMode.Fullscreen, - _ => 0, + false => FullScreenMode.Windowed, }; } #endregion @@ -125,9 +125,10 @@ public static ScreenManager Load() int height = (int)valueHeight; FullScreenMode fullscreen = (FullScreenMode)valueFullscreen; #if DEBUG - LogWriteLine($"Loaded Genshin Settings: {_ValueNameScreenManagerWidth} : {width}", LogType.Debug, true); - LogWriteLine($"Loaded Genshin Settings: {_ValueNameScreenManagerHeight} : {height}", LogType.Debug, true); - LogWriteLine($"Loaded Genshin Settings: {_ValueNameScreenManagerFullscreen} : {fullscreen}", LogType.Debug, true); + LogWriteLine($"Loaded Zenless Settings:\r\n\t" + + $"{_ValueNameScreenManagerWidth} : {width}\r\n\t" + + $"{_ValueNameScreenManagerHeight} : {height}\r\n\t" + + $"{_ValueNameScreenManagerFullscreen} : {fullscreen}", LogType.Debug, true); #endif return new ScreenManager { width = width, height = height, fullscreen = fullscreen }; } @@ -155,9 +156,10 @@ public override void Save() RegistryRoot?.SetValue(_ValueNameScreenManagerWidth, width, RegistryValueKind.DWord); RegistryRoot?.SetValue(_ValueNameScreenManagerHeight, height, RegistryValueKind.DWord); #if DEBUG - LogWriteLine($"Saved Genshin Settings: {_ValueNameScreenManagerFullscreen} : {RegistryRoot?.GetValue(_ValueNameScreenManagerFullscreen, null)}", LogType.Debug, true); - LogWriteLine($"Saved Genshin Settings: {_ValueNameScreenManagerWidth} : {RegistryRoot?.GetValue(_ValueNameScreenManagerWidth, null)}", LogType.Debug, true); - LogWriteLine($"Saved Genshin Settings: {_ValueNameScreenManagerHeight} : {RegistryRoot?.GetValue(_ValueNameScreenManagerHeight, null)}", LogType.Debug, true); + LogWriteLine($"Saved Zenless Settings:]\r\n\t" + + $"{_ValueNameScreenManagerFullscreen} : {RegistryRoot?.GetValue(_ValueNameScreenManagerFullscreen, null)}\r\n\t" + + $"{_ValueNameScreenManagerWidth} : {RegistryRoot?.GetValue(_ValueNameScreenManagerWidth, null)}\r\n\t" + + $"{_ValueNameScreenManagerHeight} : {RegistryRoot?.GetValue(_ValueNameScreenManagerHeight, null)}", LogType.Debug, true); #endif } catch (Exception ex) diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Settings.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Settings.cs index c66d0225d..0cb792af1 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Settings.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Settings.cs @@ -1,4 +1,5 @@ using CollapseLauncher.GameSettings.Base; +using CollapseLauncher.GameSettings.Zenless.Context; using CollapseLauncher.GameVersioning; using CollapseLauncher.Interfaces; using System; @@ -11,10 +12,10 @@ namespace CollapseLauncher.GameSettings.Zenless internal class ZenlessSettings : SettingsBase { #region Variables - #nullable enable +#nullable enable [DebuggerBrowsable(DebuggerBrowsableState.Never)] private byte[]? _magicReDo; - #nullable restore +#nullable restore [DebuggerBrowsable(DebuggerBrowsableState.Never)] private byte[] MagicReDo { @@ -22,7 +23,7 @@ private byte[] MagicReDo { if (_magicReDo != null) return _magicReDo; _magicReDo = (_gameVersionManager as GameTypeZenlessVersion)?.GamePreset - .GetGameDataTemplate("ImSleepin", new byte[] {1, 0, 0, 0}); + .GetGameDataTemplate("ImSleepin", new byte[] { 1, 0, 0, 0 }); if (_magicReDo == null || _magicReDo.Length == 0) throw new NullReferenceException("MagicReDo value for ZZZ settings is empty!"); return _magicReDo; @@ -31,34 +32,40 @@ private byte[] MagicReDo #endregion #region Properties - public GeneralData GeneralData {get;set;} + public GeneralData GeneralData { get; set; } #endregion - + public ZenlessSettings(IGameVersionCheck GameVersionManager) : base(GameVersionManager) { // Initialize magic - _ = MagicReDo; - + _ = MagicReDo; + InitializeSettings(); } public sealed override void InitializeSettings() { + const string ZZZSettingsConfigFile = @"{0}_Data\Persistent\LocalStorage\{1}"; + base.InitializeSettings(); + SettingsScreen = ScreenManager.Load(); + GeneralData = GeneralData.LoadWithMagic( + MagicReDo, + SettingsGameVersionManager.Create(_gameVersionManager, ZZZSettingsConfigFile, "GENERAL_DATA.bin"), + ZenlessSettingsJSONContext.Default); } public override void ReloadSettings() { - GeneralData = GeneralData.Load(MagicReDo); - InitializeSettings(); } - + public override void SaveSettings() { base.SaveSettings(); SettingsScreen.Save(); + //GeneralData.Save(); } public override IGameSettingsUniversal AsIGameSettingsUniversal() => this; diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Sleepy.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Sleepy.cs new file mode 100644 index 000000000..521550fe5 --- /dev/null +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Sleepy.cs @@ -0,0 +1,363 @@ +/* + * Initial Implementation Credit by: @Shatyuka + */ + +using System; +using System.Buffers; +using System.IO; +using System.Runtime.CompilerServices; +using System.Text; + +namespace CollapseLauncher.GameSettings.Zenless; + +#nullable enable +internal static class Sleepy +{ + // https://github.com/dotnet/runtime/blob/a7efcd9ca9255dc9faa8b4a2761cdfdb62619610/src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/Formatters/Binary/BinaryEnums.cs#L7C1-L32C6 + private enum BinaryHeaderEnum + { + SerializedStreamHeader = 0, + Object = 1, + ObjectWithMap = 2, + ObjectWithMapAssemId = 3, + ObjectWithMapTyped = 4, + ObjectWithMapTypedAssemId = 5, + ObjectString = 6, + Array = 7, + MemberPrimitiveTyped = 8, + MemberReference = 9, + ObjectNull = 10, + MessageEnd = 11, + Assembly = 12, + ObjectNullMultiple256 = 13, + ObjectNullMultiple = 14, + ArraySinglePrimitive = 15, + ArraySingleObject = 16, + ArraySingleString = 17, + CrossAppDomainMap = 18, + CrossAppDomainString = 19, + CrossAppDomainAssembly = 20, + MethodCall = 21, + MethodReturn = 22, + BinaryReference = -1 + } + + // https://github.com/dotnet/runtime/blob/a7efcd9ca9255dc9faa8b4a2761cdfdb62619610/src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/Formatters/Binary/BinaryEnums.cs#L35 + private enum BinaryTypeEnum + { + Primitive = 0, + String = 1, + Object = 2, + ObjectUrt = 3, + ObjectUser = 4, + ObjectArray = 5, + StringArray = 6, + PrimitiveArray = 7, + } + + // https://github.com/dotnet/runtime/blob/a7efcd9ca9255dc9faa8b4a2761cdfdb62619610/src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/Formatters/Binary/BinaryEnums.cs#L47 + private enum BinaryArrayTypeEnum + { + Single = 0, + Jagged = 1, + Rectangular = 2, + SingleOffset = 3, + JaggedOffset = 4, + RectangularOffset = 5, + } + + // https://github.com/dotnet/runtime/blob/a7efcd9ca9255dc9faa8b4a2761cdfdb62619610/src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/Formatters/Binary/BinaryEnums.cs#L99 + private enum InternalArrayTypeE + { + Empty = 0, + Single = 1, + Jagged = 2, + Rectangular = 3, + Base64 = 4, + } + + internal static string ReadString(string filePath, ReadOnlySpan magic) + { + // Get the FileInfo + FileInfo fileInfo = new FileInfo(filePath); + if (!fileInfo.Exists) + throw new FileNotFoundException("[Sleepy::ReadString] File does not exist!"); + + // Open the stream and get the thing + using FileStream stream = fileInfo.Open(FileMode.Open, FileAccess.Read, FileShare.Read); + return ReadString(stream, magic); + } + + internal static unsafe string ReadString(Stream stream, ReadOnlySpan magic) + { + // Stream assertion + if (!stream.CanRead) throw new ArgumentException("[Sleepy::ReadString] Stream must be readable!", nameof(stream)); + + // Assign the reader + using BinaryReader reader = new BinaryReader(stream, Encoding.UTF8, true); + + // Emulate and Assert the BinaryFormatter header info + reader.EmulateSleepyBinaryFormatterHeaderAssertion(); + + // Get the data length + int length = reader.GetBinaryFormatterDataLength(); + int magicLength = magic.Length; + + // Alloc temporary buffers + bool isRent = length <= 1 << 17; // Check if length <= 128 KiB + char[] bufferChars = isRent ? ArrayPool.Shared.Rent(length) : new char[length]; + + // Do the do + CreateEvil(magic, out bool[] evil, out int evilsCount); + fixed (bool* evp = &evil[0]) + fixed (char* bp = &bufferChars[0]) + { + try + { + // Do the do (pt. 2) + int j = InternalDecode(magic, evp, reader, length, magicLength, bp); + + // Emulate and Assert the BinaryFormatter footer + reader.EmulateSleepyBinaryFormatterFooterAssertion(); + + // Return + return new string(bp, 0, j); + } + finally + { + // Return and clear the buffer, to only returns the return string. + if (isRent) ArrayPool.Shared.Return(bufferChars, true); + else Array.Clear(bufferChars); + } + } + } + + private static unsafe int InternalDecode(ReadOnlySpan magic, bool* evil, BinaryReader reader, int length, int magicLength, char* bp) + { + bool eepy = false; + + int j = 0; + int i = 0; + int n = 0; + + amimir: + n = i % magicLength; + byte c = reader.ReadByte(); + byte ch = (byte)(c ^ magic[n]); + + if (*(evil + n)) + { + eepy = ch != 0; + } + else + { + if (eepy) + { + ch += 0x40; + eepy = false; + } + *(bp + j++) = (char)ch; + } + + if (++i < length) goto amimir; + return j; + } + + internal static void WriteString(string filePath, ReadOnlySpan content, ReadOnlySpan magic) + { + // Get the FileInfo + FileInfo fileInfo = new FileInfo(filePath); + + // Create the stream and write the thing + using FileStream stream = fileInfo.Open(FileMode.Create, FileAccess.Write, FileShare.Write); + WriteString(stream, content, magic); + } + + internal static unsafe void WriteString(Stream stream, ReadOnlySpan content, ReadOnlySpan magic) + { + // Stream assertion + if (!stream.CanWrite) throw new ArgumentException("[Sleepy::WriteString] Stream must be writable!", nameof(stream)); + + // Magic assertion + if (magic.Length == 0) throw new ArgumentException("[Sleepy::WriteString] Magic cannot be empty!", nameof(magic)); + + // Assign the writer + using BinaryWriter writer = new BinaryWriter(stream, Encoding.UTF8, true); + + // Emulate to write the BinaryFormatter header + writer.EmulateSleepyBinaryFormatterHeaderWrite(); + + // Do the do + int contentLen = content.Length; + int bufferLen = contentLen * 2; + bool isRent = bufferLen <= 2 << 17; + + // Alloc temporary buffers + byte[] contentBytes = isRent ? ArrayPool.Shared.Rent(bufferLen) : new byte[bufferLen]; + byte[] encodedBytes = isRent ? ArrayPool.Shared.Rent(bufferLen) : new byte[bufferLen]; + + // Do the do + CreateEvil(magic, out bool[] evil, out int evilsCount); + + fixed (char* cp = &content[0]) + fixed (byte* bp = &contentBytes[0]) + fixed (byte* ep = &encodedBytes[0]) + fixed (bool* evp = &evil[0]) + { + try + { + // Get the string bytes + _ = Encoding.UTF8.GetBytes(cp, contentLen, bp, bufferLen); + + // Do the do (pt. 2) + int h = InternalWrite(magic, contentLen, bp, ep, evp); + + writer.Write7BitEncodedInt(h); + writer.BaseStream.Write(encodedBytes, 0, h); + writer.EmulateSleepyBinaryFormatterFooterWrite(); + } + finally + { + // Return and clear the buffer. + if (isRent) ArrayPool.Shared.Return(contentBytes, true); + else Array.Clear(contentBytes); + + if (isRent) ArrayPool.Shared.Return(encodedBytes, true); + else Array.Clear(encodedBytes); + } + } + } + + private static unsafe int InternalWrite(ReadOnlySpan magic, int contentLen, byte* bp, byte* ep, bool* evil) + { + int h = 0; + int i = 0; + int j = 0; + + amimir: + int n = i % magic.Length; + byte ch = *(bp + j); + if (*(evil + n)) + { + byte eepy = 0; + if (*(bp + j) > 0x40) + { + ch -= 0x40; + eepy = 1; + } + *(ep + h++) = (byte)(eepy ^ magic[n]); + + n = ++i % magic.Length; + } + + *(ep + h++) = (byte)(ch ^ magic[n]); + ++i; + ++j; + if (j < contentLen) goto amimir; + return h; + } + + private static void CreateEvil(ReadOnlySpan magic, out bool[] evilist, out int evilsCount) + { + int magicLength = magic.Length; + int i = 0; + evilist = new bool[magicLength]; + evilsCount = 0; + evilist: + int n = i % magicLength; + evilist[i] = (magic[n] & 0xC0) == 0xC0; + if (evilist[i]) ++evilsCount; + if (++i < magicLength) goto evilist; + } + + private static void EmulateSleepyBinaryFormatterHeaderAssertion(this BinaryReader reader) + { + // Do assert [class] -> [string object] + // START! + // Check if the first byte is SerializedStreamHeader + reader.LogAssertInfoByteEnum(BinaryHeaderEnum.SerializedStreamHeader); + + // Check if the type is an Object + reader.LogAssertInfoInt32Enum(BinaryHeaderEnum.Object); + + // Check if the type is a BinaryReference + reader.LogAssertInfoInt32Enum(BinaryHeaderEnum.BinaryReference); + + // Check if the BinaryReference type is a String + reader.LogAssertInfoInt32Enum(BinaryTypeEnum.String); + + // Check for the binary array type and check if it's Single + reader.LogAssertInfoInt32Enum(BinaryArrayTypeEnum.Single); + + // Check for the binary type and check if it's StringArray (UTF-8) + reader.LogAssertInfoByteEnum(BinaryTypeEnum.StringArray); + + // Check for the internal array type and check if it's Single + reader.LogAssertInfoInt32Enum(InternalArrayTypeE.Single); + } + + // Do assert [class] -> [EOF mark] + // START! + private static void EmulateSleepyBinaryFormatterFooterAssertion(this BinaryReader reader) => + reader.LogAssertInfoByteEnum(BinaryHeaderEnum.MessageEnd); + + private static void EmulateSleepyBinaryFormatterHeaderWrite(this BinaryWriter writer) + { + // Emulate to write Sleepy BinaryFormatter header information + writer.WriteEnumAsByte(BinaryHeaderEnum.SerializedStreamHeader); + writer.WriteEnumAsInt32(BinaryHeaderEnum.Object); + writer.WriteEnumAsInt32(BinaryHeaderEnum.BinaryReference); + writer.WriteEnumAsInt32(BinaryTypeEnum.String); + writer.WriteEnumAsInt32(BinaryArrayTypeEnum.Single); + writer.WriteEnumAsByte(BinaryTypeEnum.StringArray); + writer.WriteEnumAsInt32(InternalArrayTypeE.Single); + } + + // Emulate to write Sleepy BinaryFormatter footer EOF + private static void EmulateSleepyBinaryFormatterFooterWrite(this BinaryWriter writer) => + writer.WriteEnumAsByte(BinaryHeaderEnum.MessageEnd); + + private static void WriteEnumAsByte(this BinaryWriter writer, T headerEnum) + where T : struct, Enum + { + int enumValue = Unsafe.As(ref headerEnum); + writer.Write((byte)enumValue); + } + + private static void WriteEnumAsInt32(this BinaryWriter writer, T headerEnum) + where T : struct, Enum + { + int enumValue = Unsafe.As(ref headerEnum); + writer.Write(enumValue); + } + + private static void LogAssertInfoByteEnum(this BinaryReader stream, T assertHeaderEnum) + where T : struct, Enum + { + int currentInt = stream.ReadByte(); + LogAssertInfo(stream, ref assertHeaderEnum, ref currentInt); + } + + private static void LogAssertInfoInt32Enum(this BinaryReader stream, T assertHeaderEnum) + where T : struct, Enum + { + int currentInt = stream.ReadInt32(); + LogAssertInfo(stream, ref assertHeaderEnum, ref currentInt); + } + + private static void LogAssertInfo(BinaryReader reader, ref T assertHeaderEnum, ref int currentInt) + where T : struct, Enum + { + int intAssertCasted = Unsafe.As(ref assertHeaderEnum); + if (intAssertCasted != currentInt) + { + string? assertHeaderEnumValueName = Enum.GetName(assertHeaderEnum); + T comparedEnumCasted = Unsafe.As(ref currentInt); + string? comparedHeaderEnumValueName = Enum.GetName(comparedEnumCasted); + + throw new InvalidDataException($"[Sleepy::LogAssertInfo] BinaryFormatter header is not valid at stream pos: {reader.BaseStream.Position:x8}. Expecting object enum: {assertHeaderEnumValueName} but getting: {comparedHeaderEnumValueName} instead!"); + } + } + + private static int GetBinaryFormatterDataLength(this BinaryReader reader) => reader.Read7BitEncodedInt(); +} diff --git a/CollapseLauncher/Classes/Interfaces/Class/JSONSerializerHelper.cs b/CollapseLauncher/Classes/Interfaces/Class/JSONSerializerHelper.cs index 58f34b084..3cdcf7a81 100644 --- a/CollapseLauncher/Classes/Interfaces/Class/JSONSerializerHelper.cs +++ b/CollapseLauncher/Classes/Interfaces/Class/JSONSerializerHelper.cs @@ -4,15 +4,17 @@ using System.Text; using System.Text.Encodings.Web; using System.Text.Json; +using System.Text.Json.Nodes; using System.Text.Json.Serialization; using System.Text.Json.Serialization.Metadata; +#nullable enable namespace CollapseLauncher { internal static partial class JSONSerializerHelper { private const short MAX_ALLOWED_RENT_BUFFER = (4 << 10) - 1; - private const byte MIN_ALLOWED_CHAR_LENGTH = 2; + private const byte MIN_ALLOWED_CHAR_LENGTH = 2; private static readonly JavaScriptEncoder jsonEncoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping; private static readonly JsonWriterOptions jsonWriterOptions = new JsonWriterOptions() @@ -30,36 +32,56 @@ internal static partial class JSONSerializerHelper AllowTrailingCommas = true, CommentHandling = JsonCommentHandling.Skip }; + private static readonly JsonNodeOptions jsonNodeOptions = new JsonNodeOptions() + { + PropertyNameCaseInsensitive = false // Restricted to use the exact property name case + }; + private static readonly JsonDocumentOptions jsonDocumentOptions = new JsonDocumentOptions() + { + AllowTrailingCommas = true, + CommentHandling = JsonCommentHandling.Skip + }; private static readonly ArrayBufferWriter jsonBufferWriter = new ArrayBufferWriter(4 << 10); private static readonly Utf8JsonWriter jsonWriter = new Utf8JsonWriter(jsonBufferWriter, jsonWriterOptions); private static readonly Utf8JsonWriter jsonWriterIndented = new Utf8JsonWriter(jsonBufferWriter, jsonWriterOptionsIndented); -#nullable enable internal static T? Deserialize(this string data, JsonSerializerContext context, T? defaultType = null) where T : class => InnerDeserialize(data, context, defaultType); internal static T? Deserialize(this string data, JsonSerializerContext context, T? defaultType = null) where T : struct => InnerDeserialize(data, context, defaultType); + internal static JsonNode? DeserializeAsJsonNode(this string data) + => InnerDeserializeAsJsonNode(data); + internal static T? Deserialize(this ReadOnlySpan data, JsonSerializerContext context, T? defaultType = null) where T : class => InnerDeserialize(data, context, defaultType); internal static T? Deserialize(this ReadOnlySpan data, JsonSerializerContext context, T? defaultType = null) where T : struct => InnerDeserialize(data, context, defaultType); + internal static JsonNode? DeserializeAsJsonNode(this ReadOnlySpan data) + => InnerDeserializeAsJsonNode(data); + internal static T? Deserialize(this ReadOnlySpan data, JsonSerializerContext context, T? defaultType = null) where T : class => InnerDeserialize(data, context, defaultType); internal static T? Deserialize(this ReadOnlySpan data, JsonSerializerContext context, T? defaultType = null) where T : struct => InnerDeserialize(data, context, defaultType); + internal static JsonNode? DeserializeAsJsonNode(this ReadOnlySpan data) + => InnerDeserializeAsJsonNode(data); + internal static T? Deserialize(this Stream data, JsonSerializerContext context, T? defaultType = null) where T : class => InnerDeserializeStream(data, context, defaultType); internal static T? Deserialize(this Stream data, JsonSerializerContext context, T? defaultType = null) where T : struct => InnerDeserializeStream(data, context, defaultType); + internal static JsonNode? DeserializeAsJsonNode(this Stream data) + => InnerDeserializeStreamAsJsonNode(data); + private static T? InnerDeserializeStream(Stream data, JsonSerializerContext context, T? defaultType) { // Check if the data length is 0, then return default value @@ -111,9 +133,62 @@ internal static partial class JSONSerializerHelper return JsonSerializer.Deserialize(ref jsonReader, typeInfo) ?? defaultType ?? default; } + private static JsonNode? InnerDeserializeStreamAsJsonNode(Stream data) + { + // Check if the data length is 0, then return default value + if (data.Length == 0) return JsonNode.Parse("{}", jsonNodeOptions); + + // Try deserialize to JSON Node + return JsonNode.Parse(data, jsonNodeOptions, jsonDocumentOptions); + } + + private static JsonNode? InnerDeserializeAsJsonNode(ReadOnlySpan data) + { + // Check if the data length is less than 2 bytes (assuming the buffer is "{}"), then return default value + if (data.Length <= MIN_ALLOWED_CHAR_LENGTH) return JsonNode.Parse("{}", jsonNodeOptions); + + // Try trimming the \0 char at the end of the data + ReadOnlySpan dataTrimmed = data.TrimEnd((char)0x00); + + // Get the temporary buffer + int tempBufferLength = dataTrimmed.Length * 2; + bool IsUseRentBuffer = tempBufferLength <= MAX_ALLOWED_RENT_BUFFER; + byte[] tempBuffer = IsUseRentBuffer ? ArrayPool.Shared.Rent(tempBufferLength) : new byte[tempBufferLength]; + + try + { + // Convert the char[] buffer into byte[] + int bufferWritten = Encoding.UTF8.GetBytes(dataTrimmed, tempBuffer); + // Start deserialize and return + return InnerDeserializeAsJsonNode(tempBuffer.AsSpan(0, bufferWritten)); + } + catch { throw; } + finally + { + // Once the process is completed, then return the rented buffer (if it's being used) + if (IsUseRentBuffer) ArrayPool.Shared.Return(tempBuffer); + } + } + + private static JsonNode? InnerDeserializeAsJsonNode(ReadOnlySpan data) + { + // Check if the data length is less than 2 bytes (assuming the buffer is "{}"), then return default value + if (data.Length <= MIN_ALLOWED_CHAR_LENGTH) return JsonNode.Parse("{}", jsonNodeOptions); + + // Try trimming the \0 char at the end of the data + ReadOnlySpan dataTrimmed = data.TrimEnd((byte)0x00); + Utf8JsonReader jsonReader = new Utf8JsonReader(dataTrimmed, jsonReaderOptions); + + // Try deserialize to JSON Node + return JsonNode.Parse(ref jsonReader, jsonNodeOptions); + } + internal static string Serialize(this T? value, JsonSerializerContext context, bool isIncludeNullEndChar = true, bool isWriteIndented = false) => InnerSerialize(value, context, isIncludeNullEndChar, isWriteIndented); + internal static string SerializeJsonNode(this JsonNode? node, JsonSerializerContext context, bool isIncludeNullEndChar = true, bool isWriteIndented = false) + => InnerSerializeJsonNode(node, context, isIncludeNullEndChar, isWriteIndented); + private static string InnerSerialize(this T? data, JsonSerializerContext context, bool isIncludeNullEndChar, bool isWriteIndented) { const string _defaultValue = "{}"; @@ -155,6 +230,47 @@ private static string InnerSerialize(this T? data, JsonSerializerContext cont } } } -#nullable disable + + private static string InnerSerializeJsonNode(this JsonNode? node, JsonSerializerContext context, bool isIncludeNullEndChar, bool isWriteIndented) + { + const string _defaultValue = "{}"; + // Check if the node is null, then return default value + if (node == null) return _defaultValue; + + // Lock the buffer + lock (jsonBufferWriter) + { + // Clear the writer and its buffer + jsonBufferWriter.Clear(); + + // SANITY CHECK: Check if the buffer is already zero-ed + if (jsonBufferWriter.WrittenCount != 0) throw new InvalidOperationException("Illegal Fault: The buffer hasn't been zeroed!"); + + // Reset the writer state + if (isWriteIndented) jsonWriterIndented.Reset(); + else jsonWriter.Reset(); + + // Assign the writer + Utf8JsonWriter writer = isWriteIndented ? ref jsonWriterIndented : ref jsonWriter; + + // Lock the writer + lock (writer) + { + // Try get the JsonSerializerOptions + JsonSerializerOptions jsonOptions = context.Options; + + // Try serialize the JSON Node into JSON string + node.WriteTo(writer, jsonOptions); + + // Write the buffer to string + ReadOnlySpan buffer = jsonBufferWriter.WrittenSpan; + string returnValue = Encoding.UTF8.GetString(buffer); + + // If the serialization accepts \0 char at the end of the return, then return it with \0 + // Otherwise, return as is. + return isIncludeNullEndChar ? returnValue + '\0' : returnValue; + } + } + } } } diff --git a/CollapseLauncher/Classes/Interfaces/Class/JSONSerializerHelperAsync.cs b/CollapseLauncher/Classes/Interfaces/Class/JSONSerializerHelperAsync.cs index 21a728fd2..f902b1f53 100644 --- a/CollapseLauncher/Classes/Interfaces/Class/JSONSerializerHelperAsync.cs +++ b/CollapseLauncher/Classes/Interfaces/Class/JSONSerializerHelperAsync.cs @@ -1,28 +1,38 @@ using System; using System.IO; using System.Text.Json; +using System.Text.Json.Nodes; using System.Text.Json.Serialization; using System.Text.Json.Serialization.Metadata; using System.Threading; using System.Threading.Tasks; +#nullable enable namespace CollapseLauncher { internal static partial class JSONSerializerHelper { -#nullable enable internal static async ValueTask DeserializeAsync(this Stream data, JsonSerializerContext context, CancellationToken token = default, T? defaultType = null) where T : class => await InnerDeserializeStreamAsync(data, context, token, defaultType); internal static async ValueTask DeserializeAsync(this Stream data, JsonSerializerContext context, CancellationToken token = default, T? defaultType = null) where T : struct => await InnerDeserializeStreamAsync(data, context, token, defaultType); + internal static async Task DeserializeAsNodeAsync(this Stream data, CancellationToken token = default) + => await InnerDeserializeStreamAsNodeAsync(data, token); + private static async ValueTask InnerDeserializeStreamAsync(Stream data, JsonSerializerContext context, CancellationToken token, T? defaultType) => // Check if the data cannot be read, then throw !data.CanRead ? throw new NotSupportedException("Stream is not readable! Cannot deserialize the stream to JSON!") : // Try deserialize. If it returns a null, then return the default value (T?)await JsonSerializer.DeserializeAsync(data, typeof(T), context, token) ?? defaultType; + private static async Task InnerDeserializeStreamAsNodeAsync(Stream data, CancellationToken token) => + // Check if the data cannot be read, then throw + !data.CanRead ? throw new NotSupportedException("Stream is not readable! Cannot deserialize the stream to JSON!") : + // Try deserialize to JSON Node + await JsonNode.ParseAsync(data, jsonNodeOptions, jsonDocumentOptions, token); + internal static async ValueTask SerializeAsync(this T? value, Stream targetStream, JsonSerializerContext context, CancellationToken token = default) where T : class => await InnerSerializeStreamAsync(value, targetStream, context, token); @@ -40,6 +50,5 @@ private static async ValueTask InnerSerializeStreamAsync(this T? value, Strea await JsonSerializer.SerializeAsync(targetStream, value, typeInfo, token); } -#nullable restore } } diff --git a/CollapseLauncher/Classes/Interfaces/IGameSettingsValue.cs b/CollapseLauncher/Classes/Interfaces/IGameSettingsValue.cs index c6d9d1d98..2b06b2a87 100644 --- a/CollapseLauncher/Classes/Interfaces/IGameSettingsValue.cs +++ b/CollapseLauncher/Classes/Interfaces/IGameSettingsValue.cs @@ -1,4 +1,7 @@ -using System; +using CollapseLauncher.GameSettings.Base; +using System; +using System.Diagnostics; +using System.Text.Json.Serialization; namespace CollapseLauncher.Interfaces { @@ -7,4 +10,12 @@ internal interface IGameSettingsValue : IEquatable abstract static T Load(); void Save(); } + + internal interface IGameSettingsValueMagic : IGameSettingsValue + { + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + byte[] Magic { get; } + + abstract static T LoadWithMagic(byte[] magic, SettingsGameVersionManager versionManager, JsonSerializerContext context); + } } diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs index b6ba24734..e70775f70 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs @@ -151,8 +151,8 @@ private void InitializeSettings(object sender, RoutedEventArgs e) #if !GSPBYPASSGAMERUNNING Overlay.Visibility = Visibility.Visible; PageContent.Visibility = Visibility.Collapsed; - OverlayTitle.Text = Lang._ZenlessGameSettingsPage.OverlayGameRunningTitle; - OverlaySubtitle.Text = Lang._ZenlessGameSettingsPage.OverlayGameRunningSubtitle; + OverlayTitle.Text = Lang._GameSettingsPage.OverlayGameRunningTitle; + OverlaySubtitle.Text = Lang._GameSettingsPage.OverlayGameRunningSubtitle; #endif } else if (GameInstallationState == GameInstallStateEnum.NotInstalled diff --git a/CollapseLauncher/packages.lock.json b/CollapseLauncher/packages.lock.json index 9038897b6..167d2be56 100644 --- a/CollapseLauncher/packages.lock.json +++ b/CollapseLauncher/packages.lock.json @@ -161,12 +161,6 @@ "Microsoft.WindowsAppSDK": "1.5.240227000" } }, - "Microsoft.NET.ILLink.Tasks": { - "type": "Direct", - "requested": "[8.0.5, )", - "resolved": "8.0.5", - "contentHash": "4ISW1Ndgz86FkIbu5rVlMqhhtojdy5rUlxC/N+9kPh9qbYcvHiCvYbHKzAPVIx9OPYIjT9trXt7JI42Y5Ukq6A==" - }, "Microsoft.NETCore.Platforms": { "type": "Direct", "requested": "[8.0.0-preview.7.23375.6, )", From a20a3a992c0f6428ab9df99c8d39a1999a1ecc36 Mon Sep 17 00:00:00 2001 From: Bagus Nur Listiyono Date: Mon, 8 Jul 2024 18:36:46 +0700 Subject: [PATCH 08/44] Add enums --- .../GameSettings/Zenless/Enums.cs | 80 +++++++++++++++++++ .../Zenless/FileClass/GeneralData.cs | 9 +-- 2 files changed, 83 insertions(+), 6 deletions(-) create mode 100644 CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Enums.cs diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Enums.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Enums.cs new file mode 100644 index 000000000..1c7a1d8e1 --- /dev/null +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Enums.cs @@ -0,0 +1,80 @@ +// ReSharper disable InconsistentNaming +namespace CollapseLauncher.GameSettings.Zenless.Enums; + +enum LanguageText +{ + Unset = -1, + en_us = 1, + zh_cn = 2, + zh_tw = 3, + fr_fr = 4, + de_de = 5, + es_es = 6, + pt_pt = 7, + ru_ru = 8, + ja_jp = 9, + ko_kr = 10, + th_th = 11, + vi_vn = 12, + id_id = 13 +} + +enum LanguageVoice +{ + Unset = -1, + en_us = 1, + zh_cn = 2, + ja_jp = 3, + ko_kr = 4 +} + +enum FpsOption +{ + Lo30, + Hi60, + Unlimited +} + +enum AntiAliasingOption +{ + Off, + TAA, + SMAA +} + +enum QualityOption2 +{ + Low, + High +} + +enum QualityOption3 +{ + Low, + Medium, + High +} + +enum QualityOption4 +{ + Off, + Low, + Medium, + High +} + +enum AudioPlaybackDevice +{ + Headphones, + Speakers, + TV +} + +public static class ServerName +{ + public const string Europe = "prod_gf_eu"; + public const string America = "prod_gf_us"; + public const string Asia = "prod_gf_jp"; + public const string TW_HK_MO = "prod_gf_sg"; + // TODO : find sn for CN regions +} diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs index 4d2aee390..bfac40e24 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs @@ -1,4 +1,5 @@ using CollapseLauncher.GameSettings.Base; +using CollapseLauncher.GameSettings.Zenless.Enums; using CollapseLauncher.GameSettings.Zenless.JsonProperties; using CollapseLauncher.Interfaces; using System.Collections.Generic; @@ -29,14 +30,10 @@ internal class GeneralData : MagicNodeBaseValues, IGameSettingsValu public int SelectedServerIndex { get; set; } = 0; [JsonPropertyName("DeviceLanguageType")] - public int DeviceLanguageType { get; set; } = -1; + public LanguageText DeviceLanguageType { get; set; } = LanguageText.Unset; [JsonPropertyName("DeviceLanguageVoiceType")] - public int DeviceLanguageVoiceType - { - get => GetValue(SettingsJsonNode, "DeviceLanguageVoiceType", -1); // Set the default value here - set => SetValue(SettingsJsonNode, "DeviceLanguageVoiceType", value); - } + public LanguageVoice DeviceLanguageVoiceType { get; set; } = LanguageVoice.Unset; [JsonPropertyName("selectServerName")] public string? SelectedServerName From b947fa1454879b8cbbe231aa497f4bc117404394 Mon Sep 17 00:00:00 2001 From: Kemal Setya Adhi Date: Tue, 9 Jul 2024 01:06:23 +0700 Subject: [PATCH 09/44] Implement Node to Value APIs Thank you for forcing us to use JsonNode this time. Fucc you, HoYo! :frick: --- .../BaseClass/MagicNodeBaseValues.cs | 257 ++++++++++++++---- .../Zenless/FileClass/GeneralData.cs | 195 +++++++++++-- .../Zenless/JsonProperties/Properties.cs | 56 +++- 3 files changed, 411 insertions(+), 97 deletions(-) diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs index de914003e..227aaa430 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs @@ -3,13 +3,211 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Runtime.CompilerServices; +using System.Text.Json; using System.Text.Json.Nodes; using System.Text.Json.Serialization; #nullable enable namespace CollapseLauncher.GameSettings.Base { + public enum JsonEnumStoreType + { + AsNumber, + AsString, + AsNumberString + } + + internal static class MagicNodeBaseValuesExt + { + internal static JsonNode EnsureCreated([NotNull] this JsonNode node, string keyName) + where T : JsonNode + { + ArgumentNullException.ThrowIfNull(node, nameof(node)); + + // Try get if the type is an array or object + bool isTryCreateArray = typeof(T) == typeof(JsonArray); + + // Set parent node as object + JsonObject parentNodeObj = node.AsObject(); + JsonNode? valueNode = null; + + // If the value node does not exist, then create and add a new one + if (!(parentNodeObj.TryGetPropertyValue(keyName, out valueNode) && valueNode != null)) + { + // Otherwise, create a new empty one. + JsonNode? jsonValueNode = isTryCreateArray ? + JsonArray.Create(new JsonElement(), new JsonNodeOptions { PropertyNameCaseInsensitive = true }) : + JsonObject.Create(new JsonElement(), new JsonNodeOptions { PropertyNameCaseInsensitive = true }); + valueNode = jsonValueNode; + parentNodeObj.Add(new KeyValuePair(keyName, jsonValueNode)); + } + + // If the value node keeps returning null, SCREW IT!!! + if (valueNode == null) + throw new TypeInitializationException( + nameof(T), + new NullReferenceException( + $"Failed to create the type of {nameof(T)} in the parent node as it is a null!" + )); + + // Return as a reference + return valueNode; + } + + public static string? GetNodeValue(this JsonNode? node, string keyName, string? defaultValue) + { + // Get node as object + JsonObject? jsonObject = node?.AsObject(); + + // If node is null, return the default value + if (jsonObject == null) return defaultValue; + + // Try get node as struct value + if (jsonObject.TryGetPropertyValue(keyName, out JsonNode? jsonNodeValue) && jsonNodeValue != null) + { + string returnValue = jsonNodeValue.AsValue().GetValue(); + return returnValue; + } + + return defaultValue; + } + + public static TValue GetNodeValue(this JsonNode? node, string keyName, TValue defaultValue) + where TValue : struct + { + // Get node as object + JsonObject? jsonObject = node?.AsObject(); + + // If node is null, return the default value + if (jsonObject == null) return defaultValue; + + // Try get node as struct value + if (jsonObject.TryGetPropertyValue(keyName, out JsonNode? jsonNodeValue) && jsonNodeValue != null) + { + TValue returnValue = jsonNodeValue.AsValue().GetValue(); + return returnValue; + } + + return defaultValue; + } + + public static TEnum GetNodeValueEnum(this JsonNode? node, string keyName, TEnum defaultValue) + where TEnum : struct + { + // Get node as object + JsonObject? jsonObject = node?.AsObject(); + + // If node is null, return the default value + if (jsonObject == null) return defaultValue; + + // Try get node as struct value + if (jsonObject.TryGetPropertyValue(keyName, out JsonNode? jsonNodeValue) && jsonNodeValue != null) + { + // Get the JsonValue representative from the node and get the kind/type + JsonValue enumValueRaw = jsonNodeValue.AsValue(); + JsonValueKind enumValueRawKind = enumValueRaw.GetValueKind(); + + // Decide the return value + switch (enumValueRawKind) + { + case JsonValueKind.Number: // If it's a number + int enumAsInt = (int)enumValueRaw; // Cast JsonValue as int + return EnumFromInt(enumAsInt); // Cast and return it as an enum + case JsonValueKind.String: // If it's a string + string? enumAsString = (string?)enumValueRaw; // Cast JsonValue as string + + if (Enum.TryParse(enumAsString, true, out TEnum enumParsedFromString)) // Try parse as a named member + return enumParsedFromString; // If successful, return the returned value + + // If the string is actually a number as a string, then try parse it as int + if (int.TryParse(enumAsString, null, out int enumAsIntFromString)) + return EnumFromInt(enumAsIntFromString); // Cast and return it as an enum + + // Throw if all the attempts were failed + throw new InvalidDataException($"String value: {enumAsString} at key: {keyName} is not a valid member of enum: {nameof(TEnum)}"); + } + } + + TEnum EnumFromInt(int value) => Unsafe.As(ref value); // Unsafe casting from int to TEnum + + // Otherwise, return the default value instead + return defaultValue; + } + + public static void SetNodeValue(this JsonNode? node, string keyName, TValue value) + { + // If node is null, return and ignore + if (node == null) return; + + // Get node as object + JsonObject? jsonObject = node.AsObject(); + + // If node is null, return and ignore + if (jsonObject == null) return; + + // Create an instance of the JSON node value + JsonValue? jsonValue = JsonValue.Create(value); + + // If the node has object, then assign the new value + if (jsonObject.ContainsKey(keyName)) + node[keyName] = jsonValue; + // Otherwise, add it + else + jsonObject.Add(new KeyValuePair(keyName, jsonValue)); + } + + public static void SetNodeValueEnum(this JsonNode? node, string keyName, TEnum value, JsonEnumStoreType enumStoreType = JsonEnumStoreType.AsNumber) + where TEnum : struct, Enum + { + // If node is null, return and ignore + if (node == null) return; + + // Get node as object + JsonObject? jsonObject = node.AsObject(); + + // If node is null, return and ignore + if (jsonObject == null) return; + + // Create an instance of the JSON node value + JsonValue? jsonValue = enumStoreType switch + { + JsonEnumStoreType.AsNumber => AsEnumNumber(value), + JsonEnumStoreType.AsString => AsEnumString(value), + JsonEnumStoreType.AsNumberString => AsEnumNumberString(value), + _ => throw new NotSupportedException($"Enum store type: {enumStoreType} is not supported!") + }; + + // If the node has object, then assign the new value + if (jsonObject.ContainsKey(keyName)) + node[keyName] = jsonValue; + // Otherwise, add it + else + jsonObject.Add(new KeyValuePair(keyName, jsonValue)); + + JsonValue AsEnumNumber(TEnum value) + { + int enumAsNumber = Unsafe.As(ref value); + return JsonValue.Create(enumAsNumber); + } + + JsonValue? AsEnumString(TEnum value) + { + string? enumName = Enum.GetName(value); + return JsonValue.Create(enumName); + } + + JsonValue? AsEnumNumberString(TEnum value) + { + int enumAsNumber = Unsafe.As(ref value); + string enumAsNumberString = $"{enumAsNumber}"; + return JsonValue.Create(enumAsNumberString); + } + } + } + internal class MagicNodeBaseValues where T : MagicNodeBaseValues, new() { @@ -103,64 +301,5 @@ protected virtual void InjectNodeAndMagic(JsonNode? jsonNode, byte[] magic, Sett Magic = magic; Context = context; } - - protected virtual string GetValue(JsonNode? node, string keyName, string defaultValue) - { - // Get node as object - JsonObject? jsonObject = node?.AsObject(); - - // If node is null, return the default value - if (jsonObject == null) return defaultValue; - - // Try get node as struct value - if (jsonObject.TryGetPropertyValue(keyName, out JsonNode? jsonNodeValue) && jsonNodeValue != null) - { - string returnValue = jsonNodeValue.AsValue().GetValue(); - return returnValue; - } - - return defaultValue; - } - - protected virtual TValue GetValue(JsonNode? node, string keyName, TValue defaultValue) - where TValue : struct - { - // Get node as object - JsonObject? jsonObject = node?.AsObject(); - - // If node is null, return the default value - if (jsonObject == null) return defaultValue; - - // Try get node as struct value - if (jsonObject.TryGetPropertyValue(keyName, out JsonNode? jsonNodeValue) && jsonNodeValue != null) - { - TValue returnValue = jsonNodeValue.AsValue().GetValue(); - return returnValue; - } - - return defaultValue; - } - - protected virtual void SetValue(JsonNode? node, string keyName, TValue value) - { - // If node is null, return and ignore - if (node == null) return; - - // Get node as object - JsonObject? jsonObject = node.AsObject(); - - // If node is null, return and ignore - if (jsonObject == null) return; - - // Create an instance of the JSON node value - JsonValue? jsonValue = JsonValue.Create(value); - - // If the node has object, then assign the new value - if (jsonObject.ContainsKey(keyName)) - node[keyName] = jsonValue; - // Otherwise, add it - else - jsonObject.Add(new KeyValuePair(keyName, jsonValue)); - } } } diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs index bfac40e24..df8a0e27e 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs @@ -2,71 +2,208 @@ using CollapseLauncher.GameSettings.Zenless.Enums; using CollapseLauncher.GameSettings.Zenless.JsonProperties; using CollapseLauncher.Interfaces; -using System.Collections.Generic; +using System.Text.Json.Nodes; using System.Text.Json.Serialization; +#nullable enable namespace CollapseLauncher.GameSettings.Zenless; internal class GeneralData : MagicNodeBaseValues, IGameSettingsValueMagic { - #region Fields -#nullable enable + #region Node Based Properties + private JsonNode? _systemSettingDataMap; + private JsonNode? _keyboardBindingMap; + private JsonNode? _mouseBindingMap; + private JsonNode? _gamepadBindingMap; + + [JsonPropertyName("SystemSettingDataMap")] + [JsonIgnore] // We ignore this one from getting serialized to default JSON value + public JsonNode? SystemSettingDataMap + { + // Cache the SystemSettingDataMap inside of the parent SettingsJsonNode + // and ensure that the node for SystemSettingDataMap exists. If not exist, + // create a new one (via EnsureCreated()). + get => _systemSettingDataMap ??= SettingsJsonNode?.EnsureCreated("SystemSettingDataMap"); + } + + [JsonPropertyName("KeyboardBindingMap")] + [JsonIgnore] // We ignore this one from getting serialized to default JSON value + public JsonNode? KeyboardBindingMap + { + // Cache the KeyboardBindingMap inside of the parent SettingsJsonNode + // and ensure that the node for KeyboardBindingMap exists. If not exist, + // create a new one (via EnsureCreated()). + get => _keyboardBindingMap ??= SettingsJsonNode?.EnsureCreated("KeyboardBindingMap"); + } + + [JsonPropertyName("MouseBindingMap")] + [JsonIgnore] // We ignore this one from getting serialized to default JSON value + public JsonNode? MouseBindingMap + { + // Cache the MouseBindingMap inside of the parent SettingsJsonNode + // and ensure that the node for MouseBindingMap exists. If not exist, + // create a new one (via EnsureCreated()). + get => _mouseBindingMap ??= SettingsJsonNode?.EnsureCreated("MouseBindingMap"); + } + + [JsonPropertyName("GamepadBindingMap")] + [JsonIgnore] // We ignore this one from getting serialized to default JSON value + public JsonNode? GamepadBindingMap + { + // Cache the GamepadBindingMap inside of the parent SettingsJsonNode + // and ensure that the node for GamepadBindingMap exists. If not exist, + // create a new one (via EnsureCreated()). + get => _gamepadBindingMap ??= SettingsJsonNode?.EnsureCreated("GamepadBindingMap"); + } #endregion #region Properties [JsonPropertyName("$Type")] - public string? TypeString { get; set; } = "MoleMole.GeneralLocalDataItem"; + public string? TypeString + { + get => SettingsJsonNode.GetNodeValue("$Type", "MoleMole.GeneralLocalDataItem"); + set => SettingsJsonNode.SetNodeValue("$Type", value); + } [JsonPropertyName("deviceUUID")] - public string? DeviceUUID { get; set; } + public string? DeviceUUID + { + get => SettingsJsonNode.GetNodeValue("deviceUUID", string.Empty); + set => SettingsJsonNode.SetNodeValue("deviceUUID", value); + } [JsonPropertyName("userLocalDataVersionId")] - public string? UserLocalDataVersionId { get; set; } = "0.0.1"; + public string? UserLocalDataVersionId + { + get => SettingsJsonNode.GetNodeValue("userLocalDataVersionId", "0.0.1"); + set => SettingsJsonNode.SetNodeValue("userLocalDataVersionId", value); + } [JsonPropertyName("curAccountName")] - public string? CurrentAccountName { get; set; } + public string? CurrentAccountName + { + get => SettingsJsonNode.GetNodeValue("curAccountName", string.Empty); + set => SettingsJsonNode.SetNodeValue("curAccountName", value); + } [JsonPropertyName("selectedServerIndex")] - public int SelectedServerIndex { get; set; } = 0; + public int SelectedServerIndex + { + get => SettingsJsonNode.GetNodeValue("selectedServerIndex", 0); + set => SettingsJsonNode.SetNodeValue("selectedServerIndex", value); + } [JsonPropertyName("DeviceLanguageType")] - public LanguageText DeviceLanguageType { get; set; } = LanguageText.Unset; + public LanguageText DeviceLanguageType + { + get => SettingsJsonNode.GetNodeValueEnum("DeviceLanguageType", LanguageText.Unset); + set => SettingsJsonNode.SetNodeValueEnum("DeviceLanguageType", value, JsonEnumStoreType.AsNumber); + } [JsonPropertyName("DeviceLanguageVoiceType")] - public LanguageVoice DeviceLanguageVoiceType { get; set; } = LanguageVoice.Unset; + public LanguageVoice DeviceLanguageVoiceType + { + get => SettingsJsonNode.GetNodeValueEnum("DeviceLanguageVoiceType", LanguageVoice.Unset); + set => SettingsJsonNode.SetNodeValueEnum("DeviceLanguageVoiceType", value, JsonEnumStoreType.AsNumber); + } [JsonPropertyName("selectServerName")] public string? SelectedServerName { - get => GetValue(SettingsJsonNode, "selectServerName", "prod_gf_jp"); // Set the default value here - set => SetValue(SettingsJsonNode, "selectServerName", value); + get => SettingsJsonNode.GetNodeValue("selectServerName", "prod_gf_jp"); + set => SettingsJsonNode.SetNodeValue("selectServerName", value); } - [JsonPropertyName("SystemSettingDataMap")] - public Dictionary SystemSettingDataMap { get; set; } = new Dictionary(); - - [JsonPropertyName("KeyboardBindingMap")] - public Dictionary KeyboardBindingMap { get; set; } = new Dictionary(); - - [JsonPropertyName("MouseBindingMap")] - public Dictionary MouseBindingMap { get; set; } = new Dictionary(); - - [JsonPropertyName("GamepadBindingMap")] - public Dictionary GamepadBindingMap { get; set; } = new Dictionary(); - [JsonPropertyName("HDRSettingRecordState")] - public int HDRSettingRecordState { get; set; } = 0; + public int HDRSettingRecordState + { + get => SettingsJsonNode.GetNodeValue("HDRSettingRecordState", 0); + set => SettingsJsonNode.SetNodeValue("HDRSettingRecordState", value); + } [JsonPropertyName("HDRMaxLuminosityLevel")] - public int HDRMaxLuminosityLevel { get; set; } = -1; + public int HDRMaxLuminosityLevel + { + get => SettingsJsonNode.GetNodeValue("HDRMaxLuminosityLevel", -1); + set => SettingsJsonNode.SetNodeValue("HDRMaxLuminosityLevel", value); + } [JsonPropertyName("HDRUIPaperWhiteLevel")] - public int HDRUIPaperWhiteLevel { get; set; } = -1; + public int HDRUIPaperWhiteLevel + { + get => SettingsJsonNode.GetNodeValue("HDRUIPaperWhiteLevel", -1); + set => SettingsJsonNode.SetNodeValue("HDRUIPaperWhiteLevel", value); + } [JsonPropertyName("LastVHSStoreOpenTime")] - public string? LastVHSStoreOpenTime { get; set; } = "01/01/0001 00:00:00"; + public string? LastVHSStoreOpenTime + { + get => SettingsJsonNode.GetNodeValue("LastVHSStoreOpenTime", "01/01/0001 00:00:00"); + set => SettingsJsonNode.SetNodeValue("LastVHSStoreOpenTime", value); + } [JsonPropertyName("DisableBattleUIOptimization")] - public bool DisableBattleUIOptimization { get; set; } = false; + public bool DisableBattleUIOptimization + { + get => SettingsJsonNode.GetNodeValue("DisableBattleUIOptimization", false); + set => SettingsJsonNode.SetNodeValue("DisableBattleUIOptimization", value); + } + #endregion + + #region Single Node Dependent Properties + private SystemSettingLocalData? _fpsData; + + [JsonIgnore] + public FpsOption Fps + { + // Initialize the field under _fpsData as SystemSettingLocalData + get => (_fpsData.HasValue ? _fpsData : _fpsData = SystemSettingDataMap! + .AsSystemSettingLocalData("110", FpsOption.Hi60)).Value.GetDataEnum(); + set => _fpsData?.SetDataEnum(value); + } + + private SystemSettingLocalData? _vSyncData; + + [JsonIgnore] + public bool VSync + { + // Initialize the field under _vSyncData as SystemSettingLocalData + get => (_vSyncData.HasValue ? _vSyncData : _vSyncData = SystemSettingDataMap! + .AsSystemSettingLocalData("8")).Value.GetData(); + set => _vSyncData?.SetData(value); + } + + private SystemSettingLocalData? _renderResolutionData; + + [JsonIgnore] + public QualityOption3 RenderResolution + { + // Initialize the field under _renderResolutionData as SystemSettingLocalData + get => (_renderResolutionData.HasValue ? _renderResolutionData : _renderResolutionData = SystemSettingDataMap! + .AsSystemSettingLocalData("9", QualityOption3.Medium)).Value.GetDataEnum(); + set => _renderResolutionData?.SetDataEnum(value); + } + + private SystemSettingLocalData? _antiAliasingData; + + [JsonIgnore] + public AntiAliasingOption AntiAliasing + { + // Initialize the field under _antiAliasingData as SystemSettingLocalData + get => (_antiAliasingData.HasValue ? _antiAliasingData : _antiAliasingData = SystemSettingDataMap! + .AsSystemSettingLocalData("12")).Value.GetDataEnum(); + set => _antiAliasingData?.SetDataEnum(value); + } + + private SystemSettingLocalData? _shadowQualityData; + + [JsonIgnore] + public AntiAliasingOption ShadowQuality + { + // Initialize the field under _shadowQualityData as SystemSettingLocalData + get => (_shadowQualityData.HasValue ? _shadowQualityData : _shadowQualityData = SystemSettingDataMap! + .AsSystemSettingLocalData("12")).Value.GetDataEnum(); + set => _shadowQualityData?.SetDataEnum(value); + } #endregion } diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/JsonProperties/Properties.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/JsonProperties/Properties.cs index 2f22e72bf..26dd95ffb 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/JsonProperties/Properties.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/JsonProperties/Properties.cs @@ -1,15 +1,53 @@ -using System.Text.Json.Serialization; +using CollapseLauncher.GameSettings.Base; +using System; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Nodes; +#nullable enable namespace CollapseLauncher.GameSettings.Zenless.JsonProperties; -public class SystemSettingDataMap +public struct SystemSettingLocalData + where TData : struct { - [JsonPropertyName("$Type")] - public string TypeString { get; set; } = "MoleMole.SystemSettingLocalData"; + private const string TypeKey = "MoleMole.SystemSettingLocalData"; - [JsonPropertyName("Version")] - public int Version { get; set; } = 1; + private readonly int _defaultVersion; + private readonly TData _defaultData; + private readonly JsonNode _node; - [JsonPropertyName("Data")] - public int Data { get; set; } = 0; -} \ No newline at end of file + public int GetVersion() => _node.GetNodeValue("Version", _defaultVersion); + public void SetVersion(int value) => _node.SetNodeValue("Version", value); + + public TData GetData() => _node.GetNodeValue("Data", _defaultData); + public TData GetDataEnum() => _node.GetNodeValueEnum("Data", _defaultData); + + public void SetData(TData value) => _node.SetNodeValue("Data", value); + public void SetDataEnum(TDataEnum value, JsonEnumStoreType enumStoreType = JsonEnumStoreType.AsNumber) + where TDataEnum : struct, Enum => _node.SetNodeValueEnum("Data", value, enumStoreType); + + public SystemSettingLocalData([NotNull] JsonNode node, TData defaultData = default, int defaultVersion = 1) + { + ArgumentNullException.ThrowIfNull(node, nameof(node)); + _node = node; + _defaultVersion = defaultVersion; + _defaultData = defaultData; + + string? keyVal = node.GetNodeValue("$Type", ""); + if (!(keyVal?.Equals(TypeKey, StringComparison.OrdinalIgnoreCase) ?? false)) + node.SetNodeValue("$Type", TypeKey); + } +} + +public static class SystemSettingLocalDataExt +{ + public static SystemSettingLocalData AsSystemSettingLocalData( + [NotNull] this JsonNode node, string keyName, TData defaultData = default, int defaultVersion = 1) + where TData : struct + { + ArgumentNullException.ThrowIfNull(node, nameof(node)); + + JsonNode ensuredNode = node.EnsureCreated(keyName); + SystemSettingLocalData map = new SystemSettingLocalData(ensuredNode, defaultData, defaultVersion); + return map; + } +} From d048271390e6184c3e25f335770a96f08a85a94d Mon Sep 17 00:00:00 2001 From: Bagus Nur Listiyono Date: Tue, 9 Jul 2024 06:35:56 +0700 Subject: [PATCH 10/44] Fix bool casting --- .../GameSettings/BaseClass/MagicNodeBaseValues.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs index 227aaa430..57ac15f9b 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs @@ -87,8 +87,17 @@ public static TValue GetNodeValue(this JsonNode? node, string keyName, T // Try get node as struct value if (jsonObject.TryGetPropertyValue(keyName, out JsonNode? jsonNodeValue) && jsonNodeValue != null) { - TValue returnValue = jsonNodeValue.AsValue().GetValue(); - return returnValue; + if (typeof(TValue) == typeof(bool) && jsonNodeValue.GetValueKind() == JsonValueKind.Number) + { + // Assuming 0 is false, and any non-zero number is true + int numValue = jsonNodeValue.AsValue().GetValue(); + bool boolValue = numValue != 0; + return (TValue)(object)boolValue; // Cast bool to TValue + } + else + { + return jsonNodeValue.AsValue().GetValue(); + } } return defaultValue; From 93c46f046bfb3919a78c42117534dc86b4c01c81 Mon Sep 17 00:00:00 2001 From: Bagus Nur Listiyono Date: Tue, 9 Jul 2024 06:37:40 +0700 Subject: [PATCH 11/44] Add more properties to GeneralData Also suppress interface warning by implementing Load() and LoadWithMagic() but cast the method from base --- .../GameSettings/Zenless/Enums.cs | 16 +-- .../Zenless/FileClass/GeneralData.cs | 102 +++++++++++++++++- 2 files changed, 107 insertions(+), 11 deletions(-) diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Enums.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Enums.cs index 1c7a1d8e1..7ac9fc64a 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Enums.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Enums.cs @@ -1,7 +1,7 @@ // ReSharper disable InconsistentNaming namespace CollapseLauncher.GameSettings.Zenless.Enums; -enum LanguageText +public enum LanguageText { Unset = -1, en_us = 1, @@ -19,7 +19,7 @@ enum LanguageText id_id = 13 } -enum LanguageVoice +public enum LanguageVoice { Unset = -1, en_us = 1, @@ -28,34 +28,34 @@ enum LanguageVoice ko_kr = 4 } -enum FpsOption +public enum FpsOption { Lo30, Hi60, Unlimited } -enum AntiAliasingOption +public enum AntiAliasingOption { Off, TAA, SMAA } -enum QualityOption2 +public enum QualityOption2 { Low, High } -enum QualityOption3 +public enum QualityOption3 { Low, Medium, High } -enum QualityOption4 +public enum QualityOption4 { Off, Low, @@ -63,7 +63,7 @@ enum QualityOption4 High } -enum AudioPlaybackDevice +public enum AudioPlaybackDevice { Headphones, Speakers, diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs index df8a0e27e..2dd0aeaa7 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs @@ -2,6 +2,8 @@ using CollapseLauncher.GameSettings.Zenless.Enums; using CollapseLauncher.GameSettings.Zenless.JsonProperties; using CollapseLauncher.Interfaces; +using Hi3Helper; +using System; using System.Text.Json.Nodes; using System.Text.Json.Serialization; @@ -151,6 +153,9 @@ public bool DisableBattleUIOptimization #endregion #region Single Node Dependent Properties + + #region Graphics + private SystemSettingLocalData? _fpsData; [JsonIgnore] @@ -195,15 +200,106 @@ public AntiAliasingOption AntiAliasing set => _antiAliasingData?.SetDataEnum(value); } - private SystemSettingLocalData? _shadowQualityData; + private SystemSettingLocalData? _shadowQualityData; [JsonIgnore] - public AntiAliasingOption ShadowQuality + public QualityOption3 ShadowQuality { // Initialize the field under _shadowQualityData as SystemSettingLocalData get => (_shadowQualityData.HasValue ? _shadowQualityData : _shadowQualityData = SystemSettingDataMap! - .AsSystemSettingLocalData("12")).Value.GetDataEnum(); + .AsSystemSettingLocalData("12")).Value.GetDataEnum(); set => _shadowQualityData?.SetDataEnum(value); } + + private SystemSettingLocalData? _fxQualityData; + + [JsonIgnore] + public QualityOption3 FxQuality + { + get => (_fxQualityData.HasValue + ? _fxQualityData + : _fxQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("16")).Value + .GetDataEnum(); + set => _fxQualityData?.SetDataEnum(value); + } + + private SystemSettingLocalData? _colorQualityData; + + [JsonIgnore] + public QualityOption3 ColorQuality + { + get => (_colorQualityData.HasValue + ? _colorQualityData + : _colorQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("108")).Value + .GetDataEnum(); + set => _colorQualityData?.SetDataEnum(value); + } + + private SystemSettingLocalData? _charQualityData; + + [JsonIgnore] + public QualityOption2 CharacterQuality + { + get => (_charQualityData.HasValue + ? _charQualityData + : _charQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("99")).Value + .GetDataEnum(); + set => _charQualityData?.SetDataEnum(value); + } + + private SystemSettingLocalData? _envQualityData; + + [JsonIgnore] + public QualityOption2 EnvironmentQuality + { + get => (_envQualityData.HasValue + ? _envQualityData + : _envQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("109")).Value + .GetDataEnum(); + set => _envQualityData?.SetDataEnum(value); + } + + private SystemSettingLocalData? _reflQualityData; + + [JsonIgnore] + public QualityOption4 ReflectionQuality + { + get => (_reflQualityData.HasValue + ? _reflQualityData + : _reflQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("15")).Value + .GetDataEnum(); + set => _reflQualityData?.SetDataEnum(value); + } + + + + #endregion #endregion + + [Obsolete("Loading settings with Load() is not supported for IGameSettingsValueMagic member. Use LoadWithMagic() instead!", true)] + public new static GeneralData Load() => throw new NotSupportedException("Loading settings with Load() is not supported for IGameSettingsValueMagic member. Use LoadWithMagic() instead!"); + + public new static GeneralData LoadWithMagic(byte[] magic, SettingsGameVersionManager versionManager, + JsonSerializerContext context) + { + var returnVal = Base.MagicNodeBaseValues.LoadWithMagic(magic, versionManager, context); + + #if DEBUG + const bool isPrintDebug = true; + if (isPrintDebug) + { + Logger.LogWriteLine($"Zenless GeneralData parsed value:\r\n\t" + + $"FPS : {returnVal.Fps}\r\n\t" + + $"VSync : {returnVal.VSync}\r\n\t" + + $"RenRes: {returnVal.RenderResolution}\r\n\t" + + $"AA : {returnVal.AntiAliasing}\r\n\t" + + $"Shadow: {returnVal.ShadowQuality}\r\n\t" + + $"CharQ : {returnVal.CharacterQuality}\r\n\t" + + $"RelfQ : {returnVal.ReflectionQuality}\r\n\t", + LogType.Debug, true); + } + #endif + + return returnVal; + } } From 460f61b1b5e52abbf16df0ecc74f93b80470c4b0 Mon Sep 17 00:00:00 2001 From: Bagus Nur Listiyono Date: Tue, 9 Jul 2024 06:45:28 +0700 Subject: [PATCH 12/44] forgor to set default values also add render res enum --- .../GameSettings/Zenless/Enums.cs | 7 ++ .../Zenless/FileClass/GeneralData.cs | 104 ++++++++++++------ 2 files changed, 77 insertions(+), 34 deletions(-) diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Enums.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Enums.cs index 7ac9fc64a..00ef1a7f7 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Enums.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Enums.cs @@ -35,6 +35,13 @@ public enum FpsOption Unlimited } +public enum RenderResOption +{ + f08, + f10, + f12 +} + public enum AntiAliasingOption { Off, diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs index 2dd0aeaa7..818492ff1 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs @@ -173,20 +173,30 @@ public FpsOption Fps public bool VSync { // Initialize the field under _vSyncData as SystemSettingLocalData - get => (_vSyncData.HasValue ? _vSyncData : _vSyncData = SystemSettingDataMap! - .AsSystemSettingLocalData("8")).Value.GetData(); - set => _vSyncData?.SetData(value); + get => + (_vSyncData.HasValue + ? _vSyncData + : _vSyncData = SystemSettingDataMap! + .AsSystemSettingLocalData("8")).Value.GetData(); + set => + _vSyncData?.SetData(value); } - private SystemSettingLocalData? _renderResolutionData; + private SystemSettingLocalData? _renderResolutionData; [JsonIgnore] - public QualityOption3 RenderResolution + public RenderResOption RenderResolution { // Initialize the field under _renderResolutionData as SystemSettingLocalData - get => (_renderResolutionData.HasValue ? _renderResolutionData : _renderResolutionData = SystemSettingDataMap! - .AsSystemSettingLocalData("9", QualityOption3.Medium)).Value.GetDataEnum(); - set => _renderResolutionData?.SetDataEnum(value); + get => + (_renderResolutionData.HasValue + ? _renderResolutionData + : _renderResolutionData = SystemSettingDataMap! + .AsSystemSettingLocalData("9", + RenderResOption.f10)).Value + .GetDataEnum(); + set => + _renderResolutionData?.SetDataEnum(value); } private SystemSettingLocalData? _antiAliasingData; @@ -195,9 +205,15 @@ public QualityOption3 RenderResolution public AntiAliasingOption AntiAliasing { // Initialize the field under _antiAliasingData as SystemSettingLocalData - get => (_antiAliasingData.HasValue ? _antiAliasingData : _antiAliasingData = SystemSettingDataMap! - .AsSystemSettingLocalData("12")).Value.GetDataEnum(); - set => _antiAliasingData?.SetDataEnum(value); + get => + (_antiAliasingData.HasValue + ? _antiAliasingData + : _antiAliasingData = SystemSettingDataMap! + .AsSystemSettingLocalData("12", + AntiAliasingOption.TAA)).Value + .GetDataEnum(); + set => + _antiAliasingData?.SetDataEnum(value); } private SystemSettingLocalData? _shadowQualityData; @@ -206,9 +222,14 @@ public AntiAliasingOption AntiAliasing public QualityOption3 ShadowQuality { // Initialize the field under _shadowQualityData as SystemSettingLocalData - get => (_shadowQualityData.HasValue ? _shadowQualityData : _shadowQualityData = SystemSettingDataMap! - .AsSystemSettingLocalData("12")).Value.GetDataEnum(); - set => _shadowQualityData?.SetDataEnum(value); + get => + (_shadowQualityData.HasValue + ? _shadowQualityData + : _shadowQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("12", + QualityOption3.Medium)).Value + .GetDataEnum(); + set => + _shadowQualityData?.SetDataEnum(value); } private SystemSettingLocalData? _fxQualityData; @@ -216,11 +237,14 @@ public QualityOption3 ShadowQuality [JsonIgnore] public QualityOption3 FxQuality { - get => (_fxQualityData.HasValue + get => + (_fxQualityData.HasValue ? _fxQualityData - : _fxQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("16")).Value - .GetDataEnum(); - set => _fxQualityData?.SetDataEnum(value); + : _fxQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("16", + QualityOption3.Medium)).Value + .GetDataEnum(); + set => + _fxQualityData?.SetDataEnum(value); } private SystemSettingLocalData? _colorQualityData; @@ -228,11 +252,14 @@ public QualityOption3 FxQuality [JsonIgnore] public QualityOption3 ColorQuality { - get => (_colorQualityData.HasValue + get => + (_colorQualityData.HasValue ? _colorQualityData - : _colorQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("108")).Value - .GetDataEnum(); - set => _colorQualityData?.SetDataEnum(value); + : _colorQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("108", + QualityOption3.Medium)).Value + .GetDataEnum(); + set => + _colorQualityData?.SetDataEnum(value); } private SystemSettingLocalData? _charQualityData; @@ -240,11 +267,14 @@ public QualityOption3 ColorQuality [JsonIgnore] public QualityOption2 CharacterQuality { - get => (_charQualityData.HasValue + get => + (_charQualityData.HasValue ? _charQualityData - : _charQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("99")).Value - .GetDataEnum(); - set => _charQualityData?.SetDataEnum(value); + : _charQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("99", + QualityOption2.High)).Value + .GetDataEnum(); + set => + _charQualityData?.SetDataEnum(value); } private SystemSettingLocalData? _envQualityData; @@ -252,11 +282,14 @@ public QualityOption2 CharacterQuality [JsonIgnore] public QualityOption2 EnvironmentQuality { - get => (_envQualityData.HasValue + get => + (_envQualityData.HasValue ? _envQualityData - : _envQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("109")).Value - .GetDataEnum(); - set => _envQualityData?.SetDataEnum(value); + : _envQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("109", + QualityOption2.High)).Value + .GetDataEnum(); + set => + _envQualityData?.SetDataEnum(value); } private SystemSettingLocalData? _reflQualityData; @@ -264,11 +297,14 @@ public QualityOption2 EnvironmentQuality [JsonIgnore] public QualityOption4 ReflectionQuality { - get => (_reflQualityData.HasValue + get => + (_reflQualityData.HasValue ? _reflQualityData - : _reflQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("15")).Value - .GetDataEnum(); - set => _reflQualityData?.SetDataEnum(value); + : _reflQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("15", + QualityOption4.Medium)).Value + .GetDataEnum(); + set => + _reflQualityData?.SetDataEnum(value); } From 9c198c692b707fd366c968811f7f7bd27b004c1a Mon Sep 17 00:00:00 2001 From: Bagus Nur Listiyono Date: Tue, 9 Jul 2024 09:25:14 +0700 Subject: [PATCH 13/44] Rearrange and Finalize GeneralData type - Do manual conversion of int <=> bool - Add :FRICK: tons of the keys - New enum, preset --- .../GameSettings/Zenless/Enums.cs | 7 + .../Zenless/FileClass/GeneralData.cs | 242 ++++++++++++++---- 2 files changed, 205 insertions(+), 44 deletions(-) diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Enums.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Enums.cs index 00ef1a7f7..8349cef3a 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Enums.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Enums.cs @@ -28,6 +28,13 @@ public enum LanguageVoice ko_kr = 4 } +public enum GraphicsPresetOption +{ + High, + Medium, + Low +} + public enum FpsOption { Lo30, diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs index 818492ff1..f8fa2e3f6 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs @@ -155,19 +155,25 @@ public bool DisableBattleUIOptimization #region Single Node Dependent Properties #region Graphics - - private SystemSettingLocalData? _fpsData; + // Key 3 Preset + private SystemSettingLocalData? _graphicsPresData; [JsonIgnore] - public FpsOption Fps + public GraphicsPresetOption GraphicsPreset { - // Initialize the field under _fpsData as SystemSettingLocalData - get => (_fpsData.HasValue ? _fpsData : _fpsData = SystemSettingDataMap! - .AsSystemSettingLocalData("110", FpsOption.Hi60)).Value.GetDataEnum(); - set => _fpsData?.SetDataEnum(value); + get => (_graphicsPresData.HasValue + ? _graphicsPresData + : _graphicsPresData = + SystemSettingDataMap! + .AsSystemSettingLocalData("3", GraphicsPresetOption.Medium)) + .Value.GetDataEnum(); + set => _graphicsPresData?.SetDataEnum(value); } - - private SystemSettingLocalData? _vSyncData; + + // Key 5 Resolution Select + + // Key 8 VSync + private SystemSettingLocalData? _vSyncData; [JsonIgnore] public bool VSync @@ -177,11 +183,12 @@ public bool VSync (_vSyncData.HasValue ? _vSyncData : _vSyncData = SystemSettingDataMap! - .AsSystemSettingLocalData("8")).Value.GetData(); + .AsSystemSettingLocalData("8", 1)).Value.GetData() == 1; set => - _vSyncData?.SetData(value); + _vSyncData?.SetData(value ? 1 : 0); } + // Key 9 Render Resolution private SystemSettingLocalData? _renderResolutionData; [JsonIgnore] @@ -198,7 +205,25 @@ public RenderResOption RenderResolution set => _renderResolutionData?.SetDataEnum(value); } + + // Key 10 Shadow + private SystemSettingLocalData? _shadowQualityData; + [JsonIgnore] + public QualityOption3 ShadowQuality + { + // Initialize the field under _shadowQualityData as SystemSettingLocalData + get => + (_shadowQualityData.HasValue + ? _shadowQualityData + : _shadowQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("10", + QualityOption3.Medium)).Value + .GetDataEnum(); + set => + _shadowQualityData?.SetDataEnum(value); + } + + // Key 12 Anti Aliasing private SystemSettingLocalData? _antiAliasingData; [JsonIgnore] @@ -215,23 +240,51 @@ public AntiAliasingOption AntiAliasing set => _antiAliasingData?.SetDataEnum(value); } + + // Key 13 Volumetric Fog + private SystemSettingLocalData? _volFogQualityData; - private SystemSettingLocalData? _shadowQualityData; + [JsonIgnore] + public QualityOption4 VolumetricFogQuality + { + get => (_volFogQualityData.HasValue + ? _volFogQualityData + : _volFogQualityData = SystemSettingDataMap! + .AsSystemSettingLocalData("13", QualityOption4.Medium)).Value + .GetDataEnum(); + set => _volFogQualityData?.SetDataEnum(value); + } + + // Key 14 Bloom + private SystemSettingLocalData? _bloomData; [JsonIgnore] - public QualityOption3 ShadowQuality + public bool Bloom + { + get => (_bloomData.HasValue + ? _bloomData + : _bloomData = SystemSettingDataMap!.AsSystemSettingLocalData("14", 1)).Value + .GetData() == 1; + set => _bloomData?.SetData(value ? 1 : 0); + } + + // Key 15 Reflection + private SystemSettingLocalData? _reflQualityData; + + [JsonIgnore] + public QualityOption4 ReflectionQuality { - // Initialize the field under _shadowQualityData as SystemSettingLocalData get => - (_shadowQualityData.HasValue - ? _shadowQualityData - : _shadowQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("12", - QualityOption3.Medium)).Value - .GetDataEnum(); + (_reflQualityData.HasValue + ? _reflQualityData + : _reflQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("15", + QualityOption4.Medium)).Value + .GetDataEnum(); set => - _shadowQualityData?.SetDataEnum(value); + _reflQualityData?.SetDataEnum(value); } - + + // Key 16 Effects private SystemSettingLocalData? _fxQualityData; [JsonIgnore] @@ -246,22 +299,20 @@ public QualityOption3 FxQuality set => _fxQualityData?.SetDataEnum(value); } + + // Key 95 Color Filter Effect + private SystemSettingLocalData? _colorFilterData; - private SystemSettingLocalData? _colorQualityData; - - [JsonIgnore] - public QualityOption3 ColorQuality + public int ColorFilter { - get => - (_colorQualityData.HasValue - ? _colorQualityData - : _colorQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("108", - QualityOption3.Medium)).Value - .GetDataEnum(); - set => - _colorQualityData?.SetDataEnum(value); + get => (_colorFilterData.HasValue + ? _colorFilterData + : _colorFilterData = SystemSettingDataMap!.AsSystemSettingLocalData("95", 10)) + .Value.GetData(); + set => _colorFilterData?.SetData(value); } - + + // Key 99 Character Quality private SystemSettingLocalData? _charQualityData; [JsonIgnore] @@ -276,7 +327,37 @@ public QualityOption2 CharacterQuality set => _charQualityData?.SetDataEnum(value); } + + // Key 107 Distortion + private SystemSettingLocalData? _distortionData; + [JsonIgnore] + public bool Distortion + { + get => (_distortionData.HasValue + ? _distortionData + : _distortionData = SystemSettingDataMap!.AsSystemSettingLocalData("107", 1)) + .Value.GetData() == 1; + set => _distortionData?.SetData(value ? 1 : 0); + } + + // Key 108 Shading Quality + private SystemSettingLocalData? _colorQualityData; + + [JsonIgnore] + public QualityOption3 ColorQuality + { + get => + (_colorQualityData.HasValue + ? _colorQualityData + : _colorQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("108", + QualityOption3.Medium)).Value + .GetDataEnum(); + set => + _colorQualityData?.SetDataEnum(value); + } + + // Key 109 Environment Quality private SystemSettingLocalData? _envQualityData; [JsonIgnore] @@ -291,24 +372,97 @@ public QualityOption2 EnvironmentQuality set => _envQualityData?.SetDataEnum(value); } + + // Key 110 FPS + private SystemSettingLocalData? _fpsData; - private SystemSettingLocalData? _reflQualityData; + [JsonIgnore] + public FpsOption Fps + { + // Initialize the field under _fpsData as SystemSettingLocalData + get => (_fpsData.HasValue ? _fpsData : _fpsData = SystemSettingDataMap! + .AsSystemSettingLocalData("110", FpsOption.Hi60)).Value.GetDataEnum(); + set => _fpsData?.SetDataEnum(value); + } + + #endregion + + #region Audio + // Key 31 Main Volume + private SystemSettingLocalData? _mainVolData; [JsonIgnore] - public QualityOption4 ReflectionQuality + public int Audio_MainVolume { - get => - (_reflQualityData.HasValue - ? _reflQualityData - : _reflQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("15", - QualityOption4.Medium)).Value - .GetDataEnum(); - set => - _reflQualityData?.SetDataEnum(value); + get => (_mainVolData.HasValue + ? _mainVolData + : _mainVolData = SystemSettingDataMap!.AsSystemSettingLocalData("31", 10)).Value.GetData(); + set => _mainVolData?.SetData(value); } + // Key 32 Music + private SystemSettingLocalData? _musicVolData; + + [JsonIgnore] + public int Audio_MusicVolume + { + get => (_musicVolData.HasValue + ? _musicVolData + : _musicVolData = SystemSettingDataMap!.AsSystemSettingLocalData("32", 10)).Value.GetData(); + set => _musicVolData?.SetData(value); + } + // Key 33 Dialog + private SystemSettingLocalData? _dialogVolData; + [JsonIgnore] + public int Audio_DialogVolume + { + get => (_dialogVolData.HasValue + ? _dialogVolData + : _dialogVolData = SystemSettingDataMap!.AsSystemSettingLocalData("33", 10)).Value.GetData(); + set => _dialogVolData?.SetData(value); + } + + // Key 34 SFX + private SystemSettingLocalData? _sfxVolData; + + [JsonIgnore] + public int Audio_SfxVolume + { + get => (_sfxVolData.HasValue + ? _sfxVolData + : _sfxVolData = SystemSettingDataMap!.AsSystemSettingLocalData("34", 10)).Value.GetData(); + set => _sfxVolData?.SetData(value); + } + + // Key 10104 Playback Device Type + private SystemSettingLocalData? _playDevData; + + [JsonIgnore] + public AudioPlaybackDevice Audio_PlaybackDevice + { + get => (_playDevData.HasValue + ? _playDevData + : _playDevData = + SystemSettingDataMap!.AsSystemSettingLocalData("10104", + AudioPlaybackDevice.Headphones)).Value + .GetDataEnum(); + set => _playDevData?.SetDataEnum(value); + } + + // Key 10113 Mute on Background + private SystemSettingLocalData? _muteAudOnMinimizeData; + + [JsonIgnore] + public bool Audio_MuteOnMinimize + { + get => (_muteAudOnMinimizeData.HasValue + ? _muteAudOnMinimizeData + : _muteAudOnMinimizeData = SystemSettingDataMap!.AsSystemSettingLocalData("10113", 1)).Value.GetData() == 1; + set => _muteAudOnMinimizeData?.SetData((value ? 1 : 0)); + } + #endregion #endregion From c98931fa3b110e148f02769f0df4dd5b3732752e Mon Sep 17 00:00:00 2001 From: Kemal Setya Adhi Date: Tue, 9 Jul 2024 22:02:58 +0700 Subject: [PATCH 14/44] Ensure the non existed node to use default values + This is useful to avoid NRE error if the new GeneralData() or any MagicNodeBaseValues members are faulty or null. So instead of letting it null, we will instead create a new default instance of it. + This also can be useful to create a new default values of GENERAL_DATA.bin or any MagicNodeBaseValues type files if don't exist. --- .../BaseClass/MagicNodeBaseValues.cs | 51 +++++++++++++++---- .../Zenless/FileClass/GeneralData.cs | 10 ++-- .../Zenless/JsonProperties/Properties.cs | 2 +- 3 files changed, 47 insertions(+), 16 deletions(-) diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs index 57ac15f9b..1aeb94574 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs @@ -22,27 +22,53 @@ public enum JsonEnumStoreType internal static class MagicNodeBaseValuesExt { - internal static JsonNode EnsureCreated([NotNull] this JsonNode node, string keyName) + internal static JsonObject EnsureCreatedObject(this JsonNode? node, string keyName) + { + // If the node is empty, then create a new instance of it + if (node == null) + node = new JsonObject(); + + // Return + return node.EnsureCreatedInner(keyName); + } + + internal static JsonArray EnsureCreatedArray(this JsonNode? node, string keyName) + { + // If the node is empty, then create a new instance of it + if (node == null) + node = new JsonArray(); + + // Return + return node.EnsureCreatedInner(keyName); + } + + private static T EnsureCreatedInner(this JsonNode? node, string keyName) where T : JsonNode { - ArgumentNullException.ThrowIfNull(node, nameof(node)); + // SANITATION: Avoid creation of JsonNode directly + if (typeof(T) == typeof(JsonNode)) + throw new InvalidOperationException("You cannot initialize the parent JsonNode type. Only JsonObject or JsonArray is accepted!"); // Try get if the type is an array or object bool isTryCreateArray = typeof(T) == typeof(JsonArray); // Set parent node as object - JsonObject parentNodeObj = node.AsObject(); + JsonObject? parentNodeObj = node?.AsObject(); JsonNode? valueNode = null; // If the value node does not exist, then create and add a new one - if (!(parentNodeObj.TryGetPropertyValue(keyName, out valueNode) && valueNode != null)) + if (!(parentNodeObj?.TryGetPropertyValue(keyName, out valueNode) ?? false && valueNode != null)) { // Otherwise, create a new empty one. + JsonNodeOptions options = new JsonNodeOptions + { + PropertyNameCaseInsensitive = true + }; JsonNode? jsonValueNode = isTryCreateArray ? - JsonArray.Create(new JsonElement(), new JsonNodeOptions { PropertyNameCaseInsensitive = true }) : - JsonObject.Create(new JsonElement(), new JsonNodeOptions { PropertyNameCaseInsensitive = true }); + new JsonArray(options) : + new JsonObject(options); valueNode = jsonValueNode; - parentNodeObj.Add(new KeyValuePair(keyName, jsonValueNode)); + parentNodeObj?.Add(new KeyValuePair(keyName, jsonValueNode)); } // If the value node keeps returning null, SCREW IT!!! @@ -53,8 +79,8 @@ internal static JsonNode EnsureCreated([NotNull] this JsonNode node, string k $"Failed to create the type of {nameof(T)} in the parent node as it is a null!" )); - // Return as a reference - return valueNode; + // Return object node + return (T)valueNode; } public static string? GetNodeValue(this JsonNode? node, string keyName, string? defaultValue) @@ -272,8 +298,13 @@ public T DefaultValue(byte[] magic, SettingsGameVersionManager versionManager, J // Generate dummy data T data = new T(); + if (data is GeneralData generalData) + { + Console.WriteLine(generalData.AntiAliasing); + } + // Generate raw JSON string - string rawJson = data.Serialize(Context, false, false); + string rawJson = data.Serialize(context, false, false); // Deserialize it back to JSON Node and inject // the node and magic diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs index f8fa2e3f6..2f91dc68c 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs @@ -25,7 +25,7 @@ public JsonNode? SystemSettingDataMap // Cache the SystemSettingDataMap inside of the parent SettingsJsonNode // and ensure that the node for SystemSettingDataMap exists. If not exist, // create a new one (via EnsureCreated()). - get => _systemSettingDataMap ??= SettingsJsonNode?.EnsureCreated("SystemSettingDataMap"); + get => _systemSettingDataMap ??= SettingsJsonNode.EnsureCreatedObject("SystemSettingDataMap"); } [JsonPropertyName("KeyboardBindingMap")] @@ -35,7 +35,7 @@ public JsonNode? KeyboardBindingMap // Cache the KeyboardBindingMap inside of the parent SettingsJsonNode // and ensure that the node for KeyboardBindingMap exists. If not exist, // create a new one (via EnsureCreated()). - get => _keyboardBindingMap ??= SettingsJsonNode?.EnsureCreated("KeyboardBindingMap"); + get => _keyboardBindingMap ??= SettingsJsonNode.EnsureCreatedObject("KeyboardBindingMap"); } [JsonPropertyName("MouseBindingMap")] @@ -45,7 +45,7 @@ public JsonNode? MouseBindingMap // Cache the MouseBindingMap inside of the parent SettingsJsonNode // and ensure that the node for MouseBindingMap exists. If not exist, // create a new one (via EnsureCreated()). - get => _mouseBindingMap ??= SettingsJsonNode?.EnsureCreated("MouseBindingMap"); + get => _mouseBindingMap ??= SettingsJsonNode.EnsureCreatedObject("MouseBindingMap"); } [JsonPropertyName("GamepadBindingMap")] @@ -55,7 +55,7 @@ public JsonNode? GamepadBindingMap // Cache the GamepadBindingMap inside of the parent SettingsJsonNode // and ensure that the node for GamepadBindingMap exists. If not exist, // create a new one (via EnsureCreated()). - get => _gamepadBindingMap ??= SettingsJsonNode?.EnsureCreated("GamepadBindingMap"); + get => _gamepadBindingMap ??= SettingsJsonNode.EnsureCreatedObject("GamepadBindingMap"); } #endregion @@ -472,7 +472,7 @@ public bool Audio_MuteOnMinimize public new static GeneralData LoadWithMagic(byte[] magic, SettingsGameVersionManager versionManager, JsonSerializerContext context) { - var returnVal = Base.MagicNodeBaseValues.LoadWithMagic(magic, versionManager, context); + var returnVal = MagicNodeBaseValues.LoadWithMagic(magic, versionManager, context); #if DEBUG const bool isPrintDebug = true; diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/JsonProperties/Properties.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/JsonProperties/Properties.cs index 26dd95ffb..63951e86c 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/JsonProperties/Properties.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/JsonProperties/Properties.cs @@ -46,7 +46,7 @@ public static SystemSettingLocalData AsSystemSettingLocalData( { ArgumentNullException.ThrowIfNull(node, nameof(node)); - JsonNode ensuredNode = node.EnsureCreated(keyName); + JsonNode ensuredNode = node.EnsureCreatedObject(keyName); SystemSettingLocalData map = new SystemSettingLocalData(ensuredNode, defaultData, defaultVersion); return map; } From 1e89356813560f4342015276c37aa8f389e10e85 Mon Sep 17 00:00:00 2001 From: Kemal Setya Adhi Date: Tue, 9 Jul 2024 22:03:59 +0700 Subject: [PATCH 15/44] Oopsie, forgor to remove test codes --- .../GameSettings/BaseClass/MagicNodeBaseValues.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs index 1aeb94574..810eab2aa 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs @@ -298,11 +298,6 @@ public T DefaultValue(byte[] magic, SettingsGameVersionManager versionManager, J // Generate dummy data T data = new T(); - if (data is GeneralData generalData) - { - Console.WriteLine(generalData.AntiAliasing); - } - // Generate raw JSON string string rawJson = data.Serialize(context, false, false); From cbc1242add520c43d7a83f0ecf6b88548bf7bc62 Mon Sep 17 00:00:00 2001 From: Kemal Setya Adhi Date: Tue, 9 Jul 2024 23:21:31 +0700 Subject: [PATCH 16/44] Fix crash while saving values --- .../GameSettings/BaseClass/MagicNodeBaseValues.cs | 2 +- .../Classes/GameManagement/GameSettings/Zenless/Settings.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs index 810eab2aa..fb491dc45 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.IO; using System.Runtime.CompilerServices; using System.Text.Json; @@ -333,6 +332,7 @@ public bool Equals(GeneralData? other) protected virtual void InjectNodeAndMagic(JsonNode? jsonNode, byte[] magic, SettingsGameVersionManager versionManager, JsonSerializerContext context) { SettingsJsonNode = jsonNode; + GameVersionManager = versionManager; Magic = magic; Context = context; } diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Settings.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Settings.cs index 0cb792af1..5fdd3427e 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Settings.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Settings.cs @@ -65,7 +65,7 @@ public override void SaveSettings() { base.SaveSettings(); SettingsScreen.Save(); - //GeneralData.Save(); + GeneralData.Save(); } public override IGameSettingsUniversal AsIGameSettingsUniversal() => this; From 982367a92ec88234b021c65b566da613391b8341 Mon Sep 17 00:00:00 2001 From: Kemal Setya Adhi Date: Tue, 9 Jul 2024 23:29:48 +0700 Subject: [PATCH 17/44] Fix potential JSON string buffer went blank under Utf8JsonWriter + Fix the bug by flushing the writer before reusing it --- .../Classes/Interfaces/Class/JSONSerializerHelper.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CollapseLauncher/Classes/Interfaces/Class/JSONSerializerHelper.cs b/CollapseLauncher/Classes/Interfaces/Class/JSONSerializerHelper.cs index 3cdcf7a81..7cab23567 100644 --- a/CollapseLauncher/Classes/Interfaces/Class/JSONSerializerHelper.cs +++ b/CollapseLauncher/Classes/Interfaces/Class/JSONSerializerHelper.cs @@ -220,6 +220,9 @@ private static string InnerSerialize(this T? data, JsonSerializerContext cont // Try serialize the type into JSON string JsonSerializer.Serialize(writer, data, typeInfo); + // Flush the writter + writer.Flush(); + // Write the buffer to string ReadOnlySpan buffer = jsonBufferWriter.WrittenSpan; string returnValue = Encoding.UTF8.GetString(buffer); @@ -262,6 +265,9 @@ private static string InnerSerializeJsonNode(this JsonNode? node, JsonSerializer // Try serialize the JSON Node into JSON string node.WriteTo(writer, jsonOptions); + // Flush the writter + writer.Flush(); + // Write the buffer to string ReadOnlySpan buffer = jsonBufferWriter.WrittenSpan; string returnValue = Encoding.UTF8.GetString(buffer); From 0917dcd33e9967cd06c10b0731b016c203037182 Mon Sep 17 00:00:00 2001 From: Bagus Nur Listiyono Date: Tue, 9 Jul 2024 23:56:26 +0700 Subject: [PATCH 18/44] Implement resolution selector WARNING: NOT YET TIED INTO GENERAL DATA --- .../ZenlessGameSettingsPage.Ext.cs | 8 +-- .../ZenlessGameSettingsPage.xaml.cs | 54 ++++++++++++++++++- .../XAMLs/MainApp/Pages/HomePage.xaml.cs | 9 ++++ Hi3Helper.Core/Classes/Data/ScreenData.cs | 14 +++++ 4 files changed, 80 insertions(+), 5 deletions(-) diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs index 69a35cfb6..710818e41 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs @@ -96,10 +96,10 @@ public bool IsCustomResolutionEnabled } } - public bool IsCanExclusiveFullscreen - { - get => !(!IsFullscreenEnabled || IsCustomResolutionEnabled); - } + /// + /// Zenless does not support exclusive fullscreen + /// + public readonly bool IsCanExclusiveFullscreen = false; public bool IsExclusiveFullscreenEnabled { diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs index e70775f70..f6bb34d29 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs @@ -7,6 +7,7 @@ using CollapseLauncher.Interfaces; using CollapseLauncher.Statics; using Hi3Helper; + using Hi3Helper.Screen; using Hi3Helper.Shared.ClassStruct; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Media; @@ -14,6 +15,7 @@ using Microsoft.Win32; using RegistryUtils; using System; + using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Numerics; @@ -144,7 +146,11 @@ private void InitializeSettings(object sender, RoutedEventArgs e) { try { - GameResolutionSelector.ItemsSource = ScreenResolutionsList; + var resList = new List(); + resList.AddRange(GetResPairs_Fullscreen()); + resList.AddRange(GetResPairs_Windowed()); + + GameResolutionSelector.ItemsSource = resList; if (CurrentGameProperty.IsGameRunning) { @@ -179,6 +185,52 @@ private void InitializeSettings(object sender, RoutedEventArgs e) } } + private static readonly List acceptableHeight = [4320, 2880, 2160, 1440, 1280, 1080, 900, 720]; + + private List GetResPairs_Fullscreen() + { + var displayProp = ScreenProp.GetScreenSize(); + var nativeAspRatio = (double)displayProp.Width / displayProp.Height; + + acceptableHeight.RemoveAll(h => h > ScreenProp.GetMaxHeight()); + + List resPairs = new List(); + + foreach (var h in acceptableHeight) + { + int w = (int)(h * nativeAspRatio); + // TODO: remove identifier + resPairs.Add($"{w}x{h} Fullscreen"); + } + + return resPairs; + } + + private List GetResPairs_Windowed() + { + var displayProp = ScreenProp.GetScreenSize(); + var nativeAspRatio = (double)displayProp.Width / displayProp.Height; + var wideRatio = (double)16 / 9; + var ulWideRatio = (double)21 / 9; + + acceptableHeight.RemoveAll(h => h > ScreenProp.GetMaxHeight()); + + List resPairs = new List(); + + // If res is 21:9 then add proper native to the list + if (Math.Abs(nativeAspRatio - ulWideRatio) < 0.01) + resPairs.Add($"{displayProp.Width}x{displayProp.Height}"); + + foreach (var h in acceptableHeight) + { + int w = (int)(h * wideRatio); + // TODO: remove identifier + resPairs.Add($"{w}x{h} Windowed"); + } + + return resPairs; + } + private void ApplyButton_Click(object sender, RoutedEventArgs e) { try diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs index 63f3db045..164607234 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs @@ -1658,6 +1658,15 @@ internal string GetLaunchArguments(IGameSettingsUniversal _Settings) parameter.AppendFormat("-screen-width {0} -screen-height {1} ", screenSize.Width, screenSize.Height); } + if (CurrentGameProperty._GameVersion.GameType == GameNameType.Zenless) + { + // does not support exclusive mode at all + // also doesn't properly support dx12 or dx11 st + + Size screenSize = _Settings.SettingsScreen.sizeRes; + parameter.AppendFormat("-screen-width {0} -screen-height {1} ", screenSize.Width, screenSize.Height); + } + if (_Settings.SettingsCollapseScreen.UseBorderlessScreen) { parameter.Append("-popupwindow "); diff --git a/Hi3Helper.Core/Classes/Data/ScreenData.cs b/Hi3Helper.Core/Classes/Data/ScreenData.cs index d74faf860..8956a1816 100644 --- a/Hi3Helper.Core/Classes/Data/ScreenData.cs +++ b/Hi3Helper.Core/Classes/Data/ScreenData.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Drawing; +using System.Runtime.InteropServices; using static Hi3Helper.InvokeProp; namespace Hi3Helper.Screen @@ -34,5 +35,18 @@ public static void InitScreenResolution() Width = GetSystemMetrics(SystemMetric.SM_CXSCREEN), Height = GetSystemMetrics(SystemMetric.SM_CYSCREEN) }; + + public static int GetMaxHeight() + { + devMode = new DEVMODE(); + int maxHeight = 0; + + for (int i = 0; EnumDisplaySettings(null, i, ref devMode); i++) + { + if (devMode.dmPelsHeight > maxHeight) maxHeight = (int)devMode.dmPelsHeight; + } + + return maxHeight; + } } } From b1a09acb6cd217181fefe6fd0c187752596ee4d8 Mon Sep 17 00:00:00 2001 From: Bagus Nur Listiyono Date: Wed, 10 Jul 2024 00:02:33 +0700 Subject: [PATCH 19/44] Do not modify source acH --- .../ZenlessGameSettingsPage.xaml.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs index f6bb34d29..7daf83144 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs @@ -191,12 +191,13 @@ private List GetResPairs_Fullscreen() { var displayProp = ScreenProp.GetScreenSize(); var nativeAspRatio = (double)displayProp.Width / displayProp.Height; - - acceptableHeight.RemoveAll(h => h > ScreenProp.GetMaxHeight()); + var acH = acceptableHeight; + + acH.RemoveAll(h => h > ScreenProp.GetMaxHeight()); List resPairs = new List(); - foreach (var h in acceptableHeight) + foreach (var h in acH) { int w = (int)(h * nativeAspRatio); // TODO: remove identifier @@ -212,8 +213,9 @@ private List GetResPairs_Windowed() var nativeAspRatio = (double)displayProp.Width / displayProp.Height; var wideRatio = (double)16 / 9; var ulWideRatio = (double)21 / 9; - - acceptableHeight.RemoveAll(h => h > ScreenProp.GetMaxHeight()); + var acH = acceptableHeight; + + acH.RemoveAll(h => h > ScreenProp.GetMaxHeight()); List resPairs = new List(); @@ -221,7 +223,7 @@ private List GetResPairs_Windowed() if (Math.Abs(nativeAspRatio - ulWideRatio) < 0.01) resPairs.Add($"{displayProp.Width}x{displayProp.Height}"); - foreach (var h in acceptableHeight) + foreach (var h in acH) { int w = (int)(h * wideRatio); // TODO: remove identifier From 64bd009aee9060274053d6e60b237c044c9965be Mon Sep 17 00:00:00 2001 From: Kemal Setya Adhi Date: Wed, 10 Jul 2024 22:10:10 +0700 Subject: [PATCH 20/44] Fix log issues + Fix double disposal when cleaning-up log folder + Fix crash upon cleaning-up log folder + Fix the log files don't contain launcher infos + Fix where the launcher have "vUnknown" prefixes + Fix logs do not append to the last line on launcher re-run + Fix Console window sometime spawned for a moment on start-up, even with "Show Console" not enabled --- .../Helper/Update/LauncherUpdateHelper.cs | 8 +- CollapseLauncher/Program.cs | 8 -- .../XAMLs/MainApp/Pages/SettingsPage.xaml.cs | 9 +- Hi3Helper.Core/Classes/Logger/ILog.cs | 10 ++ Hi3Helper.Core/Classes/Logger/LoggerBase.cs | 94 +++++++++++++------ .../Classes/Shared/Region/LauncherConfig.cs | 48 ++++++++-- 6 files changed, 117 insertions(+), 60 deletions(-) diff --git a/CollapseLauncher/Classes/Helper/Update/LauncherUpdateHelper.cs b/CollapseLauncher/Classes/Helper/Update/LauncherUpdateHelper.cs index 985e4c2f1..31785563d 100644 --- a/CollapseLauncher/Classes/Helper/Update/LauncherUpdateHelper.cs +++ b/CollapseLauncher/Classes/Helper/Update/LauncherUpdateHelper.cs @@ -5,7 +5,6 @@ using Squirrel; using Squirrel.Sources; using System; - using System.Reflection; using System.Threading.Tasks; namespace CollapseLauncher.Helper.Update @@ -14,13 +13,12 @@ internal static class LauncherUpdateHelper { static LauncherUpdateHelper() { - Version? version = Assembly.GetExecutingAssembly().GetName().Version; - if (version == null) + string? versionString = LauncherConfig.AppCurrentVersionString; + if (string.IsNullOrEmpty(versionString)) throw new NullReferenceException("App cannot retrieve the current version of the executable!"); - _launcherCurrentVersion = new GameVersion(version); + _launcherCurrentVersion = new GameVersion(versionString); _launcherCurrentVersionString = _launcherCurrentVersion.VersionString; - LoggerBase.CurrentLauncherVersion = _launcherCurrentVersion.VersionString; } internal static AppUpdateVersionProp? AppUpdateVersionProp; diff --git a/CollapseLauncher/Program.cs b/CollapseLauncher/Program.cs index aae2e5342..faf6ecb04 100644 --- a/CollapseLauncher/Program.cs +++ b/CollapseLauncher/Program.cs @@ -89,14 +89,6 @@ public static void Main(params string[] args) switch (m_appMode) { - case AppMode.Launcher: - if (!IsConsoleEnabled) - { - LoggerConsole.DisposeConsole(); - _log = new LoggerNull(logPath, Encoding.UTF8); - } - - break; case AppMode.ElevateUpdater: RunElevateUpdate(); return; diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/SettingsPage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/SettingsPage.xaml.cs index 8b085cbdb..fa7f1d631 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/SettingsPage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/SettingsPage.xaml.cs @@ -227,14 +227,7 @@ private async void ClearLogsFolder(object sender, RoutedEventArgs e) { try { - if (Directory.Exists(AppGameLogsFolder)) - { - _log.Dispose(); - Directory.Delete(AppGameLogsFolder, true); - _log.SetFolderPathAndInitialize(AppGameLogsFolder, Encoding.UTF8); - } - - Directory.CreateDirectory(AppGameLogsFolder); + _log?.ResetLogFiles(AppGameLogsFolder, Encoding.UTF8); (sender as Button).IsEnabled = false; } catch (Exception ex) diff --git a/Hi3Helper.Core/Classes/Logger/ILog.cs b/Hi3Helper.Core/Classes/Logger/ILog.cs index c5f4dda9a..e572bfea6 100644 --- a/Hi3Helper.Core/Classes/Logger/ILog.cs +++ b/Hi3Helper.Core/Classes/Logger/ILog.cs @@ -45,5 +45,15 @@ public interface ILog : IDisposable /// The path of the log folder /// Encoding for the log void SetFolderPathAndInitialize(string folderPath, Encoding logEncoding); + +#nullable enable + /// + /// Reset and clean up all the old log files in the respective folder and + /// reload the log writer. + /// + /// The path of the logs to be cleaned up and reloaded + /// The encoding of the log writer (Default is if null) + void ResetLogFiles(string? reloadToPath, Encoding? encoding = null); +#nullable restore } } diff --git a/Hi3Helper.Core/Classes/Logger/LoggerBase.cs b/Hi3Helper.Core/Classes/Logger/LoggerBase.cs index 9ee751682..ce4fbb814 100644 --- a/Hi3Helper.Core/Classes/Logger/LoggerBase.cs +++ b/Hi3Helper.Core/Classes/Logger/LoggerBase.cs @@ -2,8 +2,6 @@ using System.IO; using System.Text; #if !APPLYUPDATE -using System.Diagnostics; -using System.Linq; using Hi3Helper.Shared.Region; #endif @@ -12,9 +10,9 @@ namespace Hi3Helper public class LoggerBase { #region Properties - public static string CurrentLauncherVersion = "vUnknown"; private FileStream _logStream { get; set; } private StreamWriter _logWriter { get; set; } + private object _lockObject = new object(); private string _logFolder { get; set; } #if !APPLYUPDATE private string _logPath { get; set; } @@ -47,24 +45,66 @@ public void SetFolderPathAndInitialize(string folderPath, Encoding logEncoding) } #endif - // Try dispose the _logWriter even though it's not initialized. - // This will be used if the program need to change the log folder to another location. - _logWriter?.Dispose(); - _logStream?.Dispose(); + lock (_lockObject) + { + // Try dispose the _logWriter even though it's not initialized. + // This will be used if the program need to change the log folder to another location. + DisposeBase(); #if !APPLYUPDATE - try + try + { + // Initialize writer and the path of the log file. + InitializeWriter(false, logEncoding); + } + catch + { + // If the initialization above fails, then use fallback. + InitializeWriter(true, logEncoding); + } +#endif + } + } + +#nullable enable + public void ResetLogFiles(string? reloadToPath, Encoding? encoding = null) + { + lock (_lockObject) { - // Initialize writer and the path of the log file. - InitializeWriter(false, logEncoding); + DisposeBase(); + + if (!string.IsNullOrEmpty(_logFolder) && Directory.Exists(_logFolder)) + DeleteLogFilesInner(_logFolder); + + if (!string.IsNullOrEmpty(reloadToPath) && !Directory.Exists(reloadToPath)) + Directory.CreateDirectory(reloadToPath); + + if (!string.IsNullOrEmpty(reloadToPath)) + _logFolder = reloadToPath; + + encoding ??= Encoding.UTF8; + + SetFolderPathAndInitialize(_logFolder, encoding); } - catch + } + + private void DeleteLogFilesInner(string folderPath) + { + DirectoryInfo dirInfo = new DirectoryInfo(folderPath); + foreach (FileInfo fileInfo in dirInfo.EnumerateFiles("log-*-id*.log", SearchOption.TopDirectoryOnly)) { - // If the initialization above fails, then use fallback. - InitializeWriter(true, logEncoding); + try + { + fileInfo.Delete(); + LogWriteLine($"Removed log file: {fileInfo.FullName}", LogType.Default); + } + catch (Exception ex) + { + LogWriteLine($"Cannot remove log file: {fileInfo.FullName}\r\n{ex}", LogType.Error); + } } -#endif } +#nullable restore #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously public virtual async void LogWriteLine() { } @@ -147,31 +187,23 @@ private void InitializeWriter(bool isFallback, Encoding logEncoding) // Append the build name fallbackString += LauncherConfig.IsPreview ? "-pre" : "-sta"; // Append current app version - fallbackString += CurrentLauncherVersion; + fallbackString += LauncherConfig.AppCurrentVersionString; // Append the current instance number fallbackString += $"-id{GetTotalInstance()}"; _logPath = Path.Combine(_logFolder, $"log-{dateString + fallbackString}.log"); // Initialize _logWriter to the given _logPath. // The FileShare.ReadWrite is still being used to avoid potential conflict if the launcher needs - // to warm-restart itself in rare occassion (like update mechanism with Squirrel). - _logStream = new FileStream(_logPath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite); - _logWriter = new StreamWriter(_logStream, logEncoding, -1, false) { AutoFlush = true }; - } + // to warm-restart itself in rare occasion (like update mechanism with Squirrel). + _logStream = new FileStream(_logPath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite); + // Seek the file to the EOF + _logStream.Seek(0, SeekOrigin.End); - private int GetTotalInstance() - { - // Get this app's process name - string currentProcName = Path.GetFileNameWithoutExtension(LauncherConfig.AppExecutablePath); - - // Get the process count - int procCount = Process.GetProcesses() - .Where(x => x.ProcessName == currentProcName) - .Select(x => x.ProcessName).Count(); - - // If the procCount > 0, then procCount - 1. Else, 0 - return procCount > 0 ? procCount - 1 : 0; + // Initialize the StreamWriter + _logWriter = new StreamWriter(_logStream, logEncoding) { AutoFlush = true }; } + + private int GetTotalInstance() => InvokeProp.EnumerateInstances(); #endif private ArgumentException ThrowInvalidType() => new ArgumentException("Type must be Default, Error, Warning, Scheme, Game, NoTag or Empty!"); diff --git a/Hi3Helper.Core/Classes/Shared/Region/LauncherConfig.cs b/Hi3Helper.Core/Classes/Shared/Region/LauncherConfig.cs index 18e115ea3..3f76b74d6 100644 --- a/Hi3Helper.Core/Classes/Shared/Region/LauncherConfig.cs +++ b/Hi3Helper.Core/Classes/Shared/Region/LauncherConfig.cs @@ -202,20 +202,52 @@ public static string AppGameFolder set => SetAppConfigValue("GameFolder", value); } public static string[] AppCurrentArgument; + private static string _appExecutablePath; public static string AppExecutablePath { get { - string execName = Path.GetFileNameWithoutExtension(Process.GetCurrentProcess().MainModule!.FileName); - string dirPath = AppFolder; - return Path.Combine(dirPath!, execName + ".exe"); + if (string.IsNullOrEmpty(_appExecutablePath)) + { + using Process currentProcess = Process.GetCurrentProcess(); + string execName = Path.GetFileNameWithoutExtension(currentProcess.MainModule?.FileName); + string dirPath = AppFolder; + return _appExecutablePath = Path.Combine(dirPath!, execName + ".exe"); + } + return _appExecutablePath; } } - public static string AppExecutableName { get => Path.GetFileName(AppExecutablePath); } - public static string AppGameImgFolder { get => Path.Combine(AppGameFolder!, "_img"); } - public static string AppGameImgCachedFolder { get => Path.Combine(AppGameImgFolder!, "cached"); } - public static string AppGameLogsFolder { get => Path.Combine(AppGameFolder!, "_logs"); } - + public static string AppExecutableName { get => Path.GetFileName(AppExecutablePath); } + public static string AppGameImgFolder { get => Path.Combine(AppGameFolder!, "_img"); } + public static string AppGameImgCachedFolder { get => Path.Combine(AppGameImgFolder!, "cached"); } + public static string AppGameLogsFolder { get => Path.Combine(AppGameFolder!, "_logs"); } + + private static string _appCurrentVersionString; + public static string AppCurrentVersionString + { + get + { + if (string.IsNullOrEmpty(_appCurrentVersionString)) + { + string executablePath = AppExecutablePath; + if (string.IsNullOrEmpty(executablePath)) + return "Unknown"; + + try + { + FileVersionInfo verInfo = FileVersionInfo.GetVersionInfo(executablePath); + string version = $"{verInfo.FileMajorPart}.{verInfo.FileMinorPart}.{verInfo.FileBuildPart}"; + return _appCurrentVersionString = version; + } + catch + { + return "Unknown"; + } + } + return _appCurrentVersionString; + } + } + public static readonly string AppConfigFile = Path.Combine(AppDataFolder!, "config.ini"); public static readonly string AppNotifIgnoreFile = Path.Combine(AppDataFolder, "ignore_notif_ids.json"); From 18154bfbd619d20f6a8a045aac8743d6921e42ae Mon Sep 17 00:00:00 2001 From: Kemal Setya Adhi Date: Wed, 10 Jul 2024 23:21:20 +0700 Subject: [PATCH 21/44] Implement ResolutionIndex property --- .../Zenless/FileClass/GeneralData.cs | 17 +++++++++++++++-- .../ZenlessGameSettingsPage.xaml.cs | 8 +++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs index 2f91dc68c..7951b364a 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs @@ -169,9 +169,22 @@ public GraphicsPresetOption GraphicsPreset .Value.GetDataEnum(); set => _graphicsPresData?.SetDataEnum(value); } - + // Key 5 Resolution Select - + private SystemSettingLocalData? _resolutionIndexData; + + [JsonIgnore] + public int ResolutionIndex + { + get => (_resolutionIndexData.HasValue + ? _resolutionIndexData + : _resolutionIndexData = + SystemSettingDataMap! + .AsSystemSettingLocalData("5", -1)) + .Value.GetData(); + set => _resolutionIndexData?.SetData(value); + } + // Key 8 VSync private SystemSettingLocalData? _vSyncData; diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs index 7daf83144..3cc436f77 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs @@ -24,8 +24,9 @@ using static Hi3Helper.Logger; using static Hi3Helper.Shared.Region.LauncherConfig; using static CollapseLauncher.Statics.GamePropertyVault; + using System.Linq; - namespace CollapseLauncher.Pages +namespace CollapseLauncher.Pages { [SuppressMessage("ReSharper", "PossibleNullReferenceException")] public partial class ZenlessGameSettingsPage @@ -147,6 +148,11 @@ private void InitializeSettings(object sender, RoutedEventArgs e) try { var resList = new List(); + List resFullscreen = GetResPairs_Fullscreen(); + List resWindowed = GetResPairs_Windowed(); + ScreenResolutionIsFullscreenIdx.AddRange(Enumerable.Range(0, resFullscreen.Count).Select(_ => true)); + ScreenResolutionIsFullscreenIdx.AddRange(Enumerable.Range(0, resWindowed.Count).Select(_ => false)); + resList.AddRange(GetResPairs_Fullscreen()); resList.AddRange(GetResPairs_Windowed()); From e75b384b4f02ced9f8976ab72d4d62ab34d24f6a Mon Sep 17 00:00:00 2001 From: Kemal Setya Adhi Date: Thu, 11 Jul 2024 00:04:47 +0700 Subject: [PATCH 22/44] Properly switch values based on fullscreen state Also adding VSync option --- .../ZenlessGameSettingsPage.Ext.cs | 35 ++- .../ZenlessGameSettingsPage.xaml | 254 +++++++++--------- 2 files changed, 154 insertions(+), 135 deletions(-) diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs index 710818e41..2472b9760 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs @@ -1,9 +1,7 @@ -using CollapseLauncher.GameSettings.Zenless; -using Hi3Helper; -using Hi3Helper.Screen; +using Hi3Helper.Screen; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; -using System; +using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Drawing; @@ -25,6 +23,8 @@ private void OnPropertyChanged([CallerMemberName] string propertyName = null) #endregion #region GameResolution + private List ScreenResolutionIsFullscreenIdx = new List(); + public bool IsFullscreenEnabled { get => Settings.SettingsScreen.isfullScreen; @@ -185,6 +185,25 @@ public string ResolutionSelected } set => Settings.SettingsScreen.sizeResString = value; } + + public int ResolutionIndexSelected + { + get + { + int res = Settings.GeneralData?.ResolutionIndex ?? -1; + bool isFullscreen = res >= 0 && (res + 1) < ScreenResolutionIsFullscreenIdx.Count ? ScreenResolutionIsFullscreenIdx[res] : false; + IsFullscreenEnabled = isFullscreen; + + return res; + } + set + { + if (value < 0) return; + Settings.GeneralData.ResolutionIndex = value; + bool isFullscreen = value >= 0 && (value + 1) < ScreenResolutionIsFullscreenIdx.Count ? ScreenResolutionIsFullscreenIdx[value] : false; + IsFullscreenEnabled = isFullscreen; + } + } #endregion #region Misc @@ -308,5 +327,13 @@ private void GameLaunchDelay_OnValueChanged(NumberBox sender, NumberBoxValueChan sender.Value = 0; } #endregion + + #region Graphics Settings - GENERAL_DATA > SystemSettingDataMap + public bool EnableVSync + { + get => Settings.GeneralData?.VSync ?? false; + set => Settings.GeneralData.VSync = value; + } + #endregion } } diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml index c2a29b00d..a5a69ab1d 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml +++ b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml @@ -28,7 +28,7 @@ TextWrapping="Wrap" /> - + - - - - - - - - - - - + + + + IsChecked="{x:Bind IsFullscreenEnabled, Mode=TwoWay}" + Visibility="Collapsed"> @@ -117,9 +113,39 @@ - - + + + + + + + + + + - - - - - - - - - - + SelectedIndex="{x:Bind ResolutionIndexSelected, Mode=TwoWay}" /> + Grid.Column="1" + Margin="0,16"> + Style="{ThemeResource SubtitleTextBlockStyle}" + Text="{x:Bind helper:Locale.Lang._GameSettingsPage.Graphics_SpecPanel}" + TextWrapping="Wrap" /> + Style="{ThemeResource TitleLargeTextBlockStyle}" + Text="{x:Bind helper:Locale.Lang._GameSettingsPage.CustomArgs_Title}" /> + Style="{ThemeResource SubtitleTextBlockStyle}" + Text="{x:Bind helper:Locale.Lang._GameSettingsPage.CustomArgs_Subtitle}" /> + IsOn="{x:Bind IsUseCustomArgs, Mode=TwoWay}" + OffContent="{x:Bind helper:Locale.Lang._Misc.Disabled}" + OnContent="{x:Bind helper:Locale.Lang._Misc.Enabled}" /> + Margin="0,0,0,16" + HorizontalAlignment="Stretch" + CornerRadius="8,8,0,0" + Text="{x:Bind CustomArgsValue, Mode=TwoWay}" /> - - - - - + TextWrapping="WrapWholeWords"> + + + + + @@ -260,76 +252,76 @@ + Margin="0,0,8,0" + VerticalAlignment="Stretch" + Style="{ThemeResource TitleLargeTextBlockStyle}" + Text="{x:Bind helper:Locale.Lang._GameSettingsPage.Advanced_Title}" /> + Margin="8,12,0,8" + VerticalAlignment="Stretch" + VerticalContentAlignment="Stretch" + FontSize="26" + FontWeight="SemiBold" + IsOn="{x:Bind IsUseAdvancedSettings, Mode=TwoWay}" + OffContent="{x:Bind helper:Locale.Lang._Misc.Disabled}" + OnContent="{x:Bind helper:Locale.Lang._Misc.Enabled}" /> + Visibility="Collapsed"> - - - + TextWrapping="WrapWholeWords"> + + + + Style="{ThemeResource SubtitleTextBlockStyle}" + Text="{x:Bind helper:Locale.Lang._GameSettingsPage.Advanced_GLC_PreLaunch_Title}" /> + Header="{x:Bind helper:Locale.Lang._GameSettingsPage.Advanced_GLC_PreLaunch_Subtitle}" + IsOn="{x:Bind IsUsePreLaunchCommand, Mode=TwoWay}" + OffContent="{x:Bind helper:Locale.Lang._Misc.Disabled}" + OnContent="{x:Bind helper:Locale.Lang._Misc.Enabled}" /> + Margin="0,0,0,0" + Header="{x:Bind helper:Locale.Lang._GameSettingsPage.Advanced_GLC_PreLaunch_Exit}" + IsOn="{x:Bind IsPreLaunchCommandExitOnGameClose, Mode=TwoWay}" + OffContent="{x:Bind helper:Locale.Lang._Misc.Disabled}" + OnContent="{x:Bind helper:Locale.Lang._Misc.Enabled}" /> + Width="200" + Margin="0,0,0,12" + HorizontalAlignment="Left" + Header="{x:Bind helper:Locale.Lang._GameSettingsPage.Advanced_GLC_PreLaunch_Delay}" + ValueChanged="GameLaunchDelay_OnValueChanged" + Value="{x:Bind LaunchDelay, Mode=TwoWay}" /> + Margin="0,0,0,4" + HorizontalAlignment="Stretch" + CornerRadius="8,8,0,0" + IsSpellCheckEnabled="False" + Text="{x:Bind PreLaunchCommand, Mode=TwoWay}" /> + FontWeight="Semibold" + Text="{x:Bind helper:Locale.Lang._GameSettingsPage.Advanced_GLC_WarningAdmin}" /> + Style="{ThemeResource SubtitleTextBlockStyle}" + Text="{x:Bind helper:Locale.Lang._GameSettingsPage.Advanced_GLC_PostExit_Title}" /> + Header="{x:Bind helper:Locale.Lang._GameSettingsPage.Advanced_GLC_PostExit_Subtitle}" + IsOn="{x:Bind IsUsePostExitCommand, Mode=TwoWay}" + OffContent="{x:Bind helper:Locale.Lang._Misc.Disabled}" + OnContent="{x:Bind helper:Locale.Lang._Misc.Enabled}" /> + Margin="0,0,0,4" + HorizontalAlignment="Stretch" + CornerRadius="8,8,0,0" + IsSpellCheckEnabled="False" + Text="{x:Bind PostExitCommand, Mode=TwoWay}" /> + FontWeight="Semibold" + Text="{x:Bind helper:Locale.Lang._GameSettingsPage.Advanced_GLC_WarningAdmin}" /> From f7cf54567af278b1019ea020cee5ba6cfdf23130 Mon Sep 17 00:00:00 2001 From: Bagus Nur Listiyono Date: Thu, 11 Jul 2024 23:11:34 +0700 Subject: [PATCH 23/44] Only append screen cmd when using custom res --- CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs index 164607234..6ec6b0203 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs @@ -1662,9 +1662,12 @@ internal string GetLaunchArguments(IGameSettingsUniversal _Settings) { // does not support exclusive mode at all // also doesn't properly support dx12 or dx11 st - - Size screenSize = _Settings.SettingsScreen.sizeRes; - parameter.AppendFormat("-screen-width {0} -screen-height {1} ", screenSize.Width, screenSize.Height); + + if (_Settings.SettingsCollapseScreen.UseCustomResolution) + { + Size screenSize = _Settings.SettingsScreen.sizeRes; + parameter.AppendFormat("-screen-width {0} -screen-height {1} ", screenSize.Width, screenSize.Height); + } } if (_Settings.SettingsCollapseScreen.UseBorderlessScreen) From 5e1da85482624aeb2b751e92030bd48912da340e Mon Sep 17 00:00:00 2001 From: Bagus Nur Listiyono Date: Fri, 12 Jul 2024 01:15:26 +0700 Subject: [PATCH 24/44] Add docs comments --- .../GameSettings/Zenless/Enums.cs | 41 +++++++++++++++- .../Zenless/FileClass/GeneralData.cs | 48 ++++++++++++++++++- 2 files changed, 87 insertions(+), 2 deletions(-) diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Enums.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Enums.cs index 8349cef3a..8b175b115 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Enums.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Enums.cs @@ -1,6 +1,10 @@ // ReSharper disable InconsistentNaming namespace CollapseLauncher.GameSettings.Zenless.Enums; +/// +/// Represent int for corresponding text languages
+/// Default : -1 (Unset) +///
public enum LanguageText { Unset = -1, @@ -19,6 +23,10 @@ public enum LanguageText id_id = 13 } +/// +/// Represent int for corresponding audio languages
+/// Default : -1 (Unset) +///
public enum LanguageVoice { Unset = -1, @@ -28,13 +36,23 @@ public enum LanguageVoice ko_kr = 4 } +/// +/// Represent int for graphics preset options
+/// Needs to be set to 4 (Custom) for other Graphics options to apply
+/// Default : 2 (Medium) +///
public enum GraphicsPresetOption { High, Medium, - Low + Low, + Custom } +/// +/// Available options for in-game FPS limiter
+/// Default : 1 (60 FPS) +///
public enum FpsOption { Lo30, @@ -42,6 +60,10 @@ public enum FpsOption Unlimited } +/// +/// Available options for render resolutions. 0.8, 1.0, 1.2
+/// Default : 1 (1.0) +///
public enum RenderResOption { f08, @@ -49,6 +71,10 @@ public enum RenderResOption f12 } +/// +/// Available options for AntiAliasing
+/// Default : TAA +///
public enum AntiAliasingOption { Off, @@ -56,12 +82,18 @@ public enum AntiAliasingOption SMAA } +/// +/// Available options for graphics settings that has 2 options
+///
public enum QualityOption2 { Low, High } +/// +/// Available options for graphics settings that has 3 options
+///
public enum QualityOption3 { Low, @@ -69,6 +101,9 @@ public enum QualityOption3 High } +/// +/// Available options for graphics settings that has 4 options
+///
public enum QualityOption4 { Off, @@ -77,6 +112,10 @@ public enum QualityOption4 High } +/// +/// Available options for Audio Playback Device. Alters sound profile
+/// Default : Headphones +///
public enum AudioPlaybackDevice { Headphones, diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs index 7951b364a..b327f925b 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs @@ -158,6 +158,10 @@ public bool DisableBattleUIOptimization // Key 3 Preset private SystemSettingLocalData? _graphicsPresData; + /// + /// Sets the preset for Graphics Settings + /// + /// [JsonIgnore] public GraphicsPresetOption GraphicsPreset { @@ -173,6 +177,9 @@ public GraphicsPresetOption GraphicsPreset // Key 5 Resolution Select private SystemSettingLocalData? _resolutionIndexData; + /// + /// Sets the resolution based on the in-game logic. + /// [JsonIgnore] public int ResolutionIndex { @@ -188,6 +195,9 @@ public int ResolutionIndex // Key 8 VSync private SystemSettingLocalData? _vSyncData; + /// + /// Set VSync mode + /// [JsonIgnore] public bool VSync { @@ -204,6 +214,10 @@ public bool VSync // Key 9 Render Resolution private SystemSettingLocalData? _renderResolutionData; + /// + /// Sets the render resolution used in-game + /// + /// [JsonIgnore] public RenderResOption RenderResolution { @@ -222,6 +236,10 @@ public RenderResOption RenderResolution // Key 10 Shadow private SystemSettingLocalData? _shadowQualityData; + /// + /// Sets the in-game quality settings for Shadow + /// + /// [JsonIgnore] public QualityOption3 ShadowQuality { @@ -257,6 +275,10 @@ public AntiAliasingOption AntiAliasing // Key 13 Volumetric Fog private SystemSettingLocalData? _volFogQualityData; + /// + /// Sets the in-game quality settings for Volumetric Fog + /// + /// [JsonIgnore] public QualityOption4 VolumetricFogQuality { @@ -284,6 +306,10 @@ public bool Bloom // Key 15 Reflection private SystemSettingLocalData? _reflQualityData; + /// + /// Sets the in-game quality settings for Reflection + /// + /// [JsonIgnore] public QualityOption4 ReflectionQuality { @@ -300,6 +326,10 @@ public QualityOption4 ReflectionQuality // Key 16 Effects private SystemSettingLocalData? _fxQualityData; + /// + /// Sets the in-game quality settings for Effects + /// + /// [JsonIgnore] public QualityOption3 FxQuality { @@ -328,6 +358,10 @@ public int ColorFilter // Key 99 Character Quality private SystemSettingLocalData? _charQualityData; + /// + /// Sets the in-game quality settings for Character + /// + /// [JsonIgnore] public QualityOption2 CharacterQuality { @@ -343,7 +377,7 @@ public QualityOption2 CharacterQuality // Key 107 Distortion private SystemSettingLocalData? _distortionData; - + [JsonIgnore] public bool Distortion { @@ -357,6 +391,10 @@ public bool Distortion // Key 108 Shading Quality private SystemSettingLocalData? _colorQualityData; + /// + /// Sets the in-game quality settings for Color + /// + /// [JsonIgnore] public QualityOption3 ColorQuality { @@ -373,6 +411,10 @@ public QualityOption3 ColorQuality // Key 109 Environment Quality private SystemSettingLocalData? _envQualityData; + /// + /// Sets the in-game quality settings for Environment + /// + /// [JsonIgnore] public QualityOption2 EnvironmentQuality { @@ -389,6 +431,10 @@ public QualityOption2 EnvironmentQuality // Key 110 FPS private SystemSettingLocalData? _fpsData; + /// + /// Sets the in-game frame limiter + /// + /// [JsonIgnore] public FpsOption Fps { From 86e69579361ba1266bb06af0593284ddadf3a9df Mon Sep 17 00:00:00 2001 From: Bagus Nur Listiyono Date: Fri, 12 Jul 2024 01:25:46 +0700 Subject: [PATCH 25/44] Finalize ui backend --- .../ZenlessGameSettingsPage.Ext.cs | 191 +++++++++++++++++- 1 file changed, 190 insertions(+), 1 deletion(-) diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs index 2472b9760..73f366262 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs @@ -1,4 +1,5 @@ -using Hi3Helper.Screen; +using CollapseLauncher.GameSettings.Zenless.Enums; +using Hi3Helper.Screen; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using System.Collections.Generic; @@ -20,6 +21,71 @@ private void OnPropertyChanged([CallerMemberName] string propertyName = null) // Raise the PropertyChanged event, passing the name of the property whose value has changed. this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } + + private GraphicsPresetOption? oldPreset; + public void PresetSelector(object sender, SelectionChangedEventArgs _) + { + GraphicsPresetOption idx = (GraphicsPresetOption)((ComboBox)sender).SelectedIndex; + if (oldPreset == idx) return; + oldPreset = idx; + // ## Qual Preset + // 1. Low + // + // - VSync: On + // - Render Res: 1.0 + // - AA: TAA + // - Shadow: Medium + // - FX : Low + // - Shading: High + // - Char: High + // - Env: High + // - Mirror: Low + // - VolFog: Low + // - Bloom: Enable + // - Distortion: Enable + // + // 2. Medium + // + // - VSync: On + // - Render Res: 1.0 + // - AA: TAA + // - Shadow: High + // - FX : Medium + // - Shading: High + // - Char: High + // - Env: High + // - Mirror: Medium + // - VolFog: Medium + // - Bloom: Enable + // - Distortion: Enable + // + // 3. High + // + // - VSync: On + // - Render Res: 1.0 + // - AA: TAA + // - Shadow: High + // - FX : High + // - Shading: High + // - Char: High + // - Env: High + // - Mirror: High + // - VolFog: High + // - Bloom: Enable + // - Distortion: Enable + switch (idx) + { + case GraphicsPresetOption.High: + VSyncToggle.IsEnabled = true; + break; + case GraphicsPresetOption.Medium: + VSyncToggle.IsEnabled = true; + break; + case GraphicsPresetOption.Low: + VSyncToggle.IsEnabled = true; + break; + } + } #endregion #region GameResolution @@ -334,6 +400,129 @@ public bool EnableVSync get => Settings.GeneralData?.VSync ?? false; set => Settings.GeneralData.VSync = value; } + + public int Graphics_Preset + { + get => (int)Settings.GeneralData.GraphicsPreset; + set => Settings.GeneralData.GraphicsPreset = (GraphicsPresetOption)value; + } + + public int Graphics_RenderRes + { + get => (int)Settings.GeneralData.RenderResolution; + set => Settings.GeneralData.RenderResolution = (RenderResOption)value; + } + + public int Graphics_Shadow + { + get => (int)Settings.GeneralData.ShadowQuality; + set => Settings.GeneralData.ShadowQuality = (QualityOption3)value; + } + + public int Graphics_AntiAliasing + { + get => (int)Settings.GeneralData.AntiAliasing; + set => Settings.GeneralData.AntiAliasing = (AntiAliasingOption)value; + } + + public int Graphics_VolFog + { + get => (int)Settings.GeneralData.VolumetricFogQuality; + set => Settings.GeneralData.VolumetricFogQuality = (QualityOption4)value; + } + + public bool Graphics_Bloom + { + get => Settings.GeneralData.Bloom; + set => Settings.GeneralData.Bloom = value; + } + + public int Graphics_Reflection + { + get => (int)Settings.GeneralData.ReflectionQuality; + set => Settings.GeneralData.ReflectionQuality = (QualityOption4)value; + } + + public int Graphics_Effects + { + get => (int)Settings.GeneralData.FxQuality; + set => Settings.GeneralData.FxQuality = (QualityOption3)value; + } + + public int Graphics_ColorFilter + { + get => Settings.GeneralData.ColorFilter; + set => Settings.GeneralData.ColorFilter = value; + } + + public int Graphics_Character + { + get => (int)Settings.GeneralData.CharacterQuality; + set => Settings.GeneralData.CharacterQuality = (QualityOption2)value; + } + + public bool Graphics_Distortion + { + get => Settings.GeneralData.Distortion; + set => Settings.GeneralData.Distortion = value; + } + + public int Graphics_Color + { + get => (int)Settings.GeneralData.ColorQuality; + set => Settings.GeneralData.ColorQuality = (QualityOption3)value; + } + + public int Graphics_Environment + { + get => (int)Settings.GeneralData.EnvironmentQuality; + set => Settings.GeneralData.EnvironmentQuality = (QualityOption2)value; + } + + public int Graphics_Fps + { + get => (int)Settings.GeneralData.Fps; + set => Settings.GeneralData.Fps = (FpsOption)value; + } + #endregion + + #region Audio Settings - GENERAL_DATA > SystemSettingDataMap + + public int Audio_VolMain + { + get => Settings.GeneralData.Audio_MainVolume; + set => Settings.GeneralData.Audio_MainVolume = value; + } + + public int Audio_VolMusic + { + get => Settings.GeneralData.Audio_MusicVolume; + set => Settings.GeneralData.Audio_MusicVolume = value; + } + + public int Audio_VolDialog + { + get => Settings.GeneralData.Audio_DialogVolume; + set => Settings.GeneralData.Audio_DialogVolume = value; + } + + public int Audio_VolSfx + { + get => Settings.GeneralData.Audio_SfxVolume; + set => Settings.GeneralData.Audio_SfxVolume = value; + } + + public int Audio_PlaybackDevice + { + get => (int)Settings.GeneralData.Audio_PlaybackDevice; + set => Settings.GeneralData.Audio_PlaybackDevice = (AudioPlaybackDevice)value; + } + + public bool Audio_MuteOnMinimize + { + get => Settings.GeneralData.Audio_MuteOnMinimize; + set => Settings.GeneralData.Audio_MuteOnMinimize = value; + } #endregion } } From 1aa01332637fb8028e2e0ba970a81d043dee51a0 Mon Sep 17 00:00:00 2001 From: Bagus Nur Listiyono Date: Fri, 12 Jul 2024 15:26:35 +0700 Subject: [PATCH 26/44] Frontend! - Preset selector logic still buggy - Might look wonky atm - Some strings not localized --- .../GameSettings/Zenless/Enums.cs | 5 +- .../Zenless/FileClass/GeneralData.cs | 12 +- .../ZenlessGameSettingsPage.Ext.cs | 94 +++- .../ZenlessGameSettingsPage.xaml | 489 +++++++++++++++++- 4 files changed, 574 insertions(+), 26 deletions(-) diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Enums.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Enums.cs index 8b175b115..ec54e0aec 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Enums.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Enums.cs @@ -84,6 +84,7 @@ public enum AntiAliasingOption /// /// Available options for graphics settings that has 2 options
+/// Low, High ///
public enum QualityOption2 { @@ -93,6 +94,7 @@ public enum QualityOption2 /// /// Available options for graphics settings that has 3 options
+/// Low, Medium, High ///
public enum QualityOption3 { @@ -103,6 +105,7 @@ public enum QualityOption3 /// /// Available options for graphics settings that has 4 options
+/// Off, Low, Medium, High ///
public enum QualityOption4 { @@ -114,7 +117,7 @@ public enum QualityOption4 /// /// Available options for Audio Playback Device. Alters sound profile
-/// Default : Headphones +/// Default : Headphones, Options: Headphones, Speakers, TV ///
public enum AudioPlaybackDevice { diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs index b327f925b..6d8f62716 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs @@ -389,23 +389,23 @@ public bool Distortion } // Key 108 Shading Quality - private SystemSettingLocalData? _colorQualityData; + private SystemSettingLocalData? _shadingQualityData; /// /// Sets the in-game quality settings for Color /// /// [JsonIgnore] - public QualityOption3 ColorQuality + public QualityOption3 ShadingQuality { get => - (_colorQualityData.HasValue - ? _colorQualityData - : _colorQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("108", + (_shadingQualityData.HasValue + ? _shadingQualityData + : _shadingQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("108", QualityOption3.Medium)).Value .GetDataEnum(); set => - _colorQualityData?.SetDataEnum(value); + _shadingQualityData?.SetDataEnum(value); } // Key 109 Environment Quality diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs index 73f366262..6626be925 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs @@ -76,16 +76,61 @@ public void PresetSelector(object sender, SelectionChangedEventArgs _) switch (idx) { case GraphicsPresetOption.High: - VSyncToggle.IsEnabled = true; + VSyncToggle.IsEnabled = true; + RenderResolutionSelector.SelectedIndex = (int)RenderResOption.f10; + AntiAliasingSelector.SelectedIndex = (int)AntiAliasingOption.TAA; + ShadowQualitySelector.SelectedIndex = (int)QualityOption3.High; + FxQualitySelector.SelectedIndex = (int)QualityOption3.High; + ShadingQualitySelector.SelectedIndex = (int)QualityOption3.High; + CharacterQualitySelector.SelectedIndex = (int)QualityOption2.High; + EnvironmentQualitySelector.SelectedIndex = (int)QualityOption2.High; + ReflectionQualitySelector.SelectedIndex = (int)QualityOption4.High; + VolumetricFogSelector.SelectedIndex = (int)QualityOption4.High; + BloomToggle.IsChecked = true; + DistortionToggle.IsChecked = true; break; case GraphicsPresetOption.Medium: - VSyncToggle.IsEnabled = true; + VSyncToggle.IsEnabled = true; + RenderResolutionSelector.SelectedIndex = (int)RenderResOption.f10; + AntiAliasingSelector.SelectedIndex = (int)AntiAliasingOption.TAA; + ShadowQualitySelector.SelectedIndex = (int)QualityOption3.High; + FxQualitySelector.SelectedIndex = (int)QualityOption3.Medium; + ShadingQualitySelector.SelectedIndex = (int)QualityOption3.High; + CharacterQualitySelector.SelectedIndex = (int)QualityOption2.High; + EnvironmentQualitySelector.SelectedIndex = (int)QualityOption2.High; + ReflectionQualitySelector.SelectedIndex = (int)QualityOption4.Medium; + VolumetricFogSelector.SelectedIndex = (int)QualityOption4.Medium; + BloomToggle.IsChecked = true; + DistortionToggle.IsChecked = true; break; case GraphicsPresetOption.Low: - VSyncToggle.IsEnabled = true; + VSyncToggle.IsEnabled = true; + RenderResolutionSelector.SelectedIndex = (int)RenderResOption.f10; + AntiAliasingSelector.SelectedIndex = (int)AntiAliasingOption.TAA; + ShadowQualitySelector.SelectedIndex = (int)QualityOption3.Medium; + FxQualitySelector.SelectedIndex = (int)QualityOption3.Low; + ShadingQualitySelector.SelectedIndex = (int)QualityOption3.High; + CharacterQualitySelector.SelectedIndex = (int)QualityOption2.High; + EnvironmentQualitySelector.SelectedIndex = (int)QualityOption2.High; + ReflectionQualitySelector.SelectedIndex = (int)QualityOption4.Low; + VolumetricFogSelector.SelectedIndex = (int)QualityOption4.Low; + BloomToggle.IsChecked = true; + DistortionToggle.IsChecked = true; break; } } + + public void EnforceCustomPreset() + { + if (GraphicsPresetSelector.SelectedIndex == (int)GraphicsPresetOption.Custom) return; + GraphicsPresetSelector.SelectedIndex = (int)GraphicsPresetOption.Custom; + } + + public void EnforceCustomPreset_Checkbox(object _, DependencyPropertyChangedEventArgs n) => + EnforceCustomPreset(); + + public void EnforceCustomPreset_ComboBox(object _, SelectionChangedEventArgs n) => + EnforceCustomPreset(); #endregion #region GameResolution @@ -394,37 +439,67 @@ private void GameLaunchDelay_OnValueChanged(NumberBox sender, NumberBoxValueChan } #endregion + #region Language Settings - GENERAL_DATA + public int Lang_Text + { + get + { + var v = (int)Settings.GeneralData.DeviceLanguageType; + if (v <= 0) return 1; + return v; + } + set => Settings.GeneralData.DeviceLanguageType = (LanguageText)value; + } + + public int Lang_Audio + { + get + { + var v = (int)Settings.GeneralData.DeviceLanguageVoiceType; + if (v <= 0) return 1; + return v; + } + set => Settings.GeneralData.DeviceLanguageVoiceType = (LanguageVoice)value; + } + #endregion + #region Graphics Settings - GENERAL_DATA > SystemSettingDataMap + //done public bool EnableVSync { get => Settings.GeneralData?.VSync ?? false; set => Settings.GeneralData.VSync = value; } + //done public int Graphics_Preset { get => (int)Settings.GeneralData.GraphicsPreset; set => Settings.GeneralData.GraphicsPreset = (GraphicsPresetOption)value; } + //done public int Graphics_RenderRes { get => (int)Settings.GeneralData.RenderResolution; set => Settings.GeneralData.RenderResolution = (RenderResOption)value; } + //done public int Graphics_Shadow { get => (int)Settings.GeneralData.ShadowQuality; set => Settings.GeneralData.ShadowQuality = (QualityOption3)value; } + //done public int Graphics_AntiAliasing { get => (int)Settings.GeneralData.AntiAliasing; set => Settings.GeneralData.AntiAliasing = (AntiAliasingOption)value; } + //done public int Graphics_VolFog { get => (int)Settings.GeneralData.VolumetricFogQuality; @@ -437,12 +512,14 @@ public bool Graphics_Bloom set => Settings.GeneralData.Bloom = value; } + //done public int Graphics_Reflection { get => (int)Settings.GeneralData.ReflectionQuality; set => Settings.GeneralData.ReflectionQuality = (QualityOption4)value; } + //done public int Graphics_Effects { get => (int)Settings.GeneralData.FxQuality; @@ -455,6 +532,7 @@ public int Graphics_ColorFilter set => Settings.GeneralData.ColorFilter = value; } + //done public int Graphics_Character { get => (int)Settings.GeneralData.CharacterQuality; @@ -467,23 +545,27 @@ public bool Graphics_Distortion set => Settings.GeneralData.Distortion = value; } - public int Graphics_Color + //done + public int Graphics_Shading { - get => (int)Settings.GeneralData.ColorQuality; - set => Settings.GeneralData.ColorQuality = (QualityOption3)value; + get => (int)Settings.GeneralData.ShadingQuality; + set => Settings.GeneralData.ShadingQuality = (QualityOption3)value; } + //done public int Graphics_Environment { get => (int)Settings.GeneralData.EnvironmentQuality; set => Settings.GeneralData.EnvironmentQuality = (QualityOption2)value; } + //done public int Graphics_Fps { get => (int)Settings.GeneralData.Fps; set => Settings.GeneralData.Fps = (FpsOption)value; } + #endregion #region Audio Settings - GENERAL_DATA > SystemSettingDataMap diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml index a5a69ab1d..ba07f488b 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml +++ b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml @@ -48,7 +48,8 @@ + IsChecked="{x:Bind EnableVSync, Mode=TwoWay}" + IsEnabledChanged="EnforceCustomPreset_Checkbox"> @@ -101,7 +102,7 @@
+ IsChecked="{x:Bind IsMobileMode, Mode=TwoWay}" + IsEnabled="False" + Visibility="Collapsed"> @@ -190,16 +193,6 @@ IsEnabled="{x:Bind IsCanResolutionWH, Mode=OneWay}" Value="{x:Bind ResolutionH, Mode=TwoWay}" /> - - - - - - - - - - @@ -217,9 +210,479 @@ Style="{ThemeResource SubtitleTextBlockStyle}" Text="{x:Bind helper:Locale.Lang._GameSettingsPage.Graphics_SpecPanel}" TextWrapping="Wrap" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 30cba6010b7f9c77032b275dbf38b24d3c0c057a Mon Sep 17 00:00:00 2001 From: Bagus Nur Listiyono Date: Fri, 12 Jul 2024 15:46:53 +0700 Subject: [PATCH 27/44] Fix preset behavior - No longer need to select twice to get the desired preset - Fixed checkbox settings not affected/ing the preset - Fixed dum dum with Vsync checkbox in preset --- .../ZenlessGameSettingsPage.Ext.cs | 110 ++++++------------ .../ZenlessGameSettingsPage.xaml | 9 +- 2 files changed, 40 insertions(+), 79 deletions(-) diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs index 6626be925..2cc807cf1 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs @@ -7,6 +7,7 @@ using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.Runtime.CompilerServices; +// ReSharper disable InconsistentNaming namespace CollapseLauncher.Pages { @@ -23,60 +24,17 @@ private void OnPropertyChanged([CallerMemberName] string propertyName = null) } private GraphicsPresetOption? oldPreset; - public void PresetSelector(object sender, SelectionChangedEventArgs _) + private void PresetSelector(object sender, SelectionChangedEventArgs _) { + _changingPreset = true; GraphicsPresetOption idx = (GraphicsPresetOption)((ComboBox)sender).SelectedIndex; if (oldPreset == idx) return; oldPreset = idx; - // ## Qual Preset - // 1. Low - // - // - VSync: On - // - Render Res: 1.0 - // - AA: TAA - // - Shadow: Medium - // - FX : Low - // - Shading: High - // - Char: High - // - Env: High - // - Mirror: Low - // - VolFog: Low - // - Bloom: Enable - // - Distortion: Enable - // - // 2. Medium - // - // - VSync: On - // - Render Res: 1.0 - // - AA: TAA - // - Shadow: High - // - FX : Medium - // - Shading: High - // - Char: High - // - Env: High - // - Mirror: Medium - // - VolFog: Medium - // - Bloom: Enable - // - Distortion: Enable - // - // 3. High - // - // - VSync: On - // - Render Res: 1.0 - // - AA: TAA - // - Shadow: High - // - FX : High - // - Shading: High - // - Char: High - // - Env: High - // - Mirror: High - // - VolFog: High - // - Bloom: Enable - // - Distortion: Enable + switch (idx) { case GraphicsPresetOption.High: - VSyncToggle.IsEnabled = true; + VSyncToggle.IsChecked = true; RenderResolutionSelector.SelectedIndex = (int)RenderResOption.f10; AntiAliasingSelector.SelectedIndex = (int)AntiAliasingOption.TAA; ShadowQualitySelector.SelectedIndex = (int)QualityOption3.High; @@ -90,7 +48,7 @@ public void PresetSelector(object sender, SelectionChangedEventArgs _) DistortionToggle.IsChecked = true; break; case GraphicsPresetOption.Medium: - VSyncToggle.IsEnabled = true; + VSyncToggle.IsChecked = true; RenderResolutionSelector.SelectedIndex = (int)RenderResOption.f10; AntiAliasingSelector.SelectedIndex = (int)AntiAliasingOption.TAA; ShadowQualitySelector.SelectedIndex = (int)QualityOption3.High; @@ -104,7 +62,7 @@ public void PresetSelector(object sender, SelectionChangedEventArgs _) DistortionToggle.IsChecked = true; break; case GraphicsPresetOption.Low: - VSyncToggle.IsEnabled = true; + VSyncToggle.IsChecked = true; RenderResolutionSelector.SelectedIndex = (int)RenderResOption.f10; AntiAliasingSelector.SelectedIndex = (int)AntiAliasingOption.TAA; ShadowQualitySelector.SelectedIndex = (int)QualityOption3.Medium; @@ -118,22 +76,35 @@ public void PresetSelector(object sender, SelectionChangedEventArgs _) DistortionToggle.IsChecked = true; break; } + + _changingPreset = false; } - public void EnforceCustomPreset() + private bool _changingPreset; + + private async void EnforceCustomPreset() { + if (_changingPreset) return; if (GraphicsPresetSelector.SelectedIndex == (int)GraphicsPresetOption.Custom) return; + _changingPreset = true; GraphicsPresetSelector.SelectedIndex = (int)GraphicsPresetOption.Custom; + await System.Threading.Tasks.Task.Delay(200); + _changingPreset = false; } - public void EnforceCustomPreset_Checkbox(object _, DependencyPropertyChangedEventArgs n) => + private void EnforceCustomPreset_Checkbox(object _, RoutedEventArgs n) + { EnforceCustomPreset(); + } - public void EnforceCustomPreset_ComboBox(object _, SelectionChangedEventArgs n) => + private void EnforceCustomPreset_ComboBox(object _, SelectionChangedEventArgs n) + { EnforceCustomPreset(); + } #endregion #region GameResolution + // ReSharper disable once FieldCanBeMadeReadOnly.Local private List ScreenResolutionIsFullscreenIdx = new List(); public bool IsFullscreenEnabled @@ -464,42 +435,36 @@ public int Lang_Audio #endregion #region Graphics Settings - GENERAL_DATA > SystemSettingDataMap - //done public bool EnableVSync { get => Settings.GeneralData?.VSync ?? false; set => Settings.GeneralData.VSync = value; } - - //done + public int Graphics_Preset { get => (int)Settings.GeneralData.GraphicsPreset; set => Settings.GeneralData.GraphicsPreset = (GraphicsPresetOption)value; } - - //done + public int Graphics_RenderRes { get => (int)Settings.GeneralData.RenderResolution; set => Settings.GeneralData.RenderResolution = (RenderResOption)value; } - - //done + public int Graphics_Shadow { get => (int)Settings.GeneralData.ShadowQuality; set => Settings.GeneralData.ShadowQuality = (QualityOption3)value; } - - //done + public int Graphics_AntiAliasing { get => (int)Settings.GeneralData.AntiAliasing; set => Settings.GeneralData.AntiAliasing = (AntiAliasingOption)value; } - - //done + public int Graphics_VolFog { get => (int)Settings.GeneralData.VolumetricFogQuality; @@ -511,15 +476,13 @@ public bool Graphics_Bloom get => Settings.GeneralData.Bloom; set => Settings.GeneralData.Bloom = value; } - - //done + public int Graphics_Reflection { get => (int)Settings.GeneralData.ReflectionQuality; set => Settings.GeneralData.ReflectionQuality = (QualityOption4)value; } - - //done + public int Graphics_Effects { get => (int)Settings.GeneralData.FxQuality; @@ -531,8 +494,7 @@ public int Graphics_ColorFilter get => Settings.GeneralData.ColorFilter; set => Settings.GeneralData.ColorFilter = value; } - - //done + public int Graphics_Character { get => (int)Settings.GeneralData.CharacterQuality; @@ -544,28 +506,24 @@ public bool Graphics_Distortion get => Settings.GeneralData.Distortion; set => Settings.GeneralData.Distortion = value; } - - //done + public int Graphics_Shading { get => (int)Settings.GeneralData.ShadingQuality; set => Settings.GeneralData.ShadingQuality = (QualityOption3)value; } - - //done + public int Graphics_Environment { get => (int)Settings.GeneralData.EnvironmentQuality; set => Settings.GeneralData.EnvironmentQuality = (QualityOption2)value; } - - //done + public int Graphics_Fps { get => (int)Settings.GeneralData.Fps; set => Settings.GeneralData.Fps = (FpsOption)value; } - #endregion #region Audio Settings - GENERAL_DATA > SystemSettingDataMap diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml index ba07f488b..cbe9765b8 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml +++ b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml @@ -48,8 +48,9 @@ + Unchecked="EnforceCustomPreset_Checkbox"> @@ -462,8 +463,9 @@ + Unchecked="EnforceCustomPreset_Checkbox"> @@ -473,8 +475,9 @@ + Unchecked="EnforceCustomPreset_Checkbox"> From 0fb49bf304bdc04014767de19622ec06341d70f3 Mon Sep 17 00:00:00 2001 From: Bagus Nur Listiyono Date: Fri, 12 Jul 2024 16:05:28 +0700 Subject: [PATCH 28/44] Implement audio control settings --- .../ZenlessGameSettingsPage.xaml | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml index cbe9765b8..0b9470c1a 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml +++ b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml @@ -4,6 +4,7 @@ + + + + + + + + + + + + + + + + + Date: Fri, 12 Jul 2024 16:18:11 +0700 Subject: [PATCH 29/44] Adjust margins and style --- .../Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml index 0b9470c1a..cca0ffd2a 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml +++ b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml @@ -262,6 +262,8 @@ Text="Color Filter" TextWrapping="WrapWholeWords" /> - + @@ -612,7 +614,7 @@ Text="Audio Playback Device" TextWrapping="Wrap" /> -
From 0772934ea9b9f23063810da6898e3194ce3403a6 Mon Sep 17 00:00:00 2001 From: Bagus Nur Listiyono Date: Sat, 13 Jul 2024 06:29:42 +0700 Subject: [PATCH 30/44] Localization --- .../ZenlessGameSettingsPage.Ext.cs | 4 +- .../ZenlessGameSettingsPage.xaml | 37 +++++++++------- .../ZenlessGameSettingsPage.xaml.cs | 16 ++++++- .../Locale/LangZenlessGameSettingsPage.cs | 42 +++++++++++++++++++ Hi3Helper.Core/Lang/en_US.json | 12 ++++++ 5 files changed, 94 insertions(+), 17 deletions(-) create mode 100644 Hi3Helper.Core/Lang/Locale/LangZenlessGameSettingsPage.cs diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs index 2cc807cf1..ffa15476f 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs @@ -143,7 +143,9 @@ public bool IsBorderlessEnabled } else { - GameWindowResizable.IsEnabled = true; + if (GameResolutionFullscreen.IsChecked == false) + GameWindowResizable.IsEnabled = true; + else GameWindowResizable.IsEnabled = false; GameResolutionFullscreen.IsEnabled = true; } } diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml index cca0ffd2a..7e7b75487 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml +++ b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml @@ -1,6 +1,7 @@  + - + + + + - @@ -611,7 +620,7 @@ - - - + + + diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs index 3cc436f77..d21c69bb8 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs @@ -10,6 +10,7 @@ using Hi3Helper.Screen; using Hi3Helper.Shared.ClassStruct; using Microsoft.UI.Xaml; + using Microsoft.UI.Xaml.Controls.Primitives; using Microsoft.UI.Xaml.Media; using Microsoft.UI.Xaml.Navigation; using Microsoft.Win32; @@ -35,8 +36,8 @@ public partial class ZenlessGameSettingsPage private ZenlessSettings Settings { get; set; } private Brush InheritApplyTextColor { get; set; } private RegistryMonitor RegistryWatcher { get; set; } - - private bool IsNoReload = false; + + private bool IsNoReload = false; public ZenlessGameSettingsPage() { @@ -52,6 +53,7 @@ public ZenlessGameSettingsPage() }); LoadPage(); + this.ColorFilterSlider.LayoutUpdated += ColFilSlider_DisablePopup; } catch (Exception ex) { @@ -300,5 +302,15 @@ private void OnUnload(object sender, RoutedEventArgs e) RegistryWatcher?.Dispose(); }); } + + // https://stackoverflow.com/questions/76737204/regarding-sliders-in-win-ui + private void ColFilSlider_DisablePopup(object _, object n) + { + if (VisualTreeHelper.GetOpenPopupsForXamlRoot(this.ColorFilterSlider.XamlRoot) + .FirstOrDefault() is { } popup) + { + popup.Visibility = Visibility.Collapsed; + } + } } } diff --git a/Hi3Helper.Core/Lang/Locale/LangZenlessGameSettingsPage.cs b/Hi3Helper.Core/Lang/Locale/LangZenlessGameSettingsPage.cs new file mode 100644 index 000000000..58c09a10a --- /dev/null +++ b/Hi3Helper.Core/Lang/Locale/LangZenlessGameSettingsPage.cs @@ -0,0 +1,42 @@ +// ReSharper disable InconsistentNaming +namespace Hi3Helper +{ + public sealed partial class Locale + { + public sealed partial class LocalizationParams + { + public LangZenlessGameSettingsPage _ZenlessGameSettingsPage { get; set; } = + LangFallback?._ZenlessGameSettingsPage; + + public sealed class LangZenlessGameSettingsPage + { + public string Graphics_ColorFilter { get; set; } = + LangFallback?._ZenlessGameSettingsPage.Graphics_ColorFilter; + + public string Graphics_RenderRes { get; set; } = + LangFallback?._ZenlessGameSettingsPage.Graphics_RenderRes; + + public string Graphics_EffectsQ { get; set; } = + LangFallback?._ZenlessGameSettingsPage.Graphics_EffectsQ; + + public string Graphics_ShadingQ { get; set; } = + LangFallback?._ZenlessGameSettingsPage.Graphics_ShadingQ; + + public string Graphics_Distortion { get; set; } = + LangFallback?._ZenlessGameSettingsPage.Graphics_Distortion; + + public string Audio_PlaybackDev { get; set; } = + LangFallback?._ZenlessGameSettingsPage.Audio_PlaybackDev; + + public string Audio_PlaybackDev_Headphones { get; set; } = + LangFallback?._ZenlessGameSettingsPage.Audio_PlaybackDev_Headphones; + + public string Audio_PlaybackDev_Speakers { get; set; } = + LangFallback?._ZenlessGameSettingsPage.Audio_PlaybackDev_Speakers; + + public string Audio_PlaybackDev_TV { get; set; } = + LangFallback?._ZenlessGameSettingsPage.Audio_PlaybackDev_TV; + } + } + } +} \ No newline at end of file diff --git a/Hi3Helper.Core/Lang/en_US.json b/Hi3Helper.Core/Lang/en_US.json index 9af95f00c..d973466b6 100644 --- a/Hi3Helper.Core/Lang/en_US.json +++ b/Hi3Helper.Core/Lang/en_US.json @@ -1287,5 +1287,17 @@ "CDNCheckboxItemLatencyFormat": " ({0} ms)", "CDNCheckboxItemLatencyUnknownFormat": " (unknown)", "CDNCheckboxItemLatencyRecommendedFormat": " [Recommended]" + }, + + "_ZenlessGameSettingsPage": { + "Graphics_ColorFilter": "Color Filter", + "Graphics_RenderRes": "Render Resolution", + "Graphics_EffectsQ": "Effects Quality", + "Graphics_ShadingQ": "Shading Quality", + "Graphics_Distortion": "Distortion", + "Audio_PlaybackDev": "Audio Playback Device", + "Audio_PlaybackDev_Headphones": "Headphones", + "Audio_PlaybackDev_Speakers": "Speakers", + "Audio_PlaybackDev_TV": "TV" } } From a8e1cd89c9ca07fcc43302749e985d3e0fd1028b Mon Sep 17 00:00:00 2001 From: Bagus Nur Listiyono Date: Sat, 13 Jul 2024 06:52:26 +0700 Subject: [PATCH 31/44] CodeQA --- .../BaseClass/MagicNodeBaseValues.cs | 29 +-- .../BaseClass/SettingsGameVersionManager.cs | 4 +- .../Zenless/FileClass/GeneralData.cs | 62 ++--- .../Zenless/JsonProperties/Properties.cs | 2 + .../Zenless/RegistryClass/ScreenManager.cs | 12 +- .../GameSettings/Zenless/Sleepy.cs | 229 +++++++++--------- CollapseLauncher/Classes/GamePropertyVault.cs | 3 +- .../Interfaces/Class/JSONSerializerHelper.cs | 2 - .../ZenlessGameSettingsPage.Ext.cs | 3 +- .../ZenlessGameSettingsPage.xaml | 2 +- .../ZenlessGameSettingsPage.xaml.cs | 2 - Hi3Helper.Core/Classes/Data/ScreenData.cs | 1 - Hi3Helper.Core/Classes/Logger/ILog.cs | 6 +- Hi3Helper.Core/Lang/en_US.json | 2 +- 14 files changed, 176 insertions(+), 183 deletions(-) diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs index fb491dc45..968123715 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs @@ -53,17 +53,16 @@ private static T EnsureCreatedInner(this JsonNode? node, string keyName) // Set parent node as object JsonObject? parentNodeObj = node?.AsObject(); - JsonNode? valueNode = null; // If the value node does not exist, then create and add a new one - if (!(parentNodeObj?.TryGetPropertyValue(keyName, out valueNode) ?? false && valueNode != null)) + if (!(parentNodeObj?.TryGetPropertyValue(keyName, out var valueNode) ?? false)) { // Otherwise, create a new empty one. JsonNodeOptions options = new JsonNodeOptions { PropertyNameCaseInsensitive = true }; - JsonNode? jsonValueNode = isTryCreateArray ? + JsonNode jsonValueNode = isTryCreateArray ? new JsonArray(options) : new JsonObject(options); valueNode = jsonValueNode; @@ -153,7 +152,7 @@ public static TEnum GetNodeValueEnum(this JsonNode? node, string keyName, case JsonValueKind.String: // If it's a string string? enumAsString = (string?)enumValueRaw; // Cast JsonValue as string - if (Enum.TryParse(enumAsString, true, out TEnum enumParsedFromString)) // Try parse as a named member + if (Enum.TryParse(enumAsString, true, out TEnum enumParsedFromString)) // Try parse as a named member return enumParsedFromString; // If successful, return the returned value // If the string is actually a number as a string, then try parse it as int @@ -177,10 +176,7 @@ public static void SetNodeValue(this JsonNode? node, string keyName, TVa if (node == null) return; // Get node as object - JsonObject? jsonObject = node.AsObject(); - - // If node is null, return and ignore - if (jsonObject == null) return; + JsonObject jsonObject = node.AsObject(); // Create an instance of the JSON node value JsonValue? jsonValue = JsonValue.Create(value); @@ -200,10 +196,7 @@ public static void SetNodeValueEnum(this JsonNode? node, string keyName, if (node == null) return; // Get node as object - JsonObject? jsonObject = node.AsObject(); - - // If node is null, return and ignore - if (jsonObject == null) return; + JsonObject jsonObject = node.AsObject(); // Create an instance of the JSON node value JsonValue? jsonValue = enumStoreType switch @@ -221,21 +214,21 @@ public static void SetNodeValueEnum(this JsonNode? node, string keyName, else jsonObject.Add(new KeyValuePair(keyName, jsonValue)); - JsonValue AsEnumNumber(TEnum value) + JsonValue AsEnumNumber(TEnum v) { - int enumAsNumber = Unsafe.As(ref value); + int enumAsNumber = Unsafe.As(ref v); return JsonValue.Create(enumAsNumber); } - JsonValue? AsEnumString(TEnum value) + JsonValue? AsEnumString(TEnum v) { - string? enumName = Enum.GetName(value); + string? enumName = Enum.GetName(v); return JsonValue.Create(enumName); } - JsonValue? AsEnumNumberString(TEnum value) + JsonValue AsEnumNumberString(TEnum v) { - int enumAsNumber = Unsafe.As(ref value); + int enumAsNumber = Unsafe.As(ref v); string enumAsNumberString = $"{enumAsNumber}"; return JsonValue.Create(enumAsNumberString); } diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/SettingsGameVersionManager.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/SettingsGameVersionManager.cs index 96d395c79..5057972f1 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/SettingsGameVersionManager.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/SettingsGameVersionManager.cs @@ -30,10 +30,10 @@ internal static SettingsGameVersionManager Create(IGameVersionCheck? gameVersion internal IGameVersionCheck? VersionManager { get; set; } internal string? GameFolder => VersionManager?.GameDirPath; - internal string? GameExecutable => Path.GetFileNameWithoutExtension(VersionManager?.GamePreset.GameExecutableName!); + internal string GameExecutable => Path.GetFileNameWithoutExtension(VersionManager?.GamePreset.GameExecutableName!); internal string? ConfigFileLocationFormat { get; set; } internal string? ConfigFileName { get; set; } - internal string? ConfigFilePath { get => Path.Combine(GameFolder ?? string.Empty, string.Format(ConfigFileLocationFormat ?? string.Empty, GameExecutable, ConfigFileName)); } + internal string ConfigFilePath { get => Path.Combine(GameFolder ?? string.Empty, string.Format(ConfigFileLocationFormat ?? string.Empty, GameExecutable, ConfigFileName)); } } } diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs index 6d8f62716..4771b9b70 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs @@ -6,10 +6,10 @@ using System; using System.Text.Json.Nodes; using System.Text.Json.Serialization; +// ReSharper disable ReturnTypeCanBeNotNullable #nullable enable namespace CollapseLauncher.GameSettings.Zenless; - internal class GeneralData : MagicNodeBaseValues, IGameSettingsValueMagic { #region Node Based Properties @@ -22,7 +22,7 @@ internal class GeneralData : MagicNodeBaseValues, IGameSettingsValu [JsonIgnore] // We ignore this one from getting serialized to default JSON value public JsonNode? SystemSettingDataMap { - // Cache the SystemSettingDataMap inside of the parent SettingsJsonNode + // Cache the SystemSettingDataMap inside the parent SettingsJsonNode // and ensure that the node for SystemSettingDataMap exists. If not exist, // create a new one (via EnsureCreated()). get => _systemSettingDataMap ??= SettingsJsonNode.EnsureCreatedObject("SystemSettingDataMap"); @@ -32,7 +32,7 @@ public JsonNode? SystemSettingDataMap [JsonIgnore] // We ignore this one from getting serialized to default JSON value public JsonNode? KeyboardBindingMap { - // Cache the KeyboardBindingMap inside of the parent SettingsJsonNode + // Cache the KeyboardBindingMap inside the parent SettingsJsonNode // and ensure that the node for KeyboardBindingMap exists. If not exist, // create a new one (via EnsureCreated()). get => _keyboardBindingMap ??= SettingsJsonNode.EnsureCreatedObject("KeyboardBindingMap"); @@ -42,7 +42,7 @@ public JsonNode? KeyboardBindingMap [JsonIgnore] // We ignore this one from getting serialized to default JSON value public JsonNode? MouseBindingMap { - // Cache the MouseBindingMap inside of the parent SettingsJsonNode + // Cache the MouseBindingMap inside the parent SettingsJsonNode // and ensure that the node for MouseBindingMap exists. If not exist, // create a new one (via EnsureCreated()). get => _mouseBindingMap ??= SettingsJsonNode.EnsureCreatedObject("MouseBindingMap"); @@ -52,7 +52,7 @@ public JsonNode? MouseBindingMap [JsonIgnore] // We ignore this one from getting serialized to default JSON value public JsonNode? GamepadBindingMap { - // Cache the GamepadBindingMap inside of the parent SettingsJsonNode + // Cache the GamepadBindingMap inside the parent SettingsJsonNode // and ensure that the node for GamepadBindingMap exists. If not exist, // create a new one (via EnsureCreated()). get => _gamepadBindingMap ??= SettingsJsonNode.EnsureCreatedObject("GamepadBindingMap"); @@ -169,7 +169,7 @@ public GraphicsPresetOption GraphicsPreset ? _graphicsPresData : _graphicsPresData = SystemSettingDataMap! - .AsSystemSettingLocalData("3", GraphicsPresetOption.Medium)) + .AsSystemSettingLocalData("3", GraphicsPresetOption.Medium)) .Value.GetDataEnum(); set => _graphicsPresData?.SetDataEnum(value); } @@ -187,7 +187,7 @@ public int ResolutionIndex ? _resolutionIndexData : _resolutionIndexData = SystemSettingDataMap! - .AsSystemSettingLocalData("5", -1)) + .AsSystemSettingLocalData("5", -1)) .Value.GetData(); set => _resolutionIndexData?.SetData(value); } @@ -206,7 +206,7 @@ public bool VSync (_vSyncData.HasValue ? _vSyncData : _vSyncData = SystemSettingDataMap! - .AsSystemSettingLocalData("8", 1)).Value.GetData() == 1; + .AsSystemSettingLocalData("8", 1)).Value.GetData() == 1; set => _vSyncData?.SetData(value ? 1 : 0); } @@ -226,9 +226,9 @@ public RenderResOption RenderResolution (_renderResolutionData.HasValue ? _renderResolutionData : _renderResolutionData = SystemSettingDataMap! - .AsSystemSettingLocalData("9", - RenderResOption.f10)).Value - .GetDataEnum(); + .AsSystemSettingLocalData("9", + RenderResOption.f10)).Value + .GetDataEnum(); set => _renderResolutionData?.SetDataEnum(value); } @@ -247,7 +247,7 @@ public QualityOption3 ShadowQuality get => (_shadowQualityData.HasValue ? _shadowQualityData - : _shadowQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("10", + : _shadowQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("10", QualityOption3.Medium)).Value .GetDataEnum(); set => @@ -265,9 +265,9 @@ public AntiAliasingOption AntiAliasing (_antiAliasingData.HasValue ? _antiAliasingData : _antiAliasingData = SystemSettingDataMap! - .AsSystemSettingLocalData("12", - AntiAliasingOption.TAA)).Value - .GetDataEnum(); + .AsSystemSettingLocalData("12", + AntiAliasingOption.TAA)).Value + .GetDataEnum(); set => _antiAliasingData?.SetDataEnum(value); } @@ -285,8 +285,8 @@ public QualityOption4 VolumetricFogQuality get => (_volFogQualityData.HasValue ? _volFogQualityData : _volFogQualityData = SystemSettingDataMap! - .AsSystemSettingLocalData("13", QualityOption4.Medium)).Value - .GetDataEnum(); + .AsSystemSettingLocalData("13", QualityOption4.Medium)).Value + .GetDataEnum(); set => _volFogQualityData?.SetDataEnum(value); } @@ -298,7 +298,7 @@ public bool Bloom { get => (_bloomData.HasValue ? _bloomData - : _bloomData = SystemSettingDataMap!.AsSystemSettingLocalData("14", 1)).Value + : _bloomData = SystemSettingDataMap!.AsSystemSettingLocalData("14", 1)).Value .GetData() == 1; set => _bloomData?.SetData(value ? 1 : 0); } @@ -316,7 +316,7 @@ public QualityOption4 ReflectionQuality get => (_reflQualityData.HasValue ? _reflQualityData - : _reflQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("15", + : _reflQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("15", QualityOption4.Medium)).Value .GetDataEnum(); set => @@ -336,7 +336,7 @@ public QualityOption3 FxQuality get => (_fxQualityData.HasValue ? _fxQualityData - : _fxQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("16", + : _fxQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("16", QualityOption3.Medium)).Value .GetDataEnum(); set => @@ -350,7 +350,7 @@ public int ColorFilter { get => (_colorFilterData.HasValue ? _colorFilterData - : _colorFilterData = SystemSettingDataMap!.AsSystemSettingLocalData("95", 10)) + : _colorFilterData = SystemSettingDataMap!.AsSystemSettingLocalData("95", 10)) .Value.GetData(); set => _colorFilterData?.SetData(value); } @@ -368,7 +368,7 @@ public QualityOption2 CharacterQuality get => (_charQualityData.HasValue ? _charQualityData - : _charQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("99", + : _charQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("99", QualityOption2.High)).Value .GetDataEnum(); set => @@ -383,7 +383,7 @@ public bool Distortion { get => (_distortionData.HasValue ? _distortionData - : _distortionData = SystemSettingDataMap!.AsSystemSettingLocalData("107", 1)) + : _distortionData = SystemSettingDataMap!.AsSystemSettingLocalData("107", 1)) .Value.GetData() == 1; set => _distortionData?.SetData(value ? 1 : 0); } @@ -401,7 +401,7 @@ public QualityOption3 ShadingQuality get => (_shadingQualityData.HasValue ? _shadingQualityData - : _shadingQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("108", + : _shadingQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("108", QualityOption3.Medium)).Value .GetDataEnum(); set => @@ -421,7 +421,7 @@ public QualityOption2 EnvironmentQuality get => (_envQualityData.HasValue ? _envQualityData - : _envQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("109", + : _envQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("109", QualityOption2.High)).Value .GetDataEnum(); set => @@ -440,7 +440,7 @@ public FpsOption Fps { // Initialize the field under _fpsData as SystemSettingLocalData get => (_fpsData.HasValue ? _fpsData : _fpsData = SystemSettingDataMap! - .AsSystemSettingLocalData("110", FpsOption.Hi60)).Value.GetDataEnum(); + .AsSystemSettingLocalData("110", FpsOption.Hi60)).Value.GetDataEnum(); set => _fpsData?.SetDataEnum(value); } @@ -504,9 +504,9 @@ public AudioPlaybackDevice Audio_PlaybackDevice get => (_playDevData.HasValue ? _playDevData : _playDevData = - SystemSettingDataMap!.AsSystemSettingLocalData("10104", - AudioPlaybackDevice.Headphones)).Value - .GetDataEnum(); + SystemSettingDataMap!.AsSystemSettingLocalData("10104", + AudioPlaybackDevice.Headphones)).Value + .GetDataEnum(); set => _playDevData?.SetDataEnum(value); } @@ -518,7 +518,7 @@ public bool Audio_MuteOnMinimize { get => (_muteAudOnMinimizeData.HasValue ? _muteAudOnMinimizeData - : _muteAudOnMinimizeData = SystemSettingDataMap!.AsSystemSettingLocalData("10113", 1)).Value.GetData() == 1; + : _muteAudOnMinimizeData = SystemSettingDataMap!.AsSystemSettingLocalData("10113", 1)).Value.GetData() == 1; set => _muteAudOnMinimizeData?.SetData((value ? 1 : 0)); } @@ -551,4 +551,4 @@ public bool Audio_MuteOnMinimize return returnVal; } -} +} \ No newline at end of file diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/JsonProperties/Properties.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/JsonProperties/Properties.cs index 63951e86c..42e4084ac 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/JsonProperties/Properties.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/JsonProperties/Properties.cs @@ -2,6 +2,8 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Text.Json.Nodes; +// ReSharper disable UnusedTypeParameter +// ReSharper disable RedundantNullableFlowAttribute #nullable enable namespace CollapseLauncher.GameSettings.Zenless.JsonProperties; diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/RegistryClass/ScreenManager.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/RegistryClass/ScreenManager.cs index db6175969..c9b7800b9 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/RegistryClass/ScreenManager.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/RegistryClass/ScreenManager.cs @@ -21,7 +21,7 @@ internal class ScreenManager : BaseScreenSettingData, IGameSettingsValue magic) @@ -100,36 +102,36 @@ internal static unsafe string ReadString(Stream stream, ReadOnlySpan magic reader.EmulateSleepyBinaryFormatterHeaderAssertion(); // Get the data length - int length = reader.GetBinaryFormatterDataLength(); + int length = reader.GetBinaryFormatterDataLength(); int magicLength = magic.Length; // Alloc temporary buffers - bool isRent = length <= 1 << 17; // Check if length <= 128 KiB + bool isRent = length <= 1 << 17; // Check if length <= 128 KiB char[] bufferChars = isRent ? ArrayPool.Shared.Rent(length) : new char[length]; // Do the do CreateEvil(magic, out bool[] evil, out int evilsCount); fixed (bool* evp = &evil[0]) - fixed (char* bp = &bufferChars[0]) - { - try - { - // Do the do (pt. 2) - int j = InternalDecode(magic, evp, reader, length, magicLength, bp); - - // Emulate and Assert the BinaryFormatter footer - reader.EmulateSleepyBinaryFormatterFooterAssertion(); - - // Return - return new string(bp, 0, j); - } - finally + fixed (char* bp = &bufferChars[0]) { - // Return and clear the buffer, to only returns the return string. - if (isRent) ArrayPool.Shared.Return(bufferChars, true); - else Array.Clear(bufferChars); + try + { + // Do the do (pt. 2) + int j = InternalDecode(magic, evp, reader, length, magicLength, bp); + + // Emulate and Assert the BinaryFormatter footer + reader.EmulateSleepyBinaryFormatterFooterAssertion(); + + // Return + return new string(bp, 0, j); + } + finally + { + // Return and clear the buffer, to only returns the return string. + if (isRent) ArrayPool.Shared.Return(bufferChars, true); + else Array.Clear(bufferChars); + } } - } } private static unsafe int InternalDecode(ReadOnlySpan magic, bool* evil, BinaryReader reader, int length, int magicLength, char* bp) @@ -138,11 +140,10 @@ private static unsafe int InternalDecode(ReadOnlySpan magic, bool* evil, B int j = 0; int i = 0; - int n = 0; - amimir: - n = i % magicLength; - byte c = reader.ReadByte(); + amimir: + var n = i % magicLength; + byte c = reader.ReadByte(); byte ch = (byte)(c ^ magic[n]); if (*(evil + n)) @@ -153,8 +154,8 @@ private static unsafe int InternalDecode(ReadOnlySpan magic, bool* evil, B { if (eepy) { - ch += 0x40; - eepy = false; + ch += 0x40; + eepy = false; } *(bp + j++) = (char)ch; } @@ -188,9 +189,9 @@ internal static unsafe void WriteString(Stream stream, ReadOnlySpan conten writer.EmulateSleepyBinaryFormatterHeaderWrite(); // Do the do - int contentLen = content.Length; - int bufferLen = contentLen * 2; - bool isRent = bufferLen <= 2 << 17; + int contentLen = content.Length; + int bufferLen = contentLen * 2; + bool isRent = bufferLen <= 2 << 17; // Alloc temporary buffers byte[] contentBytes = isRent ? ArrayPool.Shared.Rent(bufferLen) : new byte[bufferLen]; @@ -200,32 +201,32 @@ internal static unsafe void WriteString(Stream stream, ReadOnlySpan conten CreateEvil(magic, out bool[] evil, out int evilsCount); fixed (char* cp = &content[0]) - fixed (byte* bp = &contentBytes[0]) - fixed (byte* ep = &encodedBytes[0]) - fixed (bool* evp = &evil[0]) - { - try - { - // Get the string bytes - _ = Encoding.UTF8.GetBytes(cp, contentLen, bp, bufferLen); - - // Do the do (pt. 2) - int h = InternalWrite(magic, contentLen, bp, ep, evp); - - writer.Write7BitEncodedInt(h); - writer.BaseStream.Write(encodedBytes, 0, h); - writer.EmulateSleepyBinaryFormatterFooterWrite(); - } - finally - { - // Return and clear the buffer. - if (isRent) ArrayPool.Shared.Return(contentBytes, true); - else Array.Clear(contentBytes); - - if (isRent) ArrayPool.Shared.Return(encodedBytes, true); - else Array.Clear(encodedBytes); - } - } + fixed (byte* bp = &contentBytes[0]) + fixed (byte* ep = &encodedBytes[0]) + fixed (bool* evp = &evil[0]) + { + try + { + // Get the string bytes + _ = Encoding.UTF8.GetBytes(cp, contentLen, bp, bufferLen); + + // Do the do (pt. 2) + int h = InternalWrite(magic, contentLen, bp, ep, evp); + + writer.Write7BitEncodedInt(h); + writer.BaseStream.Write(encodedBytes, 0, h); + writer.EmulateSleepyBinaryFormatterFooterWrite(); + } + finally + { + // Return and clear the buffer. + if (isRent) ArrayPool.Shared.Return(contentBytes, true); + else Array.Clear(contentBytes); + + if (isRent) ArrayPool.Shared.Return(encodedBytes, true); + else Array.Clear(encodedBytes); + } + } } private static unsafe int InternalWrite(ReadOnlySpan magic, int contentLen, byte* bp, byte* ep, bool* evil) @@ -234,16 +235,16 @@ private static unsafe int InternalWrite(ReadOnlySpan magic, int contentLen int i = 0; int j = 0; - amimir: - int n = i % magic.Length; + amimir: + int n = i % magic.Length; byte ch = *(bp + j); if (*(evil + n)) { byte eepy = 0; if (*(bp + j) > 0x40) { - ch -= 0x40; - eepy = 1; + ch -= 0x40; + eepy = 1; } *(ep + h++) = (byte)(eepy ^ magic[n]); @@ -260,10 +261,10 @@ private static unsafe int InternalWrite(ReadOnlySpan magic, int contentLen private static void CreateEvil(ReadOnlySpan magic, out bool[] evilist, out int evilsCount) { int magicLength = magic.Length; - int i = 0; - evilist = new bool[magicLength]; + int i = 0; + evilist = new bool[magicLength]; evilsCount = 0; - evilist: + evilist: int n = i % magicLength; evilist[i] = (magic[n] & 0xC0) == 0xC0; if (evilist[i]) ++evilsCount; @@ -275,31 +276,31 @@ private static void EmulateSleepyBinaryFormatterHeaderAssertion(this BinaryReade // Do assert [class] -> [string object] // START! // Check if the first byte is SerializedStreamHeader - reader.LogAssertInfoByteEnum(BinaryHeaderEnum.SerializedStreamHeader); + reader.LogAssertInfoByteEnum(BinaryHeaderEnum.SerializedStreamHeader); // Check if the type is an Object - reader.LogAssertInfoInt32Enum(BinaryHeaderEnum.Object); + reader.LogAssertInfoInt32Enum(BinaryHeaderEnum.Object); // Check if the type is a BinaryReference - reader.LogAssertInfoInt32Enum(BinaryHeaderEnum.BinaryReference); + reader.LogAssertInfoInt32Enum(BinaryHeaderEnum.BinaryReference); // Check if the BinaryReference type is a String - reader.LogAssertInfoInt32Enum(BinaryTypeEnum.String); + reader.LogAssertInfoInt32Enum(BinaryTypeEnum.String); // Check for the binary array type and check if it's Single - reader.LogAssertInfoInt32Enum(BinaryArrayTypeEnum.Single); + reader.LogAssertInfoInt32Enum(BinaryArrayTypeEnum.Single); // Check for the binary type and check if it's StringArray (UTF-8) - reader.LogAssertInfoByteEnum(BinaryTypeEnum.StringArray); + reader.LogAssertInfoByteEnum(BinaryTypeEnum.StringArray); // Check for the internal array type and check if it's Single - reader.LogAssertInfoInt32Enum(InternalArrayTypeE.Single); + reader.LogAssertInfoInt32Enum(InternalArrayTypeE.Single); } // Do assert [class] -> [EOF mark] // START! private static void EmulateSleepyBinaryFormatterFooterAssertion(this BinaryReader reader) => - reader.LogAssertInfoByteEnum(BinaryHeaderEnum.MessageEnd); + reader.LogAssertInfoByteEnum(BinaryHeaderEnum.MessageEnd); private static void EmulateSleepyBinaryFormatterHeaderWrite(this BinaryWriter writer) { @@ -315,7 +316,7 @@ private static void EmulateSleepyBinaryFormatterHeaderWrite(this BinaryWriter wr // Emulate to write Sleepy BinaryFormatter footer EOF private static void EmulateSleepyBinaryFormatterFooterWrite(this BinaryWriter writer) => - writer.WriteEnumAsByte(BinaryHeaderEnum.MessageEnd); + writer.WriteEnumAsByte(BinaryHeaderEnum.MessageEnd); private static void WriteEnumAsByte(this BinaryWriter writer, T headerEnum) where T : struct, Enum @@ -351,13 +352,13 @@ private static void LogAssertInfo(BinaryReader reader, ref T assertHeaderEnum int intAssertCasted = Unsafe.As(ref assertHeaderEnum); if (intAssertCasted != currentInt) { - string? assertHeaderEnumValueName = Enum.GetName(assertHeaderEnum); - T comparedEnumCasted = Unsafe.As(ref currentInt); - string? comparedHeaderEnumValueName = Enum.GetName(comparedEnumCasted); + string? assertHeaderEnumValueName = Enum.GetName(assertHeaderEnum); + T comparedEnumCasted = Unsafe.As(ref currentInt); + string? comparedHeaderEnumValueName = Enum.GetName(comparedEnumCasted); throw new InvalidDataException($"[Sleepy::LogAssertInfo] BinaryFormatter header is not valid at stream pos: {reader.BaseStream.Position:x8}. Expecting object enum: {assertHeaderEnumValueName} but getting: {comparedHeaderEnumValueName} instead!"); } } private static int GetBinaryFormatterDataLength(this BinaryReader reader) => reader.Read7BitEncodedInt(); -} +} \ No newline at end of file diff --git a/CollapseLauncher/Classes/GamePropertyVault.cs b/CollapseLauncher/Classes/GamePropertyVault.cs index 3500cc014..66b98c793 100644 --- a/CollapseLauncher/Classes/GamePropertyVault.cs +++ b/CollapseLauncher/Classes/GamePropertyVault.cs @@ -1,5 +1,4 @@ -using CollapseLauncher.GameSettings.Base; -using CollapseLauncher.GameSettings.Genshin; +using CollapseLauncher.GameSettings.Genshin; using CollapseLauncher.GameSettings.Honkai; using CollapseLauncher.GameSettings.StarRail; using CollapseLauncher.GameSettings.Zenless; diff --git a/CollapseLauncher/Classes/Interfaces/Class/JSONSerializerHelper.cs b/CollapseLauncher/Classes/Interfaces/Class/JSONSerializerHelper.cs index 7cab23567..6214d4e68 100644 --- a/CollapseLauncher/Classes/Interfaces/Class/JSONSerializerHelper.cs +++ b/CollapseLauncher/Classes/Interfaces/Class/JSONSerializerHelper.cs @@ -111,7 +111,6 @@ internal static partial class JSONSerializerHelper // Start deserialize and return return InnerDeserialize(tempBuffer.AsSpan(0, bufferWritten), context, defaultType); } - catch { throw; } finally { // Once the process is completed, then return the rented buffer (if it's being used) @@ -162,7 +161,6 @@ internal static partial class JSONSerializerHelper // Start deserialize and return return InnerDeserializeAsJsonNode(tempBuffer.AsSpan(0, bufferWritten)); } - catch { throw; } finally { // Once the process is completed, then return the rented buffer (if it's being used) diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs index ffa15476f..6ca8a5df9 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs @@ -284,7 +284,8 @@ public int ResolutionIndexSelected { if (value < 0) return; Settings.GeneralData.ResolutionIndex = value; - bool isFullscreen = value >= 0 && (value + 1) < ScreenResolutionIsFullscreenIdx.Count ? ScreenResolutionIsFullscreenIdx[value] : false; + // ReSharper disable once SimplifyConditionalTernaryExpression + bool isFullscreen = (value + 1) < ScreenResolutionIsFullscreenIdx.Count ? ScreenResolutionIsFullscreenIdx[value] : false; IsFullscreenEnabled = isFullscreen; } } diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml index 7e7b75487..b26c164b7 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml +++ b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml @@ -257,7 +257,7 @@ - The line to print into console or write into the log file void LogWriteLine(string line); - /// + /// + /// Print log to the console. + /// + /// The line to print into console or write into the log file /// Type of the log line /// Write the log line into the log file // ReSharper disable MethodOverloadWithOptionalParameter @@ -54,6 +57,5 @@ public interface ILog : IDisposable /// The path of the logs to be cleaned up and reloaded /// The encoding of the log writer (Default is if null) void ResetLogFiles(string? reloadToPath, Encoding? encoding = null); -#nullable restore } } diff --git a/Hi3Helper.Core/Lang/en_US.json b/Hi3Helper.Core/Lang/en_US.json index d973466b6..8c1e15966 100644 --- a/Hi3Helper.Core/Lang/en_US.json +++ b/Hi3Helper.Core/Lang/en_US.json @@ -1290,7 +1290,7 @@ }, "_ZenlessGameSettingsPage": { - "Graphics_ColorFilter": "Color Filter", + "Graphics_ColorFilter": "Color Filter Strength", "Graphics_RenderRes": "Render Resolution", "Graphics_EffectsQ": "Effects Quality", "Graphics_ShadingQ": "Shading Quality", From e8bf71e0ff5ef04f92ec6c110e7446e204dad5b9 Mon Sep 17 00:00:00 2001 From: Bagus Nur Listiyono Date: Sat, 13 Jul 2024 13:25:41 +0700 Subject: [PATCH 32/44] CodeQA pt 2 --- .../GameSettings/BaseClass/MagicNodeBaseValues.cs | 6 +++--- CollapseLauncher/Classes/Helper/Metadata/PresetConfig.cs | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs index 968123715..30cb8b423 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs @@ -264,7 +264,7 @@ public static T LoadWithMagic(byte[] magic, SettingsGameVersionManager versionMa try { - string? filePath = versionManager.ConfigFilePath; + string filePath = versionManager.ConfigFilePath; if (!File.Exists(filePath)) throw new FileNotFoundException("MagicNodeBaseValues config file not found!"); string raw = Sleepy.ReadString(filePath, magic); @@ -305,7 +305,7 @@ public T DefaultValue(byte[] magic, SettingsGameVersionManager versionManager, J public void Save() { // Get the file and dir path - string? filePath = GameVersionManager.ConfigFilePath; + string filePath = GameVersionManager.ConfigFilePath; string? fileDirPath = Path.GetDirectoryName(filePath); // Create the dir if not exist @@ -314,7 +314,7 @@ public void Save() // Write into the file string jsonString = SettingsJsonNode.SerializeJsonNode(Context, false, false); - Sleepy.WriteString(filePath!, jsonString, Magic); + Sleepy.WriteString(filePath, jsonString, Magic); } public bool Equals(GeneralData? other) diff --git a/CollapseLauncher/Classes/Helper/Metadata/PresetConfig.cs b/CollapseLauncher/Classes/Helper/Metadata/PresetConfig.cs index 15e7b77f4..823e87f00 100644 --- a/CollapseLauncher/Classes/Helper/Metadata/PresetConfig.cs +++ b/CollapseLauncher/Classes/Helper/Metadata/PresetConfig.cs @@ -75,6 +75,7 @@ internal class PresetConfig { #region Constants + // ReSharper disable once UnusedMember.Local private const string PrefixRegInstallLocation = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{0}"; @@ -473,6 +474,7 @@ private void SetVoiceLanguageID_StarRail(int langID) if (kvpTemp == null) return null; + // ReSharper disable once ConstantConditionalAccessQualifier verData = kvpTemp?.Value; } From 3769465fe1709256091f47dadf9e1c4414d21634 Mon Sep 17 00:00:00 2001 From: Bagus Nur Listiyono Date: Sat, 13 Jul 2024 13:39:19 +0700 Subject: [PATCH 33/44] Remove method to disable slider popup It causes EVERY popup to be disabled, not just sliders and yeah, it does it for the entire app LMAO --- .../GameSettingsPages/ZenlessGameSettingsPage.xaml.cs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs index 17e174cce..945d39340 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs @@ -51,7 +51,6 @@ public ZenlessGameSettingsPage() }); LoadPage(); - this.ColorFilterSlider.LayoutUpdated += ColFilSlider_DisablePopup; } catch (Exception ex) { @@ -300,15 +299,5 @@ private void OnUnload(object sender, RoutedEventArgs e) RegistryWatcher?.Dispose(); }); } - - // https://stackoverflow.com/questions/76737204/regarding-sliders-in-win-ui - private void ColFilSlider_DisablePopup(object _, object n) - { - if (VisualTreeHelper.GetOpenPopupsForXamlRoot(this.ColorFilterSlider.XamlRoot) - .FirstOrDefault() is { } popup) - { - popup.Visibility = Visibility.Collapsed; - } - } } } From de2e51183b5108dd7c92f0507545e2bf97018168 Mon Sep 17 00:00:00 2001 From: Kemal Setya Adhi Date: Sat, 13 Jul 2024 15:32:57 +0700 Subject: [PATCH 34/44] Avoid RequiresUnreferencedCode for JsonNode creation --- .../BaseClass/MagicNodeBaseValues.cs | 45 +++++++++++++++++-- .../Zenless/JsonProperties/Properties.cs | 3 +- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs index 30cb8b423..b51cb131a 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs @@ -3,11 +3,13 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Runtime.CompilerServices; using System.Text.Json; using System.Text.Json.Nodes; using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; #nullable enable namespace CollapseLauncher.GameSettings.Base @@ -21,6 +23,12 @@ public enum JsonEnumStoreType internal static class MagicNodeBaseValuesExt { + private static JsonSerializerOptions JsonSerializerOpts = new JsonSerializerOptions + { + AllowTrailingCommas = true, + ReadCommentHandling = JsonCommentHandling.Skip + }; + internal static JsonObject EnsureCreatedObject(this JsonNode? node, string keyName) { // If the node is empty, then create a new instance of it @@ -114,7 +122,7 @@ public static TValue GetNodeValue(this JsonNode? node, string keyName, T if (typeof(TValue) == typeof(bool) && jsonNodeValue.GetValueKind() == JsonValueKind.Number) { // Assuming 0 is false, and any non-zero number is true - int numValue = jsonNodeValue.AsValue().GetValue(); + int numValue = jsonNodeValue.AsValue().GetValue(); bool boolValue = numValue != 0; return (TValue)(object)boolValue; // Cast bool to TValue } @@ -170,7 +178,7 @@ public static TEnum GetNodeValueEnum(this JsonNode? node, string keyName, return defaultValue; } - public static void SetNodeValue(this JsonNode? node, string keyName, TValue value) + public static void SetNodeValue(this JsonNode? node, string keyName, TValue value, JsonSerializerContext? context = null) { // If node is null, return and ignore if (node == null) return; @@ -179,7 +187,7 @@ public static void SetNodeValue(this JsonNode? node, string keyName, TVa JsonObject jsonObject = node.AsObject(); // Create an instance of the JSON node value - JsonValue? jsonValue = JsonValue.Create(value); + JsonValue? jsonValue = CreateJsonValue(value, context); // If the node has object, then assign the new value if (jsonObject.ContainsKey(keyName)) @@ -233,6 +241,37 @@ JsonValue AsEnumNumberString(TEnum v) return JsonValue.Create(enumAsNumberString); } } + + private static JsonValue? CreateJson(TDynamic dynamicValue, JsonSerializerContext context) + { + JsonTypeInfo jsonTypeInfo = context.GetTypeInfo(typeof(TDynamic)) + ?? throw new NotSupportedException($"Context does not include a JsonTypeInfo of type {nameof(TDynamic)}"); + JsonTypeInfo jsonTypeInfoT = (JsonTypeInfo)jsonTypeInfo; + return JsonValue.Create(dynamicValue, jsonTypeInfoT); + } + + private static JsonValue? CreateJsonValue(TValue value, JsonSerializerContext? context) + => value switch + { + bool v_bool => JsonValue.Create(v_bool), + byte v_byte => JsonValue.Create(v_byte), + sbyte v_sbyte => JsonValue.Create(v_sbyte), + short v_short => JsonValue.Create(v_short), + char v_char => JsonValue.Create(v_char), + int v_int => JsonValue.Create(v_int), + uint v_uint => JsonValue.Create(v_uint), + long v_long => JsonValue.Create(v_long), + ulong v_ulong => JsonValue.Create(v_ulong), + float v_float => JsonValue.Create(v_float), + double v_double => JsonValue.Create(v_double), + decimal v_decimal => JsonValue.Create(v_decimal), + string v_string => JsonValue.Create(v_string), + DateTime v_datetime => JsonValue.Create(v_datetime), + DateTimeOffset v_datetimeoffset => JsonValue.Create(v_datetimeoffset), + Guid v_guid => JsonValue.Create(v_guid), + JsonElement v_jsonelement => JsonValue.Create(v_jsonelement), + _ => CreateJson(value, context ?? throw new NotSupportedException("You cannot pass a null context while setting a non-struct value to JsonValue")) + }; } internal class MagicNodeBaseValues diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/JsonProperties/Properties.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/JsonProperties/Properties.cs index 42e4084ac..8326e9aea 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/JsonProperties/Properties.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/JsonProperties/Properties.cs @@ -2,6 +2,7 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Text.Json.Nodes; +using System.Text.Json.Serialization; // ReSharper disable UnusedTypeParameter // ReSharper disable RedundantNullableFlowAttribute @@ -23,7 +24,7 @@ public struct SystemSettingLocalData public TData GetData() => _node.GetNodeValue("Data", _defaultData); public TData GetDataEnum() => _node.GetNodeValueEnum("Data", _defaultData); - public void SetData(TData value) => _node.SetNodeValue("Data", value); + public void SetData(TData value, JsonSerializerContext? context = null) => _node.SetNodeValue("Data", value, context); public void SetDataEnum(TDataEnum value, JsonEnumStoreType enumStoreType = JsonEnumStoreType.AsNumber) where TDataEnum : struct, Enum => _node.SetNodeValueEnum("Data", value, enumStoreType); From 39e0a9fdf835d80d9182407a9cc3a5f5319aeb3e Mon Sep 17 00:00:00 2001 From: Kemal Setya Adhi Date: Sat, 13 Jul 2024 15:40:33 +0700 Subject: [PATCH 35/44] Kod Ki Ei :facepalm: --- .../GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs index b51cb131a..f3bf97f16 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.IO; using System.Runtime.CompilerServices; using System.Text.Json; From 0fed906105e1a5ad670292212fd9d8e2822f3660 Mon Sep 17 00:00:00 2001 From: Kemal Setya Adhi Date: Sat, 13 Jul 2024 15:53:17 +0700 Subject: [PATCH 36/44] Avoid re-init on creating default values --- .../GameSettings/BaseClass/MagicNodeBaseValues.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs index f3bf97f16..16459d9d6 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs @@ -319,11 +319,11 @@ public static T LoadWithMagic(byte[] magic, SettingsGameVersionManager versionMa catch (Exception ex) { Logger.LogWriteLine($"Failed to parse MagicNodeBaseValues settings\r\n{ex}", LogType.Error, true); - return new T().DefaultValue(magic, versionManager, context); + return DefaultValue(magic, versionManager, context); } } - public T DefaultValue(byte[] magic, SettingsGameVersionManager versionManager, JsonSerializerContext context) + private static T DefaultValue(byte[] magic, SettingsGameVersionManager versionManager, JsonSerializerContext context) { // Generate dummy data T data = new T(); From 785e9dcf91c10f0302569488318a6f4ee148e872 Mon Sep 17 00:00:00 2001 From: Kemal Setya Adhi Date: Sat, 13 Jul 2024 16:54:57 +0700 Subject: [PATCH 37/44] Move IGameSettingsValueMagic to base class --- .../GameSettings/BaseClass/MagicNodeBaseValues.cs | 5 +++-- .../GameSettings/Zenless/FileClass/GeneralData.cs | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs index 16459d9d6..5fb160e79 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs @@ -1,4 +1,5 @@ using CollapseLauncher.GameSettings.Zenless; +using CollapseLauncher.Interfaces; using Hi3Helper; using System; using System.Collections.Generic; @@ -273,8 +274,8 @@ JsonValue AsEnumNumberString(TEnum v) }; } - internal class MagicNodeBaseValues - where T : MagicNodeBaseValues, new() + internal class MagicNodeBaseValues : IGameSettingsValueMagic + where T : MagicNodeBaseValues, new() { [JsonIgnore] [DebuggerBrowsable(DebuggerBrowsableState.Never)] diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs index 4771b9b70..6fe5d190c 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs @@ -1,7 +1,6 @@ using CollapseLauncher.GameSettings.Base; using CollapseLauncher.GameSettings.Zenless.Enums; using CollapseLauncher.GameSettings.Zenless.JsonProperties; -using CollapseLauncher.Interfaces; using Hi3Helper; using System; using System.Text.Json.Nodes; @@ -10,7 +9,7 @@ #nullable enable namespace CollapseLauncher.GameSettings.Zenless; -internal class GeneralData : MagicNodeBaseValues, IGameSettingsValueMagic +internal class GeneralData : MagicNodeBaseValues { #region Node Based Properties private JsonNode? _systemSettingDataMap; From fc9f086fa3ebb95c05a3f5e95af32fd9904110f1 Mon Sep 17 00:00:00 2001 From: Kemal Setya Adhi Date: Sat, 13 Jul 2024 16:55:16 +0700 Subject: [PATCH 38/44] Use ``JsonNode.DeepEquals`` to compare values --- .../GameSettings/BaseClass/MagicNodeBaseValues.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs index 5fb160e79..9e1848897 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/MagicNodeBaseValues.cs @@ -356,10 +356,7 @@ public void Save() Sleepy.WriteString(filePath, jsonString, Magic); } - public bool Equals(GeneralData? other) - { - return true; - } + public bool Equals(T? other) => JsonNode.DeepEquals(this.SettingsJsonNode, other?.SettingsJsonNode); protected virtual void InjectNodeAndMagic(JsonNode? jsonNode, byte[] magic, SettingsGameVersionManager versionManager, JsonSerializerContext context) { From e2010dba61cc860a801ee79f7de048e48e467b38 Mon Sep 17 00:00:00 2001 From: Kemal Setya Adhi Date: Sat, 13 Jul 2024 16:55:42 +0700 Subject: [PATCH 39/44] Fix Borderless checkbox not disabled if fullscreen is used --- .../Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs index 6ca8a5df9..af9874f1e 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.Ext.cs @@ -119,6 +119,7 @@ public bool IsFullscreenEnabled GameWindowResizable.IsChecked = false; GameResolutionFullscreenExclusive.IsEnabled = !IsCustomResolutionEnabled; GameResolutionBorderless.IsChecked = false; + GameResolutionBorderless.IsEnabled = false; return; } GameWindowResizable.IsEnabled = true; From f5cf481fd18453ca706acc907a8ae4b11bf5a7ab Mon Sep 17 00:00:00 2001 From: Kemal Setya Adhi Date: Sat, 13 Jul 2024 21:51:36 +0700 Subject: [PATCH 40/44] Fix QA --- .../Classes/EventsManagement/EventsHandler.cs | 8 +- .../Classes/FileDialogCOM/Interfaces.cs | 1 - CollapseLauncher/Classes/GamePropertyVault.cs | 128 ++++---- .../Helper/Update/LauncherUpdateHelper.cs | 9 +- .../XAMLs/MainApp/MainPage.xaml.cs | 286 ++++++++++-------- .../XAMLs/MainApp/Pages/HomePage.xaml.cs | 6 +- .../XAMLs/MainApp/Pages/SettingsPage.xaml.cs | 27 +- Hi3Helper.Core/Classes/Logger/LoggerBase.cs | 3 +- 8 files changed, 264 insertions(+), 204 deletions(-) diff --git a/CollapseLauncher/Classes/EventsManagement/EventsHandler.cs b/CollapseLauncher/Classes/EventsManagement/EventsHandler.cs index 624d2f770..989320cdd 100644 --- a/CollapseLauncher/Classes/EventsManagement/EventsHandler.cs +++ b/CollapseLauncher/Classes/EventsManagement/EventsHandler.cs @@ -11,6 +11,8 @@ using static CollapseLauncher.InnerLauncherConfig; using static Hi3Helper.Locale; using static Hi3Helper.Shared.Region.LauncherConfig; +// ReSharper disable CheckNamespace +// ReSharper disable AssignNullToNotNullAttribute namespace CollapseLauncher { @@ -301,11 +303,11 @@ internal class BackgroundImgChangerInvoker { public static event EventHandler ImgEvent; public static event EventHandler IsImageHide; - BackgroundImgProperty property; + public void ChangeBackground(string ImgPath, Action ActionAfterLoaded, - bool IsCustom, bool IsForceRecreateCache = false, bool IsRequestInit = false) + bool IsCustom, bool IsForceRecreateCache = false, bool IsRequestInit = false) { - ImgEvent?.Invoke(this, property = new BackgroundImgProperty(ImgPath, IsCustom, IsForceRecreateCache, IsRequestInit, ActionAfterLoaded)); + ImgEvent?.Invoke(this, new BackgroundImgProperty(ImgPath, IsCustom, IsForceRecreateCache, IsRequestInit, ActionAfterLoaded)); } public void ToggleBackground(bool Hide) => IsImageHide?.Invoke(this, Hide); diff --git a/CollapseLauncher/Classes/FileDialogCOM/Interfaces.cs b/CollapseLauncher/Classes/FileDialogCOM/Interfaces.cs index facf3e5ea..c0e43be6e 100644 --- a/CollapseLauncher/Classes/FileDialogCOM/Interfaces.cs +++ b/CollapseLauncher/Classes/FileDialogCOM/Interfaces.cs @@ -156,7 +156,6 @@ internal partial interface IFileSaveDialog [GeneratedComInterface] internal partial interface IFileDialogEvents; // This dialog is no longer being used - [ComImport] [Guid(IIDGuid.IShellItem)] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] [GeneratedComInterface] diff --git a/CollapseLauncher/Classes/GamePropertyVault.cs b/CollapseLauncher/Classes/GamePropertyVault.cs index 66b98c793..b0e75a966 100644 --- a/CollapseLauncher/Classes/GamePropertyVault.cs +++ b/CollapseLauncher/Classes/GamePropertyVault.cs @@ -19,6 +19,7 @@ using System.IO; using System.Linq; using System.Threading.Tasks; +// ReSharper disable CheckNamespace namespace CollapseLauncher.Statics { @@ -26,41 +27,44 @@ internal class GamePresetProperty : IDisposable { internal GamePresetProperty(UIElement UIElementParent, RegionResourceProp APIResouceProp, string GameName, string GameRegion) { - PresetConfig GamePreset = LauncherMetadataHelper.LauncherMetadataConfig[GameName][GameRegion]; - - _APIResouceProp = APIResouceProp!.Copy(); - switch (GamePreset!.GameType) + if (LauncherMetadataHelper.LauncherMetadataConfig != null) { - case GameNameType.Honkai: - _GameVersion = new GameTypeHonkaiVersion(UIElementParent, _APIResouceProp, GameName, GameRegion); - _GameSettings = new HonkaiSettings(_GameVersion); - _GameCache = new HonkaiCache(UIElementParent, _GameVersion); - _GameRepair = new HonkaiRepair(UIElementParent, _GameVersion, _GameCache, _GameSettings); - _GameInstall = new HonkaiInstall(UIElementParent, _GameVersion, _GameCache, _GameSettings); - break; - case GameNameType.StarRail: - _GameVersion = new GameTypeStarRailVersion(UIElementParent, _APIResouceProp, GameName, GameRegion); - _GameSettings = new StarRailSettings(_GameVersion); - _GameCache = new StarRailCache(UIElementParent, _GameVersion); - _GameRepair = new StarRailRepair(UIElementParent, _GameVersion); - _GameInstall = new StarRailInstall(UIElementParent, _GameVersion); - break; - case GameNameType.Genshin: - _GameVersion = new GameTypeGenshinVersion(UIElementParent, _APIResouceProp, GameName, GameRegion); - _GameSettings = new GenshinSettings(_GameVersion); - _GameCache = null; - _GameRepair = new GenshinRepair(UIElementParent, _GameVersion, _GameVersion.GameAPIProp!.data!.game!.latest!.decompressed_path); - _GameInstall = new GenshinInstall(UIElementParent, _GameVersion); - break; - case GameNameType.Zenless: - _GameVersion = new GameTypeZenlessVersion(UIElementParent, _APIResouceProp, GameName, GameRegion); - _GameSettings = new ZenlessSettings(_GameVersion); - _GameCache = null; - _GameRepair = null; - _GameInstall = new ZenlessInstall(UIElementParent, _GameVersion); - break; - default: - throw new NotSupportedException($"[GamePresetProperty.Ctor] Game type: {GamePreset.GameType} ({GamePreset.ProfileName} - {GamePreset.ZoneName}) is not supported!"); + PresetConfig GamePreset = LauncherMetadataHelper.LauncherMetadataConfig[GameName][GameRegion]; + + _APIResouceProp = APIResouceProp!.Copy(); + switch (GamePreset!.GameType) + { + case GameNameType.Honkai: + _GameVersion = new GameTypeHonkaiVersion(UIElementParent, _APIResouceProp, GameName, GameRegion); + _GameSettings = new HonkaiSettings(_GameVersion); + _GameCache = new HonkaiCache(UIElementParent, _GameVersion); + _GameRepair = new HonkaiRepair(UIElementParent, _GameVersion, _GameCache, _GameSettings); + _GameInstall = new HonkaiInstall(UIElementParent, _GameVersion, _GameCache, _GameSettings); + break; + case GameNameType.StarRail: + _GameVersion = new GameTypeStarRailVersion(UIElementParent, _APIResouceProp, GameName, GameRegion); + _GameSettings = new StarRailSettings(_GameVersion); + _GameCache = new StarRailCache(UIElementParent, _GameVersion); + _GameRepair = new StarRailRepair(UIElementParent, _GameVersion); + _GameInstall = new StarRailInstall(UIElementParent, _GameVersion); + break; + case GameNameType.Genshin: + _GameVersion = new GameTypeGenshinVersion(UIElementParent, _APIResouceProp, GameName, GameRegion); + _GameSettings = new GenshinSettings(_GameVersion); + _GameCache = null; + _GameRepair = new GenshinRepair(UIElementParent, _GameVersion, _GameVersion.GameAPIProp!.data!.game!.latest!.decompressed_path); + _GameInstall = new GenshinInstall(UIElementParent, _GameVersion); + break; + case GameNameType.Zenless: + _GameVersion = new GameTypeZenlessVersion(UIElementParent, _APIResouceProp, GameName, GameRegion); + _GameSettings = new ZenlessSettings(_GameVersion); + _GameCache = null; + _GameRepair = null; + _GameInstall = new ZenlessInstall(UIElementParent, _GameVersion); + break; + default: + throw new NotSupportedException($"[GamePresetProperty.Ctor] Game type: {GamePreset.GameType} ({GamePreset.ProfileName} - {GamePreset.ZoneName}) is not supported!"); + } } } @@ -103,9 +107,9 @@ internal bool IsGameRunning // returns a null if something goes wrong. If not, then pass it to .Where(x) method which will select the given value with the certain logic. // (in this case, we need to ensure that the MainWindowHandle is not a non-zero pointer) and then piped into null-break operator. internal Process? GetGameProcessWithActiveWindow() => - Process.GetProcessesByName(Path.GetFileNameWithoutExtension(_GamePreset!.GameExecutableName))? - .Where(x => x.MainWindowHandle != IntPtr.Zero)? - .FirstOrDefault(); + Process + .GetProcessesByName(Path.GetFileNameWithoutExtension(_GamePreset!.GameExecutableName)) + .FirstOrDefault(x => x.MainWindowHandle != IntPtr.Zero); #nullable disable /* @@ -146,33 +150,41 @@ internal static class GamePropertyVault public static void LoadGameProperty(UIElement UIElementParent, RegionResourceProp APIResouceProp, string GameName, string GameRegion) { - PresetConfig GamePreset = LauncherMetadataHelper.LauncherMetadataConfig[GameName][GameRegion]; + if (LauncherMetadataHelper.LauncherMetadataConfig != null) + { + PresetConfig GamePreset = LauncherMetadataHelper.LauncherMetadataConfig[GameName][GameRegion]; + + LastGameHashID = LastGameHashID == 0 ? GamePreset!.HashID : LastGameHashID; + CurrentGameHashID = GamePreset!.HashID; + } - LastGameHashID = LastGameHashID == 0 ? GamePreset!.HashID : LastGameHashID; - CurrentGameHashID = GamePreset!.HashID; RegisterGameProperty(UIElementParent, APIResouceProp, GameName, GameRegion); } private static void RegisterGameProperty(UIElement UIElementParent, RegionResourceProp APIResouceProp, string GameName, string GameRegion) { - PresetConfig GamePreset = LauncherMetadataHelper.LauncherMetadataConfig[GameName][GameRegion]; - - CleanupUnusedGameProperty(); - if (Vault!.ContainsKey(GamePreset!.HashID)) + if (LauncherMetadataHelper.LauncherMetadataConfig != null) { -#if DEBUG - Logger.LogWriteLine($"[GamePropertyVault] Game property has been cached by Hash ID: {GamePreset.HashID}", LogType.Debug, true); -#endif - // Try reinitialize the config file on reloading cached game property - Vault[GamePreset!.HashID]?._GameVersion?.Reinitialize(); - return; + PresetConfig GamePreset = LauncherMetadataHelper.LauncherMetadataConfig[GameName][GameRegion]; + + CleanupUnusedGameProperty(); + if (Vault!.ContainsKey(GamePreset!.HashID)) + { + #if DEBUG + Logger.LogWriteLine($"[GamePropertyVault] Game property has been cached by Hash ID: {GamePreset.HashID}", LogType.Debug, true); + #endif + // Try reinitialize the config file on reloading cached game property + Vault[GamePreset!.HashID]?._GameVersion?.Reinitialize(); + return; + } + + GamePresetProperty Property = new GamePresetProperty(UIElementParent, APIResouceProp, GameName, GameRegion); + Vault.Add(GamePreset.HashID, Property); + #if DEBUG + Logger.LogWriteLine($"[GamePropertyVault] Creating & caching game property by Hash ID: {GamePreset.HashID}", LogType.Debug, true); } - GamePresetProperty Property = new GamePresetProperty(UIElementParent, APIResouceProp, GameName, GameRegion); - Vault.Add(GamePreset.HashID, Property); -#if DEBUG - Logger.LogWriteLine($"[GamePropertyVault] Creating & caching game property by Hash ID: {GamePreset.HashID}", LogType.Debug, true); -#endif + #endif } #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously @@ -182,9 +194,9 @@ private static async void CleanupUnusedGameProperty() if (Vault == null || Vault.Count == 0) return; int[] unusedGamePropertyHashID = Vault.Values - .Where(x => !x!._GameInstall!.IsRunning && !x.IsGameRunning && x._GamePreset!.HashID != CurrentGameHashID)? - .Select(x => x._GamePreset.HashID)? - .ToArray(); + .Where(x => !x!._GameInstall!.IsRunning && !x.IsGameRunning && x._GamePreset!.HashID != CurrentGameHashID) + .Select(x => x._GamePreset.HashID) + .ToArray(); foreach (int key in unusedGamePropertyHashID) { diff --git a/CollapseLauncher/Classes/Helper/Update/LauncherUpdateHelper.cs b/CollapseLauncher/Classes/Helper/Update/LauncherUpdateHelper.cs index 31785563d..c1c4c28e1 100644 --- a/CollapseLauncher/Classes/Helper/Update/LauncherUpdateHelper.cs +++ b/CollapseLauncher/Classes/Helper/Update/LauncherUpdateHelper.cs @@ -6,6 +6,7 @@ using Squirrel.Sources; using System; using System.Threading.Tasks; +// ReSharper disable CheckNamespace namespace CollapseLauncher.Helper.Update { @@ -24,11 +25,11 @@ static LauncherUpdateHelper() internal static AppUpdateVersionProp? AppUpdateVersionProp; internal static bool IsLauncherUpdateAvailable; - private static GameVersion _launcherCurrentVersion; + private static readonly GameVersion _launcherCurrentVersion; internal static GameVersion? LauncherCurrentVersion => _launcherCurrentVersion; - private static string _launcherCurrentVersionString; + private static readonly string _launcherCurrentVersionString; internal static string LauncherCurrentVersionString => _launcherCurrentVersionString; @@ -66,7 +67,7 @@ internal static async Task IsUpdateAvailable(bool isForceCheckUpdate = fal IsLauncherUpdateAvailable = LauncherCurrentVersion.Compare(remoteVersion); bool isUserIgnoreUpdate = (LauncherConfig.GetAppConfigValue("DontAskUpdate").ToBoolNullable() ?? false) && !isForceCheckUpdate; - bool isUpdateRoutineSkipped = isUserIgnoreUpdate && !(AppUpdateVersionProp?.IsForceUpdate ?? false); + bool isUpdateRoutineSkipped = isUserIgnoreUpdate && !AppUpdateVersionProp.IsForceUpdate; return IsLauncherUpdateAvailable && !isUpdateRoutineSkipped; } @@ -75,7 +76,7 @@ internal static async Task IsUpdateAvailable(bool isForceCheckUpdate = fal private static async ValueTask GetUpdateMetadata(string updateChannel) { string relativePath = ConverterTool.CombineURLFromString(updateChannel, "fileindex.json"); - await using BridgedNetworkStream ms = await FallbackCDNUtil.TryGetCDNFallbackStream(relativePath, default); + await using BridgedNetworkStream ms = await FallbackCDNUtil.TryGetCDNFallbackStream(relativePath); return await ms.DeserializeAsync(InternalAppJSONContext.Default); } } diff --git a/CollapseLauncher/XAMLs/MainApp/MainPage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/MainPage.xaml.cs index 8e3178666..dad402ff1 100644 --- a/CollapseLauncher/XAMLs/MainApp/MainPage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/MainPage.xaml.cs @@ -44,6 +44,10 @@ using static Hi3Helper.Logger; using static Hi3Helper.Shared.Region.LauncherConfig; using UIElementExtensions = CollapseLauncher.Extension.UIElementExtensions; +// ReSharper disable CheckNamespace +// ReSharper disable RedundantExtendsListEntry +// ReSharper disable InconsistentNaming +// ReSharper disable UnusedMember.Local namespace CollapseLauncher { @@ -65,8 +69,8 @@ public partial class MainPage : Page internal static List PreviousTagString = new(); #nullable enable - internal static BackgroundMediaUtility? CurrentBackgroundHandler = null; - private BackgroundMediaUtility? LocalBackgroundHandler = null; + internal static BackgroundMediaUtility? CurrentBackgroundHandler; + private BackgroundMediaUtility? _localBackgroundHandler; #nullable restore #endregion @@ -75,8 +79,8 @@ public MainPage() { try { - LogWriteLine($"Welcome to Collapse Launcher v{LauncherUpdateHelper.LauncherCurrentVersionString} - {MainEntryPoint.GetVersionString()}", LogType.Default, false); - LogWriteLine($"Application Data Location:\r\n\t{AppDataFolder}", LogType.Default); + LogWriteLine($"Welcome to Collapse Launcher v{LauncherUpdateHelper.LauncherCurrentVersionString} - {MainEntryPoint.GetVersionString()}"); + LogWriteLine($"Application Data Location:\r\n\t{AppDataFolder}"); InitializeComponent(); m_mainPage = this; ToggleNotificationPanelBtn.Translation += Shadow16; @@ -97,10 +101,10 @@ private void Page_Unloaded(object sender, RoutedEventArgs e) { UnsubscribeEvents(); #if !DISABLEDISCORD - AppDiscordPresence.Dispose(); + AppDiscordPresence?.Dispose(); #endif ImageLoaderHelper.DestroyWaifu2X(); - LocalBackgroundHandler?.Dispose(); + _localBackgroundHandler?.Dispose(); } private async void StartRoutine(object sender, RoutedEventArgs e) @@ -123,7 +127,7 @@ private async void StartRoutine(object sender, RoutedEventArgs e) if (!await CheckForAdminAccess(this)) { if (WindowUtility.CurrentWindow is MainWindow mainWindow) - mainWindow?.CloseApp(); + mainWindow.CloseApp(); return; } @@ -157,7 +161,7 @@ private async Task InitializeStartup() // Initialize the background image utility CurrentBackgroundHandler = await BackgroundMediaUtility.CreateInstanceAsync(this, BackgroundAcrylicMask, BackgroundOverlayTitleBar, BackgroundNewBackGrid, BackgroundNewMediaPlayerGrid); - LocalBackgroundHandler = CurrentBackgroundHandler; + _localBackgroundHandler = CurrentBackgroundHandler; Type Page = typeof(HomePage); @@ -334,7 +338,7 @@ private void ChangeTitleDragAreaInvoker_TitleBarEvent(object sender, ChangeTitle { UpdateLayout(); - InputNonClientPointerSource nonClientInputSrc = InputNonClientPointerSource.GetForWindowId(WindowUtility.CurrentWindowId.Value); + InputNonClientPointerSource nonClientInputSrc = InputNonClientPointerSource.GetForWindowId(WindowUtility.CurrentWindowId ?? throw new NullReferenceException()); WindowUtility.EnableWindowNonClientArea(); WindowUtility.SetWindowTitlebarDragArea(DragAreaMode_Full); @@ -447,34 +451,50 @@ private void BackgroundImg_IsImageHideEvent(object sender, bool e) private void CustomBackgroundChanger_Event(object sender, BackgroundImgProperty e) { - LauncherMetadataHelper.CurrentMetadataConfig.GameLauncherApi.GameBackgroundImgLocal = e.ImgPath; - IsCustomBG = e.IsCustom; + if (LauncherMetadataHelper.CurrentMetadataConfig?.GameLauncherApi != null) + { + LauncherMetadataHelper.CurrentMetadataConfig.GameLauncherApi.GameBackgroundImgLocal = e.ImgPath; + IsCustomBG = e.IsCustom; - if (e.IsCustom) - SetAndSaveConfigValue("CustomBGPath", LauncherMetadataHelper.CurrentMetadataConfig.GameLauncherApi.GameBackgroundImgLocal); + if (e.IsCustom) + SetAndSaveConfigValue("CustomBGPath", + LauncherMetadataHelper.CurrentMetadataConfig.GameLauncherApi + .GameBackgroundImgLocal); - if (!File.Exists(LauncherMetadataHelper.CurrentMetadataConfig.GameLauncherApi.GameBackgroundImgLocal)) - { - LogWriteLine($"Custom background file {e.ImgPath} is missing!", LogType.Warning, true); - LauncherMetadataHelper.CurrentMetadataConfig.GameLauncherApi.GameBackgroundImgLocal = AppDefaultBG; - } + if (!File.Exists(LauncherMetadataHelper.CurrentMetadataConfig.GameLauncherApi.GameBackgroundImgLocal)) + { + LogWriteLine($"Custom background file {e.ImgPath} is missing!", LogType.Warning, true); + LauncherMetadataHelper.CurrentMetadataConfig.GameLauncherApi.GameBackgroundImgLocal = AppDefaultBG; + } - CurrentBackgroundHandler?.LoadBackground(LauncherMetadataHelper.CurrentMetadataConfig.GameLauncherApi.GameBackgroundImgLocal, e.IsRequestInit, e.IsForceRecreateCache, (Exception ex) => - { - LauncherMetadataHelper.CurrentMetadataConfig.GameLauncherApi.GameBackgroundImgLocal = AppDefaultBG; - LogWriteLine($"An error occured while loading background {e.ImgPath}\r\n{ex}", LogType.Error, true); - ErrorSender.SendException(ex); - }, e.ActionAfterLoaded); + CurrentBackgroundHandler + ?.LoadBackground(LauncherMetadataHelper.CurrentMetadataConfig.GameLauncherApi.GameBackgroundImgLocal, + e.IsRequestInit, e.IsForceRecreateCache, ex => + { + LauncherMetadataHelper + .CurrentMetadataConfig + .GameLauncherApi + .GameBackgroundImgLocal = + AppDefaultBG; + LogWriteLine($"An error occured while loading background {e.ImgPath}\r\n{ex}", + LogType.Error, true); + ErrorSender.SendException(ex); + }, e.ActionAfterLoaded); + } } internal async void ChangeBackgroundImageAsRegionAsync(bool ShowLoadingMsg = false) { IsCustomBG = GetAppConfigValue("UseCustomBG").ToBool(); - bool isAPIBackgroundAvailable = !string.IsNullOrEmpty(LauncherMetadataHelper.CurrentMetadataConfig.GameLauncherApi.GameBackgroundImg); + bool isAPIBackgroundAvailable = !string.IsNullOrEmpty(LauncherMetadataHelper.CurrentMetadataConfig?.GameLauncherApi?.GameBackgroundImg); if (IsCustomBG) { string BGPath = GetAppConfigValue("CustomBGPath").ToString(); - LauncherMetadataHelper.CurrentMetadataConfig.GameLauncherApi.GameBackgroundImgLocal = string.IsNullOrEmpty(BGPath) ? AppDefaultBG : BGPath; + if (LauncherMetadataHelper.CurrentMetadataConfig?.GameLauncherApi != null) + { + LauncherMetadataHelper.CurrentMetadataConfig.GameLauncherApi.GameBackgroundImgLocal = + string.IsNullOrEmpty(BGPath) ? AppDefaultBG : BGPath; + } } else if (isAPIBackgroundAvailable) { @@ -491,10 +511,10 @@ internal async void ChangeBackgroundImageAsRegionAsync(bool ShowLoadingMsg = fal } // Use default background if the API background is empty (in-case HoYo did something catchy) - if (!isAPIBackgroundAvailable && !IsCustomBG) + if (!isAPIBackgroundAvailable && !IsCustomBG && LauncherMetadataHelper.CurrentMetadataConfig?.GameLauncherApi != null) LauncherMetadataHelper.CurrentMetadataConfig.GameLauncherApi.GameBackgroundImgLocal = AppDefaultBG; - if (!IsCustomBG || IsFirstStartup) + if ((!IsCustomBG || IsFirstStartup) && LauncherMetadataHelper.CurrentMetadataConfig?.GameLauncherApi != null) { BackgroundImgChanger.ChangeBackground(LauncherMetadataHelper.CurrentMetadataConfig.GameLauncherApi.GameBackgroundImgLocal, () => @@ -573,7 +593,7 @@ private async void RunBackgroundCheck() // Run the update check and trigger routine LauncherUpdateHelper.RunUpdateCheckDetached(); #else - LogWriteLine("Running debug build, stopping update checks!", LogType.Error, false); + LogWriteLine("Running debug build, stopping update checks!", LogType.Error); #endif } catch (JsonException ex) @@ -622,7 +642,7 @@ private async Task FetchNotificationFeed() NotificationData = await networkStream.DeserializeAsync(InternalAppJSONContext.Default, TokenSource.Token); IsLoadNotifComplete = true; - NotificationData.EliminatePushList(); + NotificationData?.EliminatePushList(); } catch (Exception ex) { @@ -632,7 +652,7 @@ private async Task FetchNotificationFeed() private void GenerateLocalAppNotification() { - NotificationData.AppPush.Add(new NotificationProp + NotificationData?.AppPush.Add(new NotificationProp { Show = true, MsgId = 0, @@ -648,7 +668,7 @@ private void GenerateLocalAppNotification() if (IsPreview) { - NotificationData.AppPush.Add(new NotificationProp + NotificationData?.AppPush.Add(new NotificationProp { Show = true, MsgId = -1, @@ -671,7 +691,7 @@ private void GenerateLocalAppNotification() private Button GenerateNotificationButtonStartProcess(string IconGlyph, string PathOrURL, string Text, bool IsUseShellExecute = true) { - return NotificationPush.GenerateNotificationButton(IconGlyph, Text, (s, e) => + return NotificationPush.GenerateNotificationButton(IconGlyph, Text, (_, _) => { new Process { @@ -696,8 +716,8 @@ private async void RunTimeoutCancel(CancellationTokenSource Token) private async Task SpawnPushAppNotification() { - TypedEventHandler ClickCloseAction = null; - if (NotificationData.AppPush == null) return; + TypedEventHandler ClickCloseAction; + if (NotificationData?.AppPush == null) return; foreach (NotificationProp Entry in NotificationData.AppPush) { // Check for Close Action for certain MsgIds @@ -705,11 +725,11 @@ private async Task SpawnPushAppNotification() { case 0: { - ClickCloseAction = new TypedEventHandler((sender, args) => - { - NotificationData.AddIgnoredMsgIds(0); - SaveLocalNotificationData(); - }); + ClickCloseAction = (_, _) => + { + NotificationData?.AddIgnoredMsgIds(0); + SaveLocalNotificationData(); + }; } break; default: @@ -743,17 +763,17 @@ private void SpawnAppUpdatedNotification() { string UpdateNotifFile = Path.Combine(AppDataFolder, "_NewVer"); string NeedInnoUpdateFile = Path.Combine(AppDataFolder, "_NeedInnoLogUpdate"); - TypedEventHandler ClickClose = new TypedEventHandler((sender, args) => - { - File.Delete(UpdateNotifFile); - }); + TypedEventHandler ClickClose = (_, _) => + { + File.Delete(UpdateNotifFile); + }; // If the update was handled by squirrel and it needs Inno Setup Log file to get updated, then do the routine if (File.Exists(NeedInnoUpdateFile)) { try { - string InnoLogPath = Path.Combine(Path.GetDirectoryName(AppFolder), "unins000.dat"); + string InnoLogPath = Path.Combine(Path.GetDirectoryName(AppFolder) ?? string.Empty, "unins000.dat"); if (File.Exists(InnoLogPath)) InnoSetupLogUpdate.UpdateInnoSetupLog(InnoLogPath); File.Delete(NeedInnoUpdateFile); } @@ -798,7 +818,10 @@ private void SpawnAppUpdatedNotification() } } } - catch { } + catch + { + // ignored + } } private InfoBarSeverity NotifSeverity2InfoBarSeverity(NotifSeverity inp) @@ -816,16 +839,16 @@ private InfoBarSeverity NotifSeverity2InfoBarSeverity(NotifSeverity inp) } } - private void SpawnNotificationPush(string Title, string Content, NotifSeverity Severity, int MsgId = 0, bool IsClosable = true, + private void SpawnNotificationPush(string Title, string TextContent, NotifSeverity Severity, int MsgId = 0, bool IsClosable = true, bool Disposable = false, TypedEventHandler CloseClickHandler = null, FrameworkElement OtherContent = null, bool IsAppNotif = true, bool? Show = false, bool ForceShowNotificationPanel = false) { if (!(Show ?? false)) return; - if (NotificationData.CurrentShowMsgIds.Contains(MsgId)) return; + if (NotificationData?.CurrentShowMsgIds.Contains(MsgId) ?? false) return; - if (NotificationData.IsMsgIdIgnored(MsgId)) return; + if (NotificationData?.IsMsgIdIgnored(MsgId) ?? false) return; - NotificationData.CurrentShowMsgIds.Add(MsgId); + NotificationData?.CurrentShowMsgIds.Add(MsgId); DispatcherQueue?.TryEnqueue(() => { @@ -833,13 +856,13 @@ private void SpawnNotificationPush(string Title, string Content, NotifSeverity S InfoBar Notification = new InfoBar { - Title = Title, - Message = Content, - Severity = NotifSeverity2InfoBarSeverity(Severity), - IsClosable = IsClosable, + Title = Title, + Message = TextContent, + Severity = NotifSeverity2InfoBarSeverity(Severity), + IsClosable = IsClosable, IsIconVisible = true, - Shadow = SharedShadow, - IsOpen = true + Shadow = SharedShadow, + IsOpen = true } .WithMargin(4d, 4d, 4d, 0d).WithWidth(600) .WithCornerRadius(8).WithHorizontalAlignment(HorizontalAlignment.Right); @@ -879,7 +902,7 @@ private void SpawnNotificationPush(string Title, string Content, NotifSeverity S private void SpawnNotificationoUI(int tagID, InfoBar Notification) { Grid Container = UIElementExtensions.CreateGrid().WithTag(tagID); - Notification.Loaded += (a, b) => + Notification.Loaded += (_, _) => { NoNotificationIndicator.Opacity = NotificationContainer.Children.Count > 0 ? 0f : 1f; NewNotificationCountBadge.Visibility = Visibility.Visible; @@ -888,16 +911,16 @@ private void SpawnNotificationoUI(int tagID, InfoBar Notification) NotificationPanelClearAllGrid.Visibility = NotificationContainer.Children.Count > 0 ? Visibility.Visible : Visibility.Collapsed; }; - Notification.Closed += (s, a) => + Notification.Closed += (s, _) => { s.Translation -= Shadow32; s.SetHeight(0d); s.SetMargin(0d); int msg = (int)s.Tag; - if (NotificationData.CurrentShowMsgIds.Contains(msg)) + if (NotificationData?.CurrentShowMsgIds.Contains(msg) ?? false) { - NotificationData.CurrentShowMsgIds.Remove(msg); + NotificationData?.CurrentShowMsgIds.Remove(msg); } NotificationContainer.Children.Remove(Container); NoNotificationIndicator.Opacity = NotificationContainer.Children.Count > 0 ? 0f : 1f; @@ -917,11 +940,11 @@ private void SpawnNotificationoUI(int tagID, InfoBar Notification) private void RemoveNotificationUI(int tagID) { - Grid notif = NotificationContainer.Children.OfType().Where(x => (int)x.Tag == tagID).FirstOrDefault(); + Grid notif = NotificationContainer.Children.OfType().FirstOrDefault(x => (int)x.Tag == tagID); if (notif != null) { NotificationContainer.Children.Remove(notif); - InfoBar notifBar = notif.Children.OfType()?.FirstOrDefault(); + InfoBar notifBar = notif.Children.OfType().FirstOrDefault(); if (notifBar != null && notifBar.IsClosable) notifBar.IsOpen = false; } @@ -936,8 +959,8 @@ private async void ClearAllNotification(object sender, RoutedEventArgs args) for (; stackIndex < NotificationContainer.Children.Count;) { if (NotificationContainer.Children[stackIndex] is not Grid container - || container.Children == null || container.Children.Count == 0 - || container.Children[0] is not InfoBar notifBar || notifBar == null + || container.Children == null || container.Children.Count == 0 + || container.Children[0] is not InfoBar notifBar || !notifBar.IsClosable) { ++stackIndex; @@ -962,16 +985,22 @@ private async void ClearAllNotification(object sender, RoutedEventArgs args) private void NeverAskNotif_Checked(object sender, RoutedEventArgs e) { - string[] Data = (sender as CheckBox).Tag.ToString().Split(','); - NotificationData.AddIgnoredMsgIds(int.Parse(Data[0]), bool.Parse(Data[1])); - SaveLocalNotificationData(); + string[] Data = (sender as CheckBox)?.Tag.ToString()?.Split(','); + if (Data != null) + { + NotificationData?.AddIgnoredMsgIds(int.Parse(Data[0]), bool.Parse(Data[1])); + SaveLocalNotificationData(); + } } private void NeverAskNotif_Unchecked(object sender, RoutedEventArgs e) { - string[] Data = (sender as CheckBox).Tag.ToString().Split(','); - NotificationData.RemoveIgnoredMsgIds(int.Parse(Data[0]), bool.Parse(Data[1])); - SaveLocalNotificationData(); + string[] Data = (sender as CheckBox)?.Tag.ToString()?.Split(','); + if (Data != null) + { + NotificationData?.RemoveIgnoredMsgIds(int.Parse(Data[0]), bool.Parse(Data[1])); + SaveLocalNotificationData(); + } } private async void ForceShowNotificationPanel() @@ -1087,7 +1116,7 @@ private async Task CheckMetadataUpdateInBackground() ) .WithMargin(0d, 0d, 0d, 16d); - UpdateMetadatabtn.Loaded += async (a, b) => + UpdateMetadatabtn.Loaded += async (a, _) => { TextBlock Text = new TextBlock { @@ -1102,8 +1131,12 @@ private async Task CheckMetadataUpdateInBackground() StackPanel StackPane = UIElementExtensions.CreateStackPanel(Orientation.Horizontal); StackPane.AddElementToStackPanel(LoadBar); StackPane.AddElementToStackPanel(Text); - (a as Button).Content = StackPane; - (a as Button).IsEnabled = false; + Button aButton = a as Button; + if (aButton != null) + { + aButton.Content = StackPane; + aButton.IsEnabled = false; + } // Put 2 seconds delay before updating int i = 2; @@ -1125,7 +1158,7 @@ private async Task CheckMetadataUpdateInBackground() catch (Exception ex) { LogWriteLine($"Error has occured while updating metadata!\r\n{ex}", LogType.Error, true); - ErrorSender.SendException(ex, ErrorType.Unhandled); + ErrorSender.SendException(ex); } }; SpawnNotificationPush( @@ -1345,7 +1378,7 @@ void Navigate(Type sourceType, string tagStr) internal void InvokeMainPageNavigateByTag(string tagStr) { - NavigationViewItem item = NavigationViewControl.MenuItems.OfType()?.Where(x => x.Tag.GetType() == typeof(string) && (string)x.Tag == tagStr)?.FirstOrDefault(); + NavigationViewItem item = NavigationViewControl.MenuItems.OfType().FirstOrDefault(x => x.Tag is string tag && tag == tagStr); if (item != null) { NavigationViewControl.SelectedItem = item; @@ -1375,18 +1408,18 @@ private async void ShowHideNotificationLostFocusBackground(bool show) { if (show) { - NotificationLostFocusBackground.Visibility = Visibility.Visible; - NotificationLostFocusBackground.Opacity = 0.3; - NotificationPanel.Translation += Shadow48; - ToggleNotificationPanelBtn.Translation -= Shadow16; - (ToggleNotificationPanelBtn.Content as FontIcon).FontFamily = FontCollections.FontAwesomeSolid; + NotificationLostFocusBackground.Visibility = Visibility.Visible; + NotificationLostFocusBackground.Opacity = 0.3; + NotificationPanel.Translation += Shadow48; + ToggleNotificationPanelBtn.Translation -= Shadow16; + ((FontIcon)ToggleNotificationPanelBtn.Content).FontFamily = FontCollections.FontAwesomeSolid; } else { - NotificationLostFocusBackground.Opacity = 0; - NotificationPanel.Translation -= Shadow48; - ToggleNotificationPanelBtn.Translation += Shadow16; - (ToggleNotificationPanelBtn.Content as FontIcon).FontFamily = FontCollections.FontAwesomeRegular; + NotificationLostFocusBackground.Opacity = 0; + NotificationPanel.Translation -= Shadow48; + ToggleNotificationPanelBtn.Translation += Shadow16; + ((FontIcon)ToggleNotificationPanelBtn.Content).FontFamily = FontCollections.FontAwesomeRegular; await Task.Delay(200); NotificationLostFocusBackground.Visibility = Visibility.Collapsed; } @@ -1553,7 +1586,7 @@ private void InitKeyboardShortcuts() true, false, null, - NotificationPush.GenerateNotificationButton("", Lang._AppNotification.NotifKbShortcutBtn, (o, e) => ShowKeybinds_Invoked(null, null)), + NotificationPush.GenerateNotificationButton("", Lang._AppNotification.NotifKbShortcutBtn, (_, _) => ShowKeybinds_Invoked(null, null)), true, true, true @@ -1571,36 +1604,39 @@ private void CreateKeyboardShortcutHandlers() LoadKbShortcuts(); int numIndex = 0; - VirtualKeyModifiers keyModifier = KbShortcutList["GameSelection"].Modifier; - for (; numIndex <= LauncherMetadataHelper.CurrentGameNameCount; numIndex++) + if (KbShortcutList != null) { - KeyboardAccelerator keystroke = new KeyboardAccelerator() + VirtualKeyModifiers keyModifier = KbShortcutList["GameSelection"].Modifier; + for (; numIndex <= LauncherMetadataHelper.CurrentGameNameCount; numIndex++) { - Modifiers = keyModifier, - Key = VirtualKey.Number1 + numIndex, - }; - keystroke.Invoked += KeyboardGameShortcut_Invoked; - KeyboardHandler.KeyboardAccelerators.Add(keystroke); + KeyboardAccelerator keystroke = new KeyboardAccelerator() + { + Modifiers = keyModifier, + Key = VirtualKey.Number1 + numIndex, + }; + keystroke.Invoked += KeyboardGameShortcut_Invoked; + KeyboardHandler.KeyboardAccelerators.Add(keystroke); - KeyboardAccelerator keystrokeNP = new KeyboardAccelerator() - { - Key = VirtualKey.NumberPad1 + numIndex, - }; - keystrokeNP.Invoked += KeyboardGameShortcut_Invoked; - KeyboardHandler.KeyboardAccelerators.Add(keystrokeNP); - } + KeyboardAccelerator keystrokeNP = new KeyboardAccelerator() + { + Key = VirtualKey.NumberPad1 + numIndex, + }; + keystrokeNP.Invoked += KeyboardGameShortcut_Invoked; + KeyboardHandler.KeyboardAccelerators.Add(keystrokeNP); + } - numIndex = 0; - keyModifier = KbShortcutList["RegionSelection"].Modifier; - while (numIndex < LauncherMetadataHelper.CurrentGameRegionMaxCount) - { - KeyboardAccelerator keystroke = new KeyboardAccelerator() + numIndex = 0; + keyModifier = KbShortcutList["RegionSelection"].Modifier; + while (numIndex < LauncherMetadataHelper.CurrentGameRegionMaxCount) { - Modifiers = keyModifier, - Key = VirtualKey.Number1 + numIndex++, - }; - keystroke.Invoked += KeyboardGameRegionShortcut_Invoked; - KeyboardHandler.KeyboardAccelerators.Add(keystroke); + KeyboardAccelerator keystroke = new KeyboardAccelerator() + { + Modifiers = keyModifier, + Key = VirtualKey.Number1 + numIndex++, + }; + keystroke.Invoked += KeyboardGameRegionShortcut_Invoked; + KeyboardHandler.KeyboardAccelerators.Add(keystroke); + } } KeyboardAccelerator keystrokeF5 = new KeyboardAccelerator() @@ -1633,13 +1669,16 @@ private void CreateKeyboardShortcutHandlers() foreach (var func in actions) { - KeyboardAccelerator kbfunc = new KeyboardAccelerator() + if (KbShortcutList != null) { - Modifiers = KbShortcutList[func.Key].Modifier, - Key = KbShortcutList[func.Key].Key - }; - kbfunc.Invoked += func.Value; - KeyboardHandler.KeyboardAccelerators.Add(kbfunc); + KeyboardAccelerator kbfunc = new KeyboardAccelerator() + { + Modifiers = KbShortcutList[func.Key].Modifier, + Key = KbShortcutList[func.Key].Key + }; + kbfunc.Invoked += func.Value; + KeyboardHandler.KeyboardAccelerators.Add(kbfunc); + } } } catch (Exception error) @@ -1664,9 +1703,9 @@ private void RefreshPage_Invoked(KeyboardAccelerator sender, KeyboardAccelerator case "settings": return; default: - string Tag = PreviousTag; + string itemTag = PreviousTag; PreviousTag = "Empty"; - NavigateInnerSwitch(Tag); + NavigateInnerSwitch(itemTag); if (LauncherFrame != null && LauncherFrame.BackStack != null && LauncherFrame.BackStack.Count > 0) LauncherFrame.BackStack.RemoveAt(LauncherFrame.BackStack.Count - 1); if (PreviousTagString != null && PreviousTagString.Count > 0) @@ -1696,9 +1735,7 @@ private void RestoreCurrentRegion() var gameName = GetAppConfigValue("GameCategory").ToString(); #nullable enable List? gameNameCollection = LauncherMetadataHelper.GetGameNameCollection()!; - List? gameRegionCollection = LauncherMetadataHelper.GetGameRegionCollection(gameName)!; - - gameName ??= gameRegionCollection.FirstOrDefault(); + _ = LauncherMetadataHelper.GetGameRegionCollection(gameName)!; var indexCategory = gameNameCollection.IndexOf(gameName!); if (indexCategory < 0) indexCategory = 0; @@ -1758,7 +1795,7 @@ private void KeyboardGameRegionShortcut_Invoked(KeyboardAccelerator sender, Keyb DisableInstantRegionChange = false; } - private async void ShowKeybinds_Invoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args) + private async void ShowKeybinds_Invoked(KeyboardAccelerator? sender, KeyboardAcceleratorInvokedEventArgs? args) { if (CannotUseKbShortcuts) return; @@ -1954,7 +1991,6 @@ private bool SetActivatedRegion() string gameName = args.Game; - #nullable enable List? gameNameCollection = LauncherMetadataHelper.GetGameNameCollection()!; List? gameRegionCollection = LauncherMetadataHelper.GetGameRegionCollection(gameName)!; if (gameRegionCollection == null) @@ -1966,7 +2002,6 @@ private bool SetActivatedRegion() gameRegionCollection = LauncherMetadataHelper.GetGameRegionCollection(gameName)!; } SetAndSaveConfigValue("GameCategory", gameName); - #nullable restore if (args.Region != null) { @@ -1981,7 +2016,6 @@ private bool SetActivatedRegion() int oldGameRegionIndex = LauncherMetadataHelper.GetPreviousGameRegion(gameName); string oldGameRegion = gameRegionCollection.ElementAt(oldGameRegionIndex); - if (oldGameRegion == null) return true; LauncherMetadataHelper.SetPreviousGameRegion(gameName, gameRegion); SetAndSaveConfigValue("GameRegion", gameRegion); @@ -2009,7 +2043,7 @@ private async void ChangeToActivatedRegion() { #if !DISABLEDISCORD if (GetAppConfigValue("EnableDiscordRPC").ToBool() && !sameRegion) - AppDiscordPresence.SetupPresence(); + AppDiscordPresence?.SetupPresence(); #endif InvokeLoadingRegionPopup(false); LauncherFrame.BackStack.Clear(); diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs index 6ec6b0203..e28edd71e 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs @@ -933,7 +933,11 @@ private async void CheckRunningGameInstance(CancellationToken Token) StartGameBtn.IsEnabled = true; StartGameBtnText!.Text = Lang._HomePage.StartBtn; StartGameBtnIcon.Glyph = StartGameBtnIconGlyph; - StartGameBtnAnimatedIconGrid.Opacity = 1; + if (StartGameBtnAnimatedIconGrid != null) + { + StartGameBtnAnimatedIconGrid.Opacity = 1; + } + StartGameBtnIcon.Opacity = 0; GameStartupSetting.IsEnabled = true; diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/SettingsPage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/SettingsPage.xaml.cs index fa7f1d631..a9c5d86d6 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/SettingsPage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/SettingsPage.xaml.cs @@ -28,7 +28,6 @@ using static CollapseLauncher.Dialogs.SimpleDialogs; using static CollapseLauncher.Helper.Image.Waifu2X; using static CollapseLauncher.InnerLauncherConfig; - using static CollapseLauncher.RegionResourceListHelper; using static CollapseLauncher.WindowSize.WindowSize; using static CollapseLauncher.FileDialogCOM.FileDialogNative; using static Hi3Helper.Locale; @@ -38,6 +37,7 @@ using TaskSched = Microsoft.Win32.TaskScheduler.Task; using Task = System.Threading.Tasks.Task; +// ReSharper disable CheckNamespace // ReSharper disable PossibleNullReferenceException // ReSharper disable AssignNullToNotNullAttribute @@ -66,7 +66,7 @@ public SettingsPage() AboutApp.FindAndSetTextBlockWrapping(TextWrapping.Wrap, HorizontalAlignment.Center, TextAlignment.Center, true); LoadAppConfig(); - this.DataContext = this; + DataContext = this; string Version = $" {LauncherUpdateHelper.LauncherCurrentVersionString}"; #if DEBUG @@ -171,7 +171,7 @@ private async void ClearMetadataFolder(object sender, RoutedEventArgs e) try { var collapsePath = Process.GetCurrentProcess().MainModule?.FileName; - if (collapsePath == null || LauncherMetadataHelper.LauncherMetadataFolder == null) return; + if (collapsePath == null) return; Directory.Delete(LauncherMetadataHelper.LauncherMetadataFolder, true); Process.Start(collapsePath); (WindowUtility.CurrentWindow as MainWindow)?.CloseApp(); @@ -191,9 +191,9 @@ private async void ClearMetadataFolder(object sender, RoutedEventArgs e) private void OpenAppDataFolder(object sender, RoutedEventArgs e) { - new Process() + new Process { - StartInfo = new ProcessStartInfo() + StartInfo = new ProcessStartInfo { UseShellExecute = true, FileName = "explorer.exe", @@ -295,11 +295,18 @@ private async void CheckUpdate(object sender, RoutedEventArgs e) LauncherUpdateInvoker.UpdateEvent += LauncherUpdateInvoker_UpdateEvent; bool isUpdateAvailable = await LauncherUpdateHelper.IsUpdateAvailable(true); - LauncherUpdateWatcher.GetStatus(new LauncherUpdateProperty { IsUpdateAvailable = isUpdateAvailable, NewVersionName = LauncherUpdateHelper.AppUpdateVersionProp.Version.Value }); + if (LauncherUpdateHelper.AppUpdateVersionProp.Version != null) + { + LauncherUpdateWatcher.GetStatus(new LauncherUpdateProperty + { + IsUpdateAvailable = isUpdateAvailable, + NewVersionName = LauncherUpdateHelper.AppUpdateVersionProp.Version.Value + }); + } } catch (Exception ex) { - ErrorSender.SendException(ex, ErrorType.Unhandled); + ErrorSender.SendException(ex); UpdateLoadingStatus.Visibility = Visibility.Collapsed; UpdateAvailableStatus.Visibility = Visibility.Collapsed; UpToDateStatus.Visibility = Visibility.Collapsed; @@ -406,7 +413,7 @@ private TaskSched CreateScheduledTask(string taskName) taskDefinition.Principal.RunLevel = TaskRunLevel.Highest; taskDefinition.Settings.Enabled = false; taskDefinition.Triggers.Add(new LogonTrigger()); - taskDefinition.Actions.Add(new ExecAction(collapseStartupTarget, null, null)); + taskDefinition.Actions.Add(new ExecAction(collapseStartupTarget)); TaskSched task = TaskService.Instance.RootFolder.RegisterTaskDefinition(taskName, taskDefinition); taskDefinition.Dispose(); @@ -490,7 +497,7 @@ private bool IsBGCustom } } BGPathDisplay.Text = LauncherMetadataHelper.CurrentMetadataConfig.GameLauncherApi.GameBackgroundImgLocal; - BackgroundImgChanger.ChangeBackground(LauncherMetadataHelper.CurrentMetadataConfig.GameLauncherApi.GameBackgroundImgLocal, null, true, true, false); + BackgroundImgChanger.ChangeBackground(LauncherMetadataHelper.CurrentMetadataConfig.GameLauncherApi.GameBackgroundImgLocal, null, true, true); AppBGCustomizer.Visibility = Visibility.Visible; AppBGCustomizerNote.Visibility = Visibility.Visible; @@ -979,7 +986,7 @@ private bool IsStartupToTray TaskSched task = ts.GetTask(_collapseStartupTaskName); task.Definition.Actions.Clear(); - task.Definition.Actions.Add(new ExecAction(collapseStartupTarget, value ? "tray" : null, null)); + task.Definition.Actions.Add(new ExecAction(collapseStartupTarget, value ? "tray" : null)); task.RegisterChanges(); task.Dispose(); } diff --git a/Hi3Helper.Core/Classes/Logger/LoggerBase.cs b/Hi3Helper.Core/Classes/Logger/LoggerBase.cs index ce4fbb814..eee3f582d 100644 --- a/Hi3Helper.Core/Classes/Logger/LoggerBase.cs +++ b/Hi3Helper.Core/Classes/Logger/LoggerBase.cs @@ -3,6 +3,7 @@ using System.Text; #if !APPLYUPDATE using Hi3Helper.Shared.Region; +// ReSharper disable CheckNamespace #endif namespace Hi3Helper @@ -39,7 +40,7 @@ public void SetFolderPathAndInitialize(string folderPath, Encoding logEncoding) #if !APPLYUPDATE // Check if the directory exist. If not, then create. - if (!Directory.Exists(_logFolder)) + if (!string.IsNullOrEmpty(_logFolder) && !Directory.Exists(_logFolder)) { Directory.CreateDirectory(_logFolder); } From cc3e51369d4a0cf1df3f29beb9aaa0798b8822f5 Mon Sep 17 00:00:00 2001 From: Kemal Setya Adhi Date: Sat, 13 Jul 2024 22:10:53 +0700 Subject: [PATCH 41/44] Fix build I'm stoopid --- CollapseLauncher/Classes/GamePropertyVault.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CollapseLauncher/Classes/GamePropertyVault.cs b/CollapseLauncher/Classes/GamePropertyVault.cs index b0e75a966..55ad2ae3a 100644 --- a/CollapseLauncher/Classes/GamePropertyVault.cs +++ b/CollapseLauncher/Classes/GamePropertyVault.cs @@ -180,11 +180,11 @@ private static void RegisterGameProperty(UIElement UIElementParent, RegionResour GamePresetProperty Property = new GamePresetProperty(UIElementParent, APIResouceProp, GameName, GameRegion); Vault.Add(GamePreset.HashID, Property); - #if DEBUG +#if DEBUG Logger.LogWriteLine($"[GamePropertyVault] Creating & caching game property by Hash ID: {GamePreset.HashID}", LogType.Debug, true); +#endif } - #endif } #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously From 6291521a15c91fe973cea0abc9c771a6d4d25839 Mon Sep 17 00:00:00 2001 From: Kemal Setya Adhi Date: Sat, 13 Jul 2024 22:35:59 +0700 Subject: [PATCH 42/44] Adding more localization strings --- .../Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml | 8 ++++---- Hi3Helper.Core/Lang/Locale/LangGameSettingsPage.cs | 4 ++++ Hi3Helper.Core/Lang/en_US.json | 5 +++++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml index b26c164b7..5a74eeccd 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml +++ b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml @@ -53,7 +53,7 @@ Checked="EnforceCustomPreset_Checkbox" IsChecked="{x:Bind EnableVSync, Mode=TwoWay}" Unchecked="EnforceCustomPreset_Checkbox"> - @@ -235,7 +235,7 @@ - + @@ -243,7 +243,7 @@ Margin="0,0,0,8" HorizontalAlignment="Left" Style="{ThemeResource BodyStrongTextBlockStyle}" - Text="{x:Bind helper:Locale.Lang._StarRailGameSettingsPage.Graphics_FPS}" + Text="{x:Bind helper:Locale.Lang._GameSettingsPage.Graphics_FPS}" TextWrapping="WrapWholeWords" /> - + diff --git a/Hi3Helper.Core/Lang/Locale/LangGameSettingsPage.cs b/Hi3Helper.Core/Lang/Locale/LangGameSettingsPage.cs index 530b6a16a..0b03d3a1b 100644 --- a/Hi3Helper.Core/Lang/Locale/LangGameSettingsPage.cs +++ b/Hi3Helper.Core/Lang/Locale/LangGameSettingsPage.cs @@ -20,9 +20,12 @@ public sealed class LangGameSettingsPage public string Graphics_ResCustom { get; set; } = LangFallback?._GameSettingsPage.Graphics_ResCustom; public string Graphics_ResCustomW { get; set; } = LangFallback?._GameSettingsPage.Graphics_ResCustomW; public string Graphics_ResCustomH { get; set; } = LangFallback?._GameSettingsPage.Graphics_ResCustomH; + public string Graphics_VSync { get; set; } = LangFallback?._GameSettingsPage.Graphics_VSync; + public string Graphics_FPS { get; set; } = LangFallback?._GameSettingsPage.Graphics_FPS; public string Graphics_FPSPanel { get; set; } = LangFallback?._GameSettingsPage.Graphics_FPSPanel; public string Graphics_FPSInCombat { get; set; } = LangFallback?._GameSettingsPage.Graphics_FPSInCombat; public string Graphics_FPSInMenu { get; set; } = LangFallback?._GameSettingsPage.Graphics_FPSInMenu; + public string Graphics_FPSUnlimited { get; set; } = LangFallback?._GameSettingsPage.Graphics_FPSUnlimited; public string Graphics_APIPanel { get; set; } = LangFallback?._GameSettingsPage.Graphics_APIPanel; public string Graphics_APIHelp1 { get; set; } = LangFallback?._GameSettingsPage.Graphics_APIHelp1; public string Graphics_APIHelp2 { get; set; } = LangFallback?._GameSettingsPage.Graphics_APIHelp2; @@ -53,6 +56,7 @@ public sealed class LangGameSettingsPage public string Graphics_Legacy_Title { get; set; } = LangFallback?._GameSettingsPage.Graphics_Legacy_Title; public string Graphics_Legacy_Subtitle { get; set; } = LangFallback?._GameSettingsPage.Graphics_Legacy_Subtitle; public string SpecDisabled { get; set; } = LangFallback?._GameSettingsPage.SpecDisabled; + public string SpecCustom { get; set; } = LangFallback?._GameSettingsPage.SpecCustom; public string SpecLow { get; set; } = LangFallback?._GameSettingsPage.SpecLow; public string SpecMedium { get; set; } = LangFallback?._GameSettingsPage.SpecMedium; public string SpecHigh { get; set; } = LangFallback?._GameSettingsPage.SpecHigh; diff --git a/Hi3Helper.Core/Lang/en_US.json b/Hi3Helper.Core/Lang/en_US.json index 8c1e15966..3aacd4bf6 100644 --- a/Hi3Helper.Core/Lang/en_US.json +++ b/Hi3Helper.Core/Lang/en_US.json @@ -270,6 +270,10 @@ "Graphics_ResCustomW": "W", "Graphics_ResCustomH": "H", + "Graphics_VSync": "VSync", + + "Graphics_FPS": "FPS", + "Graphics_FPSUnlimited": "Unlimited", "Graphics_FPSPanel": "Maximum FPS", "Graphics_FPSInCombat": "In-Combat", "Graphics_FPSInMenu": "Main Menu", @@ -309,6 +313,7 @@ "Graphics_Legacy_Subtitle": "These settings are not present in-game and might not have any effect.", "SpecDisabled": "Disabled", + "SpecCustom": "Custom", "SpecLow": "Low", "SpecMedium": "Medium", "SpecHigh": "High", From 3f6dc10a25d3f39c380eb64d73f99939fcd018bf Mon Sep 17 00:00:00 2001 From: Kemal Setya Adhi Date: Sun, 14 Jul 2024 00:02:48 +0700 Subject: [PATCH 43/44] Fix missing in-code localization --- .../Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs | 4 ++-- Hi3Helper.Core/Lang/Locale/LangGameSettingsPage.cs | 2 ++ Hi3Helper.Core/Lang/en_US.json | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs index 945d39340..f4f18ca48 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs @@ -206,7 +206,7 @@ private List GetResPairs_Fullscreen() { int w = (int)(h * nativeAspRatio); // TODO: remove identifier - resPairs.Add($"{w}x{h} Fullscreen"); + resPairs.Add(string.Format(Lang._GameSettingsPage.Graphics_ResPrefixFullscreen, w, h)); } return resPairs; @@ -232,7 +232,7 @@ private List GetResPairs_Windowed() { int w = (int)(h * wideRatio); // TODO: remove identifier - resPairs.Add($"{w}x{h} Windowed"); + resPairs.Add(string.Format(Lang._GameSettingsPage.Graphics_ResPrefixWindowed, w, h)); } return resPairs; diff --git a/Hi3Helper.Core/Lang/Locale/LangGameSettingsPage.cs b/Hi3Helper.Core/Lang/Locale/LangGameSettingsPage.cs index 0b03d3a1b..fe305638a 100644 --- a/Hi3Helper.Core/Lang/Locale/LangGameSettingsPage.cs +++ b/Hi3Helper.Core/Lang/Locale/LangGameSettingsPage.cs @@ -20,6 +20,8 @@ public sealed class LangGameSettingsPage public string Graphics_ResCustom { get; set; } = LangFallback?._GameSettingsPage.Graphics_ResCustom; public string Graphics_ResCustomW { get; set; } = LangFallback?._GameSettingsPage.Graphics_ResCustomW; public string Graphics_ResCustomH { get; set; } = LangFallback?._GameSettingsPage.Graphics_ResCustomH; + public string Graphics_ResPrefixFullscreen { get; set; } = LangFallback?._GameSettingsPage.Graphics_ResPrefixFullscreen; + public string Graphics_ResPrefixWindowed { get; set; } = LangFallback?._GameSettingsPage.Graphics_ResPrefixWindowed; public string Graphics_VSync { get; set; } = LangFallback?._GameSettingsPage.Graphics_VSync; public string Graphics_FPS { get; set; } = LangFallback?._GameSettingsPage.Graphics_FPS; public string Graphics_FPSPanel { get; set; } = LangFallback?._GameSettingsPage.Graphics_FPSPanel; diff --git a/Hi3Helper.Core/Lang/en_US.json b/Hi3Helper.Core/Lang/en_US.json index 3aacd4bf6..cc16a9d1a 100644 --- a/Hi3Helper.Core/Lang/en_US.json +++ b/Hi3Helper.Core/Lang/en_US.json @@ -269,6 +269,8 @@ "Graphics_ResCustom": "Use Custom Resolution", "Graphics_ResCustomW": "W", "Graphics_ResCustomH": "H", + "Graphics_ResPrefixFullscreen": "{0}x{1} Fullscreen", + "Graphics_ResPrefixWindowed": "{0}x{1} Windowed", "Graphics_VSync": "VSync", From c55c27b1583b0de287880e78af88038644b21209 Mon Sep 17 00:00:00 2001 From: Kemal Setya Adhi Date: Sun, 14 Jul 2024 00:04:19 +0700 Subject: [PATCH 44/44] Remove TODO comments --- .../Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs index f4f18ca48..687ad1c5e 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs @@ -205,7 +205,6 @@ private List GetResPairs_Fullscreen() foreach (var h in acH) { int w = (int)(h * nativeAspRatio); - // TODO: remove identifier resPairs.Add(string.Format(Lang._GameSettingsPage.Graphics_ResPrefixFullscreen, w, h)); } @@ -231,7 +230,6 @@ private List GetResPairs_Windowed() foreach (var h in acH) { int w = (int)(h * wideRatio); - // TODO: remove identifier resPairs.Add(string.Format(Lang._GameSettingsPage.Graphics_ResPrefixWindowed, w, h)); }