From e7c8d7f949e08d4299e6848e988ce95e9d2fccf2 Mon Sep 17 00:00:00 2001 From: Kemal Setya Adhi Date: Thu, 4 Apr 2024 01:58:42 +0700 Subject: [PATCH] [Big Changes] Switch to MetadataV3 --- .../Classes/CachesManagement/Honkai/Fetch.cs | 29 +- .../CachesManagement/Honkai/HonkaiCache.cs | 1 + CollapseLauncher/Classes/ClassesContext.cs | 13 +- CollapseLauncher/Classes/CoCreateInstance.cs | 1 - .../DiscordPresence/DiscordPresenceManager.cs | 20 +- .../BackgroundActivityManager.cs | 10 +- .../Honkai/RegistryClass/Preset.cs | 6 +- .../GameSettings/Honkai/Settings.cs | 4 +- .../GameVersion/BaseClass/GameVersionBase.cs | 16 +- .../GameVersion/Genshin/VersionCheck.cs | 6 +- .../GameVersion/Honkai/VersionCheck.cs | 4 +- .../GameVersion/StarRail/VersionCheck.cs | 6 +- CollapseLauncher/Classes/GamePropertyVault.cs | 31 +- .../Classes/Helper/Metadata/BHI3LInfo.cs | 20 + .../Classes/Helper/Metadata/DataCooker.cs | 160 +++++ .../Helper/Metadata/GameDataTemplate.cs | 10 + .../Helper/Metadata/GameDataVersion.cs | 33 + .../Helper/Metadata/GeneralDataProp.cs | 103 +++ .../Classes/Helper/Metadata/Hashable.cs | 7 + .../Metadata/JsonConverter/Extension.cs | 43 ++ .../JsonConverter/ServeV3Converter.cs | 73 +++ .../Helper/Metadata/LauncherMetadataHelper.cs | 461 +++++++++++++ .../Helper/Metadata/MasterKeyConfig.cs | 9 + .../Classes/Helper/Metadata/MetadataType.cs | 12 + .../Classes/Helper/Metadata/PresetConfig.cs | 446 +++++++++++++ .../Classes/Helper/Metadata/Stamp.cs | 15 + .../Classes/Helper/Metadata/SteamGameProp.cs | 9 + .../BaseClass/InstallManagerBase.cs | 4 +- .../GameConversionManagement.cs | 7 +- .../Class/CommunityToolsProperty.cs | 7 +- .../Classes/Interfaces/Class/Structs.cs | 4 +- .../Classes/Interfaces/IGameVersionCheck.cs | 6 +- .../Classes/Properties/InnerLauncherConfig.cs | 68 +- .../RegionManagement/RegionManagement.cs | 41 +- .../Classes/RepairManagement/Genshin/Fetch.cs | 4 +- .../ShortcutCreator/ShortcutCreator.cs | 18 +- .../Classes/ShortcutCreator/SteamShortcut.cs | 14 +- .../ShortcutCreator/SteamShortcutParser.cs | 6 +- CollapseLauncher/CollapseLauncher.csproj | 60 ++ .../XAMLs/Invoker/Classes/Migrate.cs | 4 +- CollapseLauncher/XAMLs/MainApp/MainPage.xaml | 4 +- .../XAMLs/MainApp/MainPage.xaml.cs | 147 +++-- .../XAMLs/MainApp/Pages/CachesPage.xaml.cs | 8 +- .../Pages/Dialogs/InstallationConvert.xaml.cs | 27 +- .../Pages/Dialogs/KeyboardShortcuts.cs | 5 +- .../MainApp/Pages/Dialogs/SimpleDialogs.cs | 12 +- .../GenshinGameSettingsPage.xaml.cs | 8 +- .../HonkaiGameSettingsPage.xaml.cs | 8 +- .../StarRailGameSettingsPage.xaml.cs | 10 +- .../XAMLs/MainApp/Pages/HomePage.Variable.cs | 11 +- .../XAMLs/MainApp/Pages/HomePage.xaml.cs | 34 +- .../MainApp/Pages/OOBE/OOBESelectGame.xaml.cs | 10 +- .../Pages/OOBE/OOBESelectGameBG.xaml.cs | 4 +- .../Pages/OOBE/OOBEStartUpMenu.xaml.cs | 32 +- .../XAMLs/MainApp/Pages/RepairPage.xaml.cs | 10 +- .../XAMLs/MainApp/Pages/SettingsPage.xaml.cs | 11 +- .../XAMLs/MainApp/Pages/StartupPage.xaml.cs | 27 +- .../Classes/Preset/Classes/ClassesContext.cs | 6 - .../Preset/Classes/PresetConfigClasses.cs | 14 - .../Classes/Preset/Classes/PresetConfigV2.cs | 615 ------------------ .../Classes/Shared/Region/ConfigV2Store.cs | 135 +--- .../Classes/Shared/Region/LauncherConfig.cs | 14 +- Hi3Helper.EncTool | 2 +- 63 files changed, 1798 insertions(+), 1147 deletions(-) rename {Hi3Helper.Core => CollapseLauncher}/Classes/DiscordPresence/DiscordPresenceManager.cs (91%) create mode 100644 CollapseLauncher/Classes/Helper/Metadata/BHI3LInfo.cs create mode 100644 CollapseLauncher/Classes/Helper/Metadata/DataCooker.cs create mode 100644 CollapseLauncher/Classes/Helper/Metadata/GameDataTemplate.cs create mode 100644 CollapseLauncher/Classes/Helper/Metadata/GameDataVersion.cs create mode 100644 CollapseLauncher/Classes/Helper/Metadata/GeneralDataProp.cs create mode 100644 CollapseLauncher/Classes/Helper/Metadata/Hashable.cs create mode 100644 CollapseLauncher/Classes/Helper/Metadata/JsonConverter/Extension.cs create mode 100644 CollapseLauncher/Classes/Helper/Metadata/JsonConverter/ServeV3Converter.cs create mode 100644 CollapseLauncher/Classes/Helper/Metadata/LauncherMetadataHelper.cs create mode 100644 CollapseLauncher/Classes/Helper/Metadata/MasterKeyConfig.cs create mode 100644 CollapseLauncher/Classes/Helper/Metadata/MetadataType.cs create mode 100644 CollapseLauncher/Classes/Helper/Metadata/PresetConfig.cs create mode 100644 CollapseLauncher/Classes/Helper/Metadata/Stamp.cs create mode 100644 CollapseLauncher/Classes/Helper/Metadata/SteamGameProp.cs delete mode 100644 Hi3Helper.Core/Classes/Preset/Classes/PresetConfigClasses.cs delete mode 100644 Hi3Helper.Core/Classes/Preset/Classes/PresetConfigV2.cs diff --git a/CollapseLauncher/Classes/CachesManagement/Honkai/Fetch.cs b/CollapseLauncher/Classes/CachesManagement/Honkai/Fetch.cs index fc1487d86..8daf30e8a 100644 --- a/CollapseLauncher/Classes/CachesManagement/Honkai/Fetch.cs +++ b/CollapseLauncher/Classes/CachesManagement/Honkai/Fetch.cs @@ -1,4 +1,5 @@ -using CollapseLauncher.Interfaces; +using CollapseLauncher.Helper.Metadata; +using CollapseLauncher.Interfaces; using Hi3Helper; using Hi3Helper.EncTool; using Hi3Helper.EncTool.Parser.KianaDispatch; @@ -14,7 +15,6 @@ using static Hi3Helper.Data.ConverterTool; using static Hi3Helper.Locale; using static Hi3Helper.Logger; -using static Hi3Helper.Preset.ConfigV2Store; namespace CollapseLauncher { @@ -86,15 +86,9 @@ private async Task BuildGameRepoURL(CancellationToken token) try { // Init the key and decrypt it if exist. - string key = null; - if (_gameVersionManager.GamePreset.DispatcherKey != null) - { - mhyEncTool Decryptor = new mhyEncTool(); - Decryptor.InitMasterKey(ConfigV2!.MasterKey, ConfigV2.MasterKeyBitLength, RSAEncryptionPadding.Pkcs1); - - key = _gameVersionManager.GamePreset.DispatcherKey; - Decryptor.DecryptStringWithMasterKey(ref key); - } + if (string.IsNullOrEmpty(_gameVersionManager.GamePreset.DispatcherKey)) + throw new NullReferenceException($"Dispatcher key is null or empty!"); + string key = _gameVersionManager.GamePreset.DispatcherKey; // Try assign dispatcher dispatch = await KianaDispatch.GetDispatch(baseURL, _gameVersionManager.GamePreset.GameDispatchURLTemplate, _gameVersionManager.GamePreset.GameDispatchChannelName, key, _gameVersion.VersionArray, token); @@ -239,7 +233,18 @@ private void BuildDataPatchConfig(MemoryStream stream, List assetInd private byte[] GetAssetIndexSalt(string data) { // Get the salt from the string and return as byte[] - mhyEncTool saltTool = new mhyEncTool(data, ConfigV2!.MasterKey); + byte[] key; + if (DataCooker.IsServeV3Data(LauncherMetadataHelper.CurrentMasterKey.Key)) + { + DataCooker.GetServeV3DataSize(LauncherMetadataHelper.CurrentMasterKey.Key, out long keyCompSize, out long keyDecompSize); + key = new byte[keyCompSize]; + DataCooker.ServeV3Data(LauncherMetadataHelper.CurrentMasterKey.Key, key, (int)keyCompSize, (int)keyDecompSize, out _); + } + else + { + key = LauncherMetadataHelper.CurrentMasterKey.Key; + } + mhyEncTool saltTool = new mhyEncTool(data, key); return saltTool.GetSalt(); } diff --git a/CollapseLauncher/Classes/CachesManagement/Honkai/HonkaiCache.cs b/CollapseLauncher/Classes/CachesManagement/Honkai/HonkaiCache.cs index 0a0ef26c7..3ac9d2cfa 100644 --- a/CollapseLauncher/Classes/CachesManagement/Honkai/HonkaiCache.cs +++ b/CollapseLauncher/Classes/CachesManagement/Honkai/HonkaiCache.cs @@ -46,6 +46,7 @@ private async Task CheckRoutine() // Reset status and progress // ResetStatusAndProgress(); + _token = new CancellationTokenSourceWrapper(); // Step 1: Fetch asset indexes _assetIndex = await Fetch(_token!.Token); diff --git a/CollapseLauncher/Classes/ClassesContext.cs b/CollapseLauncher/Classes/ClassesContext.cs index da1254c10..c94d3acb8 100644 --- a/CollapseLauncher/Classes/ClassesContext.cs +++ b/CollapseLauncher/Classes/ClassesContext.cs @@ -1,18 +1,25 @@ -using CollapseLauncher.Interfaces; +using CollapseLauncher.Helper.Metadata; +using CollapseLauncher.Interfaces; using Hi3Helper.EncTool.Parser.AssetMetadata; using Hi3Helper.Shared.ClassStruct; +using System.Collections.Generic; using System.Text.Json.Serialization; namespace CollapseLauncher { - [JsonSourceGenerationOptions(IncludeFields = false, GenerationMode = JsonSourceGenerationMode.Metadata, IgnoreReadOnlyFields = true)] + // [JsonSourceGenerationOptions(IncludeFields = false, GenerationMode = JsonSourceGenerationMode.Metadata, IgnoreReadOnlyFields = true)] [JsonSerializable(typeof(RegionResourcePluginValidate))] [JsonSerializable(typeof(CommunityToolsProperty))] [JsonSerializable(typeof(AppUpdateVersionProp))] [JsonSerializable(typeof(RegionResourceProp))] [JsonSerializable(typeof(NotificationPush))] - [JsonSerializable(typeof(CacheAsset))] + [JsonSerializable(typeof(GeneralDataProp))] + [JsonSerializable(typeof(MasterKeyConfig))] [JsonSerializable(typeof(AudioPCKType[]))] + [JsonSerializable(typeof(PresetConfig))] + [JsonSerializable(typeof(List))] + [JsonSerializable(typeof(CacheAsset))] + [JsonSerializable(typeof(BHI3LInfo))] [JsonSerializable(typeof(int[]))] internal sealed partial class InternalAppJSONContext : JsonSerializerContext; } diff --git a/CollapseLauncher/Classes/CoCreateInstance.cs b/CollapseLauncher/Classes/CoCreateInstance.cs index 8872c53fd..feb6e7c78 100644 --- a/CollapseLauncher/Classes/CoCreateInstance.cs +++ b/CollapseLauncher/Classes/CoCreateInstance.cs @@ -2,7 +2,6 @@ using System; using System.Diagnostics; using System.Globalization; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; #pragma warning disable IL2050 diff --git a/Hi3Helper.Core/Classes/DiscordPresence/DiscordPresenceManager.cs b/CollapseLauncher/Classes/DiscordPresence/DiscordPresenceManager.cs similarity index 91% rename from Hi3Helper.Core/Classes/DiscordPresence/DiscordPresenceManager.cs rename to CollapseLauncher/Classes/DiscordPresence/DiscordPresenceManager.cs index c4f73b9b2..343d35290 100644 --- a/Hi3Helper.Core/Classes/DiscordPresence/DiscordPresenceManager.cs +++ b/CollapseLauncher/Classes/DiscordPresence/DiscordPresenceManager.cs @@ -1,11 +1,12 @@ #if !DISABLEDISCORD +using CollapseLauncher.Helper.Metadata; using DiscordRPC; -using Hi3Helper.Preset; +using Hi3Helper; using System; using static Hi3Helper.Locale; using static Hi3Helper.Shared.Region.LauncherConfig; -namespace Hi3Helper.DiscordPresence +namespace CollapseLauncher.DiscordPresence { #region Enums public enum ActivityType @@ -21,7 +22,6 @@ public enum ActivityType } #endregion - public class DiscordPresenceManager : IDisposable { #region Properties @@ -231,12 +231,12 @@ private void BuildActivityGameStatus(string activityName, bool isGameStatusEnabl { _presence = new RichPresence { - Details = $"{activityName} {(!isGameStatusEnabled ? ConfigV2Store.CurrentConfigV2GameCategory : null)}", - State = $"{Lang._Misc.DiscordRP_Region} {ConfigV2Store.CurrentConfigV2GameRegion}", + Details = $"{activityName} {(!isGameStatusEnabled ? LauncherMetadataHelper.CurrentMetadataConfigGameName : null)}", + State = $"{Lang._Misc.DiscordRP_Region} {LauncherMetadataHelper.CurrentMetadataConfigGameRegion}", Assets = new Assets { - LargeImageKey = $"game-{ConfigV2Store.CurrentConfigV2.GameType.ToString().ToLower()}-logo", - LargeImageText = $"{ConfigV2Store.CurrentConfigV2GameCategory} - {ConfigV2Store.CurrentConfigV2GameRegion}", + LargeImageKey = $"game-{LauncherMetadataHelper.CurrentMetadataConfig.GameType.ToString().ToLower()}-logo", + LargeImageText = $"{LauncherMetadataHelper.CurrentMetadataConfigGameName} - {LauncherMetadataHelper.CurrentMetadataConfigGameRegion}", SmallImageKey = "launcher-logo", SmallImageText = $"Collapse Launcher v{AppCurrentVersionString} {(IsPreview ? "Preview" : "Stable")}" }, @@ -258,11 +258,11 @@ private void BuildActivityAppStatus(string activityName) _presence = new RichPresence() { Details = activityName, - State = $"{Lang._Misc.DiscordRP_Region} {ConfigV2Store.CurrentConfigV2GameRegion}", + State = $"{Lang._Misc.DiscordRP_Region} {LauncherMetadataHelper.CurrentMetadataConfigGameRegion}", Assets = new Assets { - LargeImageKey = $"game-{ConfigV2Store.CurrentConfigV2.GameType.ToString().ToLower()}-logo", - LargeImageText = $"{ConfigV2Store.CurrentConfigV2GameCategory}", + LargeImageKey = $"game-{LauncherMetadataHelper.CurrentMetadataConfig.GameType.ToString().ToLower()}-logo", + LargeImageText = $"{LauncherMetadataHelper.CurrentMetadataConfigGameName}", SmallImageKey = "launcher-logo", SmallImageText = $"Collapse Launcher v{AppCurrentVersionString} {(IsPreview ? "Preview" : "Stable")}" }, diff --git a/CollapseLauncher/Classes/EventsManagement/BackgroundActivityManager.cs b/CollapseLauncher/Classes/EventsManagement/BackgroundActivityManager.cs index 094942990..0c241bca3 100644 --- a/CollapseLauncher/Classes/EventsManagement/BackgroundActivityManager.cs +++ b/CollapseLauncher/Classes/EventsManagement/BackgroundActivityManager.cs @@ -1,9 +1,9 @@ using CollapseLauncher.Extension; +using CollapseLauncher.Helper.Metadata; using CollapseLauncher.Interfaces; using CollapseLauncher.Statics; using Hi3Helper; using Hi3Helper.Data; -using Hi3Helper.Preset; using Hi3Helper.Shared.Region; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; @@ -92,10 +92,10 @@ private static void AttachEventToNotification(int hashID, IBackgroundActivity ac { Source = new BitmapImage(new Uri(CurrentGameProperty!._GameVersion!.GameType switch { - GameType.Honkai => "ms-appx:///Assets/Images/GameLogo/honkai-logo.png", - GameType.Genshin => "ms-appx:///Assets/Images/GameLogo/genshin-logo.png", - GameType.StarRail => "ms-appx:///Assets/Images/GameLogo/starrail-logo.png", - GameType.Zenless => "ms-appx:///Assets/Images/GameLogo/zenless-logo.png", + GameNameType.Honkai => "ms-appx:///Assets/Images/GameLogo/honkai-logo.png", + GameNameType.Genshin => "ms-appx:///Assets/Images/GameLogo/genshin-logo.png", + GameNameType.StarRail => "ms-appx:///Assets/Images/GameLogo/starrail-logo.png", + GameNameType.Zenless => "ms-appx:///Assets/Images/GameLogo/zenless-logo.png", _ => "ms-appx:///Assets/Images/GameMascot/PaimonWhat.png" })) }.WithWidthAndHeight(64)); diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Honkai/RegistryClass/Preset.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Honkai/RegistryClass/Preset.cs index 299aebb64..ec7b20b18 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Honkai/RegistryClass/Preset.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Honkai/RegistryClass/Preset.cs @@ -1,5 +1,5 @@ -using CollapseLauncher.Interfaces; -using Hi3Helper.Preset; +using CollapseLauncher.Helper.Metadata; +using CollapseLauncher.Interfaces; using Microsoft.Win32; using System; using System.Collections.Generic; @@ -66,7 +66,7 @@ public Preset(string presetJSONPath, TObjectType jsonContext) /// The type of the game /// JSON source generation context /// The instance of preset - public static Preset LoadPreset(GameType gameType, TObjectType jsonContext) + public static Preset LoadPreset(GameNameType gameType, TObjectType jsonContext) { string presetPath = Path.Combine(AppFolder, $"Assets\\Presets\\{gameType}\\", $"{typeof(T1).Name}.json"); return new Preset(presetPath, jsonContext); diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Honkai/Settings.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Honkai/Settings.cs index 1ed93771c..6e94cbba2 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Honkai/Settings.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Honkai/Settings.cs @@ -1,8 +1,8 @@ using CollapseLauncher.GameSettings.Base; using CollapseLauncher.GameSettings.Honkai.Context; using CollapseLauncher.GameSettings.Universal; +using CollapseLauncher.Helper.Metadata; using CollapseLauncher.Interfaces; -using Hi3Helper.Preset; using Microsoft.Win32; using System.IO; // ReSharper disable PossibleNullReferenceException @@ -56,7 +56,7 @@ private void InitializeSettings() SettingsCollapseMisc = CollapseMiscSetting.Load(); // Load Preset - Preset_SettingsGraphics = Preset.LoadPreset(GameType.Honkai, HonkaiSettingsJSONContext.Default); + Preset_SettingsGraphics = Preset.LoadPreset(GameNameType.Honkai, HonkaiSettingsJSONContext.Default); } public void ReloadSettings() => InitializeSettings(); diff --git a/CollapseLauncher/Classes/GameManagement/GameVersion/BaseClass/GameVersionBase.cs b/CollapseLauncher/Classes/GameManagement/GameVersion/BaseClass/GameVersionBase.cs index 3ce3a79f2..4590be4c0 100644 --- a/CollapseLauncher/Classes/GameManagement/GameVersion/BaseClass/GameVersionBase.cs +++ b/CollapseLauncher/Classes/GameManagement/GameVersion/BaseClass/GameVersionBase.cs @@ -1,6 +1,6 @@ +using CollapseLauncher.Helper.Metadata; using CollapseLauncher.Interfaces; using Hi3Helper.Data; -using Hi3Helper.Preset; using Hi3Helper.Shared.ClassStruct; using Hi3Helper.Shared.Region; using Microsoft.UI.Xaml; @@ -20,8 +20,8 @@ internal class GameVersionBase : IGameVersionCheck private const string _defaultIniVersionSection = "General"; private string _defaultGameDirPath { get => Path.Combine(LauncherConfig.AppGameFolder, GamePreset.ProfileName, GamePreset.GameDirectoryName); } - private int gameChannelID => GamePreset.ChannelID; - private int gameSubChannelID => GamePreset.SubChannelID; + private int gameChannelID => GamePreset.ChannelID ?? 0; + private int gameSubChannelID => GamePreset.SubChannelID ?? 0; private IniSection _defaultIniProfile { @@ -90,9 +90,9 @@ protected string GameIniVersionPath } protected string GameConfigDirPath { get; set; } public GameVersionBase AsVersionBase => this; - public PresetConfigV2 GamePreset { get; set; } + public PresetConfig GamePreset { get; set; } public RegionResourceProp GameAPIProp { get; set; } - public GameType GameType => GamePreset.GameType; + public GameNameType GameType => GamePreset.GameType; public GameVendorProp VendorTypeProp { get; private set; } public string GameDirPath { @@ -111,8 +111,8 @@ public string GameOutputLogName { get => GameType switch { - GameType.Genshin => "output_log.txt", - GameType.Honkai => "output_log.txt", + GameNameType.Genshin => "output_log.txt", + GameNameType.Honkai => "output_log.txt", _ => "Player.log" }; } @@ -206,7 +206,7 @@ protected Dictionary PluginVersionsInstalled protected DeltaPatchProperty GameDeltaPatchProp { get => CheckDeltaPatchUpdate(GameDirPath, GamePreset.ProfileName, GameVersionAPI); } #endregion - protected GameVersionBase(UIElement parentUIElement, RegionResourceProp gameRegionProp, PresetConfigV2 gamePreset) + protected GameVersionBase(UIElement parentUIElement, RegionResourceProp gameRegionProp, PresetConfig gamePreset) { GamePreset = gamePreset; ParentUIElement = parentUIElement; diff --git a/CollapseLauncher/Classes/GameManagement/GameVersion/Genshin/VersionCheck.cs b/CollapseLauncher/Classes/GameManagement/GameVersion/Genshin/VersionCheck.cs index 9833ed852..33101943e 100644 --- a/CollapseLauncher/Classes/GameManagement/GameVersion/Genshin/VersionCheck.cs +++ b/CollapseLauncher/Classes/GameManagement/GameVersion/Genshin/VersionCheck.cs @@ -1,5 +1,5 @@ -using CollapseLauncher.Interfaces; -using Hi3Helper.Preset; +using CollapseLauncher.Helper.Metadata; +using CollapseLauncher.Interfaces; using Microsoft.UI.Xaml; using System.Collections.Generic; @@ -11,7 +11,7 @@ internal class GameTypeGenshinVersion : GameVersionBase, IGameVersionCheck public readonly List _audioVoiceLanguageList = new List { "Chinese", "English(US)", "Japanese", "Korean" }; #endregion - public GameTypeGenshinVersion(UIElement parentUIElement, RegionResourceProp gameRegionProp, PresetConfigV2 gamePreset) + public GameTypeGenshinVersion(UIElement parentUIElement, RegionResourceProp gameRegionProp, PresetConfig gamePreset) : base(parentUIElement, gameRegionProp, gamePreset) { // Try check for reinitializing game version. diff --git a/CollapseLauncher/Classes/GameManagement/GameVersion/Honkai/VersionCheck.cs b/CollapseLauncher/Classes/GameManagement/GameVersion/Honkai/VersionCheck.cs index 1b40c8e02..eca6adb99 100644 --- a/CollapseLauncher/Classes/GameManagement/GameVersion/Honkai/VersionCheck.cs +++ b/CollapseLauncher/Classes/GameManagement/GameVersion/Honkai/VersionCheck.cs @@ -1,5 +1,5 @@ +using CollapseLauncher.Helper.Metadata; using CollapseLauncher.Interfaces; -using Hi3Helper.Preset; using Microsoft.UI.Xaml; using System; @@ -17,7 +17,7 @@ internal class GameTypeHonkaiVersion : GameVersionBase, IGameVersionCheck public bool IsPreloadSenadinaVersion { get => GameVersionAPIPreload.HasValue ? GameVersionAPIPreload.Value.ToVersion() >= senadinaVersion : false; } #endregion - public GameTypeHonkaiVersion(UIElement parentUIElement, RegionResourceProp gameRegionProp, PresetConfigV2 gamePreset) + public GameTypeHonkaiVersion(UIElement parentUIElement, RegionResourceProp gameRegionProp, PresetConfig gamePreset) : base(parentUIElement, gameRegionProp, gamePreset) { // Try check for reinitializing game version from XMF file. diff --git a/CollapseLauncher/Classes/GameManagement/GameVersion/StarRail/VersionCheck.cs b/CollapseLauncher/Classes/GameManagement/GameVersion/StarRail/VersionCheck.cs index 8e6ce6d19..da535ded5 100644 --- a/CollapseLauncher/Classes/GameManagement/GameVersion/StarRail/VersionCheck.cs +++ b/CollapseLauncher/Classes/GameManagement/GameVersion/StarRail/VersionCheck.cs @@ -1,7 +1,7 @@ -using CollapseLauncher.Interfaces; +using CollapseLauncher.Helper.Metadata; +using CollapseLauncher.Interfaces; using Hi3Helper.EncTool.Parser.AssetMetadata; using Hi3Helper.EncTool.Proto.StarRail; -using Hi3Helper.Preset; using Microsoft.UI.Xaml; using System.Text; @@ -13,7 +13,7 @@ internal class GameTypeStarRailVersion : GameVersionBase, IGameVersionCheck public SRMetadata StarRailMetadataTool { get; set; } #endregion - public GameTypeStarRailVersion(UIElement parentUIElement, RegionResourceProp gameRegionProp, PresetConfigV2 gamePreset) + public GameTypeStarRailVersion(UIElement parentUIElement, RegionResourceProp gameRegionProp, PresetConfig gamePreset) : base(parentUIElement, gameRegionProp, gamePreset) { // Try check for reinitializing game version. diff --git a/CollapseLauncher/Classes/GamePropertyVault.cs b/CollapseLauncher/Classes/GamePropertyVault.cs index 4fb72e439..b92dc81cb 100644 --- a/CollapseLauncher/Classes/GamePropertyVault.cs +++ b/CollapseLauncher/Classes/GamePropertyVault.cs @@ -2,12 +2,12 @@ using CollapseLauncher.GameSettings.Honkai; using CollapseLauncher.GameSettings.StarRail; using CollapseLauncher.GameVersioning; +using CollapseLauncher.Helper.Metadata; using CollapseLauncher.InstallManager.Genshin; using CollapseLauncher.InstallManager.Honkai; using CollapseLauncher.InstallManager.StarRail; using CollapseLauncher.Interfaces; using Hi3Helper; -using Hi3Helper.Preset; using Hi3Helper.Shared.ClassStruct; using Microsoft.UI.Xaml; using System; @@ -20,28 +20,28 @@ namespace CollapseLauncher.Statics { internal class GamePresetProperty : IDisposable { - internal GamePresetProperty(UIElement UIElementParent, RegionResourceProp APIResouceProp, PresetConfigV2 GamePreset) + internal GamePresetProperty(UIElement UIElementParent, RegionResourceProp APIResouceProp, PresetConfig GamePreset) { _GamePreset = CopyGamePreset(GamePreset); _APIResouceProp = APIResouceProp!.Copy(); switch (GamePreset!.GameType) { - case GameType.Honkai: + case GameNameType.Honkai: _GameVersion = new GameTypeHonkaiVersion(UIElementParent, _APIResouceProp, _GamePreset); _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 GameType.StarRail: + case GameNameType.StarRail: _GameVersion = new GameTypeStarRailVersion(UIElementParent, _APIResouceProp, _GamePreset); _GameSettings = new StarRailSettings(_GameVersion); _GameCache = new StarRailCache(UIElementParent, _GameVersion); _GameRepair = new StarRailRepair(UIElementParent, _GameVersion); _GameInstall = new StarRailInstall(UIElementParent, _GameVersion); break; - case GameType.Genshin: + case GameNameType.Genshin: _GameVersion = new GameTypeGenshinVersion(UIElementParent, _APIResouceProp, _GamePreset); _GameSettings = new GenshinSettings(_GameVersion); _GameCache = null; @@ -70,17 +70,16 @@ private static List CopyReturn(IEnumerable Source) #region Goofy Ah Copy Method. TODO: Use Generics for this :pepehands: [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("ReSharper", "PossibleNullReferenceException")] - private PresetConfigV2 CopyGamePreset(PresetConfigV2 GamePreset) => - new PresetConfigV2() + private PresetConfig CopyGamePreset(PresetConfig GamePreset) => + new PresetConfig() { ActualGameDataLocation = GamePreset.ActualGameDataLocation, BetterHi3LauncherVerInfoReg = GamePreset.BetterHi3LauncherVerInfoReg, ConvertibleTo = CopyReturn(GamePreset.ConvertibleTo), DispatcherKey = GamePreset.DispatcherKey, DispatcherKeyBitLength = GamePreset.DispatcherKeyBitLength, - FallbackGameType = GamePreset.FallbackGameType, FallbackLanguage = GamePreset.FallbackLanguage, - GameChannel = GamePreset.GameChannel, + Channel = GamePreset.Channel, GameDefaultCVLanguage = GamePreset.GameDefaultCVLanguage, GameDirectoryName = GamePreset.GameDirectoryName, GameDispatchArrayURL = CopyReturn(GamePreset.GameDispatchArrayURL), @@ -124,17 +123,17 @@ private PresetConfigV2 CopyGamePreset(PresetConfigV2 GamePreset) => GameDataTemplates = new Dictionary(GamePreset.GameDataTemplates ?? new Dictionary()), ZoneSteamAssets = new Dictionary(GamePreset.ZoneSteamAssets ?? new Dictionary()), #if DEBUG - IsRepairEnabled = true, - IsCacheUpdateEnabled = true, + IsRepairEnabled = true, + IsCacheUpdateEnabled = true, #else - IsRepairEnabled = GamePreset.IsRepairEnabled, - IsCacheUpdateEnabled = GamePreset.IsCacheUpdateEnabled, + IsRepairEnabled = GamePreset.IsRepairEnabled, + IsCacheUpdateEnabled = GamePreset.IsCacheUpdateEnabled, #endif }; #endregion internal RegionResourceProp _APIResouceProp { get; set; } - internal PresetConfigV2 _GamePreset { get; set; } + internal PresetConfig _GamePreset { get; set; } internal IGameSettings _GameSettings { get; set; } internal IRepair _GameRepair { get; set; } internal ICache _GameCache { get; set; } @@ -198,14 +197,14 @@ internal static class GamePropertyVault public static int CurrentGameHashID { get; set; } public static GamePresetProperty GetCurrentGameProperty() => Vault![CurrentGameHashID]; - public static void LoadGameProperty(UIElement UIElementParent, RegionResourceProp APIResouceProp, PresetConfigV2 GamePreset) + public static void LoadGameProperty(UIElement UIElementParent, RegionResourceProp APIResouceProp, PresetConfig GamePreset) { LastGameHashID = LastGameHashID == 0 ? GamePreset!.HashID : LastGameHashID; CurrentGameHashID = GamePreset!.HashID; RegisterGameProperty(UIElementParent, APIResouceProp, GamePreset); } - public static void RegisterGameProperty(UIElement UIElementParent, RegionResourceProp APIResouceProp, PresetConfigV2 GamePreset) + public static void RegisterGameProperty(UIElement UIElementParent, RegionResourceProp APIResouceProp, PresetConfig GamePreset) { CleanupUnusedGameProperty(); if (Vault!.ContainsKey(GamePreset!.HashID)) diff --git a/CollapseLauncher/Classes/Helper/Metadata/BHI3LInfo.cs b/CollapseLauncher/Classes/Helper/Metadata/BHI3LInfo.cs new file mode 100644 index 000000000..b8d1e5829 --- /dev/null +++ b/CollapseLauncher/Classes/Helper/Metadata/BHI3LInfo.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CollapseLauncher.Helper.Metadata +{ + internal class BHI3LInfo + { + public BHI3LInfo_GameInfo game_info { get; set; } + } + + internal class BHI3LInfo_GameInfo + { + public string version { get; set; } + public string install_path { get; set; } + public bool installed { get; set; } + } +} diff --git a/CollapseLauncher/Classes/Helper/Metadata/DataCooker.cs b/CollapseLauncher/Classes/Helper/Metadata/DataCooker.cs new file mode 100644 index 000000000..c6e7bedcf --- /dev/null +++ b/CollapseLauncher/Classes/Helper/Metadata/DataCooker.cs @@ -0,0 +1,160 @@ +using Hi3Helper; +using Hi3Helper.EncTool; +using System; +using System.Buffers; +using System.Buffers.Text; +using System.IO.Compression; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Text; + +namespace CollapseLauncher.Helper.Metadata +{ + internal enum CompressionType : byte { None, Brotli } + internal static class DataCooker + { + private const long COLLAPSESIG = 7310310183885631299; + private const int ALLOWEDBUFFERPOOLSIZE = 1 << 20; // 1 MiB + + internal static mhyEncTool EncryptTool = new mhyEncTool(); + internal static RSA RSAInstance = null; + + internal static string ServeV3Data(string data) + { + if (!Base64.IsValid(data)) + return data; + + byte[] dataBytes = Convert.FromBase64String(data); + if (IsServeV3Data(dataBytes)) + { + GetServeV3DataSize(dataBytes, out long compressedSize, out long decompressedSize); + byte[] outBuffer = new byte[decompressedSize]; + ServeV3Data(dataBytes, outBuffer, (int)compressedSize, (int)decompressedSize, out int dataWritten); + return Encoding.UTF8.GetString(outBuffer.AsSpan(0, dataWritten)); + } + + return data; + } + + internal static bool IsServeV3Data(ReadOnlySpan data) + { + if (data.Length < 8) return false; + + long signature = MemoryMarshal.Read(data); + if (signature != COLLAPSESIG) return false; + + return true; + } + + internal static void GetServeV3DataSize(ReadOnlySpan data, out long compressedSize, out long decompressedSize) + { + if (data.Length < 32) throw new FormatException($"The MetadataV3 data format is corrupted!"); + + int readOffset = sizeof(long) * 2; + compressedSize = MemoryMarshal.Read(data.Slice(readOffset)); + decompressedSize = MemoryMarshal.Read(data.Slice(readOffset += sizeof(long))); + } + + private static void GetServeV3Attribute(ReadOnlySpan data, out CompressionType compressionType, out bool isUseEncryption) + { + int readOffset = sizeof(long); + long attribNumber = MemoryMarshal.Read(data.Slice(readOffset)); + + compressionType = (CompressionType)(byte)attribNumber; + isUseEncryption = (byte)(attribNumber >> 8) == 1; + } + + + internal static void ServeV3Data(ReadOnlySpan data, Span outData, int compressedSize, int decompressedSize, out int dataWritten) + { + GetServeV3Attribute(data, out CompressionType compressionType, out bool isUseEncryption); + int readOffset = sizeof(long) * 4; + + ReadOnlySpan dataRawBuffer = data.Slice(readOffset, data.Length - readOffset); + byte[] decryptedDataSpan = null; + int encBitLength = LauncherMetadataHelper.CurrentMasterKey.BitSize; + + bool isDecryptPoolAllowed = dataRawBuffer.Length <= ALLOWEDBUFFERPOOLSIZE; + bool isDecryptPoolUsed = false; + + try + { + if (isUseEncryption) + { + decryptedDataSpan = isDecryptPoolAllowed ? ArrayPool.Shared.Rent(dataRawBuffer.Length) : new byte[dataRawBuffer.Length]; + isDecryptPoolUsed = isDecryptPoolAllowed; + + if (RSAInstance == null) + { + RSAInstance = RSA.Create(); + byte[] key; + if (IsServeV3Data(LauncherMetadataHelper.CurrentMasterKey.Key)) + { + GetServeV3DataSize(LauncherMetadataHelper.CurrentMasterKey.Key, out long keyCompSize, out long keyDecompSize); + key = new byte[keyCompSize]; + ServeV3Data(LauncherMetadataHelper.CurrentMasterKey.Key, key, (int)keyCompSize, (int)keyDecompSize, out _); + } + else + { + key = LauncherMetadataHelper.CurrentMasterKey.Key; + } + + RSAInstance.ImportRSAPrivateKey(key, out _); + } + + int offset = 0; + int offsetOut = 0; + while (offset < dataRawBuffer.Length) + { + int decryptWritten = RSAInstance.Decrypt(dataRawBuffer.Slice(offset, encBitLength), decryptedDataSpan.AsSpan(offsetOut), RSAEncryptionPadding.Pkcs1); + offsetOut += decryptWritten; + offset += encBitLength; + } + + dataRawBuffer = decryptedDataSpan.AsSpan(0, offsetOut); + } + + if (dataRawBuffer.Length != compressedSize) + throw new DataMisalignedException($"RAW data is misaligned!"); + + switch (compressionType) + { + case CompressionType.None: + if (!isUseEncryption) data.Slice(readOffset, decompressedSize).CopyTo(outData); + dataWritten = decompressedSize; + break; + case CompressionType.Brotli: + { + Span dataDecompressed = outData; + BrotliDecoder decoder = new BrotliDecoder(); + + int offset = 0; + int decompressedWritten = 0; + while (offset < compressedSize) + { + decoder.Decompress(dataRawBuffer.Slice(offset), dataDecompressed.Slice(decompressedWritten), out int dataConsumedWritten, out int dataDecodedWritten); + decompressedWritten += dataDecodedWritten; + offset += dataConsumedWritten; + } + if (decompressedSize != decompressedWritten) + throw new DataMisalignedException($"Decompressed data is misaligned!"); + + dataWritten = decompressedWritten; + } + break; + default: + throw new FormatException($"Decompression format is not supported! ({compressionType})"); + } + +#if DEBUG + Logger.LogWriteLine($"[DataCooker::ServeV3Data()] Loaded ServeV3 data [IsPooled: {isDecryptPoolUsed}][TCompress: {compressionType} | IsEncrypt: {isUseEncryption}][CompSize: {compressedSize} | UncompSize: {decompressedSize}]", LogType.Debug, true); +#endif + } + finally + { + if (isDecryptPoolAllowed && isDecryptPoolUsed && decryptedDataSpan != null) + ArrayPool.Shared.Return(decryptedDataSpan, true); + } + } + } +} diff --git a/CollapseLauncher/Classes/Helper/Metadata/GameDataTemplate.cs b/CollapseLauncher/Classes/Helper/Metadata/GameDataTemplate.cs new file mode 100644 index 000000000..65837750a --- /dev/null +++ b/CollapseLauncher/Classes/Helper/Metadata/GameDataTemplate.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +#nullable enable +namespace CollapseLauncher.Helper.Metadata +{ + public class GameDataTemplate + { + public Dictionary? DataVersion { get; set; } + } +} diff --git a/CollapseLauncher/Classes/Helper/Metadata/GameDataVersion.cs b/CollapseLauncher/Classes/Helper/Metadata/GameDataVersion.cs new file mode 100644 index 000000000..c27d61ce1 --- /dev/null +++ b/CollapseLauncher/Classes/Helper/Metadata/GameDataVersion.cs @@ -0,0 +1,33 @@ +using System; + +#nullable enable +namespace CollapseLauncher.Helper.Metadata +{ + public class GameDataVersion + { + private byte[]? _data; + public byte[]? Data + { + get => _data; + init + { + if (DataCooker.IsServeV3Data(value)) + { + DataCooker.GetServeV3DataSize(value, out long compressedSize, out long decompressedSize); + byte[] dataOut = new byte[decompressedSize]; + DataCooker.ServeV3Data(value, dataOut, (int)compressedSize, (int)decompressedSize, out int dataWritten); + _data = dataOut; + } + _data = value; + } + } + public long Length { get; set; } + public bool isCompressed { get; set; } + + public static int GetBytesToIntVersion(byte[] version) + { + if (version.Length != 4) throw new FormatException("Argument: version must have 4 bytes"); + return version[0] | version[1] << 8 | version[2] << 16 | version[3] << 24; + } + } +} diff --git a/CollapseLauncher/Classes/Helper/Metadata/GeneralDataProp.cs b/CollapseLauncher/Classes/Helper/Metadata/GeneralDataProp.cs new file mode 100644 index 000000000..3ab3f6cb6 --- /dev/null +++ b/CollapseLauncher/Classes/Helper/Metadata/GeneralDataProp.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace CollapseLauncher.Helper.Metadata +{ + // WARNING!!! + // This feature is only available for Genshin. + [SuppressMessage("ReSharper", "RedundantDefaultMemberInitializer")] + public class GeneralDataProp + { + public string deviceUUID { get; set; } = ""; + public string userLocalDataVersionId { get; set; } = "0.0.1"; + public int deviceLanguageType { get; set; } = 1; + public int deviceVoiceLanguageType { get; set; } = 2; + [JsonPropertyName("selectedServerName")] + public string _selectedServerName { get; set; } = "os_asia"; + [JsonIgnore] + public ServerRegionID selectedServerName + { + get + { + string valueFromReg = _selectedServerName; + // ReSharper disable once RedundantTypeArgumentsOfMethod + if (!Enum.TryParse(valueFromReg, true, out ServerRegionID result)) + return ServerRegionID.os_asia; + + return result; + } + set => _selectedServerName = $"{value}"; + } + public int localLevelIndex { get; set; } = 0; + public string deviceID { get; set; } = ""; + public string targetUID { get; set; } = ""; + public string curAccountName { get; set; } = ""; + public string uiSaveData { get; set; } = ""; + public string inputData { get; set; } = ""; + public string graphicsData { get; set; } = ""; + public string globalPerfData { get; set; } = ""; + public int miniMapConfig { get; set; } = 1; + public bool enableCameraSlope { get; set; } = true; + public bool enableCameraCombatLock { get; set; } = true; + public bool completionPkg { get; set; } = false; + public bool completionPlayGoPkg { get; set; } = false; + public bool onlyPlayWithPSPlayer { get; set; } = false; + public bool needPlayGoFullPkgPatch { get; set; } = false; + public bool resinNotification { get; set; } = true; + public bool exploreNotification { get; set; } = true; + public int volumeGlobal { get; set; } = 10; + public int volumeSFX { get; set; } = 10; + public int volumeMusic { get; set; } = 10; + public int volumeVoice { get; set; } = 10; + public int audioAPI { get; set; } = -1; + public int audioDynamicRange { get; set; } = 0; + public int audioOutput { get; set; } = 1; + public bool _audioSuccessInit { get; set; } = true; + public bool enableAudioChangeAndroidMinimumBufferCapacity { get; set; } = true; + public int audioAndroidMiniumBufferCapacity { get; set; } = 2 << 10; + public bool motionBlur { get; set; } = true; + public bool gyroAiming { get; set; } = false; + public bool firstHDRSetting { get; set; } = true; + public double maxLuminosity { get; set; } = 0.0f; + public double uiPaperWhite { get; set; } = 0.0f; + public double scenePaperWhite { get; set; } = 0.0f; + public double gammaValue { get; set; } = 2.200000047683716; + public List _overrideControllerMapKeyList { get; set; } = new List(); + public List _overrideControllerMapValueList { get; set; } = new List(); + public bool rewiredDisableKeyboard { get; set; } = false; + public bool rewiredEnableKeyboard { get; set; } = false; + public bool rewiredEnableEDS { get; set; } = false; + public bool disableRewiredDelayInit { get; set; } = false; + public bool disableRewiredInitProtection { get; set; } = false; + public int lastSeenPreDownloadTime { get; set; } = 0; + public bool enableEffectAssembleInEditor { get; set; } = true; + public bool forceDisableQuestResourceManagement { get; set; } = false; + public bool needReportQuestResourceDeleteStatusFiles { get; set; } = false; + public bool mtrCached { get; set; } = true; + public bool mtrIsOpen { get; set; } = true; + public int mtrMaxTTL { get; set; } = 32; + public int mtrTimeOut { get; set; } = 5000; + public int mtrTraceCount { get; set; } = 5; + public int mtrAbortTimeOutCount { get; set; } = 3; + public int mtrAutoTraceInterval { get; set; } = 3600; + public int mtrTraceCDEachReason { get; set; } = 600; + public int mtrTimeInterval { get; set; } = 1000; + public List mtrBanReasons { get; set; } = new List(); + public List _customDataKeyList { get; set; } = new List(); + public List _customDataValueList { get; set; } = new List(); + public List _serializedCodeSwitches { get; set; } = new List(); + public bool urlCheckCached { get; set; } = false; + public bool urlCheckIsOpen { get; set; } = false; + public bool urlCheckAllIP { get; set; } = false; + public int urlCheckTimeOut { get; set; } = 5000; + public int urlCheckSueecssTraceCount { get; set; } = 5; + public int urlCheckErrorTraceCount { get; set; } = 30; + public int urlCheckAbortTimeOutCount { get; set; } = 3; + public int urlCheckTimeInterval { get; set; } = 1000; + public int urlCheckCDEachReason { get; set; } = 600; + public List urlCheckBanReasons { get; set; } = new List(); + public bool mtrUseOldWinVersion { get; set; } = false; + } +} diff --git a/CollapseLauncher/Classes/Helper/Metadata/Hashable.cs b/CollapseLauncher/Classes/Helper/Metadata/Hashable.cs new file mode 100644 index 000000000..8a781ebd8 --- /dev/null +++ b/CollapseLauncher/Classes/Helper/Metadata/Hashable.cs @@ -0,0 +1,7 @@ +namespace CollapseLauncher.Helper.Metadata +{ + public class Hashable + { + public long Hash { get; set; } + } +} diff --git a/CollapseLauncher/Classes/Helper/Metadata/JsonConverter/Extension.cs b/CollapseLauncher/Classes/Helper/Metadata/JsonConverter/Extension.cs new file mode 100644 index 000000000..e2c33212b --- /dev/null +++ b/CollapseLauncher/Classes/Helper/Metadata/JsonConverter/Extension.cs @@ -0,0 +1,43 @@ +using System; +using System.Buffers.Text; +using System.Text; + +namespace CollapseLauncher.Helper.Metadata.JsonConverter +{ + internal static class Extension + { + internal static string GetServeV3String(ReadOnlySpan data) + { + // Check if the string is a base64 string. If yes, then try to decode them + bool isValidB64Data = Base64.IsValid(data); + if (isValidB64Data) + { + // Get the output data span and decode the base64 data to raw data + Span dataBytes = new byte[data.Length]; + Base64.DecodeFromUtf8(data, dataBytes, out _, out int dataB64DecodedWritten, true); + + // Check if the data is ServeV3 data. If yes, then process the data + if (DataCooker.IsServeV3Data(dataBytes)) + { + // Get the size and initialize the output buffer + DataCooker.GetServeV3DataSize(dataBytes, out long compressedSize, out long decompressedSize); + byte[] outBuffer = new byte[decompressedSize]; + + // Decode the ServeV3 data to the outBuffer + DataCooker.ServeV3Data(dataBytes.Slice(0, dataB64DecodedWritten), outBuffer, (int)compressedSize, (int)decompressedSize, out int dataWritten); + + // Get the string out of outBuffer and return it + string outString = Encoding.UTF8.GetString(outBuffer.AsSpan(0, dataWritten)); + return outString; + } + + // If it's not ServeV3 data, then return it as a raw string + return Encoding.UTF8.GetString(data); + } + + // If it's not base64 data, then return the string. + return Encoding.UTF8.GetString(data); + } + } + +} diff --git a/CollapseLauncher/Classes/Helper/Metadata/JsonConverter/ServeV3Converter.cs b/CollapseLauncher/Classes/Helper/Metadata/JsonConverter/ServeV3Converter.cs new file mode 100644 index 000000000..7c69c2437 --- /dev/null +++ b/CollapseLauncher/Classes/Helper/Metadata/JsonConverter/ServeV3Converter.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +#nullable enable +namespace CollapseLauncher.Helper.Metadata.JsonConverter +{ + internal class ServeV3StringConverter : JsonConverter + { + public override bool CanConvert(Type type) => true; + + public override string Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) => Extension.GetServeV3String(reader.ValueSpan); + + public override void Write( + Utf8JsonWriter writer, + string baseType, + JsonSerializerOptions options) + { + throw new JsonException($"Serializing is not supported!"); + } + } + + public class ServeV3StringListConverter : JsonConverter?> + { + public override bool CanConvert(Type type) => true; + + public override List? Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + // Initialize and check if the token is a start of an array + List? returnValue = null; + if (reader.TokenType != JsonTokenType.StartArray) // Throw if it's not + throw new JsonException($"The start token of the JSON field is not a start of an array!"); + + // Read the next value or token + reader.Read(); + + // If the token is a string, initialize the list + if (reader.TokenType == JsonTokenType.String) + returnValue = new List(); + + // Loop and read the value if the token is currently a string + while (reader.TokenType == JsonTokenType.String) + { + // Try retrieve the data if it's a raw string or a ServeV3 string + string returnString = Extension.GetServeV3String(reader.ValueSpan); + returnValue?.Add(returnString); // Add the string + reader.Read(); // Read the next token + } + + // If the token is not an end of an array, then throw + if (reader.TokenType != JsonTokenType.EndArray) + throw new JsonException($"The end token of the JSON field is not an end of an array!"); + + // Return the list + return returnValue; + } + + public override void Write( + Utf8JsonWriter writer, + List? baseType, + JsonSerializerOptions options) + { + throw new JsonException($"Serializing is not supported!"); + } + } +} diff --git a/CollapseLauncher/Classes/Helper/Metadata/LauncherMetadataHelper.cs b/CollapseLauncher/Classes/Helper/Metadata/LauncherMetadataHelper.cs new file mode 100644 index 000000000..41137bc33 --- /dev/null +++ b/CollapseLauncher/Classes/Helper/Metadata/LauncherMetadataHelper.cs @@ -0,0 +1,461 @@ +using CollapseLauncher.Helper.Loading; +using Hi3Helper; +using Hi3Helper.Data; +using Hi3Helper.Shared.Region; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +#nullable enable +namespace CollapseLauncher.Helper.Metadata +{ + internal static class LauncherMetadataHelper + { + private const string MetadataVersion = "v3"; + private const string LauncherMetadataStampPrefix = "stamp.json"; + + #region Metadata and Stamp path/url prefixes + internal static string CurrentLauncherChannel => LauncherConfig.IsPreview ? "preview" : "stable"; + internal static string LauncherMetadataFolder => Path.Combine(LauncherConfig.AppGameFolder, "_metadatav3"); + internal static string LauncherStampRemoteURLPath => ConverterTool.CombineURLFromString($"/metadata/{MetadataVersion}/{CurrentLauncherChannel}/", LauncherMetadataStampPrefix); + #endregion + + #region Metadata Stamp List and Config Dictionary + internal static List? LauncherMetadataStamp { get; private set; } + internal static List? NewUpdateMetadataStamp { get; private set; } + internal static List? LauncherGameNameCollection => LauncherGameNameRegionCollection?.Keys.ToList(); + internal static Dictionary?>? LauncherMetadataConfig { get; private set; } + #endregion + + /* TODO: add real-time change support + private static TimeSpan _lastModifiedCurrentConfig; + private static PresetConfig? _currentConfig; + private static long _currentConfigHash; + */ + + #region Game Name & Region Collection and Current Config + internal static PresetConfig? CurrentMetadataConfig; + internal static string? CurrentMetadataConfigGameName; + internal static string? CurrentMetadataConfigGameRegion; + internal static Dictionary?>? LauncherGameNameRegionCollection { get; private set; } + #endregion + + #region Current Master Key config + internal static MasterKeyConfig? CurrentMasterKey { get; private set; } + #endregion + + #region Current Game Name and Max Region Counts + internal static int CurrentGameNameCount { get; private set; } + internal static int CurrentGameRegionMaxCount { get; private set; } + #endregion + + #region Internal State Lock Boolean + private static bool IsUpdateCheckRunning = false; + private static bool IsUpdateRoutineRunning = false; + #endregion + + internal static PresetConfig? GetMetadataConfig(string gameName, string gameRegion) + { + PresetConfig? config = LauncherMetadataConfig?[gameName]?[gameRegion]; + if (config != null) + { + CurrentMetadataConfig = config; + CurrentMetadataConfigGameName = gameName; + CurrentMetadataConfigGameRegion = gameRegion; + } + + return LauncherMetadataConfig?[gameName]?[gameRegion]; + // TODO: add real-time change support + /* + Stamp? stamp = LauncherMetadataStamp?.FirstOrDefault(x => x.GameName == gameName && x.GameRegion == gameRegion); + if (stamp == null) + throw new KeyNotFoundException($"Metadata stamp with Game Name: {gameName} and Game Region: {gameRegion} is not found!"); + + if (string.IsNullOrEmpty(stamp.MetadataPath)) + throw new NullReferenceException($"The current stamp for Game Name: {gameName} and Game Region: {gameRegion} has empty or undefined MetadataPath!"); + + string configLocalFilePath = Path.Combine(LauncherMetadataFolder, stamp.MetadataPath); + FileInfo configLocalFileInfo = new FileInfo(configLocalFilePath); + */ + } + + internal static async ValueTask Initialize(bool isCacheUpdateModeOnly = false, bool isShowLoadingMessage = true) + { + if (isShowLoadingMessage) + { + LoadingMessageHelper.ShowLoadingFrame(); + LoadingMessageHelper.SetMessage("Initializing", "Loading Launcher Metadata"); + } + + // Initialize the variable and create the metadata folder if it doesn't exist + string metadataFolder = LauncherMetadataFolder; + if (!Directory.Exists(metadataFolder)) + Directory.CreateDirectory(metadataFolder); + + // Get the current channel + string currentChannel = CurrentLauncherChannel; + + // Initialize the stamp and config file + await InitializeStamp(currentChannel); + await InitializeConfig(currentChannel, isCacheUpdateModeOnly, isShowLoadingMessage); + } + + internal static async ValueTask InitializeStamp(string currentChannel, bool throwAfterRetry = false) + { + string stampLocalFilePath = Path.Combine(LauncherMetadataFolder, LauncherMetadataStampPrefix); + string stampRemoteFilePath = LauncherStampRemoteURLPath; + + FileStream? stampLocalStream = null; + + // Load the stamp file + try + { + // Get the local stream + stampLocalStream = new FileStream(stampLocalFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite); + + // Check if the file is empty, then download the file + if (stampLocalStream.Length == 0) + { + // Get the stream and download the file + await using BridgedNetworkStream stampRemoteStream = await FallbackCDNUtil.TryGetCDNFallbackStream(stampRemoteFilePath, default); + await stampRemoteStream.CopyToAsync(stampLocalStream); + + // Reset the position to 0 + stampLocalStream.Position = 0; + } + + // Deserialize the stream + LauncherMetadataStamp = await stampLocalStream.DeserializeAsync>(InternalAppJSONContext.Default); + } + catch (Exception ex) + { + // Throw if it's allowed + if (throwAfterRetry) + throw new TypeLoadException($"Failed while trying to load Metadata Stamp!", ex); + + // Or, retry the method recursively one more time. + Logger.LogWriteLine($"An error has occurred while initializing Metadata Stamp! Retrying...\r\n{ex}", LogType.Warning, true); + + // Try dispose and delete the old file first, then retry to initialize the stamp once again. + if (stampLocalStream != null) await stampLocalStream.DisposeAsync(); + if (File.Exists(stampLocalFilePath)) File.Delete(stampLocalFilePath); + await InitializeStamp(currentChannel, true); + } + finally + { + // Dispose the local stream + if (stampLocalStream != null) await stampLocalStream.DisposeAsync(); + } + } + + internal static async ValueTask InitializeConfig(string currentChannel, bool isCacheUpdateModeOnly, bool isShowLoadingMessage) + { + if (LauncherMetadataStamp == null) throw new NullReferenceException($"The Metadata Stamp list is not initialized!"); + if (LauncherMetadataStamp.Count == 0) throw new InvalidOperationException($"The Metadata Stamp list is empty!"); + + // Initialize the dictionary of the config + if (LauncherMetadataConfig == null) + LauncherMetadataConfig = new Dictionary?>(); + LauncherMetadataConfig.Clear(); + + // Initialize the game name region collection if it's null + if (LauncherGameNameRegionCollection == null) + LauncherGameNameRegionCollection = new Dictionary?>(); + LauncherGameNameRegionCollection.Clear(); + + // Find and iterate the master key first + Stamp? masterKeyStamp = LauncherMetadataStamp?.FirstOrDefault(x => x.MetadataType == MetadataType.MasterKey); + if (masterKeyStamp == null) throw new KeyNotFoundException($"Master key information is not found in the stamp!"); + await LoadConfigInner(masterKeyStamp, currentChannel, false, true); + + // Iterate the stamp and try to load the configs + int index = 1; + List stampList = LauncherMetadataStamp!.Where(x => x.MetadataType == MetadataType.PresetConfigV2).ToList(); + foreach (Stamp stamp in stampList) + { + if (isShowLoadingMessage) + LoadingMessageHelper.SetMessage("Initializing", $"Loading Game Configuration [{index++}/{stampList?.Count}]: {InnerLauncherConfig.GetGameTitleRegionTranslationString(stamp.GameName, Locale.Lang._GameClientTitles)} - {InnerLauncherConfig.GetGameTitleRegionTranslationString(stamp.GameRegion, Locale.Lang._GameClientRegions)}"); + + await LoadConfigInner(stamp, currentChannel, false, false, isCacheUpdateModeOnly); + } + + // Save the current count of game name and game regions + CurrentGameNameCount = LauncherMetadataConfig.Keys.Count; + CurrentGameRegionMaxCount = LauncherMetadataConfig.Max(x => x.Value?.Count ?? 0); + } + + internal static async ValueTask LoadConfigInner(Stamp stamp, string currentChannel, bool throwAfterRetry = false, bool allowDeserializeKey = false, bool isCacheUpdateModeOnly = false) + { + if (string.IsNullOrEmpty(stamp.MetadataPath)) throw new NullReferenceException($"The metadata stamp for this {stamp.MetadataType} type is empty!"); + + string configLocalFilePath = Path.Combine(LauncherMetadataFolder, stamp.MetadataPath); + string configRemoteFilePath = ConverterTool.CombineURLFromString($"/metadata/{MetadataVersion}/{currentChannel}/", stamp.MetadataPath); + + FileStream? configLocalStream = null; + try + { + // Get the local stream + configLocalStream = new FileStream(configLocalFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite); + + // Check if the file doesn't exist, then download the file + if (configLocalStream.Length == 0) + { + // Get the stream and download the file + await using BridgedNetworkStream stampRemoteStream = await FallbackCDNUtil.TryGetCDNFallbackStream(configRemoteFilePath, default); + await stampRemoteStream.CopyToAsync(configLocalStream); + + // Reset the position to 0 + configLocalStream.Position = 0; + } + + if (stamp.MetadataType == MetadataType.MasterKey && allowDeserializeKey) + { + // Deserialize the key config + MasterKeyConfig? keyConfig = await configLocalStream.DeserializeAsync(InternalAppJSONContext.Default); + if (keyConfig == null) throw new InvalidDataException($"Master key config seems to be empty!"); + + // Assign the key to instance property + CurrentMasterKey = keyConfig; + } + + if (stamp.MetadataType == MetadataType.PresetConfigV2) + { + if (string.IsNullOrEmpty(stamp.GameName) || string.IsNullOrEmpty(stamp.GameRegion)) throw new NullReferenceException($"Game name or region property inside the stamp is empty!"); + + // Deserialize the config + PresetConfig? presetConfig = await configLocalStream.DeserializeAsync(InternalAppJSONContext.Default); + if (presetConfig == null) throw new InvalidDataException($"Config seems to be empty!"); + + // Ignore if the isCacheUpdateModeOnly is true and the config doesn't support cache update + if (isCacheUpdateModeOnly && (!presetConfig.IsCacheUpdateEnabled ?? false)) + return; + + // Generate HashID and GameName + string HashComposition = $"{stamp.LastUpdated} - {stamp.GameName} - {stamp.GameRegion}"; + int HashID = ConverterTool.BytesToCRC32Int(HashComposition); + presetConfig.HashID = HashID; + presetConfig.GameName = stamp.GameName; + + // Dispose the file first + await configLocalStream.DisposeAsync(); + + // Get the file timestamp and set it to the variable + FileInfo configFileInfo = new FileInfo(configLocalFilePath); + DateTime configFileLastModified = configFileInfo.LastWriteTimeUtc; + + // If the dictionary doesn't contains the dictionary of the game, then initialize it + if (!LauncherMetadataConfig?.ContainsKey(stamp.GameName) ?? false) + { + // Initialize and add the game preset config dictionary + Dictionary? presetConfigDict = new Dictionary(); + LauncherMetadataConfig?.Add(stamp.GameName, presetConfigDict); + } + + // If the game name region collection is not exist, create a new one + if (!LauncherGameNameRegionCollection?.ContainsKey(stamp.GameName) ?? false) + LauncherGameNameRegionCollection?.Add(stamp.GameName, new List()); + + // Add the game region name into collection + LauncherGameNameRegionCollection?[stamp.GameName]?.Add(stamp.GameRegion); + + // If the game preset config dictionary doesn't have the game region, then add it. + if (!LauncherMetadataConfig?[stamp.GameName]?.ContainsKey(stamp.GameRegion) ?? false) + LauncherMetadataConfig?[stamp.GameName]?.Add(stamp.GameRegion, presetConfig); + } + } + catch (Exception ex) + { + // Throw if it's allowed + if (throwAfterRetry) + throw new TypeLoadException($"Failed while trying to load Metadata Config for: {stamp.GameName} - {stamp.GameRegion}!", ex); + + // Or, retry the method recursively one more time. + Logger.LogWriteLine($"An error has occurred while initializing Metadata Stamp! Retrying...\r\n{ex}", LogType.Warning, true); + + // Try dispose and delete the old file first, then retry to initialize the config once again. + if (configLocalStream != null) await configLocalStream.DisposeAsync(); + if (File.Exists(configLocalFilePath)) File.Delete(configLocalFilePath); + await LoadConfigInner(stamp, currentChannel, true, allowDeserializeKey, isCacheUpdateModeOnly); + } + finally + { + // Dispose the local stream + if (configLocalStream != null) await configLocalStream.DisposeAsync(); + } + } + + internal static async ValueTask IsMetadataHasUpdate() + { + // Delay the routine if the update check or routine is running + while (IsUpdateCheckRunning || IsUpdateRoutineRunning) await Task.Delay(1000); + + try + { + IsUpdateCheckRunning = true; + + // Get the remote stream + string stampRemoteFilePath = LauncherStampRemoteURLPath; + await using BridgedNetworkStream stampRemoteStream = await FallbackCDNUtil.TryGetCDNFallbackStream(stampRemoteFilePath, default); + + // Check and throw if the stream returns null or empty + if (stampRemoteStream == null) + throw new NullReferenceException("MetadataV3 stamp check stream returns a null or empty, which means there might be an issue while retrieving stream of the stamp!"); + + // Deserialize the stream + List? remoteMetadataStampList = await stampRemoteStream.DeserializeAsync>(InternalAppJSONContext.Default); + + // Check and throw if the metadata stamp returns null or empty + if (remoteMetadataStampList == null || remoteMetadataStampList.Count == 0) + throw new NullReferenceException($"MetadataV3 stamp list is returns a null or empty after deserialization!"); + + if (NewUpdateMetadataStamp == null) + NewUpdateMetadataStamp = new List(); + + // Make sure to clear the new update list first + NewUpdateMetadataStamp?.Clear(); + + // Do iteration and check if the stamp is outdated + bool isOutdatedStampDetected = false; + foreach (Stamp? remoteMetadataStamp in remoteMetadataStampList) + { + // Check if the local stamp does not have one, then add it to new update stamp list + Stamp? localStamp = LauncherMetadataStamp?.FirstOrDefault(x => remoteMetadataStamp?.GameRegion == x.GameRegion + && remoteMetadataStamp?.GameName == x.GameName + && remoteMetadataStamp?.LastUpdated == x.LastUpdated + && remoteMetadataStamp?.MetadataPath == x.MetadataPath + && remoteMetadataStamp?.MetadataType == x.MetadataType); + + // If null, then add it to new update list + if (localStamp == null && remoteMetadataStamp != null) + { + Logger.LogWriteLine($"A new metadata config was found! [Name: {remoteMetadataStamp.GameName} | Region: {remoteMetadataStamp.GameRegion} | Type: {remoteMetadataStamp.MetadataType}] at {remoteMetadataStamp.LastUpdated}", LogType.Default, true); + isOutdatedStampDetected = true; + NewUpdateMetadataStamp?.Add(remoteMetadataStamp); + } + } + + // Return the status + return isOutdatedStampDetected; + } + catch (Exception ex) + { + Logger.LogWriteLine($"An error has occurred while checking MetadataV3 update!\r\n{ex}", LogType.Error, true); + return false; + } + finally + { + // Clear the new update stamp list and set the lock state + IsUpdateCheckRunning = false; + } + } + + internal static async ValueTask RunMetadataUpdate() + { + // Delay the routine if the update check or routine is running + while (IsUpdateCheckRunning || IsUpdateRoutineRunning) await Task.Delay(1000); + + try + { + IsUpdateRoutineRunning = true; + + // If the new update list is null or empty, then return + if (NewUpdateMetadataStamp == null || NewUpdateMetadataStamp.Count == 0) + { + Logger.LogWriteLine($"The new update stamp is empty! Please make sure that IsMetadataHasUpdate() has been executed and returns true."); + return; + } + + // Remove the old metadata config file first + foreach (Stamp newUpdateStamp in NewUpdateMetadataStamp) + { + // Ensure if the MetadataPath is not empty + if (string.IsNullOrEmpty(newUpdateStamp.MetadataPath)) + throw new NullReferenceException($"MetadataPath defined inside of the stamp is empty or null!"); + + // Get the local config file path and remove it if it exist + string configLocalFilePath = Path.Combine(LauncherMetadataFolder, newUpdateStamp.MetadataPath); + if (File.Exists(configLocalFilePath)) + File.Delete(configLocalFilePath); + + Logger.LogWriteLine($"Removed old metadata config file! [Name: {newUpdateStamp.GameName} | Region: {newUpdateStamp.GameRegion} | Type: {newUpdateStamp.MetadataType}]\r\nLocation: {configLocalFilePath}", LogType.Default, true); + } + + // Then remove the stamp file + string stampLocalFilePath = Path.Combine(LauncherMetadataFolder, LauncherMetadataStampPrefix); + if (File.Exists(stampLocalFilePath)) + File.Delete(stampLocalFilePath); + + Logger.LogWriteLine($"Removed old metadata stamp file!\r\nLocation: {stampLocalFilePath}", LogType.Default, true); + + // Then reinitialize the metadata + await Initialize(false, true); + } + catch (Exception ex) + { + Logger.LogWriteLine($"An error has occurred while updating MetadataV3!\r\n{ex}", LogType.Error, true); + } + finally + { + IsUpdateRoutineRunning = false; + } + } + + internal static List? GetGameNameCollection() => LauncherGameNameRegionCollection?.Keys.ToList(); + + internal static List? GetGameRegionCollection(string gameName) + { + if (!LauncherGameNameRegionCollection?.ContainsKey(gameName) ?? false) + { + Logger.LogWriteLine($"Game region collection for name: \"{gameName}\" isn't exist!", LogType.Error, true); + return null; + } + + return LauncherGameNameRegionCollection?[gameName]; + } + + internal static int GetPreviousGameRegion(string gameName) + { + // Get the config key name + string iniKeyName = $"LastRegion_{gameName!.Replace(" ", string.Empty)}"; + string? gameRegion; + + // Get the region collection + List? gameRegionCollection = GetGameRegionCollection(gameName); + gameRegionCollection ??= LauncherGameNameRegionCollection?.FirstOrDefault().Value; + + // Throw if the collection is empty or null + if (gameRegionCollection == null || gameRegionCollection.Count == 0) + throw new NullReferenceException($"Game region collection is null or empty!"); + + // If the config key name is not exist, then return the first region + if (!LauncherConfig.IsConfigKeyExist(iniKeyName)) + { + gameRegion = gameRegionCollection.FirstOrDefault(); + LauncherConfig.SetAndSaveConfigValue(iniKeyName, gameRegion); + return 0; + } + + // Get the last region and find the index inside the collection. + // If not found, then set the region to the first. + gameRegion = LauncherConfig.GetAppConfigValue(iniKeyName).ToString(); + int indexOfGameRegion = gameRegionCollection?.IndexOf(gameRegion) ?? -1; + return indexOfGameRegion < 1 ? 0 : indexOfGameRegion; + } + + public static void SetPreviousGameRegion(string GameCategoryName, string RegionName, bool isSave = true) + { + string iniKeyName = $"LastRegion_{GameCategoryName!.Replace(" ", string.Empty)}"; + + if (isSave) + { + LauncherConfig.SetAndSaveConfigValue(iniKeyName, RegionName); + } + else + { + LauncherConfig.SetAppConfigValue(iniKeyName, RegionName); + } + } + } +} diff --git a/CollapseLauncher/Classes/Helper/Metadata/MasterKeyConfig.cs b/CollapseLauncher/Classes/Helper/Metadata/MasterKeyConfig.cs new file mode 100644 index 000000000..f5b1d7457 --- /dev/null +++ b/CollapseLauncher/Classes/Helper/Metadata/MasterKeyConfig.cs @@ -0,0 +1,9 @@ +namespace CollapseLauncher.Helper.Metadata +{ +#nullable enable + public class MasterKeyConfig : Hashable + { + public byte[]? Key { get; set; } + public int BitSize { get; set; } + } +} diff --git a/CollapseLauncher/Classes/Helper/Metadata/MetadataType.cs b/CollapseLauncher/Classes/Helper/Metadata/MetadataType.cs new file mode 100644 index 000000000..99837ee80 --- /dev/null +++ b/CollapseLauncher/Classes/Helper/Metadata/MetadataType.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace CollapseLauncher.Helper.Metadata +{ + [JsonConverter(typeof(JsonStringEnumConverter))] + public enum MetadataType + { + Unknown, + PresetConfigV2, + MasterKey + } +} diff --git a/CollapseLauncher/Classes/Helper/Metadata/PresetConfig.cs b/CollapseLauncher/Classes/Helper/Metadata/PresetConfig.cs new file mode 100644 index 000000000..1553ffbc4 --- /dev/null +++ b/CollapseLauncher/Classes/Helper/Metadata/PresetConfig.cs @@ -0,0 +1,446 @@ +using CollapseLauncher.Helper.Metadata.JsonConverter; +using Hi3Helper; +using Hi3Helper.Data; +using Hi3Helper.EncTool.Parser.AssetMetadata; +using Microsoft.Win32; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using static Hi3Helper.Logger; + +#nullable enable +namespace CollapseLauncher.Helper.Metadata +{ + [JsonConverter(typeof(JsonStringEnumConverter))] + public enum GameChannel + { + Stable, + Beta, + DevRelease + } + + [JsonConverter(typeof(JsonStringEnumConverter))] + public enum ServerRegionID + { + os_usa = 0, + os_euro = 1, + os_asia = 2, + os_cht = 3, + cn_gf01 = 4, + cn_qd01 = 5 + } + + [JsonConverter(typeof(JsonStringEnumConverter))] + public enum GameNameType + { + Honkai, + Genshin, + StarRail, + Zenless, + Unknown = int.MinValue + } + + [JsonConverter(typeof(JsonStringEnumConverter))] + public enum GameVendorType + { + miHoYo, + Cognosphere + } + + internal class PresetConfig + { + #region Constants + private const string PrefixRegInstallLocation = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{0}"; + private const string PrefixRegGameConfig = "Software\\{0}\\{1}"; + private const string PrefixDefaultProgramFiles = "{1}Program Files\\{0}"; + #endregion + + #region Config Propeties + [JsonPropertyName("GameChannel")] + public GameChannel Channel { get; init; } + public AudioLanguageType GameDefaultCVLanguage { get; init; } + public GameNameType GameType { get; init; } = GameNameType.Unknown; + public GameVendorType VendorType { get; init; } + + [JsonConverter(typeof(ServeV3StringConverter))] + public string? BetterHi3LauncherVerInfoReg { get; init; } + [JsonConverter(typeof(ServeV3StringConverter))] + public string? DispatcherKey { get; init; } + [JsonConverter(typeof(ServeV3StringConverter))] + public string? FallbackLanguage { get; init; } + [JsonConverter(typeof(ServeV3StringConverter))] + public string? InternalGameNameFolder { get; init; } + [JsonConverter(typeof(ServeV3StringConverter))] + public string? InternalGameNameInConfig { get; init; } + [JsonConverter(typeof(ServeV3StringConverter))] + public string? GameDirectoryName { get; init; } + [JsonConverter(typeof(ServeV3StringConverter))] + public string? GameDispatchURL { get; init; } + [JsonConverter(typeof(ServeV3StringConverter))] + public string? GameDispatchChannelName { get; init; } + [JsonConverter(typeof(ServeV3StringConverter))] + public string? GameDispatchDefaultName { get; init; } + [JsonConverter(typeof(ServeV3StringConverter))] + public string? GameDispatchURLTemplate { get; init; } + [JsonConverter(typeof(ServeV3StringConverter))] + public string? GameExecutableName { get; init; } + [JsonConverter(typeof(ServeV3StringConverter))] + public string? GameGatewayDefault { get; init; } + [JsonConverter(typeof(ServeV3StringConverter))] + public string? GameGatewayURLTemplate { get; init; } + [JsonConverter(typeof(ServeV3StringConverter))] + public string? GameName { get; set; } + [JsonConverter(typeof(ServeV3StringConverter))] + public string? LauncherSpriteURL { get; init; } + [JsonConverter(typeof(ServeV3StringConverter))] + public string? LauncherSpriteURLMultiLangFallback { get; init; } + [JsonConverter(typeof(ServeV3StringConverter))] + public string? LauncherPluginURL { get; init; } + [JsonConverter(typeof(ServeV3StringConverter))] + public string? LauncherResourceURL { get; init; } + [JsonConverter(typeof(ServeV3StringConverter))] + public string? ProfileName { get; init; } + [JsonConverter(typeof(ServeV3StringConverter))] + public string? ProtoDispatchKey { get; init; } + [JsonConverter(typeof(ServeV3StringConverter))] + public string? SteamInstallRegistryLocation { get; init; } + [JsonConverter(typeof(ServeV3StringConverter))] + public string? ZoneDescription { get; init; } + [JsonConverter(typeof(ServeV3StringConverter))] + public string? ZoneFullname { get; init; } + [JsonConverter(typeof(ServeV3StringConverter))] + public string? ZoneLogoURL { get; init; } + [JsonConverter(typeof(ServeV3StringConverter))] + public string? ZoneName { get; init; } + [JsonConverter(typeof(ServeV3StringConverter))] + public string? ZonePosterURL { get; init; } + [JsonConverter(typeof(ServeV3StringConverter))] + public string? ZoneURL { get; init; } + + [JsonConverter(typeof(ServeV3StringListConverter))] + public List? ConvertibleTo { get; init; } + [JsonConverter(typeof(ServeV3StringListConverter))] + public List? GameSupportedLanguages { get; init; } + [JsonConverter(typeof(ServeV3StringListConverter))] + public List? GameDispatchArrayURL { get; init; } + + public bool? IsCacheUpdateEnabled { get; init; } + public bool? IsConvertible { get; init; } + public bool? IsExperimental { get; init; } + public bool? IsGenshin { get; init; } + public bool? IsHideSocMedDesc { get; init; } = true; + public bool? IsRepairEnabled { get; init; } + public bool? LauncherSpriteURLMultiLang { get; init; } + public bool? UseRightSideProgress { get; init; } + + public byte? CachesListGameVerID { get; init; } + + public int? ChannelID { get; init; } + public int? DispatcherKeyBitLength { get; init; } + public int? LauncherID { get; init; } + public int? SubChannelID { get; init; } + public int? SteamGameID { get; init; } + + public Dictionary? GameDataTemplates { get; init; } = new Dictionary(); + public Dictionary? ZoneSteamAssets { get; init; } = new Dictionary(); + + public DateTime LastModifiedFile { get; set; } + #endregion + + #region Dynamic Config Properties + private string? SystemDriveLetter { get => Path.GetPathRoot(Environment.GetFolderPath(Environment.SpecialFolder.System)); } + public string InstallRegistryLocation { get => string.Format(PrefixRegInstallLocation, InternalGameNameInConfig); } + public string DefaultGameLocation { get => string.Format(PrefixDefaultProgramFiles, InternalGameNameFolder, SystemDriveLetter); } + public string ConfigRegistryLocation { get => string.Format(PrefixRegGameConfig, VendorType, InternalGameNameInConfig); } + public string? ActualGameDataLocation { get; set; } + public int HashID { get; set; } + #endregion + + #region Language Handler + public string? GetGameLanguage() + { + ReadOnlySpan value; + RegistryKey? keys = Registry.CurrentUser.OpenSubKey(ConfigRegistryLocation!); + + byte[]? result = (byte[]?)keys?.GetValue("MIHOYOSDK_CURRENT_LANGUAGE_h2559149783"); + + if (keys is null || result is null || result.Length is 0) + { + LogWriteLine($"Language registry on \u001b[32;1m{Path.GetFileName(ConfigRegistryLocation)}\u001b[0m version doesn't exist. Fallback value will be used.", LogType.Warning, true); + return FallbackLanguage; + } + + value = Encoding.UTF8.GetString(result).AsSpan().Trim('\0'); + return new string(value); + } + + // WARNING!!! + // This feature is only available for Genshin and Star Rail. + public int GetVoiceLanguageID() + { + string RegPath = Path.GetFileName(ConfigRegistryLocation); + switch (GameType) + { + case GameNameType.Genshin: + return GetVoiceLanguageID_Genshin(RegPath); + case GameNameType.StarRail: + return GetVoiceLanguageID_StarRail(RegPath); + default: + return int.MinValue; + } + } + + private int GetVoiceLanguageID_StarRail(string RegPath) + { + try + { + string regValue; + RegistryKey? keys = Registry.CurrentUser.OpenSubKey(ConfigRegistryLocation!); + byte[]? value = (byte[]?)keys?.GetValue("LanguageSettings_LocalAudioLanguage_h882585060"); + + if (keys is null || value is null || value.Length is 0) + { + LogWriteLine($"Voice Language ID registry on {RegPath} doesn't exist. Fallback value will be used (2 / ja-jp).", LogType.Warning, true); + return 2; + } + + regValue = Encoding.UTF8.GetString(value).AsSpan().Trim('\0').ToString(); + return GetStarRailVoiceLanguageByName(regValue); + } + catch (JsonException ex) + { + LogWriteLine($"System.Text.Json cannot deserialize language ID registry in this path: {RegPath}\r\nFallback value will be used (2 / ja-jp).\r\n{ex}", LogType.Warning, true); + return 2; + } + catch (Exception ex) + { + LogWriteLine($"Launcher cannot evaluate an existing language ID registry on {RegPath}\r\nFallback value will be used (2 / ja-jp).\r\n{ex}", LogType.Warning, true); + return 2; + } + } + + private int GetVoiceLanguageID_Genshin(string RegPath) + { + try + { + ReadOnlySpan regValue; + RegistryKey? keys = Registry.CurrentUser.OpenSubKey(ConfigRegistryLocation!); + byte[]? value = (byte[]?)keys?.GetValue("GENERAL_DATA_h2389025596"); + + if (keys is null || value is null || value.Length is 0) + { + LogWriteLine($"Voice Language ID registry on {RegPath} doesn't exist. Fallback value will be used (2 / ja-jp).", LogType.Warning, true); + return 2; + } + + regValue = Encoding.UTF8.GetString(value).AsSpan().Trim('\0'); + GeneralDataProp? RegValues = (GeneralDataProp?)JsonSerializer.Deserialize(new string(regValue), typeof(GeneralDataProp), InternalAppJSONContext.Default); + return RegValues?.deviceVoiceLanguageType ?? 2; + } + catch (JsonException ex) + { + LogWriteLine($"System.Text.Json cannot deserialize language ID registry in this path: {RegPath}\r\nFallback value will be used (2 / ja-jp).\r\n{ex}", LogType.Warning, true); + return 2; + } + catch (Exception ex) + { + LogWriteLine($"Launcher cannot evaluate an existing language ID registry on {RegPath}\r\nFallback value will be used (2 / ja-jp).\r\n{ex}", LogType.Warning, true); + return 2; + } + } + + // WARNING!!! + // This feature is only available for Genshin and Star Rail. + public void SetVoiceLanguageID(int LangID) + { + switch (GameType) + { + case GameNameType.Genshin: + SetVoiceLanguageID_Genshin(LangID); + break; + case GameNameType.StarRail: + SetVoiceLanguageID_StarRail(LangID); + break; + } + } + + public int GetStarRailVoiceLanguageByName(string name) => name switch + { + "cn" => 0, + "tw" => 1, + "en" => 2, + "jp" => 3, + "kr" => 4, + _ => 3 // Set to JP by default + }; + + public string GetStarRailVoiceLanguageByID(int id) => id switch + { + 0 => "cn", + 1 => "cn", // Force Traditional Chinese value to use Simplified Chinese since they shared the same VO files (as provided by the API) + 2 => "en", + 3 => "jp", + 4 => "kr", + _ => "jp" // Set to JP by default + }; + + public string GetStarRailVoiceLanguageFullNameByID(int id) => id switch + { + 0 => "Chinese(PRC)", + 1 => "Chinese(PRC)", // Force Traditional Chinese value to use Simplified Chinese since they shared the same VO files (as provided by the API) + 2 => "English", + 3 => "Japanese", + 4 => "Korean", + _ => "Japanese" // Set to JP by default + }; + + public string GetStarRailVoiceLanguageFullNameByName(string name) => name switch + { + "cn" => "Chinese(PRC)", + "tw" => "Chinese(PRC)", // Force Traditional Chinese value to use Simplified Chinese since they shared the same VO files (as provided by the API) + "en" => "English", + "jp" => "Japanese", + "kr" => "Korean", + _ => "Japanese" // Set to JP by default + }; + + private void SetVoiceLanguageID_Genshin(int LangID) + { + try + { + RegistryKey? keys = Registry.CurrentUser.OpenSubKey(ConfigRegistryLocation!, true); + if (keys is null) keys = Registry.CurrentUser.CreateSubKey(ConfigRegistryLocation!); + var result = (byte[]?)keys.GetValue("GENERAL_DATA_h2389025596"); + var initValue = new GeneralDataProp(); + if (result != null) + { + ReadOnlySpan regValue = Encoding.UTF8.GetString(result).AsSpan().Trim('\0'); + initValue = (GeneralDataProp?)JsonSerializer.Deserialize(new string(regValue), typeof(GeneralDataProp), InternalAppJSONContext.Default) ?? initValue; + } + + initValue.deviceVoiceLanguageType = LangID; + keys.SetValue("GENERAL_DATA_h2389025596", + Encoding.UTF8.GetBytes( + JsonSerializer.Serialize(initValue, typeof(GeneralDataProp), InternalAppJSONContext.Default) + '\0')); + } + catch (Exception ex) + { + LogWriteLine($"Cannot save voice language ID: {LangID} to the registry!\r\n{ex}", LogType.Error, true); + } + } + + private void SetVoiceLanguageID_StarRail(int LangID) + { + try + { + RegistryKey? keys = Registry.CurrentUser.OpenSubKey(ConfigRegistryLocation!, true); + if (keys is null) keys = Registry.CurrentUser.CreateSubKey(ConfigRegistryLocation!); + + string initValue = GetStarRailVoiceLanguageByID(LangID); + keys.SetValue("LanguageSettings_LocalAudioLanguage_h882585060", Encoding.UTF8.GetBytes(initValue + '\0')); + } + catch (Exception ex) + { + LogWriteLine($"Cannot save voice language ID: {LangID} to the registry!\r\n{ex}", LogType.Error, true); + } + } + + #endregion + + #region Game Data Template Handler + public byte[]? GetGameDataTemplate(string key, byte[] gameVersion) + { + if (GameDataTemplates == null || !GameDataTemplates.ContainsKey(key)) return null; + + GameDataTemplate value = GameDataTemplates[key]; + if (value.DataVersion == null) return null; + + int verInt = GameDataVersion.GetBytesToIntVersion(gameVersion); + if (!value.DataVersion.ContainsKey(verInt)) return null; + + GameDataVersion verData = value.DataVersion[verInt]; + + if (!DataCooker.IsServeV3Data(verData.Data)) + return verData.Data; + + DataCooker.GetServeV3DataSize(verData.Data, out long compressedSize, out long decompressedSize); + byte[] outData = new byte[decompressedSize]; + DataCooker.ServeV3Data(verData.Data, outData, (int)compressedSize, (int)decompressedSize, out int _); + return outData; + } + #endregion + + #region Genshin Registry Handler + // WARNING!!! + // This feature is only available for Genshin. + public int GetRegServerNameID() + { + if (!IsGenshin ?? true) return 0; + RegistryKey? keys = Registry.CurrentUser.OpenSubKey(ConfigRegistryLocation!); + byte[]? value = (byte[]?)keys?.GetValue("GENERAL_DATA_h2389025596", new byte[] { }, RegistryValueOptions.None); + + if (keys is null || value is null || value.Length is 0) + { + LogWriteLine($"Server name ID registry on \u001b[32;1m{Path.GetFileName(ConfigRegistryLocation)}\u001b[0m doesn't exist. Fallback value will be used (0 / USA).", LogType.Warning, true); + return 0; + } + + string regValue = new string(Encoding.UTF8.GetString(value).AsSpan().Trim('\0')); + + try + { + return (int?)(((GeneralDataProp?)JsonSerializer.Deserialize(regValue, typeof(GeneralDataProp), InternalAppJSONContext.Default))?.selectedServerName) ?? 0; + } + catch (Exception ex) + { + LogWriteLine($"Error while getting existing GENERAL_DATA_h2389025596 value on {ZoneFullname}! Returning value 0 as fallback!\r\nValue: {regValue}\r\n{ex}", LogType.Warning, true); + return 0; + } + } + #endregion + + #region Game Configs Check + public bool CheckExistingGame() + { + try + { + string? Value = (string?)Registry.GetValue(InstallRegistryLocation!, "InstallPath", null); + return TryCheckGameLocation(Value); + } + catch (Exception e) + { + LogWriteLine($"{e}", LogType.Error, true); + return false; + } + } + + public bool TryCheckGameLocation(in string? Path) + { + if (string.IsNullOrEmpty(Path)) return CheckInnerGameConfig(DefaultGameLocation!); + if (Directory.Exists(Path)) return CheckInnerGameConfig(Path); + + return false; + } + + private bool CheckInnerGameConfig(in string GamePath) + { + string ConfigPath = Path.Combine(GamePath, "config.ini"); + if (!File.Exists(ConfigPath)) return false; + + IniFile Ini = new IniFile(); + Ini.Load(ConfigPath); + + string? path1 = Ini["launcher"]!["game_install_path"].ToString(); + + if (path1 == null) return false; + + ActualGameDataLocation = ConverterTool.NormalizePath(path1); + + return File.Exists(Path.Combine(ActualGameDataLocation!, "config.ini")) && File.Exists(Path.Combine(ActualGameDataLocation!, GameExecutableName!)); + } + #endregion + } +} diff --git a/CollapseLauncher/Classes/Helper/Metadata/Stamp.cs b/CollapseLauncher/Classes/Helper/Metadata/Stamp.cs new file mode 100644 index 000000000..fe3c6ff2c --- /dev/null +++ b/CollapseLauncher/Classes/Helper/Metadata/Stamp.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +#nullable enable +namespace CollapseLauncher.Helper.Metadata +{ + public class Stamp + { + public long LastUpdated { get; set; } + public string? MetadataPath { get; set; } = null; + public MetadataType? MetadataType { get; set; } = null; + public bool? MetadataInclude { get; set; } = null; + public string? GameName { get; set; } = null; + public string? GameRegion { get; set; } = null; + } +} diff --git a/CollapseLauncher/Classes/Helper/Metadata/SteamGameProp.cs b/CollapseLauncher/Classes/Helper/Metadata/SteamGameProp.cs new file mode 100644 index 000000000..138df0f68 --- /dev/null +++ b/CollapseLauncher/Classes/Helper/Metadata/SteamGameProp.cs @@ -0,0 +1,9 @@ +#nullable enable +namespace CollapseLauncher.Helper.Metadata +{ + public class SteamGameProp + { + public string? URL { get; set; } + public string? MD5 { get; set; } + } +} diff --git a/CollapseLauncher/Classes/InstallManagement/BaseClass/InstallManagerBase.cs b/CollapseLauncher/Classes/InstallManagement/BaseClass/InstallManagerBase.cs index 88b2be8cb..d46a2c844 100644 --- a/CollapseLauncher/Classes/InstallManagement/BaseClass/InstallManagerBase.cs +++ b/CollapseLauncher/Classes/InstallManagement/BaseClass/InstallManagerBase.cs @@ -1,13 +1,13 @@ using CollapseLauncher.CustomControls; using CollapseLauncher.Extension; using CollapseLauncher.FileDialogCOM; +using CollapseLauncher.Helper.Metadata; using CollapseLauncher.Interfaces; using CollapseLauncher.Pages; using Hi3Helper; using Hi3Helper.Data; using Hi3Helper.EncTool.Parser.AssetIndex; using Hi3Helper.Http; -using Hi3Helper.Preset; using Hi3Helper.Shared.ClassStruct; using Hi3Helper.Shared.Region; using Microsoft.UI.Text; @@ -1401,7 +1401,7 @@ private bool TryGetExistingBHI3LPath(ref string OutputPath) { // Try parsing the config value = Encoding.UTF8.GetString(keyValue); - config = value.Deserialize(CoreLibraryJSONContext.Default); + config = value.Deserialize(InternalAppJSONContext.Default); } catch (Exception ex) { diff --git a/CollapseLauncher/Classes/InstallManagement/GameConversionManagement.cs b/CollapseLauncher/Classes/InstallManagement/GameConversionManagement.cs index da79d2312..b3d6c20df 100644 --- a/CollapseLauncher/Classes/InstallManagement/GameConversionManagement.cs +++ b/CollapseLauncher/Classes/InstallManagement/GameConversionManagement.cs @@ -1,4 +1,5 @@ -using Hi3Helper; +using CollapseLauncher.Helper.Metadata; +using Hi3Helper; using Hi3Helper.Http; using Hi3Helper.Preset; using Hi3Helper.Shared.ClassStruct; @@ -22,7 +23,7 @@ public class GameConversionManagement : IDisposable { public event EventHandler ProgressChanged; - private PresetConfigV2 SourceProfile, TargetProfile; + private PresetConfig SourceProfile, TargetProfile; private List SourceFileManifest; private List TargetFileManifest; private Http _http; @@ -36,7 +37,7 @@ public class GameConversionManagement : IDisposable string ConvertStatus, ConvertDetail; byte DownloadThread; - public GameConversionManagement(PresetConfigV2 SourceProfile, PresetConfigV2 TargetProfile, + internal GameConversionManagement(PresetConfig SourceProfile, PresetConfig TargetProfile, string BaseURL, string GameVersion, string CookbookPath, CancellationToken Token = new CancellationToken()) { this._http = new Http(); diff --git a/CollapseLauncher/Classes/Interfaces/Class/CommunityToolsProperty.cs b/CollapseLauncher/Classes/Interfaces/Class/CommunityToolsProperty.cs index ef40ca98b..78e02ef70 100644 --- a/CollapseLauncher/Classes/Interfaces/Class/CommunityToolsProperty.cs +++ b/CollapseLauncher/Classes/Interfaces/Class/CommunityToolsProperty.cs @@ -1,4 +1,5 @@ -using Hi3Helper; +using CollapseLauncher.Helper.Metadata; +using Hi3Helper; using Hi3Helper.Preset; using Hi3Helper.Shared.Region; using System; @@ -11,8 +12,8 @@ namespace CollapseLauncher { public class CommunityToolsProperty { - public Dictionary> OfficialToolsDictionary { get; set; } - public Dictionary> CommunityToolsDictionary { get; set; } + public Dictionary> OfficialToolsDictionary { get; set; } + public Dictionary> CommunityToolsDictionary { get; set; } [JsonIgnore(Condition = JsonIgnoreCondition.Always)] public ObservableCollection OfficialToolsList; diff --git a/CollapseLauncher/Classes/Interfaces/Class/Structs.cs b/CollapseLauncher/Classes/Interfaces/Class/Structs.cs index 5965bfce7..ebdaf36bb 100644 --- a/CollapseLauncher/Classes/Interfaces/Class/Structs.cs +++ b/CollapseLauncher/Classes/Interfaces/Class/Structs.cs @@ -1,6 +1,6 @@ -using Hi3Helper.Data; +using CollapseLauncher.Helper.Metadata; +using Hi3Helper.Data; using Hi3Helper.Http; -using Hi3Helper.Preset; using System; using System.IO; diff --git a/CollapseLauncher/Classes/Interfaces/IGameVersionCheck.cs b/CollapseLauncher/Classes/Interfaces/IGameVersionCheck.cs index 876bead41..56e5c28e2 100644 --- a/CollapseLauncher/Classes/Interfaces/IGameVersionCheck.cs +++ b/CollapseLauncher/Classes/Interfaces/IGameVersionCheck.cs @@ -1,5 +1,5 @@ using CollapseLauncher.GameVersioning; -using Hi3Helper.Preset; +using CollapseLauncher.Helper.Metadata; using Hi3Helper.Shared.ClassStruct; using System.Collections.Generic; @@ -37,7 +37,7 @@ internal interface IGameVersionCheck /// /// Returns or sets the game preset /// - PresetConfigV2 GamePreset { get; set; } + PresetConfig GamePreset { get; set; } /// /// Returns or set the API properties @@ -47,7 +47,7 @@ internal interface IGameVersionCheck /// /// Returns the type of the game /// - GameType GameType { get; } + GameNameType GameType { get; } /// /// Returns the name of the engine output log file diff --git a/CollapseLauncher/Classes/Properties/InnerLauncherConfig.cs b/CollapseLauncher/Classes/Properties/InnerLauncherConfig.cs index e8f29fe32..74c2ee77e 100644 --- a/CollapseLauncher/Classes/Properties/InnerLauncherConfig.cs +++ b/CollapseLauncher/Classes/Properties/InnerLauncherConfig.cs @@ -1,8 +1,8 @@ -using CollapseLauncher.Extension; +using CollapseLauncher.DiscordPresence; +using CollapseLauncher.Extension; +using CollapseLauncher.Helper.Metadata; using CollapseLauncher.Pages; using Hi3Helper; -using Hi3Helper.Http; -using Hi3Helper.Preset; using Hi3Helper.Shared.ClassStruct; using Microsoft.UI.Text; using Microsoft.UI.Windowing; @@ -13,13 +13,10 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Threading.Tasks; using Windows.Foundation; using Windows.UI; using Windows.UI.ViewManagement; using static Hi3Helper.InvokeProp; -using static Hi3Helper.Logger; -using static Hi3Helper.Preset.ConfigV2Store; using static Hi3Helper.Shared.Region.LauncherConfig; namespace CollapseLauncher @@ -70,6 +67,11 @@ public enum AppMode public static GameVersion AppCurrentVersion; public static Color SystemAppTheme { get => new UISettings().GetColorValue(UIColorType.Background); } public static AppThemeMode CurrentAppTheme; +#if !DISABLEDISCORD +#pragma warning disable CA2211 + public static DiscordPresenceManager AppDiscordPresence; +#pragma warning restore CA2211 +#endif public static bool IsAppThemeLight { get => CurrentAppTheme switch @@ -90,7 +92,7 @@ public static string GetComboBoxGameRegionValue(object obj) public static List BuildGameTitleListUI() { List list = new List(); - foreach (string title in ConfigV2GameCategory) + foreach (string title in LauncherMetadataHelper.GetGameNameCollection()) { StackPanel panel = UIElementExtensions.CreateStackPanel(Orientation.Horizontal); TextBlock gameTitleTextBlock = panel.AddElementToStackPanel(new TextBlock { Text = title }); @@ -105,18 +107,18 @@ public static List BuildGameTitleListUI() public static List BuildGameRegionListUI(string GameCategory, List GameCategoryList = null) { - GameCategoryList ??= ConfigV2GameRegions; + GameCategoryList ??= LauncherMetadataHelper.GetGameRegionCollection(GameCategory); List list = new List(); foreach (string region in GameCategoryList) { - PresetConfigV2 config = ConfigV2.MetadataV2[GameCategory][region]; + PresetConfig config = LauncherMetadataHelper.GetMetadataConfig(GameCategory, region); StackPanel panel = UIElementExtensions.CreateStackPanel(Orientation.Horizontal); TextBlock gameRegionTextBlock = panel.AddElementToStackPanel(new TextBlock { Text = region }); TextBlock gameRegionTranslatedTextBlock = GetGameTitleRegionTranslationTextBlock(ref gameRegionTextBlock, Locale.Lang._GameClientRegions); if (gameRegionTranslatedTextBlock != null) panel.AddElementToStackPanel(gameRegionTranslatedTextBlock); - if (config.IsExperimental) + if (config.Channel != GameChannel.Stable) { Grid expTag = UIElementExtensions.CreateGrid() .WithPadding(4d, 0d) @@ -130,7 +132,7 @@ public static List BuildGameRegionListUI(string GameCategory, List CheckForNewConfigV2() - { - Stamp ConfigStamp = null; - - try - { - await using BridgedNetworkStream s = await FallbackCDNUtil.TryGetCDNFallbackStream(string.Format(AppGameConfigV2URLPrefix, (IsPreview ? "preview" : "stable") + "stamp"), default); - ConfigStamp = await s.DeserializeAsync(CoreLibraryJSONContext.Default); - } - catch (Exception ex) - { - LogWriteLine($"Failed while checking for new metadata!\r\n{ex}", LogType.Error, true); - return false; - } - - bool isMetadataOutdated = ConfigV2LastUpdate < ConfigStamp?.LastUpdated; - LogWriteLine($"Checking for metadata update...\r\n" + - $" LocalStamp : {ConfigV2LastUpdate}\r\n" + - $" RemoteStamp : {ConfigStamp?.LastUpdated}\r\n" + - $" Out of date?: {isMetadataOutdated}", LogType.Warning, true); - return isMetadataOutdated; - } - - public static async Task DownloadConfigV2Files(bool Stamp, bool Content) - { - using (Http _httpClient = new Http()) - { - if (!Directory.Exists(AppGameConfigMetadataFolder)) - Directory.CreateDirectory(AppGameConfigMetadataFolder); - - if (Stamp) await GetConfigV2Content(_httpClient, "stamp", AppGameConfigV2StampPath); - if (Content) await GetConfigV2Content(_httpClient, "config", AppGameConfigV2MetadataPath); - } - } - - private static async Task GetConfigV2Content(Http _httpClient, string prefix, string output) - { - string URL = string.Format(AppGameConfigV2URLPrefix, (IsPreview ? "preview" : "stable") + prefix); - - await using FileStream fs = new FileStream(output, FileMode.Create, FileAccess.Write); - await using BridgedNetworkStream networkStream = await FallbackCDNUtil.TryGetCDNFallbackStream(URL, default); - await networkStream.CopyToAsync(fs); - } - internal static bool IsWindowCurrentlyFocused() { IntPtr currentForegroundWindow = GetForegroundWindow(); diff --git a/CollapseLauncher/Classes/RegionManagement/RegionManagement.cs b/CollapseLauncher/Classes/RegionManagement/RegionManagement.cs index a0b53ca68..412349e8a 100644 --- a/CollapseLauncher/Classes/RegionManagement/RegionManagement.cs +++ b/CollapseLauncher/Classes/RegionManagement/RegionManagement.cs @@ -1,9 +1,9 @@ using CollapseLauncher.Helper.Loading; +using CollapseLauncher.Helper.Metadata; using CollapseLauncher.Pages; using CollapseLauncher.Statics; using Hi3Helper; using Hi3Helper.Data; -using Hi3Helper.Preset; using Hi3Helper.Shared.ClassStruct; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; @@ -21,7 +21,6 @@ using static CollapseLauncher.RegionResourceListHelper; using static Hi3Helper.Locale; using static Hi3Helper.Logger; -using static Hi3Helper.Preset.ConfigV2Store; using static Hi3Helper.Shared.Region.LauncherConfig; namespace CollapseLauncher @@ -63,12 +62,12 @@ private enum ResourceLoadingType private uint LoadTimeoutStep = 5; // Step 5 seconds for each timeout retries private CancellationTokenSourceWrapper CurrentRegionLoadTokenSource; - private string RegionToChangeName { get => $"{GetGameTitleRegionTranslationString(CurrentConfigV2GameCategory, Lang._GameClientTitles)} - {GetGameTitleRegionTranslationString(CurrentConfigV2GameRegion, Lang._GameClientRegions)}"; } + private string RegionToChangeName { get => $"{GetGameTitleRegionTranslationString(LauncherMetadataHelper.CurrentMetadataConfigGameName, Lang._GameClientTitles)} - {GetGameTitleRegionTranslationString(LauncherMetadataHelper.CurrentMetadataConfigGameRegion, Lang._GameClientRegions)}"; } private List LastNavigationItem; private HomeMenuPanel LastRegionNewsProp; public static string PreviousTag = string.Empty; - public async Task LoadRegionFromCurrentConfigV2(PresetConfigV2 preset) + internal async Task LoadRegionFromCurrentConfigV2(PresetConfig preset) { IsExplicitCancel = false; LogWriteLine($"Initializing {RegionToChangeName}...", LogType.Scheme, true); @@ -142,7 +141,7 @@ public void ClearMainPageState() ResetRegionProp(); } - private async ValueTask TryLoadResourceInfo(ResourceLoadingType resourceType, PresetConfigV2 preset, bool ShowLoadingMsg = true) + private async ValueTask TryLoadResourceInfo(ResourceLoadingType resourceType, PresetConfig preset, bool ShowLoadingMsg = true) { uint CurrentTimeout = resourceType == ResourceLoadingType.DownloadBackground ? BackgroundImageLoadTimeout : LoadTimeout; uint RetryCount = 0; @@ -197,9 +196,9 @@ private async ValueTask TryLoadResourceInfo(ResourceLoadingType resourceTy return false; } - private async ValueTask FetchLauncherLocalizedResources(CancellationToken Token, PresetConfigV2 Preset) + private async ValueTask FetchLauncherLocalizedResources(CancellationToken Token, PresetConfig Preset) { - regionBackgroundProp = Preset.LauncherSpriteURLMultiLang ? + regionBackgroundProp = Preset.LauncherSpriteURLMultiLang ?? false ? await TryGetMultiLangResourceProp(Token, Preset) : await TryGetSingleLangResourceProp(Token, Preset); @@ -315,7 +314,7 @@ internal static async ValueTask TryDownloadToCompleteness(string url, FileInfo f } } - private string GetDeviceId(PresetConfigV2 Preset) + private string GetDeviceId(PresetConfig Preset) { var deviceId = (string)Registry.GetValue(Preset.InstallRegistryLocation, "UUID", null); if (deviceId == null) @@ -328,7 +327,7 @@ private string GetDeviceId(PresetConfigV2 Preset) return deviceId; } - private async ValueTask FetchLauncherDownloadInformation(CancellationToken token, PresetConfigV2 Preset) + private async ValueTask FetchLauncherDownloadInformation(CancellationToken token, PresetConfig Preset) { _gameAPIProp = await FallbackCDNUtil.DownloadAsJSONType(Preset.LauncherResourceURL, InternalAppJSONContext.Default, token); if (!string.IsNullOrEmpty(Preset.LauncherPluginURL)) @@ -379,22 +378,22 @@ private async ValueTask FetchLauncherDownloadInformation(CancellationToken token #endif } - private async ValueTask TryGetMultiLangResourceProp(CancellationToken Token, PresetConfigV2 Preset) + private async ValueTask TryGetMultiLangResourceProp(CancellationToken Token, PresetConfig Preset) { RegionResourceProp ret = await GetMultiLangResourceProp(Lang.LanguageID.ToLower(), Token, Preset); return ret.data.adv == null || ((ret.data.adv.version ?? 5) <= 4 - && Preset.GameType == GameType.Honkai) ? + && Preset.GameType == GameNameType.Honkai) ? await GetMultiLangResourceProp(Preset.LauncherSpriteURLMultiLangFallback ?? "en-us", Token, Preset) : ret; } - private async ValueTask GetMultiLangResourceProp(string langID, CancellationToken token, PresetConfigV2 Preset) + private async ValueTask GetMultiLangResourceProp(string langID, CancellationToken token, PresetConfig Preset) => await FallbackCDNUtil.DownloadAsJSONType(string.Format(Preset.LauncherSpriteURL, langID), InternalAppJSONContext.Default, token); - private async ValueTask TryGetSingleLangResourceProp(CancellationToken token, PresetConfigV2 Preset) + private async ValueTask TryGetSingleLangResourceProp(CancellationToken token, PresetConfig Preset) => await FallbackCDNUtil.DownloadAsJSONType(Preset.LauncherSpriteURL, InternalAppJSONContext.Default, token); private void ResetRegionProp() @@ -409,7 +408,7 @@ private void ResetRegionProp() }; } - private void GetLauncherAdvInfo(CancellationToken Token, PresetConfigV2 Preset) + private void GetLauncherAdvInfo(CancellationToken Token, PresetConfig Preset) { if (regionBackgroundProp.data.icon.Count == 0) return; @@ -449,7 +448,7 @@ private void GetLauncherAdvInfo(CancellationToken Token, PresetConfigV2 Preset) } string desc = url; - if (!Preset.IsHideSocMedDesc) + if (!Preset.IsHideSocMedDesc ?? false) { desc = item.tittle; if (string.IsNullOrEmpty(desc) && links.Any() && !string.IsNullOrEmpty(links[0].title)) @@ -561,7 +560,7 @@ private uint SendTimeoutCancelationMessage(Exception ex, uint currentTimeout, bo if (ShowLoadingMsg) { // Send the message to loading status - LoadingMessageHelper.SetMessage(null, string.Format(Lang._MainPage.RegionLoadingSubtitleTimeOut, $"{CurrentConfigV2GameCategory} - {CurrentConfigV2GameRegion}", currentTimeout)); + LoadingMessageHelper.SetMessage(null, string.Format(Lang._MainPage.RegionLoadingSubtitleTimeOut, $"{LauncherMetadataHelper.CurrentMetadataConfigGameName} - {LauncherMetadataHelper.CurrentMetadataConfigGameRegion}", currentTimeout)); if (!IsLoadRegionCancellationRequestEnabled) { IsLoadRegionCancellationRequestEnabled = true; @@ -570,7 +569,7 @@ private uint SendTimeoutCancelationMessage(Exception ex, uint currentTimeout, bo } // Send the exception without changing into the Error page - LogWriteLine($"Loading Game: {CurrentConfigV2GameCategory} - {CurrentConfigV2GameRegion} has timed-out (> {currentTimeout} seconds). Retrying...", Hi3Helper.LogType.Warning); + LogWriteLine($"Loading Game: {LauncherMetadataHelper.CurrentMetadataConfigGameName} - {LauncherMetadataHelper.CurrentMetadataConfigGameRegion} has timed-out (> {currentTimeout} seconds). Retrying...", Hi3Helper.LogType.Warning); ErrorSender.SendExceptionWithoutPage(ex, ErrorType.Connection); }); @@ -616,7 +615,7 @@ private async void WatchAndCancelIfTimeout(CancellationTokenSourceWrapper TokenS } } - private void FinalizeLoadRegion(PresetConfigV2 preset) + private void FinalizeLoadRegion(PresetConfig preset) { // Log if region has been successfully loaded LogWriteLine($"Initializing Region {preset.ZoneFullname} Done!", LogType.Scheme, true); @@ -628,7 +627,7 @@ private void FinalizeLoadRegion(PresetConfigV2 preset) InitializeNavigationItems(); } - private void LoadGameStaticsByGameType(PresetConfigV2 preset) + private void LoadGameStaticsByGameType(PresetConfig preset) { GamePropertyVault.AttachNotifForCurrentGame(); DisposeAllPageStatics(); @@ -743,11 +742,11 @@ private async Task LoadRegionRootButton() // Set and Save CurrentRegion in AppConfig SetAndSaveConfigValue("GameCategory", GameCategory); - SetPreviousGameRegion(GameCategory, GameRegion); + LauncherMetadataHelper.SetPreviousGameRegion(GameCategory, GameRegion); // Load Game ConfigV2 List before loading the region IsLoadRegionComplete = false; - PresetConfigV2 Preset = LoadCurrentConfigV2(GameCategory, GameRegion); + PresetConfig Preset = LauncherMetadataHelper.GetMetadataConfig(GameCategory, GameRegion); // Start region loading ShowAsyncLoadingTimedOutPill(); diff --git a/CollapseLauncher/Classes/RepairManagement/Genshin/Fetch.cs b/CollapseLauncher/Classes/RepairManagement/Genshin/Fetch.cs index 28215d7fa..0c4183d84 100644 --- a/CollapseLauncher/Classes/RepairManagement/Genshin/Fetch.cs +++ b/CollapseLauncher/Classes/RepairManagement/Genshin/Fetch.cs @@ -1,4 +1,5 @@ using CollapseLauncher.GameVersioning; +using CollapseLauncher.Helper.Metadata; using Hi3Helper; using Hi3Helper.Data; using Hi3Helper.EncTool; @@ -15,7 +16,6 @@ using static Hi3Helper.Data.ConverterTool; using static Hi3Helper.Locale; using static Hi3Helper.Logger; -using static Hi3Helper.Preset.ConfigV2Store; namespace CollapseLauncher { @@ -454,7 +454,7 @@ private YSDispatchDec InitializeMasterDecryptor() YSDispatchDec decryptor = new YSDispatchDec(); // Initialize the master key - decryptor.InitMasterKey(ConfigV2.MasterKey, ConfigV2.MasterKeyBitLength, RSAEncryptionPadding.Pkcs1); + decryptor.InitMasterKey(LauncherMetadataHelper.CurrentMasterKey.Key, LauncherMetadataHelper.CurrentMasterKey.BitSize, RSAEncryptionPadding.Pkcs1); // Return the decryptor return decryptor; diff --git a/CollapseLauncher/Classes/ShortcutCreator/ShortcutCreator.cs b/CollapseLauncher/Classes/ShortcutCreator/ShortcutCreator.cs index edd70518c..ff7047006 100644 --- a/CollapseLauncher/Classes/ShortcutCreator/ShortcutCreator.cs +++ b/CollapseLauncher/Classes/ShortcutCreator/ShortcutCreator.cs @@ -1,4 +1,4 @@ -using Hi3Helper.Preset; +using CollapseLauncher.Helper.Metadata; using Microsoft.Win32; using System.IO; using System.Linq; @@ -9,18 +9,18 @@ namespace CollapseLauncher.ShortcutUtils { public static class ShortcutCreator { - public static void CreateShortcut(string path, PresetConfigV2 preset, bool play = false) + internal static void CreateShortcut(string path, PresetConfig preset, bool play = false) { string shortcutName = string.Format("{0} ({1}) - Collapse Launcher.url", preset.GameName, preset.ZoneName).Replace(":", ""); string url = string.Format("collapse://open -g \"{0}\" -r \"{1}\"", preset.GameName, preset.ZoneName); - + if (play) url += " -p"; string icon = Path.Combine(Path.GetDirectoryName(AppExecutablePath), "Assets/Images/GameIcon/" + preset.GameType switch { - GameType.StarRail => "icon-starrail.ico", - GameType.Genshin => "icon-genshin.ico", + GameNameType.StarRail => "icon-starrail.ico", + GameNameType.Genshin => "icon-genshin.ico", _ => "icon-honkai.ico", }); @@ -37,7 +37,7 @@ public static void CreateShortcut(string path, PresetConfigV2 preset, bool play /// Source: /// https://github.com/Heroic-Games-Launcher/HeroicGamesLauncher/blob/8bdee1383446d3b81e240a4300baaf337d48ec92/src/backend/shortcuts/nonesteamgame/nonesteamgame.ts - public static bool AddToSteam(PresetConfigV2 preset, bool play) + internal static bool AddToSteam(PresetConfig preset, bool play) { var paths = GetShortcutsPath(); @@ -52,7 +52,7 @@ public static bool AddToSteam(PresetConfigV2 preset, bool play) string userId = splitPath[splitPath.Length - 3]; parser.Insert(preset, play); - + parser.Save(); LogWriteLine(string.Format("[ShortcutCreator::AddToSteam] Added shortcut for {0} - {1} for Steam3ID {2} ", preset.GameName, preset.ZoneName, userId)); } @@ -60,7 +60,7 @@ public static bool AddToSteam(PresetConfigV2 preset, bool play) return true; } - public static bool IsAddedToSteam(PresetConfigV2 preset) + internal static bool IsAddedToSteam(PresetConfig preset) { var paths = GetShortcutsPath(); @@ -100,7 +100,7 @@ private static string[] GetShortcutsPath() { string y = x.Split("\\").Last(); return y != "ac" && y != "0" && y != "anonymous"; - } ).ToArray(); + }).ToArray(); for (int i = 0; i < res.Length; i++) { diff --git a/CollapseLauncher/Classes/ShortcutCreator/SteamShortcut.cs b/CollapseLauncher/Classes/ShortcutCreator/SteamShortcut.cs index b68773b7d..eefd1939f 100644 --- a/CollapseLauncher/Classes/ShortcutCreator/SteamShortcut.cs +++ b/CollapseLauncher/Classes/ShortcutCreator/SteamShortcut.cs @@ -1,5 +1,5 @@ -using Hi3Helper.Data; -using Hi3Helper.Preset; +using CollapseLauncher.Helper.Metadata; +using Hi3Helper.Data; using System; using System.Buffers; using System.Collections.Generic; @@ -41,9 +41,9 @@ public sealed class SteamShortcut public string tags = ""; #endregion - public SteamShortcut() { } + internal SteamShortcut() { } - public SteamShortcut(PresetConfigV2 preset, bool play = false) + internal SteamShortcut(PresetConfig preset, bool play = false) { AppName = string.Format("{0} - {1}", preset.GameName, preset.ZoneName); @@ -109,7 +109,7 @@ private static uint GenerateGridId(string exe, string appname) return (appId >> 32) - 0x10000000; } - public void MoveImages(string path, PresetConfigV2 preset) + internal void MoveImages(string path, PresetConfig preset) { if (preset == null) return; @@ -120,8 +120,8 @@ public void MoveImages(string path, PresetConfigV2 preset) string iconName = preset.GameType switch { - GameType.StarRail => "icon-starrail.ico", - GameType.Genshin => "icon-genshin.ico", + GameNameType.StarRail => "icon-starrail.ico", + GameNameType.Genshin => "icon-genshin.ico", _ => "icon-honkai.ico", }; diff --git a/CollapseLauncher/Classes/ShortcutCreator/SteamShortcutParser.cs b/CollapseLauncher/Classes/ShortcutCreator/SteamShortcutParser.cs index a439db83c..5508725ac 100644 --- a/CollapseLauncher/Classes/ShortcutCreator/SteamShortcutParser.cs +++ b/CollapseLauncher/Classes/ShortcutCreator/SteamShortcutParser.cs @@ -1,4 +1,4 @@ -using Hi3Helper.Preset; +using CollapseLauncher.Helper.Metadata; using System.Collections.Generic; using System.IO; using System.Linq; @@ -25,7 +25,7 @@ public SteamShortcutParser(string path) public bool Contains(SteamShortcut shortcut) => _shortcuts.FindIndex(x => x.preliminaryAppID == shortcut.preliminaryAppID) != -1; - public void Insert(PresetConfigV2 preset, bool play = false) + internal void Insert(PresetConfig preset, bool play = false) { SteamShortcut shortcut = new SteamShortcut(preset, play); @@ -33,7 +33,7 @@ public void Insert(PresetConfigV2 preset, bool play = false) { _shortcuts.RemoveAll(x => x.preliminaryAppID == shortcut.preliminaryAppID); } - + _shortcuts.Add(shortcut); shortcut.MoveImages(_path, preset); } diff --git a/CollapseLauncher/CollapseLauncher.csproj b/CollapseLauncher/CollapseLauncher.csproj index d8f294c9e..efb7931b2 100644 --- a/CollapseLauncher/CollapseLauncher.csproj +++ b/CollapseLauncher/CollapseLauncher.csproj @@ -236,4 +236,64 @@ --> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CollapseLauncher/XAMLs/Invoker/Classes/Migrate.cs b/CollapseLauncher/XAMLs/Invoker/Classes/Migrate.cs index a27f22d44..242c8d877 100644 --- a/CollapseLauncher/XAMLs/Invoker/Classes/Migrate.cs +++ b/CollapseLauncher/XAMLs/Invoker/Classes/Migrate.cs @@ -1,6 +1,6 @@ -using Hi3Helper; +using CollapseLauncher.Helper.Metadata; +using Hi3Helper; using Hi3Helper.Data; -using Hi3Helper.Preset; using Microsoft.Win32; using System; using System.Collections.Generic; diff --git a/CollapseLauncher/XAMLs/MainApp/MainPage.xaml b/CollapseLauncher/XAMLs/MainApp/MainPage.xaml index da950fc5f..29c35ef30 100644 --- a/CollapseLauncher/XAMLs/MainApp/MainPage.xaml +++ b/CollapseLauncher/XAMLs/MainApp/MainPage.xaml @@ -8,7 +8,7 @@ xmlns:local="using:CollapseLauncher" xmlns:localPage="using:CollapseLauncher.Pages" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:p="using:Hi3Helper.Preset" + xmlns:p="using:CollapseLauncher.Helper.Metadata" Unloaded="Page_Unloaded" mc:Ignorable="d"> @@ -102,7 +102,7 @@ Margin="0,0,0,0" Canvas.ZIndex="2" CornerRadius="15,0,0,15" - ItemsSource="{x:Bind p:ConfigV2Store.ConfigV2GameCategory, Mode=OneWay}" + ItemsSource="{x:Bind p:LauncherMetadataHelper.LauncherGameNameCollection, Mode=OneWay}" PlaceholderText="Honkai Impact 3rd" SelectionChanged="SetGameCategoryChange" /> ? gameNameCollection = LauncherMetadataHelper.GetGameNameCollection(); + List? gameRegionCollection = LauncherMetadataHelper.GetGameRegionCollection(lastName); + + int indexOfName = gameNameCollection.IndexOf(lastName); + int indexOfRegion = gameRegionCollection.IndexOf(lastRegion); // Rebuild Game Titles and Regions ComboBox items ComboBoxGameCategory.ItemsSource = BuildGameTitleListUI(); - ComboBoxGameCategory.SelectedIndex = indexOfCategory; + ComboBoxGameCategory.SelectedIndex = indexOfName; ComboBoxGameRegion.SelectedIndex = indexOfRegion; InitializeNavigationItems(false); @@ -459,7 +452,7 @@ internal async void ChangeBackgroundImageAsRegionAsync(bool ShowLoadingMsg = fal } else { - if (!await TryLoadResourceInfo(ResourceLoadingType.DownloadBackground, CurrentConfigV2, ShowLoadingMsg)) + if (!await TryLoadResourceInfo(ResourceLoadingType.DownloadBackground, LauncherMetadataHelper.CurrentMetadataConfig, ShowLoadingMsg)) { regionBackgroundProp.imgLocalPath = AppDefaultBG; } @@ -954,27 +947,29 @@ private async void ForceShowNotificationPanel() #endregion #region Game Selector Method - private PresetConfigV2 LoadSavedGameSelection() + private PresetConfig LoadSavedGameSelection() { ComboBoxGameCategory.ItemsSource = BuildGameTitleListUI(); - string GameCategory = GetAppConfigValue("GameCategory").ToString(); + string gameName = GetAppConfigValue("GameCategory").ToString(); - if (!GetConfigV2Regions(GameCategory)) - GameCategory = ConfigV2GameCategory.FirstOrDefault(); + List? gameCollection = LauncherMetadataHelper.GetGameNameCollection(); + List? regionCollection = LauncherMetadataHelper.GetGameRegionCollection(gameName); + if (regionCollection == null) + gameName = LauncherMetadataHelper.LauncherGameNameRegionCollection.Keys.FirstOrDefault(); - ComboBoxGameRegion.ItemsSource = BuildGameRegionListUI(GameCategory); + ComboBoxGameRegion.ItemsSource = BuildGameRegionListUI(gameName); - int IndexCategory = ConfigV2GameCategory.IndexOf(GameCategory); + int IndexCategory = gameCollection.IndexOf(gameName); if (IndexCategory < 0) IndexCategory = 0; - int IndexRegion = GetPreviousGameRegion(GameCategory); + int IndexRegion = LauncherMetadataHelper.GetPreviousGameRegion(gameName); ComboBoxGameCategory.SelectedIndex = IndexCategory; ComboBoxGameRegion.SelectedIndex = IndexRegion; CurrentGameCategory = ComboBoxGameCategory.SelectedIndex; CurrentGameRegion = ComboBoxGameRegion.SelectedIndex; - return LoadCurrentConfigV2(GetComboBoxGameRegionValue(ComboBoxGameCategory.SelectedValue), GetComboBoxGameRegionValue(ComboBoxGameRegion.SelectedValue)); + return LauncherMetadataHelper.GetMetadataConfig(GetComboBoxGameRegionValue(ComboBoxGameCategory.SelectedValue), GetComboBoxGameRegionValue(ComboBoxGameRegion.SelectedValue)); } #nullable enable @@ -983,7 +978,7 @@ private void SetGameCategoryChange(object sender, SelectionChangedEventArgs e) object? selectedItem = ((ComboBox)sender).SelectedItem; if (selectedItem == null) return; string SelectedCategoryString = GetComboBoxGameRegionValue(selectedItem); - GetConfigV2Regions(SelectedCategoryString); + // REMOVED: GetConfigV2Regions(SelectedCategoryString); List CurRegionList = BuildGameRegionListUI(SelectedCategoryString); ComboBoxGameRegion.ItemsSource = CurRegionList; @@ -1005,9 +1000,9 @@ private void EnableRegionChangeButton(object sender, SelectionChangedEventArgs e string category = GetComboBoxGameRegionValue(ComboBoxGameCategory.SelectedValue); string region = GetComboBoxGameRegionValue(selValue); - PresetConfigV2 preset = ConfigV2.MetadataV2[category][region]; - ChangeRegionWarningText.Text = preset.GameChannel != GameChannel.Stable ? string.Format(Lang._MainPage.RegionChangeWarnExper1, preset.GameChannel) : string.Empty; - ChangeRegionWarning.Visibility = preset.GameChannel != GameChannel.Stable ? Visibility.Visible : Visibility.Collapsed; + PresetConfig preset = LauncherMetadataHelper.GetMetadataConfig(category, region); + ChangeRegionWarningText.Text = preset.Channel != GameChannel.Stable ? string.Format(Lang._MainPage.RegionChangeWarnExper1, preset.Channel) : string.Empty; + ChangeRegionWarning.Visibility = preset.Channel != GameChannel.Stable ? Visibility.Visible : Visibility.Collapsed; ChangeRegionConfirmBtn.IsEnabled = !LockRegionChangeBtn; ChangeRegionConfirmBtnNoWarning.IsEnabled = !LockRegionChangeBtn; @@ -1018,7 +1013,7 @@ private void EnableRegionChangeButton(object sender, SelectionChangedEventArgs e #region Metadata Update Method private async Task CheckMetadataUpdateInBackground() { - bool IsUpdate = await CheckForNewConfigV2(); + bool IsUpdate = await LauncherMetadataHelper.IsMetadataHasUpdate(); if (IsUpdate) { Button UpdateMetadatabtn = @@ -1062,7 +1057,7 @@ private async Task CheckMetadataUpdateInBackground() try { - await DownloadConfigV2Files(true, true); + await LauncherMetadataHelper.RunMetadataUpdate(); IsChangeDragArea = false; MainFrameChanger.ChangeWindowFrame(typeof(MainPage)); } @@ -1141,17 +1136,17 @@ private void InitializeNavigationItems(bool ResetSelection = true) switch (CurrentGameVersionCheck.GameType) { - case GameType.Honkai: + case GameNameType.Honkai: NavigationViewControl.MenuItems.Add(new NavigationViewItem() { Content = Lang._GameSettingsPage.PageTitle, Icon = IconGameSettings, Tag = "honkaigamesettings" }); break; - case GameType.StarRail: + case GameNameType.StarRail: NavigationViewControl.MenuItems.Add(new NavigationViewItem() { Content = Lang._StarRailGameSettingsPage.PageTitle, Icon = IconGameSettings, Tag = "starrailgamesettings" }); break; } - if (CurrentGameVersionCheck.GameType == GameType.Genshin) + if (CurrentGameVersionCheck.GameType == GameNameType.Genshin) { NavigationViewControl.MenuItems.Add(new NavigationViewItem() { Content = Lang._GenshinGameSettingsPage.PageTitle, Icon = IconGameSettings, Tag = "genshingamesettings" }); @@ -1206,7 +1201,7 @@ void NavigateInnerSwitch(string itemTag) case "caches": if (GetCurrentGameProperty()._GameVersion.GamePreset.IsCacheUpdateEnabled ?? false) - Navigate(IsGameInstalled() || (m_appMode == AppMode.Hi3CacheUpdater && GetCurrentGameProperty()._GameVersion.GamePreset.GameType == GameType.Honkai) ? typeof(CachesPage) : typeof(NotInstalledPage), itemTag); + Navigate(IsGameInstalled() || (m_appMode == AppMode.Hi3CacheUpdater && GetCurrentGameProperty()._GameVersion.GamePreset.GameType == GameNameType.Honkai) ? typeof(CachesPage) : typeof(NotInstalledPage), itemTag); else Navigate(typeof(UnavailablePage), itemTag); break; @@ -1428,7 +1423,7 @@ private void SpawnWebView2Panel(Uri URL) private int GetIndexOfRegionStringOrDefault(string category) { - int? index = GetPreviousGameRegion(category); + int? index = LauncherMetadataHelper.GetPreviousGameRegion(category); return index == -1 || index == null ? 0 : index ?? 0; } @@ -1469,7 +1464,7 @@ private void CreateKeyboardShortcutHandlers() int numIndex = 0; VirtualKeyModifiers keyModifier = KbShortcutList["GameSelection"].Modifier; - for (; numIndex <= ConfigV2.GameCount; numIndex++) + for (; numIndex <= LauncherMetadataHelper.CurrentGameNameCount; numIndex++) { KeyboardAccelerator keystroke = new KeyboardAccelerator() { @@ -1489,7 +1484,7 @@ private void CreateKeyboardShortcutHandlers() numIndex = 0; keyModifier = KbShortcutList["RegionSelection"].Modifier; - while (numIndex < ConfigV2.MaxRegionCount) + while (numIndex < LauncherMetadataHelper.CurrentGameRegionMaxCount) { KeyboardAccelerator keystroke = new KeyboardAccelerator() { @@ -1587,15 +1582,17 @@ private async void DisableKbShortcuts(int time = 500) private void RestoreCurrentRegion() { - string GameCategory = GetAppConfigValue("GameCategory").ToString(); + string gameName = GetAppConfigValue("GameCategory").ToString(); - if (!GetConfigV2Regions(GameCategory)) - GameCategory = ConfigV2GameCategory.FirstOrDefault(); + List? gameNameCollection = LauncherMetadataHelper.GetGameNameCollection(); + List? gameRegionCollection = LauncherMetadataHelper.GetGameRegionCollection(gameName); + if (gameRegionCollection == null) + gameName = gameRegionCollection.FirstOrDefault(); - int IndexCategory = ConfigV2GameCategory.IndexOf(GameCategory); + int IndexCategory = gameNameCollection.IndexOf(gameName); if (IndexCategory < 0) IndexCategory = 0; - int IndexRegion = GetPreviousGameRegion(GameCategory); + int IndexRegion = LauncherMetadataHelper.GetPreviousGameRegion(gameName); ComboBoxGameCategory.SelectedIndex = IndexCategory; ComboBoxGameRegion.SelectedIndex = IndexRegion; @@ -1685,7 +1682,7 @@ private void OpenScreenshot_Invoked(KeyboardAccelerator sender, KeyboardAccelera string ScreenshotFolder = Path.Combine(NormalizePath(GameDirPath), CurrentGameProperty._GameVersion.GamePreset.GameType switch { - GameType.StarRail => $"{Path.GetFileNameWithoutExtension(CurrentGameProperty._GameVersion.GamePreset.GameExecutableName)}_Data\\ScreenShots", + GameNameType.StarRail => $"{Path.GetFileNameWithoutExtension(CurrentGameProperty._GameVersion.GamePreset.GameExecutableName)}_Data\\ScreenShots", _ => "ScreenShot" }); @@ -1743,7 +1740,7 @@ private void ForceCloseGame_Invoked(KeyboardAccelerator sender, KeyboardAccelera { if (!CurrentGameProperty.IsGameRunning) return; - PresetConfigV2 gamePreset = CurrentGameProperty._GameVersion.GamePreset; + PresetConfig gamePreset = CurrentGameProperty._GameVersion.GamePreset; try { var gameProcess = Process.GetProcessesByName(gamePreset.GameExecutableName.Split('.')[0]); @@ -1795,13 +1792,13 @@ private void GoGameSettings_Invoked(KeyboardAccelerator sender, KeyboardAccelera NavigationViewControl.SelectedItem = NavigationViewControl.MenuItems.Last(); switch (CurrentGameProperty._GamePreset.GameType) { - case GameType.Honkai: + case GameNameType.Honkai: Navigate(typeof(HonkaiGameSettingsPage), "honkaigamesettings"); break; - case GameType.Genshin: + case GameNameType.Genshin: Navigate(typeof(GenshinGameSettingsPage), "genshingamesettings"); break; - case GameType.StarRail: + case GameNameType.StarRail: Navigate(typeof(StarRailGameSettingsPage), "starrailgamesettings"); break; } @@ -1840,38 +1837,40 @@ private bool SetActivatedRegion() string oldGameCategory = GetAppConfigValue("GameCategory").ToString(); - string GameName = args.Game; + string gameName = args.Game; - if (!GetConfigV2Regions(GameName)) + List? gameNameCollection = LauncherMetadataHelper.GetGameNameCollection(); + List? gameRegionCollection = LauncherMetadataHelper.GetGameRegionCollection(gameName); + if (gameRegionCollection == null) { - bool res = int.TryParse(args.Game, out int Game); - if (!res || Game < 0 || Game >= ConfigV2GameCategory.Count) + bool res = int.TryParse(args.Game, out int gameIndex); + if (!res || gameIndex < 0 || gameIndex >= gameNameCollection.Count) return true; - GameName = ConfigV2GameCategory[Game]; + gameName = gameNameCollection[gameIndex]; + gameRegionCollection = LauncherMetadataHelper.GetGameRegionCollection(gameName); } - - SetAndSaveConfigValue("GameCategory", GameName); - GetConfigV2Regions(GameName); + SetAndSaveConfigValue("GameCategory", gameName); if (args.Region != null) { - string GameRegion = args.Region; - if (!ConfigV2GameRegions.Contains(GameRegion)) + string gameRegion = args.Region; + if (!gameRegionCollection.Contains(gameRegion)) { - bool res = int.TryParse(args.Region, out int Region); - if (!res || Region < 0 || Region >= ConfigV2GameRegions.Count) + bool res = int.TryParse(args.Region, out int regionIndex); + if (!res || regionIndex < 0 || regionIndex >= gameRegionCollection.Count) return true; - GameRegion = ConfigV2GameRegions[Region]; + gameRegion = gameRegionCollection[regionIndex]; } - string oldGameRegion = ConfigV2GameRegions.ElementAt(GetPreviousGameRegion(GameName)); + int oldGameRegionIndex = LauncherMetadataHelper.GetPreviousGameRegion(gameName); + string oldGameRegion = gameRegionCollection.ElementAt(oldGameRegionIndex); if (oldGameRegion == null) return true; - SetPreviousGameRegion(GameName, GameRegion); - SetAndSaveConfigValue("GameRegion", GameRegion); + LauncherMetadataHelper.SetPreviousGameRegion(gameName, gameRegion); + SetAndSaveConfigValue("GameRegion", gameRegion); - if (oldGameCategory == GameName && oldGameRegion == GameRegion) + if (oldGameCategory == gameName && oldGameRegion == gameRegion) return true; } @@ -1892,7 +1891,7 @@ public void OpenAppActivation() LockRegionChangeBtn = true; - PresetConfigV2 Preset = LoadSavedGameSelection(); + PresetConfig Preset = LoadSavedGameSelection(); InvokeLoadingRegionPopup(true, Lang._MainPage.RegionLoadingTitle, RegionToChangeName); if (await LoadRegionFromCurrentConfigV2(Preset)) diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/CachesPage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/CachesPage.xaml.cs index bbb44176e..b46e9ed17 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/CachesPage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/CachesPage.xaml.cs @@ -1,8 +1,8 @@ -using CollapseLauncher.Statics; -using Hi3Helper; -#if !DISABLEDISCORD -using Hi3Helper.DiscordPresence; +#if !DISABLEDISCORD +using CollapseLauncher.DiscordPresence; #endif +using CollapseLauncher.Statics; +using Hi3Helper; using Hi3Helper.Shared.ClassStruct; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/Dialogs/InstallationConvert.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/Dialogs/InstallationConvert.xaml.cs index f503fc047..de88692d5 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/Dialogs/InstallationConvert.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/Dialogs/InstallationConvert.xaml.cs @@ -1,11 +1,11 @@ using CollapseLauncher.CustomControls; using CollapseLauncher.Extension; using CollapseLauncher.FileDialogCOM; +using CollapseLauncher.Helper.Metadata; using CollapseLauncher.Statics; using Hi3Helper; using Hi3Helper.Data; using Hi3Helper.Http; -using Hi3Helper.Preset; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media; @@ -19,7 +19,6 @@ using static Hi3Helper.Data.ConverterTool; using static Hi3Helper.Locale; using static Hi3Helper.Logger; -using static Hi3Helper.Preset.ConfigV2Store; using static Hi3Helper.Shared.Region.LauncherConfig; namespace CollapseLauncher.Dialogs @@ -29,8 +28,8 @@ public partial class InstallationConvert : Page string SourceDataIntegrityURL; string GameVersion; bool IsAlreadyConverted = false; - PresetConfigV2 SourceProfile; - PresetConfigV2 TargetProfile; + PresetConfig SourceProfile; + PresetConfig TargetProfile; GameConversionManagement Converter; IniFile SourceIniFile; CancellationTokenSource tokenSource = new CancellationTokenSource(); @@ -146,7 +145,7 @@ private void DoSetProfileDataLocation() }); } - private async Task FetchDataIntegrityURL(PresetConfigV2 Profile) + private async Task FetchDataIntegrityURL(PresetConfig Profile) { Http _Http = new Http(); Dictionary _RepoList; @@ -181,7 +180,7 @@ private async Task FetchDataIntegrityURL(PresetConfigV2 Profile) return _RepoList[GameVersion]; } - public bool IsSourceGameExist(PresetConfigV2 Profile) + internal bool IsSourceGameExist(PresetConfig Profile) { string INIPath = Path.Combine(AppGameFolder, Profile.ProfileName, "config.ini"); string GamePath; @@ -235,11 +234,11 @@ public bool IsSourceGameExist(PresetConfigV2 Profile) return true; } - public async Task<(PresetConfigV2, PresetConfigV2)> AskConvertionDestination() + internal async Task<(PresetConfig, PresetConfig)> AskConvertionDestination() { (ContentDialogResult Result, ComboBox SourceGame, ComboBox TargetGame) = await Dialog_SelectGameConvertRecipe(Content); - PresetConfigV2 SourceRet = null; - PresetConfigV2 TargetRet = null; + PresetConfig SourceRet = null; + PresetConfig TargetRet = null; if (SourceGame.SelectedItem == null || TargetGame.SelectedItem == null) throw new OperationCanceledException(); @@ -250,9 +249,9 @@ public bool IsSourceGameExist(PresetConfigV2 Profile) switch (Result) { case ContentDialogResult.Secondary: - SourceRet = ConfigV2.MetadataV2[CurrentConfigV2GameCategory]. + SourceRet = LauncherMetadataHelper.LauncherMetadataConfig[LauncherMetadataHelper.CurrentMetadataConfigGameName]. Values.Where(x => x.ZoneName == sourceGameRegionString).First(); - TargetRet = ConfigV2.MetadataV2[CurrentConfigV2GameCategory]. + TargetRet = LauncherMetadataHelper.LauncherMetadataConfig[LauncherMetadataHelper.CurrentMetadataConfigGameName]. Values.Where(x => x.ZoneName == targetGameRegionString).First(); break; case ContentDialogResult.Primary: @@ -264,13 +263,13 @@ public bool IsSourceGameExist(PresetConfigV2 Profile) public static List GetConvertibleNameList(string ZoneName) { List _out = new List(); - List GameTargetProfileName = ConfigV2.MetadataV2[CurrentConfigV2GameCategory].Values + List GameTargetProfileName = LauncherMetadataHelper.LauncherMetadataConfig[LauncherMetadataHelper.CurrentMetadataConfigGameName].Values .Where(x => x.ZoneName == ZoneName) .Select(x => x.ConvertibleTo) .First(); foreach (string Entry in GameTargetProfileName) - _out.Add(ConfigV2.MetadataV2[CurrentConfigV2GameCategory].Values + _out.Add(LauncherMetadataHelper.LauncherMetadataConfig[LauncherMetadataHelper.CurrentMetadataConfigGameName].Values .Where(x => x.ZoneName == Entry) .Select(x => x.ZoneName) .First()); @@ -447,7 +446,7 @@ public void ApplyConfiguration() CurrentGameProperty._GameVersion.UpdateGamePath(TargetProfile.ActualGameDataLocation); string GameCategory = GetAppConfigValue("GameCategory").ToString(); - SetPreviousGameRegion(GameCategory, TargetProfile.ZoneName); + LauncherMetadataHelper.SetPreviousGameRegion(GameCategory, TargetProfile.ZoneName); LoadAppConfig(); } diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/Dialogs/KeyboardShortcuts.cs b/CollapseLauncher/XAMLs/MainApp/Pages/Dialogs/KeyboardShortcuts.cs index eeabcef7f..a32dba2ec 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/Dialogs/KeyboardShortcuts.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/Dialogs/KeyboardShortcuts.cs @@ -1,5 +1,6 @@ using CollapseLauncher.CustomControls; using CollapseLauncher.Extension; +using CollapseLauncher.Helper.Metadata; using CommunityToolkit.WinUI; using Hi3Helper.Preset; using Microsoft.UI.Text; @@ -705,8 +706,8 @@ public string GetKey(string dictionaryKey = "") { return dictionaryKey switch { - "GameSelection" => $"1 - {ConfigV2Store.ConfigV2.GameCount}", - "RegionSelection" => $"1 - {ConfigV2Store.ConfigV2.MaxRegionCount}", + "GameSelection" => $"1 - {LauncherMetadataHelper.CurrentGameNameCount}", + "RegionSelection" => $"1 - {LauncherMetadataHelper.CurrentGameRegionMaxCount}", _ => Key.ToString() }; } diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/Dialogs/SimpleDialogs.cs b/CollapseLauncher/XAMLs/MainApp/Pages/Dialogs/SimpleDialogs.cs index a9fd5064a..0b9b8e328 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/Dialogs/SimpleDialogs.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/Dialogs/SimpleDialogs.cs @@ -1,8 +1,8 @@ using CollapseLauncher.CustomControls; using CollapseLauncher.Extension; +using CollapseLauncher.Helper.Metadata; using CollapseLauncher.Statics; using Hi3Helper; -using Hi3Helper.Preset; using Microsoft.UI.Text; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; @@ -15,7 +15,6 @@ using Windows.Foundation; using static Hi3Helper.Data.ConverterTool; using static Hi3Helper.Locale; -using static Hi3Helper.Preset.ConfigV2Store; using static Hi3Helper.Shared.Region.LauncherConfig; namespace CollapseLauncher.Dialogs @@ -140,8 +139,9 @@ await SpawnDialog( public static async Task<(ContentDialogResult, ComboBox, ComboBox)> Dialog_SelectGameConvertRecipe(UIElement Content) { - Dictionary ConvertibleRegions = new Dictionary(); - foreach (KeyValuePair Config in ConfigV2.MetadataV2[CurrentConfigV2GameCategory].Where(x => x.Value.IsConvertible ?? false)) + Dictionary ConvertibleRegions = new Dictionary(); + foreach (KeyValuePair Config in LauncherMetadataHelper.LauncherMetadataConfig[LauncherMetadataHelper.CurrentMetadataConfigGameName] + .Where(x => x.Value.IsConvertible ?? false)) ConvertibleRegions.Add(Config.Key, Config.Value); ContentDialogCollapse Dialog = new ContentDialogCollapse(); @@ -152,7 +152,7 @@ await SpawnDialog( { TargetGame.IsEnabled = true; Dialog.IsSecondaryButtonEnabled = false; - TargetGame.ItemsSource = InnerLauncherConfig.BuildGameRegionListUI(CurrentConfigV2GameCategory, InstallationConvert.GetConvertibleNameList( + TargetGame.ItemsSource = InnerLauncherConfig.BuildGameRegionListUI(LauncherMetadataHelper.CurrentMetadataConfigGameName, InstallationConvert.GetConvertibleNameList( InnerLauncherConfig.GetComboBoxGameRegionValue((sender as ComboBox).SelectedItem))); }); SelectionChangedEventHandler TargetGameChangedArgs = new SelectionChangedEventHandler((object sender, SelectionChangedEventArgs e) => @@ -163,7 +163,7 @@ await SpawnDialog( SourceGame = new ComboBox { Width = 200, - ItemsSource = InnerLauncherConfig.BuildGameRegionListUI(CurrentConfigV2GameCategory, new List(ConvertibleRegions.Keys)), + ItemsSource = InnerLauncherConfig.BuildGameRegionListUI(LauncherMetadataHelper.CurrentMetadataConfigGameName, new List(ConvertibleRegions.Keys)), PlaceholderText = Lang._InstallConvert.SelectDialogSource, CornerRadius = new CornerRadius(14) }; diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/GenshinGameSettingsPage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/GenshinGameSettingsPage.xaml.cs index 0b13cfd20..c8d37078e 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/GenshinGameSettingsPage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/GenshinGameSettingsPage.xaml.cs @@ -1,10 +1,10 @@ +#if !DISABLEDISCORD +using CollapseLauncher.DiscordPresence; +#endif using CollapseLauncher.GameSettings.Genshin; using CollapseLauncher.Interfaces; using CollapseLauncher.Statics; using Hi3Helper; -#if !DISABLEDISCORD -using Hi3Helper.DiscordPresence; -#endif using Hi3Helper.Shared.ClassStruct; using Microsoft.Graphics.Canvas; using Microsoft.Graphics.Canvas.UI.Xaml; @@ -198,7 +198,7 @@ private void InitializeSettings(object sender, RoutedEventArgs e) else { #if !DISABLEDISCORD - AppDiscordPresence.SetActivity(ActivityType.GameSettings); + InnerLauncherConfig.AppDiscordPresence.SetActivity(ActivityType.GameSettings); #endif } } diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/HonkaiGameSettingsPage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/HonkaiGameSettingsPage.xaml.cs index be9b16eef..688320719 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/HonkaiGameSettingsPage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/HonkaiGameSettingsPage.xaml.cs @@ -1,8 +1,8 @@ -using CollapseLauncher.GameSettings.Honkai; -using CollapseLauncher.Interfaces; #if !DISABLEDISCORD -using Hi3Helper.DiscordPresence; +using CollapseLauncher.DiscordPresence; #endif +using CollapseLauncher.GameSettings.Honkai; +using CollapseLauncher.Interfaces; using Hi3Helper.Shared.ClassStruct; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Media; @@ -160,7 +160,7 @@ private void InitializeSettings(object sender, RoutedEventArgs e) else { #if !DISABLEDISCORD - AppDiscordPresence.SetActivity(ActivityType.GameSettings); + InnerLauncherConfig.AppDiscordPresence.SetActivity(ActivityType.GameSettings); #endif } } diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/StarRailGameSettingsPage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/StarRailGameSettingsPage.xaml.cs index f736cbf3a..40c4c0f1d 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/StarRailGameSettingsPage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/StarRailGameSettingsPage.xaml.cs @@ -1,9 +1,9 @@ -using CollapseLauncher.Statics; +#if !DISABLEDISCORD +using CollapseLauncher.DiscordPresence; +#endif +using CollapseLauncher.Statics; using CollapseLauncher.GameSettings.StarRail; using CollapseLauncher.Interfaces; -#if !DISABLEDISCORD -using Hi3Helper.DiscordPresence; -#endif using Hi3Helper.Shared.ClassStruct; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Media; @@ -173,7 +173,7 @@ private void InitializeSettings(object sender, RoutedEventArgs e) else { #if !DISABLEDISCORD - AppDiscordPresence.SetActivity(ActivityType.GameSettings); + InnerLauncherConfig.AppDiscordPresence.SetActivity(ActivityType.GameSettings); #endif } } diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.Variable.cs b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.Variable.cs index d0b4a13d7..db327d6ab 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.Variable.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.Variable.cs @@ -1,4 +1,5 @@ -using Hi3Helper; +using CollapseLauncher.Helper.Metadata; +using Hi3Helper; using Hi3Helper.Preset; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; @@ -53,12 +54,12 @@ public string NoNewsSplashMascot { get { - GameType gameType = CurrentGameProperty._GamePreset.GameType; + GameNameType gameType = CurrentGameProperty._GamePreset.GameType; return gameType switch { - GameType.Honkai => "ms-appx:///Assets/Images/GameMascot/AiShocked.png", - GameType.StarRail => "ms-appx:///Assets/Images/GameMascot/PomPomWhat.png", - GameType.Zenless => "ms-appx:///Assets/Images/GameMascot/BangbooShocked.png", + GameNameType.Honkai => "ms-appx:///Assets/Images/GameMascot/AiShocked.png", + GameNameType.StarRail => "ms-appx:///Assets/Images/GameMascot/PomPomWhat.png", + GameNameType.Zenless => "ms-appx:///Assets/Images/GameMascot/BangbooShocked.png", _ => "ms-appx:///Assets/Images/GameMascot/PaimonWhat.png" }; } diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs index 0c4f08302..550c0ce63 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs @@ -1,17 +1,16 @@ using CollapseLauncher.CustomControls; using CollapseLauncher.Dialogs; +#if !DISABLEDISCORD +using CollapseLauncher.DiscordPresence; +#endif using CollapseLauncher.FileDialogCOM; using CollapseLauncher.Helper.Image; using CollapseLauncher.Interfaces; using CollapseLauncher.Statics; using CollapseLauncher.ShortcutUtils; using Hi3Helper; -using Hi3Helper.Preset; using Hi3Helper.Screen; using Hi3Helper.Shared.ClassStruct; -#if !DISABLEDISCORD -using Hi3Helper.DiscordPresence; -#endif using Microsoft.UI.Input; using Microsoft.UI.Text; using Microsoft.UI.Xaml; @@ -47,6 +46,7 @@ using Orientation = Microsoft.UI.Xaml.Controls.Orientation; using Hi3Helper.EncTool.WindowTool; using CollapseLauncher.Extension; +using CollapseLauncher.Helper.Metadata; namespace CollapseLauncher.Pages { @@ -714,7 +714,7 @@ private void GetCurrentGameState() { Visibility RepairGameButtonVisible = (CurrentGameProperty._GameVersion.GamePreset.IsRepairEnabled ?? false) ? Visibility.Visible : Visibility.Collapsed; - if ((!(CurrentGameProperty._GameVersion.GamePreset.IsConvertible ?? false)) || (CurrentGameProperty._GameVersion.GameType != GameType.Honkai)) + if ((!(CurrentGameProperty._GameVersion.GamePreset.IsConvertible ?? false)) || (CurrentGameProperty._GameVersion.GameType != GameNameType.Honkai)) ConvertVersionButton.Visibility = Visibility.Collapsed; // Clear the _CommunityToolsProperty statics @@ -748,7 +748,7 @@ private void GetCurrentGameState() } } - if (CurrentGameProperty._GameVersion.GameType == GameType.Genshin) OpenCacheFolderButton.Visibility = Visibility.Collapsed; + if (CurrentGameProperty._GameVersion.GameType == GameNameType.Genshin) OpenCacheFolderButton.Visibility = Visibility.Collapsed; GameInstallationState = CurrentGameProperty._GameVersion.GetGameState(); switch (GameInstallationState) @@ -1245,9 +1245,9 @@ private async void StartGame(object sender, RoutedEventArgs e) { // Initialize values IGameSettingsUniversal _Settings = CurrentGameProperty!._GameSettings!.AsIGameSettingsUniversal(); - PresetConfigV2 _gamePreset = CurrentGameProperty!._GameVersion!.GamePreset!; + PresetConfig _gamePreset = CurrentGameProperty!._GameVersion!.GamePreset!; - var isGenshin = CurrentGameProperty!._GameVersion.GameType == GameType.Genshin; + var isGenshin = CurrentGameProperty!._GameVersion.GameType == GameNameType.Genshin; var giForceHDR = false; try @@ -1382,7 +1382,7 @@ private async void GameRunningWatcher(IGameSettingsUniversal _settings) IsSkippingUpdateCheck = false; } - private void StopGame(PresetConfigV2 gamePreset) + private void StopGame(PresetConfig gamePreset) { ArgumentNullException.ThrowIfNull(gamePreset); try @@ -1402,7 +1402,7 @@ private void StopGame(PresetConfigV2 gamePreset) #endregion #region Game Resizable Window Payload - internal async void StartResizableWindowPayload(string executableName, IGameSettingsUniversal settings, GameType gameType) + internal async void StartResizableWindowPayload(string executableName, IGameSettingsUniversal settings, GameNameType gameType) { try { @@ -1417,7 +1417,7 @@ internal async void StartResizableWindowPayload(string executableName, IGameSett // This is required for Honkai: Star Rail since the game will reset its pos + size. Making // it impossible to use custom resolution (but since you are using Collapse, it's now // possible :teriStare:) - bool isNeedToResetPos = gameType == GameType.StarRail; + bool isNeedToResetPos = gameType == GameNameType.StarRail; await resizableWindowHook.StartHook(executableName, ResizableWindowHookToken.Token, isNeedToResetPos); } catch (Exception ex) @@ -1434,7 +1434,7 @@ internal string GetLaunchArguments(IGameSettingsUniversal _Settings) { StringBuilder parameter = new StringBuilder(); - if (CurrentGameProperty._GameVersion.GameType == GameType.Honkai) + if (CurrentGameProperty._GameVersion.GameType == GameNameType.Honkai) { if (_Settings.SettingsCollapseScreen.UseExclusiveFullscreen) { @@ -1477,7 +1477,7 @@ internal string GetLaunchArguments(IGameSettingsUniversal _Settings) break; } } - if (CurrentGameProperty._GameVersion.GameType == GameType.StarRail) + if (CurrentGameProperty._GameVersion.GameType == GameNameType.StarRail) { if (_Settings.SettingsCollapseScreen.UseExclusiveFullscreen) { @@ -1500,7 +1500,7 @@ internal string GetLaunchArguments(IGameSettingsUniversal _Settings) else parameter.AppendFormat("-screen-width {0} -screen-height {1} ", screenSize.Width, screenSize.Height); } - if (CurrentGameProperty._GameVersion.GameType == GameType.Genshin) + if (CurrentGameProperty._GameVersion.GameType == GameNameType.Genshin) { if (_Settings.SettingsCollapseScreen.UseExclusiveFullscreen) { @@ -1556,7 +1556,7 @@ public bool UseCustomArgs #region Media Pack public async Task CheckMediaPackInstalled() { - if (CurrentGameProperty._GameVersion.GameType != GameType.Honkai) return true; + if (CurrentGameProperty._GameVersion.GameType != GameNameType.Honkai) return true; RegistryKey reg = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Setup\WindowsFeatures\WindowsMediaVersion"); if (reg != null) @@ -1696,7 +1696,7 @@ private void OpenScreenshotFolderButton_Click(object sender, RoutedEventArgs e) { string ScreenshotFolder = Path.Combine(NormalizePath(GameDirPath), CurrentGameProperty._GameVersion.GamePreset.GameType switch { - GameType.StarRail => $"{Path.GetFileNameWithoutExtension(CurrentGameProperty._GameVersion.GamePreset.GameExecutableName)}_Data\\ScreenShots", + GameNameType.StarRail => $"{Path.GetFileNameWithoutExtension(CurrentGameProperty._GameVersion.GamePreset.GameExecutableName)}_Data\\ScreenShots", _ => "ScreenShot" }); @@ -1837,7 +1837,7 @@ private void UpdateLastPlayed(bool readRegistry = true, int value = 0) ToolTipService.SetToolTip(PlaytimeBtn, formattedText); } - private async void StartPlaytimeCounter(Process proc, PresetConfigV2 gamePreset) + private async void StartPlaytimeCounter(Process proc, PresetConfig gamePreset) { int currentPlaytime = ReadPlaytimeFromRegistry(true, gamePreset.ConfigRegistryLocation); diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/OOBE/OOBESelectGame.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/OOBE/OOBESelectGame.xaml.cs index bdb7ed913..cd7b870ff 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/OOBE/OOBESelectGame.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/OOBE/OOBESelectGame.xaml.cs @@ -1,4 +1,5 @@ using CollapseLauncher.Helper.Background; +using CollapseLauncher.Helper.Metadata; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media.Animation; @@ -8,7 +9,6 @@ using System.Threading.Tasks; using static CollapseLauncher.InnerLauncherConfig; using static CollapseLauncher.Pages.OOBE.OOBESelectGameBGProp; -using static Hi3Helper.Preset.ConfigV2Store; using static Hi3Helper.Shared.Region.LauncherConfig; namespace CollapseLauncher.Pages.OOBE @@ -21,7 +21,6 @@ public sealed partial class OOBESelectGame : Page public OOBESelectGame() { this.InitializeComponent(); - LoadConfigV2(); GameCategorySelect.ItemsSource = BuildGameTitleListUI(); BackgroundFrame.Navigate(typeof(OOBESelectGameBG)); this.RequestedTheme = IsAppThemeLight ? ElementTheme.Light : ElementTheme.Dark; @@ -32,7 +31,7 @@ private void NextPage_Click(object sender, RoutedEventArgs e) // Set and Save CurrentRegion in AppConfig string categorySelected = GetComboBoxGameRegionValue(GameCategorySelect.SelectedValue); SetAppConfigValue("GameCategory", categorySelected); - SetPreviousGameRegion(categorySelected, GetComboBoxGameRegionValue(GameRegionSelect.SelectedValue), false); + LauncherMetadataHelper.SetPreviousGameRegion(categorySelected, GetComboBoxGameRegionValue(GameRegionSelect.SelectedValue), false); SaveAppConfig(); (m_window as MainWindow).rootFrame.Navigate(typeof(MainPage), null, new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromBottom }); @@ -59,7 +58,8 @@ private async void GameSelect_SelectionChanged(object sender, SelectionChangedEv BarBGLoading.Visibility = Visibility.Visible; BarBGLoading.IsIndeterminate = true; FadeBackground(1, 0.25); - bool IsSuccess = await TryLoadGameDetails(ConfigV2.MetadataV2[_selectedCategory][_selectedRegion]); + PresetConfig gameConfig = LauncherMetadataHelper.GetMetadataConfig(_selectedCategory, _selectedRegion); + bool IsSuccess = await TryLoadGameDetails(gameConfig); BitmapData bitmapData = null; @@ -133,7 +133,7 @@ private async void FadeBackground(double from, double to) private void GameCategorySelect_SelectionChanged(object sender, SelectionChangedEventArgs e) { _selectedCategory = GetComboBoxGameRegionValue(((ComboBox)sender).SelectedValue); - GetConfigV2Regions(_selectedCategory); + // REMOVED GetConfigV2Regions(_selectedCategory); GameRegionSelect.ItemsSource = BuildGameRegionListUI(_selectedCategory); GameRegionSelect.IsEnabled = true; NextPage.IsEnabled = false; diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/OOBE/OOBESelectGameBG.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/OOBE/OOBESelectGameBG.xaml.cs index 3a0e42fa7..0bdef69a4 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/OOBE/OOBESelectGameBG.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/OOBE/OOBESelectGameBG.xaml.cs @@ -1,7 +1,7 @@ using CollapseLauncher.Helper.Image; +using CollapseLauncher.Helper.Metadata; using CommunityToolkit.WinUI; using Hi3Helper; -using Hi3Helper.Preset; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media.Imaging; @@ -68,7 +68,7 @@ public static class OOBESelectGameBGProp public static bool IsNeedLoad = false; public static bool IsSuccess = false; - public static async Task TryLoadGameDetails(PresetConfigV2 config = null) + internal static async Task TryLoadGameDetails(PresetConfig config = null) { try { diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/OOBE/OOBEStartUpMenu.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/OOBE/OOBEStartUpMenu.xaml.cs index d9b985327..8a83c3dd9 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/OOBE/OOBEStartUpMenu.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/OOBE/OOBEStartUpMenu.xaml.cs @@ -3,6 +3,7 @@ using CollapseLauncher.Helper.Animation; using CollapseLauncher.Helper.Image; using CollapseLauncher.Helper.Loading; +using CollapseLauncher.Helper.Metadata; using CommunityToolkit.WinUI.Animations; using CommunityToolkit.WinUI.Controls; using Hi3Helper; @@ -757,25 +758,22 @@ private bool CheckIfFolderIsValid(string basePath) #region Prepare Metadata and Settings Apply private async void PrepareMetadataAndApplySettings() { - if (!ConfigV2Store.IsConfigV2StampExist() || !ConfigV2Store.IsConfigV2ContentExist()) + try { - try - { - LoadingMessageHelper.SetMessage(Lang._StartupPage.Pg1LoadingTitle1, Lang._StartupPage.Pg1LoadingSubitle1); - LoadingMessageHelper.SetProgressBarState(100, true); - LoadingMessageHelper.SetProgressBarValue(100); - LoadingMessageHelper.ShowLoadingFrame(); - - await DownloadConfigV2Files(true, true); - LoadingMessageHelper.SetMessage(Lang._StartupPage.Pg1LoadingTitle1, Lang._StartupPage.Pg1LoadingSubitle2); - LoadingMessageHelper.SetProgressBarState(100, false); - LoadingMessageHelper.SetProgressBarValue(100); - await Task.Delay(5000); - - LoadingMessageHelper.HideLoadingFrame(); - } - catch { } + LoadingMessageHelper.SetMessage(Lang._StartupPage.Pg1LoadingTitle1, Lang._StartupPage.Pg1LoadingSubitle1); + LoadingMessageHelper.SetProgressBarState(100, true); + LoadingMessageHelper.SetProgressBarValue(100); + LoadingMessageHelper.ShowLoadingFrame(); + + await LauncherMetadataHelper.Initialize(false, false); + LoadingMessageHelper.SetMessage(Lang._StartupPage.Pg1LoadingTitle1, Lang._StartupPage.Pg1LoadingSubitle2); + LoadingMessageHelper.SetProgressBarState(100, false); + LoadingMessageHelper.SetProgressBarValue(100); + await Task.Delay(5000); + + LoadingMessageHelper.HideLoadingFrame(); } + catch { } HideUIs(MainUI, IntroSequenceUI); OverlayFrame.Navigate(typeof(OOBESelectGame), null, null); diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/RepairPage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/RepairPage.xaml.cs index 5e32fa271..0e8bd6222 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/RepairPage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/RepairPage.xaml.cs @@ -1,8 +1,8 @@ -using CollapseLauncher.Statics; -using Hi3Helper; -#if !DISABLEDISCORD -using Hi3Helper.DiscordPresence; +#if !DISABLEDISCORD +using CollapseLauncher.DiscordPresence; #endif +using CollapseLauncher.Statics; +using Hi3Helper; using Hi3Helper.Shared.ClassStruct; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; @@ -216,7 +216,7 @@ private void InitializeLoaded(object sender, RoutedEventArgs e) else { #if !DISABLEDISCORD - AppDiscordPresence.SetActivity(ActivityType.Repair); + InnerLauncherConfig.AppDiscordPresence.SetActivity(ActivityType.Repair); #endif } } diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/SettingsPage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/SettingsPage.xaml.cs index 6e7b68c26..2eaf016e7 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/SettingsPage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/SettingsPage.xaml.cs @@ -1,3 +1,6 @@ +#if !DISABLEDISCORD +using CollapseLauncher.DiscordPresence; +#endif using CollapseLauncher.Extension; using CollapseLauncher.Helper.Animation; using CollapseLauncher.Helper.Background; @@ -5,9 +8,6 @@ using CollapseLauncher.Pages.OOBE; using Hi3Helper; using Hi3Helper.Data; -#if !DISABLEDISCORD -using Hi3Helper.DiscordPresence; -#endif using Hi3Helper.Shared.ClassStruct; using Hi3Helper.Shared.Region; using Microsoft.UI.Xaml; @@ -32,6 +32,7 @@ using MediaType = CollapseLauncher.Helper.Background.BackgroundMediaUtility.MediaType; using TaskSched = Microsoft.Win32.TaskScheduler.Task; using Task = System.Threading.Tasks.Task; +using CollapseLauncher.Helper.Metadata; // ReSharper disable PossibleNullReferenceException // ReSharper disable AssignNullToNotNullAttribute @@ -166,8 +167,8 @@ private async void ClearMetadataFolder(object sender, RoutedEventArgs e) try { var collapsePath = Process.GetCurrentProcess().MainModule?.FileName; - if (collapsePath == null || AppGameConfigMetadataFolder == null) return; - Directory.Delete(AppGameConfigMetadataFolder, true); + if (collapsePath == null || LauncherMetadataHelper.LauncherMetadataFolder == null) return; + Directory.Delete(LauncherMetadataHelper.LauncherMetadataFolder, true); Process.Start(collapsePath); (m_window as MainWindow)?.CloseApp(); } diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/StartupPage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/StartupPage.xaml.cs index e6d61b9be..62b57de4c 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/StartupPage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/StartupPage.xaml.cs @@ -1,4 +1,5 @@ using CollapseLauncher.Extension; +using CollapseLauncher.Helper.Metadata; using Hi3Helper; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; @@ -14,7 +15,6 @@ using static CollapseLauncher.InnerLauncherConfig; using static Hi3Helper.Data.ConverterTool; using static Hi3Helper.Locale; -using static Hi3Helper.Preset.ConfigV2Store; using static Hi3Helper.Shared.Region.LauncherConfig; namespace CollapseLauncher.Pages @@ -185,21 +185,18 @@ private async Task HideLoadingPopup(bool hide, string title, string subtitle) private async void NextPage_Click(object sender, RoutedEventArgs e) { - if (!IsConfigV2StampExist() || !IsConfigV2ContentExist()) + try { - try - { - await HideLoadingPopup(false, Lang._StartupPage.Pg1LoadingTitle1, Lang._StartupPage.Pg1LoadingSubitle1); - await DownloadConfigV2Files(true, true); - Ring.IsIndeterminate = false; - OverlayTitle.Text = Lang._StartupPage.Pg1LoadingTitle1; - OverlaySubtitle.Text = Lang._StartupPage.Pg1LoadingSubitle2; - await Task.Delay(2000); - } - catch (Exception ex) - { - ErrorSender.SendException(ex, ErrorType.Connection); - } + await HideLoadingPopup(false, Lang._StartupPage.Pg1LoadingTitle1, Lang._StartupPage.Pg1LoadingSubitle1); + await LauncherMetadataHelper.Initialize(false, false); + Ring.IsIndeterminate = false; + OverlayTitle.Text = Lang._StartupPage.Pg1LoadingTitle1; + OverlaySubtitle.Text = Lang._StartupPage.Pg1LoadingSubitle2; + await Task.Delay(2000); + } + catch (Exception ex) + { + ErrorSender.SendException(ex, ErrorType.Connection); } // (m_window as MainWindow).rootFrame.Navigate(typeof(StartupPage_SelectGame), null, new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromRight }); diff --git a/Hi3Helper.Core/Classes/Preset/Classes/ClassesContext.cs b/Hi3Helper.Core/Classes/Preset/Classes/ClassesContext.cs index 0cd7b8921..0195ed9f9 100644 --- a/Hi3Helper.Core/Classes/Preset/Classes/ClassesContext.cs +++ b/Hi3Helper.Core/Classes/Preset/Classes/ClassesContext.cs @@ -1,20 +1,14 @@ using Hi3Helper.EncTool.Parser.AssetIndex; -using Hi3Helper.Preset; using Hi3Helper.Shared.ClassStruct; using System.Collections.Generic; using System.Text.Json.Serialization; using static Hi3Helper.Locale; -using static Hi3Helper.Preset.PresetConfigV2; namespace Hi3Helper { [JsonSourceGenerationOptions(IncludeFields = true, GenerationMode = JsonSourceGenerationMode.Metadata, IgnoreReadOnlyFields = false)] - [JsonSerializable(typeof(Stamp))] - [JsonSerializable(typeof(Metadata))] - [JsonSerializable(typeof(BHI3LInfo))] [JsonSerializable(typeof(DataProperties))] [JsonSerializable(typeof(YSDispatchInfo))] - [JsonSerializable(typeof(GeneralDataProp))] [JsonSerializable(typeof(PkgVersionProperties))] [JsonSerializable(typeof(DataPropertiesContent))] [JsonSerializable(typeof(FilePropertiesRemote[]))] diff --git a/Hi3Helper.Core/Classes/Preset/Classes/PresetConfigClasses.cs b/Hi3Helper.Core/Classes/Preset/Classes/PresetConfigClasses.cs deleted file mode 100644 index 9a6af0510..000000000 --- a/Hi3Helper.Core/Classes/Preset/Classes/PresetConfigClasses.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Hi3Helper.Preset -{ - public class BHI3LInfo - { - public BHI3LInfo_GameInfo game_info { get; set; } - } - - public class BHI3LInfo_GameInfo - { - public string version { get; set; } - public string install_path { get; set; } - public bool installed { get; set; } - } -} diff --git a/Hi3Helper.Core/Classes/Preset/Classes/PresetConfigV2.cs b/Hi3Helper.Core/Classes/Preset/Classes/PresetConfigV2.cs deleted file mode 100644 index 48975ad6e..000000000 --- a/Hi3Helper.Core/Classes/Preset/Classes/PresetConfigV2.cs +++ /dev/null @@ -1,615 +0,0 @@ -using Hi3Helper.Data; -using Hi3Helper.EncTool; -using Hi3Helper.EncTool.Parser.AssetMetadata; -using Microsoft.Win32; -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.IO.Compression; -using System.Linq; -using System.Security.Cryptography; -using System.Text; -using System.Text.Json; -using System.Text.Json.Serialization; -using static Hi3Helper.Logger; - -namespace Hi3Helper.Preset -{ -#nullable enable - - #region Enums - [JsonConverter(typeof(JsonStringEnumConverter))] - public enum ServerRegionID - { - os_usa = 0, - os_euro = 1, - os_asia = 2, - os_cht = 3, - cn_gf01 = 4, - cn_qd01 = 5 - } - - [JsonConverter(typeof(JsonStringEnumConverter))] - public enum GameChannel - { - Beta = 0, - Stable = 1, - DevRelease = 2 - } - - [JsonConverter(typeof(JsonStringEnumConverter))] - public enum GameType - { - Honkai = 0, - Genshin = 1, - StarRail = 2, - Zenless = 3, - Unknown = int.MinValue - } - - [JsonConverter(typeof(JsonStringEnumConverter))] - public enum GameVendorType - { - miHoYo, - Cognosphere - } - #endregion - - #region Metadata Handler - public class Stamp - { - public long LastUpdated { get; set; } - } - - public sealed class Metadata - { - public Dictionary>? MetadataV2 { get; set; } - - public string? MasterKey { get; set; } - public int MasterKeyBitLength { get; set; } - #nullable disable - public void DecryptStrings() - { - mhyEncTool Decryptor = new mhyEncTool(); - Decryptor.InitMasterKey(MasterKey, MasterKeyBitLength, RSAEncryptionPadding.Pkcs1); - - string[] gameKeys = MetadataV2!.Keys.ToArray(); - for (int i = 0; i < GameCount; i++) - { - string[] regionKeys = MetadataV2[gameKeys[i]!]!.Keys.ToArray(); - int segmentCount = MetadataV2[gameKeys[i]!]!.Count; - - for (int j = 0; j < segmentCount; j++) - { - // Dec GameDispatchArrayURL - string[] GameDispatchArrayURL = MetadataV2[gameKeys[i]]![regionKeys[j]!]!.GameDispatchArrayURL; - Decryptor.DecryptStringWithMasterKey(ref GameDispatchArrayURL); - MetadataV2[gameKeys[i]][regionKeys[j]]!.GameDispatchArrayURL = GameDispatchArrayURL; - - // Dec GameDispatchChannelName - string GameDispatchChannelName = MetadataV2[gameKeys[i]][regionKeys[j]].GameDispatchChannelName; - Decryptor.DecryptStringWithMasterKey(ref GameDispatchChannelName); - MetadataV2[gameKeys[i]][regionKeys[j]].GameDispatchChannelName = GameDispatchChannelName; - - // Dec GameDispatchDefaultName - string GameDispatchDefaultName = MetadataV2[gameKeys[i]][regionKeys[j]].GameDispatchDefaultName; - Decryptor.DecryptStringWithMasterKey(ref GameDispatchDefaultName); - MetadataV2[gameKeys[i]][regionKeys[j]].GameDispatchDefaultName = GameDispatchDefaultName; - - // Dec GameDispatchURLTemplate - string GameDispatchURLTemplate = MetadataV2[gameKeys[i]][regionKeys[j]].GameDispatchURLTemplate; - Decryptor.DecryptStringWithMasterKey(ref GameDispatchURLTemplate); - MetadataV2[gameKeys[i]][regionKeys[j]].GameDispatchURLTemplate = GameDispatchURLTemplate; - - // Dec GameGatewayURLTemplate - string GameGatewayURLTemplate = MetadataV2[gameKeys[i]][regionKeys[j]].GameGatewayURLTemplate; - Decryptor.DecryptStringWithMasterKey(ref GameGatewayURLTemplate); - MetadataV2[gameKeys[i]][regionKeys[j]].GameGatewayURLTemplate = GameGatewayURLTemplate; - } - } - } - - public void GenerateHashID() - { - foreach (KeyValuePair> game in MetadataV2!) - { - foreach (KeyValuePair region in game.Value!) - { - string HashComposition = $"{ConverterTool.GetUnixTimestamp(true)} - {game.Key} - {region.Value!.ZoneName}"; - int HashID = ConverterTool.BytesToCRC32Int(HashComposition); - region.Value.HashID = HashID; - region.Value.GameName = game.Key; - - LogWriteLine($"Cur. Session Region Hash: {HashID} ({HashComposition})", LogType.Default, true); - } - } - } - - public int GameCount => MetadataV2?.Count ?? 0; - public int MaxRegionCount => MetadataV2?.Max(x => x.Value.Count) ?? 0; - } - - #endregion - -#nullable enable - [Serializable] - public sealed class PresetConfigV2 - { - #region Constants - private const string PrefixRegInstallLocation = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{0}"; - private const string PrefixRegGameConfig = "Software\\{0}\\{1}"; - private const string PrefixDefaultProgramFiles = "{1}Program Files\\{0}"; - #endregion - - #region Language Handler - public string? GetGameLanguage() - { - ReadOnlySpan value; - RegistryKey? keys = Registry.CurrentUser.OpenSubKey(ConfigRegistryLocation!); - - byte[]? result = (byte[]?)keys?.GetValue("MIHOYOSDK_CURRENT_LANGUAGE_h2559149783"); - - if (keys is null || result is null || result.Length is 0) - { - LogWriteLine($"Language registry on \u001b[32;1m{Path.GetFileName(ConfigRegistryLocation)}\u001b[0m version doesn't exist. Fallback value will be used.", LogType.Warning, true); - return FallbackLanguage; - } - - value = Encoding.UTF8.GetString(result).AsSpan().Trim('\0'); - return new string(value); - } - - // WARNING!!! - // This feature is only available for Genshin and Star Rail. - public int GetVoiceLanguageID() - { - string RegPath = Path.GetFileName(ConfigRegistryLocation); - switch (GameType) - { - case GameType.Genshin: - return GetVoiceLanguageID_Genshin(RegPath); - case GameType.StarRail: - return GetVoiceLanguageID_StarRail(RegPath); - default: - return int.MinValue; - } - } - - private int GetVoiceLanguageID_StarRail(string RegPath) - { - try - { - string regValue; - RegistryKey? keys = Registry.CurrentUser.OpenSubKey(ConfigRegistryLocation!); - byte[]? value = (byte[]?)keys?.GetValue("LanguageSettings_LocalAudioLanguage_h882585060"); - - if (keys is null || value is null || value.Length is 0) - { - LogWriteLine($"Voice Language ID registry on {RegPath} doesn't exist. Fallback value will be used (2 / ja-jp).", LogType.Warning, true); - return 2; - } - - regValue = Encoding.UTF8.GetString(value).AsSpan().Trim('\0').ToString(); - return GetStarRailVoiceLanguageByName(regValue); - } - catch (JsonException ex) - { - LogWriteLine($"System.Text.Json cannot deserialize language ID registry in this path: {RegPath}\r\nFallback value will be used (2 / ja-jp).\r\n{ex}", LogType.Warning, true); - return 2; - } - catch (Exception ex) - { - LogWriteLine($"Launcher cannot evaluate an existing language ID registry on {RegPath}\r\nFallback value will be used (2 / ja-jp).\r\n{ex}", LogType.Warning, true); - return 2; - } - } - - private int GetVoiceLanguageID_Genshin(string RegPath) - { - try - { - ReadOnlySpan regValue; - RegistryKey? keys = Registry.CurrentUser.OpenSubKey(ConfigRegistryLocation!); - byte[]? value = (byte[]?)keys?.GetValue("GENERAL_DATA_h2389025596"); - - if (keys is null || value is null || value.Length is 0) - { - LogWriteLine($"Voice Language ID registry on {RegPath} doesn't exist. Fallback value will be used (2 / ja-jp).", LogType.Warning, true); - return 2; - } - - regValue = Encoding.UTF8.GetString(value).AsSpan().Trim('\0'); - GeneralDataProp? RegValues = (GeneralDataProp?)JsonSerializer.Deserialize(new string(regValue), typeof(GeneralDataProp), CoreLibraryJSONContext.Default); - return RegValues?.deviceVoiceLanguageType ?? 2; - } - catch (JsonException ex) - { - LogWriteLine($"System.Text.Json cannot deserialize language ID registry in this path: {RegPath}\r\nFallback value will be used (2 / ja-jp).\r\n{ex}", LogType.Warning, true); - return 2; - } - catch (Exception ex) - { - LogWriteLine($"Launcher cannot evaluate an existing language ID registry on {RegPath}\r\nFallback value will be used (2 / ja-jp).\r\n{ex}", LogType.Warning, true); - return 2; - } - } - - // WARNING!!! - // This feature is only available for Genshin and Star Rail. - public void SetVoiceLanguageID(int LangID) - { - switch (GameType) - { - case GameType.Genshin: - SetVoiceLanguageID_Genshin(LangID); - break; - case GameType.StarRail: - SetVoiceLanguageID_StarRail(LangID); - break; - } - } - - public int GetStarRailVoiceLanguageByName(string name) => name switch - { - "cn" => 0, - "tw" => 1, - "en" => 2, - "jp" => 3, - "kr" => 4, - _ => 3 // Set to JP by default - }; - - public string GetStarRailVoiceLanguageByID(int id) => id switch - { - 0 => "cn", - 1 => "cn", // Force Traditional Chinese value to use Simplified Chinese since they shared the same VO files (as provided by the API) - 2 => "en", - 3 => "jp", - 4 => "kr", - _ => "jp" // Set to JP by default - }; - - public string GetStarRailVoiceLanguageFullNameByID(int id) => id switch - { - 0 => "Chinese(PRC)", - 1 => "Chinese(PRC)", // Force Traditional Chinese value to use Simplified Chinese since they shared the same VO files (as provided by the API) - 2 => "English", - 3 => "Japanese", - 4 => "Korean", - _ => "Japanese" // Set to JP by default - }; - - public string GetStarRailVoiceLanguageFullNameByName(string name) => name switch - { - "cn" => "Chinese(PRC)", - "tw" => "Chinese(PRC)", // Force Traditional Chinese value to use Simplified Chinese since they shared the same VO files (as provided by the API) - "en" => "English", - "jp" => "Japanese", - "kr" => "Korean", - _ => "Japanese" // Set to JP by default - }; - - private void SetVoiceLanguageID_Genshin(int LangID) - { - try - { - RegistryKey? keys = Registry.CurrentUser.OpenSubKey(ConfigRegistryLocation!, true); - if (keys is null) keys = Registry.CurrentUser.CreateSubKey(ConfigRegistryLocation!); - var result = (byte[]?)keys.GetValue("GENERAL_DATA_h2389025596"); - var initValue = new GeneralDataProp(); - if (result != null) - { - ReadOnlySpan regValue = Encoding.UTF8.GetString(result).AsSpan().Trim('\0'); - initValue = (GeneralDataProp?)JsonSerializer.Deserialize(new string(regValue), typeof(GeneralDataProp), CoreLibraryJSONContext.Default) ?? initValue; - } - - initValue.deviceVoiceLanguageType = LangID; - keys.SetValue("GENERAL_DATA_h2389025596", - Encoding.UTF8.GetBytes( - JsonSerializer.Serialize(initValue, typeof(GeneralDataProp), CoreLibraryJSONContext.Default) + '\0')); - } - catch (Exception ex) - { - LogWriteLine($"Cannot save voice language ID: {LangID} to the registry!\r\n{ex}", LogType.Error, true); - } - } - - private void SetVoiceLanguageID_StarRail(int LangID) - { - try - { - RegistryKey? keys = Registry.CurrentUser.OpenSubKey(ConfigRegistryLocation!, true); - if (keys is null) keys = Registry.CurrentUser.CreateSubKey(ConfigRegistryLocation!); - - string initValue = GetStarRailVoiceLanguageByID(LangID); - keys.SetValue("LanguageSettings_LocalAudioLanguage_h882585060", Encoding.UTF8.GetBytes(initValue + '\0')); - } - catch (Exception ex) - { - LogWriteLine($"Cannot save voice language ID: {LangID} to the registry!\r\n{ex}", LogType.Error, true); - } - } - - #endregion - - public byte[]? GetGameDataTemplate(string key, byte[] gameVersion) - { - if (GameDataTemplates == null || !GameDataTemplates.ContainsKey(key)) return null; - - GameDataTemplate value = GameDataTemplates[key]; - if (value.DataVersion == null) return null; - - int verInt = GameDataVersion.GetBytesToIntVersion(gameVersion); - if (!value.DataVersion.ContainsKey(verInt)) return null; - - GameDataVersion verData = value.DataVersion[verInt]; - if (!verData.isCompressed) return verData.Data; - - byte[] dataOut = new byte[verData.Length]; - BrotliDecoder.TryDecompress(verData.Data, dataOut, out _); - return dataOut; - } - - // WARNING!!! - // This feature is only available for Genshin. - public int GetRegServerNameID() - { - if (!IsGenshin ?? true) return 0; - RegistryKey? keys = Registry.CurrentUser.OpenSubKey(ConfigRegistryLocation!); - byte[]? value = (byte[]?)keys?.GetValue("GENERAL_DATA_h2389025596", new byte[] { }, RegistryValueOptions.None); - - if (keys is null || value is null || value.Length is 0) - { - LogWriteLine($"Server name ID registry on \u001b[32;1m{Path.GetFileName(ConfigRegistryLocation)}\u001b[0m doesn't exist. Fallback value will be used (0 / USA).", LogType.Warning, true); - return 0; - } - - string regValue = new string(Encoding.UTF8.GetString(value).AsSpan().Trim('\0')); - - try - { - return (int?)(((GeneralDataProp?)JsonSerializer.Deserialize(regValue, typeof(GeneralDataProp), CoreLibraryJSONContext.Default))?.selectedServerName) ?? 0; - } - catch (Exception ex) - { - LogWriteLine($"Error while getting existing GENERAL_DATA_h2389025596 value on {ZoneFullname}! Returning value 0 as fallback!\r\nValue: {regValue}\r\n{ex}", LogType.Warning, true); - return 0; - } - } - - #region Genshin Registry Handler - // WARNING!!! - // This feature is only available for Genshin. - [SuppressMessage("ReSharper", "RedundantDefaultMemberInitializer")] - public class GeneralDataProp - { - public string deviceUUID { get; set; } = ""; - public string userLocalDataVersionId { get; set; } = "0.0.1"; - public int deviceLanguageType { get; set; } = 1; - public int deviceVoiceLanguageType { get; set; } = 2; - [JsonPropertyName("selectedServerName")] - public string _selectedServerName { get; set; } = "os_asia"; - [JsonIgnore] - public ServerRegionID selectedServerName - { - get - { - string valueFromReg = _selectedServerName; - // ReSharper disable once RedundantTypeArgumentsOfMethod - if (!Enum.TryParse(valueFromReg, true, out ServerRegionID result)) - return ServerRegionID.os_asia; - - return result; - } - set => _selectedServerName = $"{value}"; - } - public int localLevelIndex { get; set; } = 0; - public string deviceID { get; set; } = ""; - public string targetUID { get; set; } = ""; - public string curAccountName { get; set; } = ""; - public string uiSaveData { get; set; } = ""; - public string inputData { get; set; } = ""; - public string graphicsData { get; set; } = ""; - public string globalPerfData { get; set; } = ""; - public int miniMapConfig { get; set; } = 1; - public bool enableCameraSlope { get; set; } = true; - public bool enableCameraCombatLock { get; set; } = true; - public bool completionPkg { get; set; } = false; - public bool completionPlayGoPkg { get; set; } = false; - public bool onlyPlayWithPSPlayer { get; set; } = false; - public bool needPlayGoFullPkgPatch { get; set; } = false; - public bool resinNotification { get; set; } = true; - public bool exploreNotification { get; set; } = true; - public int volumeGlobal { get; set; } = 10; - public int volumeSFX { get; set; } = 10; - public int volumeMusic { get; set; } = 10; - public int volumeVoice { get; set; } = 10; - public int audioAPI { get; set; } = -1; - public int audioDynamicRange { get; set; } = 0; - public int audioOutput { get; set; } = 1; - public bool _audioSuccessInit { get; set; } = true; - public bool enableAudioChangeAndroidMinimumBufferCapacity { get; set; } = true; - public int audioAndroidMiniumBufferCapacity { get; set; } = 2 << 10; - public bool motionBlur { get; set; } = true; - public bool gyroAiming { get; set; } = false; - public bool firstHDRSetting { get; set; } = true; - public double maxLuminosity { get; set; } = 0.0f; - public double uiPaperWhite { get; set; } = 0.0f; - public double scenePaperWhite { get; set; } = 0.0f; - public double gammaValue { get; set; } = 2.200000047683716; - public List _overrideControllerMapKeyList { get; set; } = new List(); - public List _overrideControllerMapValueList { get; set; } = new List(); - public bool rewiredDisableKeyboard { get; set; } = false; - public bool rewiredEnableKeyboard { get; set; } = false; - public bool rewiredEnableEDS { get; set; } = false; - public bool disableRewiredDelayInit { get; set; } = false; - public bool disableRewiredInitProtection { get; set; } = false; - public int lastSeenPreDownloadTime { get; set; } = 0; - public bool enableEffectAssembleInEditor { get; set; } = true; - public bool forceDisableQuestResourceManagement { get; set; } = false; - public bool needReportQuestResourceDeleteStatusFiles { get; set; } = false; - public bool mtrCached { get; set; } = true; - public bool mtrIsOpen { get; set; } = true; - public int mtrMaxTTL { get; set; } = 32; - public int mtrTimeOut { get; set; } = 5000; - public int mtrTraceCount { get; set; } = 5; - public int mtrAbortTimeOutCount { get; set; } = 3; - public int mtrAutoTraceInterval { get; set; } = 3600; - public int mtrTraceCDEachReason { get; set; } = 600; - public int mtrTimeInterval { get; set; } = 1000; - public List mtrBanReasons { get; set; } = new List(); - public List _customDataKeyList { get; set; } = new List(); - public List _customDataValueList { get; set; } = new List(); - public List _serializedCodeSwitches { get; set; } = new List(); - public bool urlCheckCached { get; set; } = false; - public bool urlCheckIsOpen { get; set; } = false; - public bool urlCheckAllIP { get; set; } = false; - public int urlCheckTimeOut { get; set; } = 5000; - public int urlCheckSueecssTraceCount { get; set; } = 5; - public int urlCheckErrorTraceCount { get; set; } = 30; - public int urlCheckAbortTimeOutCount { get; set; } = 3; - public int urlCheckTimeInterval { get; set; } = 1000; - public int urlCheckCDEachReason { get; set; } = 600; - public List urlCheckBanReasons { get; set; } = new List(); - public bool mtrUseOldWinVersion { get; set; } = false; - } - #endregion - - #region Game Configs Check - public bool CheckExistingGame() - { - try - { - string? Value = (string?)Registry.GetValue(InstallRegistryLocation!, "InstallPath", null); - return TryCheckGameLocation(Value); - } - catch (Exception e) - { - LogWriteLine($"{e}", LogType.Error, true); - return false; - } - } - - public bool TryCheckGameLocation(in string? Path) - { - if (string.IsNullOrEmpty(Path)) return CheckInnerGameConfig(DefaultGameLocation!); - if (Directory.Exists(Path)) return CheckInnerGameConfig(Path); - - return false; - } - - private bool CheckInnerGameConfig(in string GamePath) - { - string ConfigPath = Path.Combine(GamePath, "config.ini"); - if (!File.Exists(ConfigPath)) return false; - - IniFile Ini = new IniFile(); - Ini.Load(ConfigPath); - - string? path1 = Ini["launcher"]!["game_install_path"].ToString(); - - if (path1 == null) return false; - - ActualGameDataLocation = ConverterTool.NormalizePath(path1); - - return File.Exists(Path.Combine(ActualGameDataLocation!, "config.ini")) && File.Exists(Path.Combine(ActualGameDataLocation!, GameExecutableName!)); - } - #endregion - - #region Metadata Properties - public string? GameName { get; set; } - public int HashID { get; set; } - private string? SystemDriveLetter { get => Path.GetPathRoot(Environment.GetFolderPath(Environment.SpecialFolder.System)); } - public string? ProfileName { get; set; } - public GameChannel GameChannel { get; set; } = GameChannel.Stable; - public bool IsExperimental { get; set; } - public string? ZoneName { get; set; } - public string? ZoneFullname { get; set; } - public string? ZoneDescription { get; set; } - public string? ZoneURL { get; set; } - public string? ZoneLogoURL { get; set; } - public string? ZonePosterURL { get; set; } - -#nullable disable - public string InstallRegistryLocation { get => string.Format(PrefixRegInstallLocation, InternalGameNameInConfig); } - public string DefaultGameLocation { get => string.Format(PrefixDefaultProgramFiles, InternalGameNameFolder, SystemDriveLetter); } - public string ConfigRegistryLocation { get => string.Format(PrefixRegGameConfig, VendorType, InternalGameNameInConfig); } -#nullable enable - public string? BetterHi3LauncherVerInfoReg { get; set; } - public string? ActualGameDataLocation { get; set; } - public string? FallbackLanguage { get; set; } - public string? SteamInstallRegistryLocation { get; set; } - public int? SteamGameID { get; set; } - public string? GameDirectoryName { get; set; } -#nullable disable - public string GameExecutableName { get; set; } -#nullable enable - public string? GameDispatchURL { get; set; } - public string[]? GameSupportedLanguages { get; set; } - public string[]? GameDispatchArrayURL { get; set; } - public string? GameDispatchChannelName { get; set; } - public string? GameDispatchDefaultName { get; set; } - public string? GameDispatchURLTemplate { get; set; } - public string? GameGatewayURLTemplate { get; set; } - public string? GameGatewayDefault { get; set; } - public AudioLanguageType GameDefaultCVLanguage { get; set; } - public string? ProtoDispatchKey { get; set; } - public bool? IsGenshin { get; set; } - public bool? IsConvertible { get; set; } - public bool IsHideSocMedDesc { get; set; } = true; - public List? ConvertibleTo { get; set; } - public GameType GameType { get; set; } = GameType.Unknown; - public GameType FallbackGameType { get; set; } = GameType.Unknown; - public GameVendorType VendorType { get; set; } = GameVendorType.miHoYo; - public int LauncherID { get; set; } - public int ChannelID { get; set; } - public int SubChannelID { get; set; } - public bool? UseRightSideProgress { get; set; } - public bool LauncherSpriteURLMultiLang { get; set; } - public string? LauncherSpriteURLMultiLangFallback { get; set; } - public string? LauncherPluginURL { get; set; } - public string? LauncherSpriteURL { get; set; } - public string? LauncherResourceURL { get; set; } - public string? DispatcherKey { get; set; } - public int? DispatcherKeyBitLength { get; set; } -#if DEBUG - public bool? IsRepairEnabled = true; - public bool? IsCacheUpdateEnabled = true; -#else - public bool? IsRepairEnabled { get; set; } - public bool? IsCacheUpdateEnabled { get; set; } -#endif - public string? InternalGameNameFolder { get; set; } - public string? InternalGameNameInConfig { get; set; } - public Dictionary? GameDataTemplates { get; set; } - public Dictionary? ZoneSteamAssets { get; set; } = new(); - #endregion - } - - #region Misc Metadata Methods - public class SteamGameProp - { - public string? URL { get; set; } - public string? MD5 { get; set; } - } - - public class GameDataTemplate - { - public Dictionary? DataVersion { get; set; } - } - - public class GameDataVersion - { - public byte[]? Data { get; set; } - public long Length { get; set; } - public bool isCompressed { get; set; } - - public static int GetBytesToIntVersion(byte[] version) - { - if (version.Length != 4) throw new FormatException("Argument: version must have 4 bytes"); - return version[0] | version[1] << 8 | version[2] << 16 | version[3] << 24; - } - } - #endregion -} diff --git a/Hi3Helper.Core/Classes/Shared/Region/ConfigV2Store.cs b/Hi3Helper.Core/Classes/Shared/Region/ConfigV2Store.cs index c67739c56..5f282702b 100644 --- a/Hi3Helper.Core/Classes/Shared/Region/ConfigV2Store.cs +++ b/Hi3Helper.Core/Classes/Shared/Region/ConfigV2Store.cs @@ -1,134 +1 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text.Json; -using static Hi3Helper.Logger; -using static Hi3Helper.Shared.Region.LauncherConfig; - -namespace Hi3Helper.Preset -{ - public static class ConfigV2Store - { - public static Metadata ConfigV2 = new Metadata(); - public static PresetConfigV2 CurrentConfigV2; - public static string CurrentConfigV2GameCategory; - public static string CurrentConfigV2GameRegion; - - public static long ConfigV2LastUpdate; - - public static bool IsGameCategoryLocked = false; - public static bool IsGameRegionsLocked = false; - public static List ConfigV2GameCategory; - public static List ConfigV2GameRegions; - - public static PresetConfigV2 LoadCurrentConfigV2(string Key, string Value) - { - CurrentConfigV2 = ConfigV2!.MetadataV2![Key!]![Value!]; - CurrentConfigV2GameCategory = Key; - CurrentConfigV2GameRegion = Value; - - return CurrentConfigV2; - } - - public static void LoadConfigV2CacheOnly() - { - LoadConfigV2(); - Dictionary> res = new Dictionary>(); - Dictionary phase1; - - foreach (KeyValuePair> a in ConfigV2!.MetadataV2!) - { - phase1 = a.Value! - .Where(b => b.Value!.IsCacheUpdateEnabled ?? false) - .ToDictionary(b => b.Key, b => b.Value); - - if (phase1.Count > 0) res.Add(a.Key!, phase1); - } - - ConfigV2.MetadataV2 = res; - ConfigV2GameCategory = ConfigV2.MetadataV2.Keys.ToList(); - } - - public static void LoadConfigV2() - { - string stamp = File.ReadAllText(AppGameConfigV2StampPath!); - string content = File.ReadAllText(AppGameConfigV2MetadataPath!); - if (string.IsNullOrEmpty(stamp)) throw new NullReferenceException($"{AppGameConfigV2StampPath} file seems to be empty. Please remove it and restart the launcher!"); - if (string.IsNullOrEmpty(content)) throw new NullReferenceException($"{AppGameConfigV2MetadataPath} file seems to be empty. Please remove it and restart the launcher!"); - - ConfigV2 = (Metadata)JsonSerializer - .Deserialize(content, typeof(Metadata), CoreLibraryJSONContext.Default); - - if (ConfigV2 is null) throw new NullReferenceException("Metadata config is broken"); - - ConfigV2GameCategory = ConfigV2.MetadataV2!.Keys.ToList(); - - ConfigV2LastUpdate = ((Stamp)JsonSerializer - .Deserialize(stamp, typeof(Stamp), CoreLibraryJSONContext.Default)!).LastUpdated; - - ConfigV2.DecryptStrings(); - ConfigV2.GenerateHashID(); - } - - public static bool GetConfigV2Regions(string GameCategoryName) - { - if (!ConfigV2!.MetadataV2!.ContainsKey(GameCategoryName!)) - { - ConfigV2GameRegions = ConfigV2.MetadataV2.FirstOrDefault().Value!.Keys.ToList(); - LogWriteLine($"Game category \"{GameCategoryName}\" isn't found!", LogType.Error, true); - return false; - } - - ConfigV2GameRegions = ConfigV2.MetadataV2[GameCategoryName]!.Keys.ToList(); - return true; - } - - public static int GetPreviousGameRegion(string GameCategoryName) - { - string iniKeyName = $"LastRegion_{GameCategoryName!.Replace(" ", string.Empty)}"; - string regionName; - - if (!IsConfigKeyExist(iniKeyName)) - { - regionName = ConfigV2GameRegions!.FirstOrDefault(); - SetAndSaveConfigValue(iniKeyName, regionName); - return ConfigV2GameRegions.IndexOf(regionName); - } - - regionName = GetAppConfigValue(iniKeyName).ToString(); - if (ConfigV2GameRegions!.Contains(regionName)) - { - return ConfigV2GameRegions.IndexOf(regionName); - } - - regionName = ConfigV2GameRegions.FirstOrDefault(); - return ConfigV2GameRegions.IndexOf(regionName); - } - - public static void SetPreviousGameRegion(string GameCategoryName, string RegionName, bool isSave = true) - { - string iniKeyName = $"LastRegion_{GameCategoryName!.Replace(" ", string.Empty)}"; - - if (isSave) - { - SetAndSaveConfigValue(iniKeyName, RegionName); - } - else - { - SetAppConfigValue(iniKeyName, RegionName); - } - } - - public static bool IsConfigV2StampExist() => CheckConfigV2StampContent(AppGameConfigV2StampPath); - public static bool IsConfigV2ContentExist() => CheckConfigV2StampContent(AppGameConfigV2MetadataPath); - - private static bool CheckConfigV2StampContent(string name) - { - FileInfo file = new FileInfo(name!); - if (!file.Exists) return false; - if (file.Length < 2) return false; - return true; - } - } -} + \ No newline at end of file diff --git a/Hi3Helper.Core/Classes/Shared/Region/LauncherConfig.cs b/Hi3Helper.Core/Classes/Shared/Region/LauncherConfig.cs index 06e89f47b..66a5c1d64 100644 --- a/Hi3Helper.Core/Classes/Shared/Region/LauncherConfig.cs +++ b/Hi3Helper.Core/Classes/Shared/Region/LauncherConfig.cs @@ -1,7 +1,4 @@ using Hi3Helper.Data; -#if !DISABLEDISCORD -using Hi3Helper.DiscordPresence; -#endif using Hi3Helper.Screen; using Hi3Helper.Shared.ClassStruct; using System; @@ -236,15 +233,10 @@ public static int AppCurrentThread } public static int AppCurrentDownloadThread => GetAppConfigValue("DownloadThread").ToInt(); public static string AppGameConfigMetadataFolder { get => Path.Combine(AppGameFolder!, "_metadata"); } - public static string AppGameConfigV2StampPath { get => Path.Combine(AppGameConfigMetadataFolder!, "stampv2.json"); } - public static string AppGameConfigV2MetadataPath { get => Path.Combine(AppGameConfigMetadataFolder!, "metadatav2.json"); } + public static string AppGameConfigV2StampPath { get => Path.Combine(AppGameConfigMetadataFolder!, "stampv3.json"); } + public static string AppGameConfigV2MetadataPath { get => Path.Combine(AppGameConfigMetadataFolder!, "metadatav3_{0}.json"); } -#if !DISABLEDISCORD -#pragma warning disable CA2211 -public static DiscordPresenceManager AppDiscordPresence; -#pragma warning restore CA2211 -#endif - public static readonly bool IsAppLangNeedRestart = false; + public static readonly bool IsAppLangNeedRestart = false; public static bool IsPreview = false; public static bool IsAppThemeNeedRestart = false; diff --git a/Hi3Helper.EncTool b/Hi3Helper.EncTool index 8aa500f0b..43fa17981 160000 --- a/Hi3Helper.EncTool +++ b/Hi3Helper.EncTool @@ -1 +1 @@ -Subproject commit 8aa500f0b34256f232bcd0276dbfb0e7c5d36564 +Subproject commit 43fa1798191a9ee6bdffca99d455a01d87cb260d