Decompiled source of Viewfinder v1.0.0
Mods/Viewfinder.dll
Decompiled 5 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Buffers.Binary; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Linq; using System.Numerics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Threading; using System.Threading.Tasks; using BoneLib; using BoneLib.BoneMenu; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSLZ.Bonelab; using Il2CppSLZ.Marrow; using Il2CppSLZ.Marrow.Interaction; using Il2CppSLZ.Marrow.Pool; using Il2CppSLZ.Marrow.Utilities; using Il2CppSLZ.Marrow.Warehouse; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using MelonLoader; using MelonLoader.Preferences; using MelonLoader.Utils; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; using UnityEngine.UI; using UnityEngine.XR; using Viewfinder; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(Mod), "Viewfinder", "1.0.0", "MarrowByte", null)] [assembly: MelonGame("Stress Level Zero", "BONELAB")] [assembly: MelonAdditionalDependencies(new string[] { "BoneLib" })] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("MarrowByte")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Photo placement for BONELAB.")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("Viewfinder")] [assembly: AssemblyTitle("Viewfinder")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NativeIntegerAttribute : Attribute { public readonly bool[] TransformFlags; public NativeIntegerAttribute() { TransformFlags = new bool[1] { true }; } public NativeIntegerAttribute(bool[] P_0) { TransformFlags = P_0; } } [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 Viewfinder { internal sealed class CaptureJob : IDisposable { private readonly record struct Source(Renderer Renderer, Matrix4x4 Matrix); public const float FieldOfView = 60f; public const float Aspect = 1.3333334f; private const int TriangleLimit = 100000; private readonly List<Source> sources = new List<Source>(); private Matrix4x4 worldToPhoto; private readonly float depth; private Vector3 position; private Quaternion rotation; private readonly int mask; private readonly Transform head; private object? imageRoutine; private ScreenPhoto? exposure; private Exception? renderError; private bool rendered; private MeshBuffers? pendingBuffers; private Mesh? bakedMesh; private Photo? photo; private readonly IEnumerator routine; public string Status { get; private set; } = "Taking photo..."; public bool ImageReady { get; private set; } public Texture? Image { get { object obj = photo?.Image; if (obj == null) { Photo? result = Result; if (result == null) { return null; } obj = result.Image; } return (Texture?)obj; } } public Photo? Result { get; private set; } public CaptureJob(Transform head, Camera? sourceCamera, float depth) { this.depth = depth; this.head = (Transform)(((Object)(object)sourceCamera != (Object)null) ? ((object)((Component)sourceCamera).transform) : ((object)head)); mask = -33; imageRoutine = MelonCoroutines.Start(CaptureFrame(sourceCamera)); routine = Run(); } public bool Step() { return routine.MoveNext(); } private IEnumerator CaptureFrame(Camera? source) { Texture2D image = null; try { position = head.position; rotation = head.rotation; CaptureJob captureJob = this; Matrix4x4 val = Matrix4x4.TRS(position, rotation, Vector3.one); captureJob.worldToPhoto = ((Matrix4x4)(ref val)).inverse; exposure = new ScreenPhoto(source, position, rotation); } catch (Exception ex) { renderError = ex; } if (renderError != null) { imageRoutine = null; yield break; } yield return null; yield return (object)new WaitForEndOfFrame(); try { image = exposure.Read(); float anchor = Mathf.Min(4f, depth * 0.5f); RaycastHit val2 = default(RaycastHit); if (Physics.Raycast(position, rotation * Vector3.forward, ref val2, depth, mask, (QueryTriggerInteraction)1)) { anchor = Mathf.Clamp(((RaycastHit)(ref val2)).distance, 1f, depth); } Quaternion val3 = Quaternion.Euler(0f, ((Quaternion)(ref rotation)).eulerAngles.y, 0f); photo = new Photo((Texture)(object)image, Quaternion.Inverse(val3) * rotation, anchor); image = null; rendered = true; } catch (Exception ex2) { renderError = ex2; } finally { if ((Object)(object)image != (Object)null) { Object.Destroy((Object)(object)image); } exposure?.Dispose(); exposure = null; imageRoutine = null; } } public Photo TakeResult() { Photo? result = Result ?? throw new InvalidOperationException("The photo is not ready."); Result = null; return result; } private IEnumerator Run() { long renderDeadline = Environment.TickCount64 + 5000; while (!rendered) { if (renderError != null) { throw new InvalidOperationException("Photo rendering failed.", renderError); } if (Environment.TickCount64 > renderDeadline) { throw new TimeoutException("The photo camera did not receive a render frame."); } yield return null; } ImageReady = true; Clipper clipper = new Clipper(60f, 1.3333334f, 0.15f, depth); Matrix4x4 matrix4x = MeshReader.ToNumerics(worldToPhoto); HashSet<int> hashSet = new HashSet<int>(); HashSet<int> hashSet2 = new HashSet<int>(); foreach (LODGroup item in Object.FindObjectsOfType<LODGroup>()) { Il2CppReferenceArray<LOD> lODs = item.GetLODs(); for (int i = 0; i < ((Il2CppArrayBase<LOD>)(object)lODs).Length; i++) { foreach (Renderer item2 in (Il2CppArrayBase<Renderer>)(object)((Il2CppArrayBase<LOD>)(object)lODs)[i].renderers) { if ((Object)(object)item2 != (Object)null) { ((i == 0) ? hashSet2 : hashSet).Add(((Object)item2).GetInstanceID()); } } } } hashSet.ExceptWith(hashSet2); RigManager rigManager = Player.RigManager; foreach (Renderer item3 in Object.FindObjectsOfType<Renderer>()) { if (!((Object)(object)item3 == (Object)null) && item3.enabled && ((Component)item3).gameObject.activeInHierarchy && (mask & (1 << ((Component)item3).gameObject.layer)) != 0 && ((Component)item3).gameObject.layer != 5 && !hashSet.Contains(((Object)item3).GetInstanceID()) && (!((Object)(object)rigManager != (Object)null) || !((Component)item3).transform.IsChildOf(((Component)rigManager).transform) || IsBodyLog(item3)) && !((Object)((Component)item3).transform.root).name.StartsWith("Viewfinder.", StringComparison.Ordinal)) { Bounds bounds = item3.bounds; if (clipper.IntersectsBox(MeshReader.ToNumerics(((Bounds)(ref bounds)).center), MeshReader.ToNumerics(((Bounds)(ref bounds)).extents), matrix4x) && (!((Object)(object)((Il2CppObjectBase)item3).TryCast<MeshRenderer>() == (Object)null) || !((Object)(object)((Il2CppObjectBase)item3).TryCast<SkinnedMeshRenderer>() == (Object)null))) { sources.Add(new Source(item3, worldToPhoto * item3.localToWorldMatrix)); } } } sources.Sort(delegate(Source a, Source b) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) Bounds bounds2 = a.Renderer.bounds; float num = ((Bounds)(ref bounds2)).SqrDistance(position); bounds2 = b.Renderer.bounds; return num.CompareTo(((Bounds)(ref bounds2)).SqrDistance(position)); }); Material ghost = Photo.Ghost(); photo.Own((Object)(object)ghost); GroundCapture ground = new GroundCapture(photo, worldToPhoto, depth, clipper, delegate(List<Vertex> vertices2, List<int>[] indices2, Material material) { AddPart(vertices2, indices2, (Material[])(object)new Material[1] { material }, ghost, null); }); IEnumerator terrain = ground.Terrain(); Status = "Capturing terrain..."; while (terrain.MoveNext()) { yield return null; } HashSet<int> capturedRenderers = new HashSet<int>(); HashSet<int> capturedMeshes = new HashSet<int>(); Dictionary<(int, Matrix4x4), Vertex[]> vertexCache = new Dictionary<(int, Matrix4x4), Vertex[]>(); long cachedBytes = 0L; int examined = 0; for (int sourceIndex = 0; sourceIndex < sources.Count && sourceIndex < 192; sourceIndex++) { if (photo.Triangles >= 95904 || examined >= 2000000) { photo.Limited = true; break; } Source source = sources[sourceIndex]; MeshReader reader = null; Material[] materials = null; Mesh mesh = null; Vertex[] cached = null; int start = 0; int count = -1; try { Renderer renderer = source.Renderer; if (!((Object)(object)renderer == (Object)null)) { SkinnedMeshRenderer val = ((Il2CppObjectBase)renderer).TryCast<SkinnedMeshRenderer>(); if ((Object)(object)val != (Object)null) { bakedMesh = new Mesh(); val.BakeMesh(bakedMesh); mesh = bakedMesh; } else { MeshFilter component = ((Component)renderer).GetComponent<MeshFilter>(); mesh = ((component != null) ? component.sharedMesh : null); } if (!((Object)(object)mesh == (Object)null)) { materials = ((IEnumerable<Material>)renderer.sharedMaterials).ToArray(); MeshRenderer val2 = ((Il2CppObjectBase)renderer).TryCast<MeshRenderer>(); start = (((Object)(object)val2 != (Object)null) ? val2.subMeshStartIndex : 0); count = (renderer.isPartOfStaticBatch ? Math.Min(materials.Length, mesh.subMeshCount - start) : (-1)); (int, Matrix4x4) key = (((Object)mesh).GetInstanceID(), source.Matrix); vertexCache.TryGetValue(key, out cached); pendingBuffers = new MeshBuffers(mesh, cached == null); goto IL_0742; } } } catch (Exception error) { Skip(error); goto IL_0742; } continue; IL_0742: while (pendingBuffers != null) { bool flag; try { flag = pendingBuffers.Poll(); if (flag) { reader = new MeshReader(mesh, pendingBuffers, source.Matrix, start, count, cached); if (pendingBuffers.UsedGpu) { photo.GpuMeshes++; } (int, Matrix4x4) key2 = (((Object)mesh).GetInstanceID(), source.Matrix); if (source.Renderer.isPartOfStaticBatch && cached == null && cachedBytes + (long)reader.Vertices.Length * 56L <= 50331648) { vertexCache.Add(key2, reader.Vertices); cachedBytes += (long)reader.Vertices.Length * 56L; } } } catch (Exception error2) { Skip(error2); flag = true; } if (flag) { break; } Status = $"Reading mesh {sourceIndex + 1}/{Math.Min(sources.Count, 192)}..."; yield return null; } ReleaseReadback(); if (reader == null || materials == null) { continue; } List<Vertex> vertices = new List<Vertex>(); List<int>[] indices = new List<int>[reader.Indices.Length]; for (int sub = 0; sub < reader.Indices.Length; sub++) { indices[sub] = new List<int>(); int[] sourceIndices = reader.Indices[sub]; for (int t = 0; t < sourceIndices.Length; t += 3) { if (examined >= 2000000) { break; } if (photo.Triangles >= 95904) { break; } photo.Triangles += clipper.Clip(reader.Vertices[sourceIndices[t]], reader.Vertices[sourceIndices[t + 1]], reader.Vertices[sourceIndices[t + 2]], vertices, indices[sub], 95904 - photo.Triangles); examined++; if (examined % 2048 == 0) { Status = $"Capturing {sourceIndex + 1}/{Math.Min(sources.Count, 192)}..."; yield return null; } } } if (vertices.Count != 0 && (Object)(object)source.Renderer != (Object)null) { AddPart(vertices, indices, materials, ghost, source.Renderer); capturedRenderers.Add(((Object)source.Renderer).GetInstanceID()); MeshFilter component2 = ((Component)source.Renderer).GetComponent<MeshFilter>(); Mesh val3 = (((Object)(object)component2 != (Object)null) ? component2.sharedMesh : null); if ((Object)(object)val3 != (Object)null) { capturedMeshes.Add(((Object)val3).GetInstanceID()); } } yield return null; } Status = "Checking ground..."; IEnumerator fallback = ground.Fallback(capturedRenderers, capturedMeshes); while (fallback.MoveNext()) { yield return null; } MelonLogger.Msg($"[Viewfinder] Ground: {ground.Terrains} terrains, {ground.Patches} collision patches."); if (sources.Count > 192 || photo.Triangles >= 100000 || examined >= 2000000) { photo.Limited = true; } if (!CaptureQuality.IsUsable(photo.Parts.Count, photo.Skipped) && ground.Terrains == 0 && ground.Patches == 0) { throw new InvalidOperationException($"Capture rejected: {photo.Parts.Count} surfaces and {photo.Skipped} failed mesh reads. Graphics: {SystemInfo.graphicsDeviceType}."); } Result = photo; photo = null; } private static bool IsBodyLog(Renderer renderer) { if (!((Object)(object)((Component)renderer).GetComponentInParent<PullCordDevice>() != (Object)null) && !((Object)(object)((Component)renderer).GetComponentInParent<PullCordHandle>() != (Object)null)) { return (Object)(object)((Component)renderer).GetComponentInParent<PullCordReceiver>() != (Object)null; } return true; } private void Skip(Exception error) { photo.Skipped++; if (photo.Skipped <= 3) { MelonLogger.Warning("[Viewfinder] Skipped mesh: " + error.Message); } } private void ReleaseReadback() { pendingBuffers?.Dispose(); pendingBuffers = null; if ((Object)(object)bakedMesh != (Object)null) { Object.Destroy((Object)(object)bakedMesh); } bakedMesh = null; } private void AddPart(List<Vertex> vertices, List<int>[] indices, Material[] sourceMaterials, Material ghost, Renderer? source) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Expected O, but got Unknown //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //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_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: 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_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) //IL_0225: Unknown result type (might be due to invalid IL or missing references) //IL_0246: 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_026c: Unknown result type (might be due to invalid IL or missing references) //IL_0271: Unknown result type (might be due to invalid IL or missing references) //IL_02aa: Unknown result type (might be due to invalid IL or missing references) //IL_02af: Unknown result type (might be due to invalid IL or missing references) //IL_0388: Unknown result type (might be due to invalid IL or missing references) //IL_038f: Expected O, but got Unknown //IL_03b5: Unknown result type (might be due to invalid IL or missing references) //IL_03bb: Expected O, but got Unknown //IL_0425: Unknown result type (might be due to invalid IL or missing references) //IL_041c: Unknown result type (might be due to invalid IL or missing references) Rigidbody val = ((source != null) ? ((Component)source).GetComponentInParent<Rigidbody>() : null); InteractableHost val2 = ((source != null) ? ((Component)source).GetComponentInParent<InteractableHost>() : null); int num = (((Object)(object)val != (Object)null && (!val.isKinematic || ((Object)(object)val2 != (Object)null && !val2.IsStatic)) && (Object)(object)source != (Object)null && !source.isPartOfStaticBatch) ? ((Object)val).GetInstanceID() : 0); Vector3 val3 = Vector3.zero; PhysicMaterial contactMaterial = null; if (num != 0) { if (!photo.Bodies.TryGetValue(num, out Photo.Body value)) { value = new Photo.Body(((Matrix4x4)(ref worldToPhoto)).MultiplyPoint3x4(val.worldCenterOfMass), Mathf.Clamp(val.mass, 0.01f, 1000f), Mathf.Max(0f, val.drag), Mathf.Max(0f, val.angularDrag)); photo.Bodies.Add(num, value); } val3 = value.Center; foreach (Collider componentsInChild in ((Component)val).GetComponentsInChildren<Collider>()) { if (!componentsInChild.isTrigger && (Object)(object)componentsInChild.attachedRigidbody == (Object)(object)val) { contactMaterial = componentsInChild.sharedMaterial; break; } } } Mesh val4 = new Mesh { name = "Viewfinder.Surface", indexFormat = (IndexFormat)1 }; photo.Own((Object)(object)val4); Vector3[] array = (Vector3[])(object)new Vector3[vertices.Count]; Vector3[] array2 = (Vector3[])(object)new Vector3[vertices.Count]; Vector2[] array3 = (Vector2[])(object)new Vector2[vertices.Count]; Vector2[] array4 = (Vector2[])(object)new Vector2[vertices.Count]; Color[] array5 = (Color[])(object)new Color[vertices.Count]; for (int i = 0; i < vertices.Count; i++) { Vertex vertex = vertices[i]; array[i] = new Vector3(vertex.Position.X, vertex.Position.Y, vertex.Position.Z) - val3; array2[i] = new Vector3(vertex.Normal.X, vertex.Normal.Y, vertex.Normal.Z); array3[i] = new Vector2(vertex.UV.X, vertex.UV.Y); array4[i] = new Vector2(vertex.LightUV.X, vertex.LightUV.Y); array5[i] = new Color(vertex.Color.X, vertex.Color.Y, vertex.Color.Z, vertex.Color.W); } val4.vertices = Il2CppStructArray<Vector3>.op_Implicit(array); val4.normals = Il2CppStructArray<Vector3>.op_Implicit(array2); val4.uv = Il2CppStructArray<Vector2>.op_Implicit(array3); val4.uv2 = Il2CppStructArray<Vector2>.op_Implicit(array4); val4.colors = Il2CppStructArray<Color>.op_Implicit(array5); val4.subMeshCount = indices.Length; Material[] array6 = (Material[])(object)new Material[indices.Length]; for (int j = 0; j < indices.Length; j++) { val4.SetTriangles(Il2CppStructArray<int>.op_Implicit(indices[j].ToArray()), j, false); array6[j] = Photo.Surface((sourceMaterials.Length != 0) ? sourceMaterials[Math.Min(j, sourceMaterials.Length - 1)] : null); photo.Own((Object)(object)array6[j]); } val4.RecalculateBounds(); val4.RecalculateTangents(); MaterialPropertyBlock val5 = new MaterialPropertyBlock(); if ((Object)(object)source != (Object)null) { source.GetPropertyBlock(val5); } MaterialPropertyBlock[] array7 = (MaterialPropertyBlock[])(object)new MaterialPropertyBlock[sourceMaterials.Length]; for (int k = 0; k < array7.Length; k++) { array7[k] = new MaterialPropertyBlock(); if ((Object)(object)source != (Object)null) { source.GetPropertyBlock(array7[k], k); } } photo.Parts.Add(new Photo.Part(val4, array6, ghost, val5, array7, (source != null) ? source.lightmapIndex : (-1), (Vector4)((source != null) ? source.lightmapScaleOffset : new Vector4(1f, 1f, 0f, 0f)), num, contactMaterial)); } public void Dispose() { if (imageRoutine != null) { MelonCoroutines.Stop(imageRoutine); } imageRoutine = null; exposure?.Dispose(); exposure = null; ReleaseReadback(); if (routine is IDisposable disposable) { disposable.Dispose(); } photo?.Release(); photo = null; Result?.Release(); Result = null; } } internal static class DisplayAssets { private static AssetBundle? bundle; private static Shader? shader; private static Shader? surface; public static Material NewMaterial() { //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Expected O, but got Unknown if ((Object)(object)shader == (Object)null) { if ((Object)(object)bundle == (Object)null) { using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Viewfinder.Visuals") ?? throw new InvalidOperationException("Photo shader resource is missing."); using MemoryStream memoryStream = new MemoryStream(); stream.CopyTo(memoryStream); bundle = AssetBundle.LoadFromMemory(Il2CppStructArray<byte>.op_Implicit(memoryStream.ToArray())); if ((Object)(object)bundle == (Object)null) { throw new InvalidOperationException("Photo shader bundle could not be loaded."); } } Object obj = bundle.LoadAsset("Assets/Photo.shader", Il2CppType.Of<Shader>()); shader = ((obj != null) ? ((Il2CppObjectBase)obj).TryCast<Shader>() : null); if ((Object)(object)shader == (Object)null || !shader.isSupported) { throw new InvalidOperationException("Photo shader is unsupported on this graphics device."); } ((Object)shader).hideFlags = (HideFlags)32; } return new Material(shader) { name = "Viewfinder.PhotoDisplay" }; } public static Material NewSurface() { //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Expected O, but got Unknown if ((Object)(object)shader == (Object)null) { Object.Destroy((Object)(object)NewMaterial()); } if ((Object)(object)surface == (Object)null) { Object obj = bundle.LoadAsset("Assets/Surface.shader", Il2CppType.Of<Shader>()); surface = ((obj != null) ? ((Il2CppObjectBase)obj).TryCast<Shader>() : null); if ((Object)(object)surface == (Object)null || !surface.isSupported) { throw new InvalidOperationException("Saved surface shader is unavailable."); } ((Object)surface).hideFlags = (HideFlags)32; } Material val = new Material(surface) { name = "Viewfinder.Surface" }; if (val.FindPass("DepthOnly") < 0 || val.FindPass("DepthNormals") < 0) { Object.Destroy((Object)(object)val); throw new InvalidOperationException("Photo surface depth passes are missing."); } return val; } public static void Dispose() { if ((Object)(object)bundle != (Object)null) { bundle.Unload(false); } bundle = null; shader = null; surface = null; } } internal readonly record struct Vertex(Vector3 Position, Vector3 Normal, Vector2 UV, Vector4 Color, Vector2 LightUV = default(Vector2)) { public static Vertex Lerp(Vertex a, Vertex b, float t) { return new Vertex(Vector3.Lerp(a.Position, b.Position, t), Vector3.Lerp(a.Normal, b.Normal, t), Vector2.Lerp(a.UV, b.UV, t), Vector4.Lerp(a.Color, b.Color, t), Vector2.Lerp(a.LightUV, b.LightUV, t)); } } internal readonly record struct ClipPlane(Vector3 Normal, float Offset) { public float Distance(Vector3 point) { return Vector3.Dot(Normal, point) + Offset; } } internal sealed class Clipper { private readonly ClipPlane[] planes; private readonly Vertex[] a = new Vertex[16]; private readonly Vertex[] b = new Vertex[16]; public Clipper(float fov, float aspect, float near, float far) { if (fov <= 0f || fov >= 179f || aspect <= 0f || near <= 0f || far <= near) { throw new ArgumentOutOfRangeException("fov"); } float num = MathF.Tan(fov * (float)Math.PI / 360f); float z = num * aspect; planes = new ClipPlane[6] { new ClipPlane(new Vector3(1f, 0f, z), 0f), new ClipPlane(new Vector3(-1f, 0f, z), 0f), new ClipPlane(new Vector3(0f, 1f, num), 0f), new ClipPlane(new Vector3(0f, -1f, num), 0f), new ClipPlane(Vector3.UnitZ, 0f - near), new ClipPlane(-Vector3.UnitZ, far) }; } public bool IntersectsBox(Vector3 center, Vector3 extents, Matrix4x4 worldToPhoto) { Vector3 point = Vector3.Transform(center, worldToPhoto); Matrix4x4 matrix = Matrix4x4.Transpose(worldToPhoto); ClipPlane[] array = planes; for (int i = 0; i < array.Length; i++) { ClipPlane clipPlane = array[i]; float num = Vector3.Dot(Vector3.Abs(Vector3.TransformNormal(clipPlane.Normal, matrix)), extents); if (clipPlane.Distance(point) + num < 0f) { return false; } } return true; } public int Clip(Vertex v0, Vertex v1, Vertex v2, List<Vertex> output, List<int> indices, int limit) { a[0] = v0; a[1] = v1; a[2] = v2; Vertex[] array = a; Vertex[] array2 = b; int num = 3; ClipPlane[] array3 = planes; for (int i = 0; i < array3.Length; i++) { ClipPlane clipPlane = array3[i]; int num2 = 0; Vertex vertex = array[num - 1]; float num3 = clipPlane.Distance(vertex.Position); for (int j = 0; j < num; j++) { Vertex vertex2 = array[j]; float num4 = clipPlane.Distance(vertex2.Position); if (num4 >= 0f != num3 >= 0f) { array2[num2++] = Vertex.Lerp(vertex, vertex2, num3 / (num3 - num4)); } if (num4 >= 0f) { array2[num2++] = vertex2; } vertex = vertex2; num3 = num4; } if (num2 < 3) { return 0; } num = num2; Vertex[] array4 = array2; array2 = array; array = array4; } int num5 = 0; for (int k = 1; k < num - 1; k++) { if (num5 >= limit) { break; } if (!(Vector3.Cross(array[k].Position - array[0].Position, array[k + 1].Position - array[0].Position).LengthSquared() < 1E-12f)) { int count = output.Count; output.Add(array[0]); output.Add(array[k]); output.Add(array[k + 1]); indices.Add(count); indices.Add(count + 1); indices.Add(count + 2); num5++; } } return num5; } } internal static class CaptureQuality { public static bool IsUsable(int surfaces, int failed) { if (surfaces > 0) { return failed <= surfaces; } return false; } } internal static class BufferValue { public static int Size(int format) { switch (format) { case 0: case 10: case 11: return 4; case 1: case 4: case 5: case 8: case 9: return 2; case 2: case 3: case 6: case 7: return 1; default: throw new NotSupportedException($"Vertex format {format} is unsupported."); } } public static float Read(byte[] data, int offset, int format) { return format switch { 0 => BitConverter.ToSingle(data, offset), 1 => (float)BitConverter.UInt16BitsToHalf(BitConverter.ToUInt16(data, offset)), 2 => (float)(int)data[offset] / 255f, 3 => MathF.Max(-1f, (float)(sbyte)data[offset] / 127f), 4 => (float)(int)BitConverter.ToUInt16(data, offset) / 65535f, 5 => MathF.Max(-1f, (float)BitConverter.ToInt16(data, offset) / 32767f), 6 => (int)data[offset], 7 => (sbyte)data[offset], 8 => (int)BitConverter.ToUInt16(data, offset), 9 => BitConverter.ToInt16(data, offset), 10 => BitConverter.ToUInt32(data, offset), 11 => BitConverter.ToInt32(data, offset), _ => throw new NotSupportedException($"Vertex format {format} is unsupported."), }; } } internal sealed class GroundCapture { private readonly Photo photo; private readonly Matrix4x4 toPhoto; private readonly Matrix4x4 toWorld; private readonly float depth; private readonly Clipper clipper; private readonly Action<List<Vertex>, List<int>[], Material> add; private readonly Material material; private const int Limit = 100000; public int Terrains { get; private set; } public int Patches { get; private set; } public GroundCapture(Photo photo, Matrix4x4 toPhoto, float depth, Clipper clipper, Action<List<Vertex>, List<int>[], Material> add) { //IL_000e: 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_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) this.photo = photo; this.toPhoto = toPhoto; toWorld = ((Matrix4x4)(ref toPhoto)).inverse; this.depth = depth; this.clipper = clipper; this.add = add; material = DisplayAssets.NewSurface(); material.mainTexture = photo.Image; material.SetFloat("_Projected", 1f); photo.Own((Object)(object)material); } public IEnumerator Terrain() { Bounds val = default(Bounds); foreach (Terrain terrain in (Il2CppArrayBase<Terrain>)(object)Terrain.activeTerrains) { if ((Object)(object)terrain == (Object)null || !((Behaviour)terrain).isActiveAndEnabled || !terrain.drawHeightmap || (Object)(object)terrain.terrainData == (Object)null) { continue; } TerrainData data = terrain.terrainData; Matrix4x4 m = toPhoto * ((Component)terrain).transform.localToWorldMatrix; Vector3 size = data.size; ((Bounds)(ref val))..ctor(size * 0.5f, size); if (!clipper.IntersectsBox(MeshReader.ToNumerics(((Bounds)(ref val)).center), MeshReader.ToNumerics(((Bounds)(ref val)).extents), MeshReader.ToNumerics(m))) { continue; } Vector3 val2 = Vector3.positiveInfinity; Vector3 val3 = Vector3.negativeInfinity; float num = Mathf.Tan((float)Math.PI / 6f); float num2 = num * 1.3333334f; float[] array = new float[2] { 0.15f, depth }; foreach (float num3 in array) { int[] array2 = new int[2] { -1, 1 }; foreach (int num4 in array2) { int[] array3 = new int[2] { -1, 1 }; foreach (int num5 in array3) { Transform transform = ((Component)terrain).transform; Matrix4x4 val4 = toWorld; Vector3 val5 = transform.InverseTransformPoint(((Matrix4x4)(ref val4)).MultiplyPoint3x4(new Vector3((float)num5 * num2 * num3, (float)num4 * num * num3, num3))); val2 = Vector3.Min(val2, val5); val3 = Vector3.Max(val3, val5); } } } float left = Mathf.Clamp(val2.x, 0f, size.x); float right = Mathf.Clamp(val3.x, 0f, size.x); float bottom = Mathf.Clamp(val2.z, 0f, size.z); float top = Mathf.Clamp(val3.z, 0f, size.z); if (right - left < 0.01f || top - bottom < 0.01f) { continue; } int cols = Mathf.Clamp(Mathf.CeilToInt((right - left) / 0.4f), 2, 80); int rows = Mathf.Clamp(Mathf.CeilToInt((top - bottom) / 0.4f), 2, 80); Vertex[] samples = new Vertex[(cols + 1) * (rows + 1)]; for (int row = 0; row <= rows; row++) { for (int l = 0; l <= cols; l++) { float num6 = Mathf.Lerp(left, right, (float)l / (float)cols); float num7 = Mathf.Lerp(bottom, top, (float)row / (float)rows); float num8 = num6 / size.x; float num9 = num7 / size.z; Vector3 point = ((Component)terrain).transform.TransformPoint(new Vector3(num6, data.GetInterpolatedHeight(num8, num9), num7)); Vector3 normal = ((Component)terrain).transform.TransformDirection(data.GetInterpolatedNormal(num8, num9)); samples[row * (cols + 1) + l] = Vertex(point, normal); } if (row % 4 == 3) { yield return null; } } List<Vertex> list = new List<Vertex>(); List<int> list2 = new List<int>(); for (int n = 0; n < rows; n++) { if (photo.Triangles >= 100000) { break; } for (int num10 = 0; num10 < cols; num10++) { if (photo.Triangles >= 100000) { break; } float num11 = Mathf.Lerp(left, right, ((float)num10 + 0.5f) / (float)cols) / size.x; float num12 = Mathf.Lerp(bottom, top, ((float)n + 0.5f) / (float)rows) / size.z; int num13 = Mathf.Clamp((int)(num11 * (float)data.holesResolution), 0, data.holesResolution - 1); int num14 = Mathf.Clamp((int)(num12 * (float)data.holesResolution), 0, data.holesResolution - 1); if (!data.IsHole(num13, num14)) { int num15 = n * (cols + 1) + num10; int num16 = num15 + 1; int num17 = num15 + cols + 1; int num18 = num17 + 1; Clip(samples[num15], samples[num17], samples[num16], list, list2); Clip(samples[num16], samples[num17], samples[num18], list, list2); } } } if (list2.Count != 0) { add(list, new List<int>[1] { list2 }, material); Terrains++; yield return null; } } } public IEnumerator Fallback(HashSet<int> capturedRenderers, HashSet<int> capturedMeshes) { SurfaceCoverage coverage = new SurfaceCoverage(); foreach (Photo.Part part in photo.Parts) { if (part.BodyId != 0) { continue; } Vector3[] array = ((IEnumerable<Vector3>)part.Mesh.vertices).ToArray(); for (int i = 0; i < part.Mesh.subMeshCount; i++) { int[] array2 = ((IEnumerable<int>)part.Mesh.GetTriangles(i)).ToArray(); for (int j = 0; j < array2.Length; j += 3) { coverage.Add(MeshReader.ToNumerics(array[array2[j]]), MeshReader.ToNumerics(array[array2[j + 1]]), MeshReader.ToNumerics(array[array2[j + 2]])); } } yield return null; } Vertex[] samples = new Vertex[1813]; int[] colliders = new int[samples.Length]; float y = Mathf.Tan((float)Math.PI / 6f); float x = y * 1.3333334f; Matrix4x4 val = toWorld; Vector3 origin = ((Matrix4x4)(ref val)).MultiplyPoint3x4(Vector3.zero); RaycastHit val3 = default(RaycastHit); for (int row = 0; row <= 36; row++) { for (int k = 0; k <= 48; k++) { Vector3 val2 = new Vector3(Mathf.Lerp(0f - x, x, (float)k / 48f), Mathf.Lerp(0f - y, y, (float)row / 36f), 1f); Vector3 normalized = ((Vector3)(ref val2)).normalized; val = toWorld; if (!Physics.Raycast(origin, ((Matrix4x4)(ref val)).MultiplyVector(normalized), ref val3, depth / normalized.z, -33, (QueryTriggerInteraction)1)) { continue; } Collider collider = ((RaycastHit)(ref val3)).collider; if ((Object)(object)collider == (Object)null || ((RaycastHit)(ref val3)).normal.y < 0.2f || ((Object)((Component)collider).transform.root).name.StartsWith("Viewfinder.", StringComparison.Ordinal) || (Terrains > 0 && (Object)(object)((Component)collider).GetComponent<TerrainCollider>() != (Object)null)) { continue; } Rigidbody attachedRigidbody = collider.attachedRigidbody; if ((Object)(object)attachedRigidbody != (Object)null && !attachedRigidbody.isKinematic) { continue; } Renderer component = ((Component)collider).GetComponent<Renderer>(); if (!((Object)(object)component != (Object)null) || !capturedRenderers.Contains(((Object)component).GetInstanceID())) { MeshCollider component2 = ((Component)collider).GetComponent<MeshCollider>(); Mesh val4 = (((Object)(object)component2 != (Object)null) ? component2.sharedMesh : null); if (!((Object)(object)val4 != (Object)null) || !capturedMeshes.Contains(((Object)val4).GetInstanceID())) { int num = row * 49 + k; samples[num] = Vertex(((RaycastHit)(ref val3)).point + ((RaycastHit)(ref val3)).normal * 0.001f, ((RaycastHit)(ref val3)).normal); colliders[num] = ((Object)collider).GetInstanceID(); } } } if (row % 3 == 2) { yield return null; } } Dictionary<int, (List<Vertex> Vertices, List<int> Indices)> patches = new Dictionary<int, (List<Vertex>, List<int>)>(); for (int l = 0; l < 36; l++) { if (photo.Triangles >= 100000) { break; } for (int m = 0; m < 48; m++) { if (photo.Triangles >= 100000) { break; } int num2 = l * 49 + m; int num3 = num2 + 1; int num4 = num2 + 48 + 1; int c = num4 + 1; Triangle(num2, num4, num3); Triangle(num3, num4, c); } } foreach (var value2 in patches.Values) { if (value2.Indices.Count != 0) { add(value2.Vertices, new List<int>[1] { value2.Indices }, material); Patches++; yield return null; } } void Triangle(int a, int b, int num6) { int num5 = colliders[a]; if (num5 != 0 && num5 == colliders[b] && num5 == colliders[num6] && !coverage.Covers(samples[a].Position, samples[b].Position, samples[num6].Position)) { if (!patches.TryGetValue(num5, out (List<Vertex>, List<int>) value)) { value = (new List<Vertex>(), new List<int>()); patches.Add(num5, value); } Clip(samples[a], samples[b], samples[num6], value.Item1, value.Item2); } } } private Vertex Vertex(Vector3 point, Vector3 normal) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0018: 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_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) Matrix4x4 val = toPhoto; Vector3 v = ((Matrix4x4)(ref val)).MultiplyPoint3x4(point); val = toPhoto; Vector3 val2 = ((Matrix4x4)(ref val)).MultiplyVector(normal); return new Vertex(Normal: MeshReader.ToNumerics(((Vector3)(ref val2)).normalized), Position: MeshReader.ToNumerics(v), UV: Vector2.Zero, Color: Vector4.One); } private void Clip(Vertex a, Vertex b, Vertex c, List<Vertex> vertices, List<int> indices) { photo.Triangles += clipper.Clip(a, b, c, vertices, indices, 100000 - photo.Triangles); } } internal sealed class MeshBuffers : IDisposable { private sealed class Readback { public GraphicsBuffer Buffer; public AsyncGPUReadbackRequest Request; public int Stream; public int Length; public bool Copied; } private readonly List<Readback> pending = new List<Readback>(); private readonly long started = Environment.TickCount64; private bool usedGpu; public Dictionary<int, byte[]> Streams { get; } = new Dictionary<int, byte[]>(); public byte[] Indices { get; private set; } = Array.Empty<byte>(); public bool UsedGpu => usedGpu; public MeshBuffers(Mesh mesh, bool vertices) { //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) if (mesh.vertexCount <= 0 || mesh.vertexCount > 750000) { throw new InvalidDataException("Mesh exceeds the vertex budget."); } try { if (vertices) { HashSet<int> hashSet = new HashSet<int>(); VertexAttribute[] array = new VertexAttribute[5]; RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); VertexAttribute[] array2 = (VertexAttribute[])(object)array; foreach (VertexAttribute val in array2) { if (mesh.HasVertexAttribute(val)) { hashSet.Add(mesh.GetVertexAttributeStream(val)); } } foreach (int item in hashSet) { byte[] array3 = (mesh.isReadable ? ReadCpu(mesh, item) : null); if (array3 != null) { Streams.Add(item, array3); } else { Request(mesh.GetVertexBuffer(item), item, 67108864); } } } byte[] array4 = (mesh.isReadable ? ReadCpu(mesh, -1) : null); if (array4 != null) { Indices = array4; } else { Request(mesh.GetIndexBuffer(), -1, 25165824); } } catch { Dispose(); throw; } } private static byte[]? ReadCpu(Mesh mesh, int stream) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) MeshDataArray val = default(MeshDataArray); ((MeshDataArray)(ref val))..ctor(mesh, false); try { MeshData val2 = ((MeshDataArray)(ref val))[0]; ulong num = ((stream < 0) ? MeshData.GetIndexDataSize(val2.m_Ptr) : MeshData.GetVertexDataSize(val2.m_Ptr, stream)); IntPtr intPtr = ((stream < 0) ? MeshData.GetIndexDataPtr(val2.m_Ptr) : MeshData.GetVertexDataPtr(val2.m_Ptr, stream)); if (intPtr == IntPtr.Zero || num == 0L) { return null; } if (num > 67108864) { throw new InvalidDataException("Mesh buffer exceeds the capture budget."); } byte[] array = new byte[(uint)num]; Marshal.Copy(intPtr, array, 0, array.Length); return array; } finally { ((MeshDataArray)(ref val)).Dispose(); } } private void Request(GraphicsBuffer buffer, int stream, int limit) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Invalid comparison between Unknown and I4 //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0094: 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) try { long num = (long)buffer.count * (long)buffer.stride; if (num <= 0 || num > limit) { throw new InvalidDataException("GPU mesh buffer exceeds the capture budget."); } if ((int)SystemInfo.graphicsDeviceType == 2) { byte[] array = NativeBuffer.Read(buffer.GetNativeBufferPtr(), (int)num); if (stream < 0) { Indices = array; } else { Streams.Add(stream, array); } usedGpu = true; buffer.Dispose(); return; } if (!SystemInfo.supportsAsyncGPUReadback) { throw new NotSupportedException("GPU mesh readback is unavailable on this graphics device."); } AsyncGPUReadbackRequest request = AsyncGPUReadback.Request(buffer, (int)num, 0, (Action<AsyncGPUReadbackRequest>)null); pending.Add(new Readback { Buffer = buffer, Request = request, Stream = stream, Length = (int)num }); usedGpu = true; } catch { buffer.Dispose(); throw; } } public bool Poll() { bool flag = true; foreach (Readback item in pending) { if (item.Copied) { continue; } if (!((AsyncGPUReadbackRequest)(ref item.Request)).done) { flag = false; continue; } if (((AsyncGPUReadbackRequest)(ref item.Request)).hasError) { throw new IOException("GPU mesh readback failed."); } IntPtr dataRaw = ((AsyncGPUReadbackRequest)(ref item.Request)).GetDataRaw(0); if (dataRaw == IntPtr.Zero || ((AsyncGPUReadbackRequest)(ref item.Request)).layerDataSize < item.Length) { throw new InvalidDataException("GPU mesh readback returned incomplete data."); } byte[] array = new byte[item.Length]; Marshal.Copy(dataRaw, array, 0, array.Length); if (item.Stream < 0) { Indices = array; } else { Streams.Add(item.Stream, array); } item.Copied = true; } if (!flag && Environment.TickCount64 - started > 10000) { throw new TimeoutException("GPU mesh readback timed out."); } return flag; } public void Dispose() { foreach (Readback item in pending) { try { try { if (!item.Copied && !((AsyncGPUReadbackRequest)(ref item.Request)).done) { ((AsyncGPUReadbackRequest)(ref item.Request)).WaitForCompletion(); } } finally { item.Buffer.Dispose(); } } catch (Exception ex) { MelonLogger.Warning("[Viewfinder] Buffer cleanup: " + ex.Message); } } pending.Clear(); } } internal sealed class MeshReader { private readonly record struct Channel(byte[] Bytes, int Stride, int Offset, int Format, int Dimension) { public Vector4 Get(int index, Vector4 fallback) { if (Bytes.Length == 0) { return fallback; } int num = checked(index * Stride + Offset); int num2 = BufferValue.Size(Format); if (num < 0 || num + num2 * Dimension > Bytes.Length) { throw new InvalidDataException("Vertex buffer is shorter than its layout."); } float x = BufferValue.Read(Bytes, num, Format); float y = ((Dimension > 1) ? BufferValue.Read(Bytes, num + num2, Format) : fallback.Y); float z = ((Dimension > 2) ? BufferValue.Read(Bytes, num + num2 * 2, Format) : fallback.Z); float w = ((Dimension > 3) ? BufferValue.Read(Bytes, num + num2 * 3, Format) : fallback.W); return new Vector4(x, y, z, w); } } public Vertex[] Vertices { get; } public int[][] Indices { get; } public MeshReader(Mesh mesh, MeshBuffers buffers, Matrix4x4 toPhoto, int firstPart = 0, int partCount = -1, Vertex[]? cached = null) { //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_0281: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Unknown result type (might be due to invalid IL or missing references) //IL_028a: Unknown result type (might be due to invalid IL or missing references) int vertexCount = mesh.vertexCount; if (vertexCount <= 0 || vertexCount > 750000) { throw new InvalidDataException("Mesh exceeds the vertex budget."); } if (cached != null) { Vertices = cached; } else { Channel channel = GetChannel((VertexAttribute)0); Channel channel2 = GetChannel((VertexAttribute)1); Channel channel3 = GetChannel((VertexAttribute)4); Channel channel4 = GetChannel((VertexAttribute)5); Channel channel5 = GetChannel((VertexAttribute)3); Matrix4x4 matrix = ToNumerics(toPhoto); Matrix4x4 inverse = ((Matrix4x4)(ref toPhoto)).inverse; Matrix4x4 matrix2 = ToNumerics(((Matrix4x4)(ref inverse)).transpose); Vertices = new Vertex[vertexCount]; for (int i = 0; i < vertexCount; i++) { Vector4 vector = channel.Get(i, Vector4.Zero); Vector4 vector2 = channel2.Get(i, new Vector4(0f, 1f, 0f, 0f)); Vector4 vector3 = channel3.Get(i, Vector4.Zero); Vector4 vector4 = channel4.Get(i, Vector4.Zero); Vector3 position = Vector3.Transform(new Vector3(vector.X, vector.Y, vector.Z), matrix); Vector3 value = Vector3.TransformNormal(new Vector3(vector2.X, vector2.Y, vector2.Z), matrix2); value = ((value.LengthSquared() > 1E-12f) ? Vector3.Normalize(value) : Vector3.UnitY); if (!float.IsFinite(position.X) || !float.IsFinite(position.Y) || !float.IsFinite(position.Z)) { throw new InvalidDataException("Mesh contains invalid vertex positions."); } Vertices[i] = new Vertex(position, value, new Vector2(vector3.X, vector3.Y), channel5.Get(i, Vector4.One), new Vector2(vector4.X, vector4.Y)); } } byte[] indices = buffers.Indices; int num = (((int)mesh.indexFormat == 0) ? 2 : 4); bool flag = ((Matrix4x4)(ref toPhoto)).determinant < 0f; if (partCount < 0) { partCount = mesh.subMeshCount - firstPart; } if (firstPart < 0 || partCount <= 0 || (long)firstPart + (long)partCount > mesh.subMeshCount) { throw new InvalidDataException("Renderer submesh range is invalid."); } Indices = new int[partCount][]; for (int j = 0; j < Indices.Length; j++) { SubMeshDescriptor subMesh = mesh.GetSubMesh(firstPart + j); if ((int)((SubMeshDescriptor)(ref subMesh)).topology != 0) { Indices[j] = Array.Empty<int>(); continue; } if (((SubMeshDescriptor)(ref subMesh)).indexStart < 0 || ((SubMeshDescriptor)(ref subMesh)).indexCount < 0 || ((SubMeshDescriptor)(ref subMesh)).indexCount % 3 != 0 || ((long)((SubMeshDescriptor)(ref subMesh)).indexStart + (long)((SubMeshDescriptor)(ref subMesh)).indexCount) * num > indices.Length) { throw new InvalidDataException("Mesh indices exceed the buffer."); } int[] array = new int[((SubMeshDescriptor)(ref subMesh)).indexCount]; for (int k = 0; k < array.Length; k++) { int startIndex = (((SubMeshDescriptor)(ref subMesh)).indexStart + k) * num; long num2 = ((num == 2) ? BitConverter.ToUInt16(indices, startIndex) : BitConverter.ToUInt32(indices, startIndex)); num2 += ((SubMeshDescriptor)(ref subMesh)).baseVertex; if (num2 < 0 || num2 >= vertexCount) { throw new InvalidDataException("Mesh contains an invalid index."); } array[k] = (int)num2; } if (flag) { for (int l = 0; l < array.Length; l += 3) { ref int reference = ref array[l + 1]; ref int reference2 = ref array[l + 2]; int num3 = array[l + 2]; int num4 = array[l + 1]; reference = num3; reference2 = num4; } } Indices[j] = array; } Channel GetChannel(VertexAttribute attribute) { //IL_0006: 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_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Expected I4, but got Unknown if (!mesh.HasVertexAttribute(attribute)) { return new Channel(Array.Empty<byte>(), 0, 0, 0, 0); } int vertexAttributeStream = mesh.GetVertexAttributeStream(attribute); if (!buffers.Streams.TryGetValue(vertexAttributeStream, out byte[] value2)) { throw new InvalidDataException("Mesh vertex stream was not captured."); } return new Channel(value2, mesh.GetVertexBufferStride(vertexAttributeStream), mesh.GetVertexAttributeOffset(attribute), (int)mesh.GetVertexAttributeFormat(attribute), mesh.GetVertexAttributeDimension(attribute)); } } internal static Matrix4x4 ToNumerics(Matrix4x4 m) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: 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_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) return new Matrix4x4(m.m00, m.m10, m.m20, m.m30, m.m01, m.m11, m.m21, m.m31, m.m02, m.m12, m.m22, m.m32, m.m03, m.m13, m.m23, m.m33); } internal static Vector3 ToNumerics(Vector3 v) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) return new Vector3(v.x, v.y, v.z); } } public sealed class Mod : MelonMod { private readonly List<Projection> placed = new List<Projection>(); private readonly PlacementPose wrist = new PlacementPose(); private Photo? photo; private Projection? preview; private PhotoDisplay? display; private PhotoCard? card; private CaptureJob? capture; private IEnumerator? placement; private IEnumerator? storage; private Page? library; private readonly List<Page> savedPages = new List<Page>(); private string photoName = "Photo"; private int libraryPage; private string storageStatus = ""; private bool diagnostic; private Page? menuParent; private Page? menu; private MelonPreferences_Entry<float> depth; private MelonPreferences_Entry<float> distance; private MelonPreferences_Entry<float> scale; private MelonPreferences_Entry<bool> grabPicture; private bool enabled; private bool rightHeld = true; private bool leftHeld = true; private bool stickHeld = true; private bool loading; private bool initialized; private bool imageShown; private float turn; private long nextMenu; private string message = ""; private float messageUntil; public override void OnInitializeMelon() { MelonPreferences_Category val = MelonPreferences.CreateCategory("Viewfinder"); depth = val.CreateEntry<float>("CaptureDepth", 12f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); distance = val.CreateEntry<float>("PlacementDistance", 3f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); scale = val.CreateEntry<float>("PhotoScale", 1f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); grabPicture = val.CreateEntry<bool>("GrabPicture", false, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); depth.Value = Clamp(depth.Value, 6f, 24f, 12f); distance.Value = Clamp(distance.Value, 1f, 12f, 3f); scale.Value = Clamp(scale.Value, 0.25f, 3f, 1f); Hooking.OnLevelLoading += LevelLoading; Hooking.OnLevelUnloaded += LevelUnloaded; Hooking.OnLevelLoaded += LevelLoaded; initialized = true; MelonLogger.Msg("[Viewfinder] Open BoneMenu > Viewfinder > Toggle photo mode. F7 also toggles it."); } private static float Clamp(float value, float min, float max, float fallback) { if (!float.IsFinite(value)) { return fallback; } return Mathf.Clamp(value, min, max); } public override void OnLateUpdate() { //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) if (!initialized) { return; } try { if (Environment.TickCount64 >= nextMenu) { nextMenu = Environment.TickCount64 + 3000; BuildMenu(); } Keyboard current = Keyboard.current; if (current != null && ((ButtonControl)current.f7Key).wasPressedThisFrame) { Toggle(); } if (loading || (Object)(object)Player.Head == (Object)null || (Object)(object)Player.RigManager == (Object)null || HelperMethods.IsLoading()) { display?.Hide(); if (enabled || capture != null || placement != null || storage != null) { Reset(); } return; } bool flag = (Object)(object)Player.RightController != (Object)null && Player.RightController.GetPrimaryInteractionButtonAxis() > 0.75f; bool flag2 = (Object)(object)Player.LeftController != (Object)null && Player.LeftController.GetPrimaryInteractionButtonAxis() > 0.75f; bool flag3 = flag && !rightHeld; bool flag4 = flag2 && !leftHeld; rightHeld = flag; leftHeld = flag2; BaseController rightController = Player.RightController; bool flag5 = rightController != null && rightController.GetThumbStick(); InputDevice deviceAtXRNode = InputDevices.GetDeviceAtXRNode((XRNode)5); bool flag6 = default(bool); if (((InputDevice)(ref deviceAtXRNode)).TryGetFeatureValue(CommonUsages.primary2DAxisClick, ref flag6)) { flag5 = flag5 || flag6; } bool flag7 = flag5 && !stickHeld; stickHeld = flag5; if (enabled) { if (!flag7) { BaseController rightController2 = Player.RightController; if (rightController2 == null || !rightController2.GetBButtonDown()) { BaseController rightController3 = Player.RightController; if ((rightController3 == null || !rightController3.GetThumbStickDown()) && (current == null || !((ButtonControl)current.escapeKey).wasPressedThisFrame)) { goto IL_01bc; } } } Toggle(); return; } goto IL_01bc; IL_01bc: card?.Update(); if (capture != null) { bool num = capture.Step(); if (capture.ImageReady && !imageShown) { display.Expose(capture.Image); imageShown = true; } if (!num) { FinishCapture(); } UpdateDisplay(); return; } if (storage != null) { try { if (!storage.MoveNext()) { if (storage is IDisposable disposable) { disposable.Dispose(); } storage = null; } } catch (Exception ex) { if (storage is IDisposable disposable2) { disposable2.Dispose(); } storage = null; Say("Photo file failed: " + ex.Message); MelonLogger.Error($"[Viewfinder] Photo file: {ex}"); } UpdateDisplay(); return; } if (placement != null) { if (!placement.MoveNext()) { FinishPlacement(); } UpdateDisplay(); return; } if (!enabled) { display?.Hide(); return; } if (diagnostic) { UpdateDisplay(); return; } if (current != null && ((ButtonControl)current.backspaceKey).wasPressedThisFrame) { Undo(); } if (flag4 || (current != null && ((ButtonControl)current.rKey).wasPressedThisFrame)) { TakePhoto(); } else if (flag3 || (current != null && ((ButtonControl)current.spaceKey).wasPressedThisFrame)) { if (photo == null) { TakePhoto(); } else { PlacePhoto(); } } if (preview != null && placement == null && capture == null) { UpdatePreview(); } UpdateDisplay(); } catch (Exception error) { StopOnError(error); } } private void BuildMenu() { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0050: 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_0094: 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_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Unknown result type (might be due to invalid IL or missing references) //IL_025d: 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_02c5: Unknown result type (might be due to invalid IL or missing references) if (Page.Root == null || (menuParent == Page.Root && menu != null)) { return; } menuParent = Page.Root; menu = menuParent.CreatePage("Viewfinder", Color.cyan, 0, true); menu.CreateFunction("Toggle photo mode", Color.cyan, (Action)delegate { Guard(Toggle); }); menu.CreateFunction("Take / Retake photo", Color.white, (Action)delegate { Guard(TakePhoto); }); menu.CreateFunction("Place photo", Color.green, (Action)delegate { Guard(PlacePhoto); }); menu.CreateFunction("Clear all placements", Color.yellow, (Action)delegate { Guard(ClearPlaced); }); menu.CreateFunction("Show photo image", Color.white, (Action)delegate { Guard(delegate { if (photo != null) { display?.ShowImage(); } }); }); menu.CreateFunction("Recenter wrist rotation", Color.white, (Action)wrist.Reset); menu.CreateFunction("Undo last placement", Color.yellow, (Action)delegate { Guard(Undo); }); menu.CreateFunction("Discard photo", Color.white, (Action)delegate { Guard(Discard); }); menu.CreateFunction("Test photo display", Color.white, (Action)delegate { Guard(TestDisplay); }); library = menu.CreatePage("Saved photos", Color.cyan, 0, true); savedPages.Clear(); RefreshLibrary(); Page obj = menu.CreatePage("Settings", Color.white, 0, true); obj.CreateBool("Grab picture to place", Color.cyan, grabPicture.Value, (Action<bool>)delegate(bool value) { Guard(delegate { ReleaseCard(); grabPicture.Value = value; wrist.Reset(); UpdateDisplay(); if (placement == null) { UpdatePreview(); } }); }); obj.CreateFloat("Capture depth", Color.white, depth.Value, 2f, 6f, 24f, (Action<float>)delegate(float value) { depth.Value = value; }); obj.CreateFloat("Placement distance", Color.white, distance.Value, 0.5f, 1f, 12f, (Action<float>)delegate(float value) { distance.Value = value; }); obj.CreateFloat("Photo scale", Color.white, scale.Value, 0.25f, 0.25f, 3f, (Action<float>)delegate(float value) { scale.Value = value; }); obj.CreateFloat("Rotation", Color.white, turn, 15f, -180f, 180f, (Action<float>)delegate(float value) { turn = value; }); obj.CreateFunction("Reset placement settings", Color.white, (Action)delegate { distance.Value = 3f; scale.Value = 1f; turn = 0f; }); MelonLogger.Msg("[Viewfinder] BoneMenu ready."); } private void RefreshLibrary() { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) if (library == null) { return; } foreach (Page savedPage in savedPages) { library.RemovePage(savedPage); } savedPages.Clear(); library.RemoveAll(); library.CreateString("Photo name", Color.white, photoName, (Action<string>)delegate(string value) { photoName = value; }); library.CreateFunction("Save current photo", Color.green, (Action)delegate { Guard(SavePhoto); }); library.CreateFunction("Refresh", Color.white, (Action)delegate { Guard(RefreshLibrary); }); if (libraryPage > 0) { library.CreateFunction("Previous page", Color.white, (Action)delegate { Guard(delegate { libraryPage--; RefreshLibrary(); }); }); } List<PhotoStore.Entry> list = PhotoStore.List(libraryPage * 20, 21); if (list.Count > 20) { library.CreateFunction("Next page", Color.white, (Action)delegate { Guard(delegate { libraryPage++; RefreshLibrary(); }); }); } foreach (PhotoStore.Entry entry in list.Take(20)) { Page val = library.CreatePage(entry.Name, Color.white, 0, true); savedPages.Add(val); val.CreateFunction("Load photo", Color.green, (Action)delegate { Guard(delegate { LoadPhoto(entry); }); }); val.CreateFunction("Delete photo", Color.red, (Action)delegate { Guard(delegate { PhotoStore.Delete(entry); Menu.OpenPage(library); RefreshLibrary(); Say("Saved photo deleted."); }); }); } } private void SavePhoto() { if (photo == null || diagnostic) { Say("Take a photo first."); return; } if (capture != null || placement != null || storage != null) { Say("Wait for the current action."); return; } storageStatus = "Saving photo..."; storage = PhotoStore.Save(photo, photoName, delegate(PhotoStore.Entry entry) { libraryPage = 0; RefreshLibrary(); Say("Photo saved: " + entry.Name); }); } private void LoadPhoto(PhotoStore.Entry entry) { if (!Ready()) { Say("Load a level first."); return; } CancelActions(); diagnostic = false; enabled = true; if (display == null) { display = new PhotoDisplay(); } storageStatus = "Loading photo..."; storage = PhotoStore.Load(entry, delegate(Photo next) { UsePhoto(next); Say("Photo loaded: " + entry.Name + "\nRight trigger: place"); }); } private void UsePhoto(Photo next) { ReleaseCard(); if (display == null) { display = new PhotoDisplay(); } Projection projection = new Projection(next, !grabPicture.Value); try { display.SetPhoto(next); } catch { projection.Dispose(); throw; } preview?.Dispose(); photo?.Release(); photo = next; preview = projection; diagnostic = false; wrist.Reset(); } private void TestDisplay() { if (!Ready()) { Say("Load a level first."); return; } CancelActions(); ReleaseCard(); enabled = true; diagnostic = true; if (display == null) { display = new PhotoDisplay(); } display.TestImage(); Say("Display test: eight color bars\nRight stick click: exit"); } private void Guard(Action action) { try { action(); } catch (Exception error) { StopOnError(error); } } private void StopOnError(Exception error) { CancelActions(); ReleaseCard(); enabled = false; display?.SetPhoto(null); photo?.Release(); photo = null; display?.Hide(); MelonLogger.Error($"[Viewfinder] Photo mode stopped: {error}"); } private bool Ready() { if (!loading && (Object)(object)Player.Head != (Object)null && (Object)(object)Player.RigManager != (Object)null) { return !HelperMethods.IsLoading(); } return false; } private void Toggle() { if (!Ready()) { Say("Load a level first."); return; } enabled = !enabled; diagnostic = false; rightHeld = (leftHeld = (stickHeld = true)); if (!enabled) { CancelActions(); ReleaseCard(); display?.Hide(); return; } if (display == null) { display = new PhotoDisplay(); } display.SetPhoto(photo); if (photo != null && preview == null) { preview = new Projection(photo, !grabPicture.Value); } UpdatePreview(); Say((photo == null) ? "Right trigger: take photo\nRight stick click: exit" : "Right trigger: place | Left: retake\nRight stick click: exit"); } private void TakePhoto() { //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Invalid comparison between Unknown and I4 if (!Ready() || capture != null || placement != null || storage != null) { return; } if ((Object)(object)Player.GetObjectInHand(Player.RightHand) != (Object)null) { PhotoCard? photoCard = card; if (photoCard == null || !photoCard.Held) { Say("Empty your right hand first."); return; } } ReleaseCard(); enabled = true; diagnostic = false; if (display == null) { display = new PhotoDisplay(); } display.BeginCapture(); imageShown = false; if (preview != null) { preview.Root.SetActive(false); } Camera sourceCamera = Camera.main; foreach (Camera item in (Il2CppArrayBase<Camera>)(object)Camera.allCameras) { if (((Behaviour)item).enabled && item.stereoEnabled && (int)item.cameraType == 1) { sourceCamera = item; break; } } capture = new CaptureJob(Player.Head, sourceCamera, depth.Value); } private void FinishCapture() { Photo photo = capture.TakeResult(); capture.Dispose(); capture = null; try { UsePhoto(photo); } catch { photo.Release(); throw; } UpdatePreview(); string text = ((photo.Limited || photo.Skipped > 0) ? "\nSome geometry was skipped." : ""); Say("Photo ready" + text + "\nRight trigger: place | Left: retake"); MelonLogger.Msg($"[Viewfinder] Captured {photo.Parts.Count} surfaces, {photo.Triangles} triangles, {photo.GpuMeshes} GPU meshes, {photo.Skipped} skipped meshes. Limited: {photo.Limited}."); } private void PlacePhoto() { //IL_00ea: Unknown result type (might be due to invalid IL or missing references) if (!Ready() || photo == null || capture != null || placement != null || storage != null || diagnostic) { return; } if (placed.Count >= 6) { Say("Six placements reached. Undo or clear one."); return; } if (grabPicture.Value) { PhotoCard? photoCard = card; if (photoCard == null || !photoCard.Held) { Say("Grab the picture with your right hand first."); return; } } else if ((Object)(object)Player.GetObjectInHand(Player.RightHand) != (Object)null) { Say("Empty your right hand first."); return; } enabled = true; if (preview == null) { preview = new Projection(photo, !grabPicture.Value); } if (grabPicture.Value) { try { preview.AlignThroughCard(display.PicturePlane, Player.Head.position, scale.Value); } catch (ArgumentException ex) { Say(ex.Message); return; } } else { UpdatePreview(); } placement = preview.Place(); Say("Placing photo..."); } private void FinishPlacement() { if (placement is IDisposable disposable) { disposable.Dispose(); } placement = null; placed.Add(preview); preview = null; display?.RevealScene(); preview = new Projection(photo, !grabPicture.Value); UpdatePreview(); Say($"Photo placed ({placed.Count}/6)\nRight trigger: place again | Left: retake"); } private void UpdatePreview() { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: 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_0079: 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_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) if (photo != null && preview != null) { if (grabPicture.Value) { preview.Root.SetActive(false); return; } preview.Root.SetActive(true); Transform val = (((Object)(object)Player.RightController != (Object)null) ? ((Component)Player.RightController).transform : Player.Head); Quaternion rotation = val.rotation; Quaternion tilt = photo.Tilt; Quaternion quaternion = wrist.Update(new Quaternion(rotation.x, rotation.y, rotation.z, rotation.w), new Quaternion(tilt.x, tilt.y, tilt.z, tilt.w), turn); preview.Move(val, distance.Value, scale.Value, new Quaternion(quaternion.X, quaternion.Y, quaternion.Z, quaternion.W)); } } private void UpdateDisplay() { if (display != null && !((Object)(object)Player.Head == (Object)null)) { bool flag = enabled && grabPicture.Value && photo != null && capture == null && !diagnostic; if (flag && card == null) { card = new PhotoCard(display.Card); } else if (!flag) { ReleaseCard(); } string status = (diagnostic ? "Display test: eight color bars\nRight stick click: exit" : (capture?.Status ?? ((storage != null) ? storageStatus : ((placement != null) ? "Placing photo..." : ((Time.unscaledTime < messageUntil) ? message : ((photo == null) ? "Right trigger: take photo\nRight stick click: exit" : (grabPicture.Value ? "Grab picture: right hand\nRight trigger: place | Left: retake" : "Right trigger: place | Left: retake\nRight stick click: exit"))))))); Transform hand = (((Object)(object)Player.LeftController != (Object)null) ? ((Component)Player.LeftController).transform : (((Object)(object)Player.LeftHand != (Object)null) ? ((Component)Player.LeftHand).transform : null)); PhotoDisplay? photoDisplay = display; Transform head = Player.Head; bool active = enabled && (capture == null || capture.ImageReady); bool framing = !diagnostic && photo == null && capture == null && storage == null; bool held = card?.Held ?? false; photoDisplay.Update(head, hand, active, framing, status, null, held); card?.Follow(); } } private void Say(string text) { message = text; messageUntil = Time.unscaledTime + 5f; MelonLogger.Msg("[Viewfinder] " + text.Replace('\n', ' ')); } private void ReleaseCard() { card?.Dispose(); card = null; } private void Undo() { if (placement == null && placed.Count != 0) { int index = placed.Count - 1; placed[index].Dispose(); placed.RemoveAt(index); Say("Last placement removed."); } } private void ClearPlaced() { bool flag = placement != null; if (flag) { if (placement is IDisposable disposable) { disposable.Dispose(); } placement = null; preview?.Dispose(); preview = null; } foreach (Projection item in placed) { item.Dispose(); } placed.Clear(); if (flag && photo != null) { preview = new Projection(photo, !grabPicture.Value); UpdatePreview(); } Say("All placements cleared."); } private void CancelActions() { if (storage is IDisposable disposable) { disposable.Dispose(); } storage = null; capture?.Dispose(); capture = null; if (placement is IDisposable disposable2) { disposable2.Dispose(); } placement = null; preview?.Dispose(); preview = null; } private void Discard() { CancelActions(); ReleaseCard(); display?.SetPhoto(null); photo?.Release(); photo = null; Say("Photo discarded."); } private void Reset() { enabled = false; diagnostic = false; CancelActions(); ReleaseCard(); foreach (Projection item in placed) { item.Dispose(); } placed.Clear(); display?.Dispose(); display = null; photo?.Release(); photo = null; rightHeld = (leftHeld = (stickHeld = true)); wrist.Reset(); } private void LevelLoading(LevelInfo _) { loading = true; Reset(); } private void LevelLoaded(LevelInfo _) { loading = false; Reset(); } private void LevelUnloaded() { loading = true; Reset(); } public override void OnDeinitializeMelon() { if (initialized) { Hooking.OnLevelLoading -= LevelLoading; Hooking.OnLevelLoaded -= LevelLoaded; Hooking.OnLevelUnloaded -= LevelUnloaded; Reset(); DisplayAssets.Dispose(); initialized = false; } } } internal static class NativeBuffer { internal struct Description { public uint ByteWidth; public uint Usage; public uint BindFlags; public uint CpuAccessFlags; public uint MiscFlags; public uint StructureStride; } private struct Mapped { public nint Data; public uint RowPitch; public uint DepthPitch; } public unsafe static byte[] Read(nint pointer, int length) { if (!OperatingSystem.IsWindows()) { throw new PlatformNotSupportedException("Native mesh reads require Windows."); } if (pointer == 0 || length <= 0 || length > 67108864) { throw new ArgumentOutOfRangeException("length"); } nint num = 0; nint num2 = 0; nint num3 = 0; nint num4 = 0; nint num5 = 0; bool flag = false; int num6 = 0; try { Guid guid = new Guid("48570b85-d1ee-4fcd-a250-eb350722b037"); Marshal.ThrowExceptionForHR(Query(pointer, &guid, &num)); Description description = default(Description); ((delegate* unmanaged[Stdcall]<IntPtr, Description*, void>)Table(num)[10])(num, &description); if (length > description.ByteWidth) { throw new InvalidDataException("Requested mesh bytes exceed the native buffer."); } ((delegate* unmanaged[Stdcall]<IntPtr, IntPtr*, void>)Table(num)[3])(num, &num2); ((delegate* unmanaged[Stdcall]<IntPtr, IntPtr*, void>)Table(num2)[40])(num2, &num3); Guid guid2 = new Guid("9b7e4e00-342c-4106-a19f-4f2704f689f0"); Marshal.ThrowExceptionForHR(Query(num3, &guid2, &num5)); num6 = ((delegate* unmanaged[Stdcall]<IntPtr, int, int>)Table(num5)[5])(num5, 1); ((delegate* unmanaged[Stdcall]<IntPtr, void>)Table(num5)[3])(num5); flag = true; description.Usage = 3u; description.BindFlags = 0u; description.CpuAccessFlags = 131072u; description.MiscFlags = 0u; description.StructureStride = 0u; Marshal.ThrowExceptionForHR(((delegate* unmanaged[Stdcall]<IntPtr, Description*, IntPtr, IntPtr*, int>)Table(num2)[3])(num2, &description, (IntPtr)0, &num4)); ((delegate* unmanaged[Stdcall]<IntPtr, IntPtr, IntPtr, void>)Table(num3)[47])(num3, num4, num); Mapped mapped = default(Mapped); Marshal.ThrowExceptionForHR(((delegate* unmanaged[Stdcall]<IntPtr, IntPtr, uint, uint, uint, Mapped*, int>)Table(num3)[14])(num3, num4, 0u, 1u, 0u, &mapped)); try { if (mapped.Data == 0) { throw new InvalidDataException("The native mesh buffer could not be mapped."); } byte[] array = new byte[length]; Marshal.Copy(mapped.Data, array, 0, length); return array; } finally { ((delegate* unmanaged[Stdcall]<IntPtr, IntPtr, uint, void>)Table(num3)[15])(num3, num4, 0u); } } finally { Release(num4); if (flag) { ((delegate* unmanaged[Stdcall]<IntPtr, void>)Table(num5)[4])(num5); ((delegate* unmanaged[Stdcall]<IntPtr, int, int>)Table(num5)[5])(num5, num6); } Release(num5); Release(num3); Release(num2); Release(num); } } private unsafe static nint* Table(nint instance) { return *(nint**)instance; } private unsafe static int Query(nint instance, Guid* id, nint* result) { return ((delegate* unmanaged[Stdcall]<IntPtr, Guid*, IntPtr*, int>)(*Table(instance)))(instance, id, result); } internal unsafe static void Release(nint instance) { if (instance != 0) { ((delegate* unmanaged[Stdcall]<IntPtr, uint>)Table(instance)[2])(instance); } } } internal sealed class Photo { internal sealed record Part(Mesh Mesh, Material[] Materials, Material Ghost, MaterialPropertyBlock Properties, MaterialPropertyBlock[] Slots, int LightmapIndex, Vector4 LightmapScaleOffset, int BodyId, PhysicMaterial? ContactMaterial); internal sealed record Body(Vector3 Center, float Mass, float Drag, float AngularDrag); public readonly List<Part> Parts = new List<Part>(); public readonly Dictionary<int, Body> Bodies = new Dictionary<int, Body>(); private readonly List<Object> assets = new List<Object>(); private int references = 1; public Texture Image { get; } public Quaternion Tilt { get; } public float Anchor { get; } public int Triangles { get; set; } public int Skipped { get; set; } public int GpuMeshes { get; set; } public bool Limited { get; set; } public Photo(Texture image, Quaternion tilt, float anchor) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) Image = image; Tilt = tilt; Anchor = anchor; assets.Add((Object)(object)image); } public void Retain() { references++; } public void Release() { if (--references != 0) { return; } foreach (Object asset in assets) { if (asset != (Object)null) { Object.Destroy(asset); } } assets.Clear(); Parts.Clear(); Bodies.Clear(); } public void Own(Object asset) { assets.Add(asset); } public static Material Surface(Material? source) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected O, but got Unknown if ((Object)(object)source != (Object)null) { return new Material(source); } Shader obj = Shader.Find("Universal Render Pipeline/Unlit") ?? Shader.Find("Unlit/Texture") ?? Shader.Find("Sprites/Default"); if ((Object)(object)obj == (Object)null) { throw new InvalidOperationException("No photo surface shader is available."); } Material val = new Material(obj); try { val.renderQueue = 3005; Set(val, "_Cull", 0f); Set(val, "_ZWrite", 1f); Set(val, "_Surface", 0f); return val; } catch { Object.Destroy((Object)(object)val); throw; } } public static Material Ghost() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) Material obj = Surface(null); Tint(obj, new Color(0.15f, 0.85f, 1f, 0.3f)); obj.renderQueue = 3010; Set(obj, "_Surface", 1f); Set(obj, "_SrcBlend", 5f); Set(obj, "_DstBlend", 10f); Set(obj, "_ZWrite", 0f); obj.EnableKeyword("_SURFACE_TYPE_TRANSPARENT"); return obj; } public static void Tint(Material material, Color color) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) if (material.HasProperty("_Color")) { material.SetColor("_Color", color); } if (material.HasProperty("_BaseColor")) { material.SetColor("_BaseColor", color); } } private static void Set(Material material, string property, float value) { if (material.HasProperty(property)) { material.SetFloat(property, value); } } } internal static class PhotoAlignment { public static Matrix4x4 Create(Vector3 offset, float width, float height, float fieldOfView, float aspect) { if (!float.IsFinite(offset.X) || !float.IsFinite(offset.Y) || !float.IsFinite(offset.Z) || !float.IsFinite(width) || !float.IsFinite(height) || width <= 0f || height <= 0f || MathF.Abs(offset.Z) < 0.03f || !float.IsFinite(fieldOfView) || fieldOfView <= 0f || fieldOfView >= 180f || !float.IsFinite(aspect) || aspect <= 0f) { throw new ArgumentException("Hold the picture facing your view."); } float num = height / (2f * MathF.Tan(fieldOfView * (float)Math.PI / 360f)); return new Matrix4x4(width / (height * aspect), 0f, 0f, 0f, 0f, 1f, 0f, 0f, offset.X / num, offset.Y / num, offset.Z / num, 0f, 0f, 0f, 0f, 1f); } } internal sealed class PhotoData { internal sealed record Body(Vector3 Center, float Mass, float Drag, float AngularDrag); internal sealed record Surface(int Texture, Vector4 Color, Vector4 Tiling, bool Projected, Vector3 Offset, int Wrap); internal sealed record Part(Vertex[] Vertices, int[][] Indices, Surface[] Materials, int BodyId); public string Name = "Photo"; public Quaternion Tilt = Quaternion.Identity; public float Anchor = 3f; public byte[] Image = Array.Empty<byte>(); public readonly List<byte[]> Textures = new List<byte[]>(); public readonly Dictionary<int, Body> Bodies = new Dictionary<int, Body>(); public readonly List<Part> Parts = new List<Part>(); } internal static class PhotoArchive { private const int Magic = 1447448648; private const int MaxBytes = 268435456; private const int MaxImage = 12582912; public static void Write(string path, PhotoData data, CancellationToken cancel = default(CancellationToken)) { cancel.ThrowIfCancellationRequested(); if (data.Parts.Count == 0 || data.Parts.Count > 256 || data.Bodies.Count > 192 || data.Textures.Count > 256) { throw new InvalidDataException("Photo exceeds the scene budget."); } long num = 0L; long num2 = 0L; long num3 = (long)data.Image.Length + 16384L; foreach (PhotoData.Part part in data.Parts) { num += part.Vertices.Length; num2 += ((IEnumerable<int[]>)part.Indices).Sum((Func<int[], long>)((int[] array3) => array3.Length)); if (part.Indices.Length == 0 || part.Indices.Length > 128 || part.Materials.Length != part.Indices.Length) { throw new InvalidDataException("Photo has an invalid material count."); } num3 += (long)part.Materials.Length * 64L + 32; } num3 += num * 56 + num2 * 4 + ((IEnumerable<byte[]>)data.Textures).Sum((Func<byte[], long>)((byte[] texture) => texture.Length)); if (num > 350000 || num2 > 350000 || num3 > 268435456) { throw new InvalidDataException("Photo exceeds the archive budget."); } string text = path + ".tmp"; try { using (FileStream stream = new FileStream(text, FileMode.CreateNew, FileAccess.Write, FileShare.None)) { using ZipArchive zipArchive = new ZipArchive(stream, ZipArchiveMode.Create); using (BinaryWriter binaryWriter = new BinaryWriter(zipArchive.CreateEntry("scene.bin", CompressionLevel.Fastest).Open(), Encoding.UTF8)) { binaryWriter.Write(1447448648); binaryWriter.Write(1); byte[] bytes = Encoding.UTF8.GetBytes(data.Name); if (bytes.Length > 192) { throw new InvalidDataException("Photo name is too long."); } binaryWriter.Write(bytes.Length); binaryWriter.Write(bytes); Write(binaryWriter, new Vector4(data.Tilt.X, data.Tilt.Y, data.Tilt.Z, data.Tilt.W)); binaryWriter.Write(data.Anchor); binaryWriter.Write(data.Bodies.Count); foreach (KeyValuePair<int, PhotoData.Body> body in data.Bodies) { binaryWriter.Write(body.Key); Write(binaryWriter, body.Value.Center); binaryWriter.Write(body.Value.Mass); binaryWriter.Write(body.Value.Drag); binaryWriter.Write(body.Value.AngularDrag); } binaryWriter.Write(data.Textures.Count); binaryWriter.Write(data.Parts.Count); foreach (PhotoData.Part part2 in data.Parts) { cancel.ThrowIfCancellationRequested(); binaryWriter.Write(part2.BodyId); binaryWriter.Write(part2.Vertices.Length); Vertex[] vertices = part2.Vertices; for (int num4 = 0; num4 < vertices.Length; num4++) { Vertex vertex = vertices[num4]; Write(binaryWriter, vertex.Position); Write(binaryWriter, vertex.Normal); Write(binaryWriter, vertex.UV); Write(binaryWriter, vertex.Color); Write(binaryWriter, vertex.LightUV); } binaryWriter.Write(part2.Indices.Length); int[][] indices = part2.Indices; foreach (int[] array in indices) { binaryWriter.Write(array.Length); int[] array2 = array; foreach (int value in array2) { binaryWriter.Write(value); } } binaryWriter.Write(part2.Materials.Length); PhotoData.Surface[] materials = part2.Materials; foreach (PhotoData.Surface surface in materials) { binaryWriter.Write(surface.Texture); Write(binaryWriter, surface.Color); Write(binaryWriter, surface.Tiling); binaryWriter.Write(surface.Projected); Write(binaryWriter, surface.Offset); binaryWriter.Write(surface.Wrap); } } } AddImage(zipArchive, "photo.png", data.Image); for (int num6 = 0; num6 < data.Textures.Count; num6++) { cancel.ThrowIfCancellationRequested(); AddImage(zipArchive, $"textures/{num6}.png", data.Textures[num6]); } } cancel.ThrowIfCancellationRequested(); if (new FileInfo(text).Length > 268435456) { throw new InvalidDataException("Photo exceeds the file budget."); } File.Move(text, path); } finally { if (File.Exists(text)) { File.Delete(text); } } } public static string ReadName(string path) { using ZipArchive zip = Open(path); using BinaryReader reader = Reader(zip); return Header(reader); } public static PhotoData Read(string path, CancellationToken cancel = default(CancellationToken)) { using ZipArchive zip = Open(path); using BinaryReader binaryReader = Reader(zip); PhotoData photoData = new PhotoData { Name = Header(binaryReader) }; Vector4 vector = Read4(binaryReader); if (vector.LengthSquared() < 0.5f || vector.LengthSquared() > 1.5f) { throw new InvalidDataException("Invalid photo rotation."); } photoData.Tilt = Quaternion.Normalize(new Quaternion(vector.X, vector.Y, vector.Z, vector.W)); photoData.Anchor = Number(binaryReader); if (photoData.Anchor < 0.1f || photoData.Anchor > 100f) { throw new InvalidDataException("Invalid photo distance."); } int num = Count(binaryReader, 192); for (int i = 0; i < num; i++) { int num2 = binaryReader.ReadInt32(); PhotoData.Body body = new PhotoData.Body(Read3(binaryReader), Number(binaryReader), Number(binaryReader), Number(binaryReader)); if (num2 == 0 || body.Mass <= 0f || body.Mass > 1000f || body.Drag < 0f || body.AngularDrag < 0f || !photoData.Bodies.TryAdd(num2, body)) { throw new InvalidDataException("Invalid saved prop."); } } int num3 = Count(binaryReader, 256); int num4 = Count(binaryReader, 256); if (num4 == 0) { throw new InvalidDataException("The saved photo has no geometry."); } int num5 = 0; int num6 = 0; for (int j = 0; j < num4; j++) { cancel.ThrowIfCancellationRequested(); int num7 = binaryReader.ReadInt32(); if (num7 != 0 && !photoData.Bodies.ContainsKey(num7)) { throw new InvalidDataException("A saved prop is missing its body."); } int num8 = Count(binaryReader, 350000); num5 += num8; if (num5 > 350000) { throw new InvalidDataException("Saved geometry exceeds the vertex budget."); } Vertex[] array = new Vertex[num8]; for (int k = 0; k < num8; k++) { array[k] = new Vertex(Read3(binaryReader), Read3(binaryReader), Read2(binaryReader), Read4(binaryReader), Read2(binaryReader)); } int[][] array2 = new int[Count(binaryReader, 128)][]; if (array2.Length == 0) { throw new InvalidDataException("A saved surface has no submeshes."); } for (int l = 0; l < array2.Length; l++) { int num9 = Count(binaryReader, 350000); num6 += num9; if (num9 % 3 != 0 || num6 > 350000) { throw new InvalidDataException("Saved geometry exceeds the triangle budget."); } array2[l] = new int[num9]; for (int m = 0; m < num9; m++) { int num10 = binaryReader.ReadInt32(); if (num10 < 0 || num10 >= num8) { throw new InvalidDataException("Invalid saved mesh index."); } array2[l][m] = num10; } } int num11 = Count(binaryReader, 128); if (num11 != array2.Length) { throw new InvalidDataException("Saved material count is invalid."); } PhotoData.Surface[] array3 = new PhotoData.Surface[num11]; for (int n = 0; n < array3.Length; n++) { int num12 = binaryReader.ReadInt32(); if (num12 < -1 || num12 >= num3) { throw new InvalidDataException("Invalid saved texture index."); } array3[n] = new PhotoData.Surface(num12, Read4(binaryReader), Read4(binaryReader), binaryReader.ReadBoolean(), Read3(binaryReader), Count(binaryReader, 3)); } photoData.Parts.Add(new PhotoData.Part(array, array2, array3, num7)); } photoData.Image = ReadImage(zip, "photo.png"); for (int num13 = 0; num13 < num3; num13++) { cancel.ThrowIfCancellationRequested(); photoData.Textures.Add(ReadImage(zip, $"textures/{num13}.png")); } return photoData; } private static ZipArchive Open(string path) { if (new FileInfo(path).Length > 268435456) { throw new InvalidDataException("Saved photo is too large."); } ZipArchive zipArchive = ZipFile.OpenRead(path); try { long num = 0L; if (zipArchive.Entries.Count > 260) { throw new InvalidDataException("Saved photo has too many entries."); } foreach (ZipArchiveEntry entry in zipArchive.Entries) { num += entry.Length; if (num > 268435456) { throw new InvalidDataException("Saved photo exceeds the memory budget."); } } return zipArchive; } catch { zipArchive.Dispose(); throw; } } private static BinaryReader Reader(ZipArchive zip) { return new BinaryReader((zip.GetEntry("scene.bin") ?? throw new InvalidDataException("Saved photo is missing its scene.")).Open(), Encoding.UTF8); } private static string Header(BinaryReader reader) { if (reader.ReadInt32() != 1447448648 || reader.ReadInt32() != 1) { throw new InvalidDataException("Unsupported saved photo format."); } int num = Count(reader, 192); byte[] array = reader.ReadBytes(num); if (array.Length != num) { throw new EndOfStreamException(); } return Encoding.UTF8.GetString(array); } private static void AddImage(ZipArchive zip, string name, byte[] bytes) { if (bytes.Length == 0 || bytes.Length > 12582912) { throw new InvalidDataException("Photo texture exceeds the image budget."); } using Stream stream = zip.CreateEntry(name, CompressionLevel.NoCompression).Open(); stream.Write(bytes); } private static byte[] ReadImage(ZipArchive zip, string name) { ZipArchiveEntry zipArchiveEntry = zip.GetEntry(name) ?? throw new InvalidDataException("Saved photo is missing an image."); if (zipArchiveEntry.Length < 24 || zipArchiveEntry.Length > 12582912) { throw new InvalidDataException("Invalid saved image size."); } using Stream stream = zipArchiveEntry.Open(); using MemoryStream memoryStream = new MemoryStream(); stream.CopyTo(memoryStream); byte[] array = memoryStream.ToArray(); if (!array.AsSpan(0, 8).SequenceEqual(new byte[8] { 137, 80, 78, 71, 13, 10, 26, 10 })) { throw new InvalidDataException("Saved image is not a PNG."); } uint num = BinaryPrimitives.ReadUInt32BigEndian(array.AsSpan(16, 4)); uint num2 = BinaryPrimitives.ReadUInt32BigEndian(array.AsSpan(20, 4)); if (num == 0 || num2 == 0 || num > 2048 || num2 > 2048) { throw new InvalidDataException("Saved image dimensions exceed the budget."); } return array; } private static int Count(BinaryReader reader, int max) { int num = reader.ReadInt32(); if (num < 0 || num > max) { throw new InvalidDataException("Saved photo contains an invalid count."); } return num; } private static float Number(BinaryReader reader) { float num = reader.ReadSingle(); if (!float.IsFinite(num)) { throw new InvalidDataException("Saved photo contains an invalid number."); } return num; } private static Vector2 Read2(BinaryReader r) { return new Vector2(Number(r), Number(r)); } private static Vector3 Read3(BinaryReader r) { return new Vector3(Number(r), Number(r), Number(r)); } private static Vector4 Read4(BinaryReader r) { return new Vector4(Number(r), Number(r), Number(r), Number(r)); } private static void Write(BinaryWriter w, Vector2 v) { w.Write(v.X); w.Write(v.Y); } private static void Write(BinaryWriter w, Vector3 v) { w.Write(v.X); w.Write(v.Y); w.Write(v.Z); } private static void Write(BinaryWriter w, Vector4 v) { w.Write(v.X); w.Write(v.Y); w.Write(v.Z); w.Write(v.W); } } internal sealed class PhotoCard : IDisposable { private readonly GameObject root; private readonly Transform visual; private readonly Rigidbody body; private readonly BoxCollider collider; private readonly InteractableHost host; private bool disposed; public Transform Aim => root.transform; public bool Held { get; private set; } public PhotoCard(Transform visual) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result ty