Decompiled source of Valerius Prefab Baker v0.9.0

plugins/ValeriusPrefabBaker.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn;
using Jotunn.Entities;
using Jotunn.Managers;
using Unity.Collections;
using UnityEngine;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("ValeriusPrefabBaker")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ValeriusPrefabBaker")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")]
[assembly: AssemblyFileVersion("0.1.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.0.0")]
namespace ValeriusPrefabBaker;

internal static class BlueprintObjExporter
{
	private sealed class CachedSubmesh
	{
		public int[] Triangles;

		public string MaterialKey;

		public Color Color;

		public bool HasColor;

		public string TexturePath;
	}

	private sealed class CachedMesh
	{
		public string Name;

		public Vector3[] Vertices;

		public Vector2[] UVs;

		public List<CachedSubmesh> Submeshes = new List<CachedSubmesh>();
	}

	private sealed class CachedPrefab
	{
		public List<CachedMesh> Meshes = new List<CachedMesh>();

		public List<string> Warnings = new List<string>();
	}

	private sealed class ExportMaterial
	{
		public Color Color;

		public bool HasColor;

		public string TexturePath;
	}

	internal sealed class RawMesh
	{
		public Vector3[] Vertices;

		public Vector2[] UVs;

		public Vector3[] Normals = Array.Empty<Vector3>();

		public Vector4[] Tangents = Array.Empty<Vector4>();

		public List<int[]> Triangles = new List<int[]>();
	}

	private static readonly CultureInfo Invariant = CultureInfo.InvariantCulture;

	private const int PiecesPerFrame = 10;

	public static IEnumerator Export()
	{
		if ((Object)(object)Player.m_localPlayer == (Object)null)
		{
			Logger.LogWarning((object)"Enter a world before starting the dungeon export.");
			yield break;
		}
		string text = Path.Combine(Paths.ConfigPath, "PlanBuild");
		string outputDirectory = Path.Combine(Paths.BepInExRootPath, "ValeriusDungeonExport");
		if (!Directory.Exists(text))
		{
			Logger.LogError((object)("Blueprint directory not found: " + text));
			yield break;
		}
		string[] files = Directory.GetFiles(text, "*.blueprint");
		if (files.Length == 0)
		{
			Logger.LogWarning((object)("No .blueprint files found in: " + text));
			yield break;
		}
		Directory.CreateDirectory(outputDirectory);
		Dictionary<string, CachedPrefab> prefabCache = new Dictionary<string, CachedPrefab>();
		string[] array = files;
		foreach (string input in array)
		{
			IEnumerator routine = ExportBlueprint(input, outputDirectory, prefabCache);
			while (routine.MoveNext())
			{
				yield return routine.Current;
			}
		}
		Logger.LogInfo((object)"All blueprint exports finished.");
	}

	private static IEnumerator ExportBlueprint(string input, string outputDirectory, Dictionary<string, CachedPrefab> prefabCache)
	{
		string fileName = Path.GetFileNameWithoutExtension(input);
		string path = Path.Combine(outputDirectory, fileName + ".obj");
		string mtlPath = Path.Combine(outputDirectory, fileName + ".mtl");
		string manifestPath = Path.Combine(outputDirectory, fileName + "_manifest.txt");
		Dictionary<string, string> materialIds = new Dictionary<string, string>();
		Dictionary<string, ExportMaterial> materials = new Dictionary<string, ExportMaterial>();
		StringBuilder manifest = new StringBuilder();
		int vertexOffset = 0;
		int uvOffset = 0;
		int pieceCount = 0;
		int meshCount = 0;
		int missingCount = 0;
		int materialNumber = 0;
		int processedSinceYield = 0;
		bool readingPieces = false;
		Logger.LogInfo((object)("Starting export: " + Path.GetFileName(input)));
		using (StreamWriter obj = new StreamWriter(path, append: false, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)))
		{
			obj.WriteLine("# Exported from Valheim by ValeriusPrefabBaker");
			obj.WriteLine("mtllib " + fileName + ".mtl");
			foreach (string item in File.ReadLines(input))
			{
				string text = item.Trim();
				if (text == "#Pieces")
				{
					readingPieces = true;
				}
				else
				{
					if (!readingPieces || text.Length == 0 || text.StartsWith("#"))
					{
						continue;
					}
					try
					{
						string[] array = text.Split(new char[1] { ';' });
						if (array.Length < 10)
						{
							continue;
						}
						string text2 = array[0].Trim();
						if (text2.Length == 0)
						{
							continue;
						}
						if (!TryParsePiece(array, out var position, out var rotation, out var scale))
						{
							manifest.AppendLine("INVALID TRANSFORM: " + text2);
							missingCount++;
							continue;
						}
						if (prefabCache.TryGetValue(text2, out var value))
						{
							goto IL_0335;
						}
						GameObject prefab = PrefabManager.Instance.GetPrefab(text2);
						if ((Object)(object)prefab == (Object)null)
						{
							manifest.AppendLine("MISSING PREFAB: " + text2);
							missingCount++;
							continue;
						}
						value = (prefabCache[text2] = CachePrefab(prefab, text2, outputDirectory));
						foreach (string warning in value.Warnings)
						{
							manifest.AppendLine("MESH WARNING: " + warning);
						}
						Logger.LogInfo((object)("Cached prefab: " + text2 + " " + $"({value.Meshes.Count} meshes)"));
						goto IL_0335;
						IL_0335:
						if (value.Meshes.Count == 0)
						{
							manifest.AppendLine("ZERO GEOMETRY: " + text2);
							missingCount++;
							continue;
						}
						pieceCount++;
						manifest.AppendLine($"PIECE: {text2} | position={position} " + $"rotation={((Quaternion)(ref rotation)).eulerAngles} scale={scale}");
						Matrix4x4 val = Matrix4x4.TRS(position, rotation, scale);
						foreach (CachedMesh mesh in value.Meshes)
						{
							int num = vertexOffset;
							int num2 = uvOffset;
							obj.WriteLine($"o piece_{pieceCount}_{SafeName(mesh.Name)}");
							Vector3[] vertices = mesh.Vertices;
							foreach (Vector3 val2 in vertices)
							{
								Vector3 val3 = ((Matrix4x4)(ref val)).MultiplyPoint3x4(val2);
								obj.WriteLine("v " + F(val3.x) + " " + F(val3.y) + " " + F(val3.z));
							}
							vertexOffset += mesh.Vertices.Length;
							Vector2[] uVs = mesh.UVs;
							foreach (Vector2 val4 in uVs)
							{
								obj.WriteLine("vt " + F(val4.x) + " " + F(val4.y));
							}
							uvOffset += mesh.UVs.Length;
							foreach (CachedSubmesh submesh in mesh.Submeshes)
							{
								if (!materialIds.TryGetValue(submesh.MaterialKey, out var value2))
								{
									int i = materialNumber + 1;
									materialNumber = i;
									value2 = $"mat_{i:0000}";
									materialIds[submesh.MaterialKey] = value2;
									materials[value2] = new ExportMaterial
									{
										Color = submesh.Color,
										HasColor = submesh.HasColor,
										TexturePath = submesh.TexturePath
									};
								}
								obj.WriteLine("usemtl " + value2);
								int[] triangles = submesh.Triangles;
								for (int j = 0; j + 2 < triangles.Length; j += 3)
								{
									int num3 = triangles[j];
									int num4 = triangles[j + 1];
									int num5 = triangles[j + 2];
									if (num3 >= 0 && num4 >= 0 && num5 >= 0 && num3 < mesh.Vertices.Length && num4 < mesh.Vertices.Length && num5 < mesh.Vertices.Length)
									{
										if (mesh.UVs.Length == mesh.Vertices.Length)
										{
											obj.WriteLine($"f {num + num3 + 1}/{num2 + num3 + 1} " + $"{num + num4 + 1}/{num2 + num4 + 1} " + $"{num + num5 + 1}/{num2 + num5 + 1}");
										}
										else
										{
											obj.WriteLine($"f {num + num3 + 1} " + $"{num + num4 + 1} " + $"{num + num5 + 1}");
										}
									}
								}
							}
							meshCount++;
						}
						goto IL_07f5;
					}
					catch (Exception ex)
					{
						manifest.AppendLine("EXPORT ERROR: " + text + " | " + ex.Message);
						missingCount++;
						Logger.LogWarning((object)("Could not export a blueprint piece: " + ex.Message));
						goto IL_07f5;
					}
				}
				continue;
				IL_07f5:
				processedSinceYield++;
				if (processedSinceYield >= 10)
				{
					processedSinceYield = 0;
					yield return null;
				}
			}
		}
		using (StreamWriter streamWriter = new StreamWriter(mtlPath, append: false, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)))
		{
			foreach (KeyValuePair<string, ExportMaterial> item2 in materials)
			{
				streamWriter.WriteLine("newmtl " + item2.Key);
				ExportMaterial value3 = item2.Value;
				Color color = value3.Color;
				streamWriter.WriteLine("Kd " + F(color.r) + " " + F(color.g) + " " + F(color.b));
				streamWriter.WriteLine("d " + F(color.a));
				if (!string.IsNullOrEmpty(value3.TexturePath))
				{
					streamWriter.WriteLine("map_Kd " + value3.TexturePath);
				}
				streamWriter.WriteLine();
			}
		}
		manifest.Insert(0, $"Pieces exported: {pieceCount}\n" + $"Mesh filters exported: {meshCount}\n" + $"Missing/errors: {missingCount}\n\n");
		manifest.AppendLine();
		manifest.AppendLine("Material mapping (OBJ ID -> source material):");
		foreach (KeyValuePair<string, string> item3 in materialIds)
		{
			manifest.AppendLine(item3.Value + " -> " + item3.Key);
		}
		File.WriteAllText(manifestPath, manifest.ToString(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
		Logger.LogInfo((object)($"Finished {fileName}: pieces={pieceCount}, " + $"meshes={meshCount}, errors={missingCount}"));
		yield return null;
	}

	private static CachedPrefab CachePrefab(GameObject source, string prefabName, string outputDirectory)
	{
		//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_0100: Unknown result type (might be due to invalid IL or missing references)
		//IL_0108: Unknown result type (might be due to invalid IL or missing references)
		//IL_010d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0112: Unknown result type (might be due to invalid IL or missing references)
		//IL_0162: Unknown result type (might be due to invalid IL or missing references)
		//IL_0167: Unknown result type (might be due to invalid IL or missing references)
		//IL_016c: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_030b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0310: Unknown result type (might be due to invalid IL or missing references)
		//IL_0302: Unknown result type (might be due to invalid IL or missing references)
		CachedPrefab cachedPrefab = new CachedPrefab();
		Matrix4x4 worldToLocalMatrix = source.transform.worldToLocalMatrix;
		MeshFilter[] componentsInChildren = source.GetComponentsInChildren<MeshFilter>(true);
		Dictionary<int, RawMesh> dictionary = new Dictionary<int, RawMesh>();
		MeshFilter[] array = componentsInChildren;
		foreach (MeshFilter val in array)
		{
			Mesh sharedMesh = val.sharedMesh;
			MeshRenderer component = ((Component)val).GetComponent<MeshRenderer>();
			if ((Object)(object)sharedMesh == (Object)null || (Object)(object)component == (Object)null)
			{
				continue;
			}
			if (!((Component)val).gameObject.activeInHierarchy || !((Renderer)component).enabled)
			{
				bool flag = true;
				Transform val2 = ((Component)val).transform;
				while ((Object)(object)val2 != (Object)null && (Object)(object)val2 != (Object)(object)source.transform)
				{
					if (!((Component)val2).gameObject.activeSelf)
					{
						flag = false;
						break;
					}
					val2 = val2.parent;
				}
				if (!flag || !((Renderer)component).enabled)
				{
					continue;
				}
			}
			try
			{
				int instanceID = ((Object)sharedMesh).GetInstanceID();
				if (!dictionary.TryGetValue(instanceID, out var value))
				{
					value = (dictionary[instanceID] = ReadRawMesh(sharedMesh, prefabName, cachedPrefab.Warnings));
				}
				if (value == null)
				{
					continue;
				}
				Matrix4x4 val3 = worldToLocalMatrix * ((Component)val).transform.localToWorldMatrix;
				CachedMesh cachedMesh = new CachedMesh
				{
					Name = ((Object)val).name,
					Vertices = (Vector3[])(object)new Vector3[value.Vertices.Length],
					UVs = value.UVs
				};
				for (int j = 0; j < value.Vertices.Length; j++)
				{
					cachedMesh.Vertices[j] = ((Matrix4x4)(ref val3)).MultiplyPoint3x4(value.Vertices[j]);
				}
				Material[] sharedMaterials = ((Renderer)component).sharedMaterials;
				for (int k = 0; k < value.Triangles.Count; k++)
				{
					int[] array2 = value.Triangles[k];
					if (array2.Length == 0)
					{
						continue;
					}
					Material val4 = ((k < sharedMaterials.Length) ? sharedMaterials[k] : null);
					Texture2D val5 = null;
					if ((Object)(object)val4 != (Object)null)
					{
						if (val4.HasProperty("_MainTex"))
						{
							Texture texture = val4.GetTexture("_MainTex");
							val5 = (Texture2D)(object)((texture is Texture2D) ? texture : null);
						}
						if ((Object)(object)val5 == (Object)null && val4.HasProperty("_BaseMap"))
						{
							Texture texture2 = val4.GetTexture("_BaseMap");
							val5 = (Texture2D)(object)((texture2 is Texture2D) ? texture2 : null);
						}
					}
					string texturePath = (((Object)(object)val5 != (Object)null) ? ExportTexture(val5, outputDirectory) : null);
					string materialKey = (((Object)(object)val4 == (Object)null) ? "unknown" : (((Object)val4).name + "|" + (((Object)(object)val4.shader == (Object)null) ? "unknown" : ((Object)val4.shader).name) + "|" + (((Object)(object)val5 == (Object)null) ? "no-texture" : ((Object)val5).name)));
					cachedMesh.Submeshes.Add(new CachedSubmesh
					{
						Triangles = array2,
						MaterialKey = materialKey,
						Color = (((Object)(object)val4 != (Object)null && val4.HasProperty("_Color")) ? val4.color : (((Object)(object)val4 != (Object)null && val4.HasProperty("_BaseColor")) ? val4.GetColor("_BaseColor") : Color.white)),
						HasColor = ((Object)(object)val4 != (Object)null && (val4.HasProperty("_Color") || val4.HasProperty("_BaseColor"))),
						TexturePath = texturePath
					});
				}
				if (cachedMesh.Submeshes.Count > 0)
				{
					cachedPrefab.Meshes.Add(cachedMesh);
				}
			}
			catch (Exception ex)
			{
				cachedPrefab.Warnings.Add(prefabName + "/" + ((Object)val).name + "/" + ((Object)sharedMesh).name + ": " + ex.Message);
			}
		}
		return cachedPrefab;
	}

	internal static RawMesh ReadRawMesh(Mesh mesh, string prefabName, List<string> warnings)
	{
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if (mesh.isReadable)
			{
				RawMesh rawMesh = new RawMesh
				{
					Vertices = mesh.vertices,
					UVs = mesh.uv,
					Normals = mesh.normals,
					Tangents = mesh.tangents
				};
				for (int i = 0; i < mesh.subMeshCount; i++)
				{
					rawMesh.Triangles.Add(((int)mesh.GetTopology(i) == 0) ? mesh.GetTriangles(i) : Array.Empty<int>());
				}
				return rawMesh;
			}
		}
		catch (Exception ex)
		{
			warnings.Add(prefabName + "/" + ((Object)mesh).name + ": CPU read failed: " + ex.Message);
		}
		try
		{
			RawMesh rawMesh2 = ReadBackMeshFromGPU(mesh);
			warnings.Add(prefabName + "/" + ((Object)mesh).name + ": GPU readback succeeded (" + rawMesh2.Vertices.Length + " vertices)");
			return rawMesh2;
		}
		catch (Exception ex2)
		{
			warnings.Add(prefabName + "/" + ((Object)mesh).name + ": GPU recovery unavailable: " + ex2.GetType().Name + " - " + ex2.Message);
			return null;
		}
	}

	private static RawMesh ReadBackMeshFromGPU(Mesh mesh)
	{
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_0319: Unknown result type (might be due to invalid IL or missing references)
		//IL_0259: Unknown result type (might be due to invalid IL or missing references)
		//IL_017d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0182: Unknown result type (might be due to invalid IL or missing references)
		//IL_0331: Unknown result type (might be due to invalid IL or missing references)
		//IL_0336: Unknown result type (might be due to invalid IL or missing references)
		//IL_033a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0236: Unknown result type (might be due to invalid IL or missing references)
		//IL_023b: Unknown result type (might be due to invalid IL or missing references)
		//IL_02fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0300: Unknown result type (might be due to invalid IL or missing references)
		if (!SystemInfo.supportsAsyncGPUReadback)
		{
			throw new NotSupportedException("Async GPU readback unsupported by this graphics device");
		}
		if (!mesh.HasVertexAttribute((VertexAttribute)0))
		{
			throw new NotSupportedException("Position attribute missing");
		}
		if ((int)mesh.GetVertexAttributeFormat((VertexAttribute)0) != 0 || mesh.GetVertexAttributeDimension((VertexAttribute)0) < 3)
		{
			throw new NotSupportedException("GPU vertex position format is not Float32x3");
		}
		int vertexAttributeStream = mesh.GetVertexAttributeStream((VertexAttribute)0);
		Dictionary<int, byte[]> dictionary = new Dictionary<int, byte[]>();
		byte[] bytes = (dictionary[vertexAttributeStream] = ReadVertexStream(mesh, vertexAttributeStream));
		int vertexCount = mesh.vertexCount;
		int vertexBufferStride = mesh.GetVertexBufferStride(vertexAttributeStream);
		int vertexAttributeOffset = mesh.GetVertexAttributeOffset((VertexAttribute)0);
		RawMesh rawMesh = new RawMesh
		{
			Vertices = (Vector3[])(object)new Vector3[vertexCount]
		};
		for (int i = 0; i < vertexCount; i++)
		{
			int num = i * vertexBufferStride + vertexAttributeOffset;
			rawMesh.Vertices[i] = new Vector3(ReadFloat(bytes, num), ReadFloat(bytes, num + 4), ReadFloat(bytes, num + 8));
		}
		rawMesh.UVs = Array.Empty<Vector2>();
		if (mesh.HasVertexAttribute((VertexAttribute)4) && (int)mesh.GetVertexAttributeFormat((VertexAttribute)4) == 0 && mesh.GetVertexAttributeDimension((VertexAttribute)4) >= 2)
		{
			int vertexAttributeStream2 = mesh.GetVertexAttributeStream((VertexAttribute)4);
			if (!dictionary.TryGetValue(vertexAttributeStream2, out var value))
			{
				value = (dictionary[vertexAttributeStream2] = ReadVertexStream(mesh, vertexAttributeStream2));
			}
			int vertexBufferStride2 = mesh.GetVertexBufferStride(vertexAttributeStream2);
			int vertexAttributeOffset2 = mesh.GetVertexAttributeOffset((VertexAttribute)4);
			rawMesh.UVs = (Vector2[])(object)new Vector2[vertexCount];
			for (int j = 0; j < vertexCount; j++)
			{
				int num2 = j * vertexBufferStride2 + vertexAttributeOffset2;
				rawMesh.UVs[j] = new Vector2(ReadFloat(value, num2), ReadFloat(value, num2 + 4));
			}
		}
		if (mesh.HasVertexAttribute((VertexAttribute)1) && (int)mesh.GetVertexAttributeFormat((VertexAttribute)1) == 0 && mesh.GetVertexAttributeDimension((VertexAttribute)1) >= 3)
		{
			int vertexAttributeStream3 = mesh.GetVertexAttributeStream((VertexAttribute)1);
			if (!dictionary.TryGetValue(vertexAttributeStream3, out var value2))
			{
				value2 = (dictionary[vertexAttributeStream3] = ReadVertexStream(mesh, vertexAttributeStream3));
			}
			int vertexBufferStride3 = mesh.GetVertexBufferStride(vertexAttributeStream3);
			int vertexAttributeOffset3 = mesh.GetVertexAttributeOffset((VertexAttribute)1);
			rawMesh.Normals = (Vector3[])(object)new Vector3[vertexCount];
			for (int k = 0; k < vertexCount; k++)
			{
				int num3 = k * vertexBufferStride3 + vertexAttributeOffset3;
				rawMesh.Normals[k] = new Vector3(ReadFloat(value2, num3), ReadFloat(value2, num3 + 4), ReadFloat(value2, num3 + 8));
			}
		}
		if (mesh.HasVertexAttribute((VertexAttribute)2) && (int)mesh.GetVertexAttributeFormat((VertexAttribute)2) == 0 && mesh.GetVertexAttributeDimension((VertexAttribute)2) >= 4)
		{
			int vertexAttributeStream4 = mesh.GetVertexAttributeStream((VertexAttribute)2);
			if (!dictionary.TryGetValue(vertexAttributeStream4, out var value3))
			{
				value3 = (dictionary[vertexAttributeStream4] = ReadVertexStream(mesh, vertexAttributeStream4));
			}
			int vertexBufferStride4 = mesh.GetVertexBufferStride(vertexAttributeStream4);
			int vertexAttributeOffset4 = mesh.GetVertexAttributeOffset((VertexAttribute)2);
			rawMesh.Tangents = (Vector4[])(object)new Vector4[vertexCount];
			for (int l = 0; l < vertexCount; l++)
			{
				int num4 = l * vertexBufferStride4 + vertexAttributeOffset4;
				rawMesh.Tangents[l] = new Vector4(ReadFloat(value3, num4), ReadFloat(value3, num4 + 4), ReadFloat(value3, num4 + 8), ReadFloat(value3, num4 + 12));
			}
		}
		byte[] value4 = ReadIndexBuffer(mesh);
		int num5 = (((int)mesh.indexFormat == 0) ? 2 : 4);
		for (int m = 0; m < mesh.subMeshCount; m++)
		{
			SubMeshDescriptor subMesh = mesh.GetSubMesh(m);
			if ((int)((SubMeshDescriptor)(ref subMesh)).topology != 0)
			{
				rawMesh.Triangles.Add(Array.Empty<int>());
				continue;
			}
			int[] array5 = new int[((SubMeshDescriptor)(ref subMesh)).indexCount];
			for (int n = 0; n < array5.Length; n++)
			{
				int startIndex = (((SubMeshDescriptor)(ref subMesh)).indexStart + n) * num5;
				array5[n] = (int)((num5 == 2) ? BitConverter.ToUInt16(value4, startIndex) : BitConverter.ToUInt32(value4, startIndex)) + ((SubMeshDescriptor)(ref subMesh)).baseVertex;
				if (array5[n] < 0 || array5[n] >= vertexCount)
				{
					throw new InvalidDataException("Recovered triangle index out of range");
				}
			}
			rawMesh.Triangles.Add(array5);
		}
		return rawMesh;
	}

	private static byte[] ReadVertexStream(Mesh mesh, int stream)
	{
		GraphicsBuffer val = null;
		try
		{
			val = mesh.GetVertexBuffer(stream);
			return ReadGpuBuffer(val);
		}
		finally
		{
			if (val != null)
			{
				val.Dispose();
			}
		}
	}

	private static byte[] ReadIndexBuffer(Mesh mesh)
	{
		GraphicsBuffer val = null;
		try
		{
			val = mesh.GetIndexBuffer();
			return ReadGpuBuffer(val);
		}
		finally
		{
			if (val != null)
			{
				val.Dispose();
			}
		}
	}

	private static byte[] ReadGpuBuffer(GraphicsBuffer buffer)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: 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_002b: Unknown result type (might be due to invalid IL or missing references)
		AsyncGPUReadbackRequest val = AsyncGPUReadback.Request(buffer, (Action<AsyncGPUReadbackRequest>)null);
		((AsyncGPUReadbackRequest)(ref val)).WaitForCompletion();
		if (((AsyncGPUReadbackRequest)(ref val)).hasError)
		{
			throw new InvalidOperationException("GPU refused buffer readback (backend/buffer usage limitation)");
		}
		return ((AsyncGPUReadbackRequest)(ref val)).GetData<byte>(0).ToArray();
	}

	private static float ReadFloat(byte[] bytes, int offset)
	{
		return BitConverter.ToSingle(bytes, offset);
	}

	private static string ExportTexture(Texture2D source, string outputDirectory)
	{
		//IL_009d: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a3: Expected O, but got Unknown
		//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
		string text = Path.Combine(outputDirectory, "textures");
		Directory.CreateDirectory(text);
		string text2 = SafeName(((Object)source).name);
		if (string.IsNullOrWhiteSpace(text2))
		{
			text2 = "texture";
		}
		string text3 = $"{text2}_{((Object)source).GetInstanceID()}.png";
		string path = Path.Combine(text, text3);
		if (File.Exists(path))
		{
			return "textures/" + text3;
		}
		Texture2D val = null;
		RenderTexture val2 = null;
		RenderTexture active = RenderTexture.active;
		try
		{
			val2 = RenderTexture.GetTemporary(((Texture)source).width, ((Texture)source).height, 0, (RenderTextureFormat)0, (RenderTextureReadWrite)0);
			Graphics.Blit((Texture)(object)source, val2);
			RenderTexture.active = val2;
			val = new Texture2D(((Texture)source).width, ((Texture)source).height, (TextureFormat)4, false);
			val.ReadPixels(new Rect(0f, 0f, (float)((Texture)source).width, (float)((Texture)source).height), 0, 0);
			val.Apply();
			File.WriteAllBytes(path, ImageConversion.EncodeToPNG(val));
			Logger.LogInfo((object)$"Exported texture: {((Object)source).name} ({((Texture)source).width}x{((Texture)source).height})");
			return "textures/" + text3;
		}
		catch (Exception ex)
		{
			Logger.LogWarning((object)("Could not export texture '" + ((Object)source).name + "': " + ex.Message));
			return null;
		}
		finally
		{
			RenderTexture.active = active;
			if ((Object)(object)val2 != (Object)null)
			{
				RenderTexture.ReleaseTemporary(val2);
			}
			if ((Object)(object)val != (Object)null)
			{
				Object.Destroy((Object)(object)val);
			}
		}
	}

	private static bool TryParsePiece(string[] fields, out Vector3 position, out Quaternion rotation, out Vector3 scale)
	{
		//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_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_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)
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0097: 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_00c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0100: Unknown result type (might be due to invalid IL or missing references)
		position = Vector3.zero;
		rotation = Quaternion.identity;
		scale = Vector3.one;
		try
		{
			position = new Vector3(Parse(fields[3]), Parse(fields[4]), Parse(fields[5]));
			int num = -1;
			for (int i = 6; i < fields.Length; i++)
			{
				if (fields[i].Length == 0)
				{
					num = i;
					break;
				}
			}
			if (num < 0)
			{
				return false;
			}
			switch (num - 6)
			{
			case 4:
				rotation = new Quaternion(Parse(fields[6]), Parse(fields[7]), Parse(fields[8]), Parse(fields[9]));
				break;
			case 3:
				rotation = Quaternion.Euler(Parse(fields[6]), Parse(fields[7]), Parse(fields[8]));
				break;
			default:
				return false;
			}
			if (num + 3 >= fields.Length)
			{
				return false;
			}
			scale = new Vector3(Parse(fields[num + 1]), Parse(fields[num + 2]), Parse(fields[num + 3]));
			return true;
		}
		catch
		{
			return false;
		}
	}

	private static float ReadFloat(NativeArray<byte> data, int offset)
	{
		return BitConverter.ToSingle(new byte[4]
		{
			data[offset],
			data[offset + 1],
			data[offset + 2],
			data[offset + 3]
		}, 0);
	}

	private static float Parse(string value)
	{
		return float.Parse(value, Invariant);
	}

	private static string F(float value)
	{
		return value.ToString("R", Invariant);
	}

	private static string SafeName(string value)
	{
		char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
		foreach (char oldChar in invalidFileNameChars)
		{
			value = value.Replace(oldChar, '_');
		}
		return value.Replace(' ', '_');
	}
}
internal static class ValeriusBakeRules
{
	internal enum Mode
	{
		Static,
		Interactive,
		CollisionOnly,
		VisualSeparate,
		Skip
	}

	private static ConfigFile config;

	private static ConfigEntry<bool> bakeVegetation;

	private static ConfigEntry<bool> bakeRocks;

	private static ConfigEntry<bool> bakeInteractibles;

	private static ConfigEntry<bool> bakeStructures;

	private static ConfigEntry<bool> snowCoverage;

	private static ConfigEntry<bool> disableAllSnow;

	private static ConfigEntry<string> interactive;

	private static ConfigEntry<string> boundaries;

	private static ConfigEntry<string> vegetation;

	private static ConfigEntry<string> preserved;

	private static ConfigEntry<string> skipped;

	private static ConfigEntry<string> aliases;

	private static ConfigEntry<string> alwaysBake;

	private static ConfigEntry<string> neverBake;

	private static ConfigEntry<string> protectedPrefabs;

	private static ConfigEntry<string> lockedVegetation;

	private static HashSet<string> interactiveNames;

	private static HashSet<string> boundaryNames;

	private static HashSet<string> vegetationNames;

	private static HashSet<string> preservedNames;

	private static HashSet<string> skippedNames;

	private static HashSet<string> alwaysBakeNames;

	private static HashSet<string> neverBakeNames;

	private static HashSet<string> protectedNames;

	private static HashSet<string> lockedNames;

	private static Dictionary<string, string> aliasMap;

	internal static bool PreserveVegetationColliders => true;

	internal static bool LeafShadows => false;

	internal static bool StaticCollision => true;

	internal static float SpatialChunkSize => 16f;

	internal static bool GroupBySharedMaterial => true;

	internal static bool ProtectDungeonDecorations => true;

	internal static float SnowCoverageDistance => 200f;

	internal static bool SnowCoverageEnabled
	{
		get
		{
			if (snowCoverage.Value)
			{
				return !disableAllSnow.Value;
			}
			return false;
		}
	}

	internal static bool DisableAllSnow => disableAllSnow.Value;

	internal static bool IsProtectedDecoration(string name)
	{
		if (config == null)
		{
			Initialize();
		}
		return protectedNames.Contains(name);
	}

	internal static bool ShouldLockVegetation(string name, bool separated)
	{
		if (config == null)
		{
			Initialize();
		}
		return lockedNames.Contains(name);
	}

	internal static void Initialize()
	{
		if (config == null)
		{
			config = ((BaseUnityPlugin)ValeriusPlugin.Instance).Config;
			bakeVegetation = config.Bind<bool>("General", "BakeVegetation", false, "Combine vegetation into static geometry instead of preserving original prefabs. Requires rebaking.");
			bakeRocks = config.Bind<bool>("General", "BakeRocks", true, "Combine rock geometry. Requires rebaking.");
			bakeInteractibles = config.Bind<bool>("General", "BakeInteractibles", false, "Combine interactive objects, removing their individual interactions. Requires rebaking.");
			bakeStructures = config.Bind<bool>("General", "BakeStructures", true, "Combine structural geometry. Requires rebaking.");
			snowCoverage = config.Bind<bool>("General", "EnableOverheadCheck", true, "BETA / EXPERIMENTAL: Attempt to detect overhead shelter and suppress snow on covered baked surfaces. Roof-edge results may be inaccurate. Restart to apply.");
			disableAllSnow = config.Bind<bool>("General", "DisableAllSnow", false, "BETA / EXPERIMENTAL: Attempt to disable snow on baked materials and separately spawned originals. Some vegetation shaders and snow textures may be unaffected. Does not affect world weather. Restart to apply.");
			interactive = config.Bind<string>("General", "InteractivePrefabs", "cloth_hanging_door_double,piece_cloth_hanging_door,piece_cloth_hanging_door_blue,dvergrtown_slidingdoor,crypt_skeleton_chest,TreasureChest_ashland_stone,TreasureChest_fCrypt,CastleKit_groundtorch,CastleKit_groundtorch_blue,piece_groundtorch_blue,piece_groundtorch_mist,piece_walltorch,MountainKit_brazier_blue,piece_brazierceiling01,piece_brazierfloor02,piece_dvergr_lantern,piece_dvergr_lantern_pole,piece_hoodedlantern,BogWitch_Fire_Pit,iron_grate,pickable_mushroom_magecap", "Prefab names retained as functional originals when interactive baking is disabled.");
			boundaries = config.Bind<string>("General", "BoundaryPrefabs", "InvisibleFloor,InvisibleWall,ValeriusInvisibleFloor,ValeriusInvisibleWall,ValeriusInvisibleFloor4x4,ValeriusInvisibleWall4x4", "Collision-only prefabs; never baked as visible geometry.");
			vegetation = config.Bind<string>("General", "SeparateVegetation", "FirTree,FirTree_small,FirTree_small_dead,Pinetree_01,Pinetree_Snow,SwampTree1,SwampTree1_Stub,SwampTree2_log,VineGreen,vines,VineAshlands,YggaShoot1,YggaShoot2,YggaShoot3,YggaShoot_small1,YggdrasilRoot,AshlandsBush1,AshlandsBush2,FernAshlands,Bush01,Bush01_heath,Bush02_en,shrub_2,shrub_2_heath", "Vegetation prefab names kept as originals when vegetation baking is disabled.");
			preserved = config.Bind<string>("General", "PreserveOriginalPrefabs", "Morkhalla_Banner2,dvergrprops_banner,dvergrprops_curtain,piece_banner01,piece_banner11,piece_FairylightGarland", "Always keep these decorative prefab hierarchies, animations and effects.");
			skipped = config.Bind<string>("General", "NeverInclude", "FireFlies,WaterLiquid", "Omit these prefab names completely. Has priority over all other lists.");
			aliases = config.Bind<string>("General", "Aliases", "InvisibleFloor=ValeriusInvisibleFloor,InvisibleWall=ValeriusInvisibleWall", "Map blueprint names to registered prefabs: old=new.");
			alwaysBake = config.Bind<string>("General", "AlwaysBake", "", "Always convert these prefab names to static geometry, except collision boundaries or excluded objects.");
			neverBake = config.Bind<string>("General", "NeverBake", "", "Always spawn these prefab names as complete originals.");
			protectedPrefabs = config.Bind<string>("General", "ProtectedPrefabs", "VineGreen,vines,VineAshlands,piece_dvergr_lantern,piece_dvergr_lantern_pole,piece_hoodedlantern,cloth_hanging_door_double,piece_cloth_hanging_door,piece_cloth_hanging_door_blue,dvergrprops_curtain,Morkhalla_Banner2,dvergrprops_banner,piece_banner01,piece_banner11,piece_FairylightGarland,piece_walltorch,piece_groundtorch_blue,piece_groundtorch_mist,CastleKit_groundtorch,CastleKit_groundtorch_blue,MountainKit_brazier_blue,piece_brazierceiling01,piece_brazierfloor02,BogWitch_Fire_Pit", "Dungeon-spawned originals protected from WearNTear updates.");
			lockedVegetation = config.Bind<string>("General", "LockedVegetation", "FirTree,FirTree_small,Pinetree_01,FirTree_small_dead,Pinetree_Snow,Bush01,Bush01_heath,Bush02_en,shrub_2,shrub_02,shrub_2_heath", "Dungeon-spawned originals with gravity/settling physics locked; no effect on world vegetation.");
			Refresh();
			config.SettingChanged += delegate
			{
				Refresh();
			};
		}
	}

	private static HashSet<string> Names(string csv)
	{
		HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
		string[] array = csv.Split(new char[1] { ',' });
		for (int i = 0; i < array.Length; i++)
		{
			string text = array[i].Trim();
			if (text.Length > 0)
			{
				hashSet.Add(text);
			}
		}
		return hashSet;
	}

	private static void Refresh()
	{
		interactiveNames = Names(interactive.Value);
		boundaryNames = Names(boundaries.Value);
		vegetationNames = Names(vegetation.Value);
		preservedNames = Names(preserved.Value);
		skippedNames = Names(skipped.Value);
		alwaysBakeNames = Names(alwaysBake.Value);
		neverBakeNames = Names(neverBake.Value);
		protectedNames = Names(protectedPrefabs.Value);
		lockedNames = Names(lockedVegetation.Value);
		aliasMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
		string[] array = aliases.Value.Split(new char[1] { ',' });
		foreach (string text in array)
		{
			int num = text.IndexOf('=');
			if (num > 0 && num != text.Length - 1)
			{
				aliasMap[text.Substring(0, num).Trim()] = text.Substring(num + 1).Trim();
			}
		}
	}

	internal static string Resolve(string original)
	{
		if (config == null)
		{
			Initialize();
		}
		if (!aliasMap.TryGetValue(original, out var value))
		{
			return original;
		}
		return value;
	}

	private static bool Has(HashSet<string> set, string original, string registered)
	{
		if (!set.Contains(original))
		{
			return set.Contains(registered);
		}
		return true;
	}

	internal static string CategoryFor(string original, string registered)
	{
		if (config == null)
		{
			Initialize();
		}
		if (Has(skippedNames, original, registered))
		{
			return "Skip";
		}
		if (Has(boundaryNames, original, registered))
		{
			return "Boundary";
		}
		if (Has(alwaysBakeNames, original, registered))
		{
			return "ForceBake";
		}
		if (Has(preservedNames, original, registered))
		{
			return "PreservedDecoration";
		}
		if (Has(neverBakeNames, original, registered))
		{
			return "ForceOriginal";
		}
		if (Has(vegetationNames, original, registered))
		{
			return "Vegetation";
		}
		if (Has(interactiveNames, original, registered))
		{
			return "Interactible";
		}
		GameObject prefab = PrefabManager.Instance.GetPrefab(registered);
		if ((Object)(object)prefab != (Object)null)
		{
			if ((Object)(object)prefab.GetComponentInChildren<MineRock5>(true) != (Object)null)
			{
				return "Rock";
			}
			if ((Object)(object)prefab.GetComponent<Piece>() != (Object)null)
			{
				return "Structure";
			}
		}
		return "Unclassified";
	}

	internal static Mode ModeFor(string original, string registered)
	{
		switch (CategoryFor(original, registered))
		{
		case "Skip":
			return Mode.Skip;
		case "Boundary":
			return Mode.CollisionOnly;
		case "ForceBake":
			return Mode.Static;
		case "PreservedDecoration":
			return Mode.VisualSeparate;
		case "ForceOriginal":
			return Mode.Interactive;
		case "Vegetation":
			if (!bakeVegetation.Value)
			{
				return Mode.VisualSeparate;
			}
			return Mode.Static;
		case "Rock":
			if (!bakeRocks.Value)
			{
				return Mode.VisualSeparate;
			}
			return Mode.Static;
		case "Interactible":
			if (!bakeInteractibles.Value)
			{
				return Mode.Interactive;
			}
			return Mode.Static;
		case "Structure":
			if (!bakeStructures.Value)
			{
				return Mode.Interactive;
			}
			return Mode.Static;
		default:
			return Mode.Static;
		}
	}
}
internal sealed class ValeriusDynamicSpawner : MonoBehaviour
{
	private sealed class SpawnConfig
	{
		internal string[] Names;

		internal Matrix4x4[] Matrices;

		internal bool[] Vegetation;

		internal float YOffset;
	}

	private const string ClothDoorName = "cloth_hanging_door_double";

	private const int MaxTransformProbes = 20;

	private int transformProbes;

	private string[] names;

	private Matrix4x4[] matrices;

	private bool[] vegetation;

	private float yOffset;

	private const string KeyPrefix = "prefabbaker_original_";

	internal const string OriginalZdoKey = "prefabbaker_dungeon_original";

	internal const string SceneryZdoKey = "prefabbaker_scenery_lock";

	internal const string DecorationZdoKey = "prefabbaker_protected_decoration";

	internal const string VisualSnowZdoKey = "prefabbaker_no_snow";

	internal const string VisualClothZdoKey = "prefabbaker_cloth_lod";

	private static readonly Dictionary<string, SpawnConfig> Configs = new Dictionary<string, SpawnConfig>(StringComparer.Ordinal);

	private static bool VerboseDiagnostics => ValeriusPlugin.VerboseDiagnostics;

	private static string PrefabKey(string objectName)
	{
		if (!objectName.EndsWith("(Clone)", StringComparison.Ordinal))
		{
			return objectName;
		}
		return objectName.Substring(0, objectName.Length - "(Clone)".Length);
	}

	internal void Configure(string[] prefabNames, Matrix4x4[] transforms, bool[] vegetationFlags, float originOffset)
	{
		names = prefabNames;
		matrices = transforms;
		vegetation = vegetationFlags;
		yOffset = originOffset;
		Configs[PrefabKey(((Object)((Component)this).gameObject).name)] = new SpawnConfig
		{
			Names = prefabNames,
			Matrices = transforms,
			Vegetation = vegetationFlags,
			YOffset = originOffset
		};
		if (VerboseDiagnostics)
		{
			Logger.LogWarning((object)("[PrefabBaker originals] Configured " + PrefabKey(((Object)((Component)this).gameObject).name) + " entries=" + prefabNames.Length));
		}
	}

	internal static void LockVegetationPhysics(GameObject obj, string prefabName)
	{
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		int num = 0;
		int num2 = 0;
		Rigidbody[] componentsInChildren = obj.GetComponentsInChildren<Rigidbody>(true);
		foreach (Rigidbody obj2 in componentsInChildren)
		{
			obj2.linearVelocity = Vector3.zero;
			obj2.angularVelocity = Vector3.zero;
			obj2.useGravity = false;
			obj2.isKinematic = true;
			num++;
		}
		MonoBehaviour[] componentsInChildren2 = obj.GetComponentsInChildren<MonoBehaviour>(true);
		foreach (MonoBehaviour val in componentsInChildren2)
		{
			if ((Object)(object)val != (Object)null && ((object)val).GetType().Name == "StaticPhysics")
			{
				((Behaviour)val).enabled = false;
				num2++;
			}
		}
		if (VerboseDiagnostics)
		{
			Logger.LogWarning((object)("[PrefabBaker vegetation lock] " + prefabName + " rigidbodies=" + num + " staticPhysics=" + num2));
		}
	}

	private static void DecodeTransform(Matrix4x4 m, out Vector3 scale, out Quaternion rotation)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: 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_0027: 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)
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//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_0093: Unknown result type (might be due to invalid IL or missing references)
		//IL_009a: Unknown result type (might be due to invalid IL or missing references)
		//IL_009f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0103: Unknown result type (might be due to invalid IL or missing references)
		//IL_0104: Unknown result type (might be due to invalid IL or missing references)
		//IL_0105: Unknown result type (might be due to invalid IL or missing references)
		//IL_010b: Unknown result type (might be due to invalid IL or missing references)
		//IL_010c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0111: 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_0119: Unknown result type (might be due to invalid IL or missing references)
		//IL_011e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0137: Unknown result type (might be due to invalid IL or missing references)
		//IL_012e: Unknown result type (might be due to invalid IL or missing references)
		//IL_012f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0130: 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)
		Vector3 val = default(Vector3);
		((Vector3)(ref val))..ctor(m.m00, m.m10, m.m20);
		Vector3 val2 = default(Vector3);
		((Vector3)(ref val2))..ctor(m.m01, m.m11, m.m21);
		Vector3 val3 = default(Vector3);
		((Vector3)(ref val3))..ctor(m.m02, m.m12, m.m22);
		scale = new Vector3(((Vector3)(ref val)).magnitude, ((Vector3)(ref val2)).magnitude, ((Vector3)(ref val3)).magnitude);
		if (((Matrix4x4)(ref m)).determinant < 0f)
		{
			scale.x = 0f - scale.x;
		}
		if (scale.x != 0f)
		{
			val /= scale.x;
		}
		if (scale.y != 0f)
		{
			val2 /= scale.y;
		}
		if (scale.z != 0f)
		{
			val3 /= scale.z;
		}
		if (((Vector3)(ref val3)).sqrMagnitude < 1E-06f || ((Vector3)(ref val2)).sqrMagnitude < 1E-06f)
		{
			rotation = Quaternion.identity;
			return;
		}
		((Vector3)(ref val3)).Normalize();
		Vector3 val4 = val2 - Vector3.Dot(val2, val3) * val3;
		val2 = ((Vector3)(ref val4)).normalized;
		rotation = ((((Vector3)(ref val2)).sqrMagnitude < 1E-06f) ? Quaternion.identity : Quaternion.LookRotation(val3, val2));
	}

	private static bool IsDungeonCloth(string prefabName)
	{
		if (!prefabName.Equals("cloth_hanging_door_double", StringComparison.OrdinalIgnoreCase) && !prefabName.Equals("piece_cloth_hanging_door", StringComparison.OrdinalIgnoreCase) && !prefabName.Equals("piece_cloth_hanging_door_blue", StringComparison.OrdinalIgnoreCase) && !prefabName.Equals("dvergrprops_curtain", StringComparison.OrdinalIgnoreCase) && !prefabName.Equals("Morkhalla_Banner2", StringComparison.OrdinalIgnoreCase) && !prefabName.Equals("dvergrprops_banner", StringComparison.OrdinalIgnoreCase) && !prefabName.Equals("piece_banner01", StringComparison.OrdinalIgnoreCase))
		{
			return prefabName.Equals("piece_banner11", StringComparison.OrdinalIgnoreCase);
		}
		return true;
	}

	private static string DescribeRenderBounds(GameObject obj)
	{
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: 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_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_005e: Unknown result type (might be due to invalid IL or missing references)
		Renderer[] componentsInChildren = obj.GetComponentsInChildren<Renderer>(true);
		if (componentsInChildren.Length == 0)
		{
			return "none";
		}
		Bounds bounds = componentsInChildren[0].bounds;
		for (int i = 1; i < componentsInChildren.Length; i++)
		{
			((Bounds)(ref bounds)).Encapsulate(componentsInChildren[i].bounds);
		}
		return "center=" + ((object)((Bounds)(ref bounds)).center/*cast due to .constrained prefix*/).ToString() + " size=" + ((object)((Bounds)(ref bounds)).size/*cast due to .constrained prefix*/).ToString();
	}

	private IEnumerator CheckOriginalPlacement(GameObject obj, string prefabName, int index, Vector3 expected, Vector3 intendedScale)
	{
		//IL_001c: 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_0026: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)obj == (Object)null))
		{
			Vector3 initial = obj.transform.position;
			Vector3 initialScale = obj.transform.lossyScale;
			yield return (object)new WaitForSeconds(2f);
			if (!((Object)(object)obj == (Object)null))
			{
				Vector3 position = obj.transform.position;
				string[] obj2 = new string[22]
				{
					"[PrefabBaker placement CHECK] ",
					prefabName,
					" #",
					index.ToString(),
					" expected=",
					((Vector3)(ref expected)).ToString("F3"),
					" spawned=",
					((Vector3)(ref initial)).ToString("F3"),
					" after2s=",
					((Vector3)(ref position)).ToString("F3"),
					" drift=",
					null,
					null,
					null,
					null,
					null,
					null,
					null,
					null,
					null,
					null,
					null
				};
				Vector3 val = position - initial;
				obj2[11] = ((Vector3)(ref val)).ToString("F3");
				obj2[12] = " intendedScale=";
				obj2[13] = ((Vector3)(ref intendedScale)).ToString("F3");
				obj2[14] = " instanceScale=";
				obj2[15] = ((Vector3)(ref initialScale)).ToString("F3");
				obj2[16] = " afterScale=";
				val = obj.transform.lossyScale;
				obj2[17] = ((Vector3)(ref val)).ToString("F3");
				obj2[18] = " hasStaticPhysics=";
				obj2[19] = ((Object)(object)obj.GetComponentInChildren<StaticPhysics>(true) != (Object)null).ToString();
				obj2[20] = " renderBounds=";
				obj2[21] = DescribeRenderBounds(obj);
				Logger.LogInfo((object)string.Concat(obj2));
			}
		}
	}

	private static bool IsTransformTarget(string prefabName)
	{
		if (!prefabName.Equals("SwampTree2_log", StringComparison.OrdinalIgnoreCase) && !prefabName.Equals("piece_dvergr_lantern_pole", StringComparison.OrdinalIgnoreCase) && !prefabName.Equals("dverger_pole_lantern", StringComparison.OrdinalIgnoreCase) && !prefabName.Equals("YggdrasilRoot", StringComparison.OrdinalIgnoreCase))
		{
			return prefabName.StartsWith("YggaShoot", StringComparison.OrdinalIgnoreCase);
		}
		return true;
	}

	private IEnumerator MonitorCloth(GameObject obj)
	{
		bool first = true;
		bool priorActive = false;
		bool priorEnabled = false;
		bool priorVisible = false;
		int reports = 0;
		while ((Object)(object)obj != (Object)null && reports < 12)
		{
			Renderer[] componentsInChildren = obj.GetComponentsInChildren<Renderer>(true);
			bool flag = false;
			bool flag2 = false;
			Renderer[] array = componentsInChildren;
			foreach (Renderer val in array)
			{
				if (!((Object)(object)val == (Object)null))
				{
					flag |= val.enabled && ((Component)val).gameObject.activeInHierarchy;
					flag2 |= val.isVisible;
				}
			}
			bool activeInHierarchy = obj.activeInHierarchy;
			if (first || priorActive != activeInHierarchy || priorEnabled != flag || priorVisible != flag2)
			{
				first = false;
				priorActive = activeInHierarchy;
				priorEnabled = flag;
				priorVisible = flag2;
				float num = (((Object)(object)Player.m_localPlayer == (Object)null) ? (-1f) : Vector3.Distance(((Component)Player.m_localPlayer).transform.position, obj.transform.position));
				Logger.LogInfo((object)("[PrefabBaker cloth probe] active=" + activeInHierarchy + " enabled=" + flag + " visible=" + flag2 + " renderers=" + componentsInChildren.Length + " playerDistance=" + num.ToString("F1") + "m"));
				reports++;
			}
			yield return (object)new WaitForSeconds(2f);
		}
	}

	private unsafe IEnumerator Start()
	{
		if (!((Object)((Component)this).gameObject).name.EndsWith("(Clone)", StringComparison.Ordinal))
		{
			yield break;
		}
		if (!Configs.TryGetValue(PrefabKey(((Object)((Component)this).gameObject).name), out var value))
		{
			Logger.LogWarning((object)("[PrefabBaker originals] No spawn configuration for " + ((Object)((Component)this).gameObject).name));
			yield break;
		}
		names = value.Names;
		matrices = value.Matrices;
		vegetation = value.Vegetation;
		yOffset = value.YOffset;
		if (names == null || matrices == null || vegetation == null || names.Length != matrices.Length || names.Length != vegetation.Length)
		{
			Logger.LogWarning((object)("[PrefabBaker originals] Invalid spawn configuration for " + ((Object)((Component)this).gameObject).name));
			yield break;
		}
		if (VerboseDiagnostics)
		{
			Logger.LogWarning((object)("[PrefabBaker originals] Starting " + ((Object)((Component)this).gameObject).name + " entries=" + names.Length));
		}
		ZNetView component = ((Component)this).GetComponent<ZNetView>();
		for (int frame = 0; frame < 300; frame++)
		{
			if (!((Object)(object)component == (Object)null) && component.IsValid() && !((Object)(object)ZNetScene.instance == (Object)null) && !((Object)(object)ZNet.instance == (Object)null))
			{
				break;
			}
			yield return null;
			component = ((Component)this).GetComponent<ZNetView>();
		}
		if ((Object)(object)component == (Object)null || !component.IsValid() || (Object)(object)ZNetScene.instance == (Object)null || (Object)(object)ZNet.instance == (Object)null)
		{
			Logger.LogWarning((object)("[PrefabBaker originals] Root not initialized: " + ((Object)this).name));
			yield break;
		}
		bool server = ZNet.instance.IsServer();
		ZDO rootZdo = component.GetZDO();
		int networked = 0;
		int local = 0;
		int missing = 0;
		int failed = 0;
		int marked = 0;
		int nonNetworkInteractive = 0;
		for (int frame = 0; frame < names.Length; frame++)
		{
			GameObject prefab = ZNetScene.instance.GetPrefab(names[frame]);
			if ((Object)(object)prefab == (Object)null)
			{
				prefab = PrefabManager.Instance.GetPrefab(names[frame]);
			}
			if ((Object)(object)prefab == (Object)null)
			{
				missing++;
				if (missing <= 5)
				{
					Logger.LogWarning((object)("[PrefabBaker originals] MISSING " + names[frame] + " index=" + frame));
				}
				continue;
			}
			bool flag = (Object)(object)prefab.GetComponent<ZNetView>() != (Object)null;
			if ((frame < 20 || names[frame].IndexOf("grate", StringComparison.OrdinalIgnoreCase) >= 0 || names[frame].IndexOf("door", StringComparison.OrdinalIgnoreCase) >= 0) && VerboseDiagnostics)
			{
				Logger.LogWarning((object)("[PrefabBaker originals] SOURCE " + names[frame] + " index=" + frame + " net=" + flag + " door=" + ((Object)(object)prefab.GetComponentInChildren<Door>(true) != (Object)null) + " rootScale=" + ((object)prefab.transform.localScale/*cast due to .constrained prefix*/).ToString() + " vegetation=" + vegetation[frame]));
			}
			if (flag && !server)
			{
				continue;
			}
			if (!flag && !vegetation[frame])
			{
				int num = nonNetworkInteractive + 1;
				nonNetworkInteractive = num;
				if (num <= 20 && VerboseDiagnostics)
				{
					Logger.LogWarning((object)("[PrefabBaker originals] Local non-networked decoration: " + names[frame] + " index=" + frame));
				}
			}
			string key = "prefabbaker_original_" + frame;
			if (flag && rootZdo != null && rootZdo.GetBool(key, false))
			{
				marked++;
				continue;
			}
			GameObject obj = null;
			try
			{
				Matrix4x4 val = matrices[frame];
				DecodeTransform(val, out var scale, out var rotation);
				Vector3 val2 = ((Component)this).transform.TransformPoint(new Vector3(val.m03, val.m13 + yOffset, val.m23));
				obj = Object.Instantiate<GameObject>(prefab, val2, ((Component)this).transform.rotation * rotation);
				obj.transform.localScale = Vector3.Scale(prefab.transform.localScale, Vector3.Scale(((Component)this).transform.lossyScale, scale));
				if (!obj.activeSelf)
				{
					obj.SetActive(true);
				}
				ValeriusDungeonVisualFix.Ensure(obj, ValeriusBakeRules.DisableAllSnow, IsDungeonCloth(names[frame]));
				if ((names[frame].Equals("SwampTree2_log", StringComparison.OrdinalIgnoreCase) || names[frame].Equals("YggdrasilRoot", StringComparison.OrdinalIgnoreCase) || names[frame].Equals("piece_dvergr_lantern_pole", StringComparison.OrdinalIgnoreCase)) && transformProbes < 9)
				{
					transformProbes++;
					((MonoBehaviour)this).StartCoroutine(CheckOriginalPlacement(obj, names[frame], frame, val2, scale));
				}
				if (VerboseDiagnostics && transformProbes < 20 && IsTransformTarget(names[frame]))
				{
					transformProbes++;
					string[] obj2 = new string[26]
					{
						"[PrefabBaker transform probe] prefab=",
						names[frame],
						" index=",
						frame.ToString(),
						" storedPos=",
						((object)new Vector3(val.m03, val.m13, val.m23)/*cast due to .constrained prefix*/).ToString(),
						" blueprintScale=",
						null,
						null,
						null,
						null,
						null,
						null,
						null,
						null,
						null,
						null,
						null,
						null,
						null,
						null,
						null,
						null,
						null,
						null,
						null
					};
					Vector3 val3 = scale;
					obj2[7] = ((object)(*(Vector3*)(&val3))/*cast due to .constrained prefix*/).ToString();
					obj2[8] = " prefabScale=";
					obj2[9] = ((object)prefab.transform.localScale/*cast due to .constrained prefix*/).ToString();
					obj2[10] = " dungeonScale=";
					obj2[11] = ((object)((Component)this).transform.lossyScale/*cast due to .constrained prefix*/).ToString();
					obj2[12] = " spawnedPos=";
					obj2[13] = ((object)obj.transform.position/*cast due to .constrained prefix*/).ToString();
					obj2[14] = " spawnedScale=";
					obj2[15] = ((object)obj.transform.localScale/*cast due to .constrained prefix*/).ToString();
					obj2[16] = " rotation=";
					Quaternion rotation2 = obj.transform.rotation;
					obj2[17] = ((object)((Quaternion)(ref rotation2)).eulerAngles/*cast due to .constrained prefix*/).ToString();
					obj2[18] = " networked=";
					obj2[19] = flag.ToString();
					obj2[20] = " prefabRootPos=";
					obj2[21] = ((object)prefab.transform.position/*cast due to .constrained prefix*/).ToString();
					obj2[22] = " sourceBounds=";
					obj2[23] = DescribeRenderBounds(prefab);
					obj2[24] = " spawnedBounds=";
					obj2[25] = DescribeRenderBounds(obj);
					Logger.LogInfo((object)string.Concat(obj2));
				}
				if (ValeriusBakeRules.IsProtectedDecoration(names[frame]))
				{
					obj.AddComponent<ValeriusProtectedDecoration>();
					WearNTear[] componentsInChildren = obj.GetComponentsInChildren<WearNTear>(true);
					foreach (WearNTear obj3 in componentsInChildren)
					{
						obj3.m_health = Mathf.Max(obj3.m_health, 1000000f);
					}
				}
				if (vegetation[frame] && ValeriusBakeRules.ShouldLockVegetation(names[frame], vegetation[frame]))
				{
					LockVegetationPhysics(obj, names[frame]);
				}
				if (!flag)
				{
					obj.transform.SetParent(((Component)this).transform, true);
					local++;
				}
			}
			catch (Exception ex)
			{
				failed++;
				if ((Object)(object)obj != (Object)null)
				{
					Object.Destroy((Object)(object)obj);
				}
				Logger.LogWarning((object)("[PrefabBaker originals] FAILED " + names[frame] + " index=" + frame + ": " + ex));
				continue;
			}
			if (flag)
			{
				ZNetView child = null;
				for (int i = 0; i < 30; i++)
				{
					try
					{
						if ((Object)(object)obj != (Object)null)
						{
							child = obj.GetComponent<ZNetView>();
						}
						if ((Object)(object)child != (Object)null && child.IsValid() && child.GetZDO() != null)
						{
							break;
						}
					}
					catch (Exception ex2)
					{
						Logger.LogWarning((object)("[PrefabBaker originals] ZDO check failed " + names[frame] + " index=" + frame + ": " + ex2.Message));
						break;
					}
					yield return null;
				}
				if ((Object)(object)obj == (Object)null || (Object)(object)child == (Object)null || !child.IsValid() || child.GetZDO() == null)
				{
					failed++;
					if ((Object)(object)obj != (Object)null)
					{
						Object.Destroy((Object)(object)obj);
					}
					Logger.LogWarning((object)("[PrefabBaker originals] ZDO FAILED " + names[frame] + " index=" + frame));
					continue;
				}
				child.GetZDO().Set("prefabbaker_dungeon_original", true);
				if (vegetation[frame] && ValeriusBakeRules.ShouldLockVegetation(names[frame], vegetation[frame]))
				{
					child.GetZDO().Set("prefabbaker_scenery_lock", true);
					if (VerboseDiagnostics)
					{
						Logger.LogWarning((object)("[PrefabBaker scenery ZDO] marked " + names[frame] + " index=" + frame));
					}
				}
				if (ValeriusBakeRules.IsProtectedDecoration(names[frame]))
				{
					child.GetZDO().Set("prefabbaker_protected_decoration", true);
				}
				if (ValeriusBakeRules.DisableAllSnow)
				{
					child.GetZDO().Set("prefabbaker_no_snow", true);
				}
				if (IsDungeonCloth(names[frame]))
				{
					child.GetZDO().Set("prefabbaker_cloth_lod", true);
				}
				if (rootZdo != null)
				{
					rootZdo.Set(key, true);
				}
				networked++;
			}
			if (frame % 25 == 0)
			{
				yield return null;
			}
		}
		if (VerboseDiagnostics || missing > 0 || failed > 0 || nonNetworkInteractive > 0)
		{
			Logger.LogInfo((object)("[PrefabBaker originals] " + ((Object)this).name + " total=" + names.Length + " networked=" + networked + " localVisual=" + local + " marked=" + marked + " missing=" + missing + " noNetworkInteractive=" + nonNetworkInteractive + " failed=" + failed + " server=" + server));
		}
	}
}
internal sealed class ValeriusProtectedDecoration : MonoBehaviour
{
}
[HarmonyPatch]
internal static class ValeriusDecorationWearPatch
{
	private static MethodBase TargetMethod()
	{
		return AccessTools.Method(typeof(WearNTear), "UpdateWear", (Type[])null, (Type[])null);
	}

	private static bool Prefix(WearNTear __instance)
	{
		if ((Object)(object)__instance == (Object)null || !ValeriusBakeRules.ProtectDungeonDecorations)
		{
			return true;
		}
		if ((Object)(object)((Component)__instance).GetComponentInParent<ValeriusProtectedDecoration>() != (Object)null)
		{
			return false;
		}
		ZNetView componentInParent = ((Component)__instance).GetComponentInParent<ZNetView>();
		if ((Object)(object)componentInParent == (Object)null || !componentInParent.IsValid())
		{
			return true;
		}
		ZDO zDO = componentInParent.GetZDO();
		if (zDO != null)
		{
			return !zDO.GetBool("prefabbaker_protected_decoration", false);
		}
		return true;
	}
}
[HarmonyPatch(typeof(StaticPhysics), "SUpdate")]
internal static class ValeriusSceneryStaticPhysicsPatch
{
	private static bool Prefix(StaticPhysics __instance)
	{
		if ((Object)(object)__instance == (Object)null)
		{
			return true;
		}
		ZNetView componentInParent = ((Component)__instance).GetComponentInParent<ZNetView>();
		if ((Object)(object)componentInParent == (Object)null || !componentInParent.IsValid())
		{
			return true;
		}
		ZDO zDO = componentInParent.GetZDO();
		if (zDO != null)
		{
			return !zDO.GetBool("prefabbaker_scenery_lock", false);
		}
		return true;
	}
}
internal sealed class ValeriusDungeonVisualFix : MonoBehaviour
{
	private bool noSnow;

	private bool cloth;

	private Coroutine retry;

	internal static void Ensure(GameObject instance, bool removeSnow, bool fixCloth)
	{
		if (!((Object)(object)instance == (Object)null) && (removeSnow || fixCloth))
		{
			ValeriusDungeonVisualFix valeriusDungeonVisualFix = instance.GetComponent<ValeriusDungeonVisualFix>();
			if ((Object)(object)valeriusDungeonVisualFix == (Object)null)
			{
				valeriusDungeonVisualFix = instance.AddComponent<ValeriusDungeonVisualFix>();
			}
			valeriusDungeonVisualFix.noSnow |= removeSnow;
			valeriusDungeonVisualFix.cloth |= fixCloth;
			valeriusDungeonVisualFix.Apply();
			if (((Behaviour)valeriusDungeonVisualFix).isActiveAndEnabled && valeriusDungeonVisualFix.retry == null)
			{
				valeriusDungeonVisualFix.retry = ((MonoBehaviour)valeriusDungeonVisualFix).StartCoroutine(valeriusDungeonVisualFix.ReapplyAfterInitialization());
			}
		}
	}

	private void Apply()
	{
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_005e: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		if (noSnow && ValeriusBakeRules.DisableAllSnow)
		{
			ValeriusPrefabBaker.ApplyNoSnowToInstance(((Component)this).gameObject);
		}
		if (cloth)
		{
			LODGroup[] componentsInChildren = ((Component)this).GetComponentsInChildren<LODGroup>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				componentsInChildren[i].ForceLOD(0);
			}
			SkinnedMeshRenderer[] componentsInChildren2 = ((Component)this).GetComponentsInChildren<SkinnedMeshRenderer>(true);
			foreach (SkinnedMeshRenderer obj in componentsInChildren2)
			{
				obj.updateWhenOffscreen = true;
				Bounds localBounds = ((Renderer)obj).localBounds;
				((Bounds)(ref localBounds)).Expand(20f);
				((Renderer)obj).localBounds = localBounds;
			}
		}
	}

	private IEnumerator ReapplyAfterInitialization()
	{
		yield return null;
		Apply();
		yield return (object)new WaitForSeconds(1f);
		Apply();
		yield return (object)new WaitForSeconds(3f);
		Apply();
		retry = null;
	}
}
[HarmonyPatch]
internal static class ValeriusDungeonVisualOnNetworkSpawn
{
	private static MethodBase TargetMethod()
	{
		return AccessTools.Method(typeof(ZNetScene), "CreateObject", new Type[1] { typeof(ZDO) }, (Type[])null);
	}

	private static void Postfix(ZDO zdo, GameObject __result)
	{
		if (zdo == null || (Object)(object)__result == (Object)null)
		{
			return;
		}
		bool flag = zdo.GetBool("prefabbaker_no_snow", false);
		bool flag2 = zdo.GetBool("prefabbaker_cloth_lod", false);
		bool flag3 = zdo.GetBool("prefabbaker_scenery_lock", false);
		bool flag4 = zdo.GetBool("prefabbaker_protected_decoration", false);
		if (flag || flag2 || flag3 || flag4)
		{
			if (flag || flag2)
			{
				ValeriusDungeonVisualFix.Ensure(__result, flag, flag2);
			}
			if (flag3)
			{
				ValeriusDynamicSpawner.LockVegetationPhysics(__result, ((Object)__result).name);
			}
			if (flag4 && (Object)(object)__result.GetComponent<ValeriusProtectedDecoration>() == (Object)null)
			{
				__result.AddComponent<ValeriusProtectedDecoration>();
			}
		}
	}
}
internal static class ValeriusMeshExtractor
{
	internal sealed class RawMesh
	{
		public Vector3[] Vertices;

		public Vector2[] UVs;

		public Vector3[] Normals;

		public Vector4[] Tangents;

		public List<int[]> Triangles = new List<int[]>();
	}

	internal static RawMesh ReadRawMesh(Mesh mesh, string prefabName, List<string> warnings)
	{
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if (mesh.isReadable)
			{
				RawMesh rawMesh = new RawMesh
				{
					Vertices = mesh.vertices,
					UVs = mesh.uv,
					Normals = mesh.normals,
					Tangents = mesh.tangents
				};
				for (int i = 0; i < mesh.subMeshCount; i++)
				{
					rawMesh.Triangles.Add(((int)mesh.GetTopology(i) == 0) ? mesh.GetTriangles(i) : Array.Empty<int>());
				}
				return rawMesh;
			}
		}
		catch (Exception ex)
		{
			warnings.Add(prefabName + "/" + ((Object)mesh).name + ": CPU read failed: " + ex.Message);
		}
		try
		{
			RawMesh rawMesh2 = ReadBackMeshFromGPU(mesh);
			warnings.Add(prefabName + "/" + ((Object)mesh).name + ": GPU readback succeeded (" + rawMesh2.Vertices.Length + " vertices)");
			return rawMesh2;
		}
		catch (Exception ex2)
		{
			warnings.Add(prefabName + "/" + ((Object)mesh).name + ": GPU recovery unavailable: " + ex2.GetType().Name + " - " + ex2.Message);
			return null;
		}
	}

	private static RawMesh ReadBackMeshFromGPU(Mesh mesh)
	{
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_019b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_017d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0182: Unknown result type (might be due to invalid IL or missing references)
		if (!SystemInfo.supportsAsyncGPUReadback)
		{
			throw new NotSupportedException("Async GPU readback unsupported by this graphics device");
		}
		if (!mesh.HasVertexAttribute((VertexAttribute)0))
		{
			throw new NotSupportedException("Position attribute missing");
		}
		if ((int)mesh.GetVertexAttributeFormat((VertexAttribute)0) != 0 || mesh.GetVertexAttributeDimension((VertexAttribute)0) < 3)
		{
			throw new NotSupportedException("GPU vertex position format is not Float32x3");
		}
		int vertexAttributeStream = mesh.GetVertexAttributeStream((VertexAttribute)0);
		Dictionary<int, byte[]> dictionary = new Dictionary<int, byte[]>();
		byte[] bytes = (dictionary[vertexAttributeStream] = ReadVertexStream(mesh, vertexAttributeStream));
		int vertexCount = mesh.vertexCount;
		int vertexBufferStride = mesh.GetVertexBufferStride(vertexAttributeStream);
		int vertexAttributeOffset = mesh.GetVertexAttributeOffset((VertexAttribute)0);
		RawMesh rawMesh = new RawMesh
		{
			Vertices = (Vector3[])(object)new Vector3[vertexCount]
		};
		for (int i = 0; i < vertexCount; i++)
		{
			int num = i * vertexBufferStride + vertexAttributeOffset;
			rawMesh.Vertices[i] = new Vector3(ReadFloat(bytes, num), ReadFloat(bytes, num + 4), ReadFloat(bytes, num + 8));
		}
		rawMesh.UVs = Array.Empty<Vector2>();
		if (mesh.HasVertexAttribute((VertexAttribute)4) && (int)mesh.GetVertexAttributeFormat((VertexAttribute)4) == 0 && mesh.GetVertexAttributeDimension((VertexAttribute)4) >= 2)
		{
			int vertexAttributeStream2 = mesh.GetVertexAttributeStream((VertexAttribute)4);
			if (!dictionary.TryGetValue(vertexAttributeStream2, out var value))
			{
				value = (dictionary[vertexAttributeStream2] = ReadVertexStream(mesh, vertexAttributeStream2));
			}
			int vertexBufferStride2 = mesh.GetVertexBufferStride(vertexAttributeStream2);
			int vertexAttributeOffset2 = mesh.GetVertexAttributeOffset((VertexAttribute)4);
			rawMesh.UVs = (Vector2[])(object)new Vector2[vertexCount];
			for (int j = 0; j < vertexCount; j++)
			{
				int num2 = j * vertexBufferStride2 + vertexAttributeOffset2;
				rawMesh.UVs[j] = new Vector2(ReadFloat(value, num2), ReadFloat(value, num2 + 4));
			}
		}
		byte[] value2 = ReadIndexBuffer(mesh);
		int num3 = (((int)mesh.indexFormat == 0) ? 2 : 4);
		for (int k = 0; k < mesh.subMeshCount; k++)
		{
			SubMeshDescriptor subMesh = mesh.GetSubMesh(k);
			if ((int)((SubMeshDescriptor)(ref subMesh)).topology != 0)
			{
				rawMesh.Triangles.Add(Array.Empty<int>());
				continue;
			}
			int[] array3 = new int[((SubMeshDescriptor)(ref subMesh)).indexCount];
			for (int l = 0; l < array3.Length; l++)
			{
				int startIndex = (((SubMeshDescriptor)(ref subMesh)).indexStart + l) * num3;
				array3[l] = (int)((num3 == 2) ? BitConverter.ToUInt16(value2, startIndex) : BitConverter.ToUInt32(value2, startIndex)) + ((SubMeshDescriptor)(ref subMesh)).baseVertex;
				if (array3[l] < 0 || array3[l] >= vertexCount)
				{
					throw new InvalidDataException("Recovered triangle index out of range");
				}
			}
			rawMesh.Triangles.Add(array3);
		}
		rawMesh.Normals = Array.Empty<Vector3>();
		rawMesh.Tangents = Array.Empty<Vector4>();
		return rawMesh;
	}

	private static byte[] ReadVertexStream(Mesh mesh, int stream)
	{
		GraphicsBuffer val = null;
		try
		{
			val = mesh.GetVertexBuffer(stream);
			return ReadGpuBuffer(val);
		}
		finally
		{
			if (val != null)
			{
				val.Dispose();
			}
		}
	}

	private static byte[] ReadIndexBuffer(Mesh mesh)
	{
		GraphicsBuffer val = null;
		try
		{
			val = mesh.GetIndexBuffer();
			return ReadGpuBuffer(val);
		}
		finally
		{
			if (val != null)
			{
				val.Dispose();
			}
		}
	}

	private static byte[] ReadGpuBuffer(GraphicsBuffer buffer)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: 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_002b: Unknown result type (might be due to invalid IL or missing references)
		AsyncGPUReadbackRequest val = AsyncGPUReadback.Request(buffer, (Action<AsyncGPUReadbackRequest>)null);
		((AsyncGPUReadbackRequest)(ref val)).WaitForCompletion();
		if (((AsyncGPUReadbackRequest)(ref val)).hasError)
		{
			throw new InvalidOperationException("GPU refused buffer readback (backend/buffer usage limitation)");
		}
		return ((AsyncGPUReadbackRequest)(ref val)).GetData<byte>(0).ToArray();
	}

	private static float ReadFloat(byte[] bytes, int offset)
	{
		return BitConverter.ToSingle(bytes, offset);
	}
}
[BepInPlugin("PrefabBaker", "Valerius Prefab Baker", "0.9.0")]
public sealed class ValeriusPlugin : BaseUnityPlugin
{
	private Harmony harmony;

	private const float ProgressCooldownSeconds = 5f;

	private static float lastProgressTime = -1000f;

	internal static ValeriusPlugin Instance { get; private set; }

	internal static bool VerboseDiagnostics { get; set; } = false;

	internal static void ShowBakeProgress(string message)
	{
		if (!((Object)(object)Player.m_localPlayer == (Object)null))
		{
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (!(realtimeSinceStartup - lastProgressTime < 5f))
			{
				lastProgressTime = realtimeSinceStartup;
				((Character)Player.m_localPlayer).Message((MessageType)1, "[PrefabBaker] " + message, 0, (Sprite)null, false);
			}
		}
	}

	internal static void ShowBakeCompletion(string message)
	{
		if ((Object)(object)Player.m_localPlayer != (Object)null)
		{
			((Character)Player.m_localPlayer).Message((MessageType)1, "[PrefabBaker] " + message, 0, (Sprite)null, false);
		}
		lastProgressTime = Time.realtimeSinceStartup;
	}

	private void Awake()
	{
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Expected O, but got Unknown
		Instance = this;
		harmony = new Harmony("PrefabBaker.patches");
		harmony.PatchAll(typeof(ValeriusPlugin).Assembly);
		ValeriusPrefabBaker.Register();
		((BaseUnityPlugin)this).Logger.LogInfo((object)"PrefabBaker initialized.");
	}

	private void OnDestroy()
	{
		Harmony obj = harmony;
		if (obj != null)
		{
			obj.UnpatchSelf();
		}
		if ((Object)(object)Instance == (Object)(object)this)
		{
			Instance = null;
		}
	}
}
internal static class ValeriusPrefabBaker
{
	private sealed class Part
	{
		public string Prefab;

		public int FilterIndex;

		public int MaterialIndex;

		public Matrix4x4 Local;

		public Vector3[] V;

		public Vector2[] UV;

		public Vector3[] N;

		public Vector4[] Tan;

		public int[] T;
	}

	private sealed class Piece
	{
		public int PrefabIndex;

		public Matrix4x4 World;
	}

	private sealed class PrefabData
	{
		public string Name;

		public readonly List<int> PartIndexes = new List<int>();
	}

	private sealed class DynamicEntry
	{
		public string Name;

		public Matrix4x4 Transform;

		public ValeriusBakeRules.Mode Mode;
	}

	private sealed class Buffer
	{
		public string MaterialRef;

		public Material Material;

		public readonly List<Vector3> V = new List<Vector3>();

		public readonly List<Vector2> UV = new List<Vector2>();

		public readonly List<Vector3> N = new List<Vector3>();

		public readonly List<Vector4> Tan = new List<Vector4>();

		public bool HasAllTangents = true;

		public readonly List<int> T = new List<int>();
	}

	private sealed class SnowCoverIndex
	{
		private const float Grid = 2.5f;

		private const int MaxCells = 350000;

		private const int MaxStampCellsPerTriangle = 256;

		private readonly Dictionary<long, float> roof = new Dictionary<long, float>();

		internal int TriangleCount { get; private set; }

		internal int OversizedCount { get; private set; }

		internal bool AtLimit { get; private set; }

		internal int CellCount => roof.Count;

		private static long Key(int x, int z)
		{
			return ((long)x << 32) ^ (uint)z;
		}

		private static float ProjectedHeight(Vector3 a, Vector3 b, Vector3 c, float x, float z)
		{
			//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_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: 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_0028: 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_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: 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_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			float num = (b.z - c.z) * (a.x - c.x) + (c.x - b.x) * (a.z - c.z);
			if (Mathf.Abs(num) < 1E-05f)
			{
				return float.NaN;
			}
			float num2 = ((b.z - c.z) * (x - c.x) + (c.x - b.x) * (z - c.z)) / num;
			float num3 = ((c.z - a.z) * (x - c.x) + (a.x - c.x) * (z - c.z)) / num;
			if (num2 < -0.0001f || num3 < -0.0001f || num2 + num3 > 1.0001f)
			{
				return float.NaN;
			}
			return num2 * a.y + num3 * b.y + (1f - num2 - num3) * c.y;
		}

		private void Stamp(int x, int z, float y)
		{
			if (float.IsNaN(y))
			{
				return;
			}
			long key = Key(x, z);
			if (roof.TryGetValue(key, out var value))
			{
				if (y > value)
				{
					roof[key] = y;
				}
			}
			else if (roof.Count < 350000)
			{
				roof.Add(key, y);
			}
			else
			{
				AtLimit = true;
			}
		}

		private void Add(Vector3 a, Vector3 b, Vector3 c)
		{
			//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_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: 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_0028: 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_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: 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_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_022f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_0231: Unknown result type (might be due to invalid IL or missing references)
			//IL_0236: Unknown result type (might be due to invalid IL or missing references)
			//IL_0237: Unknown result type (might be due to invalid IL or missing references)
			//IL_0241: 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_0249: Unknown result type (might be due to invalid IL or missing references)
			//IL_025b: Unknown result type (might be due to invalid IL or missing references)
			//IL_026d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			if (Mathf.Abs((b.x - a.x) * (c.z - a.z) - (c.x - a.x) * (b.z - a.z)) < 0.08f)
			{
				return;
			}
			Vector3 val = Vector3.Cross(b - a, c - a);
			if (Mathf.Abs(val.y) < ((Vector3)(ref val)).magnitude * 0.7f)
			{
				return;
			}
			TriangleCount++;
			int num = Mathf.FloorToInt(Mathf.Min(a.x, Mathf.Min(b.x, c.x)) / 2.5f);
			int num2 = Mathf.FloorToInt(Mathf.Max(a.x, Mathf.Max(b.x, c.x)) / 2.5f);
			int num3 = Mathf.FloorToInt(Mathf.Min(a.z, Mathf.Min(b.z, c.z)) / 2.5f);
			int num4 = Mathf.FloorToInt(Mathf.Max(a.z, Mathf.Max(b.z, c.z)) / 2.5f);
			long num5 = (long)(num2 - num + 1) * (long)(num4 - num3 + 1);
			if (num5 > 256)
			{
				OversizedCount++;
				int num6 = Mathf.Max(1, Mathf.CeilToInt(Mathf.Sqrt((float)num5 / 256f)));
				for (int i = num; i <= num2; i += num6)
				{
					for (int j = num3; j <= num4; j += num6)
					{
						Stamp(i, j, ProjectedHeight(a, b, c, ((float)i + 0.5f) * 2.5f, ((float)j + 0.5f) * 2.5f));
					}
				}
				return;
			}
			int num7 = 0;
			for (int k = num; k <= num2; k++)
			{
				for (int l = num3; l <= num4; l++)
				{
					float num8 = ProjectedHeight(a, b, c, ((float)k + 0.5f) * 2.5f, ((float)l + 0.5f) * 2.5f);
					if (!float.IsNaN(num8))
					{
						Stamp(k, l, num8);
						num7++;
					}
				}
			}
			if (num7 == 0)
			{
				Vector3 val2 = (a + b + c) / 3f;
				Stamp(Mathf.FloorToInt(val2.x / 2.5f), Mathf.FloorToInt(val2.z / 2.5f), val2.y);
			}
		}

		private float CoverFraction(Vector3 pos, float distance)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: 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)
			float num = pos.x / 2.5f - 0.5f;
			float num2 = pos.z / 2.5f - 0.5f;
			int num3 = Mathf.FloorToInt(num);
			int num4 = Mathf.FloorToInt(num2);
			float num5 = num - (float)num3;
			float num6 = num2 - (float)num4;
			float num7 = 0f;
			for (int i = 0; i < 2; i++)
			{
				for (int j = 0; j < 2; j++)
				{
					if (roof.TryGetValue(Key(num3 + i, num4 + j), out var value) && !(value <= pos.y + 0.35f) && !(value >= pos.y + distance))
					{
						num7 += ((i == 0) ? (1f - num5) : num5) * ((j == 0) ? (1f - num6) : num6);
					}
				}
			}
			return Mathf.Clamp01(num7);
		}

		internal bool HasCover(Vector3 pos, float distance)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			float num = CoverFraction(pos, distance);
			if (num <= 0.2f)
			{
				return false;
			}
			if (num >= 0.8f)
			{
				return true;
			}
			float num2 = Mathf.PerlinNoise(pos.x * 0.17f + 29.31f, pos.z * 0.17f + 81.73f);
			float num3 = Mathf.PerlinNoise(pos.x * 0.53f + 113.9f, pos.z * 0.53f + 19.1f);
			float num4 = (num2 - 0.5f) * 0.16f + (num3 - 0.5f) * 0.05f;
			return num + num4 >= 0.5f;
		}

		internal static SnowCoverIndex Build(List<PrefabData> prefabs, List<Part> parts, List<Piece> pieces)
		{
			//IL_007c: 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_0088: 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_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: 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_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			SnowCoverIndex snowCoverIndex = new SnowCoverIndex();
			foreach (Piece piece in pieces)
			{
				if (piece.PrefabIndex < 0 || piece.PrefabIndex >= prefabs.Count)
				{
					continue;
				}
				foreach (int partIndex in prefabs[piece.PrefabIndex].PartIndexes)
				{
					if (partIndex < 0 || partIndex >= parts.Count)
					{
						continue;
					}
					Part part = parts[partIndex];
					Matrix4x4 val = piece.World * part.Local;
					for (int i = 0; i + 2 < part.T.Length; i += 3)
					{
						int num = part.T[i];
						int num2 = part.T[i + 1];
						int num3 = part.T[i + 2];
						if (num >= 0 && num2 >= 0 && num3 >= 0 && num < part.V.Length && num2 < part.V.Length && num3 < part.V.Length)
						{
							snowCoverIndex.Add(((Matrix4x4)(ref val)).MultiplyPoint3x4(part.V[num]), ((Matrix4x4)(ref val)).MultiplyPoint3x4(part.V[num2]), ((Matrix4x4)(ref val)).MultiplyPoint3x4(part.V[num3]));
						}
					}
				}
			}
			return snowCoverIndex;
		}
	}

	private const int Magic = 827870278;

	private const int MaxVerts = 45000;

	private static readonly CultureInfo CI = CultureInfo.InvariantCulture;

	private static readonly string Root = Path.Combine(Paths.BepInExRootPath, "PrefabBakerBakes");

	private static bool running;

	private static bool lastBakeSucceeded;

	private static readonly Dictionary<int, Material> noSnowVariants = new Dictionary<int, Material>();

	private static readonly int[] snowPropertyIds = new int[5]
	{
		Shader.PropertyToID("_AddSnow"),
		Shader.PropertyToID("_Snow"),
		Shader.PropertyToID("_SnowAmount"),
		Shader.PropertyToID("_SnowEnabled"),
		Shader.PropertyToID("_EnableSnow")
	};

	private static float CellSize => ValeriusBakeRules.SpatialChunkSize;

	private static bool VerboseDiagnostics => ValeriusPlugin.VerboseDiagnostics;

	public static void Register()
	{
		CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new BakeRuntimeCommand());
		ValeriusBakeRules.Initialize();
		PrefabManager.OnVanillaPrefabsAvailable += LoadSaved;
	}

	public static void Begin(MonoBehaviour runner, string name)
	{
		if (running)
		{
			Logger.LogWarning((object)"Runtime bake already running");
		}
		else if ((Object)(object)Player.m_localPlayer == (Object)null)
		{
			Logger.LogWarning((object)"Enter a world first");
		}
		else if (string.IsNullOrWhiteSpace(name) || Path.GetFileName(name) != name || name.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0)
		{
			Logger.LogWarning((object)"Invalid blueprint name");
		}
		else
		{
			runner.StartCoroutine(Bake(name));
		}
	}

	public static void BeginAll(MonoBehaviour runner)
	{
		if (running)
		{
			Logger.LogWarning((object)"[PrefabBaker] Bake already running");
			return;
		}
		if ((Object)(object)Player.m_localPlayer == (Object)null)
		{
			Logger.LogWarning((object)"[PrefabBaker] Enter a world first");
			return;
		}
		string text = Path.Combine(Paths.ConfigPath, "PlanBuild");
		if (!Directory.Exists(text))
		{
			Logger.LogWarning((object)("[PrefabBaker] Blueprint directory missing: " + text));
			return;
		}
		string[] array = (from n in Directory.GetFiles(text, "*.blueprint").Select(Path.GetFileNameWithoutExtension)
			where !n.StartsWith("BakedBluePrint_", StringComparison.OrdinalIgnoreCase)
			select n).OrderBy<string, string>((string n) => n, StringComparer.OrdinalIgnoreCase).ToArray();
		if (array.Length == 0)
		{
			Logger.LogWarning((object)("[PrefabBaker] No source blueprints found in " + text));
			return;
		}
		running = true;
		runner.StartCoroutine(BakeAll(array));
	}

	private static IEnumerator BakeAll(string[] names)
	{
		try
		{
			Logger.LogInfo((object)("[PrefabBaker] Batch starting: " + names.Length + " source blueprints"));
			int succeeded = 0;
			int failed = 0;
			for (int i = 0; i < names.Length; i++)
			{
				yield return Bake(names[i], partOfBatch: true);
				if (lastBakeSucceeded)
				{
					succeeded++;
				}
				else
				{
					failed++;
				}
				running = true;
			}
			Logger.LogInfo((object)("[PrefabBaker] Batch complete: " + succeeded + " succeeded, " + failed + " failed"));
			ValeriusPlugin.ShowBakeCompletion("Batch complete: " + succeeded + " succeeded, " + failed + " failed. Restart Valheim to load updated prefabs.");
		}
		finally
		{
			running = false;
		}
	}

	private static void BakeProgress(string message, bool completed = false)
	{
		if (completed)
		{
			ValeriusPlugin.ShowBakeCompletion(message);
		}
		else
		{
			ValeriusPlugin.ShowBakeProgress(message);
		}
	}

	private static IEnumerator Bake(string name, bool partOfBatch = false)
	{
		lastBakeSucceeded = false;
		running = true;
		try
		{
			string source = Path.Combine(Paths.ConfigPath, "PlanBuild", name + ".blueprint");
			if (!File.Exists(source))
			{
				Logger.LogError((object)("Blueprint not found: " + source));
				yield break;
			}
			Directory.CreateDirectory(Root);
			List<PrefabData> prefabs = new List<PrefabData>();
			Dictionary<string, int> prefabIds = new Dictionary<string, int>(StringComparer.Ordinal);
			List<Part> parts = new List<Part>();
			List<Piece> pieces = new List<Piece>();
			List<DynamicEntry> dynamicEntries = new List<DynamicEntry>();
			List<string> warnings = new List<string>();
			bool inside = false;
			int lineNo = 0;
			int failed = 0;
			int seen = 0;
			bool flag = false;
			int total = 0;
			int nextProgress = 5;
			foreach (string item2 in File.ReadLines(source))
			{
				string text = item2.Trim();
				if (text == "#Pieces")
				{
					flag = true;
				}
				else if (flag && text.Length > 0 && !text.StartsWith("#"))
				{
					total++;
				}
			}
			BakeProgress(name + ": 0%");
			foreach (string item3 in File.ReadLines(source))
			{
				lineNo++;
				string text2 = item3.Trim();
				if (text2 == "#Pieces")
				{
					inside = true;
				}
				else
				{
					if (!inside || text2.Length == 0 || text2.StartsWith("#"))
					{
						continue;
					}
					seen++;
					if (total > 0 && nextProgress <= 100 && (long)seen * 100L >= (long)nextProgress * (long)total)
					{
						int num = Math.Min(100, (int)((long)seen * 100L / total / 5 * 5));
						BakeProgress(name + ": " + num + "%");
						nextProgress = num + 5;
					}
					string[] array = text2.Split(new char[1] { ';' });
					if (array.Length < 10 || !TryTransform(array, out var result))
					{
						failed++;
						continue;
					}
					string original = array[0].Trim();
					string text3 = ValeriusBakeRules.Resolve(original);
					ValeriusBakeRules.Mode mode = ValeriusBakeRules.ModeFor(original, text3);
					switch (mode)
					{
					default:
						if ((Object)(object)PrefabManager.Instance.GetPrefab(text3) == (Object)null)
						{
							warnings.Add("MISSING SPECIAL PREFAB: " + text3);
							failed++;
							break;
						}
						dynamicEntries.Add(new DynamicEntry
						{
							Name = text3,
							Transform = result,
							Mode = mode
						});
						if (seen % 20 == 0)
						{
							yield return null;
						}
						break;
					case ValeriusBakeRules.Mode.Static:
					{
						if (!prefabIds.TryGetValue(text3, out var value))
						{
							GameObject prefab = PrefabManager.Instance.GetPrefab(text3);
							if ((Object)(object)prefab == (Object)null)
							{
								warnings.Add("MISSING PREFAB: " + text3);
								failed++;
								break;
							}
							value = prefabs.Count;
							PrefabData prefabData = new PrefabData
							{
								Name = text3
							};
							prefabs.Add(prefabData);
							prefabIds.Add(text3, value);
							MeshFilter[] componentsInChildren = prefab.GetComponentsInChildren<MeshFilter>(true);
							Matrix4x4 worldToLocalMatrix = prefab.transform.worldToLocalMatrix;
							for (int i = 0; i < componentsInChildren.Length; i++)
							{
								MeshFilter val = componentsInChildren[i];
								MeshRenderer component = ((Component)val).GetComponent<MeshRenderer>();
								if ((Object)(object)val.sharedMesh == (Object)null || (Object)(object)component == (Object)null || !((Renderer)component).enabled || !ActiveBelowRoot(((Component)val).transform, prefab.transform))
								{
									continue;
								}
								try
								{
									ValeriusMeshExtractor.RawMesh rawMesh = ValeriusMeshExtractor.ReadRawMesh(val.sharedMesh, text3, warnings);
									if (rawMesh == null)
									{
										continue;
									}
									Material[] sharedMaterials = ((Renderer)component).sharedMaterials;
									for (int j = 0; j < rawMesh.Triangles.Count; j++)
									{
										if (j < sharedMaterials.Length && !((Object)(object)sharedMaterials[j] == (Object)null) && rawMesh.Triangles[j].Length != 0)
										{
											Part item = new Part
											{
												Prefab = text3,
												FilterIndex = i,
												MaterialIndex = j,
												Local = worldToLocalMatrix * ((Component)val).transform.localToWorldMatrix,
												V = rawMesh.Vertices,
												UV = rawMesh.UVs,
												N = rawMesh.Normals,
												Tan = rawMesh.Tangents,
												T = rawMesh.Triangles[j]
											};
											prefabData.PartIndexes.Add(parts.Count);
											parts.Add(item);
										}
									}
								}
								catch (Exception ex)
								{
									warnings.Add(text3 + "/" + ((Object)val).name + ": " + ex.Message);
								}
							}
							if (prefabData.PartIndexes.Count == 0)
							{
								warnings.Add("ZERO GEOMETRY: " + text3);
							}
						}
						pieces.Add(new Piece
						{
							PrefabIndex = value,
							World = result
						});
						if (seen % 20 == 0)
						{
							yield return null;
						}
						break;
					}
					case ValeriusBakeRules.Mode.Skip:
						break;
					}
				}
			}
			string text4 = Path.Combine(Root, "BakedPrefab_" + name + ".flex");
			string text5 = text4 + ".tmp";
			using (FileStream output = new FileStream(text5, FileMode.Create, FileAccess.Write, FileShare.None))
			{
				using BinaryWriter binaryWriter = new BinaryWriter(output);
				binaryWriter.Write(827870278);
				binaryWriter.Write(name);
				binaryWriter.Write(prefabs.Count);
				foreach (PrefabData item4 in prefabs)
				{
					binaryWriter.Write(item4.Name);
					binaryWriter.Write(item4.PartIndexes.Count);
					foreach (int partIndex in item4.PartIndexes)
					{
						binaryWriter.Write(partIndex);
					}
				}
				binaryWriter.Write(parts.Count);
				foreach (Part item5 in parts)
				{
					binaryWriter.Write(item5.Prefab);
					binaryWriter.Write(item5.FilterIndex);
					binaryWriter.Write(item5.MaterialIndex);
					WriteMatrix(binaryWriter, item5.Local);
					binaryWriter.Write(item5.V.Length);
					Vector3[] v = item5.V;
					foreach (Vector3 val2 in v)
					{
						binaryWriter.Write(val2.x);
						binaryWriter.Write(val2.y);
						binaryWriter.Write(val2.z);
					}
					binaryWriter.Write((item5.UV != null) ? item5.UV.Length : 0);
					if (item5.UV != null)
					{
						Vector2[] uV = item5.UV;
						foreach (Vector2 val3 in uV)
						{
							binaryWriter.Write(val3.x);
							binaryWriter.Write(val3.y);
						}
					}
					binaryWriter.Write((item5.N != null) ? item5.N.Length : 0);
					if (item5.N != null)
					{
						v = item5.N;
						foreach (Vector3 val4 in v)
						{
							binaryWriter.Write(val4.x);
							binaryWriter.Write(val4.y);
							binaryWriter.Write(val4.z);
						}
					}
					binaryWriter.Write((item5.Tan != null) ? item5.Tan.Length : 0);
					if (item5.Tan != null)
					{
						Vector4[] tan = item5.Tan;
						foreach (Vector4 val5 in tan)
						{
							binaryWriter.Write(val5.x);
							binaryWriter.Write(val5.y);
							binaryWriter.Write(val5.z);
							binaryWriter.Write(val5.w);
						}
					}
					binaryWriter.Write(item5.T.Length);
					int[] t = item5.T;
					foreach (int value2 in t)
					{
						binaryWriter.Write(value2);
					}
				}
				binaryWriter.Write(pieces.Count);
				foreach (Piece item6 in pieces)
				{
					binaryWriter.Write(item6.PrefabIndex);
					WriteMatrix(binaryWriter, item6.World);
				}
				binaryWriter.Write(dynamicEntries.Count);
				foreach (DynamicEntry item7 in dynamicEntries)
				{
					binaryWriter.Write(item7.Name);
					binaryWriter.Write((byte)item7.Mode);
					WriteMatrix(binaryWriter, item7.Transform);
				}
			}
			if (File.Exists(text4))
			{
				File.Delete(text4);
			}
			File.Move(text5, text4);
			string text6 = "BakedPrefab_" + name;
			string text7 = "BakedBluePrint_" + name;
			string path = Path.Combine(Paths.ConfigPath, "PlanBuild", text7 + ".blueprint");
			string[] contents = new string[10]
			{
				"#Name:" + text7,
				"#Creator:PrefabBaker",
				"#Description:Single-prefab wrapper generated from " + name,
				"#Category:Valerius",
				"#Center:",
				"#Coordinates:0,0,0",
				"#Rotation:0,0,0",
				"#SnapPoints",
				"#Pieces",
				text6 + ";;0;0;0;0;0;0;1;;1;1;1;"
			};
			try
			{
				if (string.Equals(Path.GetFullPath(source), Path.GetFullPath(path), StringComparison.OrdinalIgnoreCase))
				{
					throw new IOException("Wrapper path would overwrite input blueprint");
				}
				File.WriteAllLines(path, contents);
			}
			catch (Exception ex2)
			{
				Logger.LogWarning((object)("[PrefabBaker] Flex saved, but wrapper blueprint could not be written: " + ex2.Message));
			}
			File.WriteAllLines(Path.Combine(Root, "BakedPrefab_" + name + "_report.txt"), new string[6]
			{
				"Blueprint: " + name,
				"Instances: " + pieces.Count,
				"Unique prefabs: " + prefabs.Count,
				"Submeshes: " + parts.Count,
				"Skipped lines: " + failed,
				"Special objects: " + dynamicEntries.Count
			}.Concat(warnings).ToArray());
			lastBakeSucceeded = true;
			if (!partOfBatch)
			{
				BakeProgress(name + ": Complete! Restart Valheim to load the prefab.", completed: true);
			}
			Logger.LogInfo((object)("[PrefabBaker] Baked " + name + ": " + pieces.Count + " static, " + dynamicEntries.Count + " originals, " + failed + " skipped"));
		}
		finally
		{
			running = false;
		}
	}

	private static bool ActiveBelowRoot(Transform current, Transform root)
	{
		while ((Object)(object)current != (Object)null && (Object)(object)current != (Object)(object)root)
		{
			if (!((Component)current).gameObject.activeSelf)
			{
				return false;
			}
			current = current.parent;
		}
		return true;
	}

	private static string Alias(string name)
	{
		if (name == "InvisibleFloor")
		{
			return "ValeriusInvisibleFloor";
		}
		if (name == "InvisibleWall")
		{
			return "ValeriusInvisibleWall";
		}
		return name;
	}

	private static bool TryTransform(string[] f, out Matrix4x4 result)
	{
		//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_00af: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ea: Unknown result type (might be due to invalid IL or missing reference