Decompiled source of ItemDisplayFix v1.0.0

plugins/ItemDisplayFix.dll

Decompiled 12 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace ItemDisplayFix;

[BepInPlugin("evreborns.itemdisplayfix", "ItemDisplayFix", "1.0.0")]
public class ItemDisplayFixPlugin : BaseUnityPlugin
{
	private static ManualLogSource log;

	private static readonly Dictionary<string, GameObject> prefabs = new Dictionary<string, GameObject>(StringComparer.OrdinalIgnoreCase);

	private static bool built;

	private static readonly string[] bodies = new string[18]
	{
		"CommandoBody", "HuntressBody", "Bandit2Body", "ToolbotBody", "EngiBody", "MageBody", "MercBody", "TreebotBody", "LoaderBody", "CrocoBody",
		"CaptainBody", "RailgunnerBody", "VoidSurvivorBody", "SeekerBody", "ChefBody", "FalseSonBody", "HereticBody", "ScavBody"
	};

	private static Harmony harmony;

	private static HarmonyMethod prefixHM;

	private static HarmonyMethod postfixHM;

	private static readonly HashSet<Type> patched = new HashSet<Type>();

	private void Awake()
	{
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Expected O, but got Unknown
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_003a: Expected O, but got Unknown
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		//IL_005a: Expected O, but got Unknown
		log = ((BaseUnityPlugin)this).Logger;
		harmony = new Harmony("evreborns.itemdisplayfix");
		postfixHM = new HarmonyMethod(typeof(ItemDisplayFixPlugin).GetMethod("PopulatePostfix", BindingFlags.Static | BindingFlags.NonPublic));
		prefixHM = new HarmonyMethod(typeof(ItemDisplayFixPlugin).GetMethod("PopulatePrefix", BindingFlags.Static | BindingFlags.NonPublic));
		PatchAll();
	}

	private static void PatchAll()
	{
		try
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				Type[] types;
				try
				{
					types = assembly.GetTypes();
				}
				catch (ReflectionTypeLoadException ex)
				{
					types = ex.Types;
				}
				catch
				{
					continue;
				}
				Type[] array = types;
				foreach (Type type in array)
				{
					if (type == null || patched.Contains(type))
					{
						continue;
					}
					FieldInfo fieldInfo = FindDictField(type);
					if (fieldInfo == null)
					{
						continue;
					}
					MethodInfo method = type.GetMethod("PopulateDisplays", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
					if (!(method == null))
					{
						patched.Add(type);
						try
						{
							harmony.Patch((MethodBase)method, prefixHM, postfixHM, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
							log.LogInfo((object)("Template ItemDisplays dipatch: " + type.FullName));
						}
						catch (Exception ex2)
						{
							log.LogWarning((object)("Gagal patch " + type.FullName + ": " + ex2.Message));
						}
					}
				}
			}
		}
		catch (Exception ex3)
		{
			log.LogError((object)ex3);
		}
	}

	private void Start()
	{
		PatchAll();
		try
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				Type[] types;
				try
				{
					types = assembly.GetTypes();
				}
				catch (ReflectionTypeLoadException ex)
				{
					types = ex.Types;
				}
				catch
				{
					continue;
				}
				Type[] array = types;
				foreach (Type type in array)
				{
					if (type == null)
					{
						continue;
					}
					FieldInfo fieldInfo = FindDictField(type);
					if (!(fieldInfo == null))
					{
						IDictionary dictionary = null;
						try
						{
							dictionary = fieldInfo.GetValue(null) as IDictionary;
						}
						catch
						{
						}
						if (dictionary != null)
						{
							Fill(dictionary, type.FullName);
						}
					}
				}
			}
		}
		catch (Exception ex2)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)ex2);
		}
	}

	private static FieldInfo FindDictField(Type t)
	{
		FieldInfo fieldInfo = null;
		try
		{
			fieldInfo = t.GetField("itemDisplayPrefabs", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		}
		catch
		{
		}
		if (fieldInfo == null || !typeof(IDictionary).IsAssignableFrom(fieldInfo.FieldType))
		{
			return null;
		}
		return fieldInfo;
	}

	private static void PopulatePrefix()
	{
		try
		{
			Build();
		}
		catch (Exception ex)
		{
			if (log != null)
			{
				log.LogWarning((object)ex.Message);
			}
		}
	}

	private static void PopulatePostfix(MethodBase __originalMethod)
	{
		try
		{
			FieldInfo fieldInfo = FindDictField(__originalMethod.DeclaringType);
			IDictionary dictionary = ((fieldInfo != null) ? (fieldInfo.GetValue(null) as IDictionary) : null);
			if (dictionary != null)
			{
				Fill(dictionary, __originalMethod.DeclaringType.FullName);
			}
		}
		catch (Exception ex)
		{
			if (log != null)
			{
				log.LogWarning((object)ex.Message);
			}
		}
	}

	private static void Fill(IDictionary d, string owner)
	{
		Build();
		int num = 0;
		foreach (KeyValuePair<string, GameObject> prefab in prefabs)
		{
			string key = prefab.Key.ToLowerInvariant();
			if (!d.Contains(key))
			{
				d[key] = prefab.Value;
				num++;
			}
		}
		if (num > 0 && log != null)
		{
			log.LogInfo((object)("Mengisi " + num + " model item untuk " + owner));
		}
	}

	private static void Build()
	{
		if (built)
		{
			return;
		}
		built = true;
		Type type = AccessTools.TypeByName("RoR2.LegacyResourcesAPI");
		MethodInfo methodInfo = null;
		int num = 0;
		if (type != null)
		{
			MethodInfo[] methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public);
			foreach (MethodInfo methodInfo2 in methods)
			{
				if (methodInfo2.Name != "Load" || !methodInfo2.IsGenericMethodDefinition)
				{
					continue;
				}
				ParameterInfo[] parameters = methodInfo2.GetParameters();
				if (parameters.Length != 0 && !(parameters[0].ParameterType != typeof(string)) && (parameters.Length == 1 || (parameters.Length == 2 && parameters[1].ParameterType == typeof(bool))))
				{
					methodInfo = methodInfo2.MakeGenericMethod(typeof(GameObject));
					num = parameters.Length;
					if (parameters.Length == 1)
					{
						break;
					}
				}
			}
		}
		Type type2 = AccessTools.TypeByName("UnityEngine.AddressableAssets.Addressables");
		MethodInfo loadAsync = null;
		if (type2 != null)
		{
			MethodInfo[] methods2 = type2.GetMethods(BindingFlags.Static | BindingFlags.Public);
			foreach (MethodInfo methodInfo3 in methods2)
			{
				if (methodInfo3.Name == "LoadAssetAsync" && methodInfo3.IsGenericMethodDefinition)
				{
					ParameterInfo[] parameters2 = methodInfo3.GetParameters();
					if (parameters2.Length == 1 && parameters2[0].ParameterType == typeof(object))
					{
						loadAsync = methodInfo3.MakeGenericMethod(typeof(GameObject));
						break;
					}
				}
			}
		}
		if (methodInfo == null)
		{
			log.LogError((object)"LegacyResourcesAPI.Load tidak ditemukan.");
			return;
		}
		int num2 = 0;
		string[] array = bodies;
		foreach (string text in array)
		{
			try
			{
				object[] parameters3 = ((num == 2) ? new object[2]
				{
					"Prefabs/CharacterBodies/" + text,
					false
				} : new object[1] { "Prefabs/CharacterBodies/" + text });
				object? obj = methodInfo.Invoke(null, parameters3);
				GameObject val = (GameObject)((obj is GameObject) ? obj : null);
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				Component component = val.GetComponent("ModelLocator");
				if ((Object)(object)component == (Object)null)
				{
					continue;
				}
				object member = GetMember(component, "modelTransform");
				Transform val2 = (Transform)((member is Transform) ? member : null);
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				Component component2 = ((Component)val2).GetComponent("CharacterModel");
				if (!((Object)(object)component2 == (Object)null))
				{
					object member2 = GetMember(component2, "itemDisplayRuleSet");
					Object val3 = (Object)((member2 is Object) ? member2 : null);
					if (!(val3 == (Object)null))
					{
						num2 += ProcessIdrs(val3, loadAsync);
					}
				}
			}
			catch (Exception ex)
			{
				log.LogDebug((object)(text + ": " + ex.Message));
			}
		}
		log.LogInfo((object)("ItemDisplayFix: " + prefabs.Count + " model item ditemukan, " + num2 + " aturan display diperbaiki."));
	}

	private static int ProcessIdrs(Object idrs, MethodInfo loadAsync)
	{
		int num = 0;
		if (!(GetMember(idrs, "keyAssetRuleGroups") is Array array))
		{
			return 0;
		}
		for (int i = 0; i < array.Length; i++)
		{
			object value = array.GetValue(i);
			object member = GetMember(value, "displayRuleGroup");
			if (member == null || !(GetMember(member, "rules") is Array array2))
			{
				continue;
			}
			for (int j = 0; j < array2.Length; j++)
			{
				object value2 = array2.GetValue(j);
				FieldInfo field = value2.GetType().GetField("followerPrefab");
				if (field == null)
				{
					continue;
				}
				object? value3 = field.GetValue(value2);
				GameObject val = (GameObject)((value3 is GameObject) ? value3 : null);
				if ((Object)(object)val == (Object)null && loadAsync != null)
				{
					object obj = FindAddressKey(value2);
					if (obj != null)
					{
						try
						{
							object obj2 = loadAsync.Invoke(null, new object[1] { obj });
							MethodInfo method = obj2.GetType().GetMethod("WaitForCompletion");
							object? obj3 = method.Invoke(obj2, null);
							val = (GameObject)((obj3 is GameObject) ? obj3 : null);
						}
						catch
						{
							val = null;
						}
						if ((Object)(object)val != (Object)null)
						{
							field.SetValue(value2, val);
							array2.SetValue(value2, j);
							num++;
						}
					}
				}
				if ((Object)(object)val != (Object)null && !prefabs.ContainsKey(((Object)val).name))
				{
					prefabs[((Object)val).name] = val;
				}
			}
		}
		return num;
	}

	private static object FindAddressKey(object rule)
	{
		FieldInfo[] fields = rule.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		foreach (FieldInfo fieldInfo in fields)
		{
			if (fieldInfo.FieldType == typeof(GameObject))
			{
				continue;
			}
			object value = fieldInfo.GetValue(rule);
			if (value == null)
			{
				continue;
			}
			Type type = value.GetType();
			bool flag = false;
			Type type2 = type;
			while (type2 != null)
			{
				if (type2.Name == "AssetReference")
				{
					flag = true;
					break;
				}
				type2 = type2.BaseType;
			}
			if (flag)
			{
				PropertyInfo property = type.GetProperty("RuntimeKey");
				object obj = ((property != null) ? property.GetValue(value, null) : null);
				MethodInfo method = type.GetMethod("RuntimeKeyIsValid", Type.EmptyTypes);
				if ((!(method != null) || (bool)method.Invoke(value, null)) && obj != null && obj.ToString().Length > 0)
				{
					return obj;
				}
			}
			else if (fieldInfo.FieldType == typeof(string) && fieldInfo.Name.ToLowerInvariant().Contains("address"))
			{
				string text = value as string;
				if (!string.IsNullOrEmpty(text))
				{
					return text;
				}
			}
		}
		return null;
	}

	private static object GetMember(object o, string name)
	{
		if (o == null)
		{
			return null;
		}
		Type type = o.GetType();
		FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		if (field != null)
		{
			return field.GetValue(o);
		}
		PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		if (!(property != null))
		{
			return null;
		}
		return property.GetValue(o, null);
	}
}