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.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("RESTORE")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("RESTORE")] [assembly: AssemblyTitle("RESTORE")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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 RestoreMod { [BepInPlugin("com.asher.restore", "RESTORE", "0.4.4")] public sealed class RestorePlugin : BaseUnityPlugin { public const string PluginGuid = "com.asher.restore"; public const string PluginName = "RESTORE"; public const string PluginVersion = "0.4.4"; private Harmony harmony; private string modRoot; internal static RestorePlugin Instance { get; private set; } internal static ManualLogSource Log { get; private set; } internal RestoreBundleManager Bundles { get; private set; } private void Awake() { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; modRoot = ResolveModRoot(); Directory.CreateDirectory(modRoot); ((BaseUnityPlugin)this).Logger.LogInfo((object)"RESTORE 0.4.4 starting."); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Plugin DLL: " + ResolvePluginDllPath())); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Mod root: " + modRoot)); RestorePlayerData.Initialize(((BaseUnityPlugin)this).Logger); harmony = new Harmony("com.asher.restore"); RestoreQuestUiFix.Initialize(harmony, ((BaseUnityPlugin)this).Logger); Bundles = new RestoreBundleManager(modRoot, ((BaseUnityPlugin)this).Logger); Bundles.Load(); RestoreLocalization.Initialize(modRoot, harmony, ((BaseUnityPlugin)this).Logger); RestoreRoomLoader.Initialize(this, Bundles, harmony, ((BaseUnityPlugin)this).Logger); ((BaseUnityPlugin)this).Logger.LogInfo((object)"RESTORE startup complete."); } private string ResolveModRoot() { if (((BaseUnityPlugin)this).Info != null && !string.IsNullOrWhiteSpace(((BaseUnityPlugin)this).Info.Location)) { string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location); if (!string.IsNullOrWhiteSpace(directoryName)) { return Path.GetFullPath(directoryName); } } string location = Assembly.GetExecutingAssembly().Location; if (!string.IsNullOrWhiteSpace(location)) { string directoryName2 = Path.GetDirectoryName(location); if (!string.IsNullOrWhiteSpace(directoryName2)) { return Path.GetFullPath(directoryName2); } } return Path.GetFullPath(Paths.PluginPath); } private string ResolvePluginDllPath() { if (((BaseUnityPlugin)this).Info != null && !string.IsNullOrWhiteSpace(((BaseUnityPlugin)this).Info.Location)) { return ((BaseUnityPlugin)this).Info.Location; } return Assembly.GetExecutingAssembly().Location; } private void OnDestroy() { RestoreRoomLoader.Shutdown(); RestoreQuestUiFix.Shutdown(); RestoreLocalization.Shutdown(); RestorePlayerData.Shutdown(); if (harmony != null) { harmony.UnpatchSelf(); harmony = null; } if (Bundles != null) { Bundles.Unload(unloadLoadedObjects: false); Bundles = null; } Instance = null; Log = null; } public static T GetDataAsset(string assetName) where T : Object { if ((Object)(object)Instance == (Object)null || Instance.Bundles == null) { return default(T); } return Instance.Bundles.GetDataAsset(assetName); } public static T[] GetAllDataAssets() where T : Object { if ((Object)(object)Instance == (Object)null || Instance.Bundles == null) { return Array.Empty(); } return Instance.Bundles.GetAllDataAssets(); } public static void ReloadLocalization() { RestoreLocalization.Reload(); } } public sealed class RestoreBundleManager { private readonly string modRoot; private readonly ManualLogSource logger; private readonly Dictionary dataAssets = new Dictionary(StringComparer.OrdinalIgnoreCase); private readonly List overlayAssets = new List(); public AssetBundle DataBundle { get; private set; } public AssetBundle OverlayBundle { get; private set; } public RestoreBundleManager(string root, ManualLogSource log) { modRoot = root; logger = log; } public void Load() { Directory.CreateDirectory(modRoot); RestoreAudioResolver.Initialize(logger); DataBundle = LoadBundle("000_restore_data"); OverlayBundle = LoadBundle("restore_room_overlays"); RestoreAudioResolver.RegisterBundle(DataBundle, "000_restore_data"); RestoreAudioResolver.RegisterBundle(OverlayBundle, "restore_room_overlays"); RestoreAudioResolver.RefreshLoadedGameClips(); IndexDataAssets(); IndexOverlayAssets(); } public T GetDataAsset(string assetName) where T : Object { if ((Object)(object)DataBundle == (Object)null || string.IsNullOrWhiteSpace(assetName)) { return default(T); } if (!dataAssets.TryGetValue(NormalizeAssetName(assetName), out var value)) { logger.LogWarning((object)("Data asset not found: " + assetName)); return default(T); } T val = DataBundle.LoadAsset(value); if ((Object)(object)val == (Object)null) { logger.LogWarning((object)("Data asset exists but could not be loaded as " + typeof(T).FullName + ": " + value)); return default(T); } RestoreAudioResolver.RegisterBundledAsset((Object)(object)val); return RestoreAudioResolver.RepairAsset(val); } public T[] GetAllDataAssets() where T : Object { if ((Object)(object)DataBundle == (Object)null) { return Array.Empty(); } T[] array = DataBundle.LoadAllAssets(); for (int i = 0; i < array.Length; i++) { RestoreAudioResolver.RegisterBundledAsset((Object)(object)array[i]); array[i] = RestoreAudioResolver.RepairAsset(array[i]); } return array; } public List LoadOverlayPrefabs(string sceneName) { List list = new List(); if ((Object)(object)OverlayBundle == (Object)null || string.IsNullOrWhiteSpace(sceneName)) { return list; } string sceneName2 = NormalizeAssetName(sceneName); for (int i = 0; i < overlayAssets.Count; i++) { string text = overlayAssets[i]; if (MatchesScene(NormalizeAssetName(text), sceneName2)) { GameObject val = OverlayBundle.LoadAsset(text); if ((Object)(object)val == (Object)null) { logger.LogWarning((object)("Could not load overlay prefab: " + text)); continue; } RestoreAudioResolver.RegisterBundledAsset((Object)(object)val); RestoreAudioResolver.RepairGameObject(val); list.Add(val); logger.LogInfo((object)("Matched overlay prefab '" + ((Object)val).name + "' to scene '" + sceneName + "'.")); } } return list; } public void Unload(bool unloadLoadedObjects) { if ((Object)(object)OverlayBundle != (Object)null) { OverlayBundle.Unload(unloadLoadedObjects); OverlayBundle = null; } if ((Object)(object)DataBundle != (Object)null) { DataBundle.Unload(unloadLoadedObjects); DataBundle = null; } dataAssets.Clear(); overlayAssets.Clear(); } private AssetBundle LoadBundle(string fileName) { string text = Path.Combine(modRoot, fileName); string text2 = Path.Combine(modRoot, "Bundles", fileName); string text3 = null; if (File.Exists(text)) { text3 = text; } else if (File.Exists(text2)) { text3 = text2; } if (text3 == null) { logger.LogError((object)("Bundle not found. Checked:\n" + text + "\n" + text2)); return null; } logger.LogInfo((object)("Loading bundle from: " + text3)); AssetBundle val = AssetBundle.LoadFromFile(text3); if ((Object)(object)val == (Object)null) { logger.LogError((object)("Unity failed to load bundle: " + text3)); return null; } logger.LogInfo((object)("Loaded bundle: " + Path.GetFileName(text3))); return val; } private void IndexDataAssets() { dataAssets.Clear(); if ((Object)(object)DataBundle == (Object)null) { return; } string[] allAssetNames = DataBundle.GetAllAssetNames(); for (int i = 0; i < allAssetNames.Length; i++) { string key = NormalizeAssetName(allAssetNames[i]); if (!dataAssets.ContainsKey(key)) { dataAssets.Add(key, allAssetNames[i]); } } logger.LogInfo((object)("Indexed " + dataAssets.Count + " data asset(s).")); } private void IndexOverlayAssets() { overlayAssets.Clear(); if ((Object)(object)OverlayBundle == (Object)null) { return; } string[] allAssetNames = OverlayBundle.GetAllAssetNames(); for (int i = 0; i < allAssetNames.Length; i++) { if (allAssetNames[i].EndsWith(".prefab", StringComparison.OrdinalIgnoreCase)) { overlayAssets.Add(allAssetNames[i]); } } overlayAssets.Sort(StringComparer.OrdinalIgnoreCase); logger.LogInfo((object)("Indexed " + overlayAssets.Count + " overlay prefab(s).")); } private static bool MatchesScene(string assetName, string sceneName) { if (assetName.Equals(sceneName, StringComparison.OrdinalIgnoreCase)) { return true; } if (assetName.Equals(sceneName + "r", StringComparison.OrdinalIgnoreCase)) { return true; } if (assetName.StartsWith(sceneName + "__", StringComparison.OrdinalIgnoreCase)) { return true; } if (assetName.StartsWith(sceneName + "+", StringComparison.OrdinalIgnoreCase)) { return true; } return false; } private static string NormalizeAssetName(string value) { if (string.IsNullOrWhiteSpace(value)) { return string.Empty; } value = value.Replace('\\', '/'); return Path.GetFileNameWithoutExtension(Path.GetFileName(value)).Trim(); } } public static class RestoreRoomLoader { private sealed class UnpackedObjectState { public Transform Transform; public bool WasActive; } private static RestorePlugin plugin; private static RestoreBundleManager bundles; private static ManualLogSource logger; private static readonly HashSet spawnedSceneHandles = new HashSet(); private static bool initialized; public static void Initialize(RestorePlugin owner, RestoreBundleManager bundleManager, Harmony harmony, ManualLogSource log) { if (!initialized) { plugin = owner; bundles = bundleManager; logger = log; initialized = true; SceneManager.sceneLoaded += OnSceneLoaded; SceneManager.sceneUnloaded += OnSceneUnloaded; PatchSetupSceneRefs(harmony); } } public static void Shutdown() { if (initialized) { SceneManager.sceneLoaded -= OnSceneLoaded; SceneManager.sceneUnloaded -= OnSceneUnloaded; spawnedSceneHandles.Clear(); plugin = null; bundles = null; logger = null; initialized = false; } } private static void PatchSetupSceneRefs(Harmony harmony) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Expected O, but got Unknown Type type = AccessTools.TypeByName("GameManager"); if (type == null) { logger.LogWarning((object)"GameManager type not found. Room overlays will use the sceneLoaded fallback."); return; } MethodInfo methodInfo = AccessTools.Method(type, "SetupSceneRefs", (Type[])null, (Type[])null); if (methodInfo == null) { logger.LogWarning((object)"GameManager.SetupSceneRefs was not found. Room overlays will use the sceneLoaded fallback."); return; } MethodInfo methodInfo2 = AccessTools.Method(typeof(RestoreRoomLoader), "BeforeSetupSceneRefs", (Type[])null, (Type[])null); harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); logger.LogInfo((object)"Patched GameManager.SetupSceneRefs for pre-setup overlays."); } private static void BeforeSetupSceneRefs() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) SpawnForScene(SceneManager.GetActiveScene()); } private static void OnSceneLoaded(Scene scene, LoadSceneMode mode) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)plugin != (Object)null) { ((MonoBehaviour)plugin).StartCoroutine(SpawnFallback(scene)); } } private static IEnumerator SpawnFallback(Scene scene) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) yield return null; SpawnForScene(scene); } private static void OnSceneUnloaded(Scene scene) { spawnedSceneHandles.Remove(((Scene)(ref scene)).handle); } private static void SpawnForScene(Scene scene) { //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) if (!initialized || bundles == null || !((Scene)(ref scene)).IsValid() || !((Scene)(ref scene)).isLoaded || spawnedSceneHandles.Contains(((Scene)(ref scene)).handle)) { return; } List list = bundles.LoadOverlayPrefabs(((Scene)(ref scene)).name); if (list == null || list.Count == 0) { return; } spawnedSceneHandles.Add(((Scene)(ref scene)).handle); List list2 = new List(); for (int i = 0; i < list.Count; i++) { GameObject val = list[i]; if (!((Object)(object)val == (Object)null)) { GameObject val2 = InstantiateInactive(val); if ((Object)(object)val2 == (Object)null) { logger.LogWarning((object)("Failed to instantiate overlay prefab at index " + i + " for scene " + ((Scene)(ref scene)).name + ".")); } else { ((Object)val2).name = ((Object)val).name; SceneManager.MoveGameObjectToScene(val2, scene); list2.Add(val2); } } } for (int j = 0; j < list2.Count; j++) { GameObject val3 = list2[j]; if (!((Object)(object)val3 == (Object)null)) { ApplyDeleteMarkers(val3, scene); } } HashSet usedRootNames = CollectExistingRootNames(scene, list2); int num = 0; for (int k = 0; k < list2.Count; k++) { GameObject val4 = list2[k]; if (!((Object)(object)val4 == (Object)null)) { num += UnpackOverlayContents(val4, scene, usedRootNames); } } logger.LogInfo((object)("Loaded " + list2.Count + " overlay prefab container(s) and unpacked " + num + " root object(s) in " + ((Scene)(ref scene)).name + ".")); } private static GameObject InstantiateInactive(GameObject prefab) { if ((Object)(object)prefab == (Object)null) { return null; } bool activeSelf = prefab.activeSelf; try { prefab.SetActive(false); return Object.Instantiate(prefab); } finally { prefab.SetActive(activeSelf); } } private static void ApplyDeleteMarkers(GameObject overlayInstance, Scene scene) { //IL_00ae: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)overlayInstance == (Object)null) { return; } Transform[] componentsInChildren = overlayInstance.GetComponentsInChildren(true); List list = new List(); foreach (Transform val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && ((Object)val).name.Equals("DELETE", StringComparison.OrdinalIgnoreCase)) { list.Add(val); } } for (int j = 0; j < list.Count; j++) { Transform val2 = list[j]; if ((Object)(object)val2 == (Object)null) { continue; } List list2 = new List(); for (int k = 0; k < val2.childCount; k++) { Transform child = val2.GetChild(k); if (!((Object)(object)child == (Object)null)) { list2.Add(((Object)child).name); } } for (int l = 0; l < list2.Count; l++) { DeleteSceneTargets(scene, overlayInstance.transform, list2[l]); } } list.Sort((Transform left, Transform right) => GetTransformDepth(right).CompareTo(GetTransformDepth(left))); for (int num = 0; num < list.Count; num++) { Transform val3 = list[num]; if (!((Object)(object)val3 == (Object)null)) { Object.DestroyImmediate((Object)(object)((Component)val3).gameObject); } } } private static int GetTransformDepth(Transform transform) { int num = 0; Transform val = transform; while ((Object)(object)val != (Object)null) { num++; val = val.parent; } return num; } private static void DeleteSceneTargets(Scene scene, Transform overlayRoot, string targetNameOrPath) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrWhiteSpace(targetNameOrPath)) { return; } List list = FindSceneObjects(scene, targetNameOrPath); int num = 0; for (int i = 0; i < list.Count; i++) { GameObject val = list[i]; if (!((Object)(object)val == (Object)null) && (!((Object)(object)overlayRoot != (Object)null) || (!((Object)(object)val.transform == (Object)(object)overlayRoot) && !val.transform.IsChildOf(overlayRoot)))) { val.SetActive(false); Object.DestroyImmediate((Object)(object)val); num++; } } if (num == 0) { logger.LogWarning((object)("DELETE marker found no target in " + ((Scene)(ref scene)).name + ": " + targetNameOrPath)); } else { logger.LogInfo((object)("DELETE removed " + num + " object(s): " + targetNameOrPath)); } } private static int UnpackOverlayContents(GameObject overlayContainer, Scene scene, HashSet usedRootNames) { //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)overlayContainer == (Object)null) { return 0; } Transform transform = overlayContainer.transform; int childCount = transform.childCount; if (childCount == 0) { logger.LogWarning((object)("Overlay prefab container '" + ((Object)overlayContainer).name + "' contained no root children to unpack.")); Object.DestroyImmediate((Object)(object)overlayContainer); return 0; } List list = new List(childCount); for (int i = 0; i < childCount; i++) { Transform child = transform.GetChild(i); if (!((Object)(object)child == (Object)null)) { UnpackedObjectState unpackedObjectState = new UnpackedObjectState(); unpackedObjectState.Transform = child; unpackedObjectState.WasActive = ((Component)child).gameObject.activeSelf; list.Add(unpackedObjectState); } } for (int j = 0; j < list.Count; j++) { Transform transform2 = list[j].Transform; if (!((Object)(object)transform2 == (Object)null)) { ((Component)transform2).gameObject.SetActive(false); } } int num = 0; for (int k = 0; k < list.Count; k++) { Transform transform3 = list[k].Transform; if (!((Object)(object)transform3 == (Object)null)) { string name = ((Object)((Component)transform3).gameObject).name; string uniqueRootName = GetUniqueRootName(name, usedRootNames); ((Object)((Component)transform3).gameObject).name = uniqueRootName; transform3.SetParent((Transform)null, true); if (((Component)transform3).gameObject.scene != scene) { SceneManager.MoveGameObjectToScene(((Component)transform3).gameObject, scene); } usedRootNames.Add(uniqueRootName); num++; if (!uniqueRootName.Equals(name, StringComparison.Ordinal)) { logger.LogInfo((object)("Renamed unpacked overlay object '" + name + "' to '" + uniqueRootName + "' in " + ((Scene)(ref scene)).name + ".")); } } } Object.DestroyImmediate((Object)(object)overlayContainer); for (int l = 0; l < list.Count; l++) { Transform transform4 = list[l].Transform; if (!((Object)(object)transform4 == (Object)null)) { ((Component)transform4).gameObject.SetActive(list[l].WasActive); } } return num; } private static HashSet CollectExistingRootNames(Scene scene, List overlayContainers) { HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); HashSet hashSet2 = new HashSet(); if (overlayContainers != null) { for (int i = 0; i < overlayContainers.Count; i++) { GameObject val = overlayContainers[i]; if ((Object)(object)val != (Object)null) { hashSet2.Add(val); } } } GameObject[] rootGameObjects = ((Scene)(ref scene)).GetRootGameObjects(); foreach (GameObject val2 in rootGameObjects) { if (!((Object)(object)val2 == (Object)null) && !hashSet2.Contains(val2)) { hashSet.Add(((Object)val2).name); } } return hashSet; } private static string GetUniqueRootName(string requestedName, HashSet usedNames) { string text = (string.IsNullOrWhiteSpace(requestedName) ? "Overlay Object" : requestedName.Trim()); if (usedNames == null || !usedNames.Contains(text)) { return text; } int num = 1; string text2; while (true) { text2 = text + " " + num; if (!usedNames.Contains(text2)) { break; } num++; } return text2; } private static List FindSceneObjects(Scene scene, string nameOrPath) { List list = new List(); GameObject[] rootGameObjects = ((Scene)(ref scene)).GetRootGameObjects(); if (nameOrPath.IndexOf('/') >= 0) { string[] array = nameOrPath.Split('/'); foreach (GameObject val in rootGameObjects) { if ((Object)(object)val == (Object)null || !((Object)val).name.Equals(array[0], StringComparison.OrdinalIgnoreCase)) { continue; } Transform val2 = val.transform; for (int j = 1; j < array.Length; j++) { val2 = FindDirectChild(val2, array[j]); if ((Object)(object)val2 == (Object)null) { break; } } if ((Object)(object)val2 != (Object)null) { list.Add(((Component)val2).gameObject); } } return list; } foreach (GameObject val3 in rootGameObjects) { if ((Object)(object)val3 == (Object)null) { continue; } Transform[] componentsInChildren = val3.GetComponentsInChildren(true); foreach (Transform val4 in componentsInChildren) { if (!((Object)(object)val4 == (Object)null) && ((Object)val4).name.Equals(nameOrPath, StringComparison.OrdinalIgnoreCase)) { list.Add(((Component)val4).gameObject); } } } return list; } private static Transform FindDirectChild(Transform parent, string name) { if ((Object)(object)parent == (Object)null) { return null; } for (int i = 0; i < parent.childCount; i++) { Transform child = parent.GetChild(i); if (!((Object)(object)child == (Object)null) && ((Object)child).name.Equals(name, StringComparison.OrdinalIgnoreCase)) { return child; } } return null; } } public static class RestoreLocalization { private static readonly Dictionary>> localization = new Dictionary>>(StringComparer.OrdinalIgnoreCase); private static Type languageType; private static Type localisedTextCollectionType; private static Type localisedTextCollectionDataType; private static FieldInfo currentEntrySheetsField; private static MethodInfo currentLanguageMethod; private static MethodInfo sendMonoMessageMethod; private static FieldInfo collectionDataField; private static FieldInfo collectionIsInitialisedField; private static FieldInfo collectionCurrentTextsField; private static FieldInfo collectionCurrentProbabilitiesField; private static FieldInfo collectionPreviousIndexField; private static string localizationRoot; private static ManualLogSource logger; private static Harmony harmony; private static bool initialized; public static void Initialize(string modRoot, Harmony harmonyInstance, ManualLogSource log) { localizationRoot = Path.Combine(modRoot, "Localization"); harmony = harmonyInstance; logger = log; initialized = true; Directory.CreateDirectory(localizationRoot); ResolveRuntimeTypes(); if (languageType == null) { logger.LogError((object)"RESTORE could not find runtime type TeamCherry.Localization.Language."); ReloadFilesOnly(); return; } InstallLanguageSwitchPatch(); Reload(); logger.LogInfo((object)("RESTORE localization initialized using runtime type " + languageType.FullName + " from assembly " + languageType.Assembly.GetName().Name + ".")); } public static void Shutdown() { localization.Clear(); languageType = null; localisedTextCollectionType = null; localisedTextCollectionDataType = null; currentEntrySheetsField = null; currentLanguageMethod = null; sendMonoMessageMethod = null; collectionDataField = null; collectionIsInitialisedField = null; collectionCurrentTextsField = null; collectionCurrentProbabilitiesField = null; collectionPreviousIndexField = null; localizationRoot = null; logger = null; harmony = null; initialized = false; } public static void Reload() { ReloadFilesOnly(); if (!(languageType == null)) { InjectLocalizationForCurrentLanguage(); ResetLoadedLocalisedTextCollections(); } } private static void ResolveRuntimeTypes() { languageType = FindLoadedType("TeamCherry.Localization.Language"); localisedTextCollectionType = FindLoadedType("TeamCherry.Localization.LocalisedTextCollection"); localisedTextCollectionDataType = FindLoadedType("TeamCherry.Localization.LocalisedTextCollectionData"); if (languageType != null) { currentEntrySheetsField = AccessTools.Field(languageType, "_currentEntrySheets"); currentLanguageMethod = AccessTools.Method(languageType, "CurrentLanguage", Type.EmptyTypes, (Type[])null); sendMonoMessageMethod = AccessTools.Method(languageType, "SendMonoMessage", (Type[])null, (Type[])null); } if (localisedTextCollectionType != null) { collectionDataField = AccessTools.Field(localisedTextCollectionType, "data"); } if (localisedTextCollectionDataType != null) { collectionIsInitialisedField = AccessTools.Field(localisedTextCollectionDataType, "isInitialised"); collectionCurrentTextsField = AccessTools.Field(localisedTextCollectionDataType, "currentTexts"); collectionCurrentProbabilitiesField = AccessTools.Field(localisedTextCollectionDataType, "currentProbabilities"); collectionPreviousIndexField = AccessTools.Field(localisedTextCollectionDataType, "previousIndex"); } } private static Type FindLoadedType(string fullName) { Type type = AccessTools.TypeByName(fullName); if (type != null) { return type; } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { if (!(assembly == null)) { try { type = assembly.GetType(fullName, throwOnError: false); } catch { type = null; } if (type != null) { return type; } } } return null; } private static void ReloadFilesOnly() { localization.Clear(); if (string.IsNullOrEmpty(localizationRoot)) { return; } Directory.CreateDirectory(localizationRoot); string[] directories = Directory.GetDirectories(localizationRoot, "*", SearchOption.TopDirectoryOnly); Array.Sort(directories, (IComparer?)StringComparer.OrdinalIgnoreCase); int num = 0; int num2 = 0; foreach (string path in directories) { string fileName = Path.GetFileName(path); if (string.IsNullOrWhiteSpace(fileName)) { continue; } List list = new List(); list.AddRange(Directory.GetFiles(path, "*.yml", SearchOption.TopDirectoryOnly)); list.AddRange(Directory.GetFiles(path, "*.yaml", SearchOption.TopDirectoryOnly)); list.Sort(StringComparer.OrdinalIgnoreCase); for (int j = 0; j < list.Count; j++) { string text = list[j]; string text2 = NormalizeLanguageCode(Path.GetFileNameWithoutExtension(text)); try { Dictionary dictionary = ParseSimpleYaml(text); RegisterLocalization(text2, fileName, dictionary); num++; num2 += dictionary.Count; logger.LogInfo((object)("RESTORE localization loaded: " + fileName + "/" + text2 + Path.GetExtension(text) + " (" + dictionary.Count + " entries).")); } catch (Exception ex) { logger.LogError((object)("RESTORE failed to load localization file " + text + ":\n" + ex)); } } } logger.LogInfo((object)("RESTORE parsed " + num2 + " localization entries from " + num + " file(s).")); } private static void RegisterLocalization(string languageCode, string sheetName, Dictionary entries) { if (!localization.TryGetValue(languageCode, out var value)) { value = new Dictionary>(StringComparer.OrdinalIgnoreCase); localization.Add(languageCode, value); } if (!value.TryGetValue(sheetName, out var value2)) { value2 = new Dictionary(StringComparer.Ordinal); value.Add(sheetName, value2); } foreach (KeyValuePair entry in entries) { value2[entry.Key] = entry.Value; } } private static void InjectLocalizationForCurrentLanguage() { if (!initialized || languageType == null) { return; } if (currentEntrySheetsField == null) { logger.LogError((object)"RESTORE could not find runtime field Language._currentEntrySheets."); return; } if (currentLanguageMethod == null) { logger.LogError((object)"RESTORE could not find runtime method Language.CurrentLanguage()."); return; } string text; try { object obj = currentLanguageMethod.Invoke(null, null); text = NormalizeLanguageCode((obj != null) ? obj.ToString() : "EN"); } catch (Exception ex) { logger.LogError((object)("RESTORE could not read the current language:\n" + ex)); return; } Dictionary> dictionary = currentEntrySheetsField.GetValue(null) as Dictionary>; if (dictionary == null) { dictionary = new Dictionary>(); currentEntrySheetsField.SetValue(null, dictionary); } int createdSheets = 0; int appendedEntries = 0; int overriddenEntries = 0; if (!text.Equals("EN", StringComparison.OrdinalIgnoreCase)) { MergeLanguageIntoGameSheets("EN", dictionary, ref createdSheets, ref appendedEntries, ref overriddenEntries); } MergeLanguageIntoGameSheets(text, dictionary, ref createdSheets, ref appendedEntries, ref overriddenEntries); logger.LogInfo((object)("RESTORE localization injected for " + text + ": created " + createdSheets + " sheet(s), appended " + appendedEntries + " key(s), overrode " + overriddenEntries + " key(s).")); } private static void MergeLanguageIntoGameSheets(string languageCode, Dictionary> gameSheets, ref int createdSheets, ref int appendedEntries, ref int overriddenEntries) { if (!localization.TryGetValue(languageCode, out var value)) { return; } foreach (KeyValuePair> item in value) { if (!gameSheets.TryGetValue(item.Key, out var value2)) { value2 = new Dictionary(); gameSheets.Add(item.Key, value2); createdSheets++; logger.LogInfo((object)("RESTORE created localization sheet: " + item.Key + ".")); } foreach (KeyValuePair item2 in item.Value) { if (value2.ContainsKey(item2.Key)) { overriddenEntries++; } else { appendedEntries++; } value2[item2.Key] = item2.Value; } } } private static void InstallLanguageSwitchPatch() { //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Expected O, but got Unknown if (harmony == null || languageType == null) { logger.LogError((object)"RESTORE localization could not install language patches."); return; } MethodInfo methodInfo = AccessTools.Method(typeof(RestoreLocalization), "LanguageDoSwitchPostfix", (Type[])null, (Type[])null); MethodInfo[] methods = languageType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); int num = 0; foreach (MethodInfo methodInfo2 in methods) { if (methodInfo2.Name.Equals("DoSwitch", StringComparison.Ordinal)) { try { harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(methodInfo), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; logger.LogInfo((object)("RESTORE patched localization language switch: " + FormatMethod(methodInfo2) + ".")); } catch (Exception ex) { logger.LogError((object)("RESTORE failed to patch " + FormatMethod(methodInfo2) + ":\n" + ex)); } } } if (num == 0) { logger.LogError((object)"RESTORE could not find runtime Language.DoSwitch."); } } private static void LanguageDoSwitchPostfix() { InjectLocalizationForCurrentLanguage(); ResetLoadedLocalisedTextCollections(); BroadcastChangedLanguageAgain(); } private static void BroadcastChangedLanguageAgain() { if (sendMonoMessageMethod == null || currentLanguageMethod == null) { return; } try { object obj = currentLanguageMethod.Invoke(null, null); sendMonoMessageMethod.Invoke(null, new object[2] { "ChangedLanguage", new object[1] { obj } }); } catch (Exception ex) { logger.LogWarning((object)("RESTORE could not rebroadcast ChangedLanguage:\n" + ex)); } } private static void ResetLoadedLocalisedTextCollections() { if (localisedTextCollectionType == null || collectionDataField == null) { return; } Object[] array; try { array = Resources.FindObjectsOfTypeAll(localisedTextCollectionType); } catch (Exception ex) { logger.LogWarning((object)("RESTORE could not enumerate localization collections:\n" + ex)); return; } int num = 0; foreach (Object val in array) { if (val == (Object)null) { continue; } object value; try { value = collectionDataField.GetValue(val); } catch { continue; } if (value == null) { continue; } try { if (collectionIsInitialisedField != null) { collectionIsInitialisedField.SetValue(value, false); } if (collectionCurrentTextsField != null) { collectionCurrentTextsField.SetValue(value, null); } if (collectionCurrentProbabilitiesField != null) { collectionCurrentProbabilitiesField.SetValue(value, null); } if (collectionPreviousIndexField != null) { collectionPreviousIndexField.SetValue(value, -1); } num++; } catch (Exception ex2) { logger.LogWarning((object)("RESTORE could not reset localization collection '" + val.name + "': " + ex2.Message)); } } logger.LogInfo((object)("RESTORE reset " + num + " LocalisedTextCollection cache(s).")); } private static Dictionary ParseSimpleYaml(string filePath) { Dictionary dictionary = new Dictionary(StringComparer.Ordinal); string[] array = File.ReadAllLines(filePath, Encoding.UTF8); for (int i = 0; i < array.Length; i++) { string text = array[i]; if (string.IsNullOrWhiteSpace(text)) { continue; } string text2 = text.TrimStart(); if (text2.StartsWith("#", StringComparison.Ordinal) || text2.StartsWith("---", StringComparison.Ordinal) || text2.StartsWith("...", StringComparison.Ordinal)) { continue; } int num = FindYamlSeparator(text2); if (num <= 0) { logger.LogWarning((object)("RESTORE localization ignored malformed line " + (i + 1) + " in " + filePath + ": " + text)); continue; } string text3 = text2.Substring(0, num).Trim(); string text4 = text2.Substring(num + 1).Trim(); if (text3.Length != 0) { string value; switch (text4) { case "|": case "|-": case "|+": case ">": case ">-": case ">+": value = ReadBlockValue(array, ref i, CountIndent(text), text4[0] == '>'); break; default: value = DecodeScalar(text4); break; } dictionary[text3] = value; } } return dictionary; } private static int FindYamlSeparator(string value) { bool flag = false; bool flag2 = false; bool flag3 = false; for (int i = 0; i < value.Length; i++) { char c = value[i]; if (flag2 && c == '\\' && !flag3) { flag3 = true; continue; } if (c == '\'' && !flag2) { flag = !flag; } else if (c == '"' && !flag && !flag3) { flag2 = !flag2; } else if (c == ':' && !flag && !flag2) { return i; } flag3 = false; } return -1; } private static string ReadBlockValue(string[] lines, ref int lineIndex, int parentIndent, bool foldLines) { List list = new List(); int num = -1; while (lineIndex + 1 < lines.Length) { string text = lines[lineIndex + 1]; if (string.IsNullOrWhiteSpace(text)) { lineIndex++; list.Add(string.Empty); continue; } int num2 = CountIndent(text); if (num2 <= parentIndent) { break; } if (num < 0) { num = num2; } lineIndex++; string item = ((text.Length >= num) ? text.Substring(num) : string.Empty); list.Add(item); } if (!foldLines) { return string.Join("\n", list); } StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < list.Count; i++) { string text2 = list[i]; if (i > 0) { if (text2.Length == 0 || list[i - 1].Length == 0) { stringBuilder.Append('\n'); } else { stringBuilder.Append(' '); } } stringBuilder.Append(text2); } return stringBuilder.ToString(); } private static string DecodeScalar(string value) { if (value == null) { return string.Empty; } if (value.Length >= 2 && value[0] == '"' && value[value.Length - 1] == '"') { value = value.Substring(1, value.Length - 2); return value.Replace("\\\\", "\u0001").Replace("\\n", "\n").Replace("\\r", "\r") .Replace("\\t", "\t") .Replace("\\\"", "\"") .Replace("\u0001", "\\"); } if (value.Length >= 2 && value[0] == '\'' && value[value.Length - 1] == '\'') { return value.Substring(1, value.Length - 2).Replace("''", "'"); } return value; } private static int CountIndent(string value) { int i; for (i = 0; i < value.Length && (value[i] == ' ' || value[i] == '\t'); i++) { } return i; } private static string NormalizeLanguageCode(string value) { if (string.IsNullOrWhiteSpace(value)) { return "EN"; } string text = value.Trim().Replace('-', '_').ToUpperInvariant(); return text switch { "ENGLISH" => "EN", "SPANISH" => "ES", "FRENCH" => "FR", "GERMAN" => "DE", "ITALIAN" => "IT", "PORTUGUESE" => "PT", "RUSSIAN" => "RU", "JAPANESE" => "JA", "KOREAN" => "KO", "CHINESE" => "ZH", _ => text, }; } private static string FormatMethod(MethodInfo method) { ParameterInfo[] parameters = method.GetParameters(); string[] array = new string[parameters.Length]; for (int i = 0; i < parameters.Length; i++) { array[i] = parameters[i].ParameterType.Name; } return method.DeclaringType.FullName + "." + method.Name + "(" + string.Join(", ", array) + ")"; } } public static class RestoreAudioResolver { private sealed class ReferenceComparer : IEqualityComparer { public static readonly ReferenceComparer Instance = new ReferenceComparer(); public new bool Equals(object left, object right) { return left == right; } public int GetHashCode(object value) { return RuntimeHelpers.GetHashCode(value); } } private static readonly Dictionary> gameClips = new Dictionary>(StringComparer.OrdinalIgnoreCase); private static readonly HashSet bundledClipIds = new HashSet(); private static readonly HashSet repairedObjectIds = new HashSet(); private static readonly HashSet loggedResolutions = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly HashSet loggedMisses = new HashSet(StringComparer.OrdinalIgnoreCase); private static ManualLogSource logger; private static float lastRefreshTime = -1000f; public static void Initialize(ManualLogSource log) { logger = log; gameClips.Clear(); bundledClipIds.Clear(); repairedObjectIds.Clear(); loggedResolutions.Clear(); loggedMisses.Clear(); lastRefreshTime = -1000f; if (logger != null) { logger.LogInfo((object)"Audio resolver initialized. Waiting for RESTORE bundles to be registered before scanning game audio."); } } public static bool IsBundledClip(AudioClip clip) { if ((Object)(object)clip != (Object)null) { return bundledClipIds.Contains(((Object)clip).GetInstanceID()); } return false; } public static void RegisterBundle(AssetBundle bundle, string bundleLabel) { if ((Object)(object)bundle == (Object)null) { return; } int count = bundledClipIds.Count; try { AudioClip[] array = bundle.LoadAllAssets(); foreach (AudioClip val in array) { if ((Object)(object)val != (Object)null) { bundledClipIds.Add(((Object)val).GetInstanceID()); } } } catch (Exception ex) { if (logger != null) { logger.LogWarning((object)("Could not enumerate AudioClips directly from bundle '" + bundleLabel + "': " + ex)); } } try { Object[] array2 = bundle.LoadAllAssets(); for (int j = 0; j < array2.Length; j++) { RegisterBundledAsset(array2[j]); } } catch (Exception ex2) { if (logger != null) { logger.LogWarning((object)("Could not inspect all assets in bundle '" + bundleLabel + "': " + ex2)); } } if (logger != null) { logger.LogInfo((object)("Registered " + (bundledClipIds.Count - count) + " RESTORE AudioClip object(s) from bundle '" + bundleLabel + "'.")); } } public static void RegisterBundledAsset(Object asset) { if (!(asset == (Object)null)) { HashSet visited = new HashSet(ReferenceComparer.Instance); CollectBundledAudioClips(asset, visited, 0); } } public static void RefreshLoadedGameClips() { gameClips.Clear(); lastRefreshTime = Time.realtimeSinceStartup; AudioClip[] array = Resources.FindObjectsOfTypeAll(); int num = 0; int num2 = 0; foreach (AudioClip val in array) { if ((Object)(object)val == (Object)null) { continue; } if (bundledClipIds.Contains(((Object)val).GetInstanceID())) { num2++; continue; } string text = NormalizeName(((Object)val).name); if (text.Length != 0) { if (!gameClips.TryGetValue(text, out var value)) { value = new List(); gameClips.Add(text, value); } value.Add(val); num++; } } if (logger != null) { logger.LogDebug((object)("Indexed " + num + " loaded original-game AudioClip object(s); excluded " + num2 + " registered RESTORE clip object(s).")); } } public static T RepairAsset(T asset) where T : Object { if ((Object)(object)asset == (Object)null) { return default(T); } RegisterBundledAsset((Object)(object)asset); int instanceID = ((Object)asset).GetInstanceID(); if (!repairedObjectIds.Add(instanceID)) { return asset; } object obj = asset; AudioClip val = (AudioClip)((obj is AudioClip) ? obj : null); if ((Object)(object)val != (Object)null) { AudioClip obj2 = ResolveClip(val); return (T)(object)((obj2 is T) ? obj2 : null); } RepairObjectFields(asset, new HashSet(ReferenceComparer.Instance), 0); return asset; } public static void RepairGameObject(GameObject root) { if ((Object)(object)root == (Object)null) { return; } RegisterBundledAsset((Object)(object)root); int instanceID = ((Object)root).GetInstanceID(); if (!repairedObjectIds.Add(instanceID)) { return; } Component[] componentsInChildren = root.GetComponentsInChildren(true); foreach (Component val in componentsInChildren) { if (!((Object)(object)val == (Object)null)) { AudioSource val2 = (AudioSource)(object)((val is AudioSource) ? val : null); if ((Object)(object)val2 != (Object)null && (Object)(object)val2.clip != (Object)null) { val2.clip = ResolveClip(val2.clip); } RepairObjectFields(val, new HashSet(ReferenceComparer.Instance), 0); } } } public static AudioClip ResolveClip(AudioClip fallback) { if ((Object)(object)fallback == (Object)null) { return null; } if (!bundledClipIds.Contains(((Object)fallback).GetInstanceID())) { return fallback; } string text = NormalizeName(((Object)fallback).name); if (text.Length == 0) { return fallback; } AudioClip val = FindBestMatch(text, fallback); if ((Object)(object)val == (Object)null && Time.realtimeSinceStartup - lastRefreshTime >= 0.5f) { RefreshLoadedGameClips(); val = FindBestMatch(text, fallback); } if ((Object)(object)val == (Object)null) { if (loggedMisses.Add(text) && logger != null) { logger.LogDebug((object)("No loaded original-game match for RESTORE AudioClip '" + ((Object)fallback).name + "'. Using bundled fallback.")); } return fallback; } string item = ((Object)fallback).GetInstanceID() + "->" + ((Object)val).GetInstanceID(); if (loggedResolutions.Add(item) && logger != null) { logger.LogInfo((object)("Using original game AudioClip '" + ((Object)val).name + "' instead of bundled fallback '" + ((Object)fallback).name + "'. " + DescribeClip(val))); } return val; } private static AudioClip FindBestMatch(string key, AudioClip fallback) { if (!gameClips.TryGetValue(key, out var value)) { return null; } AudioClip result = null; int num = int.MinValue; for (int i = 0; i < value.Count; i++) { AudioClip val = value[i]; if (!((Object)(object)val == (Object)null) && ((Object)val).GetInstanceID() != ((Object)fallback).GetInstanceID() && !bundledClipIds.Contains(((Object)val).GetInstanceID())) { int num2 = ScoreCandidate(val, fallback); if (num2 > num) { num = num2; result = val; } } } if (num < 20) { return null; } return result; } private static int ScoreCandidate(AudioClip candidate, AudioClip fallback) { int num = 0; if (candidate.channels == fallback.channels) { num += 8; } if (candidate.frequency == fallback.frequency) { num += 8; } if (candidate.samples == fallback.samples) { num += 12; } else { int num2 = Math.Abs(candidate.samples - fallback.samples); int num3 = Math.Max(64, fallback.frequency / 20); if (num2 <= num3) { num += 6; } } float num4 = Mathf.Abs(candidate.length - fallback.length); if (num4 <= 0.01f) { num += 8; } else if (num4 <= 0.05f) { num += 5; } else if (num4 <= 0.2f) { num += 2; } return num; } private static void CollectBundledAudioClips(object value, HashSet visited, int depth) { if (value == null || depth > 8 || !visited.Add(value)) { return; } AudioClip val = (AudioClip)((value is AudioClip) ? value : null); if ((Object)(object)val != (Object)null) { bundledClipIds.Add(((Object)val).GetInstanceID()); return; } GameObject val2 = (GameObject)((value is GameObject) ? value : null); if ((Object)(object)val2 != (Object)null) { Component[] componentsInChildren = val2.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren.Length; i++) { if ((Object)(object)componentsInChildren[i] != (Object)null) { CollectBundledAudioClips(componentsInChildren[i], visited, depth + 1); } } return; } Type type = value.GetType(); if (IsTerminalType(type)) { return; } if (value is IList list) { for (int j = 0; j < list.Count; j++) { CollectBundledAudioClips(list[j], visited, depth + 1); } } else { if (value is Object && !(value is ScriptableObject) && !(value is Component)) { return; } foreach (FieldInfo relevantField in GetRelevantFields(type)) { try { CollectBundledAudioClips(relevantField.GetValue(value), visited, depth + 1); } catch { } } } } private static void RepairObjectFields(object value, HashSet visited, int depth) { if (value == null || depth > 8 || !visited.Add(value)) { return; } Type type = value.GetType(); if (IsTerminalType(type)) { return; } if (value is IList list) { RepairList(list, visited, depth); return; } foreach (FieldInfo relevantField in GetRelevantFields(type)) { object value2; try { value2 = relevantField.GetValue(value); } catch { continue; } if (value2 == null) { continue; } if (relevantField.FieldType == typeof(AudioClip)) { AudioClip val = (AudioClip)((value2 is AudioClip) ? value2 : null); AudioClip val2 = ResolveClip(val); if ((Object)(object)val2 != (Object)(object)val && !relevantField.IsInitOnly) { try { relevantField.SetValue(value, val2); } catch { } } } else if (value2 is AudioClip[] array) { for (int i = 0; i < array.Length; i++) { array[i] = ResolveClip(array[i]); } } else if (value2 is IList list2) { RepairList(list2, visited, depth + 1); } else if (!(value2 is Object) || value2 is ScriptableObject || value2 is Component) { RepairObjectFields(value2, visited, depth + 1); } } } private static void RepairList(IList list, HashSet visited, int depth) { for (int i = 0; i < list.Count; i++) { object obj = list[i]; if (obj == null) { continue; } AudioClip val = (AudioClip)((obj is AudioClip) ? obj : null); if ((Object)(object)val != (Object)null) { AudioClip val2 = ResolveClip(val); if ((Object)(object)val2 != (Object)(object)val && !list.IsReadOnly) { try { list[i] = val2; } catch { } } } else { RepairObjectFields(obj, visited, depth + 1); } } } private static IEnumerable GetRelevantFields(Type type) { Type current = type; while (current != null && current != typeof(object) && current != typeof(Object) && current != typeof(ScriptableObject) && current != typeof(Component) && current != typeof(Behaviour) && current != typeof(MonoBehaviour)) { FieldInfo[] fields = current.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (!fieldInfo.IsStatic && !fieldInfo.IsLiteral && !fieldInfo.IsNotSerialized) { yield return fieldInfo; } } current = current.BaseType; } } private static bool IsTerminalType(Type type) { if (!type.IsPrimitive && !type.IsEnum && !(type == typeof(string)) && !(type == typeof(decimal)) && !(type == typeof(Type)) && !typeof(Delegate).IsAssignableFrom(type)) { return typeof(MemberInfo).IsAssignableFrom(type); } return true; } private static string NormalizeName(string value) { if (string.IsNullOrWhiteSpace(value)) { return string.Empty; } value = value.Replace('\\', '/'); string text = Path.GetFileNameWithoutExtension(Path.GetFileName(value)); if (text.EndsWith("(Clone)", StringComparison.OrdinalIgnoreCase)) { text = text.Substring(0, text.Length - "(Clone)".Length); } return text.Trim(); } private static string DescribeClip(AudioClip clip) { if ((Object)(object)clip == (Object)null) { return string.Empty; } return "[" + clip.channels + "ch, " + clip.frequency + "Hz, " + clip.samples + " samples, " + clip.length.ToString("0.000") + "s]"; } } public static class RestorePlayerData { private static ManualLogSource logger; public static void Initialize(ManualLogSource log) { logger = log; PlayerData.ClearOptimisers(); int num = 0; FieldInfo[] fields = typeof(PlayerData).GetFields(BindingFlags.Instance | BindingFlags.Public); foreach (FieldInfo fieldInfo in fields) { if (!(fieldInfo == null) && IsSupportedType(fieldInfo.FieldType) && fieldInfo.Name.StartsWith("restore", StringComparison.OrdinalIgnoreCase)) { num++; logger.LogInfo((object)("RESTORE found real PlayerData field: " + fieldInfo.FieldType.Name + " " + fieldInfo.Name + ".")); } } logger.LogInfo((object)("RESTORE real PlayerData initialized with " + num + " injected restore field(s).")); } public static void Shutdown() { logger = null; } public static void RegisterBool(string variableName, bool defaultValue = false) { RequireField(variableName, typeof(bool), defaultValue); } public static void RegisterInt(string variableName, int defaultValue = 0) { RequireField(variableName, typeof(int), defaultValue); } public static void RegisterFloat(string variableName, float defaultValue = 0f) { RequireField(variableName, typeof(float), defaultValue); } public static void RegisterString(string variableName, string defaultValue = "") { RequireField(variableName, typeof(string), defaultValue ?? string.Empty); } public static void Register(string variableName, T defaultValue = default(T)) { RequireField(variableName, typeof(T), defaultValue); } public static bool IsRegistered(string variableName) { return GetField(variableName) != null; } public static Type GetRegisteredType(string variableName) { FieldInfo field = GetField(variableName); if (!(field != null)) { return null; } return field.FieldType; } public static bool GetBool(string variableName) { RequireField(variableName, typeof(bool), false); return PlayerData.instance.GetBool(variableName); } public static void SetBool(string variableName, bool value) { RequireField(variableName, typeof(bool), false); PlayerData.instance.SetBool(variableName, value); } public static int GetInt(string variableName) { RequireField(variableName, typeof(int), 0); return PlayerData.instance.GetInt(variableName); } public static void SetInt(string variableName, int value) { RequireField(variableName, typeof(int), 0); PlayerData.instance.SetInt(variableName, value); } public static float GetFloat(string variableName) { RequireField(variableName, typeof(float), 0f); return PlayerData.instance.GetFloat(variableName); } public static void SetFloat(string variableName, float value) { RequireField(variableName, typeof(float), 0f); PlayerData.instance.SetFloat(variableName, value); } public static string GetString(string variableName) { RequireField(variableName, typeof(string), string.Empty); return PlayerData.instance.GetString(variableName); } public static void SetString(string variableName, string value) { RequireField(variableName, typeof(string), string.Empty); PlayerData.instance.SetString(variableName, value ?? string.Empty); } public static T Get(string variableName) { Type typeFromHandle = typeof(T); if (typeFromHandle == typeof(bool)) { return (T)(object)GetBool(variableName); } if (typeFromHandle == typeof(int)) { return (T)(object)GetInt(variableName); } if (typeFromHandle == typeof(float)) { return (T)(object)GetFloat(variableName); } if (typeFromHandle == typeof(string)) { return (T)(object)GetString(variableName); } throw new NotSupportedException("RESTORE real PlayerData supports bool, int, float, and string."); } public static void Set(string variableName, T value) { Type typeFromHandle = typeof(T); if (typeFromHandle == typeof(bool)) { SetBool(variableName, (bool)(object)value); return; } if (typeFromHandle == typeof(int)) { SetInt(variableName, (int)(object)value); return; } if (typeFromHandle == typeof(float)) { SetFloat(variableName, (float)(object)value); return; } if (typeFromHandle == typeof(string)) { SetString(variableName, (string)(object)value); return; } throw new NotSupportedException("RESTORE real PlayerData supports bool, int, float, and string."); } private static FieldInfo RequireField(string variableName, Type expectedType, object expectedDefault) { if (string.IsNullOrWhiteSpace(variableName)) { throw new ArgumentException("PlayerData field name cannot be empty.", "variableName"); } FieldInfo field = GetField(variableName); if (field == null) { throw new MissingFieldException("PlayerData", variableName + ". Add it to restore_playerdata_fields.txt, place RESTORE.PlayerDataPatcher.dll in BepInEx/patchers, and restart the game."); } if (field.FieldType != expectedType) { throw new InvalidOperationException("PlayerData field '" + variableName + "' exists as " + field.FieldType.FullName + ", not " + expectedType.FullName + "."); } return field; } private static FieldInfo GetField(string variableName) { if (string.IsNullOrWhiteSpace(variableName)) { return null; } return typeof(PlayerData).GetField(variableName, BindingFlags.Instance | BindingFlags.Public); } private static bool IsSupportedType(Type type) { if (!(type == typeof(bool)) && !(type == typeof(int)) && !(type == typeof(float))) { return type == typeof(string); } return true; } } internal static class RestoreQuestUiFix { private static ManualLogSource logger; private static Type inventoryPaneStandaloneType; private static MethodInfo paneStartMethod; private static MethodInfo paneEndMethod; private static bool initialized; internal static void Initialize(Harmony harmony, ManualLogSource log) { //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Expected O, but got Unknown //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Expected O, but got Unknown logger = log; if (initialized) { return; } initialized = true; inventoryPaneStandaloneType = AccessTools.TypeByName("InventoryPaneStandalone"); if (inventoryPaneStandaloneType == null) { logger.LogError((object)"RESTORE quest UI fix could not find InventoryPaneStandalone."); return; } paneStartMethod = AccessTools.Method(inventoryPaneStandaloneType, "PaneStart", Type.EmptyTypes, (Type[])null); paneEndMethod = AccessTools.Method(inventoryPaneStandaloneType, "PaneEnd", Type.EmptyTypes, (Type[])null); MethodInfo methodInfo = AccessTools.Method(typeof(RestoreQuestUiFix), "PaneAnimationPrefix", (Type[])null, (Type[])null); if (methodInfo == null) { logger.LogError((object)"RESTORE quest UI fix could not resolve its pane-animation prefix."); return; } int num = 0; if (paneStartMethod != null) { harmony.Patch((MethodBase)paneStartMethod, new HarmonyMethod(methodInfo), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; } else { logger.LogError((object)"RESTORE quest UI fix could not find InventoryPaneStandalone.PaneStart()."); } if (paneEndMethod != null) { harmony.Patch((MethodBase)paneEndMethod, new HarmonyMethod(methodInfo), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; } else { logger.LogError((object)"RESTORE quest UI fix could not find InventoryPaneStandalone.PaneEnd()."); } logger.LogInfo((object)("RESTORE patched " + num + " InventoryPaneStandalone animation method(s) to reactivate inactive pane hierarchies.")); } internal static void Shutdown() { logger = null; inventoryPaneStandaloneType = null; paneStartMethod = null; paneEndMethod = null; initialized = false; } private static void PaneAnimationPrefix(object __instance, MethodBase __originalMethod) { if (__instance == null) { return; } Component val = (Component)((__instance is Component) ? __instance : null); if ((Object)(object)val == (Object)null) { return; } GameObject gameObject = val.gameObject; if ((Object)(object)gameObject == (Object)null || gameObject.activeInHierarchy) { return; } string text = ((__originalMethod != null) ? __originalMethod.Name : "pane animation"); try { int num = ActivateRequiredHierarchy(val.transform); if (gameObject.activeInHierarchy) { logger.LogWarning((object)("RESTORE activated " + num + " inactive GameObject(s) before " + ((object)val).GetType().FullName + "." + text + "() on '" + ((Object)gameObject).name + "'.")); } else { logger.LogError((object)("RESTORE tried to reactivate pane '" + ((Object)gameObject).name + "' before " + text + "(), but it is still inactive in hierarchy.")); } } catch (Exception ex) { logger.LogError((object)("RESTORE failed to reactivate pane '" + ((Object)gameObject).name + "' before " + text + "():\n" + ex)); } } private static int ActivateRequiredHierarchy(Transform paneTransform) { if ((Object)(object)paneTransform == (Object)null) { return 0; } List list = new List(); Transform val = paneTransform; while ((Object)(object)val != (Object)null) { GameObject gameObject = ((Component)val).gameObject; if ((Object)(object)gameObject != (Object)null && !gameObject.activeSelf) { list.Add(gameObject); } val = val.parent; } int num = 0; for (int num2 = list.Count - 1; num2 >= 0; num2--) { GameObject val2 = list[num2]; if (!((Object)(object)val2 == (Object)null) && !val2.activeSelf) { val2.SetActive(true); num++; } } return num; } } }