using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using REPOLib.Modules; using REPOLib.Objects.Sdk; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: AssemblyCompany("RepoOldModRescue")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("RepoOldModRescue")] [assembly: AssemblyTitle("RepoOldModRescue")] [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 RepoOldModRescue { [BepInPlugin("themorningstar.repooldmodrescue", "REPO Old-Mod Rescue", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public const string Guid = "themorningstar.repooldmodrescue"; internal static ManualLogSource Log = null; internal static readonly Dictionary> LevelMap = new Dictionary>(); private void Awake() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Expected O, but got Unknown //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Expected O, but got Unknown //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Expected O, but got Unknown //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Expected O, but got Unknown //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Expected O, but got Unknown //IL_0242: Unknown result type (might be due to invalid IL or missing references) //IL_024f: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; LoadLevelMap(); try { Harmony val = new Harmony("themorningstar.repooldmodrescue"); MethodInfo methodInfo = AccessTools.Method(typeof(Enemies), "RegisterEnemies", (Type[])null, (Type[])null); if (methodInfo != null) { val.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(EnemyRescue).GetMethod("Prefix")), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } else { Log.LogWarning((object)"RepoOldModRescue: Enemies.RegisterEnemies not found; enemy rescue disabled."); } MethodInfo methodInfo2 = AccessTools.Method(typeof(Valuables), "RegisterInitialValuables", (Type[])null, (Type[])null); if (methodInfo2 != null) { val.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(ValuableRescue).GetMethod("Prefix")), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } else { Log.LogWarning((object)"RepoOldModRescue: Valuables.RegisterInitialValuables not found; valuable rescue disabled."); } MethodInfo methodInfo3 = AccessTools.Method(typeof(Levels), "RegisterLevel", new Type[1] { typeof(LevelContent) }, (Type[])null); if (methodInfo3 != null) { val.Patch((MethodBase)methodInfo3, new HarmonyMethod(typeof(LevelRescue).GetMethod("Prefix")), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } else { Log.LogWarning((object)"RepoOldModRescue: Levels.RegisterLevel(LevelContent) not found; level rescue disabled."); } MethodInfo methodInfo4 = AccessTools.Method(typeof(ValuableObject), "DollarValueSetLogic", (Type[])null, (Type[])null); if (methodInfo4 != null) { val.Patch((MethodBase)methodInfo4, new HarmonyMethod(typeof(MutatorFix).GetMethod("EnsureDurability")), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } MethodInfo methodInfo5 = AccessTools.Method(AccessTools.TypeByName("Map"), "AddDoor", (Type[])null, (Type[])null); if (methodInfo5 != null) { val.Patch((MethodBase)methodInfo5, new HarmonyMethod(typeof(MapDoorGuard).GetMethod("Prefix")), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } MethodInfo methodInfo6 = AccessTools.Method(typeof(RunManager), "Awake", (Type[])null, (Type[])null); if (methodInfo6 != null) { val.Patch((MethodBase)methodInfo6, (HarmonyMethod)null, new HarmonyMethod(typeof(TargetedRescue).GetMethod("RunOnce")), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } MethodInfo methodInfo7 = AccessTools.Method(typeof(EnemyDirector), "Start", (Type[])null, (Type[])null); if (methodInfo7 != null) { val.Patch((MethodBase)methodInfo7, (HarmonyMethod)null, new HarmonyMethod(typeof(EnemyPoolTracer).GetMethod("Post")), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } Log.LogInfo((object)"RepoOldModRescue: armed (will rescue old-API mod content at REPOLib's registration drains)."); } catch (Exception ex) { Log.LogError((object)("RepoOldModRescue: setup failed: " + ex)); } } private void LoadLevelMap() { try { string path = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? ".", "level_module_map.txt"); if (!File.Exists(path)) { Log.LogWarning((object)"RepoOldModRescue: level_module_map.txt not found; broken levels won't be relinked."); return; } string[] array = File.ReadAllLines(path); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split('|'); if (array2.Length == 3) { if (!LevelMap.TryGetValue(array2[0], out var value)) { value = new List(); LevelMap[array2[0]] = value; } value.Add(new string[2] { array2[1], array2[2] }); } } Log.LogInfo((object)$"RepoOldModRescue: loaded room-module map for {LevelMap.Count} old-SDK level(s)."); } catch (Exception ex) { Log.LogError((object)("RepoOldModRescue: failed to load level map: " + ex)); } } } internal static class EnemyRescue { private static readonly FieldInfo FSetup = AccessTools.Field(typeof(EnemyContent), "_setup"); private static readonly FieldInfo FSpawn = AccessTools.Field(typeof(EnemyContent), "_spawnObjects"); public static void Prefix() { try { HashSet hashSet = new HashSet(from s in Enemies.RegisteredEnemies where (Object)(object)s != (Object)null select ((Object)s).name); foreach (EnemySetup item in Enemies.AllEnemies.Where((EnemySetup s) => (Object)(object)s != (Object)null)) { hashSet.Add(((Object)item).name); } foreach (AssetBundle item2 in AssetBundle.GetAllLoadedAssetBundles().ToList()) { if ((Object)(object)item2 == (Object)null) { continue; } EnemySetup[] array; try { array = item2.LoadAllAssets(); } catch { continue; } EnemySetup[] array2 = array; foreach (EnemySetup val in array2) { if (!((Object)(object)val == (Object)null) && !hashSet.Contains(((Object)val).name) && val.spawnObjects != null && val.spawnObjects.Count != 0 && RegisterFromSetup(val, item2)) { hashSet.Add(((Object)val).name); } } } } catch (Exception ex) { Plugin.Log.LogError((object)("RepoOldModRescue enemy rescue failed: " + ex)); } } private static bool RegisterFromSetup(EnemySetup setup, AssetBundle bundle) { try { GameObject val = FindPrefab(setup, bundle); if ((Object)(object)val == (Object)null) { Plugin.Log.LogWarning((object)("RepoOldModRescue: no prefab for orphan enemy '" + ((Object)setup).name + "'; skipping.")); return false; } if (FSetup == null || FSpawn == null) { Plugin.Log.LogError((object)"RepoOldModRescue: EnemyContent fields missing."); return false; } EnemyContent val2 = ScriptableObject.CreateInstance(); FSetup.SetValue(val2, setup); FSpawn.SetValue(val2, new List { val }); Enemies.RegisterEnemy(val2); Plugin.Log.LogInfo((object)("RepoOldModRescue: rescued old-API enemy '" + ((Object)setup).name + "' (prefab '" + ((Object)val).name + "') from bundle '" + ((Object)bundle).name + "'.")); return true; } catch (Exception ex) { Plugin.Log.LogError((object)("RepoOldModRescue: failed to register '" + ((Object)setup).name + "': " + ex)); return false; } } private static GameObject FindPrefab(EnemySetup setup, AssetBundle bundle) { GameObject[] source; try { source = bundle.LoadAllAssets(); } catch { source = (GameObject[])(object)new GameObject[0]; } GameObject val = ((IEnumerable)source).FirstOrDefault((Func)((GameObject g) => (Object)(object)g != (Object)null && (Object)(object)g.GetComponentInChildren(true) != (Object)null)); if ((Object)(object)val != (Object)null) { return ((Component)((Component)val.GetComponentInChildren(true)).transform.root).gameObject; } string text = Norm(((Object)setup).name); EnemyParent val2 = null; int num = 0; EnemyParent[] array = Resources.FindObjectsOfTypeAll(); foreach (EnemyParent val3 in array) { if (!((Object)(object)val3 == (Object)null) && !string.IsNullOrEmpty(val3.enemyName)) { string text2 = Norm(val3.enemyName); if (text2.Length >= 3 && text2.Length > num && (text.Contains(text2) || text2.Contains(text))) { val2 = val3; num = text2.Length; } } } if (!((Object)(object)val2 != (Object)null)) { return null; } return ((Component)((Component)val2).transform.root).gameObject; } private static string Norm(string s) { return (s ?? "").ToLowerInvariant().Replace(" ", "").Replace("-", "") .Replace("_", ""); } } internal static class ValuableRescue { private static readonly string[] DangerousValuables = new string[1] { "enderpearl" }; public static void Prefix() { try { ValuableObject val2 = default(ValuableObject); foreach (AssetBundle item in AssetBundle.GetAllLoadedAssetBundles().ToList()) { if ((Object)(object)item == (Object)null) { continue; } GameObject[] array; try { array = item.LoadAllAssets(); } catch { continue; } GameObject[] array2 = array; foreach (GameObject val in array2) { if (!((Object)(object)val == (Object)null) && val.TryGetComponent(ref val2) && !NetworkPrefabs.HasNetworkPrefab("Valuables/" + ((Object)val).name)) { string tag = (((Object)val).name + " " + ((Object)item).name).ToLowerInvariant(); if (DangerousValuables.Any((string x) => tag.Contains(x))) { Plugin.Log.LogInfo((object)("RepoOldModRescue: skipping unsafe old-API valuable '" + ((Object)val).name + "' (bundle '" + ((Object)item).name + "') - active behavior can't be rescued safely.")); } else if (Valuables.RegisterValuable(val) != null) { Plugin.Log.LogInfo((object)("RepoOldModRescue: rescued old-API valuable '" + ((Object)val).name + "' from bundle '" + ((Object)item).name + "'.")); } } } } } catch (Exception ex) { Plugin.Log.LogError((object)("RepoOldModRescue valuable rescue failed: " + ex)); } } } internal static class MutatorFix { private static Durability _fallback; public static void EnsureDurability(ValuableObject __instance) { if ((Object)(object)__instance == (Object)null) { return; } if ((Object)(object)__instance.durabilityPreset != (Object)null) { if ((Object)(object)_fallback == (Object)null) { _fallback = __instance.durabilityPreset; } return; } if ((Object)(object)_fallback == (Object)null) { _fallback = Resources.FindObjectsOfTypeAll().FirstOrDefault(); } if ((Object)(object)_fallback != (Object)null) { __instance.durabilityPreset = _fallback; } } } internal static class MapDoorGuard { public static bool Prefix(GameObject __1, ref GameObject __result) { if ((Object)(object)__1 == (Object)null) { __result = null; return false; } return true; } } internal static class TargetedRescue { private static bool _done; private static readonly string[] SafeBlackboxPatches = new string[12] { "PhysGrabObjectImpactDetectorPatch", "PhysGrabObjectPatch", "ValuableObjectPatch", "TruckScreenTextPatch", "ShopManagerPatch", "HaulUIPatch", "PlayerHealthPatch", "HurtColliderPatch", "EnemyBangPatch", "EnemyUpscreamAnimPatch", "FloaterAttackLogicPatch", "LevelGeneratorPatch" }; public static void RunOnce() { if (!_done) { _done = true; RescueBlackbox(); } } private static void RescueBlackbox() { //IL_0281: Unknown result type (might be due to invalid IL or missing references) //IL_028e: Expected O, but got Unknown //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Expected O, but got Unknown try { Type type = AccessTools.TypeByName("Blackbox.Blackbox"); if (type == null) { return; } Object? obj = Resources.FindObjectsOfTypeAll(type).FirstOrDefault(); MonoBehaviour val = (MonoBehaviour)(object)((obj is MonoBehaviour) ? obj : null); if ((Object)(object)val == (Object)null) { Plugin.Log.LogWarning((object)"RepoOldModRescue: Blackbox component not found; can't bootstrap."); return; } AccessTools.Property(type, "Instance")?.SetValue(null, val); string directoryName = Path.GetDirectoryName(type.Assembly.Location); AssetBundle val2 = null; foreach (AssetBundle allLoadedAssetBundle in AssetBundle.GetAllLoadedAssetBundles()) { if ((Object)(object)allLoadedAssetBundle == (Object)null) { continue; } try { if ((Object)(object)allLoadedAssetBundle.LoadAsset("DamageCanvasMultiplayer") != (Object)null) { val2 = allLoadedAssetBundle; break; } } catch { } } if ((Object)(object)val2 == (Object)null) { val2 = AssetBundle.LoadFromFile(Path.Combine(directoryName ?? ".", "mydamagecanvasbundle")); } if ((Object)(object)val2 == (Object)null) { Plugin.Log.LogWarning((object)"RepoOldModRescue: Blackbox damage-canvas bundle not found; skipping."); return; } GameObject val3 = val2.LoadAsset("DamageCanvasMultiplayer"); GameObject val4 = val2.LoadAsset("DamageCanvasMultiplayerShop"); AccessTools.Field(type, "itemDmgMonitorLevel")?.SetValue(val, val3); AccessTools.Field(type, "itemDmgMonitorShop")?.SetValue(val, val4); int num = 0; if ((Object)(object)val3 != (Object)null && !NetworkPrefabs.HasNetworkPrefab(((Object)val3).name)) { NetworkPrefabs.RegisterNetworkPrefab(val3); num++; } if ((Object)(object)val4 != (Object)null && !NetworkPrefabs.HasNetworkPrefab(((Object)val4).name)) { NetworkPrefabs.RegisterNetworkPrefab(val4); num++; } Harmony val5 = new Harmony("themorningstar.blackboxrescue"); int num2 = 0; string[] safeBlackboxPatches = SafeBlackboxPatches; foreach (string text in safeBlackboxPatches) { Type type2 = AccessTools.TypeByName("Blackbox." + text); if (!(type2 == null)) { try { val5.PatchAll(type2); num2++; } catch (Exception ex) { Plugin.Log.LogWarning((object)("RepoOldModRescue: skipped Blackbox." + text + " (" + ex.Message + ")")); } } } MethodInfo methodInfo = AccessTools.Method(typeof(LevelGenerator), "Start", (Type[])null, (Type[])null); if (methodInfo != null) { val5.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(TargetedRescue).GetMethod("BlackboxReset")), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } Plugin.Log.LogInfo((object)$"RepoOldModRescue: resurrected Blackbox (registered {num} prefab(s), applied {num2} safe patch class(es); skipped 2 levelShop-incompatible ones)."); } catch (Exception ex2) { Plugin.Log.LogError((object)("RepoOldModRescue: Blackbox rescue failed: " + ex2)); } } public static void BlackboxReset() { try { Type type = AccessTools.TypeByName("Blackbox.Blackbox"); if (type == null) { return; } object obj = AccessTools.Property(type, "Instance")?.GetValue(null); if (obj != null) { object obj2 = AccessTools.Field(type, "valueEventTracker")?.GetValue(obj); if (obj2 != null) { AccessTools.Method(obj2.GetType(), "resetRunStats", (Type[])null, (Type[])null)?.Invoke(obj2, null); } } } catch { } } } internal static class EnemyPoolTracer { private static readonly FieldInfo F1 = AccessTools.Field(typeof(EnemyDirector), "enemiesDifficulty1"); private static readonly FieldInfo F2 = AccessTools.Field(typeof(EnemyDirector), "enemiesDifficulty2"); private static readonly FieldInfo F3 = AccessTools.Field(typeof(EnemyDirector), "enemiesDifficulty3"); public static void Post(EnemyDirector __instance) { try { string text = Dump(F1); string text2 = Dump(F2); string text3 = Dump(F3); bool flag = (text + text2 + text3).ToLowerInvariant().Contains("zombie"); Plugin.Log.LogInfo((object)$"[EnemyPoolTracer] MCZombie in a difficulty pool: {flag}"); Plugin.Log.LogInfo((object)("[EnemyPoolTracer] Difficulty1 " + text)); Plugin.Log.LogInfo((object)("[EnemyPoolTracer] Difficulty2 " + text2)); Plugin.Log.LogInfo((object)("[EnemyPoolTracer] Difficulty3 " + text3)); } catch (Exception ex) { Plugin.Log.LogWarning((object)("EnemyPoolTracer failed: " + ex.Message)); } string Dump(FieldInfo f) { if (!(f?.GetValue(__instance) is IEnumerable source)) { return ""; } List list = (from object o in source where o != null select ((Object)o).name).ToList(); return $"[{list.Count}] " + string.Join(", ", list); } } } internal static class LevelRescue { public static void Prefix(LevelContent levelContent) { try { if ((Object)(object)levelContent == (Object)null || AccessTools.Field(typeof(LevelContent), "_startRooms")?.GetValue(levelContent) is IList { Count: >0 }) { return; } object? obj = AccessTools.Field(typeof(LevelContent), "_level")?.GetValue(levelContent); Object val = (Object)((obj is Object) ? obj : null); string text = ((val != (Object)null) ? val.name : ""); if (text.StartsWith("Level - ")) { text = text.Substring("Level - ".Length); } if (!Plugin.LevelMap.TryGetValue(text, out var value)) { Plugin.Log.LogWarning((object)("RepoOldModRescue: level '" + text + "' has empty modules but no map entry; can't relink (regenerate level_module_map.txt?).")); return; } HashSet hashSet = new HashSet(value.Select((string[] e) => e[1])); Dictionary dictionary = new Dictionary(); GameObject[] array = Resources.FindObjectsOfTypeAll(); foreach (GameObject val2 in array) { if ((Object)(object)val2 != (Object)null && hashSet.Contains(((Object)val2).name) && !dictionary.ContainsKey(((Object)val2).name)) { dictionary[((Object)val2).name] = val2; } } int num2 = 0; foreach (string[] item in value) { string text2 = "_" + char.ToLowerInvariant(item[0][0]) + item[0].Substring(1); if (dictionary.TryGetValue(item[1], out var value2) && AccessTools.Field(typeof(LevelContent), text2)?.GetValue(levelContent) is List list2 && !list2.Contains(value2)) { list2.Add(value2); num2++; } } Plugin.Log.LogInfo((object)$"RepoOldModRescue: relinked {num2}/{value.Count} room modules for old-SDK level '{text}'."); } catch (Exception ex) { Plugin.Log.LogError((object)("RepoOldModRescue level rescue failed: " + ex)); } } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { internal IgnoresAccessChecksToAttribute(string assemblyName) { } } }