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.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Logging; using HG.Coroutines; using HG.Reflection; using IDRSJsonLoader.Models; using R2API; using RoR2; using UnityEngine; using UnityEngine.AddressableAssets; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: OptIn] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.0.0")] [module: UnverifiableCode] namespace IDRSJsonLoader { public delegate void ParseCallback(ItemDisplayRuleSet idrs); [BepInPlugin("com.KingEnderBrine.IDRSJsonLoader", "IDRS Json Loader", "1.0.3")] public class IDRSJsonLoaderPlugin : BaseUnityPlugin { public const string Guid = "com.KingEnderBrine.IDRSJsonLoader"; public const string Name = "IDRS Json Loader"; public const string Version = "1.0.3"; private static readonly List delayedReplacementInfos = new List(); internal static IDRSJsonLoaderPlugin Instance { get; private set; } internal static ManualLogSource InstanceLogger { get { IDRSJsonLoaderPlugin instance = Instance; if (instance == null) { return null; } return ((BaseUnityPlugin)instance).Logger; } } private void Awake() { Instance = this; string[] files = Directory.GetFiles(Paths.PluginPath, "*.idrsjson", SearchOption.AllDirectories); foreach (string text in files) { try { ParseAndUpdate(File.ReadAllText(text), null); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Couldn't load file " + text)); ((BaseUnityPlugin)this).Logger.LogError((object)ex); } } } public static void ParseAndUpdate(string json, ParseCallback beforeGenerateRuntimeValues) { DelayedReplacementInfo item = new DelayedReplacementInfo { exportIdrs = JsonUtility.FromJson(json), beforeGenerateRuntimeValues = beforeGenerateRuntimeValues }; delayedReplacementInfos.Add(item); } public static void ParseAndUpdate(string json, ItemDisplayRuleSet idrs, ParseCallback beforeGenerateRuntimeValues) { DelayedReplacementInfo item = new DelayedReplacementInfo { exportIdrs = JsonUtility.FromJson(json), idrs = idrs, beforeGenerateRuntimeValues = beforeGenerateRuntimeValues }; delayedReplacementInfos.Add(item); } [SystemInitializer(new Type[] { typeof(ItemDisplayRuleSet), typeof(BodyCatalog), typeof(SkinCatalog) })] internal static IEnumerator Init() { Dictionary assetBundles = AssetBundle.GetAllLoadedAssetBundles().ToDictionary((AssetBundle b) => ((Object)b).name); Dictionary dictionary = new Dictionary(); Dictionary dictionary2 = new Dictionary(); AssetsInfo value2; foreach (DelayedReplacementInfo delayedReplacementInfo in delayedReplacementInfos) { ExportItemDisplayRuleSet exportIdrs = delayedReplacementInfo.exportIdrs; if (!Object.op_Implicit((Object)(object)delayedReplacementInfo.idrs) && !string.IsNullOrEmpty(exportIdrs.bodyName)) { GameObject val = BodyCatalog.FindBodyPrefab(exportIdrs.bodyName); if (Object.op_Implicit((Object)(object)val)) { CharacterBody component = val.GetComponent(); ModelLocator component2 = val.GetComponent(); object obj; if (component2 == null) { obj = null; } else { Transform modelTransform = component2.modelTransform; obj = ((modelTransform != null) ? ((Component)modelTransform).GetComponent() : null); } CharacterModel val2 = (CharacterModel)obj; if (Object.op_Implicit((Object)(object)val2)) { delayedReplacementInfo.characterModel = val2; if (!Object.op_Implicit((Object)(object)val2.itemDisplayRuleSet)) { val2.itemDisplayRuleSet = ScriptableObject.CreateInstance(); } if (!string.IsNullOrEmpty(exportIdrs.skinName)) { SkinDef[] bodySkinDefs = SkinCatalog.GetBodySkinDefs(component.bodyIndex); delayedReplacementInfo.skinDef = ((IEnumerable)bodySkinDefs).FirstOrDefault((Func)((SkinDef s) => ((Object)s).name == exportIdrs.skinName)); } else { delayedReplacementInfo.idrs = val2.itemDisplayRuleSet; } } } } AssetsInfo value = null; if (Object.op_Implicit((Object)(object)delayedReplacementInfo.idrs)) { if (!dictionary.TryGetValue(delayedReplacementInfo.idrs, out value)) { value = (dictionary[delayedReplacementInfo.idrs] = new AssetsInfo()); } } else if (Object.op_Implicit((Object)(object)delayedReplacementInfo.skinDef) && !dictionary2.TryGetValue(delayedReplacementInfo.skinDef, out value)) { value = (dictionary2[delayedReplacementInfo.skinDef] = new AssetsInfo()); } if (value == null) { continue; } value2 = value; if (value2.characterModel == null) { value2.characterModel = delayedReplacementInfo.characterModel; } for (int num = 0; num < exportIdrs.itemGroups.Count; num++) { ExportKeyAssetRuleGroup exportKeyAssetRuleGroup = exportIdrs.itemGroups[num]; ItemDef itemDef = ItemCatalog.GetItemDef(ItemCatalog.FindItemIndex(exportKeyAssetRuleGroup.name)); if (Object.op_Implicit((Object)(object)itemDef)) { value.groups[(Object)(object)itemDef] = exportKeyAssetRuleGroup; } } for (int num2 = 0; num2 < exportIdrs.equipmentGroups.Count; num2++) { ExportKeyAssetRuleGroup exportKeyAssetRuleGroup2 = exportIdrs.equipmentGroups[num2]; EquipmentDef equipmentDef = EquipmentCatalog.GetEquipmentDef(EquipmentCatalog.FindEquipmentIndex(exportKeyAssetRuleGroup2.name)); if (Object.op_Implicit((Object)(object)equipmentDef)) { value.groups[(Object)(object)equipmentDef] = exportKeyAssetRuleGroup2; } } } ParallelCoroutine assetsCoroutine = null; Object key2; ExportKeyAssetRuleGroup value4; foreach (KeyValuePair item in dictionary) { item.Deconstruct(out var key, out value2); ItemDisplayRuleSet val3 = key; AssetsInfo assetsInfo3 = value2; HashSet addedChildren = new HashSet(0); for (int num3 = 0; num3 < val3.keyAssetRuleGroups.Length; num3++) { Object keyAsset = val3.keyAssetRuleGroups[num3].keyAsset; if (!Object.op_Implicit(keyAsset) || !assetsInfo3.groups.Remove(keyAsset, out var value3)) { continue; } IEnumerator enumerator3 = MapGroup(val3, num3, keyAsset, value3, assetBundles, assetsInfo3.characterModel, addedChildren); if (enumerator3.MoveNext()) { if (assetsCoroutine == null) { assetsCoroutine = new ParallelCoroutine(); } assetsCoroutine.Add(enumerator3); } } if (assetsInfo3.groups.Count == 0) { continue; } int num4 = val3.keyAssetRuleGroups.Length; Array.Resize(ref val3.keyAssetRuleGroups, val3.keyAssetRuleGroups.Length + assetsInfo3.groups.Count); foreach (KeyValuePair group in assetsInfo3.groups) { group.Deconstruct(out key2, out value4); Object keyAsset2 = key2; ExportKeyAssetRuleGroup exportGroup = value4; IEnumerator enumerator5 = MapGroup(val3, num4++, keyAsset2, exportGroup, assetBundles, assetsInfo3.characterModel, addedChildren); if (enumerator5.MoveNext()) { if (assetsCoroutine == null) { assetsCoroutine = new ParallelCoroutine(); } assetsCoroutine.Add(enumerator5); } } } foreach (KeyValuePair item2 in dictionary2) { item2.Deconstruct(out var key3, out value2); SkinDef skinDef = key3; AssetsInfo assetsInfo4 = value2; if (assetsInfo4.groups.Count == 0) { continue; } HashSet addedChildren2 = new HashSet(0); foreach (KeyValuePair group2 in assetsInfo4.groups) { group2.Deconstruct(out key2, out value4); Object keyAsset3 = key2; ExportKeyAssetRuleGroup exportGroup2 = value4; IEnumerator enumerator7 = MapGroup(skinDef, keyAsset3, exportGroup2, assetBundles, assetsInfo4.characterModel, addedChildren2); if (enumerator7.MoveNext()) { if (assetsCoroutine == null) { assetsCoroutine = new ParallelCoroutine(); } assetsCoroutine.Add(enumerator7); } } } if (assetsCoroutine != null) { while (assetsCoroutine.MoveNext()) { yield return null; } } ParallelCoroutine idrsCoroutine = new ParallelCoroutine(); foreach (DelayedReplacementInfo delayedReplacementInfo2 in delayedReplacementInfos) { if (Object.op_Implicit((Object)(object)delayedReplacementInfo2.idrs)) { delayedReplacementInfo2.beforeGenerateRuntimeValues?.Invoke(delayedReplacementInfo2.idrs); idrsCoroutine.Add(delayedReplacementInfo2.idrs.GenerateRuntimeValuesAsync()); } } while (idrsCoroutine.MoveNext()) { yield return null; } delayedReplacementInfos.Clear(); } private static IEnumerator MapGroup(ItemDisplayRuleSet idrs, int index, Object keyAsset, ExportKeyAssetRuleGroup exportGroup, Dictionary assetBundles, CharacterModel characterModel, HashSet addedChildren) { ParallelCoroutine coroutine = null; KeyAssetRuleGroup group = new KeyAssetRuleGroup { displayRuleGroup = { rules = (ItemDisplayRule[])(object)new ItemDisplayRule[exportGroup.rules.Count] }, keyAsset = keyAsset }; for (int i = 0; i < exportGroup.rules.Count; i++) { IEnumerator enumerator = MapRule(group.displayRuleGroup.rules, i, exportGroup.rules[i], assetBundles, characterModel, addedChildren); if (enumerator.MoveNext()) { if (coroutine == null) { coroutine = new ParallelCoroutine(); } coroutine.Add(enumerator); } } if (coroutine != null) { while (coroutine.MoveNext()) { yield return null; } } idrs.keyAssetRuleGroups[index] = group; } private static IEnumerator MapGroup(SkinDef skinDef, Object keyAsset, ExportKeyAssetRuleGroup exportGroup, Dictionary assetBundles, CharacterModel characterModel, HashSet addedChildren) { ParallelCoroutine coroutine = null; DisplayRuleGroup group = new DisplayRuleGroup { rules = (ItemDisplayRule[])(object)new ItemDisplayRule[exportGroup.rules.Count] }; for (int i = 0; i < exportGroup.rules.Count; i++) { IEnumerator enumerator = MapRule(group.rules, i, exportGroup.rules[i], assetBundles, characterModel, addedChildren); if (enumerator.MoveNext()) { if (coroutine == null) { coroutine = new ParallelCoroutine(); } coroutine.Add(enumerator); } } if (coroutine != null) { while (coroutine.MoveNext()) { yield return null; } } SkinIDRS.AddGroupOverride(skinDef, keyAsset, group); } private static IEnumerator MapRule(ItemDisplayRule[] rules, int index, ExportItemDisplayRule exportRule, Dictionary assetBundles, CharacterModel characterModel, HashSet addedChildren) { string text = exportRule.childName; if (!string.IsNullOrEmpty(exportRule.childPath) && Object.op_Implicit((Object)(object)characterModel)) { text = StringHelpers.ChildNameFromPath(exportRule.childPath); if (!addedChildren.Contains(text)) { addedChildren.Add(text); Transform val = ((Component)characterModel).transform.Find(exportRule.childPath); if (Object.op_Implicit((Object)(object)val)) { ((Component)characterModel).GetComponent().AddChild(text, val); } } } ItemDisplayRule rule = new ItemDisplayRule { childName = text, followerPrefabAddress = new AssetReferenceGameObject(exportRule.guid ?? ""), limbMask = exportRule.limbMask, localAngles = exportRule.localAngles, localPos = exportRule.localPos, localScale = exportRule.localScale, ruleType = exportRule.ruleType }; if (!string.IsNullOrEmpty(exportRule.assetBundle)) { if (assetBundles.TryGetValue(exportRule.assetBundle, out var value)) { AssetBundleRequest request = value.LoadAssetAsync(exportRule.assetPath); while (!((AsyncOperation)request).isDone) { yield return null; } Object asset = request.asset; rule.followerPrefab = (GameObject)(object)((asset is GameObject) ? asset : null); } else { InstanceLogger.LogError((object)("AssetBundle " + exportRule.assetBundle + " was not found.")); } } rules[index] = rule; } } public static class StringHelpers { public static readonly string NamePostfix = "|IJL"; public static string ChildNameFromPath(string path) { string fileName = Path.GetFileName(path); return $"{fileName}_{(uint)GetStableHashCode(path)}{NamePostfix}"; } public static int GetStableHashCode(string str) { int num = 5381; int num2 = num; for (int i = 0; i < str.Length && str[i] != 0; i += 2) { num = ((num << 5) + num) ^ str[i]; if (i == str.Length - 1 || str[i + 1] == '\0') { break; } num2 = ((num2 << 5) + num2) ^ str[i + 1]; } return num + num2 * 1566083941; } } } namespace IDRSJsonLoader.Models { internal class AssetsInfo { public Dictionary groups = new Dictionary(); public CharacterModel characterModel; } internal class DelayedReplacementInfo { public ExportItemDisplayRuleSet exportIdrs; public ParseCallback beforeGenerateRuntimeValues; public ItemDisplayRuleSet idrs; public SkinDef skinDef; public CharacterModel characterModel; } [Serializable] public class ExportItemDisplayRule { public string guid; public string assetBundle; public string assetPath; public Vector3 localScale; public Vector3 localPos; public Vector3 localAngles; public string childName; public string childPath; public LimbFlags limbMask; public ItemDisplayRuleType ruleType; } [Serializable] public class ExportItemDisplayRuleSet { public string bodyName; public string skinName; public List itemGroups = new List(); public List equipmentGroups = new List(); } [Serializable] public class ExportKeyAssetRuleGroup { public string name; public List rules = new List(); } }