using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Serialization; using System.Runtime.Serialization.Json; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.Rendering; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("IcarusModelReplacement")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+2dd13133085a24341e5edad1e8f497dfec2a7318")] [assembly: AssemblyProduct("IcarusModelReplacement")] [assembly: AssemblyTitle("IcarusModelReplacement")] [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 IcarusModelReplacement { internal sealed class Model : IDisposable { private readonly PackInfo info; private readonly Transform[] bones; private readonly Vector3[] rest; private readonly Vector3[] positions; private readonly Vector3[] rotations; private readonly MaterialPropertyBlock lighting = new MaterialPropertyBlock(); private readonly SphericalHarmonicsL2[] probe = (SphericalHarmonicsL2[])(object)new SphericalHarmonicsL2[1]; private Material material; private Mesh mesh; private Texture2D texture; private SkinnedMeshRenderer renderer; private static readonly int Ambient0 = Shader.PropertyToID("_Global_AmbientColor0"); private static readonly int Ambient1 = Shader.PropertyToID("_Global_AmbientColor1"); private static readonly int Ambient2 = Shader.PropertyToID("_Global_AmbientColor2"); public GameObject Root { get; } public Model(ModelPack pack, Transform parent, int layer) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Expected O, but got Unknown //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_0273: Unknown result type (might be due to invalid IL or missing references) //IL_0290: Unknown result type (might be due to invalid IL or missing references) //IL_0295: Unknown result type (might be due to invalid IL or missing references) //IL_02ac: Unknown result type (might be due to invalid IL or missing references) //IL_02b1: Unknown result type (might be due to invalid IL or missing references) //IL_02bc: Unknown result type (might be due to invalid IL or missing references) //IL_0332: Unknown result type (might be due to invalid IL or missing references) //IL_0334: Unknown result type (might be due to invalid IL or missing references) //IL_034a: Unknown result type (might be due to invalid IL or missing references) //IL_034f: Unknown result type (might be due to invalid IL or missing references) //IL_0360: Unknown result type (might be due to invalid IL or missing references) //IL_036c: Expected O, but got Unknown //IL_03d7: Unknown result type (might be due to invalid IL or missing references) //IL_03dc: Unknown result type (might be due to invalid IL or missing references) //IL_03ed: Unknown result type (might be due to invalid IL or missing references) //IL_03f4: Unknown result type (might be due to invalid IL or missing references) //IL_0400: Expected O, but got Unknown //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_0421: Unknown result type (might be due to invalid IL or missing references) //IL_0426: Unknown result type (might be due to invalid IL or missing references) //IL_0437: Unknown result type (might be due to invalid IL or missing references) //IL_0443: Unknown result type (might be due to invalid IL or missing references) //IL_0444: Unknown result type (might be due to invalid IL or missing references) //IL_0453: Expected O, but got Unknown //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_01f5: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_05b1: Unknown result type (might be due to invalid IL or missing references) //IL_05b6: Unknown result type (might be due to invalid IL or missing references) //IL_05d6: Unknown result type (might be due to invalid IL or missing references) //IL_052f: Unknown result type (might be due to invalid IL or missing references) Shader val = Shader.Find("Standard"); if ((Object)(object)val == (Object)null || !val.isSupported) { throw new InvalidOperationException("The game's Standard shader is unavailable."); } info = pack.Info; bones = (Transform[])(object)new Transform[info.Bones.Length]; rest = (Vector3[])(object)new Vector3[bones.Length]; positions = (Vector3[])(object)new Vector3[bones.Length + 1]; rotations = (Vector3[])(object)new Vector3[bones.Length + 1]; Root = new GameObject(info.Name) { layer = layer }; Root.SetActive(false); Root.transform.SetParent(parent, false); Root.transform.localScale = Vector3.one * info.Scale; try { Matrix4x4[] array = (Matrix4x4[])(object)new Matrix4x4[bones.Length]; for (int i = 0; i < bones.Length; i++) { BoneInfo boneInfo = info.Bones[i]; Vector3 val2 = Vector(boneInfo.Position); bones[i] = new GameObject(boneInfo.Name) { layer = layer }.transform; bones[i].SetParent((boneInfo.Parent < 0) ? Root.transform : bones[boneInfo.Parent], false); rest[i] = val2 - ((boneInfo.Parent < 0) ? Vector3.zero : Vector(info.Bones[boneInfo.Parent].Position)); bones[i].localPosition = rest[i]; array[i] = Matrix4x4.Translate(-val2); } Vector3[] array2 = (Vector3[])(object)new Vector3[pack.Vertices.Length]; Vector3[] array3 = (Vector3[])(object)new Vector3[array2.Length]; Vector2[] array4 = (Vector2[])(object)new Vector2[array2.Length]; BoneWeight[] array5 = (BoneWeight[])(object)new BoneWeight[array2.Length]; for (int j = 0; j < array2.Length; j++) { Vertex vertex = pack.Vertices[j]; array2[j] = new Vector3(vertex.X, vertex.Y, vertex.Z); array3[j] = new Vector3(vertex.Nx, vertex.Ny, vertex.Nz); array4[j] = new Vector2(vertex.U, vertex.V); int num = j; BoneWeight val3 = default(BoneWeight); ((BoneWeight)(ref val3)).boneIndex0 = vertex.B0; ((BoneWeight)(ref val3)).boneIndex1 = vertex.B1; ((BoneWeight)(ref val3)).boneIndex2 = vertex.B2; ((BoneWeight)(ref val3)).boneIndex3 = vertex.B3; ((BoneWeight)(ref val3)).weight0 = vertex.W0; ((BoneWeight)(ref val3)).weight1 = vertex.W1; ((BoneWeight)(ref val3)).weight2 = vertex.W2; ((BoneWeight)(ref val3)).weight3 = vertex.W3; array5[num] = val3; } mesh = new Mesh { name = info.Name, indexFormat = (IndexFormat)1 }; mesh.vertices = array2; mesh.normals = array3; mesh.uv = array4; mesh.boneWeights = array5; mesh.bindposes = array; mesh.triangles = pack.Indices; mesh.RecalculateBounds(); mesh.UploadMeshData(true); texture = new Texture2D(2, 2, (TextureFormat)4, true) { name = info.Name, filterMode = (FilterMode)2, wrapMode = (TextureWrapMode)1 }; if (!ImageConversion.LoadImage(texture, pack.Texture, true)) { throw new InvalidOperationException("Cannot decode model texture."); } material = new Material(val) { name = info.Name, mainTexture = (Texture)(object)texture, color = Color.white }; material.SetFloat("_Metallic", info.Material.Metallic); material.SetFloat("_Glossiness", info.Material.Smoothness); if (!info.Material.SpecularHighlights) { material.SetFloat("_SpecularHighlights", 0f); material.EnableKeyword("_SPECULARHIGHLIGHTS_OFF"); } if (info.Material.Emission > 0f) { material.EnableKeyword("_EMISSION"); material.SetTexture("_EmissionMap", (Texture)(object)texture); float emission = info.Material.Emission; material.SetVector("_EmissionColor", new Vector4(emission, emission, emission, 1f)); } renderer = Root.AddComponent(); renderer.sharedMesh = mesh; ((Renderer)renderer).sharedMaterial = material; renderer.bones = bones; renderer.rootBone = Root.transform; renderer.quality = (SkinQuality)4; ((Renderer)renderer).lightProbeUsage = (LightProbeUsage)4; Bounds bounds = mesh.bounds; ((Bounds)(ref bounds)).Expand(info.BoundsPadding * 2f); ((Renderer)renderer).localBounds = bounds; Animate(Motion.Sample(0.0, 0f, 0f, 0f)); } catch { Dispose(); throw; } } private static Vector3 Vector(float[] v) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) return new Vector3(v[0], v[1], v[2]); } public void UpdateLighting() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) Color val = (Shader.GetGlobalColor(Ambient0) + Shader.GetGlobalColor(Ambient1) + Shader.GetGlobalColor(Ambient2)) / 3f; val.r = Mathf.Max(0.12f, val.r); val.g = Mathf.Max(0.12f, val.g); val.b = Mathf.Max(0.12f, val.b); probe[0] = RenderSettings.ambientProbe; ((SphericalHarmonicsL2)(ref probe[0])).AddAmbientLight(val); lighting.CopySHCoefficientArraysFrom(probe); ((Renderer)renderer).SetPropertyBlock(lighting); } public void Animate(Motion state) { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0067: 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) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: 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_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) Array.Clear(positions, 0, positions.Length); Array.Clear(rotations, 0, rotations.Length); MotionInfo[] motions = info.Motions; foreach (MotionInfo motionInfo in motions) { float num = state.Get(motionInfo.Kind); ref Vector3 reference = ref positions[motionInfo.Index]; reference += Vector(motionInfo.Position) * num; ref Vector3 reference2 = ref rotations[motionInfo.Index]; reference2 += Vector(motionInfo.Rotation) * num; } for (int j = 0; j < bones.Length; j++) { bones[j].localPosition = rest[j] + positions[j]; bones[j].localRotation = Quaternion.Euler(rotations[j]); } Root.transform.localPosition = Vector(info.Offset) + positions[bones.Length]; Root.transform.localRotation = Quaternion.Euler(Vector(info.Rotation)) * Quaternion.Euler(rotations[bones.Length]); } public void Dispose() { if ((Object)(object)Root != (Object)null) { Root.SetActive(false); Object.Destroy((Object)(object)Root); } if ((Object)(object)mesh != (Object)null) { Object.Destroy((Object)(object)mesh); } if ((Object)(object)material != (Object)null) { Object.Destroy((Object)(object)material); } if ((Object)(object)texture != (Object)null) { Object.Destroy((Object)(object)texture); } } } [DataContract] internal sealed class PackInfo { [DataMember(Name = "format", IsRequired = true)] public int Format { get; set; } [DataMember(Name = "name", IsRequired = true)] public string Name { get; set; } [DataMember(Name = "author", IsRequired = true)] public string Author { get; set; } [DataMember(Name = "license", IsRequired = true)] public string License { get; set; } [DataMember(Name = "source")] public string Source { get; set; } [DataMember(Name = "scale", IsRequired = true)] public float Scale { get; set; } [DataMember(Name = "offset")] public float[] Offset { get; set; } [DataMember(Name = "rotation")] public float[] Rotation { get; set; } [DataMember(Name = "boundsPadding")] public float BoundsPadding { get; set; } [DataMember(Name = "material")] public MaterialInfo Material { get; set; } [DataMember(Name = "bones", IsRequired = true)] public BoneInfo[] Bones { get; set; } [DataMember(Name = "motions")] public MotionInfo[] Motions { get; set; } } [DataContract] internal sealed class BoneInfo { [DataMember(Name = "name", IsRequired = true)] public string Name { get; set; } [DataMember(Name = "parent", IsRequired = true)] public int Parent { get; set; } [DataMember(Name = "position", IsRequired = true)] public float[] Position { get; set; } } [DataContract] internal sealed class MaterialInfo { [DataMember(Name = "smoothness")] public float Smoothness { get; set; } [DataMember(Name = "metallic")] public float Metallic { get; set; } [DataMember(Name = "emission")] public float Emission { get; set; } [DataMember(Name = "specularHighlights")] public bool SpecularHighlights { get; set; } } [DataContract] internal sealed class MotionInfo { public int Index; public Signal Kind; [DataMember(Name = "target", IsRequired = true)] public string Target { get; set; } [DataMember(Name = "signal", IsRequired = true)] public string Signal { get; set; } [DataMember(Name = "position")] public float[] Position { get; set; } [DataMember(Name = "rotation")] public float[] Rotation { get; set; } } internal struct Vertex { public float X; public float Y; public float Z; public float Nx; public float Ny; public float Nz; public float U; public float V; public byte B0; public byte B1; public byte B2; public byte B3; public float W0; public float W1; public float W2; public float W3; } internal sealed class ModelPack { public PackInfo Info; public Vertex[] Vertices; public int[] Indices; public byte[] Texture; public static ModelPack Load(string directory) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown //IL_0033: Unknown result type (might be due to invalid IL or missing references) ModelPack modelPack = new ModelPack(); using (FileStream fileStream = Open(directory, "model.json", 1048576L)) { modelPack.Info = (PackInfo)((XmlObjectSerializer)new DataContractJsonSerializer(typeof(PackInfo), new DataContractJsonSerializerSettings { MaxItemsInObjectGraph = 20000 })).ReadObject((Stream)fileStream); } modelPack.ValidateInfo(); using (FileStream stream = Open(directory, "mesh.bin.gz", 33554432L)) { using GZipStream input = new GZipStream(stream, CompressionMode.Decompress); using BinaryReader reader = new BinaryReader(input); modelPack.ReadMesh(reader); } using (FileStream fileStream2 = Open(directory, "texture.png", 67108864L)) { using BinaryReader binaryReader = new BinaryReader(fileStream2); modelPack.Texture = binaryReader.ReadBytes((int)fileStream2.Length); } modelPack.ValidateTexture(); return modelPack; } private static FileStream Open(string directory, string name, long limit) { string path = Path.Combine(directory, name); Require((File.GetAttributes(path) & FileAttributes.ReparsePoint) == 0, "Asset must be a regular file: " + name); FileStream fileStream = File.OpenRead(path); if (fileStream.Length > 0 && fileStream.Length <= limit) { return fileStream; } fileStream.Dispose(); throw new InvalidDataException("Asset size limit exceeded or empty: " + name); } private static void Require(bool condition, string message) { if (!condition) { throw new InvalidDataException(message); } } private static bool Finite(float value) { if (!float.IsNaN(value)) { return !float.IsInfinity(value); } return false; } private static void Range(float value, float min, float max, string name) { Require(Finite(value) && value >= min && value <= max, "Invalid " + name); } private static float[] Vector(float[] value, bool required = false) { if (value == null && !required) { return new float[3]; } Require(value != null && value.Length == 3, "Expected a three-component vector"); for (int i = 0; i < value.Length; i++) { Range(value[i], -1000f, 1000f, "vector component"); } return value; } private void ValidateInfo() { Require(Info != null && Info.Format == 1, "Unsupported model format"); Require(!string.IsNullOrWhiteSpace(Info.Name) && !string.IsNullOrWhiteSpace(Info.Author) && !string.IsNullOrWhiteSpace(Info.License), "Model name, author and license are required"); Range(Info.Scale, 0.001f, 100f, "scale"); Range(Info.BoundsPadding, 0f, 100f, "bounds padding"); Info.Offset = Vector(Info.Offset); Info.Rotation = Vector(Info.Rotation); Info.Material = Info.Material ?? new MaterialInfo(); Range(Info.Material.Smoothness, 0f, 1f, "smoothness"); Range(Info.Material.Metallic, 0f, 1f, "metallic"); Range(Info.Material.Emission, 0f, 2f, "emission"); Require(Info.Bones != null && Info.Bones.Length >= 1 && Info.Bones.Length <= 256, "Bone count must be 1..256"); Dictionary dictionary = new Dictionary(StringComparer.Ordinal); for (int i = 0; i < Info.Bones.Length; i++) { BoneInfo boneInfo = Info.Bones[i]; Require(boneInfo != null && !string.IsNullOrWhiteSpace(boneInfo.Name) && boneInfo.Name.Length <= 128 && boneInfo.Name != "$root" && !dictionary.ContainsKey(boneInfo.Name), "Invalid or duplicate bone name"); Require(boneInfo.Parent >= -1 && boneInfo.Parent < i, "Bone parents must precede children"); boneInfo.Position = Vector(boneInfo.Position, required: true); dictionary.Add(boneInfo.Name, i); } dictionary.Add("$root", Info.Bones.Length); Info.Motions = Info.Motions ?? Array.Empty(); Require(Info.Motions.Length <= 1024, "Too many motion bindings"); MotionInfo[] motions = Info.Motions; foreach (MotionInfo motionInfo in motions) { Require(motionInfo != null && motionInfo.Target != null && dictionary.TryGetValue(motionInfo.Target, out motionInfo.Index), "Unknown motion target"); Require(Enum.TryParse(motionInfo.Signal, out motionInfo.Kind) && Enum.GetName(typeof(Signal), motionInfo.Kind) == motionInfo.Signal, "Unknown motion signal"); motionInfo.Position = Vector(motionInfo.Position); motionInfo.Rotation = Vector(motionInfo.Rotation); } } private static float ReadFloat(BinaryReader reader) { float num = reader.ReadSingle(); Require(Finite(num), "Non-finite mesh value"); return num; } private void ReadMesh(BinaryReader reader) { Require(reader.ReadUInt32() == 1145918025 && reader.ReadInt32() == 1, "Unsupported mesh format"); int num = reader.ReadInt32(); int num2 = reader.ReadInt32(); Require(num >= 3 && num <= 250000 && num2 >= 3 && num2 <= 1500000 && num2 % 3 == 0, "Mesh size limit exceeded"); Vertices = new Vertex[num]; for (int i = 0; i < num; i++) { Vertex vertex = new Vertex { X = ReadFloat(reader), Y = ReadFloat(reader), Z = ReadFloat(reader), Nx = ReadFloat(reader), Ny = ReadFloat(reader), Nz = ReadFloat(reader), U = ReadFloat(reader), V = ReadFloat(reader), B0 = reader.ReadByte(), B1 = reader.ReadByte(), B2 = reader.ReadByte(), B3 = reader.ReadByte(), W0 = ReadFloat(reader), W1 = ReadFloat(reader), W2 = ReadFloat(reader), W3 = ReadFloat(reader) }; Require(Math.Abs(vertex.X) <= 1000f && Math.Abs(vertex.Y) <= 1000f && Math.Abs(vertex.Z) <= 1000f, "Mesh coordinate out of range"); Require(Math.Abs(vertex.Nx * vertex.Nx + vertex.Ny * vertex.Ny + vertex.Nz * vertex.Nz - 1f) < 0.01f, "Mesh normals must be unit length"); Require(vertex.U >= 0f && vertex.U <= 1f && vertex.V >= 0f && vertex.V <= 1f, "Atlas UV out of range"); Require(vertex.B0 < Info.Bones.Length && vertex.B1 < Info.Bones.Length && vertex.B2 < Info.Bones.Length && vertex.B3 < Info.Bones.Length, "Mesh bone index out of range"); Require(vertex.W0 >= vertex.W1 && vertex.W1 >= vertex.W2 && vertex.W2 >= vertex.W3 && vertex.W3 >= 0f && Math.Abs(vertex.W0 + vertex.W1 + vertex.W2 + vertex.W3 - 1f) < 1E-05f, "Skin weights must be sorted and normalized"); Vertices[i] = vertex; } Indices = new int[num2]; for (int j = 0; j < num2; j++) { Indices[j] = reader.ReadInt32(); Require(Indices[j] >= 0 && Indices[j] < num, "Triangle index out of range"); } Require(reader.BaseStream.ReadByte() == -1, "Unexpected trailing mesh data"); } private void ValidateTexture() { byte[] array = new byte[16] { 137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82 }; Require(Texture.Length >= 33, "Truncated PNG"); for (int i = 0; i < array.Length; i++) { Require(Texture[i] == array[i], "Expected PNG with IHDR"); } uint num = BigEndian(16); uint num2 = BigEndian(20); Require(num != 0 && num2 != 0 && num <= 8192 && num2 <= 8192 && (ulong)((long)num * (long)num2) <= 16777216uL, "Texture exceeds 16 megapixels"); } private uint BigEndian(int at) { return (uint)((Texture[at] << 24) | (Texture[at + 1] << 16) | (Texture[at + 2] << 8) | Texture[at + 3]); } } internal enum Signal { Constant, Stride, Step, LeftStep, RightStep, Air, Sail } internal struct Motion { private float stride; private float air; private float sail; public static Motion Sample(double phase, float run, float air, float sail) { return new Motion { stride = (float)Math.Sin(phase) * run * (1f - air), air = air, sail = sail }; } public float Get(Signal signal) { return signal switch { Signal.Constant => 1f, Signal.Stride => stride, Signal.Step => Math.Abs(stride), Signal.LeftStep => Math.Max(0f, stride), Signal.RightStep => Math.Max(0f, 0f - stride), Signal.Air => air, Signal.Sail => sail, _ => throw new ArgumentOutOfRangeException("signal"), }; } } [BepInPlugin("org.fyyy.icarusmodelreplacement", "Icarus Model Replacement", "1.0.0")] public sealed class Plugin : BaseUnityPlugin { private struct HiddenRenderer { private Renderer renderer; private bool wasHidden; public void Set(Renderer next) { if (!((Object)(object)renderer == (Object)(object)next)) { if ((Object)(object)renderer != (Object)null) { renderer.forceRenderingOff = wasHidden; } renderer = next; if ((Object)(object)renderer != (Object)null) { wasHidden = renderer.forceRenderingOff; renderer.forceRenderingOff = true; } } } } private const string Builtin = "builtin"; private MechaArmorModel armor; private Model model; private ConfigEntry modelDirectory; private string selected; private HiddenRenderer[] parts = Array.Empty(); private HiddenRenderer[] bones = Array.Empty(); private HiddenRenderer[] wrecks = Array.Empty(); private GameObject wreckageGroup; private void Awake() { modelDirectory = ((BaseUnityPlugin)this).Config.Bind("Model", "Directory", "builtin", "builtin uses the included Gugugaga. Otherwise, a model folder relative to BepInEx/plugins or absolute. Empty keeps vanilla Icarus. Changes apply in game."); } private void LateUpdate() { Player val = (GameMain.isRunning ? GameMain.mainPlayer : null); MechaArmorModel val2 = ((val != null) ? val.mechaArmorModel : null); if ((Object)(object)val2 != (Object)null && !((ManualBehaviour)val2).inited) { val2 = null; } string text = modelDirectory.Value.Trim(); if (armor != val2 || selected != text) { Clear(); armor = val2; selected = text; if ((Object)(object)armor != (Object)null && text.Length != 0) { try { SkinnedMeshRenderer[] componentsInChildren = ((Component)armor).GetComponentsInChildren(true); ModelPack modelPack = ModelPack.Load((text == "builtin") ? Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), "model") : Path.Combine(Paths.PluginPath, text)); model = new Model(modelPack, val.controller.model, ((Component)armor).gameObject.layer); Renderer[] renderers = (Renderer[])(object)componentsInChildren; parts = Hide(renderers); bones = new HiddenRenderer[armor.boneModels.Length]; ((BaseUnityPlugin)this).Logger.LogInfo((object)("Model attached: " + modelPack.Info.Name + " by " + modelPack.Info.Author + " (" + modelPack.Info.License + ").")); } catch (Exception arg) { Clear(); ((BaseUnityPlugin)this).Logger.LogError((object)$"Cannot load model '{text}'; keeping Icarus. {arg}"); return; } } } if ((Object)(object)armor == (Object)null || model == null) { return; } for (int i = 0; i < bones.Length; i++) { bones[i].Set((Renderer)(object)armor.boneModels[i]?.meshRenderer); } if ((Object)(object)wreckageGroup != (Object)(object)armor.wreckageGroup) { Restore(wrecks); wreckageGroup = armor.wreckageGroup; HiddenRenderer[] array; if (!((Object)(object)wreckageGroup != (Object)null)) { array = Array.Empty(); } else { Renderer[] renderers = (Renderer[])(object)wreckageGroup.GetComponentsInChildren(true); array = Hide(renderers); } wrecks = array; } model.Root.SetActive(((ManualBehaviour)armor).active && val.isAlive); model.UpdateLighting(); PlayerAnimator animator = val.animator; if (!((TrackedReference)(object)animator.run_slow == (TrackedReference)null)) { float air = Mathf.Clamp01(animator.driftWeight + animator.flyWeight + animator.sailWeight); Motion state = Motion.Sample((double)animator.run_slow.normalizedTime * Math.PI * 2.0, Mathf.Clamp01(animator.runWeight), air, Mathf.Clamp01(animator.sailWeight)); model.Animate(state); } } private static HiddenRenderer[] Hide(Renderer[] renderers) { HiddenRenderer[] array = new HiddenRenderer[renderers.Length]; for (int i = 0; i < array.Length; i++) { array[i].Set(renderers[i]); } return array; } private static void Restore(HiddenRenderer[] renderers) { for (int i = 0; i < renderers.Length; i++) { renderers[i].Set(null); } } private void OnDisable() { Clear(); armor = null; } private void Clear() { Restore(parts); Restore(bones); Restore(wrecks); parts = (bones = (wrecks = Array.Empty())); model?.Dispose(); model = null; wreckageGroup = null; } } }