using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.IO.Compression; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using BepInEx; using UnityEngine; using UnityEngine.Rendering; [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: AssemblyVersion("0.0.0.0")] namespace HtfModelKit; public abstract class AssetSource { private sealed class DiskSource : AssetSource { private readonly string _dir; public override string Describe => _dir; public DiskSource(string dir) { _dir = dir; } public override byte[] Read(string relative) { if (string.IsNullOrEmpty(_dir)) { return null; } try { string path = _dir + "/" + relative; return (!File.Exists(path)) ? null : File.ReadAllBytes(path); } catch (Exception e) { Kit.Ex("AssetSource.Folder(" + relative + ")", e); return null; } } } private sealed class ResourceSource : AssetSource { private readonly Assembly _asm; private readonly string _prefix; public override string Describe => "built into " + ((!(_asm == null)) ? _asm.GetName().Name : "?"); public ResourceSource(Assembly asm, string prefix) { _asm = asm; _prefix = prefix ?? ""; } public override byte[] Read(string relative) { if (_asm == null) { return null; } try { string name = _prefix + relative.Replace('/', '.'); using Stream stream = _asm.GetManifestResourceStream(name); if (stream == null) { return null; } byte[] 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; } } return array; } catch (Exception e) { Kit.Ex("AssetSource.Embedded(" + relative + ")", e); return null; } } } private sealed class Chain : AssetSource { private readonly AssetSource _first; private readonly AssetSource _second; public override string Describe => _first.Describe + " then " + _second.Describe; public Chain(AssetSource a, AssetSource b) { _first = a; _second = b; } public override byte[] Read(string relative) { byte[] array = _first.Read(relative); return array ?? _second.Read(relative); } public override byte[] ReadOrInflate(string relative) { byte[] array = _first.ReadOrInflate(relative); return array ?? _second.ReadOrInflate(relative); } } public abstract string Describe { get; } public abstract byte[] Read(string relative); public AssetSource Then(AssetSource fallback) { return (fallback != null) ? new Chain(this, fallback) : this; } public static AssetSource Folder(string path) { return new DiskSource(path); } public static AssetSource Embedded(Assembly assembly, string prefix) { return new ResourceSource(assembly, prefix); } public virtual byte[] ReadOrInflate(string relative) { byte[] array = Read(relative); if (array != null) { return array; } return Inflate(Read(relative + ".gz")); } public string ReadText(string relative) { byte[] array = ReadOrInflate(relative); if (array == null) { return null; } try { return new UTF8Encoding(encoderShouldEmitUTF8Identifier: false).GetString(array); } catch (Exception e) { Kit.Ex("AssetSource.ReadText(" + relative + ")", e); return null; } } public bool Has(string relative) { return ReadOrInflate(relative) != null; } internal static byte[] Inflate(byte[] compressed) { if (compressed == null) { return null; } try { using MemoryStream stream = new MemoryStream(compressed); using GZipStream gZipStream = new GZipStream(stream, CompressionMode.Decompress); using MemoryStream memoryStream = new MemoryStream(); byte[] array = new byte[16384]; int count; while ((count = gZipStream.Read(array, 0, array.Length)) > 0) { memoryStream.Write(array, 0, count); } return memoryStream.ToArray(); } catch (Exception ex) { Kit.Warn("could not decompress an asset: " + ex.Message); return null; } } } public static class Kit { public const string Tag = "[ModelKit] "; private static Action _info; private static Action _warn; public static void UseLogger(Action info, Action warn) { _info = info; _warn = warn; } public static void Info(string message) { try { if (_info != null) { _info(message); } else { Debug.Log((object)("[ModelKit] " + message)); } } catch { } } public static void Warn(string message) { try { if (_warn != null) { _warn(message); } else { Debug.LogWarning((object)("[ModelKit] " + message)); } } catch { } } public static void Ex(string where, Exception e) { try { Warn(where + " failed: " + ((e != null) ? (e.GetType().Name + ": " + e.Message) : "(no detail)")); } catch { } } } public static class Materials { private static readonly string[] PlainShaders = new string[6] { "Universal Render Pipeline/Lit", "Universal Render Pipeline/Simple Lit", "Universal Render Pipeline/Baked Lit", "Universal Render Pipeline/Unlit", "Standard", "Legacy Shaders/Diffuse" }; private static Shader _plain; private static bool _plainTried; public static Shader PlainShader() { if (_plainTried) { return _plain; } _plainTried = true; for (int i = 0; i < PlainShaders.Length; i++) { if (!((Object)(object)_plain == (Object)null)) { break; } try { _plain = Shader.Find(PlainShaders[i]); } catch { } } if ((Object)(object)_plain == (Object)null) { Kit.Warn("no plain lit shader was found in this build; materials will be cloned as-is"); } return _plain; } public static bool IsPlainShader(Shader s) { if ((Object)(object)s == (Object)null) { return false; } for (int i = 0; i < PlainShaders.Length; i++) { if (((Object)s).name == PlainShaders[i]) { return true; } } return false; } public static Material FindTemplate(GameObject drawnByTheGame) { //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Expected O, but got Unknown Material val = null; try { if ((Object)(object)drawnByTheGame != (Object)null) { Renderer[] componentsInChildren = drawnByTheGame.GetComponentsInChildren(true); foreach (Renderer val2 in componentsInChildren) { Material val3 = ((!((Object)(object)val2 == (Object)null)) ? val2.sharedMaterial : null); if (!((Object)(object)val3 == (Object)null)) { if ((Object)(object)val == (Object)null) { val = val3; } if (IsPlainShader(val3.shader)) { return val3; } } } } } catch (Exception e) { Kit.Ex("Materials.FindTemplate", e); } Shader val4 = PlainShader(); if ((Object)(object)val4 != (Object)null) { Kit.Info("no plain-shaded material to copy, so one was built from '" + ((Object)val4).name + "'"); return new Material(val4); } return val; } public static Material[] Build(ModelData model, Material template, bool calm) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected O, but got Unknown //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) if (model == null || model.MaterialNames == null) { return (Material[])(object)new Material[0]; } if ((Object)(object)template == (Object)null) { Shader val = PlainShader(); if ((Object)(object)val == (Object)null) { Kit.Warn("no template and no plain shader; cannot build materials"); return (Material[])(object)new Material[0]; } template = new Material(val); } Material[] array = (Material[])(object)new Material[model.MaterialNames.Length]; for (int i = 0; i < array.Length; i++) { Material val2 = new Material(template); ((Object)val2).name = "ModelKit_" + model.MaterialNames[i]; if (calm) { Calm(val2); } if ((Object)(object)model.Textures[i] != (Object)null) { SetTexture(val2, (Texture)(object)model.Textures[i]); SetColor(val2, Color.white); if (model.Cutout[i]) { EnableCutout(val2); } } else { SetTexture(val2, (Texture)(object)Texture2D.whiteTexture); SetColor(val2, model.Colors[i]); } array[i] = val2; Texture val3 = (Texture)(object)model.Textures[i]; Kit.Info(" material " + (i + 1) + "/" + array.Length + " '" + model.MaterialNames[i] + "': " + ((!((Object)(object)val3 != (Object)null)) ? ("NO TEXTURE - drawn flat in " + ColorHex(model.Colors[i])) : ("texture " + ((Object)val3).name + " " + val3.width + "x" + val3.height)) + ((!model.Cutout[i]) ? "" : " (cutout)")); } return array; } public static void SetTexture(Material m, Texture tex) { if (!((Object)(object)m == (Object)null)) { if (m.HasProperty("_BaseMap")) { m.SetTexture("_BaseMap", tex); } if (m.HasProperty("_MainTex")) { m.SetTexture("_MainTex", tex); } m.mainTexture = tex; } } public static void SetColor(Material m, Color c) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)m == (Object)null)) { if (m.HasProperty("_BaseColor")) { m.SetColor("_BaseColor", c); } if (m.HasProperty("_Color")) { m.SetColor("_Color", c); } } } public static void Calm(Material m) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)m == (Object)null) { return; } try { m.DisableKeyword("_EMISSION"); if (m.HasProperty("_EmissionColor")) { m.SetColor("_EmissionColor", Color.black); } if (m.HasProperty("_EmissionMap")) { m.SetTexture("_EmissionMap", (Texture)null); } m.globalIlluminationFlags = (MaterialGlobalIlluminationFlags)4; if (m.HasProperty("_MainTex")) { m.SetTextureOffset("_MainTex", Vector2.zero); } if (m.HasProperty("_BaseMap")) { m.SetTextureOffset("_BaseMap", Vector2.zero); } if (m.HasProperty("_Metallic")) { m.SetFloat("_Metallic", 0f); } if (m.HasProperty("_Smoothness")) { m.SetFloat("_Smoothness", 0.1f); } if (m.HasProperty("_Glossiness")) { m.SetFloat("_Glossiness", 0.1f); } string[] array = new string[9] { "_BumpMap", "_NormalMap", "_MetallicGlossMap", "_SpecGlossMap", "_ParallaxMap", "_OcclusionMap", "_DetailAlbedoMap", "_DetailNormalMap", "_DetailMask" }; for (int i = 0; i < array.Length; i++) { if (m.HasProperty(array[i])) { m.SetTexture(array[i], (Texture)null); } } } catch (Exception e) { Kit.Ex("Materials.Calm", e); } } public static void EnableCutout(Material m) { if ((Object)(object)m == (Object)null) { return; } try { m.EnableKeyword("_ALPHATEST_ON"); if (m.HasProperty("_Cutoff")) { m.SetFloat("_Cutoff", 0.5f); } if (m.HasProperty("_AlphaClip")) { m.SetFloat("_AlphaClip", 1f); } if (m.HasProperty("_Surface")) { m.SetFloat("_Surface", 0f); } m.renderQueue = 2450; } catch (Exception e) { Kit.Ex("Materials.EnableCutout", e); } } public static void Describe(Material m) { try { if ((Object)(object)m == (Object)null || (Object)(object)m.shader == (Object)null) { Kit.Info("no template material"); return; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("template shader '").Append(((Object)m.shader).name).Append("'"); if (m.HasProperty("_Metallic")) { stringBuilder.Append(", metallic ").Append(m.GetFloat("_Metallic").ToString("0.##")); } if (m.HasProperty("_Smoothness")) { stringBuilder.Append(", smoothness ").Append(m.GetFloat("_Smoothness").ToString("0.##")); } if (m.HasProperty("_BumpMap") && (Object)(object)m.GetTexture("_BumpMap") != (Object)null) { stringBuilder.Append(", bump map"); } if (m.HasProperty("_MetallicGlossMap") && (Object)(object)m.GetTexture("_MetallicGlossMap") != (Object)null) { stringBuilder.Append(", metallic map"); } if (m.IsKeywordEnabled("_EMISSION")) { stringBuilder.Append(", emission ON"); } Kit.Info(stringBuilder.ToString()); } catch (Exception e) { Kit.Ex("Materials.Describe", e); } } private static string ColorHex(Color c) { return "#" + Mathf.RoundToInt(c.r * 255f).ToString("X2") + Mathf.RoundToInt(c.g * 255f).ToString("X2") + Mathf.RoundToInt(c.b * 255f).ToString("X2"); } } public sealed class ModelData { public Mesh Mesh; public string[] MaterialNames; public Texture2D[] Textures; public Color[] Colors; public bool[] Cutout; public string Source; public int SubMeshCount => (MaterialNames != null) ? MaterialNames.Length : 0; } public static class ModelKit { public const string Version = "1.0.1"; private static readonly Dictionary _cache = new Dictionary(); public static void UseLogger(Action info, Action warn) { Kit.UseLogger(info, warn); } public static ModelData Load(string objName, AssetSource source) { return Load(objName, source, null); } public static ModelData Load(string objName, AssetSource source, TextureOptions textures) { if (string.IsNullOrEmpty(objName)) { return null; } if (_cache.TryGetValue(objName, out var value)) { return value; } ModelData modelData = ObjParser.Load(objName, source, textures); _cache[objName] = modelData; return modelData; } public static void Forget(string objName) { if (objName == null) { _cache.Clear(); } else { _cache.Remove(objName); } } public static bool Available(string objName, AssetSource source) { return source?.Has(objName) ?? false; } public static GameObject[] Attach(GameObject target, ModelData model, Material template) { return Attach(target, model, template, calmMaterials: true); } public static GameObject[] Attach(GameObject target, ModelData model, Material template, bool calmMaterials) { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Expected O, but got Unknown //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)target == (Object)null || model == null || (Object)(object)model.Mesh == (Object)null) { return (GameObject[])(object)new GameObject[0]; } try { if ((Object)(object)template == (Object)null) { template = Materials.FindTemplate(target); } Material[] array = Materials.Build(model, template, calmMaterials); if (array.Length == 0) { return (GameObject[])(object)new GameObject[0]; } GameObject val = new GameObject("ModelKit_" + ((Object)model.Mesh).name); val.transform.SetParent(target.transform, false); val.transform.localPosition = Vector3.zero; val.transform.localRotation = Quaternion.identity; val.transform.localScale = Vector3.one; val.layer = target.layer; MeshFilter val2 = val.AddComponent(); val2.sharedMesh = model.Mesh; MeshRenderer val3 = val.AddComponent(); ((Renderer)val3).sharedMaterials = array; return (GameObject[])(object)new GameObject[1] { val }; } catch (Exception e) { Kit.Ex("ModelKit.Attach", e); return (GameObject[])(object)new GameObject[0]; } } public static GameObject[] Replace(GameObject target, string objName, AssetSource source) { return Replace(target, objName, source, null, calmMaterials: true); } public static GameObject[] Replace(GameObject target, string objName, AssetSource source, TextureOptions textures, bool calmMaterials) { if ((Object)(object)target == (Object)null) { return (GameObject[])(object)new GameObject[0]; } ModelData modelData = Load(objName, source, textures); if (modelData == null) { return (GameObject[])(object)new GameObject[0]; } try { Material template = Materials.FindTemplate(target); GameObject[] array = Attach(target, modelData, template, calmMaterials); if (array.Length == 0) { return array; } Hide(target, array); return array; } catch (Exception e) { Kit.Ex("ModelKit.Replace(" + objName + ")", e); return (GameObject[])(object)new GameObject[0]; } } public static void Hide(GameObject target, GameObject[] keep) { if ((Object)(object)target == (Object)null) { return; } try { Renderer[] componentsInChildren = target.GetComponentsInChildren(true); foreach (Renderer val in componentsInChildren) { if ((Object)(object)val == (Object)null) { continue; } bool flag = false; if (keep != null) { for (int j = 0; j < keep.Length; j++) { if (flag) { break; } if ((Object)(object)keep[j] != (Object)null && ((Component)val).transform.IsChildOf(keep[j].transform)) { flag = true; } } } if (!flag) { val.enabled = false; } } } catch (Exception e) { Kit.Ex("ModelKit.Hide", e); } } public static void Place(GameObject[] parts, float scale, float yaw, float heightOffset) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) if (parts == null) { return; } for (int i = 0; i < parts.Length; i++) { if (!((Object)(object)parts[i] == (Object)null)) { try { Transform transform = parts[i].transform; transform.localScale = Vector3.one * ((!(scale <= 0f)) ? scale : 1f); transform.localRotation = Quaternion.Euler(0f, yaw, 0f); transform.localPosition = new Vector3(0f, heightOffset, 0f); } catch (Exception e) { Kit.Ex("ModelKit.Place", e); } } } } public static Bounds BoundsOf(GameObject[] parts) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) Bounds bounds = default(Bounds); ((Bounds)(ref bounds))..ctor(Vector3.zero, Vector3.zero); bool flag = false; if (parts == null) { return bounds; } foreach (GameObject val in parts) { if ((Object)(object)val == (Object)null) { continue; } Renderer[] componentsInChildren = val.GetComponentsInChildren(true); foreach (Renderer val2 in componentsInChildren) { if (!((Object)(object)val2 == (Object)null)) { if (!flag) { bounds = val2.bounds; flag = true; } else { ((Bounds)(ref bounds)).Encapsulate(val2.bounds); } } } } return bounds; } } public static class ObjParser { private static readonly char[] Space = new char[2] { ' ', '\t' }; private static readonly char[] Slashes = new char[2] { '/', '\\' }; private static float F(string s) { float result; return (!float.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out result)) ? 0f : result; } private static int Index(string s, int count) { if (!int.TryParse(s, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { return -1; } if (result > 0) { return result - 1; } if (result < 0) { return count + result; } return -1; } public static ModelData Load(string objName, AssetSource source, TextureOptions textures) { if (source == null) { Kit.Warn("no asset source was given for '" + objName + "'"); return null; } string text = source.ReadText(objName); if (text == null) { Kit.Info("'" + objName + "' was not found in " + source.Describe); return null; } try { return Parse(text, objName, source, textures ?? TextureOptions.Default); } catch (Exception e) { Kit.Ex("ObjParser.Load(" + objName + ")", e); return null; } } private static ModelData Parse(string text, string objName, AssetSource source, TextureOptions texOpts) { //IL_02a0: Unknown result type (might be due to invalid IL or missing references) //IL_02a7: Expected O, but got Unknown //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_0447: Unknown result type (might be due to invalid IL or missing references) //IL_044c: Unknown result type (might be due to invalid IL or missing references) List list = new List(); List list2 = new List(); List list3 = new List(); List list4 = new List(); List list5 = new List(); List list6 = new List(); Dictionary lookup = new Dictionary(); List> list7 = new List>(); List list8 = new List(); List list9 = null; string text2 = null; bool missingNormals = false; string[] array = text.Split('\n'); foreach (string text3 in array) { string text4 = text3.Trim(); if (text4.Length == 0 || text4[0] == '#') { continue; } string[] array2 = text4.Split(Space, StringSplitOptions.RemoveEmptyEntries); if (array2.Length == 0) { continue; } switch (array2[0]) { case "v": if (array2.Length >= 4) { list.Add(new Vector3(0f - F(array2[1]), F(array2[2]), F(array2[3]))); } break; case "vn": if (array2.Length >= 4) { list2.Add(new Vector3(0f - F(array2[1]), F(array2[2]), F(array2[3]))); } break; case "vt": if (array2.Length >= 3) { list3.Add(new Vector2(F(array2[1]), F(array2[2]))); } break; case "mtllib": if (array2.Length >= 2) { text2 = array2[1]; } break; case "usemtl": { string item = ((array2.Length < 2) ? "default" : array2[1]); int num = list8.IndexOf(item); if (num >= 0) { list9 = list7[num]; break; } list9 = new List(); list7.Add(list9); list8.Add(item); break; } case "f": if (list9 == null) { list9 = new List(); list7.Add(list9); list8.Add("default"); } AddFace(array2, list, list2, list3, list4, list5, list6, lookup, list9, ref missingNormals); break; } } if (list4.Count == 0 || list7.Count == 0) { Kit.Warn("'" + objName + "' has no faces that can be drawn"); return null; } Mesh val = new Mesh(); ((Object)val).name = objName; val.indexFormat = (IndexFormat)(list4.Count > 65000); val.SetVertices(list4); bool flag = !missingNormals && list5.Count == list4.Count; if (flag) { val.SetNormals(list5); } if (list6.Count == list4.Count) { val.SetUVs(0, list6); } List list10 = new List(); for (int j = 0; j < list7.Count; j++) { if (list7[j].Count > 0) { list10.Add(j); } } val.subMeshCount = list10.Count; string[] array3 = new string[list10.Count]; for (int k = 0; k < list10.Count; k++) { val.SetTriangles(list7[list10[k]], k); array3[k] = list8[list10[k]]; } if (!flag) { val.RecalculateNormals(); } val.RecalculateBounds(); ModelData modelData = new ModelData(); modelData.Mesh = val; modelData.MaterialNames = array3; modelData.Textures = (Texture2D[])(object)new Texture2D[array3.Length]; modelData.Colors = (Color[])(object)new Color[array3.Length]; modelData.Cutout = new bool[array3.Length]; modelData.Source = source.Describe; ModelData modelData2 = modelData; for (int l = 0; l < array3.Length; l++) { ref Color reference = ref modelData2.Colors[l]; reference = Color.white; } if (text2 != null) { ApplyMtl(modelData2, text2, objName, source, texOpts); } Kit.Info("loaded '" + objName + "': " + list4.Count + " vertices, " + val.triangles.Length / 3 + " triangles, " + array3.Length + " material(s), from " + source.Describe + ((!flag) ? " (normals calculated - the file did not supply one for every vertex)" : "")); return modelData2; } private static void AddFace(string[] p, List positions, List normals, List uvs, List outPos, List outNrm, List outUv, Dictionary lookup, List tris, ref bool missingNormals) { //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) int num = p.Length - 1; if (num < 3) { return; } int[] array = new int[num]; for (int i = 0; i < num; i++) { string text = p[i + 1]; if (!lookup.TryGetValue(text, out var value)) { string[] array2 = text.Split('/'); int num2 = Index(array2[0], positions.Count); if (num2 < 0 || num2 >= positions.Count) { return; } int num3 = ((array2.Length <= 1 || array2[1].Length <= 0) ? (-1) : Index(array2[1], uvs.Count)); int num4 = ((array2.Length <= 2 || array2[2].Length <= 0) ? (-1) : Index(array2[2], normals.Count)); outPos.Add(positions[num2]); outUv.Add((num3 < 0 || num3 >= uvs.Count) ? Vector2.zero : uvs[num3]); if (num4 >= 0 && num4 < normals.Count) { outNrm.Add(normals[num4]); } else { outNrm.Add(Vector3.zero); missingNormals = true; } value = (lookup[text] = outPos.Count - 1); } array[i] = value; } for (int j = 1; j < num - 1; j++) { tris.Add(array[0]); tris.Add(array[j + 1]); tris.Add(array[j]); } } private static void ApplyMtl(ModelData model, string mtlName, string objName, AssetSource source, TextureOptions texOpts) { //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: 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) string text = source.ReadText(mtlName); if (text == null) { Kit.Info("'" + objName + "' names '" + mtlName + "' but it is not there; using plain colours"); return; } Dictionary dictionary = new Dictionary(); Dictionary dictionary2 = new Dictionary(); string text2 = null; string[] array = text.Split('\n'); foreach (string text3 in array) { string text4 = text3.Trim(); if (text4.Length == 0 || text4[0] == '#') { continue; } string[] array2 = text4.Split(Space, StringSplitOptions.RemoveEmptyEntries); if (array2.Length < 2) { continue; } if (array2[0] == "newmtl") { text2 = array2[1]; } else { if (text2 == null) { continue; } if (array2[0] == "map_Kd") { string text5 = array2[^1]; int num = text5.LastIndexOfAny(Slashes); if (num >= 0) { text5 = text5.Substring(num + 1); } dictionary[text2] = text5; } else if (array2[0] == "Kd" && array2.Length >= 4) { dictionary2[text2] = new Color(F(array2[1]), F(array2[2]), F(array2[3]), 1f); } } } Dictionary dictionary3 = new Dictionary(); for (int j = 0; j < model.MaterialNames.Length; j++) { string key = model.MaterialNames[j]; if (dictionary2.TryGetValue(key, out var value)) { model.Colors[j] = value; } if (!dictionary.TryGetValue(key, out var value2)) { continue; } if (!dictionary3.TryGetValue(value2, out var value3)) { value3 = TextureLoader.Load(value2, source, texOpts); if ((Object)(object)value3 == (Object)null) { Kit.Warn("the model asks for texture '" + value2 + "' but it could not be loaded - that surface will be drawn as a flat colour."); } dictionary3[value2] = value3; } model.Textures[j] = value3; if ((Object)(object)value3 != (Object)null) { model.Cutout[j] = TextureLoader.HasHoles(value3); } } } } public sealed class TextureOptions { public bool PointFilter = true; public TextureWrapMode Wrap = (TextureWrapMode)0; public bool Mipmaps = true; public byte CutoutThreshold = 250; public static TextureOptions Default => new TextureOptions(); } public static class TextureLoader { public static Texture2D Load(string relative, AssetSource source, TextureOptions options) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown //IL_0085: Unknown result type (might be due to invalid IL or missing references) if (source == null) { return null; } options = options ?? TextureOptions.Default; byte[] array = source.ReadOrInflate(relative); if (array == null) { return null; } try { Texture2D val = new Texture2D(2, 2, (TextureFormat)4, options.Mipmaps); if (!ImageConversion.LoadImage(val, array)) { Object.Destroy((Object)(object)val); Kit.Warn("'" + relative + "' is not an image that can be read"); return null; } ((Object)val).name = relative; ((Texture)val).filterMode = (FilterMode)(!options.PointFilter); ((Texture)val).wrapMode = options.Wrap; val.Apply(options.Mipmaps, false); return val; } catch (Exception e) { Kit.Ex("TextureLoader.Load(" + relative + ")", e); return null; } } public static bool HasHoles(Texture2D tex) { return HasHoles(tex, 250); } public static bool HasHoles(Texture2D tex, byte threshold) { if ((Object)(object)tex == (Object)null) { return false; } try { Color32[] pixels = tex.GetPixels32(); for (int i = 0; i < pixels.Length; i++) { if (pixels[i].a < threshold) { return true; } } return false; } catch { return false; } } } [BepInPlugin("com.htf.modelkit", "How To Fish - ModelKit", "1.0.1")] public class ModelKitPlugin : BaseUnityPlugin { public const string Guid = "com.htf.modelkit"; private void Awake() { ModelKit.UseLogger(delegate(string m) { ((BaseUnityPlugin)this).Logger.LogInfo((object)m); }, delegate(string m) { ((BaseUnityPlugin)this).Logger.LogWarning((object)m); }); ((BaseUnityPlugin)this).Logger.LogInfo((object)"ModelKit 1.0.1 ready. This is a library: it does nothing on its own."); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Mods use it to load OBJ/MTL/PNG models without an AssetBundle, so a game update cannot invalidate their art."); } }