Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of PunyaFeiopar v1.2.2
BepInEx/plugins/PunyaFeiopar/FeioparReskin.dll
Decompiled a week agousing System; using System.Buffers.Binary; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Security.Cryptography; using System.Text; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyVersion("0.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; } } } [BepInPlugin("local.feioparreskin", "Feiopar Reskin", "1.2.2")] public sealed class FeioparReskin : BaseUnityPlugin { private const string PluginId = "local.feioparreskin"; private const string SkinFileName = "Feiopar.png"; private const int MaxPngBytes = 33554432; private const int MaxDimension = 4096; private static FeioparReskin instance; private static readonly int BaseColorMap = Shader.PropertyToID("_BaseColorMap"); private static readonly int EmissiveColor = Shader.PropertyToID("_EmissiveColor"); private Texture2D skin; private PunyaGeometry geometry; private bool reportedApplication; private void Awake() { //IL_0233: Unknown result type (might be due to invalid IL or missing references) //IL_024b: Unknown result type (might be due to invalid IL or missing references) //IL_0258: Expected O, but got Unknown ConfigEntry<bool> val = ((BaseUnityPlugin)this).Config.Bind<bool>("Appearance", "UsePunyaMesh", true, "Use the optional Punya.patch beside the skin and soften the eye glow."); string directoryName = Path.GetDirectoryName(typeof(FeioparReskin).Assembly.Location); string path = Path.Combine(directoryName, "Feiopar.png"); try { skin = LoadSkin(path); } catch (FileNotFoundException) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"Put Feiopar.png beside the DLL to enable the reskin."); return; } catch (Exception ex2) when (ex2 is IOException || ex2 is UnauthorizedAccessException) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Cannot read Feiopar.png: " + ex2.Message)); return; } if ((Object)(object)skin == (Object)null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)string.Format("Invalid {0}. Use a PNG up to {1}×{2} and 32 MiB.", "Feiopar.png", 4096, 4096)); return; } string path2 = Path.Combine(directoryName, "Punya.mesh"); string text = Path.Combine(directoryName, "Punya.patch"); if (val.Value && (File.Exists(text) || File.Exists(path2))) { try { if (File.Exists(text)) { using (MemoryStream data = new MemoryStream(PunyaPatch.Rebuild(text, Path.Combine(Application.dataPath, "sharedassets1.assets")), writable: false)) { geometry = PunyaGeometry.Load(data); } ((BaseUnityPlugin)this).Logger.LogInfo((object)"Rebuilt Punya geometry from local Steam v81 assets."); } else { using FileStream data2 = File.OpenRead(path2); geometry = PunyaGeometry.Load(data2); } } catch (Exception ex3) when (ex3 is IOException || ex3 is UnauthorizedAccessException || ex3 is ArgumentException || ex3 is InvalidOperationException || ex3 is OverflowException || ex3 is CryptographicException) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not load the Punya geometry. Using the stock shape. " + ex3.Message)); } } instance = this; MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(PumaAI), "Start", Type.EmptyTypes, (Type[])null); new Harmony("local.feioparreskin").Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(FeioparReskin), "ApplySkin", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); ((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("Loaded {0} ({1}×{2}).", "Feiopar.png", ((Texture)skin).width, ((Texture)skin).height)); } private static Texture2D LoadSkin(string path) { //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Expected O, but got Unknown byte[] array; using (FileStream fileStream = File.OpenRead(path)) { using BinaryReader binaryReader = new BinaryReader(fileStream); if (fileStream.Length < 33 || fileStream.Length > 33554432) { return null; } array = binaryReader.ReadBytes((int)fileStream.Length); } if (array.Length < 33 || BinaryPrimitives.ReadUInt64BigEndian(array.AsSpan(0, 8)) != 9894494448401390090uL || BinaryPrimitives.ReadUInt32BigEndian(array.AsSpan(8, 4)) != 13 || BinaryPrimitives.ReadUInt32BigEndian(array.AsSpan(12, 4)) != 1229472850) { return null; } uint num = BinaryPrimitives.ReadUInt32BigEndian(array.AsSpan(16, 4)); uint num2 = BinaryPrimitives.ReadUInt32BigEndian(array.AsSpan(20, 4)); if (num == 0 || num2 == 0 || num > 4096 || num2 > 4096) { return null; } Texture2D val = new Texture2D(2, 2, (TextureFormat)4, true, false); if (!ImageConversion.LoadImage(val, array, true)) { Object.Destroy((Object)(object)val); return null; } ((Object)val).name = "FeioparReskin_BaseColor"; ((Texture)val).filterMode = (FilterMode)2; return val; } private static void ApplySkin(PumaAI __instance) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0094: 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_00a1: 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) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) FeioparReskin feioparReskin = instance; MaterialPropertyBlock val = new MaterialPropertyBlock(); SkinnedMeshRenderer[] array = Array.FindAll(((Component)__instance).GetComponentsInChildren<SkinnedMeshRenderer>(true), HasStockColor); bool flag = feioparReskin.geometry != null && feioparReskin.geometry.TryApply(array); SkinnedMeshRenderer[] array2 = array; foreach (SkinnedMeshRenderer obj in array2) { Material sharedMaterial = ((Renderer)obj).sharedMaterial; ((Renderer)obj).GetPropertyBlock(val); val.SetTexture(BaseColorMap, (Texture)(object)feioparReskin.skin); if (flag && sharedMaterial.HasProperty(EmissiveColor)) { Color color = sharedMaterial.GetColor(EmissiveColor); val.SetColor(EmissiveColor, new Color(color.r * 0.24f, color.g * 0.24f, color.b * 0.24f, color.a)); } ((Renderer)obj).SetPropertyBlock(val); } if (!feioparReskin.reportedApplication) { feioparReskin.reportedApplication = true; if (array.Length == 0) { ((BaseUnityPlugin)feioparReskin).Logger.LogWarning((object)"Could not find the stock Feiopar color texture on the spawned enemy."); } else { ((BaseUnityPlugin)feioparReskin).Logger.LogInfo((object)string.Format("Applied {0} to {1} Feiopar renderer(s).", "Feiopar.png", array.Length)); } if (feioparReskin.geometry != null) { ((BaseUnityPlugin)feioparReskin).Logger.LogInfo((object)(flag ? "Applied Punya geometry to all three Feiopar LODs." : "Stock Feiopar rig did not match the Punya geometry. Used texture only.")); } } } private static bool HasStockColor(SkinnedMeshRenderer renderer) { Material sharedMaterial = ((Renderer)renderer).sharedMaterial; if ((Object)(object)sharedMaterial == (Object)null || !sharedMaterial.HasProperty(BaseColorMap)) { return false; } Texture texture = sharedMaterial.GetTexture(BaseColorMap); if ((Object)(object)texture != (Object)null) { return ((Object)texture).name == "PumaTextureCombined"; } return false; } } internal sealed class PunyaGeometry { private sealed class Lod { public Mesh Mesh; public int OriginalVertexCount; public string[] BoneNames; } private static readonly string[] LodNames = new string[3] { "BigCatLOD0b", "BigCatLOD1", "BigCatLOD2" }; private const int BoneCount = 38; private readonly Lod[] lods = new Lod[LodNames.Length]; public static PunyaGeometry Load(Stream data) { PunyaGeometry punyaGeometry = new PunyaGeometry(); try { using (BinaryReader binaryReader = new BinaryReader(data, Encoding.UTF8, leaveOpen: true)) { if (data.Length > 2097152 || binaryReader.ReadUInt32() != 1297698384 || binaryReader.ReadInt32() != 1 || binaryReader.ReadInt32() != LodNames.Length) { throw new IOException("Invalid mesh header."); } for (int i = 0; i < LodNames.Length; i++) { punyaGeometry.lods[i] = ReadLod(binaryReader, LodNames[i]); } if (data.Position != data.Length) { throw new IOException("Unexpected bytes after the LOD data."); } } return punyaGeometry; } catch { Lod[] array = punyaGeometry.lods; foreach (Lod lod in array) { if (lod != null) { Object.Destroy((Object)(object)lod.Mesh); } } throw; } } private static Lod ReadLod(BinaryReader reader, string name) { //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_015f: 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) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0294: Unknown result type (might be due to invalid IL or missing references) //IL_0299: Unknown result type (might be due to invalid IL or missing references) //IL_02ac: Expected O, but got Unknown int num = reader.ReadInt32(); int num2 = reader.ReadInt32(); int num3 = reader.ReadInt32(); if (num < 1 || num2 < num || num2 > 12000 || num3 < 3 || num3 > 90000 || num3 % 3 != 0) { throw new IOException("Invalid mesh size."); } string[] array = new string[38]; Matrix4x4[] array2 = (Matrix4x4[])(object)new Matrix4x4[38]; for (int i = 0; i < 38; i++) { array[i] = reader.ReadString(); } for (int j = 0; j < 38; j++) { for (int k = 0; k < 4; k++) { for (int l = 0; l < 4; l++) { ((Matrix4x4)(ref array2[j]))[k, l] = ReadFinite(reader); } } } Vector3[] array3 = (Vector3[])(object)new Vector3[num2]; Vector3[] array4 = (Vector3[])(object)new Vector3[num2]; Vector4[] array5 = (Vector4[])(object)new Vector4[num2]; Vector2[] array6 = (Vector2[])(object)new Vector2[num2]; BoneWeight[] array7 = (BoneWeight[])(object)new BoneWeight[num2]; for (int m = 0; m < num2; m++) { array3[m] = new Vector3(ReadFinite(reader), ReadFinite(reader), ReadFinite(reader)); } for (int n = 0; n < num2; n++) { array4[n] = new Vector3(ReadFinite(reader), ReadFinite(reader), ReadFinite(reader)); } for (int num4 = 0; num4 < num2; num4++) { array5[num4] = new Vector4(ReadFinite(reader), ReadFinite(reader), ReadFinite(reader), ReadFinite(reader)); } for (int num5 = 0; num5 < num2; num5++) { array6[num5] = new Vector2(ReadFinite(reader), ReadFinite(reader)); } for (int num6 = 0; num6 < num2; num6++) { ((BoneWeight)(ref array7[num6])).weight0 = ReadFinite(reader); ((BoneWeight)(ref array7[num6])).weight1 = ReadFinite(reader); ((BoneWeight)(ref array7[num6])).weight2 = ReadFinite(reader); ((BoneWeight)(ref array7[num6])).weight3 = ReadFinite(reader); } for (int num7 = 0; num7 < num2; num7++) { ((BoneWeight)(ref array7[num7])).boneIndex0 = ReadBoneIndex(reader); ((BoneWeight)(ref array7[num7])).boneIndex1 = ReadBoneIndex(reader); ((BoneWeight)(ref array7[num7])).boneIndex2 = ReadBoneIndex(reader); ((BoneWeight)(ref array7[num7])).boneIndex3 = ReadBoneIndex(reader); } int[] array8 = new int[num3]; for (int num8 = 0; num8 < num3; num8++) { array8[num8] = reader.ReadUInt16(); if (array8[num8] >= num2) { throw new IOException("Triangle references a missing vertex."); } } Mesh val = new Mesh { name = "Punya_" + name }; try { val.vertices = array3; val.normals = array4; val.tangents = array5; val.uv = array6; val.bindposes = array2; val.boneWeights = array7; val.subMeshCount = 1; val.SetTriangles(array8, 0); return new Lod { Mesh = val, OriginalVertexCount = num, BoneNames = array }; } catch { Object.Destroy((Object)(object)val); throw; } } private static float ReadFinite(BinaryReader reader) { float num = reader.ReadSingle(); if (float.IsNaN(num) || float.IsInfinity(num)) { throw new IOException("Non-finite mesh coordinate."); } return num; } private static int ReadBoneIndex(BinaryReader reader) { byte num = reader.ReadByte(); if (num >= 38) { throw new IOException("Bone index out of range."); } return num; } public bool TryApply(SkinnedMeshRenderer[] renderers) { //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) SkinnedMeshRenderer[] array = (SkinnedMeshRenderer[])(object)new SkinnedMeshRenderer[LodNames.Length]; for (int i = 0; i < LodNames.Length; i++) { string name = LodNames[i]; SkinnedMeshRenderer val = Array.Find(renderers, (SkinnedMeshRenderer renderer) => (Object)(object)renderer.sharedMesh != (Object)null && ((Object)renderer.sharedMesh).name == name && renderer.sharedMesh.vertexCount == lods[i].OriginalVertexCount); if ((Object)(object)val == (Object)null) { return false; } Transform[] bones = val.bones; if (bones.Length != 38) { return false; } for (int num = 0; num < 38; num++) { if ((Object)(object)bones[num] == (Object)null || ((Object)bones[num]).name != lods[i].BoneNames[num]) { return false; } } array[i] = val; } for (int num2 = 0; num2 < array.Length; num2++) { Bounds localBounds = ((Renderer)array[num2]).localBounds; ((Bounds)(ref localBounds)).Expand(0.16f); array[num2].sharedMesh = lods[num2].Mesh; ((Renderer)array[num2]).localBounds = localBounds; } return true; } } internal static class PunyaPatch { private sealed class StockLod { public readonly int Vertices; public readonly int VertexOffset; public readonly int VertexBytes; public readonly int IndexOffset; public readonly int IndexBytes; public readonly int PoseOffset; public readonly string Hash; public StockLod(int vertices, int vertexOffset, int vertexBytes, int indexOffset, int indexBytes, int poseOffset, string hash) { Vertices = vertices; VertexOffset = vertexOffset; VertexBytes = vertexBytes; IndexOffset = indexOffset; IndexBytes = indexBytes; PoseOffset = poseOffset; Hash = hash; } } private static readonly StockLod[] Stock = new StockLod[3] { new StockLod(4947, 9930836, 375984, 9899220, 31548, 9895696, "a4ee81eec697b8af617b6686ef66f49e05f6b1ca61e9a9a5989cbc78a48053b9"), new StockLod(2877, 15896648, 218672, 15882388, 14190, 15878864, "10c044d34cdc6db7c107a1aa6c8fa5b409be8ff5cb5b92a857f2ddef04e3ec02"), new StockLod(1506, 15686524, 114464, 15681236, 5220, 15677712, "dfb6d62d9b4b5831780a05070069dba783abf7599552b9c9f79d2b2b96b36943") }; private const int PoseBytes = 2432; public static byte[] Rebuild(string patchPath, string gameAssetPath) { using BinaryReader binaryReader = new BinaryReader(File.OpenRead(patchPath)); using BinaryReader source = new BinaryReader(File.OpenRead(gameAssetPath)); using MemoryStream memoryStream = new MemoryStream(); using BinaryWriter binaryWriter = new BinaryWriter(memoryStream); if (binaryReader.BaseStream.Length > 524288 || binaryReader.ReadUInt32() != 1146703440 || binaryReader.ReadInt32() != 1) { throw new IOException("Invalid Punya.patch header."); } binaryWriter.Write(1297698384u); binaryWriter.Write(1); binaryWriter.Write(Stock.Length); StockLod[] stock = Stock; foreach (StockLod stock2 in stock) { RebuildLod(binaryReader, source, binaryWriter, stock2); } if (binaryReader.BaseStream.Position != binaryReader.BaseStream.Length) { throw new IOException("Unexpected data after the patch."); } return memoryStream.ToArray(); } private static void RebuildLod(BinaryReader patch, BinaryReader source, BinaryWriter output, StockLod stock) { int num = patch.ReadInt32(); int num2 = patch.ReadInt32(); if (num < 0 || num > 1000 || num2 < 0 || num2 > 10000 || num2 % 3 != 0) { throw new IOException("Invalid Punya.patch LOD sizes."); } byte[] array = ReadBlock(source, stock.VertexOffset, stock.VertexBytes); byte[] array2 = ReadBlock(source, stock.IndexOffset, stock.IndexBytes); byte[] array3 = ReadBlock(source, stock.PoseOffset, 2432); using (SHA256 sHA = SHA256.Create()) { sHA.TransformBlock(array, 0, array.Length, array, 0); sHA.TransformBlock(array2, 0, array2.Length, array2, 0); sHA.TransformFinalBlock(array3, 0, array3.Length); if (!string.Equals(BitConverter.ToString(sHA.Hash).Replace("-", ""), stock.Hash, StringComparison.OrdinalIgnoreCase)) { throw new IOException("Installed Feiopar mesh data differs from the supported Steam v81 build."); } } int vertices = stock.Vertices; int num3 = (vertices * 40 + 15) & -16; int num4 = (num3 + vertices * 4 + 15) & -16; output.Write(vertices); output.Write(vertices + num); output.Write(array2.Length / 2 + num2); for (int i = 0; i < 38; i++) { output.Write(patch.ReadString()); } output.Write(array3); for (int j = 0; j < vertices; j++) { for (int k = 0; k < 3; k++) { output.Write(BitConverter.ToSingle(array, j * 40 + k * 4) + patch.ReadSingle()); } } output.Write(ReadBytes(patch, num * 12)); WriteStockAndExtras(output, patch, array, 12, 40, 12, vertices, num); WriteStockAndExtras(output, patch, array, 24, 40, 16, vertices, num); for (int l = 0; l < vertices * 2; l++) { output.Write(HalfToFloat(BitConverter.ToUInt16(array, num3 + l * 2))); } output.Write(ReadBytes(patch, num * 8)); WriteStockAndExtras(output, patch, array, num4, 32, 16, vertices, num); for (int m = 0; m < vertices; m++) { for (int n = 0; n < 4; n++) { output.Write(checked((byte)BitConverter.ToUInt32(array, num4 + m * 32 + 16 + n * 4))); } } output.Write(ReadBytes(patch, num * 4)); output.Write(array2); output.Write(ReadBytes(patch, num2 * 2)); } private static void WriteStockAndExtras(BinaryWriter output, BinaryReader patch, byte[] vertices, int start, int stride, int bytes, int count, int extras) { for (int i = 0; i < count; i++) { output.Write(vertices, start + stride * i, bytes); } output.Write(ReadBytes(patch, extras * bytes)); } private static byte[] ReadBlock(BinaryReader reader, int offset, int count) { reader.BaseStream.Position = offset; return ReadBytes(reader, count); } private static byte[] ReadBytes(BinaryReader reader, int count) { byte[] array = reader.ReadBytes(count); if (array.Length != count) { throw new EndOfStreamException(); } return array; } private static float HalfToFloat(ushort value) { int num = (value >> 10) & 0x1F; int num2 = value & 0x3FF; return (float)((double)(((value & 0x8000) == 0) ? 1 : (-1)) * num switch { 31 => throw new IOException("Non-finite UV."), 0 => (double)num2 * Math.Pow(2.0, -24.0), _ => (1.0 + (double)num2 / 1024.0) * Math.Pow(2.0, num - 15), }); } }