using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using CMRLib.Core;
using ComputerysModdingUtilities;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: StraftatMod(true)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("CMRLib")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+035ee42632884b38ea937935eb423d7c7e886381")]
[assembly: AssemblyProduct("CMRLib")]
[assembly: AssemblyTitle("CMRLib")]
[assembly: AssemblyVersion("1.0.0.0")]
[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 CMRLib
{
[BepInPlugin("cmr.core", "CMRLib", "1.0.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public sealed class CMRLibPlugin : BaseUnityPlugin
{
private void Awake()
{
((BaseUnityPlugin)this).Logger.LogInfo((object)"CMRLib 1.0.0 - starting");
Install("host", delegate
{
CmrHost.Install(((BaseUnityPlugin)this).Logger);
});
Install("text patch", delegate
{
TextPatch.Install(((BaseUnityPlugin)this).Logger);
});
Install("Mod menu card (optional)", delegate
{
ModMenuDecoration.Apply(typeof(CMRLibPlugin), "CMRLib.Resources.icon.png", "Shared runtime for the CMR mods. Does nothing on its own.", ((BaseUnityPlugin)this).Logger);
});
((BaseUnityPlugin)this).Logger.LogInfo((object)"CMRLib 1.0.0 - ready");
}
private void Install(string name, Action install)
{
try
{
install();
((BaseUnityPlugin)this).Logger.LogInfo((object)("[Init] " + name + ": installed"));
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogError((object)("[Init] " + name + ": NOT installed - " + ex.GetType().Name + ": " + ex.Message));
}
}
}
}
namespace CMRLib.Core
{
public sealed class CmrHost : MonoBehaviour
{
private static GameObject _go;
private static ManualLogSource _log;
private static bool _installed;
public static event Action Tick;
public static event Action GuiDraw;
public static void Install(ManualLogSource log)
{
_log = log;
if (!_installed)
{
_installed = true;
SceneManager.sceneLoaded += delegate
{
Ensure();
};
Ensure();
}
}
private static void Ensure()
{
//IL_0013: Unknown result type (might be due to invalid IL or missing references)
//IL_001d: Expected O, but got Unknown
if (!((Object)(object)_go != (Object)null))
{
_go = new GameObject("CMR_Host");
Object.DontDestroyOnLoad((Object)(object)_go);
_go.AddComponent<CmrHost>();
_log.LogInfo((object)"[Host] CMR_Host created");
}
}
private void Update()
{
Action tick = CmrHost.Tick;
if (tick == null)
{
return;
}
try
{
tick();
}
catch (Exception ex)
{
_log.LogError((object)("[Host] Tick failed: " + ex.GetType().Name + ": " + ex.Message));
}
}
private void OnGUI()
{
Action guiDraw = CmrHost.GuiDraw;
if (guiDraw == null)
{
return;
}
try
{
guiDraw();
}
catch (Exception ex)
{
_log.LogError((object)("[Host] GuiDraw failed: " + ex.GetType().Name + ": " + ex.Message));
}
}
private void OnDestroy()
{
if (_log != null)
{
_log.LogInfo((object)"[Host] CMR_Host destroyed - will be recreated on the next scene");
}
_go = null;
}
}
public static class EmbeddedAudio
{
private struct WavInfo
{
public int FormatTag;
public int Channels;
public int SampleRate;
public int Bits;
public int DataOffset;
public int DataLength;
}
private static readonly Dictionary<(string Asm, string Res), AudioClip> _cache = new Dictionary<(string, string), AudioClip>();
public static AudioClip Load(Assembly owner, string resourceName, ManualLogSource log)
{
if (owner == null)
{
throw new ArgumentNullException("owner");
}
if (string.IsNullOrEmpty(resourceName))
{
throw new ArgumentNullException("resourceName");
}
(string, string) key = (owner.GetName().Name, resourceName);
if (_cache.TryGetValue(key, out var value))
{
return value;
}
AudioClip val = null;
try
{
val = LoadInternal(owner, resourceName, log);
}
catch (Exception ex)
{
log.LogError((object)("[Audio] " + owner.GetName().Name + ":" + resourceName + ": " + ex.GetType().Name + ": " + ex.Message));
}
_cache[key] = val;
return val;
}
private static AudioClip LoadInternal(Assembly asm, string resourceName, ManualLogSource log)
{
byte[] array;
using (Stream stream = asm.GetManifestResourceStream(resourceName))
{
if (stream == null)
{
log.LogError((object)("[Audio] resource not found: " + resourceName + ". Available: " + string.Join(", ", asm.GetManifestResourceNames())));
return null;
}
array = new byte[stream.Length];
int num;
for (int i = 0; i < array.Length; i += num)
{
num = stream.Read(array, i, array.Length - i);
if (num <= 0)
{
break;
}
}
}
if (!TryParseWav(array, out var info, out var error))
{
log.LogError((object)("[Audio] " + resourceName + ": " + error));
return null;
}
float[] array2 = Decode(array, info);
if (array2 == null)
{
log.LogError((object)$"[Audio] {resourceName}: unsupported format, {info.Bits}-bit (tag {info.FormatTag})");
return null;
}
int num2 = array2.Length / Math.Max(1, info.Channels);
AudioClip val = AudioClip.Create(Path.GetFileNameWithoutExtension(resourceName), num2, info.Channels, info.SampleRate, false);
val.SetData(array2, 0);
log.LogInfo((object)($"[Audio] {resourceName}: {info.Channels}ch {info.SampleRate}Hz {info.Bits}bit, " + $"{val.length:0.###}s"));
return val;
}
private static bool TryParseWav(byte[] b, out WavInfo info, out string error)
{
info = default(WavInfo);
error = null;
if (b == null || b.Length < 12)
{
error = "file shorter than 12 bytes";
return false;
}
if (Tag(b, 0) != "RIFF")
{
error = "no RIFF signature";
return false;
}
if (Tag(b, 8) != "WAVE")
{
error = "not a WAVE file";
return false;
}
bool flag = false;
bool flag2 = false;
int num = 12;
while (num + 8 <= b.Length)
{
string text = Tag(b, num);
int num2 = BitConverter.ToInt32(b, num + 4);
int num3 = num + 8;
if (num2 < 0 || num3 + num2 > b.Length)
{
num2 = b.Length - num3;
}
if (text == "fmt " && num2 >= 16)
{
info.FormatTag = BitConverter.ToInt16(b, num3);
info.Channels = BitConverter.ToInt16(b, num3 + 2);
info.SampleRate = BitConverter.ToInt32(b, num3 + 4);
info.Bits = BitConverter.ToInt16(b, num3 + 14);
flag = true;
}
else if (text == "data")
{
info.DataOffset = num3;
info.DataLength = num2;
flag2 = true;
}
num = num3 + num2 + (num2 & 1);
}
if (!flag)
{
error = "no fmt chunk";
return false;
}
if (!flag2)
{
error = "no data chunk";
return false;
}
if (info.Channels <= 0 || info.SampleRate <= 0)
{
error = $"bad parameters: {info.Channels}ch {info.SampleRate}Hz";
return false;
}
return true;
}
private static float[] Decode(byte[] b, WavInfo i)
{
int dataOffset = i.DataOffset;
int dataLength = i.DataLength;
switch (i.Bits)
{
case 8:
{
float[] array2 = new float[dataLength];
for (int l = 0; l < dataLength; l++)
{
array2[l] = (float)(b[dataOffset + l] - 128) / 128f;
}
return array2;
}
case 16:
{
int num2 = dataLength / 2;
float[] array3 = new float[num2];
for (int m = 0; m < num2; m++)
{
array3[m] = (float)BitConverter.ToInt16(b, dataOffset + m * 2) / 32768f;
}
return array3;
}
case 24:
{
int num3 = dataLength / 3;
float[] array4 = new float[num3];
for (int n = 0; n < num3; n++)
{
int num4 = dataOffset + n * 3;
int num5 = b[num4] | (b[num4 + 1] << 8) | (b[num4 + 2] << 16);
if ((num5 & 0x800000) != 0)
{
num5 |= -16777216;
}
array4[n] = (float)num5 / 8388608f;
}
return array4;
}
case 32:
{
int num = dataLength / 4;
float[] array = new float[num];
if (i.FormatTag == 3)
{
for (int j = 0; j < num; j++)
{
array[j] = BitConverter.ToSingle(b, dataOffset + j * 4);
}
}
else
{
for (int k = 0; k < num; k++)
{
array[k] = (float)BitConverter.ToInt32(b, dataOffset + k * 4) / 2.1474836E+09f;
}
}
return array;
}
default:
return null;
}
}
private static string Tag(byte[] b, int off)
{
char c = (char)b[off];
string text = c.ToString();
c = (char)b[off + 1];
string text2 = c.ToString();
c = (char)b[off + 2];
string text3 = c.ToString();
c = (char)b[off + 3];
return text + text2 + text3 + c;
}
}
public static class FieldSnapshot
{
private const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
private static readonly Dictionary<Type, FieldInfo[]> _cache = new Dictionary<Type, FieldInfo[]>();
private static bool IsScalar(Type t)
{
if (!(t == typeof(float)) && !(t == typeof(int)) && !(t == typeof(bool)) && !(t == typeof(string)) && !(t == typeof(uint)))
{
return t == typeof(double);
}
return true;
}
private static FieldInfo[] ScalarFields(Type t)
{
if (_cache.TryGetValue(t, out var value))
{
return value;
}
List<FieldInfo> list = new List<FieldInfo>();
FieldInfo[] fields = t.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (FieldInfo fieldInfo in fields)
{
if (IsScalar(fieldInfo.FieldType))
{
list.Add(fieldInfo);
}
}
FieldInfo[] array = list.ToArray();
_cache[t] = array;
return array;
}
public static Dictionary<string, object> Take(Component c)
{
Dictionary<string, object> dictionary = new Dictionary<string, object>();
if ((Object)(object)c == (Object)null)
{
return dictionary;
}
FieldInfo[] array = ScalarFields(((object)c).GetType());
foreach (FieldInfo fieldInfo in array)
{
try
{
dictionary[fieldInfo.Name] = fieldInfo.GetValue(c);
}
catch
{
}
}
return dictionary;
}
public static (bool Changed, string Text) Diff(Dictionary<string, object> before, Component c)
{
if ((Object)(object)c == (Object)null)
{
return (Changed: false, Text: "component is gone");
}
List<string> list = new List<string>();
FieldInfo[] array = ScalarFields(((object)c).GetType());
foreach (FieldInfo fieldInfo in array)
{
object value;
try
{
value = fieldInfo.GetValue(c);
}
catch
{
continue;
}
if (before.TryGetValue(fieldInfo.Name, out var value2) && !object.Equals(value2, value))
{
list.Add(fieldInfo.Name + ": " + Fmt(value2) + " -> " + Fmt(value));
}
}
if (list.Count != 0)
{
return (Changed: true, Text: string.Join("; ", list.ToArray()));
}
return (Changed: false, Text: "no changes");
}
public static string Fmt(object v)
{
if (v != null)
{
if (!(v is float num))
{
if (!(v is double num2))
{
if (!(v is bool))
{
if (v is string text)
{
return "\"" + text + "\"";
}
return Convert.ToString(v, CultureInfo.InvariantCulture);
}
if (!(bool)v)
{
return "false";
}
return "true";
}
return num2.ToString("0.#####", CultureInfo.InvariantCulture);
}
return num.ToString("0.#####", CultureInfo.InvariantCulture);
}
return "null";
}
}
public static class ModMenuDecoration
{
private const string ModMenuGuid = "kestrel.straftat.modmenu";
private const string ApiType = "ModMenu.Api.ModMenuCustomisation";
public static void Apply(Type ownerType, string iconResource, string description, ManualLogSource log, params ConfigEntryBase[] hide)
{
if (ownerType == null)
{
throw new ArgumentNullException("ownerType");
}
if (!Chainloader.PluginInfos.TryGetValue("kestrel.straftat.modmenu", out var value))
{
log.LogInfo((object)"[ModMenu] not installed - settings are available via the console and the .cfg");
return;
}
Type type = AccessTools.TypeByName("ModMenu.Api.ModMenuCustomisation");
if (type == null)
{
log.LogWarning((object)(string.Format("[ModMenu] plugin {0} found but type {1} is missing - ", value.Metadata.Version, "ModMenu.Api.ModMenuCustomisation") + "the API changed, decoration skipped"));
return;
}
log.LogInfo((object)$"[ModMenu] detected {value.Metadata.Version}, settings will appear in the menu automatically");
TrySetDescription(type, ownerType, description, log);
if (iconResource != null)
{
TrySetIcon(type, ownerType, iconResource, log);
}
if (hide != null)
{
foreach (ConfigEntryBase entry in hide)
{
TryHide(type, entry, log);
}
}
}
private static bool TryRegister(Type api, string property, string guid, object value, ManualLogSource log)
{
PropertyInfo propertyInfo = AccessTools.Property(api, property);
if (propertyInfo == null)
{
log.LogWarning((object)("[ModMenu] " + property + " is absent - the API changed"));
return false;
}
if (!(propertyInfo.GetValue(null, null) is IDictionary dictionary))
{
log.LogWarning((object)("[ModMenu] " + property + " is not a dictionary - the API changed"));
return false;
}
dictionary[guid] = value;
return true;
}
private static string GuidOf(Type ownerType)
{
return (((MemberInfo)ownerType).GetCustomAttribute<BepInPlugin>() ?? throw new ArgumentException(ownerType.Name + " has no [BepInPlugin] - pass the mod's plugin type", "ownerType")).GUID;
}
private static void TrySetDescription(Type api, Type ownerType, string description, ManualLogSource log)
{
try
{
string text = GuidOf(ownerType);
if (TryRegister(api, "Descriptions", text, description, log))
{
log.LogInfo((object)("[ModMenu] description registered for " + text));
}
}
catch (Exception ex)
{
log.LogWarning((object)("[ModMenu] description not set: " + ex.GetType().Name + ": " + ex.Message));
}
}
private static void TrySetIcon(Type api, Type ownerType, string iconResource, ManualLogSource log)
{
try
{
string text = GuidOf(ownerType);
Sprite val = LoadIcon(ownerType, iconResource, log);
if (!((Object)(object)val == (Object)null) && TryRegister(api, "Icons", text, val, log))
{
log.LogInfo((object)("[ModMenu] icon registered for " + text));
}
}
catch (Exception ex)
{
log.LogWarning((object)("[ModMenu] icon not set: " + ex.GetType().Name + ": " + ex.Message));
}
}
private static Sprite LoadIcon(Type ownerType, string iconResource, ManualLogSource log)
{
//IL_0083: Unknown result type (might be due to invalid IL or missing references)
//IL_0089: Expected O, but got Unknown
//IL_00db: Unknown result type (might be due to invalid IL or missing references)
//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
Assembly assembly = ownerType.Assembly;
byte[] array;
using (Stream stream = assembly.GetManifestResourceStream(iconResource))
{
if (stream == null)
{
log.LogWarning((object)("[ModMenu] icon resource " + iconResource + " not found in " + assembly.GetName().Name));
return null;
}
array = new byte[stream.Length];
int num;
for (int i = 0; i < array.Length; i += num)
{
num = stream.Read(array, i, array.Length - i);
if (num <= 0)
{
break;
}
}
}
Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
if (!ImageConversion.LoadImage(val, array))
{
log.LogWarning((object)"[ModMenu] icon PNG could not be decoded");
return null;
}
((Object)val).hideFlags = (HideFlags)61;
((Object)val).name = assembly.GetName().Name + "_Icon";
Sprite obj = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f));
((Object)obj).hideFlags = (HideFlags)61;
((Object)obj).name = ((Object)val).name;
log.LogInfo((object)$"[ModMenu] icon loaded for {assembly.GetName().Name}: {((Texture)val).width}x{((Texture)val).height}");
return obj;
}
private static void TryHide(Type api, ConfigEntryBase entry, ManualLogSource log)
{
if (entry == null)
{
return;
}
try
{
MethodInfo methodInfo = null;
MethodInfo[] methods = api.GetMethods(BindingFlags.Static | BindingFlags.Public);
foreach (MethodInfo methodInfo2 in methods)
{
if (!(methodInfo2.Name != "HideEntry") && methodInfo2.IsGenericMethodDefinition && methodInfo2.GetParameters().Length == 1)
{
methodInfo = methodInfo2;
break;
}
}
if (methodInfo == null)
{
MethodInfo methodInfo3 = AccessTools.Method(api, "HideEntry", new Type[1] { typeof(ConfigEntryBase) }, (Type[])null);
if (methodInfo3 == null)
{
log.LogInfo((object)"[ModMenu] HideEntry is absent, skipped");
return;
}
methodInfo3.Invoke(null, new object[1] { entry });
return;
}
Type type = EntryValueType(entry);
if (type == null)
{
log.LogWarning((object)("[ModMenu] could not hide '" + entry.Definition.Key + "': value type is unknown"));
return;
}
methodInfo.MakeGenericMethod(type).Invoke(null, new object[1] { entry });
}
catch (Exception ex)
{
log.LogWarning((object)("[ModMenu] could not hide '" + entry.Definition.Key + "': " + ex.GetType().Name + ": " + ex.Message));
}
}
private static Type EntryValueType(ConfigEntryBase entry)
{
Type type = ((object)entry).GetType();
while (type != null)
{
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ConfigEntry<>))
{
return type.GetGenericArguments()[0];
}
type = type.BaseType;
}
return null;
}
}
public sealed class PrefabRef
{
public Component Owner;
public FieldInfo Field;
public Component Value;
public GameObject TargetRoot;
public override string ToString()
{
return $"{((object)Owner).GetType().Name}.{Field.Name} -> {((Object)TargetRoot).name}#{((Object)TargetRoot).GetInstanceID()}";
}
}
public static class PrefabRebinder
{
private const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
public static List<PrefabRef> FindExternalRefs(GameObject root)
{
//IL_00af: Unknown result type (might be due to invalid IL or missing references)
//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
List<PrefabRef> list = new List<PrefabRef>();
if ((Object)(object)root == (Object)null)
{
return list;
}
Component[] componentsInChildren = root.GetComponentsInChildren<Component>(true);
foreach (Component val in componentsInChildren)
{
if ((Object)(object)val == (Object)null)
{
continue;
}
Type type = ((object)val).GetType();
FieldInfo[] fields;
try
{
fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
}
catch
{
continue;
}
FieldInfo[] array = fields;
foreach (FieldInfo fieldInfo in array)
{
if (!typeof(Component).IsAssignableFrom(fieldInfo.FieldType))
{
continue;
}
Component val2;
try
{
object? value = fieldInfo.GetValue(val);
val2 = (Component)((value is Component) ? value : null);
}
catch
{
continue;
}
if ((Object)(object)val2 == (Object)null)
{
continue;
}
GameObject gameObject = ((Component)val2.transform.root).gameObject;
if (gameObject != root)
{
Scene scene = gameObject.scene;
if (!((Scene)(ref scene)).IsValid())
{
list.Add(new PrefabRef
{
Owner = val,
Field = fieldInfo,
Value = val2,
TargetRoot = gameObject
});
}
}
}
}
return list;
}
public static void ReportSharing(IEnumerable<GameObject> owners, ManualLogSource log)
{
Dictionary<int, List<string>> dictionary = new Dictionary<int, List<string>>();
Dictionary<int, string> dictionary2 = new Dictionary<int, string>();
foreach (GameObject owner in owners)
{
if ((Object)(object)owner == (Object)null)
{
continue;
}
List<PrefabRef> list = FindExternalRefs(owner);
log.LogInfo((object)$"[Rebind] {((Object)owner).name}: {list.Count} external references");
foreach (PrefabRef item in list)
{
log.LogInfo((object)$"[Rebind] {item}");
int instanceID = ((Object)item.TargetRoot).GetInstanceID();
dictionary2[instanceID] = ((Object)item.TargetRoot).name;
if (!dictionary.TryGetValue(instanceID, out var value))
{
value = (dictionary[instanceID] = new List<string>());
}
if (!value.Contains(((Object)owner).name))
{
value.Add(((Object)owner).name);
}
}
}
foreach (KeyValuePair<int, List<string>> item2 in dictionary)
{
if (item2.Value.Count >= 2)
{
log.LogWarning((object)($"[Rebind] SHARED prefab {dictionary2[item2.Key]}#{item2.Key} " + "used by: " + string.Join(", ", item2.Value.ToArray())));
}
}
}
}
public enum PrefabScope
{
Any,
Pickup,
Spawned
}
public static class PrefabRegistry
{
public const string WeaponsResourcePath = "RandomWeapons";
private static readonly string[] SpawnedMarkerTypes = new string[8] { "PredictedProjectile", "ShrapnelBallistic", "PhysicsGrenade", "ProximityMine", "Claymore", "Obus", "HandGrenadeTwo", "RebondBalle" };
private static bool _warm;
private static GameObject[] _pickups = (GameObject[])(object)new GameObject[0];
private static List<GameObject> _spawned = new List<GameObject>();
private static ManualLogSource _log;
private static ManualLogSource Log => _log ?? (_log = Logger.CreateLogSource("CMRLib.Registry"));
public static IReadOnlyList<GameObject> Pickups
{
get
{
EnsureWarm();
return _pickups;
}
}
public static IReadOnlyList<GameObject> Spawned
{
get
{
EnsureWarm();
return _spawned;
}
}
public static bool IsWarm => _warm;
public static void EnsureWarm()
{
if (!_warm)
{
_pickups = (GameObject[])(((object)Resources.LoadAll<GameObject>("RandomWeapons")) ?? ((object)new GameObject[0]));
Log.LogInfo((object)string.Format("[Registry] LoadAll(\"{0}\") -> {1} pickups", "RandomWeapons", _pickups.Length));
if (_pickups.Length == 0)
{
Log.LogWarning((object)"[Registry] Empty. Did the Resources path change? Everything below will find nothing.");
}
CollectSpawned();
Log.LogInfo((object)$"[Registry] spawned prefabs: {_spawned.Count}");
_warm = true;
}
}
private static void CollectSpawned()
{
//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
//IL_0103: Unknown result type (might be due to invalid IL or missing references)
HashSet<int> hashSet = new HashSet<int>(from p in _pickups
where (Object)(object)p != (Object)null
select ((Object)p).GetInstanceID());
Dictionary<int, GameObject> dictionary = new Dictionary<int, GameObject>();
string[] spawnedMarkerTypes = SpawnedMarkerTypes;
foreach (string text in spawnedMarkerTypes)
{
Type type = AccessTools.TypeByName(text);
if (type == null)
{
Log.LogWarning((object)("[Registry] type " + text + " not found - did the game update?"));
continue;
}
Object[] array;
try
{
array = Resources.FindObjectsOfTypeAll(type);
}
catch (Exception ex)
{
Log.LogWarning((object)("[Registry] " + text + ": " + ex.Message));
continue;
}
Object[] array2 = array;
foreach (Object obj in array2)
{
Component val = (Component)(object)((obj is Component) ? obj : null);
if (val != null)
{
GameObject gameObject = ((Component)val.transform.root).gameObject;
Scene scene = gameObject.scene;
if (!((Scene)(ref scene)).IsValid() && !hashSet.Contains(((Object)gameObject).GetInstanceID()))
{
dictionary[((Object)gameObject).GetInstanceID()] = gameObject;
}
}
}
}
_spawned = dictionary.Values.ToList();
}
public static GameObject[] Resolve(PrefabQuery query)
{
EnsureWarm();
return (query.Scope switch
{
PrefabScope.Pickup => _pickups,
PrefabScope.Spawned => _spawned,
_ => _pickups.Concat(_spawned),
}).Where((GameObject go) => (Object)(object)go != (Object)null && Matches(go, query)).ToArray();
}
private static bool Matches(GameObject go, PrefabQuery q)
{
if (!string.IsNullOrEmpty(q.Name) && !(q.Exact ? string.Equals(((Object)go).name, q.Name, StringComparison.OrdinalIgnoreCase) : (((Object)go).name.IndexOf(q.Name, StringComparison.OrdinalIgnoreCase) >= 0)))
{
return false;
}
if (!string.IsNullOrEmpty(q.Layer))
{
int num = LayerMask.NameToLayer(q.Layer);
if (num < 0)
{
return false;
}
if (go.layer != num)
{
return false;
}
}
if (!string.IsNullOrEmpty(q.RequireComponent))
{
Type type = AccessTools.TypeByName(q.RequireComponent);
if (type == null)
{
return false;
}
if ((Object)(object)go.GetComponentInChildren(type, true) == (Object)null)
{
return false;
}
}
return true;
}
}
public sealed class PrefabQuery
{
public string Name;
public bool Exact = true;
public string Layer;
public string RequireComponent;
public PrefabScope Scope;
public override string ToString()
{
List<string> list = new List<string>();
if (!string.IsNullOrEmpty(Name))
{
list.Add(Exact ? ("name==" + Name) : ("name~" + Name));
}
if (!string.IsNullOrEmpty(Layer))
{
list.Add("layer=" + Layer);
}
if (!string.IsNullOrEmpty(RequireComponent))
{
list.Add("has=" + RequireComponent);
}
if (Scope != PrefabScope.Any)
{
list.Add($"scope={Scope}");
}
return string.Join(", ", list);
}
}
public sealed class TextPatch
{
private const int RetryEveryFrames = 30;
private static readonly List<TextPatch> _pending = new List<TextPatch>();
private static ManualLogSource _log;
private static bool _installed;
private readonly string _path;
private Func<string, string> _transform;
private float? _fontSize;
private bool _keep;
private string _applied;
private string _original;
private int _lastTry = -999;
private bool _done;
private TextPatch(string path)
{
_path = path;
}
public static void Install(ManualLogSource log)
{
_log = log;
if (!_installed)
{
_installed = true;
CmrHost.Tick += TickAll;
}
}
public static TextPatch At(string path)
{
TextPatch textPatch = new TextPatch(path);
_pending.Add(textPatch);
return textPatch;
}
public TextPatch Set(string text)
{
return Transform((string _) => text);
}
public TextPatch Append(string suffix)
{
return Transform((string orig) => orig + suffix);
}
public TextPatch Prepend(string prefix)
{
return Transform((string orig) => prefix + orig);
}
public TextPatch Transform(Func<string, string> f)
{
_transform = f;
return this;
}
public TextPatch FontSize(float size)
{
_fontSize = size;
return this;
}
public TextPatch Keep()
{
_keep = true;
return this;
}
private static void TickAll()
{
for (int num = _pending.Count - 1; num >= 0; num--)
{
TextPatch textPatch = _pending[num];
if (textPatch.TryApply() && textPatch._done)
{
_pending.RemoveAt(num);
}
}
}
private bool TryApply()
{
if (Time.frameCount - _lastTry < 30)
{
return false;
}
_lastTry = Time.frameCount;
Transform val = UiFind.Path(_path);
if ((Object)(object)val == (Object)null)
{
return false;
}
TMP_Text val2 = ((Component)val).GetComponent<TMP_Text>() ?? ((Component)val).GetComponentInChildren<TMP_Text>(true);
if ((Object)(object)val2 == (Object)null)
{
_log.LogWarning((object)("[Text] " + _path + ": object found but it has no TMP_Text"));
_done = true;
return true;
}
if (_original == null)
{
_original = val2.text ?? "";
_log.LogInfo((object)("[Text] " + _path + ": original = \"" + Escape(_original) + "\""));
}
if (_applied != null && val2.text != _applied)
{
_original = val2.text ?? "";
if (_keep)
{
_log.LogInfo((object)("[Text] " + _path + ": game overwrote it, restoring ours"));
}
}
string text = ((_transform != null) ? _transform(_original) : _original);
if (val2.text != text)
{
val2.text = text;
_applied = text;
_log.LogInfo((object)("[Text] " + _path + ": \"" + Escape(_original) + "\" -> \"" + Escape(text) + "\""));
}
else
{
_applied = text;
}
if (_fontSize.HasValue && Math.Abs(val2.fontSize - _fontSize.Value) > 0.01f)
{
val2.fontSize = _fontSize.Value;
_log.LogInfo((object)$"[Text] {_path}: fontSize -> {_fontSize.Value}");
}
if (!_keep)
{
_done = true;
}
return true;
}
private static string Escape(string s)
{
if (s != null)
{
return s.Replace("\r", "\\r").Replace("\n", "\\n");
}
return "<null>";
}
}
public sealed class Tweak
{
private sealed class Step
{
public string Label;
public Func<GameObject, StepResult> Run;
}
internal readonly struct StepResult
{
public readonly bool Changed;
public readonly string Text;
public StepResult(bool changed, string text)
{
Changed = changed;
Text = text;
}
}
private readonly List<Step> _steps = new List<Step>();
public string Id { get; private set; }
public PrefabQuery Query { get; private set; }
public bool AllowMultiple { get; private set; }
public bool Optional { get; private set; }
public bool IsPinned { get; private set; }
public string PinReason { get; private set; }
internal int StepCount => _steps.Count;
private Tweak(string id, PrefabQuery query)
{
Id = id;
Query = query;
}
public static Tweak Pickup(string name)
{
return new Tweak(name, new PrefabQuery
{
Name = name,
Exact = true,
Scope = PrefabScope.Pickup
});
}
public static Tweak Spawned(string name)
{
return new Tweak(name, new PrefabQuery
{
Name = name,
Exact = true,
Scope = PrefabScope.Spawned
});
}
public static Tweak Any(string name)
{
return new Tweak(name, new PrefabQuery
{
Name = name,
Exact = true,
Scope = PrefabScope.Any
});
}
public static Tweak Where(string id, PrefabQuery query)
{
return new Tweak(id, query);
}
public Tweak OnLayer(string layer)
{
Query.Layer = layer;
return this;
}
public Tweak WithComponent(string typeName)
{
Query.RequireComponent = typeName;
return this;
}
public Tweak Multiple()
{
AllowMultiple = true;
return this;
}
public Tweak IfPresent()
{
Optional = true;
return this;
}
public Tweak Pin(string reason = null)
{
IsPinned = true;
PinReason = reason;
return this;
}
public Tweak Weapon(Action<Weapon> apply)
{
return this.Component<Weapon>(apply, "Weapon");
}
public Tweak Melee(Action<MeleeWeapon> apply)
{
return this.Component<MeleeWeapon>(apply, "MeleeWeapon");
}
public Tweak Item(Action<ItemBehaviour> apply)
{
return this.Component<ItemBehaviour>(apply, "ItemBehaviour");
}
public Tweak Rename(string weaponName)
{
_steps.Add(new Step
{
Label = "Rename -> \"" + weaponName + "\"",
Run = delegate(GameObject root)
{
ItemBehaviour componentInChildren = root.GetComponentInChildren<ItemBehaviour>(true);
if ((Object)(object)componentInChildren == (Object)null)
{
throw new TweakException("no ItemBehaviour");
}
string weaponName2 = componentInChildren.weaponName;
componentInChildren.weaponName = weaponName;
return (!(weaponName2 == weaponName)) ? new StepResult(changed: true, "weaponName: \"" + weaponName2 + "\" -> \"" + weaponName + "\"") : new StepResult(changed: false, "weaponName: already \"" + weaponName + "\"");
}
});
return this;
}
public Tweak Component<T>(Action<T> apply, string label = null) where T : Component
{
string name = label ?? typeof(T).Name;
_steps.Add(new Step
{
Label = name,
Run = delegate(GameObject root)
{
T componentInChildren = root.GetComponentInChildren<T>(true);
if ((Object)(object)componentInChildren == (Object)null)
{
throw new TweakException("no " + name);
}
Dictionary<string, object> before = FieldSnapshot.Take((Component)(object)componentInChildren);
apply(componentInChildren);
var (changed, text) = FieldSnapshot.Diff(before, (Component)(object)componentInChildren);
return new StepResult(changed, name + " { " + text + " }");
}
});
return this;
}
public Tweak Private<T>(string field, object value) where T : Component
{
_steps.Add(new Step
{
Label = typeof(T).Name + "." + field,
Run = delegate(GameObject root)
{
T componentInChildren = root.GetComponentInChildren<T>(true);
if ((Object)(object)componentInChildren == (Object)null)
{
throw new TweakException("no " + typeof(T).Name);
}
return SetField(componentInChildren, typeof(T), field, value);
}
});
return this;
}
public Tweak PrivateOn(string typeName, string field, object value)
{
_steps.Add(new Step
{
Label = typeName + "." + field,
Run = delegate(GameObject root)
{
Type type = AccessTools.TypeByName(typeName);
if (type == null)
{
throw new TweakException("type " + typeName + " not found");
}
Component componentInChildren = root.GetComponentInChildren(type, true);
if ((Object)(object)componentInChildren == (Object)null)
{
throw new TweakException("no " + typeName);
}
return SetField(componentInChildren, type, field, value);
}
});
return this;
}
public Tweak HideChild(string childName)
{
_steps.Add(new Step
{
Label = "HideChild(" + childName + ")",
Run = delegate(GameObject root)
{
List<string> list = new List<string>();
int num = 0;
Transform val = root.transform.Find(childName);
if ((Object)(object)val != (Object)null)
{
if (((Component)val).gameObject.activeSelf)
{
((Component)val).gameObject.SetActive(false);
list.Add(((Object)val).name);
}
else
{
num++;
}
}
else
{
Transform[] componentsInChildren = root.GetComponentsInChildren<Transform>(true);
foreach (Transform val2 in componentsInChildren)
{
if (string.Equals(((Object)val2).name, childName, StringComparison.OrdinalIgnoreCase))
{
if (((Component)val2).gameObject.activeSelf)
{
((Component)val2).gameObject.SetActive(false);
list.Add(((Object)val2).name);
}
else
{
num++;
}
}
}
}
if (list.Count == 0 && num == 0)
{
throw new TweakException("no child named '" + childName + "'");
}
return (list.Count <= 0) ? new StepResult(changed: false, "HideChild(" + childName + "): already hidden") : new StepResult(changed: true, "HideChild(" + childName + "): hidden" + ((list.Count > 1) ? (" " + list.Count) : ""));
}
});
return this;
}
public Tweak Raw(string describe, Action<GameObject> action)
{
_steps.Add(new Step
{
Label = describe,
Run = delegate(GameObject root)
{
action(root);
return new StepResult(changed: true, describe);
}
});
return this;
}
internal int ApplyTo(GameObject root, List<string> applied)
{
int num = 0;
foreach (Step step in _steps)
{
StepResult stepResult = step.Run(root);
applied.Add(stepResult.Text);
if (stepResult.Changed)
{
num++;
}
}
return num;
}
private static StepResult SetField(object target, Type declaring, string field, object value)
{
FieldInfo fieldInfo = AccessTools.Field(declaring, field);
if (fieldInfo == null)
{
throw new TweakException("no field " + declaring.Name + "." + field);
}
object obj = value;
if (value != null && !fieldInfo.FieldType.IsInstanceOfType(value))
{
try
{
obj = Convert.ChangeType(value, fieldInfo.FieldType);
}
catch
{
throw new TweakException(declaring.Name + "." + field + ": " + value?.GetType().Name + " is not convertible to " + fieldInfo.FieldType.Name);
}
}
object value2 = fieldInfo.GetValue(target);
fieldInfo.SetValue(target, obj);
string text = declaring.Name + "." + field;
if (!object.Equals(value2, obj))
{
return new StepResult(changed: true, text + ": " + FieldSnapshot.Fmt(value2) + " -> " + FieldSnapshot.Fmt(obj));
}
return new StepResult(changed: false, text + ": already " + FieldSnapshot.Fmt(obj));
}
}
public sealed class TweakException : Exception
{
public TweakException(string message)
: base(message)
{
}
}
public static class TweakRunner
{
public sealed class Report
{
public int Applied;
public int Missing;
public int Ambiguous;
public int Failed;
public int NoOp;
public int Pinned;
public readonly List<string> Problems = new List<string>();
public bool AllGood
{
get
{
if (Missing == 0 && Ambiguous == 0 && Failed == 0)
{
return NoOp == 0;
}
return false;
}
}
}
public static Report Run(string groupName, IEnumerable<Tweak> tweaks, ManualLogSource log)
{
Report report = new Report();
IList<Tweak> list = (tweaks as IList<Tweak>) ?? tweaks.ToList();
log.LogInfo((object)$"[Tweaks] === {groupName}: {list.Count} tweaks ===");
foreach (Tweak item in list)
{
try
{
RunOne(item, log, report);
}
catch (Exception ex)
{
report.Failed++;
string text = item.Id + ": unexpected failure - " + ex.GetType().Name + ": " + ex.Message;
report.Problems.Add(text);
log.LogError((object)("[Tweaks] " + text));
}
}
log.LogInfo((object)($"[Tweaks] === {groupName}: applied {report.Applied}, " + $"pinned-unchanged {report.Pinned}, no-op {report.NoOp}, " + $"missing {report.Missing}, ambiguous {report.Ambiguous}, " + $"failed {report.Failed} ==="));
return report;
}
private static void RunOne(Tweak tweak, ManualLogSource log, Report report)
{
GameObject[] array = PrefabRegistry.Resolve(tweak.Query);
if (array.Length == 0)
{
if (!tweak.Optional)
{
report.Missing++;
string text = $"{tweak.Id}: not found ({tweak.Query})";
report.Problems.Add(text);
log.LogWarning((object)("[Tweaks] " + text));
}
return;
}
if (array.Length > 1 && !tweak.AllowMultiple)
{
report.Ambiguous++;
string arg = string.Join(", ", array.Select((GameObject h) => $"{((Object)h).name}#{((Object)h).GetInstanceID()}"));
string text2 = $"{tweak.Id}: {array.Length} matches ({arg}) - narrow the selector " + "(layer/component) or allow .Multiple()";
report.Problems.Add(text2);
log.LogWarning((object)("[Tweaks] " + text2));
return;
}
GameObject[] array2 = array;
foreach (GameObject val in array2)
{
List<string> list = new List<string>();
try
{
int num2 = tweak.ApplyTo(val, list);
report.Applied++;
string text3 = string.Join("; ", list);
if (num2 == 0 && tweak.IsPinned)
{
report.Pinned++;
string text4 = (string.IsNullOrEmpty(tweak.PinReason) ? "" : (" - " + tweak.PinReason));
log.LogInfo((object)("[Tweaks] " + tweak.Id + " -> " + ((Object)val).name + ": pin holds current value" + text4 + " (" + text3 + ")"));
}
else if (num2 == 0)
{
report.NoOp++;
string text5 = tweak.Id + " -> " + ((Object)val).name + ": NOTHING CHANGED (" + text3 + ")";
report.Problems.Add(text5);
log.LogWarning((object)("[Tweaks] " + text5));
}
else
{
log.LogInfo((object)("[Tweaks] " + tweak.Id + " -> " + ((Object)val).name + ": " + text3));
}
}
catch (TweakException ex)
{
report.Failed++;
string text6 = $"{tweak.Id} -> {((Object)val).name}: {ex.Message} (completed {list.Count}/{tweak.StepCount})";
report.Problems.Add(text6);
log.LogWarning((object)("[Tweaks] " + text6));
}
catch (Exception ex2)
{
report.Failed++;
string text7 = tweak.Id + " -> " + ((Object)val).name + ": " + ex2.GetType().Name + ": " + ex2.Message;
report.Problems.Add(text7);
log.LogError((object)("[Tweaks] " + text7));
}
}
}
}
public static class UiFind
{
public static Transform Root(string name)
{
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
for (int i = 0; i < SceneManager.sceneCount; i++)
{
Scene sceneAt = SceneManager.GetSceneAt(i);
if (!((Scene)(ref sceneAt)).isLoaded)
{
continue;
}
GameObject[] rootGameObjects = ((Scene)(ref sceneAt)).GetRootGameObjects();
foreach (GameObject val in rootGameObjects)
{
if (((Object)val).name == name)
{
return val.transform;
}
}
}
GameObject val2 = GameObject.Find(name);
if (!((Object)(object)val2 != (Object)null))
{
return null;
}
return val2.transform;
}
public static Transform Descendant(Transform parent, string name)
{
if ((Object)(object)parent == (Object)null)
{
return null;
}
Queue<Transform> queue = new Queue<Transform>();
queue.Enqueue(parent);
while (queue.Count > 0)
{
Transform val = queue.Dequeue();
if (((Object)val).name == name)
{
return val;
}
for (int i = 0; i < val.childCount; i++)
{
queue.Enqueue(val.GetChild(i));
}
}
return null;
}
public static Transform Path(string path)
{
if (string.IsNullOrEmpty(path))
{
return null;
}
string[] array = path.Split(new char[1] { '/' });
Transform val = Root(array[0]);
for (int i = 1; i < array.Length; i++)
{
if (!((Object)(object)val != (Object)null))
{
break;
}
val = Descendant(val, array[i]);
}
return val;
}
public static string FullPath(Transform t)
{
if ((Object)(object)t == (Object)null)
{
return "<null>";
}
string text = ((Object)t).name;
Transform parent = t.parent;
while ((Object)(object)parent != (Object)null)
{
text = ((Object)parent).name + "/" + text;
parent = parent.parent;
}
return text;
}
}
}