using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; using Microsoft.CSharp.RuntimeBinder; using NIFLib.Enums; using NIFLib.Nodes; using Binder = Microsoft.CSharp.RuntimeBinder.Binder; namespace NIFLib.Core { public class NiFile : IDisposable { private readonly BinaryReader _reader; [CompilerGenerated] [SuppressMessage("ReSharper", "InconsistentNaming")] private static class O__11 { /// /// The P__0 /// public static CallSite> p__0; /// /// The P__1 /// public static CallSite> p__1; /// /// The P__2 /// public static CallSite> p__2; /// /// The P__3 /// public static CallSite> p__3; /// /// The P__4 /// public static CallSite> p__4; /// /// The P__5 /// public static CallSite> p__5; /// /// The P__6 /// public static CallSite> p__6; } public const uint InvalidRef = 4294967295u; public const string CmdTopLevelObject = "Top Level Object"; public const string CmdEndOfFile = "End Of File"; public NiHeader Header; public NiFooter Footer; public readonly Dictionary ObjectsByRef = new Dictionary(); public NifVersion Version => Header.Version; public NiFile(string filePath) : this(new BinaryReader(File.OpenRead(filePath))) { } public NiFile(BinaryReader reader) { _reader = reader; Header = new NiHeader(this, reader); ReadNiObjects(); Footer = new NiFooter(this, reader); FixRefs(); } private void ReadNiObjects() { var num = 0; string text; while (true) { if (Version >= NifVersion.VER_5_0_0_1) { if (Version <= NifVersion.VER_10_1_0_106 && _reader.ReadUInt32() != 0u) { break; } text = Header.BlockTypes[Header.BlockTypeIndex[num]].Value; } else { var num2 = _reader.ReadUInt32(); if (num2 > 30u || num2 < 6u) { goto IL_74; } text = new string(_reader.ReadChars((int)num2)); if (Header.Version < NifVersion.VER_3_3_0_13) { switch (text) { case "Top Level Object": continue; case "End Of File": return; } } } uint key; if (Version < NifVersion.VER_3_3_0_13) { key = _reader.ReadUInt32(); } else { key = (uint)num; } var exprE7 = Type.GetType("Niflib." + text); if (exprE7 == null) { goto Block_8; } var value = (NiObject)Activator.CreateInstance(exprE7, this, _reader); ObjectsByRef.Add(key, value); if (Version < NifVersion.VER_3_3_0_13) continue; num++; if (num >= Header.NumBlocks) { return; } } throw new Exception("Check value is not zero! Invalid file?"); IL_74: throw new Exception("Invalid object type string length!"); Block_8: throw new NotImplementedException(text); } private void FixRefs() { // Fix Object Refs foreach (var current in ObjectsByRef.Values) { FixRefs(current); } // Fix Footer Refs foreach (var niRef in Footer.RootNodes) { niRef.SetRef(this); } } [SuppressMessage("ReSharper", "InconsistentNaming")] private void FixRefs(object obj) { var fields = obj.GetType().GetFields(); foreach (var fieldInfo in fields) { if (fieldInfo.FieldType.Name.Contains("NiRef")) { if (fieldInfo.FieldType.IsArray) { var enumerable = (IEnumerable)fieldInfo.GetValue(obj); if (enumerable == null) { goto IL_303; } var enumerator = enumerable.GetEnumerator(); try { while (enumerator.MoveNext()) { var current = enumerator.Current; if (O__11.p__0 == null) { O__11.p__0 = CallSite>.Create(Binder.InvokeMember(CSharpBinderFlags.ResultDiscarded, "SetRef", null, typeof(NiFile), new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null) })); } O__11.p__0.Target(O__11.p__0, current, this); if (fieldInfo.Name == "Children") { if (O__11.p__2 == null) { O__11.p__2 = CallSite>.Create(Binder.UnaryOperation(CSharpBinderFlags.None, ExpressionType.IsTrue, typeof(NiFile), new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) })); } var arg_16A_0 = O__11.p__2.Target; CallSite arg_16A_1 = O__11.p__2; if (O__11.p__1 == null) { O__11.p__1 = CallSite>.Create(Binder.InvokeMember(CSharpBinderFlags.None, "IsValid", null, typeof(NiFile), new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) })); } if (arg_16A_0(arg_16A_1, O__11.p__1.Target(O__11.p__1, current))) { if (O__11.p__3 == null) { O__11.p__3 = CallSite>.Create(Binder.GetMember(CSharpBinderFlags.None, "Object", typeof(NiFile), new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) })); } if (!(O__11.p__3.Target(O__11.p__3, current) is NiAVObject expr_1C2)) { throw new Exception("no child"); } expr_1C2.Parent = (NiNode)obj; } } } goto IL_303; } finally { if (enumerator is IDisposable disposable) { disposable.Dispose(); } } } var value = fieldInfo.GetValue(obj); if (O__11.p__5 == null) { O__11.p__5 = CallSite>.Create(Binder.UnaryOperation(CSharpBinderFlags.None, ExpressionType.IsTrue, typeof(NiFile), new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) })); } var arg_2A0_0 = O__11.p__5.Target; CallSite arg_2A0_1 = O__11.p__5; if (O__11.p__4 == null) { O__11.p__4 = CallSite>.Create(Binder.BinaryOperation(CSharpBinderFlags.None, ExpressionType.Equal, typeof(NiFile), new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant, null) })); } if (!arg_2A0_0(arg_2A0_1, O__11.p__4.Target(O__11.p__4, value, null))) { if (O__11.p__6 == null) { O__11.p__6 = CallSite>.Create(Binder.InvokeMember(CSharpBinderFlags.ResultDiscarded, "SetRef", null, typeof(NiFile), new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null) })); } O__11.p__6.Target(O__11.p__6, value, this); } } IL_303:; } } public NiAVObject FindRoot() { var niAvObject = (from obj in ObjectsByRef.Values.OfType() select obj).FirstOrDefault(); if (niAvObject == null) { return null; } while (niAvObject.Parent != null) { niAvObject = niAvObject.Parent; } return niAvObject; } public void PrintNifTree() { var niAvObject = FindRoot(); if (niAvObject == null) { Console.WriteLine("No Root!"); return; } const int depth = 0; PrintNifNode(niAvObject, depth); } private static void PrintNifNode(NiObjectNET root, int depth) { var text = string.Empty; for (var i = 0; i < depth; i++) { text += "*"; } text += " "; Console.WriteLine(text + " " + root.Name); if (!(root is NiNode niNode)) return; var children = niNode.Children; foreach (var niRef in children) { if (niRef.IsValid()) { PrintNifNode(niRef.Object, depth + 1); } } } public void Dispose() { _reader?.Dispose(); } } }