Decompiled source of Viewfinder v1.0.0

Mods/Viewfinder.dll

Decompiled 5 hours ago
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