using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: IgnoresAccessChecksTo("")] [assembly: AssemblyCompany("REPOJP")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("zabuMod")] [assembly: AssemblyTitle("zabuMod")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace REPOJP.CosmeticPresetExtender { [BepInPlugin("REPOJP.CosmeticPresetExtender", "CosmeticPresetExtender", "4.0.0")] public class CosmeticPresetExtenderPlugin : BaseUnityPlugin { [Serializable] private class ExtendedPresetStore { public int version = 2; public int columns = 7; public int vanillaSlots = 28; public int currentRows = 10; public int currentSlots = 70; public string updatedAtUtc = ""; public List presets = new List(); public List cosmeticPresets = new List(); public List colorPresets = new List(); } [Serializable] private class ExtendedPresetEntry { public int index; public List cosmetics = new List(); public List colors = new List(); public string updatedAtUtc = ""; public string hash = ""; public ExtendedPresetEntry() { } public ExtendedPresetEntry(int index, List cosmetics, List colors, string updatedAtUtc, string hash) { this.index = index; this.cosmetics = CopyValues(cosmetics); this.colors = CopyValues(colors); this.updatedAtUtc = updatedAtUtc; this.hash = (string.IsNullOrEmpty(hash) ? BuildPresetHash(cosmetics, colors) : hash); } } [Serializable] private class LegacyPresetEntry { public int index; public List values = new List(); public LegacyPresetEntry() { } public LegacyPresetEntry(int index, List values) { this.index = index; this.values = CopyValues(values); } } private class SaveState { public string reason; public int targetSlots; public List> restoreCosmeticPresets; public List> restoreColorPresets; } [HarmonyPatch(typeof(MetaManager), "Awake")] private static class MetaManagerAwakePatch { private static void Prefix(MetaManager __instance) { if (!IsEnabled()) { return; } try { SetPresetSlotsField(__instance, GetTargetSlots()); } catch (Exception ex) { WriteFailureLog("MetaManager.Awake Prefix", ex); } } private static void Postfix(MetaManager __instance) { EnsurePresetRuntimeSlots(__instance, mergeExtendedStore: false, "MetaManager.Awake Postfix"); } } [HarmonyPatch(typeof(MetaManager), "Load")] private static class MetaManagerLoadPatch { private static void Prefix(MetaManager __instance) { EnsurePresetRuntimeSlots(__instance, mergeExtendedStore: false, "MetaManager.Load Prefix"); } private static void Postfix(MetaManager __instance) { EnsurePresetRuntimeSlots(__instance, mergeExtendedStore: true, "MetaManager.Load Postfix"); } } [HarmonyPatch(typeof(MetaManager), "Save")] private static class MetaManagerSavePatch { private static void Prefix(MetaManager __instance, ref SaveState __state) { if (!IsEnabled() || (Object)(object)__instance == (Object)null) { return; } try { EnsurePresetRuntimeSlots(__instance, mergeExtendedStore: false, "MetaManager.Save Prefix"); __state = BuildSaveState(__instance, insideMetaReset ? "MetaManager.Save inside Reset" : "MetaManager.Save"); if (__state == null) { return; } if (!insideMetaReset || !ProtectExtendedSlotsOnReset()) { SaveExtendedStoreFromRuntime(__instance, __state.restoreCosmeticPresets, __state.restoreColorPresets, GetTargetSlots(), createHistory: false, __state.reason); } if (insideMetaReset && ProtectExtendedSlotsOnReset()) { ExtendedPresetStore extendedPresetStore = LoadExtendedStore(allowBackupRecovery: true); NormalizeStore(extendedPresetStore); Dictionary dictionary = ToEntryMap(extendedPresetStore.presets); for (int i = 28; i < __state.targetSlots; i++) { __state.restoreCosmeticPresets[i] = new List(); __state.restoreColorPresets[i] = new List(); if (dictionary.ContainsKey(i)) { __state.restoreCosmeticPresets[i] = CopyValues(dictionary[i].cosmetics); __state.restoreColorPresets[i] = CopyValues(dictionary[i].colors); } } } PrepareVanillaOnlySave(__instance, __state); } catch (Exception ex) { WriteFailureLog("MetaManager.Save Prefix", ex); } } private static Exception Finalizer(MetaManager __instance, SaveState __state, Exception __exception) { try { if (IsEnabled() && __state != null) { RestoreRuntimeAfterVanillaSave(__instance, __state); } } catch (Exception ex) { WriteFailureLog("MetaManager.Save Finalizer", ex); } return __exception; } } [HarmonyPatch(typeof(MetaManager), "Reset")] private static class MetaManagerResetPatch { private static void Prefix(MetaManager __instance) { if (!IsEnabled()) { return; } try { EnsurePresetRuntimeSlots(__instance, mergeExtendedStore: false, "MetaManager.Reset Prefix"); SaveExtendedPresetsNow(createHistory: true, "before_meta_reset"); insideMetaReset = true; } catch (Exception ex) { insideMetaReset = true; WriteFailureLog("MetaManager.Reset Prefix", ex); } } private static void Postfix(MetaManager __instance) { try { if (IsEnabled()) { insideMetaReset = false; if (!ProtectExtendedSlotsOnReset()) { ClearExtendedStore(createHistory: true, "meta_reset_clear_extended"); } EnsurePresetRuntimeSlots(__instance, ProtectExtendedSlotsOnReset(), "MetaManager.Reset Postfix"); } } catch (Exception ex) { WriteFailureLog("MetaManager.Reset Postfix", ex); } finally { insideMetaReset = false; } } private static Exception Finalizer(Exception __exception) { insideMetaReset = false; return __exception; } } [HarmonyPatch(typeof(MetaManager), "CosmeticPresetSet")] private static class MetaManagerCosmeticPresetSetPatch { private static void Prefix(MetaManager __instance, int _index) { if (_index >= 0) { EnsurePresetRuntimeSlotsForIndex(__instance, _index, "MetaManager.CosmeticPresetSet Prefix"); } } private static void Postfix(MetaManager __instance, int _index) { if (!IsEnabled() || (Object)(object)__instance == (Object)null || _index < 28) { return; } try { List> presetList = GetPresetList(__instance, cosmeticPresetsField); List> presetList2 = GetPresetList(__instance, colorPresetsField); SaveExtendedStoreFromRuntime(__instance, presetList, presetList2, GetTargetSlots(), createHistory: false, "CosmeticPresetSet " + _index); } catch (Exception ex) { WriteFailureLog("MetaManager.CosmeticPresetSet Postfix", ex); } } } [HarmonyPatch(typeof(MenuPageCosmetics), "RefreshScrollContent")] private static class MenuPageCosmeticsRefreshScrollContentPatch { private static void Prefix() { if ((Object)(object)MetaManager.instance != (Object)null) { EnsurePresetRuntimeSlots(MetaManager.instance, mergeExtendedStore: true, "MenuPageCosmetics.RefreshScrollContent Prefix"); } } private static void Postfix(MenuPageCosmetics __instance) { RefreshPresetGridLayout(__instance); } } [HarmonyPatch(typeof(MenuElementCosmeticPreset), "Start")] private static class MenuElementCosmeticPresetStartPatch { private static void Prefix(MenuElementCosmeticPreset __instance) { if (!((Object)(object)__instance == (Object)null) && !((Object)(object)MetaManager.instance == (Object)null)) { EnsurePresetRuntimeSlotsForIndex(MetaManager.instance, __instance.presetIndex, "MenuElementCosmeticPreset.Start Prefix"); } } } [HarmonyPatch(typeof(MenuElementCosmeticPreset), "TogglePreset")] private static class MenuElementCosmeticPresetTogglePresetPatch { private static void Prefix(MenuElementCosmeticPreset __instance) { if (!((Object)(object)__instance == (Object)null) && !((Object)(object)MetaManager.instance == (Object)null)) { EnsurePresetRuntimeSlotsForIndex(MetaManager.instance, __instance.presetIndex, "MenuElementCosmeticPreset.TogglePreset Prefix"); } } } [HarmonyPatch(typeof(MenuElementCosmeticPreset), "DeletePreset")] private static class MenuElementCosmeticPresetDeletePresetPatch { private static void Prefix(MenuElementCosmeticPreset __instance) { if (!((Object)(object)__instance == (Object)null) && !((Object)(object)MetaManager.instance == (Object)null)) { EnsurePresetRuntimeSlotsForIndex(MetaManager.instance, __instance.presetIndex, "MenuElementCosmeticPreset.DeletePreset Prefix"); } } } [HarmonyPatch(typeof(DebugCommandHandler), "Start")] private static class DebugCommandHandlerStartPatch { private static void Postfix(DebugCommandHandler __instance) { RegisterCommands(__instance); } } public const string PluginGuid = "REPOJP.CosmeticPresetExtender"; public const string PluginName = "CosmeticPresetExtender"; public const string PluginVersion = "4.0.0"; private const int VanillaColumns = 7; private const int VanillaRows = 4; private const int VanillaSlots = 28; private const int MinRows = 4; private const int MaxRows = 100; private const int DefaultRows = 10; private const float PresetCellWidth = 60f; private const float PresetCellHeight = 140f; private static CosmeticPresetExtenderPlugin instance; private static ManualLogSource logSource; private static Harmony harmony; private static ConfigEntry configEnabled; private static ConfigEntry configPresetRows; private static ConfigEntry configProtectExtendedSlotsOnReset; private static ConfigEntry configBackupHistoryCount; private static ConfigEntry configDetailedLog; private static readonly FieldInfo presetSlotsField = AccessTools.Field(typeof(MetaManager), "presetSlots"); private static readonly FieldInfo cosmeticPresetsField = AccessTools.Field(typeof(MetaManager), "cosmeticPresets"); private static readonly FieldInfo colorPresetsField = AccessTools.Field(typeof(MetaManager), "colorPresets"); private static readonly FieldInfo colorsEquippedField = AccessTools.Field(typeof(MetaManager), "colorsEquipped"); private static readonly FieldInfo cosmeticAssetsField = AccessTools.Field(typeof(MetaManager), "cosmeticAssets"); private static readonly FieldInfo colorsField = AccessTools.Field(typeof(MetaManager), "colors"); private static bool insideMetaReset; private static bool commandRegistered; private static string StoreDirectory => Path.Combine(Paths.ConfigPath, "REPOJP.CosmeticPresetExtender"); private static string StoreFilePath => Path.Combine(StoreDirectory, "ExtendedPresets.json"); private static string StoreBackupFilePath => Path.Combine(StoreDirectory, "ExtendedPresets.json.bak"); private static string HistoryDirectory => Path.Combine(StoreDirectory, "history"); private void Awake() { //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Expected O, but got Unknown //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Expected O, but got Unknown //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Expected O, but got Unknown try { instance = this; logSource = ((BaseUnityPlugin)this).Logger; ((Component)this).transform.parent = null; ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); configEnabled = ((BaseUnityPlugin)this).Config.Bind("General", "Enabled", true, "Enables this mod.このMODを有効化します"); configPresetRows = ((BaseUnityPlugin)this).Config.Bind("General", "PresetRows", 10, new ConfigDescription("Cosmetic preset rows. 7 columns are fixed.コスメプリセットの行数。7列固定", (AcceptableValueBase)(object)new AcceptableValueRange(4, 100), Array.Empty())); configProtectExtendedSlotsOnReset = ((BaseUnityPlugin)this).Config.Bind("Save Safety", "ProtectExtendedSlotsOnReset", true, "Keeps slot 29 and later when vanilla meta progress is reset.バニラのメタ進行リセット時も29枠目以降を保持します"); configBackupHistoryCount = ((BaseUnityPlugin)this).Config.Bind("Save Safety", "BackupHistoryCount", 10, new ConfigDescription("Maximum history backup files.履歴バックアップファイルの最大保持数", (AcceptableValueBase)(object)new AcceptableValueRange(0, 100), Array.Empty())); configDetailedLog = ((BaseUnityPlugin)this).Config.Bind("Debug", "DetailedLog", false, "Outputs detailed debug logs.詳細ログを出力します"); harmony = new Harmony("REPOJP.CosmeticPresetExtender"); harmony.PatchAll(typeof(CosmeticPresetExtenderPlugin).Assembly); WriteLog("Loaded. Rows=" + GetTargetRows() + " Slots=" + GetTargetSlots() + " VanillaSaveSlots=" + 28); } catch (Exception ex) { if (logSource != null) { logSource.LogError((object)("Failure: Awake\n" + ex)); } } } private void OnDestroy() { try { if (harmony != null) { harmony.UnpatchSelf(); harmony = null; } } catch (Exception ex) { if (logSource != null) { logSource.LogError((object)("Failure: OnDestroy\n" + ex)); } } } private static bool IsEnabled() { return configEnabled == null || configEnabled.Value; } private static int GetTargetRows() { int num = 10; if (configPresetRows != null) { num = configPresetRows.Value; } return Mathf.Clamp(num, 4, 100); } private static int GetTargetSlots() { return GetTargetRows() * 7; } private static int GetHistoryLimit() { int num = 10; if (configBackupHistoryCount != null) { num = configBackupHistoryCount.Value; } return Mathf.Clamp(num, 0, 100); } private static bool ProtectExtendedSlotsOnReset() { return configProtectExtendedSlotsOnReset == null || configProtectExtendedSlotsOnReset.Value; } private static void WriteLog(string message) { if (logSource != null) { logSource.LogInfo((object)message); } } private static void WriteDetailLog(string message) { if (configDetailedLog != null && configDetailedLog.Value && logSource != null) { logSource.LogInfo((object)message); } } private static void WriteFailureLog(string message, Exception ex) { if (logSource != null) { logSource.LogError((object)("Failure: " + message + "\n" + ex)); } } private static void SetPresetSlotsField(MetaManager manager, int slots) { if (!((Object)(object)manager == (Object)null) && !(presetSlotsField == null)) { presetSlotsField.SetValue(manager, slots); } } private static List> GetPresetList(MetaManager manager, FieldInfo field) { if ((Object)(object)manager == (Object)null || field == null) { return null; } return field.GetValue(manager) as List>; } private static void SetPresetList(MetaManager manager, FieldInfo field, List> value) { if (!((Object)(object)manager == (Object)null) && !(field == null)) { field.SetValue(manager, value); } } private static int[] GetColorsEquipped(MetaManager manager) { if ((Object)(object)manager == (Object)null || colorsEquippedField == null) { return null; } return colorsEquippedField.GetValue(manager) as int[]; } private static int GetCosmeticAssetCount(MetaManager manager) { if ((Object)(object)manager == (Object)null || cosmeticAssetsField == null) { return 0; } object value = cosmeticAssetsField.GetValue(manager); if (!(value is ICollection collection)) { return 0; } return collection.Count; } private static int GetColorCount(MetaManager manager) { if ((Object)(object)manager == (Object)null || colorsField == null) { return 0; } object value = colorsField.GetValue(manager); if (!(value is ICollection collection)) { return 0; } return collection.Count; } private static void EnsurePresetRuntimeSlots(MetaManager manager, bool mergeExtendedStore, string reason) { if (!IsEnabled() || (Object)(object)manager == (Object)null) { return; } try { int targetSlots = GetTargetSlots(); SetPresetSlotsField(manager, targetSlots); List> list = GetPresetList(manager, cosmeticPresetsField); List> list2 = GetPresetList(manager, colorPresetsField); if (list == null) { list = new List>(); SetPresetList(manager, cosmeticPresetsField, list); } if (list2 == null) { list2 = new List>(); SetPresetList(manager, colorPresetsField, list2); } ExpandList(list, targetSlots); ExpandList(list2, targetSlots); NormalizeLists(manager, list, list2); if (mergeExtendedStore) { MergeExtendedStoreIntoRuntime(manager, list, list2, forceOverwrite: false); NormalizeLists(manager, list, list2); } WriteDetailLog("EnsurePresetRuntimeSlots: " + reason + " Rows=" + GetTargetRows() + " Slots=" + targetSlots); } catch (Exception ex) { WriteFailureLog("EnsurePresetRuntimeSlots / " + reason, ex); } } private static void EnsurePresetRuntimeSlotsForIndex(MetaManager manager, int index, string reason) { if (!IsEnabled() || (Object)(object)manager == (Object)null || index < 0) { return; } try { int num = Mathf.Max(GetTargetSlots(), index + 1); SetPresetSlotsField(manager, num); List> list = GetPresetList(manager, cosmeticPresetsField); List> list2 = GetPresetList(manager, colorPresetsField); if (list == null) { list = new List>(); SetPresetList(manager, cosmeticPresetsField, list); } if (list2 == null) { list2 = new List>(); SetPresetList(manager, colorPresetsField, list2); } ExpandList(list, num); ExpandList(list2, num); NormalizeLists(manager, list, list2); WriteDetailLog("EnsurePresetRuntimeSlotsForIndex: " + reason + " Index=" + index); } catch (Exception ex) { WriteFailureLog("EnsurePresetRuntimeSlotsForIndex / " + reason, ex); } } private static void ExpandList(List> list, int targetCount) { if (list == null) { return; } while (list.Count < targetCount) { list.Add(new List()); } for (int i = 0; i < list.Count; i++) { if (list[i] == null) { list[i] = new List(); } } } private static List> ClonePresetList(List> source, int targetCount) { List> list = new List>(); for (int i = 0; i < targetCount; i++) { if (source != null && i < source.Count && source[i] != null) { list.Add(new List(source[i])); } else { list.Add(new List()); } } return list; } private static List> CloneFirstSlots(List> source, int count) { List> list = new List>(); for (int i = 0; i < count; i++) { if (source != null && i < source.Count && source[i] != null) { list.Add(new List(source[i])); } else { list.Add(new List()); } } return list; } private static List CopyValues(List source) { if (source == null) { return new List(); } return new List(source); } private static bool HasPresetData(List cosmetics, List colors) { bool flag = cosmetics != null && cosmetics.Count > 0; bool flag2 = colors != null && colors.Count > 0; return flag || flag2; } private static void NormalizeLists(MetaManager manager, List> cosmeticPresets, List> colorPresets) { if ((Object)(object)manager == (Object)null || cosmeticPresets == null || colorPresets == null) { return; } int targetCount = Mathf.Max(GetTargetSlots(), Mathf.Max(cosmeticPresets.Count, colorPresets.Count)); ExpandList(cosmeticPresets, targetCount); ExpandList(colorPresets, targetCount); int cosmeticAssetCount = GetCosmeticAssetCount(manager); int colorCount = GetColorCount(manager); int[] colorsEquipped = GetColorsEquipped(manager); int num = ((colorsEquipped != null) ? colorsEquipped.Length : 0); for (int i = 0; i < cosmeticPresets.Count; i++) { List list = cosmeticPresets[i]; if (list == null) { cosmeticPresets[i] = new List(); continue; } for (int num2 = list.Count - 1; num2 >= 0; num2--) { int num3 = list[num2]; if (num3 < 0 || (cosmeticAssetCount > 0 && num3 >= cosmeticAssetCount)) { list.RemoveAt(num2); } } } for (int j = 0; j < colorPresets.Count; j++) { List list2 = colorPresets[j]; if (list2 == null) { colorPresets[j] = new List(); list2 = colorPresets[j]; } bool flag = HasPresetData((j < cosmeticPresets.Count) ? cosmeticPresets[j] : null, list2); if (num > 0 && flag) { while (list2.Count < num) { list2.Add(0); } while (list2.Count > num) { list2.RemoveAt(list2.Count - 1); } } for (int k = 0; k < list2.Count; k++) { if (list2[k] < 0) { list2[k] = 0; } if (colorCount > 0 && list2[k] >= colorCount) { list2[k] = 0; } } } } private static void MergeExtendedStoreIntoRuntime(MetaManager manager, List> cosmeticPresets, List> colorPresets, bool forceOverwrite) { if (!IsEnabled() || (Object)(object)manager == (Object)null || cosmeticPresets == null || colorPresets == null) { return; } try { ExtendedPresetStore extendedPresetStore = LoadExtendedStore(allowBackupRecovery: true); NormalizeStore(extendedPresetStore); int targetSlots = GetTargetSlots(); ExpandList(cosmeticPresets, targetSlots); ExpandList(colorPresets, targetSlots); Dictionary dictionary = ToEntryMap(extendedPresetStore.presets); for (int i = 28; i < targetSlots; i++) { if (!dictionary.ContainsKey(i)) { continue; } ExtendedPresetEntry extendedPresetEntry = dictionary[i]; if (!HasPresetData(cosmeticPresets[i], colorPresets[i]) || forceOverwrite) { string text = BuildPresetHash(cosmeticPresets[i], colorPresets[i]); cosmeticPresets[i] = CopyValues(extendedPresetEntry.cosmetics); colorPresets[i] = CopyValues(extendedPresetEntry.colors); string text2 = BuildPresetHash(cosmeticPresets[i], colorPresets[i]); if (text != text2) { DeletePresetIcon(i); } } } WriteDetailLog("Extended preset store merged. Entries=" + extendedPresetStore.presets.Count); } catch (Exception ex) { WriteFailureLog("MergeExtendedStoreIntoRuntime", ex); } } private static void SaveExtendedStoreFromRuntime(MetaManager manager, List> fullCosmeticPresets, List> fullColorPresets, int visibleSlots, bool createHistory, string reason) { if (!IsEnabled() || (Object)(object)manager == (Object)null) { return; } try { ExtendedPresetStore extendedPresetStore = LoadExtendedStore(allowBackupRecovery: true); NormalizeStore(extendedPresetStore); Dictionary dictionary = ToEntryMap(extendedPresetStore.presets); int num = Mathf.Max(28, visibleSlots); for (int i = 28; i < num; i++) { List list = ((fullCosmeticPresets != null && i < fullCosmeticPresets.Count && fullCosmeticPresets[i] != null) ? fullCosmeticPresets[i] : new List()); List list2 = ((fullColorPresets != null && i < fullColorPresets.Count && fullColorPresets[i] != null) ? fullColorPresets[i] : new List()); string text = BuildPresetHash(list, list2); if (dictionary.TryGetValue(i, out var value) && value != null && value.hash == text) { value.cosmetics = CopyValues(list); value.colors = CopyValues(list2); } else { dictionary[i] = new ExtendedPresetEntry(i, list, list2, DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ"), text); DeletePresetIcon(i); } } extendedPresetStore.version = 2; extendedPresetStore.columns = 7; extendedPresetStore.vanillaSlots = 28; extendedPresetStore.currentRows = GetTargetRows(); extendedPresetStore.currentSlots = GetTargetSlots(); extendedPresetStore.updatedAtUtc = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ"); extendedPresetStore.presets = ToSortedEntryList(dictionary); extendedPresetStore.cosmeticPresets = new List(); extendedPresetStore.colorPresets = new List(); WriteExtendedStore(extendedPresetStore, createHistory, reason); WriteDetailLog("Extended preset store saved. Reason=" + reason + " Entries=" + extendedPresetStore.presets.Count); } catch (Exception ex) { WriteFailureLog("SaveExtendedStoreFromRuntime / " + reason, ex); } } private static void ClearExtendedStore(bool createHistory, string reason) { try { ExtendedPresetStore store = LoadExtendedStore(allowBackupRecovery: true); NormalizeStore(store); if (createHistory) { WriteHistoryBackup(store, reason); } ExtendedPresetStore extendedPresetStore = new ExtendedPresetStore(); extendedPresetStore.version = 2; extendedPresetStore.columns = 7; extendedPresetStore.vanillaSlots = 28; extendedPresetStore.currentRows = GetTargetRows(); extendedPresetStore.currentSlots = GetTargetSlots(); extendedPresetStore.updatedAtUtc = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ"); WriteExtendedStore(extendedPresetStore, createHistory: false, reason); } catch (Exception ex) { WriteFailureLog("ClearExtendedStore / " + reason, ex); } } private static ExtendedPresetStore LoadExtendedStore(bool allowBackupRecovery) { try { if (TryReadStoreFile(StoreFilePath, out var data)) { NormalizeStore(data); return data; } if (allowBackupRecovery && TryReadStoreFile(StoreBackupFilePath, out data)) { NormalizeStore(data); RecoverPrimaryStoreFromBackup(data); return data; } } catch (Exception ex) { WriteFailureLog("LoadExtendedStore", ex); } return new ExtendedPresetStore(); } private static bool TryReadStoreFile(string path, out ExtendedPresetStore data) { data = null; try { if (string.IsNullOrEmpty(path) || !File.Exists(path)) { return false; } string text = File.ReadAllText(path, Encoding.UTF8); if (string.IsNullOrWhiteSpace(text)) { return false; } ExtendedPresetStore extendedPresetStore = JsonUtility.FromJson(text); if (extendedPresetStore == null) { return false; } if (extendedPresetStore.presets == null && extendedPresetStore.cosmeticPresets == null && extendedPresetStore.colorPresets == null) { return false; } data = extendedPresetStore; NormalizeStore(data); return true; } catch (Exception ex) { WriteFailureLog("TryReadStoreFile: " + path, ex); return false; } } private static void RecoverPrimaryStoreFromBackup(ExtendedPresetStore backupData) { try { Directory.CreateDirectory(StoreDirectory); if (File.Exists(StoreFilePath)) { string destFileName = StoreFilePath + ".corrupt_" + DateTime.Now.ToString("yyyyMMdd_HHmmss"); File.Move(StoreFilePath, destFileName); } WriteExtendedStore(backupData, createHistory: false, "recover_from_bak"); WriteLog("ExtendedPresets.json was recovered from .bak."); } catch (Exception ex) { WriteFailureLog("RecoverPrimaryStoreFromBackup", ex); } } private static void WriteExtendedStore(ExtendedPresetStore store, bool createHistory, string reason) { try { Directory.CreateDirectory(StoreDirectory); NormalizeStore(store); if (createHistory) { WriteHistoryBackup(store, reason); } string contents = JsonUtility.ToJson((object)store, true); string text = StoreFilePath + ".tmp"; File.WriteAllText(text, contents, Encoding.UTF8); if (File.Exists(StoreFilePath)) { File.Copy(StoreFilePath, StoreBackupFilePath, overwrite: true); File.Delete(StoreFilePath); } File.Move(text, StoreFilePath); CleanupHistoryBackups(); } catch (Exception ex) { WriteFailureLog("WriteExtendedStore / " + reason, ex); } } private static void WriteHistoryBackup(ExtendedPresetStore store, string reason) { try { int historyLimit = GetHistoryLimit(); if (historyLimit > 0) { Directory.CreateDirectory(HistoryDirectory); NormalizeStore(store); string text = SanitizeFileName(reason); string path = Path.Combine(HistoryDirectory, "ExtendedPresets_" + DateTime.Now.ToString("yyyyMMdd_HHmmss") + "_" + text + ".json"); string contents = JsonUtility.ToJson((object)store, true); File.WriteAllText(path, contents, Encoding.UTF8); CleanupHistoryBackups(); } } catch (Exception ex) { WriteFailureLog("WriteHistoryBackup / " + reason, ex); } } private static void CleanupHistoryBackups() { try { int historyLimit = GetHistoryLimit(); if (historyLimit <= 0 || !Directory.Exists(HistoryDirectory)) { return; } FileInfo[] files = new DirectoryInfo(HistoryDirectory).GetFiles("ExtendedPresets_*.json"); Array.Sort(files, (FileInfo a, FileInfo b) => b.CreationTimeUtc.CompareTo(a.CreationTimeUtc)); for (int i = historyLimit; i < files.Length; i++) { try { files[i].Delete(); } catch { } } } catch (Exception ex) { WriteFailureLog("CleanupHistoryBackups", ex); } } private static string SanitizeFileName(string value) { if (string.IsNullOrEmpty(value)) { return "manual"; } char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); string text = value; for (int i = 0; i < invalidFileNameChars.Length; i++) { text = text.Replace(invalidFileNameChars[i], '_'); } return text.Replace(' ', '_'); } private static void NormalizeStore(ExtendedPresetStore store) { if (store == null) { return; } if (store.presets == null) { store.presets = new List(); } if (store.cosmeticPresets == null) { store.cosmeticPresets = new List(); } if (store.colorPresets == null) { store.colorPresets = new List(); } if (store.presets.Count == 0 && (store.cosmeticPresets.Count > 0 || store.colorPresets.Count > 0)) { Dictionary dictionary = ToLegacyEntryMap(store.cosmeticPresets); Dictionary dictionary2 = ToLegacyEntryMap(store.colorPresets); Dictionary dictionary3 = new Dictionary(); foreach (int key in dictionary.Keys) { List cosmetics = ((dictionary[key] != null) ? dictionary[key].values : new List()); List colors = ((dictionary2.ContainsKey(key) && dictionary2[key] != null) ? dictionary2[key].values : new List()); dictionary3[key] = new ExtendedPresetEntry(key, cosmetics, colors, DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ"), BuildPresetHash(cosmetics, colors)); } foreach (int key2 in dictionary2.Keys) { if (!dictionary3.ContainsKey(key2)) { List colors2 = ((dictionary2[key2] != null) ? dictionary2[key2].values : new List()); dictionary3[key2] = new ExtendedPresetEntry(key2, new List(), colors2, DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ"), BuildPresetHash(new List(), colors2)); } } store.presets = ToSortedEntryList(dictionary3); } Dictionary map = ToEntryMap(store.presets); store.presets = ToSortedEntryList(map); } private static Dictionary ToLegacyEntryMap(List entries) { Dictionary dictionary = new Dictionary(); if (entries == null) { return dictionary; } foreach (LegacyPresetEntry entry in entries) { if (entry != null && entry.index >= 28) { dictionary[entry.index] = new LegacyPresetEntry(entry.index, entry.values); } } return dictionary; } private static Dictionary ToEntryMap(List entries) { Dictionary dictionary = new Dictionary(); if (entries == null) { return dictionary; } foreach (ExtendedPresetEntry entry in entries) { if (entry != null && entry.index >= 28) { dictionary[entry.index] = new ExtendedPresetEntry(entry.index, entry.cosmetics, entry.colors, entry.updatedAtUtc, entry.hash); } } return dictionary; } private static List ToSortedEntryList(Dictionary map) { List list = new List(); if (map == null) { return list; } List list2 = new List(map.Keys); list2.Sort(); foreach (int item in list2) { ExtendedPresetEntry extendedPresetEntry = map[item]; if (extendedPresetEntry != null && item >= 28) { if (string.IsNullOrEmpty(extendedPresetEntry.hash)) { extendedPresetEntry.hash = BuildPresetHash(extendedPresetEntry.cosmetics, extendedPresetEntry.colors); } if (string.IsNullOrEmpty(extendedPresetEntry.updatedAtUtc)) { extendedPresetEntry.updatedAtUtc = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ"); } list.Add(new ExtendedPresetEntry(item, extendedPresetEntry.cosmetics, extendedPresetEntry.colors, extendedPresetEntry.updatedAtUtc, extendedPresetEntry.hash)); } } return list; } private static string BuildPresetHash(List cosmetics, List colors) { StringBuilder stringBuilder = new StringBuilder(); if (cosmetics != null) { for (int i = 0; i < cosmetics.Count; i++) { if (i > 0) { stringBuilder.Append(','); } stringBuilder.Append(cosmetics[i].ToString()); } } stringBuilder.Append('|'); if (colors != null) { for (int j = 0; j < colors.Count; j++) { if (j > 0) { stringBuilder.Append(','); } stringBuilder.Append(colors[j].ToString()); } } return stringBuilder.ToString(); } private static void DeletePresetIcon(int presetIndex) { try { if (presetIndex < 0 || (Object)(object)MetaManager.instance == (Object)null) { return; } FieldInfo fieldInfo = AccessTools.Field(typeof(MetaManager), "presetCacheFolder"); string text = "CosmeticsPresets"; if (fieldInfo != null) { object value = fieldInfo.GetValue(MetaManager.instance); if (value is string && !string.IsNullOrEmpty((string)value)) { text = (string)value; } } string path = Path.Combine(Application.persistentDataPath, "Cache", "Icons", text, presetIndex.ToString().ToLowerInvariant() + ".png"); if (File.Exists(path)) { File.Delete(path); } } catch (Exception ex) { WriteFailureLog("DeletePresetIcon", ex); } } private static void RefreshPresetGridLayout(MenuPageCosmetics menuPageCosmetics) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Invalid comparison between Unknown and I4 //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) if (!IsEnabled() || (Object)(object)menuPageCosmetics == (Object)null) { return; } try { if ((int)menuPageCosmetics.selectedTab != 1 || (Object)(object)menuPageCosmetics.sectionRootTransform == (Object)null) { return; } int targetSlots = GetTargetSlots(); int num = Mathf.Max(4, Mathf.CeilToInt((float)targetSlots / 7f)); MenuElementCosmeticSection[] componentsInChildren = ((Component)menuPageCosmetics.sectionRootTransform).GetComponentsInChildren(true); MenuElementCosmeticSection[] array = componentsInChildren; foreach (MenuElementCosmeticSection val in array) { if ((Object)(object)val == (Object)null || (Object)(object)val.cosmeticListTransform == (Object)null) { continue; } MenuElementCosmeticPreset componentInChildren = ((Component)val.cosmeticListTransform).GetComponentInChildren(true); if ((Object)(object)componentInChildren == (Object)null) { continue; } GridLayoutGroup component = ((Component)val.cosmeticListTransform).GetComponent(); if (!((Object)(object)component == (Object)null)) { component.constraint = (Constraint)1; component.constraintCount = 7; component.cellSize = new Vector2(60f, 140f); float num2 = component.cellSize.y * (float)num + component.spacing.y * (float)Mathf.Max(0, num - 1) + (float)((LayoutGroup)component).padding.top + (float)((LayoutGroup)component).padding.bottom; RectTransform component2 = ((Component)val.cosmeticListTransform).GetComponent(); if ((Object)(object)component2 != (Object)null) { component2.sizeDelta = new Vector2(component2.sizeDelta.x, num2); LayoutRebuilder.ForceRebuildLayoutImmediate(component2); } RectTransform component3 = ((Component)val).GetComponent(); if ((Object)(object)component3 != (Object)null) { component3.sizeDelta = new Vector2(component3.sizeDelta.x, num2); LayoutRebuilder.ForceRebuildLayoutImmediate(component3); } } } } catch (Exception ex) { WriteFailureLog("RefreshPresetGridLayout", ex); } } private static SaveState BuildSaveState(MetaManager manager, string reason) { if ((Object)(object)manager == (Object)null) { return null; } int targetSlots = GetTargetSlots(); List> presetList = GetPresetList(manager, cosmeticPresetsField); List> presetList2 = GetPresetList(manager, colorPresetsField); ExpandList(presetList, targetSlots); ExpandList(presetList2, targetSlots); NormalizeLists(manager, presetList, presetList2); SaveState saveState = new SaveState(); saveState.reason = reason; saveState.targetSlots = targetSlots; saveState.restoreCosmeticPresets = ClonePresetList(presetList, targetSlots); saveState.restoreColorPresets = ClonePresetList(presetList2, targetSlots); return saveState; } private static void PrepareVanillaOnlySave(MetaManager manager, SaveState state) { if (!((Object)(object)manager == (Object)null) && state != null) { List> value = CloneFirstSlots(state.restoreCosmeticPresets, 28); List> value2 = CloneFirstSlots(state.restoreColorPresets, 28); SetPresetList(manager, cosmeticPresetsField, value); SetPresetList(manager, colorPresetsField, value2); SetPresetSlotsField(manager, 28); } } private static void RestoreRuntimeAfterVanillaSave(MetaManager manager, SaveState state) { if (!((Object)(object)manager == (Object)null) && state != null) { SetPresetList(manager, cosmeticPresetsField, ClonePresetList(state.restoreCosmeticPresets, state.targetSlots)); SetPresetList(manager, colorPresetsField, ClonePresetList(state.restoreColorPresets, state.targetSlots)); SetPresetSlotsField(manager, state.targetSlots); EnsurePresetRuntimeSlots(manager, mergeExtendedStore: false, "RestoreRuntimeAfterVanillaSave / " + state.reason); } } private static void RestoreExtendedPresetsFromStoreNow(bool forceOverwrite, string reason) { try { if (!((Object)(object)MetaManager.instance == (Object)null)) { EnsurePresetRuntimeSlots(MetaManager.instance, mergeExtendedStore: false, reason + " / before_restore"); List> presetList = GetPresetList(MetaManager.instance, cosmeticPresetsField); List> presetList2 = GetPresetList(MetaManager.instance, colorPresetsField); MergeExtendedStoreIntoRuntime(MetaManager.instance, presetList, presetList2, forceOverwrite); NormalizeLists(MetaManager.instance, presetList, presetList2); WriteLog("Extended presets restored. Reason=" + reason); } } catch (Exception ex) { WriteFailureLog("RestoreExtendedPresetsFromStoreNow / " + reason, ex); } } private static void SaveExtendedPresetsNow(bool createHistory, string reason) { try { if (!((Object)(object)MetaManager.instance == (Object)null)) { EnsurePresetRuntimeSlots(MetaManager.instance, mergeExtendedStore: false, reason + " / before_backup"); List> presetList = GetPresetList(MetaManager.instance, cosmeticPresetsField); List> presetList2 = GetPresetList(MetaManager.instance, colorPresetsField); SaveExtendedStoreFromRuntime(MetaManager.instance, presetList, presetList2, GetTargetSlots(), createHistory, reason); WriteLog("Extended presets saved. Reason=" + reason); } } catch (Exception ex) { WriteFailureLog("SaveExtendedPresetsNow / " + reason, ex); } } private static void RegisterCommands(DebugCommandHandler handler) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Expected O, but got Unknown //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Expected O, but got Unknown if (!IsEnabled() || (Object)(object)handler == (Object)null || commandRegistered) { return; } try { handler.Register(new ChatCommand("cpeinfo", "Shows CosmeticPresetExtender save information", (Action)delegate { ExtendedPresetStore extendedPresetStore = LoadExtendedStore(allowBackupRecovery: true); NormalizeStore(extendedPresetStore); WriteLog("Rows=" + GetTargetRows() + " Slots=" + GetTargetSlots() + " VanillaSaveSlots=" + 28 + " ExtendedEntries=" + extendedPresetStore.presets.Count + " UpdatedAtUtc=" + extendedPresetStore.updatedAtUtc); }, (Func>)null, (Func)null, false)); handler.Register(new ChatCommand("cpebackup", "Saves extended cosmetic presets now", (Action)delegate { SaveExtendedPresetsNow(createHistory: true, "chat_cpebackup"); }, (Func>)null, (Func)null, false)); handler.Register(new ChatCommand("cperestore", "Restores extended cosmetic presets now", (Action)delegate { RestoreExtendedPresetsFromStoreNow(forceOverwrite: true, "chat_cperestore"); }, (Func>)null, (Func)null, false)); commandRegistered = true; WriteLog("Chat commands registered: /cpeinfo /cpebackup /cperestore"); } catch (Exception ex) { WriteFailureLog("RegisterCommands", ex); } } } }