Decompiled source of ShaderHelperForMac v3.2.0

plugins/ShaderHelperForMac.dll

Decompiled a week 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.Text;
using AssetsTools.NET;
using AssetsTools.NET.Extra;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Mono.Cecil;
using ShaderHelperForMac.Commands;
using ShaderHelperForMac.Config;
using ShaderHelperForMac.Diagnostics;
using ShaderHelperForMac.Provenance;
using ShaderHelperForMac.Replacement;
using ShaderHelperForMac.Shaders;
using ShaderHelperForMac.Sweep;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("ShaderHelperForMac")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("3.2.0.0")]
[assembly: AssemblyInformationalVersion("3.2.0+ed52adef9958d16b08ea973bab179130d4c353bc")]
[assembly: AssemblyProduct("ShaderHelperForMac")]
[assembly: AssemblyTitle("ShaderHelperForMac")]
[assembly: AssemblyVersion("3.2.0.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace ShaderHelperForMac
{
	[HarmonyPatch(typeof(ZNetScene), "Awake")]
	internal static class PatchZNetSceneAwake
	{
		private static void Prefix()
		{
			Plugin.RetroactiveBundleSweep("ZNetScene.Awake prefix");
		}

		private static void Postfix()
		{
			Plugin.Log.LogDebug((object)"[ZNetScene.Awake] Postfix — running post-ZNetScene sweep.");
			Plugin._zNetSceneAwakeFired = true;
			Plugin.TryUpgradeFallbackShader();
			Plugin.TryUpgradeTransparentShader();
			Plugin.RefreshCanonicalShaderIdsFromLoadedAssets();
			Plugin.CheckCachedVanillaCacheVersionDrift();
			if ((Object)(object)Plugin.Instance != (Object)null)
			{
				((MonoBehaviour)Plugin.Instance).StartCoroutine(Plugin.Instance.SweepMaterialsCoroutine("ZNetScene.Awake"));
			}
		}
	}
	[HarmonyPatch(typeof(Location), "Awake")]
	internal static class PatchLocationAwake
	{
		private static void Postfix()
		{
			Plugin.Log.LogDebug((object)"[Location.Awake] Postfix — sweeping materials after location/dungeon spawn.");
			if ((Object)(object)Plugin.Instance != (Object)null)
			{
				((MonoBehaviour)Plugin.Instance).StartCoroutine(Plugin.Instance.SweepMaterialsCoroutine("Location.Awake"));
			}
		}
	}
	[HarmonyPatch]
	internal static class PatchMeleeWeaponTrailStartProbe
	{
		private static readonly FieldInfo? _materialField = AccessTools.Field(AccessTools.TypeByName("MeleeWeaponTrail"), "_material");

		private static readonly FieldInfo? _trailObjectField = AccessTools.Field(AccessTools.TypeByName("MeleeWeaponTrail"), "m_trailObject");

		private static readonly FieldInfo[] _colorFields = ResolveColorFields();

		private static readonly FieldInfo? _colorsArrayField = ResolveColorsArrayField();

		private static FieldInfo? ResolveColorsArrayField()
		{
			FieldInfo[] colorFields = _colorFields;
			foreach (FieldInfo fieldInfo in colorFields)
			{
				if (fieldInfo.Name == "_colors" && fieldInfo.FieldType == typeof(Color[]))
				{
					return fieldInfo;
				}
			}
			return null;
		}

		private static FieldInfo[] ResolveColorFields()
		{
			Type type = AccessTools.TypeByName("MeleeWeaponTrail");
			if (type == null)
			{
				return Array.Empty<FieldInfo>();
			}
			List<FieldInfo> list = new List<FieldInfo>();
			FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (FieldInfo fieldInfo in fields)
			{
				Type fieldType = fieldInfo.FieldType;
				if (fieldType == typeof(Color) || fieldType == typeof(Gradient) || fieldType == typeof(Color[]))
				{
					list.Add(fieldInfo);
				}
			}
			return list.ToArray();
		}

		private static string FormatColorField(object? value)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: 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_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0212: Unknown result type (might be due to invalid IL or missing references)
			//IL_0221: 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)
			if (value != null)
			{
				if (!(value is Color val))
				{
					if (!(value is Color[] array))
					{
						Gradient val2 = (Gradient)((value is Gradient) ? value : null);
						if (val2 != null)
						{
							StringBuilder stringBuilder = new StringBuilder();
							stringBuilder.Append("Gradient{");
							GradientColorKey[] colorKeys = val2.colorKeys;
							for (int i = 0; i < colorKeys.Length; i++)
							{
								if (i > 0)
								{
									stringBuilder.Append(',');
								}
								Color color = colorKeys[i].color;
								stringBuilder.Append($"@{colorKeys[i].time:F2}=({color.r:F2},{color.g:F2},{color.b:F2})");
							}
							stringBuilder.Append("}");
							return stringBuilder.ToString();
						}
						return "<" + value.GetType().Name + ">";
					}
					if (array.Length == 0)
					{
						return "Color[0]";
					}
					StringBuilder stringBuilder2 = new StringBuilder();
					stringBuilder2.Append("Color[").Append(array.Length).Append("]{");
					int num = Math.Min(array.Length, 4);
					for (int j = 0; j < num; j++)
					{
						if (j > 0)
						{
							stringBuilder2.Append(',');
						}
						Color val3 = array[j];
						stringBuilder2.Append($"({val3.r:F2},{val3.g:F2},{val3.b:F2},{val3.a:F2})");
					}
					if (array.Length > num)
					{
						stringBuilder2.Append(",…");
					}
					stringBuilder2.Append('}');
					return stringBuilder2.ToString();
				}
				return $"Color({val.r:F3},{val.g:F3},{val.b:F3},{val.a:F3})";
			}
			return "<null>";
		}

		private static bool IsAdditiveParticleShaderName(string? shaderName)
		{
			if (string.IsNullOrEmpty(shaderName))
			{
				return false;
			}
			switch (shaderName)
			{
			case "Legacy Shaders/Particles/Additive":
			case "Legacy Shaders/Particles/Additive (Soft)":
			case "Legacy Shaders/Particles/~Additive-Multiply":
			case "Mobile/Particles/Additive":
			case "Particles/Additive":
			case "Particles/Additive (Soft)":
				return true;
			default:
				return false;
			}
		}

		private static IEnumerable<MethodBase> TargetMethods()
		{
			Type t = AccessTools.TypeByName("MeleeWeaponTrail");
			if (!(t == null))
			{
				MethodInfo m = AccessTools.Method(t, "Start", Type.EmptyTypes, (Type[])null);
				if (m != null)
				{
					yield return m;
				}
			}
		}

		private static void Postfix(object __instance)
		{
			//IL_0211: Unknown result type (might be due to invalid IL or missing references)
			//IL_023e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0266: Unknown result type (might be due to invalid IL or missing references)
			//IL_0453: Unknown result type (might be due to invalid IL or missing references)
			//IL_0458: Unknown result type (might be due to invalid IL or missing references)
			//IL_045a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0461: Unknown result type (might be due to invalid IL or missing references)
			//IL_0475: Unknown result type (might be due to invalid IL or missing references)
			//IL_047c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0490: Unknown result type (might be due to invalid IL or missing references)
			//IL_0497: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0527: Unknown result type (might be due to invalid IL or missing references)
			//IL_0533: Unknown result type (might be due to invalid IL or missing references)
			//IL_054c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0558: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b2: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Component val = (Component)((__instance is Component) ? __instance : null);
				string text = (((Object)(object)val != (Object)null) ? TransformPath(val.transform) : "<no-transform>");
				string text2 = "<field-unreadable>";
				string text3 = "<field-unreadable>";
				if (_materialField != null)
				{
					object? value = _materialField.GetValue(__instance);
					Material val2 = (Material)((value is Material) ? value : null);
					if ((Object)(object)val2 == (Object)null)
					{
						text2 = "<null>";
						text3 = "<null>";
					}
					else
					{
						text2 = Plugin.StripCloneSuffix(((Object)val2).name);
						text3 = (((Object)(object)val2.shader != (Object)null) ? ((Object)val2.shader).name : "<null-shader>");
					}
				}
				string arg = "<none>";
				if (_colorFields.Length != 0)
				{
					StringBuilder stringBuilder = new StringBuilder();
					FieldInfo[] colorFields = _colorFields;
					foreach (FieldInfo fieldInfo in colorFields)
					{
						object value2 = null;
						try
						{
							value2 = fieldInfo.GetValue(__instance);
						}
						catch
						{
						}
						if (stringBuilder.Length > 0)
						{
							stringBuilder.Append("  ");
						}
						stringBuilder.Append(fieldInfo.Name).Append('=').Append(FormatColorField(value2));
					}
					arg = stringBuilder.ToString();
				}
				string text4 = "<trail-object-unreadable>";
				if (_trailObjectField != null)
				{
					object? value3 = _trailObjectField.GetValue(__instance);
					GameObject val3 = (GameObject)((value3 is GameObject) ? value3 : null);
					if ((Object)(object)val3 == (Object)null)
					{
						text4 = "<trail-object-null>";
					}
					else
					{
						Renderer component = val3.GetComponent<Renderer>();
						Material val4 = (((Object)(object)component != (Object)null) ? component.sharedMaterial : null);
						if ((Object)(object)val4 == (Object)null)
						{
							text4 = "<clone-mat-null>";
						}
						else
						{
							string text5 = (((Object)(object)val4.shader != (Object)null) ? ((Object)val4.shader).name : "<null-shader>");
							string text6 = (val4.HasProperty("_TintColor") ? FormatColorField(val4.GetColor("_TintColor")) : "<absent>");
							string text7 = (val4.HasProperty("_EmissionColor") ? FormatColorField(val4.GetColor("_EmissionColor")) : "<absent>");
							string text8 = (val4.HasProperty("_Color") ? FormatColorField(val4.color) : "<absent>");
							string[] shaderKeywords = val4.shaderKeywords;
							string text9 = ((shaderKeywords == null || shaderKeywords.Length == 0) ? "<none>" : string.Join("|", shaderKeywords));
							text4 = "name='" + Plugin.StripCloneSuffix(((Object)val4).name) + "' shader='" + text5 + "'" + $" matId={((Object)val4).GetInstanceID()}" + " _TintColor=" + text6 + " _EmissionColor=" + text7 + " _Color=" + text8 + " keywords=" + text9;
							if ((Object)(object)val4.shader != (Object)null && !val4.shader.isSupported && IsAdditiveParticleShaderName(text5))
							{
								Shader val5 = Plugin.FindShaderByNameInternal(text5);
								if ((Object)(object)val5 != (Object)null && ((Object)val5).GetInstanceID() != ((Object)val4.shader).GetInstanceID())
								{
									int instanceID = ((Object)val4.shader).GetInstanceID();
									val4.shader = val5;
									Plugin.Log.LogInfo((object)("[TrailRebind] '" + text5 + "' on mat '" + Plugin.StripCloneSuffix(((Object)val4).name) + "'" + $" rebound from broken instance id={instanceID} (supported=False)" + $" → canonical id={((Object)val5).GetInstanceID()} (supported=True)."));
								}
								if (val4.HasProperty("_TintColor"))
								{
									Color val6 = default(Color);
									((Color)(ref val6))..ctor(0.5f, 0.5f, 0.5f, 0.5f);
									Color color = val4.GetColor("_TintColor");
									if (!(Math.Abs(color.r - val6.r) < 0.01f) || !(Math.Abs(color.g - val6.g) < 0.01f) || !(Math.Abs(color.b - val6.b) < 0.01f) || !(Math.Abs(color.a - val6.a) < 0.01f))
									{
										val4.SetColor("_TintColor", val6);
										Plugin.Log.LogInfo((object)("[TrailRebind] '" + text5 + "' on mat '" + Plugin.StripCloneSuffix(((Object)val4).name) + "'" + $" had _TintColor=({color.r:F3},{color.g:F3}," + $"{color.b:F3},{color.a:F3}) → neutralized to" + " vanilla grey (0.5,0.5,0.5,0.5) so the trail matches vanilla with no edge color (Option B: full vanilla match)."));
									}
								}
							}
						}
					}
				}
				if (Plugin.TrailProbe)
				{
					Plugin.Log.LogInfo((object)("[TrailProbe] MeleeWeaponTrail.Start  go='" + text + "'  trailMaterial='" + text2 + "'  shader='" + text3 + "'" + $"  colorFields[{_colorFields.Length}]: {arg}" + "  liveClone: " + text4));
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogDebug((object)("[TrailProbe] swallowed exception: " + ex.GetType().Name + ": " + ex.Message));
			}
		}

		private static string TransformPath(Transform? t)
		{
			if ((Object)(object)t == (Object)null)
			{
				return "<null>";
			}
			List<string> list = new List<string>();
			Transform val = t;
			int num = 0;
			while ((Object)(object)val != (Object)null && num++ < 64)
			{
				list.Add(Plugin.StripCloneSuffix(((Object)val).name));
				val = val.parent;
			}
			StringBuilder stringBuilder = new StringBuilder();
			for (int num2 = list.Count - 1; num2 >= 0; num2--)
			{
				stringBuilder.Append('/').Append(list[num2]);
			}
			return stringBuilder.ToString();
		}
	}
	[HarmonyPatch(typeof(VisEquipment), "AttachItem")]
	internal static class PatchVisEquipmentAttachItem
	{
		private static readonly HashSet<int> _autoPolishedEquippedMatIds = new HashSet<int>();

		private static void Postfix(VisEquipment __instance)
		{
			if ((Object)(object)Plugin.Instance == (Object)null)
			{
				return;
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("[AttachItem] " + ((Object)__instance).name + " renderers:");
			Renderer[] componentsInChildren = ((Component)__instance).GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val in componentsInChildren)
			{
				Material[] sharedMaterials = val.sharedMaterials;
				foreach (Material val2 in sharedMaterials)
				{
					if (!((Object)(object)val2 == (Object)null))
					{
						string[] obj = new string[7]
						{
							"  [",
							((object)val).GetType().Name,
							"] mat='",
							((Object)val2).name,
							"' shader='",
							null,
							null
						};
						Shader shader = val2.shader;
						obj[5] = ((shader != null) ? ((Object)shader).name : null);
						obj[6] = "'";
						stringBuilder.Append(string.Concat(obj));
						if (!Plugin.IsMaterialProcessedPublic(val2))
						{
							Plugin.DumpMatPropertiesPublic(val2, "AttachItem");
							Plugin.TryReplaceMaterialPublic(val2);
						}
					}
				}
			}
			if (Plugin.AutoPolishEquippedMetal)
			{
				Renderer[] componentsInChildren2 = ((Component)__instance).GetComponentsInChildren<Renderer>(true);
				foreach (Renderer val3 in componentsInChildren2)
				{
					if ((Object)(object)val3 == (Object)null)
					{
						continue;
					}
					Material[] sharedMaterials2 = val3.sharedMaterials;
					foreach (Material val4 in sharedMaterials2)
					{
						if (!((Object)(object)val4 == (Object)null) && !((Object)(object)val4.shader == (Object)null) && !(((Object)val4.shader).name != "Custom/Creature") && val4.HasProperty("_MetallicGlossMap") && !((Object)(object)val4.GetTexture("_MetallicGlossMap") == (Object)null))
						{
							int instanceID = ((Object)val4).GetInstanceID();
							if (_autoPolishedEquippedMatIds.Add(instanceID))
							{
								val4.SetFloat("_UseGlossmap", Plugin.AutoPolishUseGlossmap ? 1f : 0f);
								val4.SetFloat("_MetalGloss", Plugin.AutoPolishMetalGloss);
								val4.SetFloat("_Glossiness", Plugin.AutoPolishGlossiness);
								val4.SetFloat("_Metallic", Plugin.AutoPolishMetalAmount);
								ManualLogSource log = Plugin.Log;
								string[] obj2 = new string[9]
								{
									$"[AutoPolishEquippedMetal] polished mat='{((Object)val4).name}'  matId={instanceID}",
									"  shader='",
									((Object)val4.shader).name,
									"'  metallicGlossMap='",
									null,
									null,
									null,
									null,
									null
								};
								Texture texture = val4.GetTexture("_MetallicGlossMap");
								obj2[4] = ((texture != null) ? ((Object)texture).name : null);
								obj2[5] = "'";
								obj2[6] = $"  → _UseGlossmap={(Plugin.AutoPolishUseGlossmap ? 1 : 0)}";
								obj2[7] = $" _MetalGloss={Plugin.AutoPolishMetalGloss:F2} _Glossiness={Plugin.AutoPolishGlossiness:F2}";
								obj2[8] = $" _Metallic={Plugin.AutoPolishMetalAmount:F2}";
								log.LogDebug((object)string.Concat(obj2));
							}
						}
					}
				}
			}
			Plugin.Log.LogDebug((object)stringBuilder.ToString());
		}
	}
	[HarmonyPatch]
	internal static class PatchAssetBundleLoadFromFile
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			Type t = typeof(AssetBundle);
			MethodInfo sync = AccessTools.Method(t, "LoadFromFile", new Type[1] { typeof(string) }, (Type[])null);
			if (sync != null)
			{
				yield return sync;
			}
			MethodInfo crc = AccessTools.Method(t, "LoadFromFile", new Type[2]
			{
				typeof(string),
				typeof(uint)
			}, (Type[])null);
			if (crc != null)
			{
				yield return crc;
			}
			MethodInfo off = AccessTools.Method(t, "LoadFromFile", new Type[3]
			{
				typeof(string),
				typeof(uint),
				typeof(ulong)
			}, (Type[])null);
			if (off != null)
			{
				yield return off;
			}
		}

		private static void Postfix(AssetBundle __result, string path)
		{
			Plugin.OnBundleLoadedFromFile(__result, path);
		}
	}
	[HarmonyPatch]
	internal static class PatchAssetBundleLoadFromMemory
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			Type t = typeof(AssetBundle);
			MethodInfo one = AccessTools.Method(t, "LoadFromMemory", new Type[1] { typeof(byte[]) }, (Type[])null);
			if (one != null)
			{
				yield return one;
			}
			MethodInfo two = AccessTools.Method(t, "LoadFromMemory", new Type[2]
			{
				typeof(byte[]),
				typeof(uint)
			}, (Type[])null);
			if (two != null)
			{
				yield return two;
			}
		}

		private static void Postfix(AssetBundle __result)
		{
			Plugin.OnBundleLoadedFromMemoryOrStream(__result);
		}
	}
	[HarmonyPatch]
	internal static class PatchAssetBundleLoadFromStream
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			Type t = typeof(AssetBundle);
			MethodInfo one = AccessTools.Method(t, "LoadFromStream", new Type[1] { typeof(Stream) }, (Type[])null);
			if (one != null)
			{
				yield return one;
			}
			MethodInfo two = AccessTools.Method(t, "LoadFromStream", new Type[2]
			{
				typeof(Stream),
				typeof(uint)
			}, (Type[])null);
			if (two != null)
			{
				yield return two;
			}
			MethodInfo three = AccessTools.Method(t, "LoadFromStream", new Type[3]
			{
				typeof(Stream),
				typeof(uint),
				typeof(uint)
			}, (Type[])null);
			if (three != null)
			{
				yield return three;
			}
		}

		private static void Postfix(AssetBundle __result)
		{
			Plugin.OnBundleLoadedFromMemoryOrStream(__result);
		}
	}
	[HarmonyPatch]
	internal static class PatchAssetBundleLoadFromFileAsync
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			Type t = typeof(AssetBundle);
			MethodInfo one = AccessTools.Method(t, "LoadFromFileAsync", new Type[1] { typeof(string) }, (Type[])null);
			if (one != null)
			{
				yield return one;
			}
			MethodInfo two = AccessTools.Method(t, "LoadFromFileAsync", new Type[2]
			{
				typeof(string),
				typeof(uint)
			}, (Type[])null);
			if (two != null)
			{
				yield return two;
			}
			MethodInfo three = AccessTools.Method(t, "LoadFromFileAsync", new Type[3]
			{
				typeof(string),
				typeof(uint),
				typeof(ulong)
			}, (Type[])null);
			if (three != null)
			{
				yield return three;
			}
		}

		private static void Postfix(AssetBundleCreateRequest __result, string path)
		{
			if (__result != null)
			{
				((AsyncOperation)__result).completed += delegate
				{
					Plugin.OnBundleLoadedFromFile(__result.assetBundle, path);
				};
			}
		}
	}
	[HarmonyPatch]
	internal static class PatchAssetBundleLoadFromMemoryAsync
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			Type t = typeof(AssetBundle);
			MethodInfo one = AccessTools.Method(t, "LoadFromMemoryAsync", new Type[1] { typeof(byte[]) }, (Type[])null);
			if (one != null)
			{
				yield return one;
			}
			MethodInfo two = AccessTools.Method(t, "LoadFromMemoryAsync", new Type[2]
			{
				typeof(byte[]),
				typeof(uint)
			}, (Type[])null);
			if (two != null)
			{
				yield return two;
			}
		}

		private static void Postfix(AssetBundleCreateRequest __result)
		{
			if (__result != null)
			{
				((AsyncOperation)__result).completed += delegate
				{
					Plugin.OnBundleLoadedFromMemoryOrStream(__result.assetBundle);
				};
			}
		}
	}
	[HarmonyPatch]
	internal static class PatchAssetBundleLoadFromStreamAsync
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			Type t = typeof(AssetBundle);
			MethodInfo one = AccessTools.Method(t, "LoadFromStreamAsync", new Type[1] { typeof(Stream) }, (Type[])null);
			if (one != null)
			{
				yield return one;
			}
			MethodInfo two = AccessTools.Method(t, "LoadFromStreamAsync", new Type[2]
			{
				typeof(Stream),
				typeof(uint)
			}, (Type[])null);
			if (two != null)
			{
				yield return two;
			}
			MethodInfo three = AccessTools.Method(t, "LoadFromStreamAsync", new Type[3]
			{
				typeof(Stream),
				typeof(uint),
				typeof(uint)
			}, (Type[])null);
			if (three != null)
			{
				yield return three;
			}
		}

		private static void Postfix(AssetBundleCreateRequest __result)
		{
			if (__result != null)
			{
				((AsyncOperation)__result).completed += delegate
				{
					Plugin.OnBundleLoadedFromMemoryOrStream(__result.assetBundle);
				};
			}
		}
	}
	internal static class AssetBundleMethodResolver
	{
		public static MethodInfo? FindNonGeneric(string name, Type[] paramTypes)
		{
			MethodInfo[] methods = typeof(AssetBundle).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo in methods)
			{
				if (methodInfo.IsGenericMethodDefinition || methodInfo.Name != name)
				{
					continue;
				}
				ParameterInfo[] parameters = methodInfo.GetParameters();
				if (parameters.Length != paramTypes.Length)
				{
					continue;
				}
				bool flag = true;
				for (int j = 0; j < parameters.Length; j++)
				{
					if (parameters[j].ParameterType != paramTypes[j])
					{
						flag = false;
						break;
					}
				}
				if (flag)
				{
					return methodInfo;
				}
			}
			return null;
		}
	}
	[HarmonyPatch]
	internal static class PatchAssetBundleLoadAssetNoType
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			MethodInfo m = AssetBundleMethodResolver.FindNonGeneric("LoadAsset", new Type[1] { typeof(string) });
			if (m != null)
			{
				yield return m;
			}
		}

		private static void Postfix(AssetBundle __instance, Object __result)
		{
			if (!(__result == (Object)null))
			{
				Plugin.OnLoadAssetResult(__instance, __result);
				Material val = (Material)(object)((__result is Material) ? __result : null);
				if (val != null)
				{
					Plugin.TryReplaceMaterialPublic(val);
				}
			}
		}
	}
	[HarmonyPatch]
	internal static class PatchAssetBundleLoadAsset
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			MethodInfo m = AssetBundleMethodResolver.FindNonGeneric("LoadAsset", new Type[2]
			{
				typeof(string),
				typeof(Type)
			});
			if (m != null)
			{
				yield return m;
			}
		}

		private static void Postfix(AssetBundle __instance, Object __result)
		{
			if (!(__result == (Object)null))
			{
				Plugin.OnLoadAssetResult(__instance, __result);
				Material val = (Material)(object)((__result is Material) ? __result : null);
				if (val != null)
				{
					Plugin.TryReplaceMaterialPublic(val);
				}
			}
		}
	}
	[HarmonyPatch]
	internal static class PatchAssetBundleLoadAllAssets
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			MethodInfo none = AssetBundleMethodResolver.FindNonGeneric("LoadAllAssets", new Type[0]);
			if (none != null)
			{
				yield return none;
			}
			MethodInfo typed = AssetBundleMethodResolver.FindNonGeneric("LoadAllAssets", new Type[1] { typeof(Type) });
			if (typed != null)
			{
				yield return typed;
			}
		}

		private static void Postfix(AssetBundle __instance, Object[] __result)
		{
			if (__result == null)
			{
				return;
			}
			foreach (Object val in __result)
			{
				if (!(val == (Object)null))
				{
					Plugin.OnLoadAssetResult(__instance, val);
					Material val2 = (Material)(object)((val is Material) ? val : null);
					if (val2 != null)
					{
						Plugin.TryReplaceMaterialPublic(val2);
					}
				}
			}
		}
	}
	[HarmonyPatch]
	internal static class PatchAssetBundleLoadAssetAsync
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			MethodInfo one = AssetBundleMethodResolver.FindNonGeneric("LoadAssetAsync", new Type[1] { typeof(string) });
			if (one != null)
			{
				yield return one;
			}
			MethodInfo two = AssetBundleMethodResolver.FindNonGeneric("LoadAssetAsync", new Type[2]
			{
				typeof(string),
				typeof(Type)
			});
			if (two != null)
			{
				yield return two;
			}
		}

		private static void Postfix(AssetBundle __instance, AssetBundleRequest __result)
		{
			if (__result == null)
			{
				return;
			}
			((AsyncOperation)__result).completed += delegate
			{
				Object asset = __result.asset;
				if (!(asset == (Object)null))
				{
					Plugin.OnLoadAssetResult(__instance, asset);
					Material val = (Material)(object)((asset is Material) ? asset : null);
					if (val != null)
					{
						Plugin.TryReplaceMaterialPublic(val);
					}
				}
			};
		}
	}
	[HarmonyPatch]
	internal static class PatchAssetBundleLoadAllAssetsAsync
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			MethodInfo none = AssetBundleMethodResolver.FindNonGeneric("LoadAllAssetsAsync", new Type[0]);
			if (none != null)
			{
				yield return none;
			}
			MethodInfo typed = AssetBundleMethodResolver.FindNonGeneric("LoadAllAssetsAsync", new Type[1] { typeof(Type) });
			if (typed != null)
			{
				yield return typed;
			}
		}

		private static void Postfix(AssetBundle __instance, AssetBundleRequest __result)
		{
			if (__result == null)
			{
				return;
			}
			((AsyncOperation)__result).completed += delegate
			{
				Object[] allAssets = __result.allAssets;
				if (allAssets != null)
				{
					Object[] array = allAssets;
					foreach (Object val in array)
					{
						if (!(val == (Object)null))
						{
							Plugin.OnLoadAssetResult(__instance, val);
							Material val2 = (Material)(object)((val is Material) ? val : null);
							if (val2 != null)
							{
								Plugin.TryReplaceMaterialPublic(val2);
							}
						}
					}
				}
			};
		}
	}
	internal static class PatchEffectListCreateSwingProbe
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			MethodInfo m = AccessTools.Method(typeof(EffectList), "Create", new Type[5]
			{
				typeof(Vector3),
				typeof(Quaternion),
				typeof(Transform),
				typeof(float),
				typeof(int)
			}, (Type[])null);
			if (m != null)
			{
				yield return m;
			}
		}

		private static void Postfix(GameObject[] __result)
		{
			try
			{
				if (__result == null)
				{
					return;
				}
				foreach (GameObject val in __result)
				{
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					Renderer[] componentsInChildren = val.GetComponentsInChildren<Renderer>(true);
					foreach (Renderer val2 in componentsInChildren)
					{
						if ((Object)(object)val2 == (Object)null)
						{
							continue;
						}
						Material[] sharedMaterials = val2.sharedMaterials;
						foreach (Material val3 in sharedMaterials)
						{
							if (!((Object)(object)val3 == (Object)null) && !((Object)(object)val3.shader == (Object)null) && ((Object)val3.shader).name.StartsWith("Hidden/InternalErrorShader", StringComparison.OrdinalIgnoreCase))
							{
								Plugin.Log.LogInfo((object)("[SwingProbe] EffectList.Create spawned error-shader material: mat='" + ((Object)val3).name + "' shader='" + ((Object)val3.shader).name + "'" + $" matId={((Object)val3).GetInstanceID()}" + " rendererType=" + ((object)val2).GetType().Name + " go='" + ((Object)((Component)val2).gameObject).name + "' rootGo='" + ((Object)val).name + "'" + $" alreadyProcessed={Plugin.IsMaterialProcessedPublic(val3)}" + $" isErrorPink={Plugin.IsErrorPinkPublic(val3)}"));
							}
						}
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogDebug((object)("[SwingProbe] swallowed exception: " + ex.GetType().Name + ": " + ex.Message));
			}
		}
	}
	internal static class PatchAttackSwingProbe2
	{
		private const int HeartbeatCap = 8;

		private static int _heartbeatCount;

		private const int SceneScanCap = 4;

		private static int _sceneScanCount;

		private const int WindowCap = 2;

		private const int WindowFrames = 30;

		private static int _windowCount;

		private static bool _windowRunning;

		private static IEnumerable<MethodBase> TargetMethods()
		{
			MethodInfo trigger = AccessTools.Method(typeof(Attack), "OnAttackTrigger", Type.EmptyTypes, (Type[])null);
			if (trigger != null)
			{
				yield return trigger;
			}
			MethodInfo melee = AccessTools.Method(typeof(Attack), "DoMeleeAttack", Type.EmptyTypes, (Type[])null);
			if (melee != null)
			{
				yield return melee;
			}
		}

		private static string TransformPath(Transform t)
		{
			StringBuilder stringBuilder = new StringBuilder();
			List<string> list = new List<string>();
			Transform val = t;
			int num = 0;
			while ((Object)(object)val != (Object)null && num++ < 64)
			{
				list.Add(Plugin.StripCloneSuffix(((Object)val).name));
				val = val.parent;
			}
			for (int num2 = list.Count - 1; num2 >= 0; num2--)
			{
				stringBuilder.Append('/').Append(list[num2]);
			}
			return stringBuilder.ToString();
		}

		private static void Postfix(MethodBase __originalMethod)
		{
			try
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer == (Object)null)
				{
					return;
				}
				string text = __originalMethod?.Name ?? "Attack";
				int num = 0;
				int num2 = 0;
				Renderer[] componentsInChildren = ((Component)localPlayer).GetComponentsInChildren<Renderer>(true);
				foreach (Renderer val in componentsInChildren)
				{
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					num++;
					Material[] sharedMaterials = val.sharedMaterials;
					foreach (Material val2 in sharedMaterials)
					{
						if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2.shader == (Object)null) && ((Object)val2.shader).name.StartsWith("Hidden/InternalErrorShader", StringComparison.OrdinalIgnoreCase))
						{
							num2++;
							Plugin.Log.LogInfo((object)("[SwingProbe2] hook=" + text + " error-shader material under local player: mat='" + ((Object)val2).name + "' shader='" + ((Object)val2.shader).name + "'" + $" matId={((Object)val2).GetInstanceID()}" + " rendererType=" + ((object)val).GetType().Name + " go='" + ((Object)((Component)val).gameObject).name + "'" + $" alreadyProcessed={Plugin.IsMaterialProcessedPublic(val2)}" + $" isErrorPink={Plugin.IsErrorPinkPublic(val2)}"));
						}
					}
				}
				if (_sceneScanCount < 4)
				{
					_sceneScanCount++;
					int num3 = 0;
					int num4 = 0;
					Renderer[] array = Resources.FindObjectsOfTypeAll<Renderer>();
					foreach (Renderer val3 in array)
					{
						if ((Object)(object)val3 == (Object)null)
						{
							continue;
						}
						num3++;
						Material[] sharedMaterials2 = val3.sharedMaterials;
						foreach (Material val4 in sharedMaterials2)
						{
							if (!((Object)(object)val4 == (Object)null) && !((Object)(object)val4.shader == (Object)null) && ((Object)val4.shader).name.StartsWith("Hidden/InternalErrorShader", StringComparison.OrdinalIgnoreCase))
							{
								num4++;
								Plugin.Log.LogInfo((object)($"[SwingProbe2-Scene] scan #{_sceneScanCount}/{4} hook={text}" + " mat='" + ((Object)val4).name + "' shader='" + ((Object)val4.shader).name + "'" + $" matId={((Object)val4).GetInstanceID()}" + " rendererType=" + ((object)val3).GetType().Name + " path='" + TransformPath(((Component)val3).transform) + "'" + $" activeInHierarchy={((Component)val3).gameObject.activeInHierarchy}" + $" rendererEnabled={val3.enabled}" + $" alreadyProcessed={Plugin.IsMaterialProcessedPublic(val4)}" + $" isErrorPink={Plugin.IsErrorPinkPublic(val4)}"));
							}
						}
					}
					Plugin.Log.LogInfo((object)($"[SwingProbe2-Scene] scan #{_sceneScanCount}/{4} hook={text}" + $" sceneRenderers={num3} sceneErrorShaderMatches={num4}"));
				}
				if (_windowCount < 2 && !_windowRunning && (Object)(object)Plugin.Instance != (Object)null)
				{
					_windowCount++;
					_windowRunning = true;
					((MonoBehaviour)Plugin.Instance).StartCoroutine(PerFrameWindowScan(text, _windowCount));
				}
				if (_heartbeatCount < 8)
				{
					_heartbeatCount++;
					Plugin.Log.LogInfo((object)($"[SwingProbe2] heartbeat #{_heartbeatCount}/{8} hook={text}" + $" renderersScanned={num} errorShaderMatches={num2}"));
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogDebug((object)("[SwingProbe2] swallowed exception: " + ex.GetType().Name + ": " + ex.Message));
			}
		}

		private static IEnumerator PerFrameWindowScan(string hook, int windowIndex)
		{
			Plugin.Log.LogInfo((object)($"[SwingProbe2-Window] window #{windowIndex}/{2} OPEN hook={hook}" + " — DE-ASSUMED per-frame scan (error-pink-by-color OR trail/line/particle" + $" renderer; reads sharedMaterials AND materials) for up to {30} frames"));
			HashSet<string> loggedKeys = new HashSet<string>(StringComparer.Ordinal);
			int distinctLogged = 0;
			MaterialPropertyBlock mpbScratch = new MaterialPropertyBlock();
			HashSet<string> pinkSourceKeys = new HashSet<string>(StringComparer.Ordinal);
			int pinkSourceLogged = 0;
			for (int frame = 0; frame < 30; frame++)
			{
				yield return (object)new WaitForEndOfFrame();
				Renderer[] all;
				try
				{
					all = Resources.FindObjectsOfTypeAll<Renderer>();
				}
				catch (Exception ex)
				{
					Plugin.Log.LogDebug((object)$"[SwingProbe2-Window] enumerate failed frame={frame}: {ex.Message}");
					continue;
				}
				Renderer[] array = all;
				foreach (Renderer r in array)
				{
					if ((Object)(object)r == (Object)null)
					{
						continue;
					}
					bool isTrailLikeRenderer = r is TrailRenderer || r is LineRenderer || r is ParticleSystemRenderer;
					Material[] shared;
					try
					{
						shared = r.sharedMaterials;
					}
					catch
					{
						shared = Array.Empty<Material>();
					}
					bool sharedHasPink = false;
					Material[] array2 = shared;
					foreach (Material mat in array2)
					{
						if (!((Object)(object)mat == (Object)null))
						{
							bool pink = false;
							try
							{
								pink = Plugin.IsErrorPinkPublic(mat);
							}
							catch
							{
							}
							if (pink)
							{
								sharedHasPink = true;
							}
							if (pink || isTrailLikeRenderer)
							{
								EmitWindowMatch(windowIndex, frame, "shared", r, mat, pink, loggedKeys, ref distinctLogged);
							}
						}
					}
					if (isTrailLikeRenderer || sharedHasPink)
					{
						Material[] inst;
						try
						{
							inst = r.materials;
						}
						catch
						{
							inst = Array.Empty<Material>();
						}
						Material[] array3 = inst;
						foreach (Material mat2 in array3)
						{
							if (!((Object)(object)mat2 == (Object)null))
							{
								bool pink2 = false;
								try
								{
									pink2 = Plugin.IsErrorPinkPublic(mat2);
								}
								catch
								{
								}
								if (pink2 || isTrailLikeRenderer)
								{
									EmitWindowMatch(windowIndex, frame, "instance", r, mat2, pink2, loggedKeys, ref distinctLogged);
								}
							}
						}
					}
					if (isTrailLikeRenderer)
					{
						try
						{
							EmitPinkSourceMatch(windowIndex, frame, r, mpbScratch, pinkSourceKeys, ref pinkSourceLogged);
						}
						catch (Exception ex2)
						{
							Exception ex3 = ex2;
							Plugin.Log.LogDebug((object)("[PinkSourceProbe] swallowed exception: " + ex3.GetType().Name + ": " + ex3.Message));
						}
					}
				}
			}
			Plugin.Log.LogInfo((object)($"[SwingProbe2-Window] window #{windowIndex} CLOSED at frame cap ({30})" + $" — distinctLogged={distinctLogged} (each line is a candidate for what the pink" + " swing arc actually is; 0 means neither error-pink-by-color nor any trail/line/ particle material appeared in the window — next probe widens render technique)."));
			_windowRunning = false;
		}

		private static void EmitWindowMatch(int windowIndex, int frame, string slot, Renderer r, Material mat, bool pink, HashSet<string> loggedKeys, ref int distinctLogged)
		{
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			string text = (((Object)(object)mat.shader != (Object)null) ? ((Object)mat.shader).name : "<null-shader>");
			string text2;
			try
			{
				text2 = TransformPath(((Component)r).transform);
			}
			catch
			{
				text2 = "<path-failed>";
			}
			string item = $"{((object)r).GetType().Name}|{text}|{text2}|{pink}|{slot}";
			if (!loggedKeys.Add(item))
			{
				return;
			}
			distinctLogged++;
			string arg = "n/a";
			try
			{
				if (mat.HasProperty("_Color"))
				{
					arg = ((object)mat.color/*cast due to .constrained prefix*/).ToString();
				}
			}
			catch
			{
			}
			Plugin.Log.LogInfo((object)($"[SwingProbe2-Window] window #{windowIndex} frame=+{frame} CANDIDATE slot={slot}" + " mat='" + ((Object)mat).name + "' shader='" + text + "'" + $" matId={((Object)mat).GetInstanceID()}" + " rendererType=" + ((object)r).GetType().Name + " path='" + text2 + "'" + $" isErrorPinkByColor={pink} color={arg}" + $" activeInHierarchy={((Component)r).gameObject.activeInHierarchy}" + $" rendererEnabled={r.enabled}" + $" alreadyProcessed={Plugin.IsMaterialProcessedPublic(mat)}"));
		}

		private static bool IsMagenta(Color c)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return MaterialClassifier.IsMagenta(c);
		}

		private static void EmitPinkSourceMatch(int windowIndex, int frame, Renderer r, MaterialPropertyBlock mpbScratch, HashSet<string> pinkSourceKeys, ref int pinkSourceLogged)
		{
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0232: 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_023b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0240: Unknown result type (might be due to invalid IL or missing references)
			//IL_0244: 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_024b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: 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_025e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: 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_00e0: 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_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: 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)
			List<(string, Color)> list = new List<(string, Color)>(4);
			Material val = null;
			string text = "<none>";
			string text2 = "<none>";
			try
			{
				Material[] sharedMaterials = r.sharedMaterials;
				if (sharedMaterials != null)
				{
					Material[] array = sharedMaterials;
					foreach (Material val2 in array)
					{
						if ((Object)(object)val2 == (Object)null)
						{
							continue;
						}
						val = val2;
						text = ((Object)val2).name;
						text2 = (((Object)(object)val2.shader != (Object)null) ? ((Object)val2.shader).name : "<null-shader>");
						if (val2.HasProperty("_Color"))
						{
							Color color = val2.color;
							if (IsMagenta(color))
							{
								list.Add(("mat._Color", color));
							}
						}
						if (val2.HasProperty("_TintColor"))
						{
							Color color2 = val2.GetColor("_TintColor");
							if (IsMagenta(color2))
							{
								list.Add(("mat._TintColor", color2));
							}
						}
						if (val2.HasProperty("_EmissionColor"))
						{
							Color color3 = val2.GetColor("_EmissionColor");
							if (IsMagenta(color3))
							{
								list.Add(("mat._EmissionColor", color3));
							}
						}
						break;
					}
				}
			}
			catch
			{
			}
			try
			{
				if (r.HasPropertyBlock())
				{
					mpbScratch.Clear();
					r.GetPropertyBlock(mpbScratch);
					Color color4 = mpbScratch.GetColor("_Color");
					Color color5 = mpbScratch.GetColor("_MainColor");
					Color color6 = mpbScratch.GetColor("_TintColor");
					if (IsMagenta(color4))
					{
						list.Add(("mpb._Color", color4));
					}
					if (IsMagenta(color5))
					{
						list.Add(("mpb._MainColor", color5));
					}
					if (IsMagenta(color6))
					{
						list.Add(("mpb._TintColor", color6));
					}
				}
			}
			catch
			{
			}
			try
			{
				ParticleSystem component = ((Component)r).gameObject.GetComponent<ParticleSystem>();
				if ((Object)(object)component != (Object)null)
				{
					MainModule main = component.main;
					MinMaxGradient startColor = ((MainModule)(ref main)).startColor;
					Color color7 = ((MinMaxGradient)(ref startColor)).color;
					if (IsMagenta(color7))
					{
						list.Add(("ps.startColor", color7));
					}
				}
			}
			catch
			{
			}
			if (list.Count == 0)
			{
				return;
			}
			string text3;
			try
			{
				text3 = TransformPath(((Component)r).transform);
			}
			catch
			{
				text3 = "<path-failed>";
			}
			string text4 = string.Join("+", list.ConvertAll(((string src, Color col) h) => h.src));
			string item = ((object)r).GetType().Name + "|" + text2 + "|" + text3 + "|" + text4;
			if (pinkSourceKeys.Add(item))
			{
				pinkSourceLogged++;
				bool flag = list.Exists(((string src, Color col) h) => ((Color)(ref h.col)).maxColorComponent > 1f);
				string arg = string.Join("  ", list.ConvertAll(((string src, Color col) h) => $"{h.src}=RGBA({h.col.r:F2},{h.col.g:F2},{h.col.b:F2},{h.col.a:F2})|HDR={((Color)(ref h.col)).maxColorComponent > 1f}"));
				Plugin.Log.LogInfo((object)($"[PinkSourceProbe] window #{windowIndex} frame=+{frame} MAGENTA-SOURCE={text4}" + $" anyHDR={flag} values=[{arg}]" + " mat='" + text + "' shader='" + text2 + "'" + $" matId={(((Object)(object)val != (Object)null) ? ((Object)val).GetInstanceID() : 0)}" + " rendererType=" + ((object)r).GetType().Name + " path='" + text3 + "'" + $" activeInHierarchy={((Component)r).gameObject.activeInHierarchy}" + $" rendererEnabled={r.enabled}"));
			}
		}
	}
	internal static class PatchShaderFindBindProbe
	{
		private static readonly HashSet<string> _shaderFindProbeFiredNames = new HashSet<string>(StringComparer.Ordinal);

		private static void Postfix(string name, Shader __result)
		{
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)__result == (Object)null)
				{
					return;
				}
				string text = ((Object)__result).name ?? string.Empty;
				if (string.IsNullOrEmpty(text) || !BundleAttribution.IsVanillaShaderName(text) || !_shaderFindProbeFiredNames.Add(text))
				{
					return;
				}
				Shader[] array = Resources.FindObjectsOfTypeAll<Shader>();
				int num = 0;
				List<int> list = new List<int>();
				int instanceID = ((Object)__result).GetInstanceID();
				Shader[] array2 = array;
				foreach (Shader val in array2)
				{
					if (!((Object)(object)val == (Object)null) && !(((Object)val).name != text) && ((Object)val).GetInstanceID() != instanceID)
					{
						num++;
						if (list.Count < 8)
						{
							list.Add(((Object)val).GetInstanceID());
						}
					}
				}
				if (num >= 1)
				{
					string text2 = string.Join(",", list);
					string text3 = ((num > list.Count) ? ("+" + (num - list.Count) + "more") : string.Empty);
					string text4 = "n/a";
					try
					{
						int num2 = __result.FindPropertyIndex("_Color");
						text4 = ((num2 < 0) ? "no-_Color" : ((object)__result.GetPropertyType(num2)/*cast due to .constrained prefix*/).ToString());
					}
					catch (Exception)
					{
					}
					Plugin.Log.LogInfo((object)("[BindProbe-ShaderFind] requestedName='" + name + "' returnedName='" + text + "'" + $" returnedId={instanceID} peerCount={num} peerIds=[{text2}{text3}]" + $" returnedRQ={__result.renderQueue} returnedIsSupported={__result.isSupported}" + " schema=" + text4 + " vanillaName=true"));
				}
			}
			catch (Exception ex2)
			{
				Plugin.Log.LogDebug((object)("[BindProbe-ShaderFind] swallowed exception for Find request '" + (name ?? "<null>") + "': " + ex2.GetType().Name + ": " + ex2.Message));
			}
		}
	}
	[DefaultExecutionOrder(30000)]
	internal sealed class KeepRendererDisabled : MonoBehaviour
	{
		private Renderer? _renderer;

		private void Awake()
		{
			_renderer = ((Component)this).GetComponent<Renderer>();
		}

		private void LateUpdate()
		{
			if ((Object)(object)_renderer != (Object)null && _renderer.enabled)
			{
				_renderer.enabled = false;
			}
		}
	}
	internal enum ShaderAction
	{
		Transparent,
		Fallback,
		Original,
		UseShader
	}
	[BepInPlugin("aaa.drummercraig.shaderhelperformac", "ShaderHelperForMac", "3.2.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "aaa.drummercraig.shaderhelperformac";

		public const string PluginName = "ShaderHelperForMac";

		public const string PluginVersion = "3.2.0";

		internal static readonly string UseTransparentShaderFile = "usetransparentshader.txt";

		internal static readonly string UseFallbackShaderFile = "usefallbackshader.txt";

		internal static readonly string UseOriginalShaderFile = "useoriginalshader.txt";

		internal static HashSet<string> _useTransparentDlls = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		internal static HashSet<string> _useFallbackDlls = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		internal static HashSet<string> _useOriginalDlls = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		internal static HashSet<string> _transparentMatNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		internal static HashSet<string> _fallbackMatNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		internal static HashSet<string> _originalMatNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		internal static List<string> _transparentMatSuffixes = new List<string>();

		internal static List<string> _fallbackMatSuffixes = new List<string>();

		internal static HashSet<string> _forceFallbackMatNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		internal static List<string> _forceFallbackMatSuffixes = new List<string>();

		internal static HashSet<string> _forceTransparentMatNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		internal static List<string> _forceTransparentMatSuffixes = new List<string>();

		internal static HashSet<string> _forceFallbackPrefixSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		internal static HashSet<string> _forceTransparentPrefixSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		internal static Dictionary<string, string> _matShaderOverrides = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

		internal static Dictionary<string, List<(string prop, string value)>> _matShaderPropertyOverrides = new Dictionary<string, List<(string, string)>>(StringComparer.OrdinalIgnoreCase);

		internal static Dictionary<string, List<(string keyword, bool enable)>> _matShaderKeywordOverrides = new Dictionary<string, List<(string, bool)>>(StringComparer.OrdinalIgnoreCase);

		internal static HashSet<string> _hideRendererMatNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		internal static List<string> _hideRendererMatSuffixes = new List<string>();

		private FileSystemWatcher? _watcher;

		private ConfigEntry<float> _cfgPeriodicSweepInterval = null;

		private ConfigEntry<string> _cfgBridgeTraceMatNames = null;

		private ConfigEntry<bool> _cfgSweepProgressTrace = null;

		private ConfigEntry<bool> _cfgVanillaRebindEnabled = null;

		private ConfigEntry<bool> _cfgPreserveAdditiveParticles = null;

		private ConfigEntry<bool> _cfgHunFXAutoDefault = null;

		private ConfigEntry<bool> _cfgRepairInternalErrorShader = null;

		private ConfigEntry<bool> _cfgTrailProbe = null;

		private ConfigEntry<bool> _cfgDiagnosticProbes = null;

		private ConfigEntry<bool> _cfgAutoPolishMetallic = null;

		private ConfigEntry<bool> _cfgAutoPolishEquippedMetal = null;

		private ConfigEntry<bool> _cfgAutoPolishUseGlossmap = null;

		private ConfigEntry<float> _cfgAutoPolishMetalGloss = null;

		private ConfigEntry<float> _cfgAutoPolishGlossiness = null;

		private ConfigEntry<float> _cfgAutoPolishMetalAmount = null;

		private static Plugin? _instance;

		internal static readonly Dictionary<string, string> ModShaderDlls = new Dictionary<string, string>();

		internal static readonly Dictionary<string, string> _bundleToDll = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

		internal static readonly HashSet<string> _tocAttributedBundles = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		internal static readonly Dictionary<string, string> _prefabRootToDll = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

		internal static readonly string[] FallbackChain = new string[2] { "Custom/Creature", "Custom/Piece" };

		internal static readonly string[] TransparentChain = new string[5] { "Particles/Standard Unlit", "Legacy Shaders/Particles/Alpha Blended", "Particles/Standard Surface2", "Sprites/Default", "UI/Default" };

		internal static readonly HashSet<int> CanonicalShaderIds = new HashSet<int>();

		internal static readonly HashSet<string> _vanillaBundleNames = new HashSet<string>(StringComparer.Ordinal);

		internal static bool _awakeVanillaSnapshotEmpty;

		internal static bool _zNetSceneAwakeFired;

		internal static readonly List<AssetBundle> _deferredMemoryStreamBundles = new List<AssetBundle>();

		internal static string? _valheimDataPath;

		internal static readonly HashSet<string> _modPrefabRootNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		internal static readonly HashSet<int> _prefabScopedModMatIds = new HashSet<int>();

		internal static readonly HashSet<int> _particleRendererMatIdHints = new HashSet<int>();

		internal static readonly HashSet<int> _forceReplacedMaterialIds = new HashSet<int>();

		internal static readonly Dictionary<int, (bool intent, Color color, Texture? map, bool keyword)> _originalEmissionByMat = new Dictionary<int, (bool, Color, Texture, bool)>();

		internal static readonly HashSet<string> _baselineMatNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		internal static readonly HashSet<string> _vanillaShaderNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		internal static readonly string[] ValheimShaderNames = new string[55]
		{
			"Custom/Piece", "Custom/Creature", "Custom/Vegetation", "Custom/StaticRock", "Custom/Lit", "Custom/LitParticles", "Custom/FlowOpaque", "Custom/Billboard", "Custom/DistantLod", "Custom/WaterLily",
			"Custom/Trilinear", "Custom/Decal", "Custom/Cliff", "Custom/Skybox", "Custom/Distortion", "Custom/Player", "Custom/Blob", "Custom/Bonemass", "Custom/Grass", "Custom/Heightmap",
			"Custom/Rug", "Custom/ShadowBlob", "Custom/Tar", "Custom/Trilinearmap", "Custom/Yggdrasil", "Custom/Yggdrasil_root", "Custom/Water", "Custom/WaterBottom", "Custom/WaterMask", "Custom/Clouds",
			"Custom/Flow", "Custom/SkyboxProcedural", "Custom/SkyObject", "Custom/icon", "Custom/LitGui", "Custom/mapshader", "Custom/UI/AntiAliasedCircle", "Custom/UI_BGBlur", "Custom/AlphaParticle", "Custom/Gradient Mapped Particle (Unlit)",
			"Custom/Mesh Flipbook Particle", "Custom/Particle (Unlit)", "Custom/ParticleDecal", "Particles/Standard Unlit2", "Particles/Standard Surface2", "Standard TwoSided", "FX/Glass/Stained BumpDistort", "FX/Glass/Stained", "Unlit/Lighting", "ToonDeferredShading",
			"ToonDeferredShading2017", "Earthquake/Tree", "Earthquake/Leaf", "Hovl/Particles/Additive", "Hovl/Particles/AlphaBlend"
		};

		internal static readonly HashSet<string> ValheimShaderNameSet = new HashSet<string>(ValheimShaderNames, StringComparer.OrdinalIgnoreCase);

		internal static readonly string[] UnityBuiltInShaderNames = new string[36]
		{
			"Standard", "Standard (Specular setup)", "Standard (Roughness setup)", "Sprites/Default", "Sprites/Mask", "UI/Default", "UI/DefaultFont", "UI/Unlit/Text", "UI/Unlit/Transparent", "Unlit/Color",
			"Unlit/Texture", "Unlit/Transparent", "Unlit/Transparent Cutout", "Diffuse", "Mobile/Diffuse", "Transparent/Diffuse", "VertexLit", "Mobile/VertexLit", "Legacy Shaders/Particles/Alpha Blended", "Legacy Shaders/Particles/Additive",
			"Legacy Shaders/Particles/Additive (Soft)", "Legacy Shaders/Particles/Multiply", "Legacy Shaders/Particles/VertexLit Blended", "Particles/Standard Unlit", "Particles/Standard Surface", "TextMeshPro/Distance Field", "TextMeshPro/Distance Field (Surface)", "TextMeshPro/Distance Field Overlay", "TextMeshPro/Distance Field SSD", "TextMeshPro/Mobile/Distance Field",
			"TextMeshPro/Mobile/Distance Field - Masking", "TextMeshPro/Mobile/Distance Field Overlay", "TextMeshPro/Bitmap", "TextMeshPro/Mobile/Bitmap", "TextMeshPro/Sprite", "GUI/Text Shader"
		};

		internal static readonly HashSet<string> UnityBuiltInShaderNameSet = new HashSet<string>(UnityBuiltInShaderNames, StringComparer.OrdinalIgnoreCase);

		internal static Material? _transparentMat;

		internal static Material? _fallbackMat;

		internal bool _initialized;

		internal bool _sweepRunning;

		internal static bool _sweepDisabled;

		internal static string? _cachedValheimVersionForDriftCheck;

		internal static int _consecutiveSweepSkips;

		internal static bool _forceNextPeriodicSweep;

		internal static ManualLogSource Log { get; private set; } = null;

		internal static Plugin Instance { get; private set; } = null;

		internal static string ConfigDir => Path.Combine(Paths.ConfigPath, "ShaderHelperForMac");

		internal static float PeriodicSweepInterval => _instance?._cfgPeriodicSweepInterval.Value ?? 30f;

		internal static HashSet<string> BridgeTraceMatNames
		{
			get
			{
				string text = _instance?._cfgBridgeTraceMatNames.Value ?? string.Empty;
				HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
				if (string.IsNullOrWhiteSpace(text))
				{
					return hashSet;
				}
				string[] array = text.Split(new char[1] { ',' });
				foreach (string text2 in array)
				{
					string text3 = text2.Trim();
					if (text3.Length > 0)
					{
						hashSet.Add(text3);
					}
				}
				return hashSet;
			}
		}

		internal static bool SweepProgressTrace => _instance?._cfgSweepProgressTrace?.Value == true;

		internal static bool VanillaRebindEnabled => _instance?._cfgVanillaRebindEnabled?.Value ?? true;

		internal static bool PreserveAdditiveParticles => _instance?._cfgPreserveAdditiveParticles?.Value == true;

		internal static bool HunFXAutoDefault => _instance?._cfgHunFXAutoDefault?.Value ?? true;

		internal static bool AutoPolishMetallic => _instance?._cfgAutoPolishMetallic?.Value == true;

		internal static bool AutoPolishEquippedMetal => _instance?._cfgAutoPolishEquippedMetal?.Value ?? true;

		internal static float AutoPolishMetalAmount => _instance?._cfgAutoPolishMetalAmount?.Value ?? 1f;

		internal static bool AutoPolishUseGlossmap => _instance?._cfgAutoPolishUseGlossmap?.Value ?? true;

		internal static float AutoPolishMetalGloss => _instance?._cfgAutoPolishMetalGloss?.Value ?? 0.7f;

		internal static float AutoPolishGlossiness => _instance?._cfgAutoPolishGlossiness?.Value ?? 0.9f;

		internal static bool RepairInternalErrorShader => _instance?._cfgRepairInternalErrorShader?.Value ?? true;

		internal static bool TrailProbe => _instance?._cfgTrailProbe?.Value == true;

		internal static bool DiagnosticProbes => _instance?._cfgDiagnosticProbes?.Value == true;

		internal static bool _transparentShaderIsParticleCapable { get; set; }

		internal static Shader? FallbackShader { get; set; }

		internal static string StripCloneSuffix(string name)
		{
			while (name.EndsWith("(Clone)", StringComparison.Ordinal))
			{
				name = name.Substring(0, name.Length - "(Clone)".Length);
			}
			return name;
		}

		internal static void PopulateLane5FromSceneWalk()
		{
			_prefabScopedModMatIds.Clear();
			if (_modPrefabRootNames.Count == 0)
			{
				return;
			}
			int num = 0;
			int num2 = 0;
			Renderer[] array = Resources.FindObjectsOfTypeAll<Renderer>();
			Renderer[] array2 = array;
			foreach (Renderer val in array2)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				bool flag = false;
				Transform val2 = ((Component)val).transform;
				while ((Object)(object)val2 != (Object)null)
				{
					string item = StripCloneSuffix(((Object)val2).name);
					if (_modPrefabRootNames.Contains(item))
					{
						flag = true;
						break;
					}
					val2 = val2.parent;
				}
				if (!flag)
				{
					continue;
				}
				num++;
				Material[] sharedMaterials = val.sharedMaterials;
				foreach (Material val3 in sharedMaterials)
				{
					if (!((Object)(object)val3 == (Object)null) && _prefabScopedModMatIds.Add(((Object)val3).GetInstanceID()))
					{
						num2++;
					}
				}
			}
			if (num > 0)
			{
				Log.LogDebug((object)$"[Provenance] Lane-5 prefab scope: {num} renderer(s) under declared [prefab] ancestor(s), admitting {num2} material instance(s)");
			}
		}

		internal static void CheckCachedVanillaCacheVersionDrift()
		{
			if (_cachedValheimVersionForDriftCheck != null)
			{
				string valheimVersionViaReflection = VanillaBaselineCache.GetValheimVersionViaReflection();
				if (!string.Equals(_cachedValheimVersionForDriftCheck, valheimVersionViaReflection, StringComparison.Ordinal))
				{
					Log.LogWarning((object)("[VanillaCache] version drift: cache was produced for Valheim '" + _cachedValheimVersionForDriftCheck + "' but this install reports '" + valheimVersionViaReflection + "'. Keeping the cache loaded; if you observe vanilla materials being replaced, run `shaderhelper capturevanilla` from a clean install to refresh."));
				}
				_cachedValheimVersionForDriftCheck = null;
			}
		}

		private void Awake()
		{
			Instance = this;
			_instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			BundleAttribution.EmitBaselineSnapshot("Awake-Start");
			_cfgPeriodicSweepInterval = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Periodic Sweep Interval", 600f, "How often (in seconds) to re-sweep all materials. The periodic sweep is now a safety net for unobserved bundle-load / prefab-spawn events; the primary mechanisms are Harmony postfixes on ZNetScene.Awake, Location.Awake, AssetBundle.Load*, VisEquipment.AttachItem, and the scene-load handler. Default raised to 600 s in v3.0.63 because the v2.1.17 admission-epoch skip-gate already short-circuits every periodic sweep on a stable scene; the cadence only matters when the safety cap (10 consecutive skips) forces a full sweep. Lower this (e.g. 120 or 30) if you run VFX-heavy mods that spawn materials frequently and you observe pink materials between sweeps. Set to 0 to disable the periodic timer entirely.");
			_cfgBridgeTraceMatNames = ((BaseUnityPlugin)this).Config.Bind<string>("Diagnostics", "Bridge Trace Mat Names", "", "Diagnostic-only. Comma-separated list of material base names (case-insensitive, after StripInstance normalization). When a material's name is in this list, BridgeMaterialProperties emits one [BridgeTrace] log line per bridge call with the source _EmissionColor, IsHdrEmission result, _EMISSION keyword state, and HasEmissiveIntent result. Empty disables the trace (default). Used to gather evidence for the v3.0.7 emission-bridge timing investigation; see LESSONS_LEARNED.md and CHANGELOG v3.0.7. Pure read-only observation — does not affect any gate, sweep, or resolver decision.");
			_cfgSweepProgressTrace = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "Sweep Progress Trace", false, "Diagnostic-only. When true, SweepMaterialsCoroutine emits one [SweepProgress] log line per SweepBatchSize (=100) renderer batch, reporting per-batch wall-clock, cumulative sweep time, renderer index, mats processed, and replacement count. Used to identify where periodic-sweep wall-clock cost is concentrated when investigating frame-time stutter. Default false; enable for one diagnostic session, then disable. Pure read-only observation — does not affect any gate, sweep decision, or resolver outcome. See CHANGELOG v3.0.16.");
			_cfgVanillaRebindEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "Vanilla Rebind Enabled", true, "When true (DEFAULT), TryReplaceMaterial rebinds any material to the canonical Unity instance of its vanilla-named shader, ONCE per material per session, AND ONLY when the material is on a ParticleSystemRenderer this sweep. Fixes the dropped-item particle indicator (item_particle on Magic_Supremacy drops) without re-introducing the v2.1.27-class weapon flicker that v3.0.27's unscoped rebind caused. Set to false to disable the rebind entirely (restoring v3.0.28 behaviour: item_particle renders pink on dropped items, but no weapon flicker risk). The flag is provided as a kill-switch only — flipping to false is intended for users who observe regressions on materials this rebind class shouldn't have touched. See CHANGELOG v3.0.30 for the v3.0.29 evidence and v3.0.30 design.");
			_cfgPreserveAdditiveParticles = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "Preserve Additive Particles", true, "Default ON (v3.1.33). When true, a gate-admitted mod material whose current shader is already a supported additive / alpha-blended particle shader (e.g. 'Legacy Shaders/Particles/Additive', 'Particles/Standard Unlit') AND has no hard mat:/suffix: rule is LEFT ON ITS OWN SHADER instead of being routed through the aggressive-family Fallback bind to Custom/Creature (which drops the additive _TintColor/_InvFade controls — the flame_TW 'good look' loss). This reproduces, without a rule, what '[useshader] Legacy Shaders/Particles/Additive' achieves. Any user [forcefallback]/[forcetransparent]/[useshader] rule still wins (the branch is gated on no-hard-rule). Vanilla materials are unaffected (the provenance veto runs first). The v3.1.32 default-OFF opt-in was validated in-game (2165 [PreserveAdditive] fires across 36 distinct VFX materials on 4 shader families, zero solid geometry caught) and flipped to default-ON in v3.1.33. Set to false to disable the preservation branch and route additive particles through the aggressive-family Fallback. See CHANGELOG v3.1.32 + v3.1.33.");
			_cfgHunFXAutoDefault = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "HunFX Auto Default", true, "Default ON (v3.1.54, validated in-game). When true, a gate-admitted mod material whose current shader is in the 'HunFX/' family (the HunFX VFX shader pack — bursts / fire / embers / slashes, shader names 'HunFX/SH_HunFX_common', 'HunFX/SH_HunFX_simple', etc.) is automatically bridged to 'Legacy Shaders/Particles/Additive' — the same target users hand-write '[useshader]' rules for on these materials, applied automatically. This branch runs BEFORE the [useshader] consumption block, so it decides purely from the material's shader identity and ignores any [useshader] rule for HunFX materials by construction. Vanilla materials are unaffected (the provenance veto runs first). The v3.1.53 default-OFF opt-in was validated in-game (449 [HunFXAutoDefault] fires across 51 distinct VFX materials on 3 HunFX shaders, all landing Additive, zero solid geometry caught) and flipped to default-ON in v3.1.54. Set to false to disable the auto-default and route HunFX materials through the aggressive-family Fallback (or your own [useshader] rules). See CHANGELOG v3.1.53 + v3.1.54.");
			_cfgRepairInternalErrorShader = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "Repair Internal Error Shader", true, "Default ON. When true, a material whose runtime shader is Unity's 'Hidden/InternalErrorShader' placeholder (the magenta error shader Unity substitutes when a real shader fails to compile or load on Metal) is admitted to the sweep and bound to the Fallback shader (Custom/Creature) so it renders visibly instead of staying pink. Set to false to leave such materials magenta — the opt-in 'show me the pink items that were on this shader' diagnostic: both recognition sites (MaterialClassifier.NeedsReplacement and ActionResolver.ResolveByShaderName) fall through, and because 'Hidden/' is on the safe-shader prefix list NeedsReplacement returns false, leaving the material on its error-shader binding. This is a sweep-side filter toggle, NOT a provenance-gate change — vanilla materials rendering correctly are never affected (their shader is not Hidden/InternalErrorShader). See CHANGELOG v3.1.34.");
			_cfgTrailProbe = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "Trail Probe", false, "Diagnostic-only (default OFF). When true, a read-only Harmony postfix on Valheim's MeleeWeaponTrail.Start emits one [TrailProbe] log line per weapon-trail component as it is created, reporting the trail material's name + shader + the owning GameObject path. The vanilla weapon swing/sweep trail is a runtime-generated mesh on a detached GameObject('Trail') with a MeshRenderer — NOT a TrailRenderer, NOT a particle — so it cannot be named by `nearby` / `capture` / `watchr` or by the offline bundle tools. Enable this, swing a weapon, then read the [TrailProbe] lines in LogOutput.log to get the material name. Pure read-only observation — mutates no material, renderer, gate, sweep, resolver, or cache. Disable when done. See CHANGELOG v3.1.56.");
			_cfgDiagnosticProbes = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "Diagnostic Probes", false, "Diagnostic-only (default OFF). When true, the [BindProbe-*] shader-collision diagnostics in the bundle-load / Awake paths (OnLoadAsset, MaterialLoad, GameObjectMaterial, ShaderLoad, Baseline, RetroactiveSweep) emit [BindProbe-*] LogInfo lines and run their Resources.FindObjectsOfTypeAll<Shader>() peer-count scans. These are pure read-only observation used to localize vanilla-shader-name collisions during development; they mutate no material, gate, sweep, resolver, baseline, or cache state. Left OFF, the plugin skips the scan + log work entirely on every bundle load and Awake snapshot — recommended for normal play. Enable only for one diagnostic session when asked to capture [BindProbe-*] evidence, then disable. See CHANGELOG v3.1.65.");
			_cfgAutoPolishMetallic = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "Auto Polish Metallic Materials", false, "Default OFF (opt-in). When true, a gate-admitted mod material whose source _Metallic >= 0.5 AND that has a metallic-gloss map bound (_MetallicGlossMap / _MetallicMap) has its smoothness slots written (_UseGlossmap=1, _MetalGloss=0.7, _Glossiness=0.9, _Smoothness=0.9) after the Fallback bridge so the matte-metal blade (e.g. BronzeClaymore_mat_TW: _Metallic=1 + metallic-roughness map + smoothness 0 = dark diffuse metal on Metal) reads polished instead of dark. This automates, without a per-sword rule, the fix the user validated by hand via '[useshader] mat:<Blade> Custom/Creature _UseGlossmap=1 _MetalGloss=0.7 _Glossiness=0.9'. The write lands at the POST-BRIDGE Frame-B site the v3.1.49 probe proved has the writable slots (the reverted v3.1.45–47 wrote inside the bridge against a different, slot-less Custom/Creature instance and no-op'd three times). Non-metals (antler/leather/wood, all _Metallic=0) never enter the branch. Any per-mod [useshader] rule still wins (a ruled material returns before reaching this branch). Vanilla materials are unaffected (the provenance veto runs first). Sweep-side property write, NOT a provenance-gate change. Set to true to enable; validate that the intended blades polish and no non-metal changes, then a future release may flip the default ON. See CHANGELOG v3.1.49 + v3.1.50.");
			_cfgAutoPolishEquippedMetal = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "Auto Polish Equipped Metal", true, "Default ON (v3.1.52, validated in-game). When true, an equipped-item material the plugin has already bridged to Custom/Creature that carries an ASSIGNED metallic-gloss texture (_MetallicGlossMap has a real texture, not just the empty property slot) has its smoothness slots written so mod-metal weapons (e.g. Warfare IronSledge_mat_TW / bronzedark_mat_TW) read polished without a per-weapon [useshader] rule. The assigned-texture gate cleanly separates metal from equipped wood/leather (whose _MetallicGlossMap is null), which the v3.1.50 'Auto Polish Metallic Materials' _Metallic>=0.5 gate could not (mod authors ship metal weapons at _Metallic 0–0.2). The written values are the tunable 'Auto Polish Use Glossmap' / 'Auto Polish MetalGloss' / 'Auto Polish Glossiness' entries below. Does NOT raise _Metallic and does NOT touch emission (a glowing mod weapon needs a per-mod [useshader] _EmissionColor rule). Only materials already on Custom/Creature (gate-admitted, mod-owned, plugin-bridged) are touched — a correctly-rendering vanilla item is never affected. Sweep-side property write at the equip hook, NOT a provenance-gate change. See CHANGELOG v3.1.51.");
			_cfgAutoPolishMetalAmount = ((BaseUnityPlugin)this).Config.Bind<float>("Diagnostics", "Auto Polish Metallic", 1f, "Tuning for 'Auto Polish Equipped Metal'. The _Metallic value written to polished equipped-metal materials (0 = dielectric/plastic, 1 = fully metallic). Default 1.0. Mods routinely ship metal weapons at _Metallic 0–0.2 (the validated iron-knife knifeironblade_mat_TW = 0), which on a PBR lit shader renders as a glossy dielectric rather than metal even after the gloss writes land — so raising metalness is needed in addition to the smoothness values. Edit, then re-equip the weapon (or reload + sweep) to see the change. Only has effect when 'Auto Polish Equipped Metal' is enabled. See CHANGELOG v3.1.52.");
			_cfgAutoPolishUseGlossmap = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "Auto Polish Use Glossmap", true, "Tuning for 'Auto Polish Equipped Metal'. When true (default), the auto-polish writes _UseGlossmap=1 so the bound metallic-gloss map's alpha drives per-texel smoothness; false writes _UseGlossmap=0 (uniform smoothness from the MetalGloss/Glossiness values). Only has effect when 'Auto Polish Equipped Metal' is enabled. See CHANGELOG v3.1.51.");
			_cfgAutoPolishMetalGloss = ((BaseUnityPlugin)this).Config.Bind<float>("Diagnostics", "Auto Polish MetalGloss", 0.7f, "Tuning for 'Auto Polish Equipped Metal'. The _MetalGloss value written to polished equipped-metal materials (0 = matte, 1 = mirror). Default 0.7. Edit, then re-equip the weapon (or reload + sweep) to see the change. Only has effect when 'Auto Polish Equipped Metal' is enabled. See CHANGELOG v3.1.51.");
			_cfgAutoPolishGlossiness = ((BaseUnityPlugin)this).Config.Bind<float>("Diagnostics", "Auto Polish Glossiness", 0.9f, "Tuning for 'Auto Polish Equipped Metal'. The _Glossiness value written to polished equipped-metal materials (0 = matte, 1 = mirror). Default 0.9. Edit, then re-equip the weapon (or reload + sweep) to see the change. Only has effect when 'Auto Polish Equipped Metal' is enabled. See CHANGELOG v3.1.51.");
			foreach (AssetBundle allLoadedAssetBundle in AssetBundle.GetAllLoadedAssetBundles())
			{
				if (!((Object)(object)allLoadedAssetBundle == (Object)null))
				{
					_vanillaBundleNames.Add(((Object)allLoadedAssetBundle).name);
				}
			}
			VanillaBaselineCache.TryExtractEmbeddedBaseline(ConfigDir, Log);
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			HashSet<string> hashSet2 = new HashSet<string>(StringComparer.Ordinal);
			HashSet<string> hashSet3 = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			if (VanillaBaselineCache.TryLoad(ConfigDir, Log, hashSet, hashSet2, hashSet3, out string cachedValheimVersion))
			{
				foreach (string item in hashSet)
				{
					_baselineMatNames.Add(item);
				}
				foreach (string item2 in hashSet2)
				{
					_vanillaBundleNames.Add(item2);
				}
				foreach (string item3 in hashSet3)
				{
					_vanillaShaderNames.Add(item3);
				}
				Log.LogDebug((object)$"[VanillaCache] loaded as authoritative baseline: {_baselineMatNames.Count} material name(s), {_vanillaShaderNames.Count} shader name(s), {_vanillaBundleNames.Count} bundle name(s).");
				_cachedValheimVersionForDriftCheck = cachedValheimVersion;
				ReplacementCore.BumpAdmissionEpoch("Awake.VanillaCacheLoaded");
			}
			else
			{
				Log.LogWarning((object)"[VanillaCache] WARNING: no on-disk vanilla baseline found. The vanilla-name veto is DISARMED for this session — mod-name collisions with Valheim materials may be replaced unsafely. The plugin ships a pre-populated baseline as an embedded resource; if it failed to extract, run `shaderhelper capturevanilla` from a clean install:");
				Log.LogWarning((object)"[VanillaCache]   1. Move all non-ShaderHelperForMac DLLs out of BepInEx/plugins/.");
				Log.LogWarning((object)"[VanillaCache]   2. Launch Valheim and load any world.");
				Log.LogWarning((object)"[VanillaCache]   3. F5 console: shaderhelper capturevanilla");
				Log.LogWarning((object)"[VanillaCache]   4. Quit, restore your mod DLLs, relaunch.");
			}
			_sweepDisabled = _baselineMatNames.Count == 0 && _vanillaShaderNames.Count == 0;
			if (_sweepDisabled)
			{
				Log.LogWarning((object)"[v3.0] SWEEP DISABLED — vanilla truth file is empty (zero baseline mat names AND zero vanilla shader names). The option-3 admission gate cannot run safely without truth data; admitting everything would include vanilla materials. The replacement sweep will refuse on every trigger this session. Diagnostic commands remain functional. Run `shaderhelper capturevanilla` from a clean install to populate the cache (see [VanillaCache] WARNING above for the 4-step remediation), then relaunch.");
			}
			ReplacementCore.BumpAdmissionEpoch("Awake.PostCacheFlip");
			_awakeVanillaSnapshotEmpty = _vanillaBundleNames.Count == 0;
			Log.LogDebug((object)($"Vanilla bundles snapshotted: {_vanillaBundleNames.Count}" + (_awakeVanillaSnapshotEmpty ? "  (empty ⇒ memory/stream bundle tagging deferred to ZNetScene.Awake retroactive sweep)" : "")));
			_valheimDataPath = Application.dataPath.Replace('\\', '/').TrimEnd(new char[1] { '/' });
			Log.LogDebug((object)("Valheim data path: " + _valheimDataPath));
			int num = ShaderScanner.ScanPlugins(Paths.PluginPath, ModShaderDlls, Log);
			Log.LogDebug((object)$"Scanned plugins: {num} mod shader(s) recorded across {ModShaderDlls.Values.Distinct().Count()} DLL(s).");
			foreach (string item4 in ModShaderDlls.Values.Distinct())
			{
				Log.LogDebug((object)("  Shaders found in: '" + item4 + "'"));
			}
			ConfigLoader.EnsureConfigFiles();
			ReloadConfigFiles();
			StartConfigWatcher();
			BundleAttribution.EmitBaselineSnapshot("Awake-End");
			SceneManager.sceneLoaded += OnSceneLoaded;
		}

		private void OnDestroy()
		{
			SceneManager.sceneLoaded -= OnSceneLoaded;
			_watcher?.Dispose();
		}

		internal static int IndexOfInlineComment(string line)
		{
			return ConfigLoader.IndexOfInlineComment(line);
		}

		internal static void ReloadConfigFiles()
		{
			ConfigLoader.ReloadConfigFiles();
		}

		private void StartConfigWatcher()
		{
			try
			{
				_watcher = new FileSystemWatcher(ConfigDir, "*.txt")
				{
					NotifyFilter = NotifyFilters.LastWrite,
					EnableRaisingEvents = true
				};
				_watcher.Changed += delegate
				{
					ReloadConfigFiles();
				};
			}
			catch
			{
			}
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			if (!_initialized)
			{
				((MonoBehaviour)this).StartCoroutine(SweepCoordinator.InitAfterFrame(this));
			}
			else
			{
				((MonoBehaviour)this).StartCoroutine(SweepCoordinator.SweepAfterFrame(this));
			}
		}

		private void RegisterCommands()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			new ConsoleCommand("shaderhelper", "[patch|patches|undo|sweep|nearby|status|…]  ShaderHelperForMac — run `shaderhelper` alone for full usage.", (ConsoleEvent)delegate(ConsoleEventArgs args)
			{
				switch ((args.Length > 1) ? args[1].ToLowerInvariant() : "help")
				{
				case "reload":
					BasicCommands.HandleReload(this, args);
					break;
				case "capturevanilla":
					CaptureVanillaCommand.Handle(this, args);
					break;
				case "sweep":
					BasicCommands.HandleSweep(this, args);
					break;
				case "dumpmod":
					BasicCommands.HandleDumpMod(this, args);
					break;
				case "scan":
					DllFilteredCommands.HandleScan(this, args);
					break;
				case "pink":
					GateAuditCommands.HandlePink(this, args);
					break;
				case "shaders":
					BasicCommands.HandleShaders(this, args);
					break;
				case "renderers":
					BasicCommands.HandleRenderers(this, args);
					break;
				case "all":
					BasicCommands.HandleAll(this, args);
					break;
				case "nearby":
					GateAuditCommands.HandleNearby(this, args);
					break;
				case "transparent":
					DllFilteredCommands.HandleTransparent(this, args);
					break;
				case "loaded":
					DllFilteredCommands.HandleLoaded(this, args);
					break;
				case "checkvanilla":
					GateAuditCommands.HandleCheckVanilla(this, args);
					break;
				case "whymat":
					WhymatCommand.Handle(this, args);
					break;
				case "explain":
					GateAuditCommands.HandleExplain(this, args);
					break;
				case "status":
					StatusCommand.Handle(this, args);
					break;
				case "fallbackchain":
					FallbackChainCommand.Handle(this, args);
					break;
				case "resolveshader":
					ResolveShaderCommand.Handle(this, args);
					break;
				case "bindprobe":
					BindProbeCommand.Handle(this, args);
					break;
				case "patch":
					ConfigAuthoringCommands.HandlePatch(this, args);
					break;
				case "patches":
					ConfigAuthoringCommands.HandlePatches(this, args);
					break;
				case "undo":
					ConfigAuthoringCommands.HandleUndo(this, args);
					break;
				case "dlls":
					DllFilteredCommands.HandleDlls(this, args);
					break;
				case "classify":
					InfoCommands.HandleClassify(this, args);
					break;
				case "dumpprefabmap":
					InfoCommands.HandleDumpPrefabMap(this, args);
					break;
				case "suggest":
					InfoCommands.HandleSuggest(this, args);
					break;
				case "prefab":
					InfoCommands.HandlePrefab(this, args);
					break;
				case "modprefabs":
					InfoCommands.HandleModPrefabs(this, args);
					break;
				case "bundles":
					InfoCommands.HandleBundles(this, args);
					break;
				case "watch":
					ObserverCommands.HandleWatch(this, args);
					break;
				case "watchr":
					ObserverCommands.HandleWatchr(this, args);
					break;
				case "matdump":
					MatDumpCommand.Handle(this, args);
					break;
				case "checkredesign":
					GateAuditCommands.HandleCheckRedesign(this, args);
					break;
				case "psprobe":
					PsProbeCommand.Handle(this, args);
					break;
				case "capture":
				{
					string text = ((args.Length > 2) ? args[2].ToLowerInvariant() : string.Empty);
					if (text == "stop")
					{
						CaptureCommand.HandleStop(this, args);
					}
					else if (text == "start")
					{
						CaptureCommand.HandleStart(this, args);
					}
					else
					{
						args.Context.AddString("Usage: shaderhelper capture start [radius]   — begin continuous nearby-material capture");
						args.Context.AddString("       shaderhelper capture stop             — end capture and emit per-material summary");
					}
					break;
				}
				default:
					args.Context.AddString("Usage:  shaderhelper patch [radius] [DllName]  — stand near pink object; auto-adds a rule to the owning mod's .txt (default radius 15). Pass DllName for large-DLL mods.");
					args.Context.AddString("        shaderhelper patches             — list every batch previously added by `patch`");
					args.Context.AddString("        shaderhelper undo [id|all|last]  — revert the most recent patch batch (or a specific id, or all)");
					args.Context.AddString("        shaderhelper sweep               — run a material sweep immediately (reloads config first)");
					args.Context.AddString("        shaderhelper reload              — reload all config files without running a sweep");
					args.Context.AddString("        shaderhelper nearby [radius] [DllName|mod] — log materials near the player; shows patch=yes when patch would add a rule");
					args.Context.AddString("        shaderhelper transparent <DllName>  — log materials made (or to be made) transparent for a mod");
					args.Context.AddString("        shaderhelper loaded [DllName]    — log all in-memory materials incl. prefabs; optionally filter by mod DLL name");
					args.Context.AddString("        shaderhelper scan <DllName>      — log all materials from a mod with shader/support info");
					args.Context.AddString("        shaderhelper pink                — log all unhandled pink/unsupported materials");
					args.Context.AddString("        shaderhelper renderers           — log materials from active scene renderers");
					args.Context.AddString("        shaderhelper all                 — log ALL loaded materials regardless of action (diagnostics)");
					args.Context.AddString("        shaderhelper explain <MatName>   — audit why a material lands on Fallback/Transparent/Original");
					args.Context.AddString("        shaderhelper checkvanilla <Name> [Name2] ...  — report which vanilla / mod-attribution sets contain each name");
					args.Context.AddString("        shaderhelper whymat <MatName>    — report which lane(s) admitted a material (legacy diagnostic)");
					args.Context.AddString("        shaderhelper watch <MatName> [seconds] — log per-frame material state for N seconds (default 5, max 30); diagnoses flicker invisible to `explain`");
					args.Context.AddString("        shaderhelper watchr <RendererPattern> [seconds] — log per-frame renderer state for N seconds; diagnoses renderer.enabled toggling");
					args.Context.AddString("        shaderhelper matdump <MatName> [--save=<label>] — dump every shader property slot; --save writes to matdump/<label>.txt for later diffing");
					args.Context.AddString("        shaderhelper matdump --diff <labelA> <labelB>   — diff two saved matdumps");
					args.Context.AddString("        shaderhelper checkredesign       — audit the live gate's per-material decisions; reports vanilla-leaks (should always be 0)");
					args.Context.AddString("        shaderhelper classify [N]        — read-only scene walk + histogram of which IsParticleLike signal drives the auto-default");
					args.Context.AddString("        shaderhelper dlls [DllName]      — dump bundle→DLL and shader→DLL attribution tables");
					args.Context.AddString("        shaderhelper dumpprefabmap [DllName] — dump captured prefab-root → DLL attribution map");
					args.Context.AddString("        shaderhelper modprefabs <ModName>   — list every prefab root attributed to one mod with their child materials");
					args.Context.AddString("        shaderhelper bundles             — enumerate every loaded AssetBundle with attribution + TOC sample");
					args.Context.AddString("        shaderhelper suggest <DllName> [write] — dry-run (or write) auto-generated [prefab] lines for a mod");
					args.Context.AddString("        shaderhelper prefab <PrefabName> — list every material (incl. ParticleSystemRenderer effects) under a GameObject by name");
					args.Context.AddString("        shaderhelper capturevanilla      — force-rebuild the vanilla-baseline cache (run after a Valheim update; clean install only)");
					args.Context.AddString("        shaderhelper status              — show current plugin state (fallback shader, rule counts, gate inputs)");
					args.Context.AddString("        shaderhelper fallbackchain       — dump FallbackChain + TransparentChain resolution state (Shader.Find result, Resources matches, isSupported per entry, ACTIVE marker)");
					args.Context.AddString("        shaderhelper resolveshader <ShaderName> — enumerate every Shader instance + Material binding for a given shader name; reveals cross-bundle name-collision splits");
					args.Context.AddString("        shaderhelper psprobe [radius] [seconds]  — read-only ParticleSystemRenderer probe; reports renderer/PS state, optionally watches over a duration");
					args.Context.AddString("        shaderhelper capture start [radius]  — begin continuous nearby-material capture (auto-stops at 60s)");
					args.Context.AddString("        shaderhelper capture stop            — end capture; emits per-material summary sorted by frames-seen ascending (transient FX first)");
					break;
				}
			}, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			Log.LogDebug((object)"Console command 'shaderhelper' registered.");
		}

		internal static void RegisterCommandsInternal(Plugin self)
		{
			self.RegisterCommands();
		}

		private static void CollectCanonicalShaderIds()
		{
			ReplacementCore.CollectCanonicalShaderIds();
		}

		internal static void RefreshCanonicalShaderIdsFromLoadedAssets()
		{
			BundleAttribution.RefreshCanonicalShaderIdsFromLoadedAssets();
		}

		private static void BuildTransparentMaterial()
		{
			ReplacementCore.BuildTransparentMaterial();
		}

		internal static bool IsParticleCapableShaderName(string name)
		{
			return MaterialClassifier.IsParticleCapableShaderName(name);
		}

		internal static bool NeedsReplacement(Shader shader)
		{
			return MaterialClassifier.NeedsReplacement(shader);
		}

		internal static bool IsErrorPink(Material mat)
		{
			return MaterialClassifier.IsErrorPink(mat);
		}

		internal static string? ExtractMatSuffix(string name)
		{
			return MaterialClassifier.ExtractMatSuffix(name);
		}

		internal static bool IsHdrEmission(Material mat)
		{
			return MaterialClassifier.IsHdrEmission(mat);
		}

		internal static bool HasEmissiveIntent(Material mat)
		{
			return MaterialClassifier.HasEmissiveIntent(mat);
		}

		private static Shader? GetEmissiveFallbackShader()
		{
			return MaterialClassifier.GetEmissiveFallbackShader();
		}

		internal static Shader? GetEmissiveFallbackShaderInternal()
		{
			return GetEmissiveFallbackShader();
		}

		internal static bool IsParticleLikeShaderName(string shaderName)
		{
			return MaterialClassifier.IsParticleLikeShaderName(shaderName);
		}

		internal static bool IsParticleLike(Material mat)
		{
			return MaterialClassifier.IsParticleLike(mat);
		}

		internal static bool NeedsReplacement(Material mat)
		{
			return MaterialClassifier.NeedsReplacement(mat);
		}

		internal static bool IsAggressivelySwappable(string shaderName)
		{
			return MaterialClassifier.IsAggressivelySwappable(shaderName);
		}

		internal static bool IsJVLMockShader(string shaderName)
		{
			return MaterialClassifier.IsJVLMockShader(shaderName);
		}

		internal static void TryUpgradeTransparentShader()
		{
			ReplacementCore.TryUpgradeTransparentShader();
		}

		private static void BuildReplacementMaterials()
		{
			ReplacementCore.BuildReplacementMaterials();
		}

		internal static void TryUpgradeFallbackShader()
		{
			ReplacementCore.TryUpgradeFallbackShader();
		}

		internal static Shader? GetReplacementShader(string shaderName)
		{
			ShaderAction action = GetAction(shaderName);
			if (1 == 0)
			{
			}
			Shader result;
			switch (action)
			{
			case ShaderAction.Transparent:
			{
				Material? transparentMat = _transparentMat;
				result = ((transparentMat != null) ? transparentMat.shader : null);
				break;
			}
			case ShaderAction.Fallback:
				result = FallbackShader;
				break;
			case ShaderAction.UseShader:
				result = null;
				break;
			default:
				result = null;
				break;
			}
			if (1 == 0)
			{
			}
			return result;
		}

		internal static bool ShouldReplace(string shaderName)
		{
			if (IsForceTransparent(shaderName))
			{
				return true;
			}
			ModShaderDlls.TryGetValue(shaderName, out string value);
			if (value != null && _useOriginalDlls.Contains(value))
			{
				return false;
			}
			if (value != null && (_useFallbackDlls.Contains(value) || _useTransparentDlls.Contains(value)))
			{
				return true;
			}
			return false;
		}

		internal static bool IsForceTransparentShader(string shaderName)
		{
			return IsForceTransparent(shaderName);
		}

		internal static bool IsForceTransparent(string shaderName)
		{
			foreach (string item in _forceTransparentPrefixSet)
			{
				if (shaderName.StartsWith(item, StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
			}
			return false;
		}

		internal static bool IsForceFallback(string shaderName)
		{
			foreach (string item in _forceFallbackPrefixSet)
			{
				if (shaderName.StartsWith(item, StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
			}
			return false;
		}

		internal static void OnBundleLoadedFromFile(AssetBundle? bundle, string? path)
		{
			BundleAttribution.OnBundleLoadedFromFile(bundle, path);
		}

		internal static void OnBundleLoadedFromMemoryOrStream(AssetBundle? bundle)
		{
			BundleAttribution.OnBundleLoadedFromMemoryOrStream(bundle);
		}

		internal static void OnLoadAssetResult(AssetBundle? bundle, Object? result)
		{
			BundleAttribution.OnLoadAssetResult(bundle, result);
		}

		internal static void RetroactiveBundleSweep(string label)
		{
			BundleAttribution.RetroactiveBundleSweep(label);
		}

		internal static ShaderAction GetAction(Material mat)
		{
			return ActionResolver.Resolve(mat);
		}

		internal static ShaderAction GetAction(string shaderName)
		{
			return ActionResolver.ResolveByShaderName(shaderName);
		}

		internal void SweepMaterials()
		{
			SweepCoordinator.RunSweepMaterials(this);
		}

		internal void SweepMaterialsWithNotify()
		{
			SweepCoordinator.RunSweepMaterialsWithNotify(this);
		}

		internal static void TryReplaceMaterialPublic(Material mat)
		{
			TryReplaceMaterial(mat);
		}

		internal static void DumpMatPropertiesPublic(Material mat, string label)
		{
			MatDump.DumpMatPropertiesIfTargeted(mat, label);
		}

		internal static bool IsMaterialProcessedPublic(Material mat)
		{
			if ((Object)(object)mat == (Object)null)
			{
				return false;
			}
			return ReplacementCore._processedMaterialIds.Contains(((Object)mat).GetInstanceID());
		}

		internal static string StripInstancePublic(string name)
		{
			return StripInstance(name);
		}

		internal static bool IsErrorPinkPublic(Material mat)
		{
			return IsErrorPink(mat);
		}

		internal static bool IsParticleLikePublic(Material mat)
		{
			return IsParticleLike(mat);
		}

		internal IEnumerator SweepMaterialsCoroutine()
		{
			return SweepCoordinator.SweepMaterialsCoroutine(this);
		}

		internal IEnumerator SweepMaterialsCoroutine(string trigger)
		{
			return SweepCoordinator.SweepMaterialsCoroutine(this, trigger);
		}

		private static Shader? FindShaderByName(string name)
		{
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Invalid comparison between Unknown and I4
			Shader val = Shader.Find(name);
			if ((Object)(object)val != (Object)null && val.isSupported)
			{
				return val;
			}
			Shader[] array = Resources.FindObjectsOfTypeAll<Shader>();
			Shader val2 = null;
			Shader[] array2 = array;
			foreach (Shader val3 in array2)
			{
				if (!((Object)(object)val3 == (Object)null) && !(((Object)val3).name != name) && val3.isSupported)
				{
					if ((Object)(object)val2 == (Object)null)
					{
						val2 = val3;
					}
					int num = val3.FindPropertyIndex("_Color");
					if (num >= 0 && (int)val3.GetPropertyType(num) == 0)
					{
						return val3;
					}
				}
			}
			return val2;
		}

		internal static Shader? FindShaderByNameInternal(string name)
		{
			return FindShaderByName(name);
		}

		internal static string StripInstance(string name)
		{
			while (name.EndsWith(" (Instance)", StringComparison.Ordinal))
			{
				name = name.Substring(0, name.Length - " (Instance)".Length);
			}
			return name;
		}

		private static bool IsSoftMatRule(Material mat)
		{
			return PatchBatch.IsSoftMatRule(mat);
		}

		internal static bool IsHardMatRule(Material mat)
		{
			return PatchBatch.IsHardMatRule(mat);
		}

		private static bool IsExplicitMatRule(Material mat)
		{
			return PatchBatch.IsExplicitMatRule(mat);
		}

		internal static bool CheckMatNameRules(string matName, out ShaderAction action, out string reason)
		{
			return PatchBatch.CheckMatNameRules(matName, out action, out reason);
		}

		internal static string GeneratePatchBatchId()
		{
			return PatchBatch.GeneratePatchBatchId();
		}

		internal static string AppendPatchBatch(string dllFile, Dictionary<string, List<string>> rulesBySection, string batchId)
		{
			return PatchBatch.AppendPatchBatch(dllFile, rulesBySection, batchId);
		}

		internal static List<(string filePath, string dllName, string batchId, string timestamp, List<string> bodyLines)> EnumeratePatchBatches()
		{
			return PatchBatch.EnumeratePatchBatches();
		}

		internal static bool RemovePatchBatch(string filePath, string batchId)
		{
			return PatchBatch.RemovePatchBatch(filePath, batchId);
		}

		internal static (string section, string matName, string line, string? dllFile)? ProposeMatRule(Material mat, float distance, string parentName)
		{
			return PatchBatch.ProposeMatRule(mat, distance, parentName);
		}

		internal static bool FileAlreadyHasMatRule(string filePath, string matName)
		{
			return PatchBatch.FileAlreadyHasMatRule(filePath, matName);
		}

		internal static int TryReplaceMaterial(Material? mat)
		{
			return ReplacementCore.TryReplaceMaterial(mat);
		}
	}
	internal static class ShaderScanner
	{
		private sealed class NullAssemblyResolver : IAssemblyResolver, IDisposable
		{
			public static readonly NullAssemblyResolver Instance = new NullAssemblyResolver();

			public AssemblyDefinition? Resolve(AssemblyNameReference name)
			{
				return null;
			}

			public AssemblyDefinition? Resolve(AssemblyNameReference name, ReaderParameters p)
			{
				return null;
			}

			public void Dispose()
			{
			}
		}

		private static readonly byte[] UnityMagic = Encoding.ASCII.GetBytes("Unity");

		private static readonly string[] SafePrefixes = new string[13]
		{
			"JVLmock_", "Standard", "Hidden/", "Sprites/", "GUI/", "Particles/", "TextMeshPro/", "UI/", "Skybox/", "Custom/",
			"Unlit/", "Autodesk Interactive", "Lux Lit Particles/"
		};

		public static int ScanPlugins(string pluginsPath, Dictionary<string, string> modShaderDlls, ManualLogSource log)
		{
			if (!Directory.Exists(pluginsPath))
			{
				return 0;
			}
			int num = 0;
			List<string> list = Directory.EnumerateFiles(pluginsPath, "*.dll", SearchOption.AllDirectories).ToList();
			List<string> list2 = Directory.EnumerateFiles(pluginsPath, "*.bundle", SearchOption.AllDirectories).ToList();
			log.LogInfo((object)$"Scanner: found {list.Count} DLL(s) and {list2.Count} bundle file(s) to scan.");
			int num2 = 0;
			foreach (string item in list)
			{
				num2++;
				long num3 = 0L;
				try
				{
					num3 = new FileInfo(item).Length;
				}
				catch
				{
				}
				if (num3 > 10485760)
				{
					log.LogInfo((object)$"[{num2}/{list.Count}] Skipping large DLL '{Path.GetFileNameWithoutExtension(item)}' ({num3 / 1024 / 1024} MB) — use suffix:/mat: rules in txt files instead.");
					continue;
				}
				int num4 = ScanDll(item, modShaderDlls, log);
				if (num4 > 0)
				{
					log.LogDebug((object)$"[{num2}/{list.Count}] '{Path.GetFileNameWithoutExtension(item)}' — {num4} shader(s)");
				}
				num += num4;
			}
			foreach (string item2 in list2)
			{
				num += ScanBundleFile(item2, modShaderDlls, log);
			}
			return num;
		}

		private static int ScanDll(string dllPath, Dictionary<string, string> modShaderDlls, ManualLogSource log)
		{
			//IL_0003: 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_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			try
			{
				AssemblyDefinition val = AssemblyDefinition.ReadAssembly(dllPath, new ReaderParameters
				{
					ReadingMode = (ReadingMode)2,
					AssemblyResolver = (IAssemblyResolver)(object)NullAssemblyResolver.Instance
				});
				try
				{
					string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(dllPath);
					int num = 0;
					foreach (EmbeddedResource item in ((IEnumerable)val.MainModule.Resources).OfType<EmbeddedResource>())
					{
						byte[] resourceData;
						try
						{
							resourceData = item.GetResourceData();
						}
						catch (Exception ex)
						{
							log.LogDebug((object)("[Diag] " + fileNameWithoutExtension + ": resource '" + ((Resource)item).Name + "' read failed: " + ex.Message));
							continue;
						}
						if (IsUnityBundle(resourceData))
						{
							log.LogDebug((object)$"[Diag] {fileNameWithoutExtension}: found Unity bundle in resource

plugins/AssetsTools.NET.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using AssetsTools.NET.Extra;
using AssetsTools.NET.Extra.Decompressors.LZ4;
using LZ4ps;
using SevenZip;
using SevenZip.Compression.LZ;
using SevenZip.Compression.LZMA;
using SevenZip.Compression.RangeCoder;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("AssetsTools.NET")]
[assembly: AssemblyDescription("A remake and port of SeriousCache's AssetTools")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("nesrak1")]
[assembly: AssemblyProduct("AssetsTools.NET")]
[assembly: AssemblyCopyright("Written by nes")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("e09d5ac2-1a2e-4ec1-94ad-3f5e22f17658")]
[assembly: AssemblyFileVersion("3.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.0", FrameworkDisplayName = ".NET Framework 4")]
[assembly: AssemblyVersion("3.0.0.0")]
namespace SevenZip
{
	internal class CRC
	{
		public static readonly uint[] Table;

		private uint _value = uint.MaxValue;

		static CRC()
		{
			Table = new uint[256];
			for (uint num = 0u; num < 256; num++)
			{
				uint num2 = num;
				for (int i = 0; i < 8; i++)
				{
					num2 = (((num2 & 1) == 0) ? (num2 >> 1) : ((num2 >> 1) ^ 0xEDB88320u));
				}
				Table[num] = num2;
			}
		}

		public void Init()
		{
			_value = uint.MaxValue;
		}

		public void UpdateByte(byte b)
		{
			_value = Table[(byte)_value ^ b] ^ (_value >> 8);
		}

		public void Update(byte[] data, uint offset, uint size)
		{
			for (uint num = 0u; num < size; num++)
			{
				_value = Table[(byte)_value ^ data[offset + num]] ^ (_value >> 8);
			}
		}

		public uint GetDigest()
		{
			return _value ^ 0xFFFFFFFFu;
		}

		private static uint CalculateDigest(byte[] data, uint offset, uint size)
		{
			CRC cRC = new CRC();
			cRC.Update(data, offset, size);
			return cRC.GetDigest();
		}

		private static bool VerifyDigest(uint digest, byte[] data, uint offset, uint size)
		{
			return CalculateDigest(data, offset, size) == digest;
		}
	}
	internal class DataErrorException : ApplicationException
	{
		public DataErrorException()
			: base("Data Error")
		{
		}
	}
	internal class InvalidParamException : ApplicationException
	{
		public InvalidParamException()
			: base("Invalid Parameter")
		{
		}
	}
	public interface ICodeProgress
	{
		void SetProgress(long inSize, long outSize);
	}
	public interface ICoder
	{
		void Code(Stream inStream, Stream outStream, long inSize, long outSize, ICodeProgress progress);
	}
	public enum CoderPropID
	{
		DefaultProp,
		DictionarySize,
		UsedMemorySize,
		Order,
		BlockSize,
		PosStateBits,
		LitContextBits,
		LitPosBits,
		NumFastBytes,
		MatchFinder,
		MatchFinderCycles,
		NumPasses,
		Algorithm,
		NumThreads,
		EndMarker
	}
	public interface ISetCoderProperties
	{
		void SetCoderProperties(CoderPropID[] propIDs, object[] properties);
	}
	public interface IWriteCoderProperties
	{
		void WriteCoderProperties(Stream outStream);
	}
	public interface ISetDecoderProperties
	{
		void SetDecoderProperties(byte[] properties);
	}
}
namespace SevenZip.Compression.RangeCoder
{
	internal class Encoder
	{
		public const uint kTopValue = 16777216u;

		private Stream Stream;

		public ulong Low;

		public uint Range;

		private uint _cacheSize;

		private byte _cache;

		private long StartPosition;

		public void SetStream(Stream stream)
		{
			Stream = stream;
		}

		public void ReleaseStream()
		{
			Stream = null;
		}

		public void Init()
		{
			StartPosition = Stream.Position;
			Low = 0uL;
			Range = uint.MaxValue;
			_cacheSize = 1u;
			_cache = 0;
		}

		public void FlushData()
		{
			for (int i = 0; i < 5; i++)
			{
				ShiftLow();
			}
		}

		public void FlushStream()
		{
			Stream.Flush();
		}

		public void CloseStream()
		{
			Stream.Close();
		}

		public void Encode(uint start, uint size, uint total)
		{
			Low += start * (Range /= total);
			Range *= size;
			while (Range < 16777216)
			{
				Range <<= 8;
				ShiftLow();
			}
		}

		public void ShiftLow()
		{
			if ((uint)Low < 4278190080u || (int)(Low >> 32) == 1)
			{
				byte b = _cache;
				do
				{
					Stream.WriteByte((byte)(b + (Low >> 32)));
					b = byte.MaxValue;
				}
				while (--_cacheSize != 0);
				_cache = (byte)((uint)Low >> 24);
			}
			_cacheSize++;
			Low = (uint)((int)Low << 8);
		}

		public void EncodeDirectBits(uint v, int numTotalBits)
		{
			for (int num = numTotalBits - 1; num >= 0; num--)
			{
				Range >>= 1;
				if (((v >> num) & 1) == 1)
				{
					Low += Range;
				}
				if (Range < 16777216)
				{
					Range <<= 8;
					ShiftLow();
				}
			}
		}

		public void EncodeBit(uint size0, int numTotalBits, uint symbol)
		{
			uint num = (Range >> numTotalBits) * size0;
			if (symbol == 0)
			{
				Range = num;
			}
			else
			{
				Low += num;
				Range -= num;
			}
			while (Range < 16777216)
			{
				Range <<= 8;
				ShiftLow();
			}
		}

		public long GetProcessedSizeAdd()
		{
			return _cacheSize + Stream.Position - StartPosition + 4;
		}
	}
	internal class Decoder
	{
		public const uint kTopValue = 16777216u;

		public uint Range;

		public uint Code;

		public Stream Stream;

		public void Init(Stream stream)
		{
			Stream = stream;
			Code = 0u;
			Range = uint.MaxValue;
			for (int i = 0; i < 5; i++)
			{
				Code = (Code << 8) | (byte)Stream.ReadByte();
			}
		}

		public void ReleaseStream()
		{
			Stream = null;
		}

		public void CloseStream()
		{
			Stream.Close();
		}

		public void Normalize()
		{
			while (Range < 16777216)
			{
				Code = (Code << 8) | (byte)Stream.ReadByte();
				Range <<= 8;
			}
		}

		public void Normalize2()
		{
			if (Range < 16777216)
			{
				Code = (Code << 8) | (byte)Stream.ReadByte();
				Range <<= 8;
			}
		}

		public uint GetThreshold(uint total)
		{
			return Code / (Range /= total);
		}

		public void Decode(uint start, uint size, uint total)
		{
			Code -= start * Range;
			Range *= size;
			Normalize();
		}

		public uint DecodeDirectBits(int numTotalBits)
		{
			uint num = Range;
			uint num2 = Code;
			uint num3 = 0u;
			for (int num4 = numTotalBits; num4 > 0; num4--)
			{
				num >>= 1;
				uint num5 = num2 - num >> 31;
				num2 -= num & (num5 - 1);
				num3 = (num3 << 1) | (1 - num5);
				if (num < 16777216)
				{
					num2 = (num2 << 8) | (byte)Stream.ReadByte();
					num <<= 8;
				}
			}
			Range = num;
			Code = num2;
			return num3;
		}

		public uint DecodeBit(uint size0, int numTotalBits)
		{
			uint num = (Range >> numTotalBits) * size0;
			uint result;
			if (Code < num)
			{
				result = 0u;
				Range = num;
			}
			else
			{
				result = 1u;
				Code -= num;
				Range -= num;
			}
			Normalize();
			return result;
		}
	}
	internal struct BitEncoder
	{
		public const int kNumBitModelTotalBits = 11;

		public const uint kBitModelTotal = 2048u;

		private const int kNumMoveBits = 5;

		private const int kNumMoveReducingBits = 2;

		public const int kNumBitPriceShiftBits = 6;

		private uint Prob;

		private static uint[] ProbPrices;

		public void Init()
		{
			Prob = 1024u;
		}

		public void UpdateModel(uint symbol)
		{
			if (symbol == 0)
			{
				Prob += 2048 - Prob >> 5;
			}
			else
			{
				Prob -= Prob >> 5;
			}
		}

		public void Encode(Encoder encoder, uint symbol)
		{
			uint num = (encoder.Range >> 11) * Prob;
			if (symbol == 0)
			{
				encoder.Range = num;
				Prob += 2048 - Prob >> 5;
			}
			else
			{
				encoder.Low += num;
				encoder.Range -= num;
				Prob -= Prob >> 5;
			}
			if (encoder.Range < 16777216)
			{
				encoder.Range <<= 8;
				encoder.ShiftLow();
			}
		}

		static BitEncoder()
		{
			ProbPrices = new uint[512];
			for (int num = 8; num >= 0; num--)
			{
				int num2 = 1 << 9 - num - 1;
				uint num3 = (uint)(1 << 9 - num);
				for (uint num4 = (uint)num2; num4 < num3; num4++)
				{
					ProbPrices[num4] = (uint)(num << 6) + (num3 - num4 << 6 >> 9 - num - 1);
				}
			}
		}

		public uint GetPrice(uint symbol)
		{
			return ProbPrices[(((Prob - symbol) ^ (int)(0 - symbol)) & 0x7FF) >> 2];
		}

		public uint GetPrice0()
		{
			return ProbPrices[Prob >> 2];
		}

		public uint GetPrice1()
		{
			return ProbPrices[2048 - Prob >> 2];
		}
	}
	internal struct BitDecoder
	{
		public const int kNumBitModelTotalBits = 11;

		public const uint kBitModelTotal = 2048u;

		private const int kNumMoveBits = 5;

		private uint Prob;

		public void UpdateModel(int numMoveBits, uint symbol)
		{
			if (symbol == 0)
			{
				Prob += 2048 - Prob >> numMoveBits;
			}
			else
			{
				Prob -= Prob >> numMoveBits;
			}
		}

		public void Init()
		{
			Prob = 1024u;
		}

		public uint Decode(Decoder rangeDecoder)
		{
			uint num = (rangeDecoder.Range >> 11) * Prob;
			if (rangeDecoder.Code < num)
			{
				rangeDecoder.Range = num;
				Prob += 2048 - Prob >> 5;
				if (rangeDecoder.Range < 16777216)
				{
					rangeDecoder.Code = (rangeDecoder.Code << 8) | (byte)rangeDecoder.Stream.ReadByte();
					rangeDecoder.Range <<= 8;
				}
				return 0u;
			}
			rangeDecoder.Range -= num;
			rangeDecoder.Code -= num;
			Prob -= Prob >> 5;
			if (rangeDecoder.Range < 16777216)
			{
				rangeDecoder.Code = (rangeDecoder.Code << 8) | (byte)rangeDecoder.Stream.ReadByte();
				rangeDecoder.Range <<= 8;
			}
			return 1u;
		}
	}
	internal struct BitTreeEncoder
	{
		private BitEncoder[] Models;

		private int NumBitLevels;

		public BitTreeEncoder(int numBitLevels)
		{
			NumBitLevels = numBitLevels;
			Models = new BitEncoder[1 << numBitLevels];
		}

		public void Init()
		{
			for (uint num = 1u; num < 1 << NumBitLevels; num++)
			{
				Models[num].Init();
			}
		}

		public void Encode(Encoder rangeEncoder, uint symbol)
		{
			uint num = 1u;
			int num2 = NumBitLevels;
			while (num2 > 0)
			{
				num2--;
				uint num3 = (symbol >> num2) & 1u;
				Models[num].Encode(rangeEncoder, num3);
				num = (num << 1) | num3;
			}
		}

		public void ReverseEncode(Encoder rangeEncoder, uint symbol)
		{
			uint num = 1u;
			for (uint num2 = 0u; num2 < NumBitLevels; num2++)
			{
				uint num3 = symbol & 1u;
				Models[num].Encode(rangeEncoder, num3);
				num = (num << 1) | num3;
				symbol >>= 1;
			}
		}

		public uint GetPrice(uint symbol)
		{
			uint num = 0u;
			uint num2 = 1u;
			int num3 = NumBitLevels;
			while (num3 > 0)
			{
				num3--;
				uint num4 = (symbol >> num3) & 1u;
				num += Models[num2].GetPrice(num4);
				num2 = (num2 << 1) + num4;
			}
			return num;
		}

		public uint ReverseGetPrice(uint symbol)
		{
			uint num = 0u;
			uint num2 = 1u;
			for (int num3 = NumBitLevels; num3 > 0; num3--)
			{
				uint num4 = symbol & 1u;
				symbol >>= 1;
				num += Models[num2].GetPrice(num4);
				num2 = (num2 << 1) | num4;
			}
			return num;
		}

		public static uint ReverseGetPrice(BitEncoder[] Models, uint startIndex, int NumBitLevels, uint symbol)
		{
			uint num = 0u;
			uint num2 = 1u;
			for (int num3 = NumBitLevels; num3 > 0; num3--)
			{
				uint num4 = symbol & 1u;
				symbol >>= 1;
				num += Models[startIndex + num2].GetPrice(num4);
				num2 = (num2 << 1) | num4;
			}
			return num;
		}

		public static void ReverseEncode(BitEncoder[] Models, uint startIndex, Encoder rangeEncoder, int NumBitLevels, uint symbol)
		{
			uint num = 1u;
			for (int i = 0; i < NumBitLevels; i++)
			{
				uint num2 = symbol & 1u;
				Models[startIndex + num].Encode(rangeEncoder, num2);
				num = (num << 1) | num2;
				symbol >>= 1;
			}
		}
	}
	internal struct BitTreeDecoder
	{
		private BitDecoder[] Models;

		private int NumBitLevels;

		public BitTreeDecoder(int numBitLevels)
		{
			NumBitLevels = numBitLevels;
			Models = new BitDecoder[1 << numBitLevels];
		}

		public void Init()
		{
			for (uint num = 1u; num < 1 << NumBitLevels; num++)
			{
				Models[num].Init();
			}
		}

		public uint Decode(Decoder rangeDecoder)
		{
			uint num = 1u;
			for (int num2 = NumBitLevels; num2 > 0; num2--)
			{
				num = (num << 1) + Models[num].Decode(rangeDecoder);
			}
			return num - (uint)(1 << NumBitLevels);
		}

		public uint ReverseDecode(Decoder rangeDecoder)
		{
			uint num = 1u;
			uint num2 = 0u;
			for (int i = 0; i < NumBitLevels; i++)
			{
				uint num3 = Models[num].Decode(rangeDecoder);
				num <<= 1;
				num += num3;
				num2 |= num3 << i;
			}
			return num2;
		}

		public static uint ReverseDecode(BitDecoder[] Models, uint startIndex, Decoder rangeDecoder, int NumBitLevels)
		{
			uint num = 1u;
			uint num2 = 0u;
			for (int i = 0; i < NumBitLevels; i++)
			{
				uint num3 = Models[startIndex + num].Decode(rangeDecoder);
				num <<= 1;
				num += num3;
				num2 |= num3 << i;
			}
			return num2;
		}
	}
}
namespace SevenZip.Compression.LZ
{
	internal interface IInWindowStream
	{
		void SetStream(Stream inStream);

		void Init();

		void ReleaseStream();

		byte GetIndexByte(int index);

		uint GetMatchLen(int index, uint distance, uint limit);

		uint GetNumAvailableBytes();
	}
	internal interface IMatchFinder : IInWindowStream
	{
		void Create(uint historySize, uint keepAddBufferBefore, uint matchMaxLen, uint keepAddBufferAfter);

		uint GetMatches(uint[] distances);

		void Skip(uint num);
	}
	public class BinTree : InWindow, IMatchFinder, IInWindowStream
	{
		private uint _cyclicBufferPos;

		private uint _cyclicBufferSize;

		private uint _matchMaxLen;

		private uint[] _son;

		private uint[] _hash;

		private uint _cutValue = 255u;

		private uint _hashMask;

		private uint _hashSizeSum;

		private bool HASH_ARRAY = true;

		private const uint kHash2Size = 1024u;

		private const uint kHash3Size = 65536u;

		private const uint kBT2HashSize = 65536u;

		private const uint kStartMaxLen = 1u;

		private const uint kHash3Offset = 1024u;

		private const uint kEmptyHashValue = 0u;

		private const uint kMaxValForNormalize = 2147483647u;

		private uint kNumHashDirectBytes;

		private uint kMinMatchCheck = 4u;

		private uint kFixHashSize = 66560u;

		public void SetType(int numHashBytes)
		{
			HASH_ARRAY = numHashBytes > 2;
			if (HASH_ARRAY)
			{
				kNumHashDirectBytes = 0u;
				kMinMatchCheck = 4u;
				kFixHashSize = 66560u;
			}
			else
			{
				kNumHashDirectBytes = 2u;
				kMinMatchCheck = 3u;
				kFixHashSize = 0u;
			}
		}

		public new void SetStream(Stream stream)
		{
			base.SetStream(stream);
		}

		public new void ReleaseStream()
		{
			base.ReleaseStream();
		}

		public new void Init()
		{
			base.Init();
			for (uint num = 0u; num < _hashSizeSum; num++)
			{
				_hash[num] = 0u;
			}
			_cyclicBufferPos = 0u;
			ReduceOffsets(-1);
		}

		public new void MovePos()
		{
			if (++_cyclicBufferPos >= _cyclicBufferSize)
			{
				_cyclicBufferPos = 0u;
			}
			base.MovePos();
			if (_pos == int.MaxValue)
			{
				Normalize();
			}
		}

		public new byte GetIndexByte(int index)
		{
			return base.GetIndexByte(index);
		}

		public new uint GetMatchLen(int index, uint distance, uint limit)
		{
			return base.GetMatchLen(index, distance, limit);
		}

		public new uint GetNumAvailableBytes()
		{
			return base.GetNumAvailableBytes();
		}

		public void Create(uint historySize, uint keepAddBufferBefore, uint matchMaxLen, uint keepAddBufferAfter)
		{
			if (historySize > 2147483391)
			{
				throw new Exception();
			}
			_cutValue = 16 + (matchMaxLen >> 1);
			uint keepSizeReserv = (historySize + keepAddBufferBefore + matchMaxLen + keepAddBufferAfter) / 2 + 256;
			Create(historySize + keepAddBufferBefore, matchMaxLen + keepAddBufferAfter, keepSizeReserv);
			_matchMaxLen = matchMaxLen;
			uint num = historySize + 1;
			if (_cyclicBufferSize != num)
			{
				_son = new uint[(_cyclicBufferSize = num) * 2];
			}
			uint num2 = 65536u;
			if (HASH_ARRAY)
			{
				num2 = historySize - 1;
				num2 |= num2 >> 1;
				num2 |= num2 >> 2;
				num2 |= num2 >> 4;
				num2 |= num2 >> 8;
				num2 >>= 1;
				num2 |= 0xFFFFu;
				if (num2 > 16777216)
				{
					num2 >>= 1;
				}
				_hashMask = num2;
				num2++;
				num2 += kFixHashSize;
			}
			if (num2 != _hashSizeSum)
			{
				_hash = new uint[_hashSizeSum = num2];
			}
		}

		public uint GetMatches(uint[] distances)
		{
			uint num;
			if (_pos + _matchMaxLen <= _streamPos)
			{
				num = _matchMaxLen;
			}
			else
			{
				num = _streamPos - _pos;
				if (num < kMinMatchCheck)
				{
					MovePos();
					return 0u;
				}
			}
			uint num2 = 0u;
			uint num3 = ((_pos > _cyclicBufferSize) ? (_pos - _cyclicBufferSize) : 0u);
			uint num4 = _bufferOffset + _pos;
			uint num5 = 1u;
			uint num6 = 0u;
			uint num7 = 0u;
			uint num10;
			if (HASH_ARRAY)
			{
				uint num8 = CRC.Table[_bufferBase[num4]] ^ _bufferBase[num4 + 1];
				num6 = num8 & 0x3FFu;
				int num9 = (int)num8 ^ (_bufferBase[num4 + 2] << 8);
				num7 = (uint)num9 & 0xFFFFu;
				num10 = ((uint)num9 ^ (CRC.Table[_bufferBase[num4 + 3]] << 5)) & _hashMask;
			}
			else
			{
				num10 = (uint)(_bufferBase[num4] ^ (_bufferBase[num4 + 1] << 8));
			}
			uint num11 = _hash[kFixHashSize + num10];
			if (HASH_ARRAY)
			{
				uint num12 = _hash[num6];
				uint num13 = _hash[1024 + num7];
				_hash[num6] = _pos;
				_hash[1024 + num7] = _pos;
				if (num12 > num3 && _bufferBase[_bufferOffset + num12] == _bufferBase[num4])
				{
					num5 = (distances[num2++] = 2u);
					distances[num2++] = _pos - num12 - 1;
				}
				if (num13 > num3 && _bufferBase[_bufferOffset + num13] == _bufferBase[num4])
				{
					if (num13 == num12)
					{
						num2 -= 2;
					}
					num5 = (distances[num2++] = 3u);
					distances[num2++] = _pos - num13 - 1;
					num12 = num13;
				}
				if (num2 != 0 && num12 == num11)
				{
					num2 -= 2;
					num5 = 1u;
				}
			}
			_hash[kFixHashSize + num10] = _pos;
			uint num14 = (_cyclicBufferPos << 1) + 1;
			uint num15 = _cyclicBufferPos << 1;
			uint val;
			uint val2 = (val = kNumHashDirectBytes);
			if (kNumHashDirectBytes != 0 && num11 > num3 && _bufferBase[_bufferOffset + num11 + kNumHashDirectBytes] != _bufferBase[num4 + kNumHashDirectBytes])
			{
				num5 = (distances[num2++] = kNumHashDirectBytes);
				distances[num2++] = _pos - num11 - 1;
			}
			uint cutValue = _cutValue;
			while (true)
			{
				if (num11 <= num3 || cutValue-- == 0)
				{
					_son[num14] = (_son[num15] = 0u);
					break;
				}
				uint num16 = _pos - num11;
				uint num17 = ((num16 <= _cyclicBufferPos) ? (_cyclicBufferPos - num16) : (_cyclicBufferPos - num16 + _cyclicBufferSize)) << 1;
				uint num18 = _bufferOffset + num11;
				uint num19 = Math.Min(val2, val);
				if (_bufferBase[num18 + num19] == _bufferBase[num4 + num19])
				{
					while (++num19 != num && _bufferBase[num18 + num19] == _bufferBase[num4 + num19])
					{
					}
					if (num5 < num19)
					{
						num5 = (distances[num2++] = num19);
						distances[num2++] = num16 - 1;
						if (num19 == num)
						{
							_son[num15] = _son[num17];
							_son[num14] = _son[num17 + 1];
							break;
						}
					}
				}
				if (_bufferBase[num18 + num19] < _bufferBase[num4 + num19])
				{
					_son[num15] = num11;
					num15 = num17 + 1;
					num11 = _son[num15];
					val = num19;
				}
				else
				{
					_son[num14] = num11;
					num14 = num17;
					num11 = _son[num14];
					val2 = num19;
				}
			}
			MovePos();
			return num2;
		}

		public void Skip(uint num)
		{
			do
			{
				uint num2;
				if (_pos + _matchMaxLen <= _streamPos)
				{
					num2 = _matchMaxLen;
				}
				else
				{
					num2 = _streamPos - _pos;
					if (num2 < kMinMatchCheck)
					{
						MovePos();
						continue;
					}
				}
				uint num3 = ((_pos > _cyclicBufferSize) ? (_pos - _cyclicBufferSize) : 0u);
				uint num4 = _bufferOffset + _pos;
				uint num9;
				if (HASH_ARRAY)
				{
					uint num5 = CRC.Table[_bufferBase[num4]] ^ _bufferBase[num4 + 1];
					uint num6 = num5 & 0x3FFu;
					_hash[num6] = _pos;
					int num7 = (int)num5 ^ (_bufferBase[num4 + 2] << 8);
					uint num8 = (uint)num7 & 0xFFFFu;
					_hash[1024 + num8] = _pos;
					num9 = ((uint)num7 ^ (CRC.Table[_bufferBase[num4 + 3]] << 5)) & _hashMask;
				}
				else
				{
					num9 = (uint)(_bufferBase[num4] ^ (_bufferBase[num4 + 1] << 8));
				}
				uint num10 = _hash[kFixHashSize + num9];
				_hash[kFixHashSize + num9] = _pos;
				uint num11 = (_cyclicBufferPos << 1) + 1;
				uint num12 = _cyclicBufferPos << 1;
				uint val;
				uint val2 = (val = kNumHashDirectBytes);
				uint cutValue = _cutValue;
				while (true)
				{
					if (num10 <= num3 || cutValue-- == 0)
					{
						_son[num11] = (_son[num12] = 0u);
						break;
					}
					uint num13 = _pos - num10;
					uint num14 = ((num13 <= _cyclicBufferPos) ? (_cyclicBufferPos - num13) : (_cyclicBufferPos - num13 + _cyclicBufferSize)) << 1;
					uint num15 = _bufferOffset + num10;
					uint num16 = Math.Min(val2, val);
					if (_bufferBase[num15 + num16] == _bufferBase[num4 + num16])
					{
						while (++num16 != num2 && _bufferBase[num15 + num16] == _bufferBase[num4 + num16])
						{
						}
						if (num16 == num2)
						{
							_son[num12] = _son[num14];
							_son[num11] = _son[num14 + 1];
							break;
						}
					}
					if (_bufferBase[num15 + num16] < _bufferBase[num4 + num16])
					{
						_son[num12] = num10;
						num12 = num14 + 1;
						num10 = _son[num12];
						val = num16;
					}
					else
					{
						_son[num11] = num10;
						num11 = num14;
						num10 = _son[num11];
						val2 = num16;
					}
				}
				MovePos();
			}
			while (--num != 0);
		}

		private void NormalizeLinks(uint[] items, uint numItems, uint subValue)
		{
			for (uint num = 0u; num < numItems; num++)
			{
				uint num2 = items[num];
				num2 = ((num2 > subValue) ? (num2 - subValue) : 0u);
				items[num] = num2;
			}
		}

		private void Normalize()
		{
			uint subValue = _pos - _cyclicBufferSize;
			NormalizeLinks(_son, _cyclicBufferSize * 2, subValue);
			NormalizeLinks(_hash, _hashSizeSum, subValue);
			ReduceOffsets((int)subValue);
		}

		public void SetCutValue(uint cutValue)
		{
			_cutValue = cutValue;
		}
	}
	public class InWindow
	{
		public byte[] _bufferBase;

		private Stream _stream;

		private uint _posLimit;

		private bool _streamEndWasReached;

		private uint _pointerToLastSafePosition;

		public uint _bufferOffset;

		public uint _blockSize;

		public uint _pos;

		private uint _keepSizeBefore;

		private uint _keepSizeAfter;

		public uint _streamPos;

		public void MoveBlock()
		{
			uint num = _bufferOffset + _pos - _keepSizeBefore;
			if (num != 0)
			{
				num--;
			}
			uint num2 = _bufferOffset + _streamPos - num;
			for (uint num3 = 0u; num3 < num2; num3++)
			{
				_bufferBase[num3] = _bufferBase[num + num3];
			}
			_bufferOffset -= num;
		}

		public virtual void ReadBlock()
		{
			if (_streamEndWasReached)
			{
				return;
			}
			while (true)
			{
				int num = (int)(0 - _bufferOffset + _blockSize - _streamPos);
				if (num == 0)
				{
					return;
				}
				int num2 = _stream.Read(_bufferBase, (int)(_bufferOffset + _streamPos), num);
				if (num2 == 0)
				{
					break;
				}
				_streamPos += (uint)num2;
				if (_streamPos >= _pos + _keepSizeAfter)
				{
					_posLimit = _streamPos - _keepSizeAfter;
				}
			}
			_posLimit = _streamPos;
			if (_bufferOffset + _posLimit > _pointerToLastSafePosition)
			{
				_posLimit = _pointerToLastSafePosition - _bufferOffset;
			}
			_streamEndWasReached = true;
		}

		private void Free()
		{
			_bufferBase = null;
		}

		public void Create(uint keepSizeBefore, uint keepSizeAfter, uint keepSizeReserv)
		{
			_keepSizeBefore = keepSizeBefore;
			_keepSizeAfter = keepSizeAfter;
			uint num = keepSizeBefore + keepSizeAfter + keepSizeReserv;
			if (_bufferBase == null || _blockSize != num)
			{
				Free();
				_blockSize = num;
				_bufferBase = new byte[_blockSize];
			}
			_pointerToLastSafePosition = _blockSize - keepSizeAfter;
		}

		public void SetStream(Stream stream)
		{
			_stream = stream;
		}

		public void ReleaseStream()
		{
			_stream = null;
		}

		public void Init()
		{
			_bufferOffset = 0u;
			_pos = 0u;
			_streamPos = 0u;
			_streamEndWasReached = false;
			ReadBlock();
		}

		public void MovePos()
		{
			_pos++;
			if (_pos > _posLimit)
			{
				if (_bufferOffset + _pos > _pointerToLastSafePosition)
				{
					MoveBlock();
				}
				ReadBlock();
			}
		}

		public byte GetIndexByte(int index)
		{
			return _bufferBase[_bufferOffset + _pos + index];
		}

		public uint GetMatchLen(int index, uint distance, uint limit)
		{
			if (_streamEndWasReached && _pos + index + limit > _streamPos)
			{
				limit = _streamPos - (uint)(int)(_pos + index);
			}
			distance++;
			uint num = _bufferOffset + _pos + (uint)index;
			uint num2;
			for (num2 = 0u; num2 < limit && _bufferBase[num + num2] == _bufferBase[num + num2 - distance]; num2++)
			{
			}
			return num2;
		}

		public uint GetNumAvailableBytes()
		{
			return _streamPos - _pos;
		}

		public void ReduceOffsets(int subValue)
		{
			_bufferOffset += (uint)subValue;
			_posLimit -= (uint)subValue;
			_pos -= (uint)subValue;
			_streamPos -= (uint)subValue;
		}
	}
	public class OutWindow
	{
		private byte[] _buffer;

		private uint _pos;

		private uint _windowSize;

		private uint _streamPos;

		private Stream _stream;

		public uint TrainSize;

		public void Create(uint windowSize)
		{
			if (_windowSize != windowSize)
			{
				_buffer = new byte[windowSize];
			}
			_windowSize = windowSize;
			_pos = 0u;
			_streamPos = 0u;
		}

		public void Init(Stream stream, bool solid)
		{
			ReleaseStream();
			_stream = stream;
			if (!solid)
			{
				_streamPos = 0u;
				_pos = 0u;
				TrainSize = 0u;
			}
		}

		public bool Train(Stream stream)
		{
			long length = stream.Length;
			uint num = (TrainSize = (uint)((length < _windowSize) ? length : _windowSize));
			stream.Position = length - num;
			_streamPos = (_pos = 0u);
			while (num != 0)
			{
				uint num2 = _windowSize - _pos;
				if (num < num2)
				{
					num2 = num;
				}
				int num3 = stream.Read(_buffer, (int)_pos, (int)num2);
				if (num3 == 0)
				{
					return false;
				}
				num -= (uint)num3;
				_pos += (uint)num3;
				_streamPos += (uint)num3;
				if (_pos == _windowSize)
				{
					_streamPos = (_pos = 0u);
				}
			}
			return true;
		}

		public void ReleaseStream()
		{
			Flush();
			_stream = null;
		}

		public void Flush()
		{
			uint num = _pos - _streamPos;
			if (num != 0)
			{
				_stream.Write(_buffer, (int)_streamPos, (int)num);
				if (_pos >= _windowSize)
				{
					_pos = 0u;
				}
				_streamPos = _pos;
			}
		}

		public void CopyBlock(uint distance, uint len)
		{
			uint num = _pos - distance - 1;
			if (num >= _windowSize)
			{
				num += _windowSize;
			}
			while (len != 0)
			{
				if (num >= _windowSize)
				{
					num = 0u;
				}
				_buffer[_pos++] = _buffer[num++];
				if (_pos >= _windowSize)
				{
					Flush();
				}
				len--;
			}
		}

		public void PutByte(byte b)
		{
			_buffer[_pos++] = b;
			if (_pos >= _windowSize)
			{
				Flush();
			}
		}

		public byte GetByte(uint distance)
		{
			uint num = _pos - distance - 1;
			if (num >= _windowSize)
			{
				num += _windowSize;
			}
			return _buffer[num];
		}
	}
}
namespace SevenZip.Compression.LZMA
{
	internal abstract class Base
	{
		public struct State
		{
			public uint Index;

			public void Init()
			{
				Index = 0u;
			}

			public void UpdateChar()
			{
				if (Index < 4)
				{
					Index = 0u;
				}
				else if (Index < 10)
				{
					Index -= 3u;
				}
				else
				{
					Index -= 6u;
				}
			}

			public void UpdateMatch()
			{
				Index = ((Index < 7) ? 7u : 10u);
			}

			public void UpdateRep()
			{
				Index = ((Index < 7) ? 8u : 11u);
			}

			public void UpdateShortRep()
			{
				Index = ((Index < 7) ? 9u : 11u);
			}

			public bool IsCharState()
			{
				return Index < 7;
			}
		}

		public const uint kNumRepDistances = 4u;

		public const uint kNumStates = 12u;

		public const int kNumPosSlotBits = 6;

		public const int kDicLogSizeMin = 0;

		public const int kNumLenToPosStatesBits = 2;

		public const uint kNumLenToPosStates = 4u;

		public const uint kMatchMinLen = 2u;

		public const int kNumAlignBits = 4;

		public const uint kAlignTableSize = 16u;

		public const uint kAlignMask = 15u;

		public const uint kStartPosModelIndex = 4u;

		public const uint kEndPosModelIndex = 14u;

		public const uint kNumPosModels = 10u;

		public const uint kNumFullDistances = 128u;

		public const uint kNumLitPosStatesBitsEncodingMax = 4u;

		public const uint kNumLitContextBitsMax = 8u;

		public const int kNumPosStatesBitsMax = 4;

		public const uint kNumPosStatesMax = 16u;

		public const int kNumPosStatesBitsEncodingMax = 4;

		public const uint kNumPosStatesEncodingMax = 16u;

		public const int kNumLowLenBits = 3;

		public const int kNumMidLenBits = 3;

		public const int kNumHighLenBits = 8;

		public const uint kNumLowLenSymbols = 8u;

		public const uint kNumMidLenSymbols = 8u;

		public const uint kNumLenSymbols = 272u;

		public const uint kMatchMaxLen = 273u;

		public static uint GetLenToPosState(uint len)
		{
			len -= 2;
			if (len < 4)
			{
				return len;
			}
			return 3u;
		}
	}
	public class Decoder : ICoder, ISetDecoderProperties
	{
		private class LenDecoder
		{
			private BitDecoder m_Choice;

			private BitDecoder m_Choice2;

			private BitTreeDecoder[] m_LowCoder = new BitTreeDecoder[16];

			private BitTreeDecoder[] m_MidCoder = new BitTreeDecoder[16];

			private BitTreeDecoder m_HighCoder = new BitTreeDecoder(8);

			private uint m_NumPosStates;

			public void Create(uint numPosStates)
			{
				for (uint num = m_NumPosStates; num < numPosStates; num++)
				{
					m_LowCoder[num] = new BitTreeDecoder(3);
					m_MidCoder[num] = new BitTreeDecoder(3);
				}
				m_NumPosStates = numPosStates;
			}

			public void Init()
			{
				m_Choice.Init();
				for (uint num = 0u; num < m_NumPosStates; num++)
				{
					m_LowCoder[num].Init();
					m_MidCoder[num].Init();
				}
				m_Choice2.Init();
				m_HighCoder.Init();
			}

			public uint Decode(SevenZip.Compression.RangeCoder.Decoder rangeDecoder, uint posState)
			{
				if (m_Choice.Decode(rangeDecoder) == 0)
				{
					return m_LowCoder[posState].Decode(rangeDecoder);
				}
				uint num = 8u;
				if (m_Choice2.Decode(rangeDecoder) == 0)
				{
					return num + m_MidCoder[posState].Decode(rangeDecoder);
				}
				num += 8;
				return num + m_HighCoder.Decode(rangeDecoder);
			}
		}

		private class LiteralDecoder
		{
			private struct Decoder2
			{
				private BitDecoder[] m_Decoders;

				public void Create()
				{
					m_Decoders = new BitDecoder[768];
				}

				public void Init()
				{
					for (int i = 0; i < 768; i++)
					{
						m_Decoders[i].Init();
					}
				}

				public byte DecodeNormal(SevenZip.Compression.RangeCoder.Decoder rangeDecoder)
				{
					uint num = 1u;
					do
					{
						num = (num << 1) | m_Decoders[num].Decode(rangeDecoder);
					}
					while (num < 256);
					return (byte)num;
				}

				public byte DecodeWithMatchByte(SevenZip.Compression.RangeCoder.Decoder rangeDecoder, byte matchByte)
				{
					uint num = 1u;
					do
					{
						uint num2 = (uint)(matchByte >> 7) & 1u;
						matchByte <<= 1;
						uint num3 = m_Decoders[(1 + num2 << 8) + num].Decode(rangeDecoder);
						num = (num << 1) | num3;
						if (num2 != num3)
						{
							while (num < 256)
							{
								num = (num << 1) | m_Decoders[num].Decode(rangeDecoder);
							}
							break;
						}
					}
					while (num < 256);
					return (byte)num;
				}
			}

			private Decoder2[] m_Coders;

			private int m_NumPrevBits;

			private int m_NumPosBits;

			private uint m_PosMask;

			public void Create(int numPosBits, int numPrevBits)
			{
				if (m_Coders == null || m_NumPrevBits != numPrevBits || m_NumPosBits != numPosBits)
				{
					m_NumPosBits = numPosBits;
					m_PosMask = (uint)((1 << numPosBits) - 1);
					m_NumPrevBits = numPrevBits;
					uint num = (uint)(1 << m_NumPrevBits + m_NumPosBits);
					m_Coders = new Decoder2[num];
					for (uint num2 = 0u; num2 < num; num2++)
					{
						m_Coders[num2].Create();
					}
				}
			}

			public void Init()
			{
				uint num = (uint)(1 << m_NumPrevBits + m_NumPosBits);
				for (uint num2 = 0u; num2 < num; num2++)
				{
					m_Coders[num2].Init();
				}
			}

			private uint GetState(uint pos, byte prevByte)
			{
				return ((pos & m_PosMask) << m_NumPrevBits) + (uint)(prevByte >> 8 - m_NumPrevBits);
			}

			public byte DecodeNormal(SevenZip.Compression.RangeCoder.Decoder rangeDecoder, uint pos, byte prevByte)
			{
				return m_Coders[GetState(pos, prevByte)].DecodeNormal(rangeDecoder);
			}

			public byte DecodeWithMatchByte(SevenZip.Compression.RangeCoder.Decoder rangeDecoder, uint pos, byte prevByte, byte matchByte)
			{
				return m_Coders[GetState(pos, prevByte)].DecodeWithMatchByte(rangeDecoder, matchByte);
			}
		}

		private OutWindow m_OutWindow = new OutWindow();

		private SevenZip.Compression.RangeCoder.Decoder m_RangeDecoder = new SevenZip.Compression.RangeCoder.Decoder();

		private BitDecoder[] m_IsMatchDecoders = new BitDecoder[192];

		private BitDecoder[] m_IsRepDecoders = new BitDecoder[12];

		private BitDecoder[] m_IsRepG0Decoders = new BitDecoder[12];

		private BitDecoder[] m_IsRepG1Decoders = new BitDecoder[12];

		private BitDecoder[] m_IsRepG2Decoders = new BitDecoder[12];

		private BitDecoder[] m_IsRep0LongDecoders = new BitDecoder[192];

		private BitTreeDecoder[] m_PosSlotDecoder = new BitTreeDecoder[4];

		private BitDecoder[] m_PosDecoders = new BitDecoder[114];

		private BitTreeDecoder m_PosAlignDecoder = new BitTreeDecoder(4);

		private LenDecoder m_LenDecoder = new LenDecoder();

		private LenDecoder m_RepLenDecoder = new LenDecoder();

		private LiteralDecoder m_LiteralDecoder = new LiteralDecoder();

		private uint m_DictionarySize;

		private uint m_DictionarySizeCheck;

		private uint m_PosStateMask;

		private bool _solid;

		public Decoder()
		{
			m_DictionarySize = uint.MaxValue;
			for (int i = 0; (long)i < 4L; i++)
			{
				m_PosSlotDecoder[i] = new BitTreeDecoder(6);
			}
		}

		private void SetDictionarySize(uint dictionarySize)
		{
			if (m_DictionarySize != dictionarySize)
			{
				m_DictionarySize = dictionarySize;
				m_DictionarySizeCheck = Math.Max(m_DictionarySize, 1u);
				uint windowSize = Math.Max(m_DictionarySizeCheck, 4096u);
				m_OutWindow.Create(windowSize);
			}
		}

		private void SetLiteralProperties(int lp, int lc)
		{
			if (lp > 8)
			{
				throw new InvalidParamException();
			}
			if (lc > 8)
			{
				throw new InvalidParamException();
			}
			m_LiteralDecoder.Create(lp, lc);
		}

		private void SetPosBitsProperties(int pb)
		{
			if (pb > 4)
			{
				throw new InvalidParamException();
			}
			uint num = (uint)(1 << pb);
			m_LenDecoder.Create(num);
			m_RepLenDecoder.Create(num);
			m_PosStateMask = num - 1;
		}

		private void Init(Stream inStream, Stream outStream)
		{
			m_RangeDecoder.Init(inStream);
			m_OutWindow.Init(outStream, _solid);
			for (uint num = 0u; num < 12; num++)
			{
				for (uint num2 = 0u; num2 <= m_PosStateMask; num2++)
				{
					uint num3 = (num << 4) + num2;
					m_IsMatchDecoders[num3].Init();
					m_IsRep0LongDecoders[num3].Init();
				}
				m_IsRepDecoders[num].Init();
				m_IsRepG0Decoders[num].Init();
				m_IsRepG1Decoders[num].Init();
				m_IsRepG2Decoders[num].Init();
			}
			m_LiteralDecoder.Init();
			for (uint num = 0u; num < 4; num++)
			{
				m_PosSlotDecoder[num].Init();
			}
			for (uint num = 0u; num < 114; num++)
			{
				m_PosDecoders[num].Init();
			}
			m_LenDecoder.Init();
			m_RepLenDecoder.Init();
			m_PosAlignDecoder.Init();
		}

		public void Code(Stream inStream, Stream outStream, long inSize, long outSize, ICodeProgress progress)
		{
			Init(inStream, outStream);
			Base.State state = default(Base.State);
			state.Init();
			uint num = 0u;
			uint num2 = 0u;
			uint num3 = 0u;
			uint num4 = 0u;
			ulong num5 = 0uL;
			if (num5 < (ulong)outSize)
			{
				if (m_IsMatchDecoders[state.Index << 4].Decode(m_RangeDecoder) != 0)
				{
					throw new DataErrorException();
				}
				state.UpdateChar();
				byte b = m_LiteralDecoder.DecodeNormal(m_RangeDecoder, 0u, 0);
				m_OutWindow.PutByte(b);
				num5++;
			}
			while (num5 < (ulong)outSize)
			{
				uint num6 = (uint)(int)num5 & m_PosStateMask;
				if (m_IsMatchDecoders[(state.Index << 4) + num6].Decode(m_RangeDecoder) == 0)
				{
					byte @byte = m_OutWindow.GetByte(0u);
					byte b2 = (state.IsCharState() ? m_LiteralDecoder.DecodeNormal(m_RangeDecoder, (uint)num5, @byte) : m_LiteralDecoder.DecodeWithMatchByte(m_RangeDecoder, (uint)num5, @byte, m_OutWindow.GetByte(num)));
					m_OutWindow.PutByte(b2);
					state.UpdateChar();
					num5++;
					continue;
				}
				uint num8;
				if (m_IsRepDecoders[state.Index].Decode(m_RangeDecoder) == 1)
				{
					if (m_IsRepG0Decoders[state.Index].Decode(m_RangeDecoder) == 0)
					{
						if (m_IsRep0LongDecoders[(state.Index << 4) + num6].Decode(m_RangeDecoder) == 0)
						{
							state.UpdateShortRep();
							m_OutWindow.PutByte(m_OutWindow.GetByte(num));
							num5++;
							continue;
						}
					}
					else
					{
						uint num7;
						if (m_IsRepG1Decoders[state.Index].Decode(m_RangeDecoder) == 0)
						{
							num7 = num2;
						}
						else
						{
							if (m_IsRepG2Decoders[state.Index].Decode(m_RangeDecoder) == 0)
							{
								num7 = num3;
							}
							else
							{
								num7 = num4;
								num4 = num3;
							}
							num3 = num2;
						}
						num2 = num;
						num = num7;
					}
					num8 = m_RepLenDecoder.Decode(m_RangeDecoder, num6) + 2;
					state.UpdateRep();
				}
				else
				{
					num4 = num3;
					num3 = num2;
					num2 = num;
					num8 = 2 + m_LenDecoder.Decode(m_RangeDecoder, num6);
					state.UpdateMatch();
					uint num9 = m_PosSlotDecoder[Base.GetLenToPosState(num8)].Decode(m_RangeDecoder);
					if (num9 >= 4)
					{
						int num10 = (int)((num9 >> 1) - 1);
						num = (2 | (num9 & 1)) << num10;
						if (num9 < 14)
						{
							num += BitTreeDecoder.ReverseDecode(m_PosDecoders, num - num9 - 1, m_RangeDecoder, num10);
						}
						else
						{
							num += m_RangeDecoder.DecodeDirectBits(num10 - 4) << 4;
							num += m_PosAlignDecoder.ReverseDecode(m_RangeDecoder);
						}
					}
					else
					{
						num = num9;
					}
				}
				if (num >= m_OutWindow.TrainSize + num5 || num >= m_DictionarySizeCheck)
				{
					if (num == uint.MaxValue)
					{
						break;
					}
					throw new DataErrorException();
				}
				m_OutWindow.CopyBlock(num, num8);
				num5 += num8;
			}
			m_OutWindow.Flush();
			m_OutWindow.ReleaseStream();
			m_RangeDecoder.ReleaseStream();
		}

		public void SetDecoderProperties(byte[] properties)
		{
			if (properties.Length < 5)
			{
				throw new InvalidParamException();
			}
			int lc = properties[0] % 9;
			int num = properties[0] / 9;
			int lp = num % 5;
			int num2 = num / 5;
			if (num2 > 4)
			{
				throw new InvalidParamException();
			}
			uint num3 = 0u;
			for (int i = 0; i < 4; i++)
			{
				num3 += (uint)(properties[1 + i] << i * 8);
			}
			SetDictionarySize(num3);
			SetLiteralProperties(lp, lc);
			SetPosBitsProperties(num2);
		}

		public bool Train(Stream stream)
		{
			_solid = true;
			return m_OutWindow.Train(stream);
		}
	}
	public class Encoder : ICoder, ISetCoderProperties, IWriteCoderProperties
	{
		private enum EMatchFinderType
		{
			BT2,
			BT4
		}

		private class LiteralEncoder
		{
			public struct Encoder2
			{
				private BitEncoder[] m_Encoders;

				public void Create()
				{
					m_Encoders = new BitEncoder[768];
				}

				public void Init()
				{
					for (int i = 0; i < 768; i++)
					{
						m_Encoders[i].Init();
					}
				}

				public void Encode(SevenZip.Compression.RangeCoder.Encoder rangeEncoder, byte symbol)
				{
					uint num = 1u;
					for (int num2 = 7; num2 >= 0; num2--)
					{
						uint num3 = (uint)(symbol >> num2) & 1u;
						m_Encoders[num].Encode(rangeEncoder, num3);
						num = (num << 1) | num3;
					}
				}

				public void EncodeMatched(SevenZip.Compression.RangeCoder.Encoder rangeEncoder, byte matchByte, byte symbol)
				{
					uint num = 1u;
					bool flag = true;
					for (int num2 = 7; num2 >= 0; num2--)
					{
						uint num3 = (uint)(symbol >> num2) & 1u;
						uint num4 = num;
						if (flag)
						{
							uint num5 = (uint)(matchByte >> num2) & 1u;
							num4 += 1 + num5 << 8;
							flag = num5 == num3;
						}
						m_Encoders[num4].Encode(rangeEncoder, num3);
						num = (num << 1) | num3;
					}
				}

				public uint GetPrice(bool matchMode, byte matchByte, byte symbol)
				{
					uint num = 0u;
					uint num2 = 1u;
					int num3 = 7;
					if (matchMode)
					{
						while (num3 >= 0)
						{
							uint num4 = (uint)(matchByte >> num3) & 1u;
							uint num5 = (uint)(symbol >> num3) & 1u;
							num += m_Encoders[(1 + num4 << 8) + num2].GetPrice(num5);
							num2 = (num2 << 1) | num5;
							if (num4 != num5)
							{
								num3--;
								break;
							}
							num3--;
						}
					}
					while (num3 >= 0)
					{
						uint num6 = (uint)(symbol >> num3) & 1u;
						num += m_Encoders[num2].GetPrice(num6);
						num2 = (num2 << 1) | num6;
						num3--;
					}
					return num;
				}
			}

			private Encoder2[] m_Coders;

			private int m_NumPrevBits;

			private int m_NumPosBits;

			private uint m_PosMask;

			public void Create(int numPosBits, int numPrevBits)
			{
				if (m_Coders == null || m_NumPrevBits != numPrevBits || m_NumPosBits != numPosBits)
				{
					m_NumPosBits = numPosBits;
					m_PosMask = (uint)((1 << numPosBits) - 1);
					m_NumPrevBits = numPrevBits;
					uint num = (uint)(1 << m_NumPrevBits + m_NumPosBits);
					m_Coders = new Encoder2[num];
					for (uint num2 = 0u; num2 < num; num2++)
					{
						m_Coders[num2].Create();
					}
				}
			}

			public void Init()
			{
				uint num = (uint)(1 << m_NumPrevBits + m_NumPosBits);
				for (uint num2 = 0u; num2 < num; num2++)
				{
					m_Coders[num2].Init();
				}
			}

			public Encoder2 GetSubCoder(uint pos, byte prevByte)
			{
				return m_Coders[(int)((pos & m_PosMask) << m_NumPrevBits) + (prevByte >> 8 - m_NumPrevBits)];
			}
		}

		private class LenEncoder
		{
			private BitEncoder _choice;

			private BitEncoder _choice2;

			private BitTreeEncoder[] _lowCoder = new BitTreeEncoder[16];

			private BitTreeEncoder[] _midCoder = new BitTreeEncoder[16];

			private BitTreeEncoder _highCoder = new BitTreeEncoder(8);

			public LenEncoder()
			{
				for (uint num = 0u; num < 16; num++)
				{
					_lowCoder[num] = new BitTreeEncoder(3);
					_midCoder[num] = new BitTreeEncoder(3);
				}
			}

			public void Init(uint numPosStates)
			{
				_choice.Init();
				_choice2.Init();
				for (uint num = 0u; num < numPosStates; num++)
				{
					_lowCoder[num].Init();
					_midCoder[num].Init();
				}
				_highCoder.Init();
			}

			public void Encode(SevenZip.Compression.RangeCoder.Encoder rangeEncoder, uint symbol, uint posState)
			{
				if (symbol < 8)
				{
					_choice.Encode(rangeEncoder, 0u);
					_lowCoder[posState].Encode(rangeEncoder, symbol);
					return;
				}
				symbol -= 8;
				_choice.Encode(rangeEncoder, 1u);
				if (symbol < 8)
				{
					_choice2.Encode(rangeEncoder, 0u);
					_midCoder[posState].Encode(rangeEncoder, symbol);
				}
				else
				{
					_choice2.Encode(rangeEncoder, 1u);
					_highCoder.Encode(rangeEncoder, symbol - 8);
				}
			}

			public void SetPrices(uint posState, uint numSymbols, uint[] prices, uint st)
			{
				uint price = _choice.GetPrice0();
				uint price2 = _choice.GetPrice1();
				uint num = price2 + _choice2.GetPrice0();
				uint num2 = price2 + _choice2.GetPrice1();
				uint num3 = 0u;
				for (num3 = 0u; num3 < 8; num3++)
				{
					if (num3 >= numSymbols)
					{
						return;
					}
					prices[st + num3] = price + _lowCoder[posState].GetPrice(num3);
				}
				for (; num3 < 16; num3++)
				{
					if (num3 >= numSymbols)
					{
						return;
					}
					prices[st + num3] = num + _midCoder[posState].GetPrice(num3 - 8);
				}
				for (; num3 < numSymbols; num3++)
				{
					prices[st + num3] = num2 + _highCoder.GetPrice(num3 - 8 - 8);
				}
			}
		}

		private class LenPriceTableEncoder : LenEncoder
		{
			private uint[] _prices = new uint[4352];

			private uint _tableSize;

			private uint[] _counters = new uint[16];

			public void SetTableSize(uint tableSize)
			{
				_tableSize = tableSize;
			}

			public uint GetPrice(uint symbol, uint posState)
			{
				return _prices[posState * 272 + symbol];
			}

			private void UpdateTable(uint posState)
			{
				SetPrices(posState, _tableSize, _prices, posState * 272);
				_counters[posState] = _tableSize;
			}

			public void UpdateTables(uint numPosStates)
			{
				for (uint num = 0u; num < numPosStates; num++)
				{
					UpdateTable(num);
				}
			}

			public new void Encode(SevenZip.Compression.RangeCoder.Encoder rangeEncoder, uint symbol, uint posState)
			{
				base.Encode(rangeEncoder, symbol, posState);
				if (--_counters[posState] == 0)
				{
					UpdateTable(posState);
				}
			}
		}

		private class Optimal
		{
			public Base.State State;

			public bool Prev1IsChar;

			public bool Prev2;

			public uint PosPrev2;

			public uint BackPrev2;

			public uint Price;

			public uint PosPrev;

			public uint BackPrev;

			public uint Backs0;

			public uint Backs1;

			public uint Backs2;

			public uint Backs3;

			public void MakeAsChar()
			{
				BackPrev = uint.MaxValue;
				Prev1IsChar = false;
			}

			public void MakeAsShortRep()
			{
				BackPrev = 0u;
				Prev1IsChar = false;
			}

			public bool IsShortRep()
			{
				return BackPrev == 0;
			}
		}

		private const uint kIfinityPrice = 268435455u;

		private static byte[] g_FastPos;

		private Base.State _state;

		private byte _previousByte;

		private uint[] _repDistances = new uint[4];

		private const int kDefaultDictionaryLogSize = 22;

		private const uint kNumFastBytesDefault = 32u;

		private const uint kNumLenSpecSymbols = 16u;

		private const uint kNumOpts = 4096u;

		private Optimal[] _optimum = new Optimal[4096];

		private IMatchFinder _matchFinder;

		private SevenZip.Compression.RangeCoder.Encoder _rangeEncoder = new SevenZip.Compression.RangeCoder.Encoder();

		private BitEncoder[] _isMatch = new BitEncoder[192];

		private BitEncoder[] _isRep = new BitEncoder[12];

		private BitEncoder[] _isRepG0 = new BitEncoder[12];

		private BitEncoder[] _isRepG1 = new BitEncoder[12];

		private BitEncoder[] _isRepG2 = new BitEncoder[12];

		private BitEncoder[] _isRep0Long = new BitEncoder[192];

		private BitTreeEncoder[] _posSlotEncoder = new BitTreeEncoder[4];

		private BitEncoder[] _posEncoders = new BitEncoder[114];

		private BitTreeEncoder _posAlignEncoder = new BitTreeEncoder(4);

		private LenPriceTableEncoder _lenEncoder = new LenPriceTableEncoder();

		private LenPriceTableEncoder _repMatchLenEncoder = new LenPriceTableEncoder();

		private LiteralEncoder _literalEncoder = new LiteralEncoder();

		private uint[] _matchDistances = new uint[548];

		private uint _numFastBytes = 32u;

		private uint _longestMatchLength;

		private uint _numDistancePairs;

		private uint _additionalOffset;

		private uint _optimumEndIndex;

		private uint _optimumCurrentIndex;

		private bool _longestMatchWasFound;

		private uint[] _posSlotPrices = new uint[256];

		private uint[] _distancesPrices = new uint[512];

		private uint[] _alignPrices = new uint[16];

		private uint _alignPriceCount;

		private uint _distTableSize = 44u;

		private int _posStateBits = 2;

		private uint _posStateMask = 3u;

		private int _numLiteralPosStateBits;

		private int _numLiteralContextBits = 3;

		private uint _dictionarySize = 4194304u;

		private uint _dictionarySizePrev = uint.MaxValue;

		private uint _numFastBytesPrev = uint.MaxValue;

		private long nowPos64;

		private bool _finished;

		private Stream _inStream;

		private EMatchFinderType _matchFinderType = EMatchFinderType.BT4;

		private bool _writeEndMark;

		private bool _needReleaseMFStream;

		private uint[] reps = new uint[4];

		private uint[] repLens = new uint[4];

		private const int kPropSize = 5;

		private byte[] properties = new byte[5];

		private uint[] tempPrices = new uint[128];

		private uint _matchPriceCount;

		private static string[] kMatchFinderIDs;

		private uint _trainSize;

		static Encoder()
		{
			g_FastPos = new byte[2048];
			kMatchFinderIDs = new string[2] { "BT2", "BT4" };
			int num = 2;
			g_FastPos[0] = 0;
			g_FastPos[1] = 1;
			for (byte b = 2; b < 22; b++)
			{
				uint num2 = (uint)(1 << (b >> 1) - 1);
				uint num3 = 0u;
				while (num3 < num2)
				{
					g_FastPos[num] = b;
					num3++;
					num++;
				}
			}
		}

		private static uint GetPosSlot(uint pos)
		{
			if (pos < 2048)
			{
				return g_FastPos[pos];
			}
			if (pos < 2097152)
			{
				return (uint)(g_FastPos[pos >> 10] + 20);
			}
			return (uint)(g_FastPos[pos >> 20] + 40);
		}

		private static uint GetPosSlot2(uint pos)
		{
			if (pos < 131072)
			{
				return (uint)(g_FastPos[pos >> 6] + 12);
			}
			if (pos < 134217728)
			{
				return (uint)(g_FastPos[pos >> 16] + 32);
			}
			return (uint)(g_FastPos[pos >> 26] + 52);
		}

		private void BaseInit()
		{
			_state.Init();
			_previousByte = 0;
			for (uint num = 0u; num < 4; num++)
			{
				_repDistances[num] = 0u;
			}
		}

		private void Create()
		{
			if (_matchFinder == null)
			{
				BinTree binTree = new BinTree();
				int type = 4;
				if (_matchFinderType == EMatchFinderType.BT2)
				{
					type = 2;
				}
				binTree.SetType(type);
				_matchFinder = binTree;
			}
			_literalEncoder.Create(_numLiteralPosStateBits, _numLiteralContextBits);
			if (_dictionarySize != _dictionarySizePrev || _numFastBytesPrev != _numFastBytes)
			{
				_matchFinder.Create(_dictionarySize, 4096u, _numFastBytes, 274u);
				_dictionarySizePrev = _dictionarySize;
				_numFastBytesPrev = _numFastBytes;
			}
		}

		public Encoder()
		{
			for (int i = 0; (long)i < 4096L; i++)
			{
				_optimum[i] = new Optimal();
			}
			for (int j = 0; (long)j < 4L; j++)
			{
				_posSlotEncoder[j] = new BitTreeEncoder(6);
			}
		}

		private void SetWriteEndMarkerMode(bool writeEndMarker)
		{
			_writeEndMark = writeEndMarker;
		}

		private void Init()
		{
			BaseInit();
			_rangeEncoder.Init();
			for (uint num = 0u; num < 12; num++)
			{
				for (uint num2 = 0u; num2 <= _posStateMask; num2++)
				{
					uint num3 = (num << 4) + num2;
					_isMatch[num3].Init();
					_isRep0Long[num3].Init();
				}
				_isRep[num].Init();
				_isRepG0[num].Init();
				_isRepG1[num].Init();
				_isRepG2[num].Init();
			}
			_literalEncoder.Init();
			for (uint num = 0u; num < 4; num++)
			{
				_posSlotEncoder[num].Init();
			}
			for (uint num = 0u; num < 114; num++)
			{
				_posEncoders[num].Init();
			}
			_lenEncoder.Init((uint)(1 << _posStateBits));
			_repMatchLenEncoder.Init((uint)(1 << _posStateBits));
			_posAlignEncoder.Init();
			_longestMatchWasFound = false;
			_optimumEndIndex = 0u;
			_optimumCurrentIndex = 0u;
			_additionalOffset = 0u;
		}

		private void ReadMatchDistances(out uint lenRes, out uint numDistancePairs)
		{
			lenRes = 0u;
			numDistancePairs = _matchFinder.GetMatches(_matchDistances);
			if (numDistancePairs != 0)
			{
				lenRes = _matchDistances[numDistancePairs - 2];
				if (lenRes == _numFastBytes)
				{
					lenRes += _matchFinder.GetMatchLen((int)(lenRes - 1), _matchDistances[numDistancePairs - 1], 273 - lenRes);
				}
			}
			_additionalOffset++;
		}

		private void MovePos(uint num)
		{
			if (num != 0)
			{
				_matchFinder.Skip(num);
				_additionalOffset += num;
			}
		}

		private uint GetRepLen1Price(Base.State state, uint posState)
		{
			return _isRepG0[state.Index].GetPrice0() + _isRep0Long[(state.Index << 4) + posState].GetPrice0();
		}

		private uint GetPureRepPrice(uint repIndex, Base.State state, uint posState)
		{
			uint price;
			if (repIndex == 0)
			{
				price = _isRepG0[state.Index].GetPrice0();
				return price + _isRep0Long[(state.Index << 4) + posState].GetPrice1();
			}
			price = _isRepG0[state.Index].GetPrice1();
			if (repIndex == 1)
			{
				return price + _isRepG1[state.Index].GetPrice0();
			}
			price += _isRepG1[state.Index].GetPrice1();
			return price + _isRepG2[state.Index].GetPrice(repIndex - 2);
		}

		private uint GetRepPrice(uint repIndex, uint len, Base.State state, uint posState)
		{
			return _repMatchLenEncoder.GetPrice(len - 2, posState) + GetPureRepPrice(repIndex, state, posState);
		}

		private uint GetPosLenPrice(uint pos, uint len, uint posState)
		{
			uint lenToPosState = Base.GetLenToPosState(len);
			uint num = ((pos >= 128) ? (_posSlotPrices[(lenToPosState << 6) + GetPosSlot2(pos)] + _alignPrices[pos & 0xF]) : _distancesPrices[lenToPosState * 128 + pos]);
			return num + _lenEncoder.GetPrice(len - 2, posState);
		}

		private uint Backward(out uint backRes, uint cur)
		{
			_optimumEndIndex = cur;
			uint posPrev = _optimum[cur].PosPrev;
			uint backPrev = _optimum[cur].BackPrev;
			do
			{
				if (_optimum[cur].Prev1IsChar)
				{
					_optimum[posPrev].MakeAsChar();
					_optimum[posPrev].PosPrev = posPrev - 1;
					if (_optimum[cur].Prev2)
					{
						_optimum[posPrev - 1].Prev1IsChar = false;
						_optimum[posPrev - 1].PosPrev = _optimum[cur].PosPrev2;
						_optimum[posPrev - 1].BackPrev = _optimum[cur].BackPrev2;
					}
				}
				uint num = posPrev;
				uint backPrev2 = backPrev;
				backPrev = _optimum[num].BackPrev;
				posPrev = _optimum[num].PosPrev;
				_optimum[num].BackPrev = backPrev2;
				_optimum[num].PosPrev = cur;
				cur = num;
			}
			while (cur != 0);
			backRes = _optimum[0].BackPrev;
			_optimumCurrentIndex = _optimum[0].PosPrev;
			return _optimumCurrentIndex;
		}

		private uint GetOptimum(uint position, out uint backRes)
		{
			if (_optimumEndIndex != _optimumCurrentIndex)
			{
				uint result = _optimum[_optimumCurrentIndex].PosPrev - _optimumCurrentIndex;
				backRes = _optimum[_optimumCurrentIndex].BackPrev;
				_optimumCurrentIndex = _optimum[_optimumCurrentIndex].PosPrev;
				return result;
			}
			_optimumCurrentIndex = (_optimumEndIndex = 0u);
			uint lenRes;
			uint numDistancePairs;
			if (!_longestMatchWasFound)
			{
				ReadMatchDistances(out lenRes, out numDistancePairs);
			}
			else
			{
				lenRes = _longestMatchLength;
				numDistancePairs = _numDistancePairs;
				_longestMatchWasFound = false;
			}
			uint num = _matchFinder.GetNumAvailableBytes() + 1;
			if (num < 2)
			{
				backRes = uint.MaxValue;
				return 1u;
			}
			if (num > 273)
			{
				num = 273u;
			}
			uint num2 = 0u;
			for (uint num3 = 0u; num3 < 4; num3++)
			{
				reps[num3] = _repDistances[num3];
				repLens[num3] = _matchFinder.GetMatchLen(-1, reps[num3], 273u);
				if (repLens[num3] > repLens[num2])
				{
					num2 = num3;
				}
			}
			if (repLens[num2] >= _numFastBytes)
			{
				backRes = num2;
				uint num4 = repLens[num2];
				MovePos(num4 - 1);
				return num4;
			}
			if (lenRes >= _numFastBytes)
			{
				backRes = _matchDistances[numDistancePairs - 1] + 4;
				MovePos(lenRes - 1);
				return lenRes;
			}
			byte indexByte = _matchFinder.GetIndexByte(-1);
			byte indexByte2 = _matchFinder.GetIndexByte((int)(0 - _repDistances[0] - 1 - 1));
			if (lenRes < 2 && indexByte != indexByte2 && repLens[num2] < 2)
			{
				backRes = uint.MaxValue;
				return 1u;
			}
			_optimum[0].State = _state;
			uint num5 = position & _posStateMask;
			_optimum[1].Price = _isMatch[(_state.Index << 4) + num5].GetPrice0() + _literalEncoder.GetSubCoder(position, _previousByte).GetPrice(!_state.IsCharState(), indexByte2, indexByte);
			_optimum[1].MakeAsChar();
			uint price = _isMatch[(_state.Index << 4) + num5].GetPrice1();
			uint num6 = price + _isRep[_state.Index].GetPrice1();
			if (indexByte2 == indexByte)
			{
				uint num7 = num6 + GetRepLen1Price(_state, num5);
				if (num7 < _optimum[1].Price)
				{
					_optimum[1].Price = num7;
					_optimum[1].MakeAsShortRep();
				}
			}
			uint num8 = ((lenRes >= repLens[num2]) ? lenRes : repLens[num2]);
			if (num8 < 2)
			{
				backRes = _optimum[1].BackPrev;
				return 1u;
			}
			_optimum[1].PosPrev = 0u;
			_optimum[0].Backs0 = reps[0];
			_optimum[0].Backs1 = reps[1];
			_optimum[0].Backs2 = reps[2];
			_optimum[0].Backs3 = reps[3];
			uint num9 = num8;
			do
			{
				_optimum[num9--].Price = 268435455u;
			}
			while (num9 >= 2);
			for (uint num3 = 0u; num3 < 4; num3++)
			{
				uint num10 = repLens[num3];
				if (num10 < 2)
				{
					continue;
				}
				uint num11 = num6 + GetPureRepPrice(num3, _state, num5);
				do
				{
					uint num12 = num11 + _repMatchLenEncoder.GetPrice(num10 - 2, num5);
					Optimal optimal = _optimum[num10];
					if (num12 < optimal.Price)
					{
						optimal.Price = num12;
						optimal.PosPrev = 0u;
						optimal.BackPrev = num3;
						optimal.Prev1IsChar = false;
					}
				}
				while (--num10 >= 2);
			}
			uint num13 = price + _isRep[_state.Index].GetPrice0();
			num9 = ((repLens[0] >= 2) ? (repLens[0] + 1) : 2u);
			if (num9 <= lenRes)
			{
				uint num14;
				for (num14 = 0u; num9 > _matchDistances[num14]; num14 += 2)
				{
				}
				while (true)
				{
					uint num15 = _matchDistances[num14 + 1];
					uint num16 = num13 + GetPosLenPrice(num15, num9, num5);
					Optimal optimal2 = _optimum[num9];
					if (num16 < optimal2.Price)
					{
						optimal2.Price = num16;
						optimal2.PosPrev = 0u;
						optimal2.BackPrev = num15 + 4;
						optimal2.Prev1IsChar = false;
					}
					if (num9 == _matchDistances[num14])
					{
						num14 += 2;
						if (num14 == numDistancePairs)
						{
							break;
						}
					}
					num9++;
				}
			}
			uint num17 = 0u;
			uint lenRes2;
			while (true)
			{
				num17++;
				if (num17 == num8)
				{
					return Backward(out backRes, num17);
				}
				ReadMatchDistances(out lenRes2, out numDistancePairs);
				if (lenRes2 >= _numFastBytes)
				{
					break;
				}
				position++;
				uint num18 = _optimum[num17].PosPrev;
				Base.State state;
				if (_optimum[num17].Prev1IsChar)
				{
					num18--;
					if (_optimum[num17].Prev2)
					{
						state = _optimum[_optimum[num17].PosPrev2].State;
						if (_optimum[num17].BackPrev2 < 4)
						{
							state.UpdateRep();
						}
						else
						{
							state.UpdateMatch();
						}
					}
					else
					{
						state = _optimum[num18].State;
					}
					state.UpdateChar();
				}
				else
				{
					state = _optimum[num18].State;
				}
				if (num18 == num17 - 1)
				{
					if (_optimum[num17].IsShortRep())
					{
						state.UpdateShortRep();
					}
					else
					{
						state.UpdateChar();
					}
				}
				else
				{
					uint num19;
					if (_optimum[num17].Prev1IsChar && _optimum[num17].Prev2)
					{
						num18 = _optimum[num17].PosPrev2;
						num19 = _optimum[num17].BackPrev2;
						state.UpdateRep();
					}
					else
					{
						num19 = _optimum[num17].BackPrev;
						if (num19 < 4)
						{
							state.UpdateRep();
						}
						else
						{
							state.UpdateMatch();
						}
					}
					Optimal optimal3 = _optimum[num18];
					switch (num19)
					{
					case 0u:
						reps[0] = optimal3.Backs0;
						reps[1] = optimal3.Backs1;
						reps[2] = optimal3.Backs2;
						reps[3] = optimal3.Backs3;
						break;
					case 1u:
						reps[0] = optimal3.Backs1;
						reps[1] = optimal3.Backs0;
						reps[2] = optimal3.Backs2;
						reps[3] = optimal3.Backs3;
						break;
					case 2u:
						reps[0] = optimal3.Backs2;
						reps[1] = optimal3.Backs0;
						reps[2] = optimal3.Backs1;
						reps[3] = optimal3.Backs3;
						break;
					case 3u:
						reps[0] = optimal3.Backs3;
						reps[1] = optimal3.Backs0;
						reps[2] = optimal3.Backs1;
						reps[3] = optimal3.Backs2;
						break;
					default:
						reps[0] = num19 - 4;
						reps[1] = optimal3.Backs0;
						reps[2] = optimal3.Backs1;
						reps[3] = optimal3.Backs2;
						break;
					}
				}
				_optimum[num17].State = state;
				_optimum[num17].Backs0 = reps[0];
				_optimum[num17].Backs1 = reps[1];
				_optimum[num17].Backs2 = reps[2];
				_optimum[num17].Backs3 = reps[3];
				uint price2 = _optimum[num17].Price;
				indexByte = _matchFinder.GetIndexByte(-1);
				indexByte2 = _matchFinder.GetIndexByte((int)(0 - reps[0] - 1 - 1));
				num5 = position & _posStateMask;
				uint num20 = price2 + _isMatch[(state.Index << 4) + num5].GetPrice0() + _literalEncoder.GetSubCoder(position, _matchFinder.GetIndexByte(-2)).GetPrice(!state.IsCharState(), indexByte2, indexByte);
				Optimal optimal4 = _optimum[num17 + 1];
				bool flag = false;
				if (num20 < optimal4.Price)
				{
					optimal4.Price = num20;
					optimal4.PosPrev = num17;
					optimal4.MakeAsChar();
					flag = true;
				}
				price = price2 + _isMatch[(state.Index << 4) + num5].GetPrice1();
				num6 = price + _isRep[state.Index].GetPrice1();
				if (indexByte2 == indexByte && (optimal4.PosPrev >= num17 || optimal4.BackPrev != 0))
				{
					uint num21 = num6 + GetRepLen1Price(state, num5);
					if (num21 <= optimal4.Price)
					{
						optimal4.Price = num21;
						optimal4.PosPrev = num17;
						optimal4.MakeAsShortRep();
						flag = true;
					}
				}
				uint val = _matchFinder.GetNumAvailableBytes() + 1;
				val = Math.Min(4095 - num17, val);
				num = val;
				if (num < 2)
				{
					continue;
				}
				if (num > _numFastBytes)
				{
					num = _numFastBytes;
				}
				if (!flag && indexByte2 != indexByte)
				{
					uint limit = Math.Min(val - 1, _numFastBytes);
					uint matchLen = _matchFinder.GetMatchLen(0, reps[0], limit);
					if (matchLen >= 2)
					{
						Base.State state2 = state;
						state2.UpdateChar();
						uint num22 = (position + 1) & _posStateMask;
						uint num23 = num20 + _isMatch[(state2.Index << 4) + num22].GetPrice1() + _isRep[state2.Index].GetPrice1();
						uint num24 = num17 + 1 + matchLen;
						while (num8 < num24)
						{
							_optimum[++num8].Price = 268435455u;
						}
						uint num25 = num23 + GetRepPrice(0u, matchLen, state2, num22);
						Optimal optimal5 = _optimum[num24];
						if (num25 < optimal5.Price)
						{
							optimal5.Price = num25;
							optimal5.PosPrev = num17 + 1;
							optimal5.BackPrev = 0u;
							optimal5.Prev1IsChar = true;
							optimal5.Prev2 = false;
						}
					}
				}
				uint num26 = 2u;
				for (uint num27 = 0u; num27 < 4; num27++)
				{
					uint num28 = _matchFinder.GetMatchLen(-1, reps[num27], num);
					if (num28 < 2)
					{
						continue;
					}
					uint num29 = num28;
					while (true)
					{
						if (num8 < num17 + num28)
						{
							_optimum[++num8].Price = 268435455u;
							continue;
						}
						uint num30 = num6 + GetRepPrice(num27, num28, state, num5);
						Optimal optimal6 = _optimum[num17 + num28];
						if (num30 < optimal6.Price)
						{
							optimal6.Price = num30;
							optimal6.PosPrev = num17;
							optimal6.BackPrev = num27;
							optimal6.Prev1IsChar = false;
						}
						if (--num28 < 2)
						{
							break;
						}
					}
					num28 = num29;
					if (num27 == 0)
					{
						num26 = num28 + 1;
					}
					if (num28 >= val)
					{
						continue;
					}
					uint limit2 = Math.Min(val - 1 - num28, _numFastBytes);
					uint matchLen2 = _matchFinder.GetMatchLen((int)num28, reps[num27], limit2);
					if (matchLen2 >= 2)
					{
						Base.State state3 = state;
						state3.UpdateRep();
						uint num31 = (position + num28) & _posStateMask;
						uint num32 = num6 + GetRepPrice(num27, num28, state, num5) + _isMatch[(state3.Index << 4) + num31].GetPrice0() + _literalEncoder.GetSubCoder(position + num28, _matchFinder.GetIndexByte((int)(num28 - 1 - 1))).GetPrice(matchMode: true, _matchFinder.GetIndexByte((int)(num28 - 1 - (reps[num27] + 1))), _matchFinder.GetIndexByte((int)(num28 - 1)));
						state3.UpdateChar();
						num31 = (position + num28 + 1) & _posStateMask;
						uint num33 = num32 + _isMatch[(state3.Index << 4) + num31].GetPrice1() + _isRep[state3.Index].GetPrice1();
						uint num34 = num28 + 1 + matchLen2;
						while (num8 < num17 + num34)
						{
							_optimum[++num8].Price = 268435455u;
						}
						uint num35 = num33 + GetRepPrice(0u, matchLen2, state3, num31);
						Optimal optimal7 = _optimum[num17 + num34];
						if (num35 < optimal7.Price)
						{
							optimal7.Price = num35;
							optimal7.PosPrev = num17 + num28 + 1;
							optimal7.BackPrev = 0u;
							optimal7.Prev1IsChar = true;
							optimal7.Prev2 = true;
							optimal7.PosPrev2 = num17;
							optimal7.BackPrev2 = num27;
						}
					}
				}
				if (lenRes2 > num)
				{
					lenRes2 = num;
					for (numDistancePairs = 0u; lenRes2 > _matchDistances[numDistancePairs]; numDistancePairs += 2)
					{
					}
					_matchDistances[numDistancePairs] = lenRes2;
					numDistancePairs += 2;
				}
				if (lenRes2 < num26)
				{
					continue;
				}
				num13 = price + _isRep[state.Index].GetPrice0();
				while (num8 < num17 + lenRes2)
				{
					_optimum[++num8].Price = 268435455u;
				}
				uint num36;
				for (num36 = 0u; num26 > _matchDistances[num36]; num36 += 2)
				{
				}
				uint num37 = num26;
				while (true)
				{
					uint num38 = _matchDistances[num36 + 1];
					uint num39 = num13 + GetPosLenPrice(num38, num37, num5);
					Optimal optimal8 = _optimum[num17 + num37];
					if (num39 < optimal8.Price)
					{
						optimal8.Price = num39;
						optimal8.PosPrev = num17;
						optimal8.BackPrev = num38 + 4;
						optimal8.Prev1IsChar = false;
					}
					if (num37 == _matchDistances[num36])
					{
						if (num37 < val)
						{
							uint limit3 = Math.Min(val - 1 - num37, _numFastBytes);
							uint matchLen3 = _matchFinder.GetMatchLen((int)num37, num38, limit3);
							if (matchLen3 >= 2)
							{
								Base.State state4 = state;
								state4.UpdateMatch();
								uint num40 = (position + num37) & _posStateMask;
								uint num41 = num39 + _isMatch[(state4.Index << 4) + num40].GetPrice0() + _literalEncoder.GetSubCoder(position + num37, _matchFinder.GetIndexByte((int)(num37 - 1 - 1))).GetPrice(matchMode: true, _matchFinder.GetIndexByte((int)(num37 - (num38 + 1) - 1)), _matchFinder.GetIndexByte((int)(num37 - 1)));
								state4.UpdateChar();
								num40 = (position + num37 + 1) & _posStateMask;
								uint num42 = num41 + _isMatch[(state4.Index << 4) + num40].GetPrice1() + _isRep[state4.Index].GetPrice1();
								uint num43 = num37 + 1 + matchLen3;
								while (num8 < num17 + num43)
								{
									_optimum[++num8].Price = 268435455u;
								}
								num39 = num42 + GetRepPrice(0u, matchLen3, state4, num40);
								optimal8 = _optimum[num17 + num43];
								if (num39 < optimal8.Price)
								{
									optimal8.Price = num39;
									optimal8.PosPrev = num17 + num37 + 1;
									optimal8.BackPrev = 0u;
									optimal8.Prev1IsChar = true;
									optimal8.Prev2 = true;
									optimal8.PosPrev2 = num17;
									optimal8.BackPrev2 = num38 + 4;
								}
							}
						}
						num36 += 2;
						if (num36 == numDistancePairs)
						{
							break;
						}
					}
					num37++;
				}
			}
			_numDistancePairs = numDistancePairs;
			_longestMatchLength = lenRes2;
			_longestMatchWasFound = true;
			return Backward(out backRes, num17);
		}

		private bool ChangePair(uint smallDist, uint bigDist)
		{
			if (smallDist < 33554432)
			{
				return bigDist >= smallDist << 7;
			}
			return false;
		}

		private void WriteEndMarker(uint posState)
		{
			if (_writeEndMark)
			{
				_isMatch[(_state.Index << 4) + posState].Encode(_rangeEncoder, 1u);
				_isRep[_state.Index].Encode(_rangeEncoder, 0u);
				_state.UpdateMatch();
				uint num = 2u;
				_lenEncoder.Encode(_rangeEncoder, num - 2, posState);
				uint symbol = 63u;
				uint lenToPosState = Base.GetLenToPosState(num);
				_posSlotEncoder[lenToPosState].Encode(_rangeEncoder, symbol);
				int num2 = 30;
				uint num3 = (uint)((1 << num2) - 1);
				_rangeEncoder.EncodeDirectBits(num3 >> 4, num2 - 4);
				_posAlignEncoder.ReverseEncode(_rangeEncoder, num3 & 0xFu);
			}
		}

		private void Flush(uint nowPos)
		{
			ReleaseMFStream();
			WriteEndMarker(nowPos & _posStateMask);
			_rangeEncoder.FlushData();
			_rangeEncoder.FlushStream();
		}

		public void CodeOneBlock(out long inSize, out long outSize, out bool finished)
		{
			inSize = 0L;
			outSize = 0L;
			finished = true;
			if (_inStream != null)
			{
				_matchFinder.SetStream(_inStream);
				_matchFinder.Init();
				_needReleaseMFStream = true;
				_inStream = null;
				if (_trainSize != 0)
				{
					_matchFinder.Skip(_trainSize);
				}
			}
			if (_finished)
			{
				return;
			}
			_finished = true;
			long num = nowPos64;
			if (nowPos64 == 0L)
			{
				if (_matchFinder.GetNumAvailableBytes() == 0)
				{
					Flush((uint)nowPos64);
					return;
				}
				ReadMatchDistances(out var _, out var _);
				uint num2 = (uint)(int)nowPos64 & _posStateMask;
				_isMatch[(_state.Index << 4) + num2].Encode(_rangeEncoder, 0u);
				_state.UpdateChar();
				byte indexByte = _matchFinder.GetIndexByte((int)(0 - _additionalOffset));
				_literalEncoder.GetSubCoder((uint)nowPos64, _previousByte).Encode(_rangeEncoder, indexByte);
				_previousByte = indexByte;
				_additionalOffset--;
				nowPos64++;
			}
			if (_matchFinder.GetNumAvailableBytes() == 0)
			{
				Flush((uint)nowPos64);
				return;
			}
			while (true)
			{
				uint backRes;
				uint optimum = GetOptimum((uint)nowPos64, out backRes);
				uint num3 = (uint)(int)nowPos64 & _posStateMask;
				uint num4 = (_state.Index << 4) + num3;
				if (optimum == 1 && backRes == uint.MaxValue)
				{
					_isMatch[num4].Encode(_rangeEncoder, 0u);
					byte indexByte2 = _matchFinder.GetIndexByte((int)(0 - _additionalOffset));
					LiteralEncoder.Encoder2 subCoder = _literalEncoder.GetSubCoder((uint)nowPos64, _previousByte);
					if (!_state.IsCharState())
					{
						byte indexByte3 = _matchFinder.GetIndexByte((int)(0 - _repDistances[0] - 1 - _additionalOffset));
						subCoder.EncodeMatched(_rangeEncoder, indexByte3, indexByte2);
					}
					else
					{
						subCoder.Encode(_rangeEncoder, indexByte2);
					}
					_previousByte = indexByte2;
					_state.UpdateChar();
				}
				else
				{
					_isMatch[num4].Encode(_rangeEncoder, 1u);
					if (backRes < 4)
					{
						_isRep[_state.Index].Encode(_rangeEncoder, 1u);
						if (backRes == 0)
						{
							_isRepG0[_state.Index].Encode(_rangeEncoder, 0u);
							if (optimum == 1)
							{
								_isRep0Long[num4].Encode(_rangeEncoder, 0u);
							}
							else
							{
								_isRep0Long[num4].Encode(_rangeEncoder, 1u);
							}
						}
						else
						{
							_isRepG0[_state.Index].Encode(_rangeEncoder, 1u);
							if (backRes == 1)
							{
								_isRepG1[_state.Index].Encode(_rangeEncoder, 0u);
							}
							else
							{
								_isRepG1[_state.Index].Encode(_rangeEncoder, 1u);
								_isRepG2[_state.Index].Encode(_rangeEncoder, backRes - 2);
							}
						}
						if (optimum == 1)
						{
							_state.UpdateShortRep();
						}
						else
						{
							_repMatchLenEncoder.Encode(_rangeEncoder, optimum - 2, num3);
							_state.UpdateRep();
						}
						uint num5 = _repDistances[backRes];
						if (backRes != 0)
						{
							for (uint num6 = backRes; num6 >= 1; num6--)
							{
								_repDistances[num6] = _repDistances[num6 - 1];
							}
							_repDistances[0] = num5;
						}
					}
					else
					{
						_isRep[_state.Index].Encode(_rangeEncoder, 0u);
						_state.UpdateMatch();
						_lenEncoder.Encode(_rangeEncoder, optimum - 2, num3);
						backRes -= 4;
						uint posSlot = GetPosSlot(backRes);
						uint lenToPosState = Base.GetLenToPosState(optimum);
						_posSlotEncoder[lenToPosState].Encode(_rangeEncoder, posSlot);
						if (posSlot >= 4)
						{
							int num7 = (int)((posSlot >> 1) - 1);
							uint num8 = (2 | (posSlot & 1)) << num7;
							uint num9 = backRes - num8;
							if (posSlot < 14)
							{
								BitTreeEncoder.ReverseEncode(_posEncoders, num8 - posSlot - 1, _rangeEncoder, num7, num9);
							}
							else
							{
								_rangeEncoder.EncodeDirectBits(num9 >> 4, num7 - 4);
								_posAlignEncoder.ReverseEncode(_rangeEncoder, num9 & 0xFu);
								_alignPriceCount++;
							}
						}
						uint num10 = backRes;
						for (uint num11 = 3u; num11 >= 1; num11--)
						{
							_repDistances[num11] = _repDistances[num11 - 1];
						}
						_repDistances[0] = num10;
						_matchPriceCount++;
					}
					_previousByte = _matchFinder.GetIndexByte((int)(optimum - 1 - _additionalOffset));
				}
				_additionalOffset -= optimum;
				nowPos64 += optimum;
				if (_additionalOffset == 0)
				{
					if (_matchPriceCount >= 128)
					{
						FillDistancesPrices();
					}
					if (_alignPriceCount >= 16)
					{
						FillAlignPrices();
					}
					inSize = nowPos64;
					outSize = _rangeEncoder.GetProcessedSizeAdd();
					if (_matchFinder.GetNumAvailableBytes() == 0)
					{
						Flush((uint)nowPos64);
						return;
					}
					if (nowPos64 - num >= 4096)
					{
						break;
					}
				}
			}
			_finished = false;
			finished = false;
		}

		private void ReleaseMFStream()
		{
			if (_matchFinder != null && _needReleaseMFStream)
			{
				_matchFinder.ReleaseStream();
				_needReleaseMFStream = false;
			}
		}

		private void SetOutStream(Stream outStream)
		{
			_rangeEncoder.SetStream(outStream);
		}

		private void ReleaseOutStream()
		{
			_rangeEncoder.ReleaseStream();
		}

		private void ReleaseStreams()
		{
			ReleaseMFStream();
			ReleaseOutStream();
		}

		private void SetStreams(Stream inStream, Stream outStream, long inSize, long outSize)
		{
			_inStream = inStream;
			_finished = false;
			Create();
			SetOutStream(outStream);
			Init();
			FillDistancesPrices();
			FillAlignPrices();
			_lenEncoder.SetTableSize(_numFastBytes + 1 - 2);
			_lenEncoder.UpdateTables((uint)(1 << _posStateBits));
			_repMatchLenEncoder.SetTableSize(_numFastBytes + 1 - 2);
			_repMatchLenEncoder.UpdateTables((uint)(1 << _posStateBits));
			nowPos64 = 0L;
		}

		public void Code(Stream inStream, Stream outStream, long inSize, long outSize, ICodeProgress progress)
		{
			_needReleaseMFStream = false;
			try
			{
				SetStreams(inStream, outStream, inSize, outSize);
				while (true)
				{
					CodeOneBlock(out var inSize2, out var outSize2, out var finished);
					if (finished)
					{
						break;
					}
					progress?.SetProgress(inSize2, outSize2);
				}
			}
			finally
			{
				ReleaseStreams();
			}
		}

		public void WriteCoderProperties(Stream outStream)
		{
			properties[0] = (byte)((_posStateBits * 5 + _numLiteralPosStateBits) * 9 + _numLiteralContextBits);
			for (int i = 0; i < 4; i++)
			{
				properties[1 + i] = (byte)((_dictionarySize >> 8 * i) & 0xFFu);
			}
			outStream.Write(properties, 0, 5);
		}

		private void FillDistancesPrices()
		{
			for (uint num = 4u; num < 128; num++)
			{
				uint posSlot = GetPosSlot(num);
				int num2 = (int)((posSlot >> 1) - 1);
				uint num3 = (2 | (posSlot & 1)) << num2;
				tempPrices[num] = BitTreeEncoder.ReverseGetPrice(_posEncoders, num3 - posSlot - 1, num2, num - num3);
			}
			for (uint num4 = 0u; num4 < 4; num4++)
			{
				BitTreeEncoder bitTreeEncoder = _posSlotEncoder[num4];
				uint num5 = num4 << 6;
				for (uint num6 = 0u; num6 < _distTableSize; num6++)
				{
					_posSlotPrices[num5 + num6] = bitTreeEncoder.GetPrice(num6);
				}
				for (uint num6 = 14u; num6 < _distTableSize; num6++)
				{
					_posSlotPrices[num5 + num6] += (num6 >> 1) - 1 - 4 << 6;
				}
				uint num7 = num4 * 128;
				uint num8;
				for (num8 = 0u; num8 < 4; num8++)
				{
					_distancesPrices[num7 + num8] = _posSlotPrices[num5 + num8];
				}
				for (; num8 < 128; num8++)
				{
					_distancesPrices[num7 + num8] = _posSlotPrices[num5 + GetPosSlot(num8)] + tempPrices[num8];
				}
			}
			_matchPriceCount = 0u;
		}

		private void FillAlignPrices()
		{
			for (uint num = 0u; num < 16; num++)
			{
				_alignPrices[num] = _posAlignEncoder.ReverseGetPrice(num);
			}
			_alignPriceCount = 0u;
		}

		private static int FindMatchFinder(string s)
		{
			for (int i = 0; i < kMatchFinderIDs.Length; i++)
			{
				if (s == kMatchFinderIDs[i])
				{
					return i;
				}
			}
			return -1;
		}

		public void SetCoderProperties(CoderPropID[] propIDs, object[] properties)
		{
			for (uint num = 0u; num < properties.Length; num++)
			{
				object obj = properties[num];
				switch (propIDs[num])
				{
				case CoderPropID.NumFastBytes:
					if (!(obj is int num2))
					{
						throw new InvalidParamException();
					}
					if (num2 < 5 || (long)num2 > 273L)
					{
						throw new InvalidParamException();
					}
					_numFastBytes = (uint)num2;
					break;
				case CoderPropID.MatchFinder:
				{
					if (!(obj is string))
					{
						throw new InvalidParamException();
					}
					EMatchFinderType matchFinderType = _matchFinderType;
					int num6 = FindMatchFinder(((string)obj).ToUpper());
					if (num6 < 0)
					{
						throw new InvalidParamException();
					}
					_matchFinderType = (EMatchFinderType)num6;
					if (_matchFinder != null && matchFinderType != _matchFinderType)
					{
						_dictionarySizePrev = uint.MaxValue;
						_matchFinder = null;
					}
					break;
				}
				case CoderPropID.DictionarySize:
				{
					if (!(obj is int num7))
					{
						throw new InvalidParamException();
					}
					if ((long)num7 < 1L || (long)num7 > 1073741824L)
					{
						throw new InvalidParamException();
					}
					_dictionarySize = (uint)num7;
					int i;
					for (i = 0; (long)i < 30L && num7 > (uint)(1 << i); i++)
					{
					}
					_distTableSize = (uint)(i * 2);
					break;
				}
				case CoderPropID.PosStateBits:
					if (!(obj is int num3))
					{
						throw new InvalidParamException();
					}
					if (num3 < 0 || (long)num3 > 4L)
					{
						throw new InvalidParamException();
					}
					_posStateBits = num3;
					_posStateMask = (uint)((1 << _posStateBits) - 1);
					break;
				case CoderPropID.LitPosBits:
					if (!(obj is int num5))
					{
						throw new InvalidParamException();
					}
					if (num5 < 0 || (long)num5 > 4L)
					{
						throw new InvalidParamException();
					}
					_numLiteralPosStateBits = num5;
					break;
				case CoderPropID.LitContextBits:
					if (!(obj is int num4))
					{
						throw new InvalidParamException();
					}
					if (num4 < 0 || (long)num4 > 8L)
					{
						throw new InvalidParamException();
					}
					_numLiteralContextBits = num4;
					break;
				case CoderPropID.EndMarker:
					if (!(obj is bool))
					{
						throw new InvalidParamException();
					}
					SetWriteEndMarkerMode((bool)obj);
					break;
				default:
					throw new InvalidParamException();
				case CoderPropID.Algorithm:
					break;
				}
			}
		}

		public void SetTrainSize(uint trainSize)
		{
			_trainSize = trainSize;
		}
	}
	public static class SevenZipHelper
	{
		private static CoderPropID[] propIDs = new CoderPropID[8]
		{
			CoderPropID.DictionarySize,
			CoderPropID.PosStateBits,
			CoderPropID.LitContextBits,
			CoderPropID.LitPosBits,
			CoderPropID.Algorithm,
			CoderPropID.NumFastBytes,
			CoderPropID.MatchFinder,
			CoderPropID.EndMarker
		};

		private static object[] properties = new object[8] { 2097152, 2, 3, 0, 2, 32, "bt4", false };

		public static byte[] Compress(byte[] inputBytes, ICodeProgress progress = null)
		{
			MemoryStream inStream = new MemoryStream(inputBytes);
			MemoryStream memoryStream = new MemoryStream();
			Compress(inStream, memoryStream, progress);
			return memoryStream.ToArray();
		}

		public static void Compress(Stream inStream, Stream outStream, ICodeProgress progress = null)
		{
			Encoder encoder = new Encoder();
			encoder.SetCoderProperties(propIDs, properties);
			encoder.WriteCoderProperties(outStream);
			encoder.Code(inStream, outStream, -1L, -1L, progress);
		}

		public static byte[] Decompress(byte[] inputBytes)
		{
			MemoryStream memoryStream = new MemoryStream(inputBytes);
			Decoder decoder = new Decoder();
			memoryStream.Seek(0L, SeekOrigin.Begin);
			MemoryStream memoryStream2 = new MemoryStream();
			byte[] array = new byte[5];
			if (memoryStream.Read(array, 0, 5) != 5)
			{
				throw new Exception("input .lzma is too short");
			}
			long num = 0L;
			for (int i = 0; i < 8; i++)
			{
				int num2 = memoryStream.ReadByte();
				if (num2 < 0)
				{
					throw new Exception("Can't Read 1");
				}
				num |= (long)((ulong)(byte)num2 << 8 * i);
			}
			decoder.SetDecoderProperties(array);
			long inSize = memoryStream.Length - memoryStream.Position;
			decoder.Code(memoryStream, memoryStream2, inSize, num, null);
			return memoryStream2.ToArray();
		}

		public static MemoryStream StreamDecompress(MemoryStream newInStream)
		{
			Decoder decoder = new Decoder();
			newInStream.Seek(0L, SeekOrigin.Begin);
			MemoryStream memoryStream = new MemoryStream();
			byte[] array = new byte[5];
			if (newInStream.Read(array, 0, 5) != 5)
			{
				throw new Exception("input .lzma is too short");
			}
			long num = 0L;
			for (int i = 0; i < 8; i++)
			{
				int num2 = newInStream.ReadByte();
				if (num2 < 0)
				{
					throw new Exception("Can't Read 1");
				}
				num |= (long)((ulong)(byte)num2 << 8 * i);
			}
			decoder.SetDecoderProperties(array);
			long inSize = newInStream.Length - newInStream.Position;
			decoder.Code(newInStream, memoryStream, inSize, num, null);
			memoryStream.Position = 0L;
			return memoryStream;
		}

		public static MemoryStream StreamDecompress(MemoryStream newInStream, long outSize)
		{
			Decoder decoder = new Decoder();
			newInStream.Seek(0L, SeekOrigin.Begin);
			MemoryStream memoryStream = new MemoryStream();
			byte[] array = new byte[5];
			if (newInStream.Read(array, 0, 5) != 5)
			{
				throw new Exception("input .lzma is too short");
			}
			decoder.SetDecoderProperties(array);
			long inSize = newInStream.Length - newInStream.Position;
			decoder.Code(newInStream, memoryStream, inSize, outSize, null);
			memoryStream.Position = 0L;
			return memoryStream;
		}

		public static void StreamDecompress(Stream compressedStream, Stream decompressedStream, long compressedSize, long decompressedSize)
		{
			long position = compressedStream.Position;
			Decoder decoder = new Decoder();
			byte[] array = new byte[5];
			if (compressedStream.Read(array, 0, 5) != 5)
			{
				throw new Exception("input .lzma is too short");
			}
			decoder.SetDecoderProperties(array);
			decoder.Code(compressedStream, decompressedStream, compressedSize - 5, decompressedSize, null);
			compressedStream.Position = position + compressedSize;
		}
	}
}
namespace SevenZip.Buffer
{
	public class InBuffer
	{
		private byte[] m_Buffer;

		private uint m_Pos;

		private uint m_Limit;

		private uint m_BufferSize;

		private Stream m_Stream;

		private bool m_StreamWasExhausted;

		private ulong m_ProcessedSize;

		public InBuffer(uint bufferSize)
		{
			m_Buffer = new byte[bufferSize];
			m_BufferSize = bufferSize;
		}

		public void Init(Stream stream)
		{
			m_Stream = stream;
			m_ProcessedSize = 0uL;
			m_Limit = 0u;
			m_Pos = 0u;
			m_StreamWasExhausted = false;
		}

		public bool ReadBlock()
		{
			if (m_StreamWasExhausted)
			{
				return false;
			}
			m_ProcessedSize += m_Pos;
			int num = m_Stream.Read(m_Buffer, 0, (int)m_BufferSize);
			m_Pos = 0u;
			m_Limit = (uint)num;
			m_StreamWasExhausted = num == 0;
			return !m_StreamWasExhausted;
		}

		public void ReleaseStream()
		{
			m_Stream = null;
		}

		public bool ReadByte(byte b)
		{
			if (m_Pos >= m_Limit && !ReadBlock())
			{
				return false;
			}
			b = m_Buffer[m_Pos++];
			return true;
		}

		public byte ReadByte()
		{
			if (m_Pos >= m_Limit && !ReadBlock())
			{
				return byte.MaxValue;
			}
			return m_Buffer[m_Pos++];
		}

		public ulong GetProcessedSize()
		{
			return m_ProcessedSize + m_Pos;
		}
	}
	public class OutBuffer
	{
		private byte[] m_Buffer;

		private uint m_Pos;

		private uint m_BufferSize;

		private Stream m_Stream;

		private ulong m_ProcessedSize;

		public OutBuffer(uint bufferSize)
		{
			m_Buffer = new byte[bufferSize];
			m_BufferSize = bufferSize;
		}

		public void SetStream(Stream stream)
		{
			m_Stream = stream;
		}

		public void FlushStream()
		{
			m_Stream.Flush();
		}

		public void CloseStream()
		{
			m_Stream.Close();
		}

		public void ReleaseStream()
		{
			m_Stream = null;
		}

		public void Init()
		{
			m_ProcessedSize = 0uL;
			m_Pos = 0u;
		}

		public void WriteByte(byte b)
		{
			m_Buffer[m_Pos++] = b;
			if (m_Pos >= m_BufferSize)
			{
				FlushData();
			}
		}

		public void FlushData()
		{
			if (m_Pos != 0)
			{
				m_Stream.Write(m_Buffer, 0, (int)m_Pos);
				m_Pos = 0u;
			}
		}

		public ulong GetProcessedSize()
		{
			return m_ProcessedSize + m_Pos;
		}
	}
}
namespace SevenZip.CommandLineParser
{
	public enum SwitchType
	{
		Simple,
		PostMinus,
		LimitedPostString,
		UnLimitedPostString,
		PostChar
	}
	public class SwitchForm
	{
		public string IDString;

		public SwitchType Type;

		public bool Multi;

		public int MinLen;

		public int MaxLen;

		public string PostCharSet;

		public SwitchForm(string idString, SwitchType type, bool multi, int minLen, int maxLen, string postCharSet)
		{
			IDString = idString;
			Type = type;
			Multi = multi;
			MinLen = minLen;
			MaxLen = maxLen;
			PostCharSet = postCharSet;
		}

		public SwitchForm(string idString, SwitchType type, bool multi, int minLen)
			: this(idString, type, multi, minLen, 0, "")
		{
		}

		public SwitchForm(string idString, SwitchType type, bool multi)
			: this(idString, type, multi, 0)
		{
		}
	}
	public class SwitchResult
	{
		public bool ThereIs;

		public bool WithMinus;

		public ArrayList PostStrings = new ArrayList();

		public int PostCharIndex;

		public SwitchResult()
		{
			ThereIs = false;
		}
	}
	public class Parser
	{
		public ArrayList NonSwitchStrings = new ArrayList();

		private SwitchResult[] _switches;

		private const char kSwitchID1 = '-';

		private const char kSwitchID2 = '/';

		private const char kSwitchMinus = '-';

		private const string kStopSwitchParsing = "--";

		public SwitchResult this[int index] => _switches[index];

		public Parser(int numSwitches)
		{
			_switches = new SwitchResult[numSwitches];
			for (int i = 0; i < numSwitches; i++)
			{
				_switches[i] = new SwitchResult();
			}
		}

		private bool ParseString(string srcString, SwitchForm[] switchForms)
		{
			int length = srcString.Length;
			if (length == 0)
			{
				return false;
			}
			int num = 0;
			if (!IsItSwitchChar(srcString[num]))
			{
				return false;
			}
			while (num < length)
			{
				if (IsItSwitchChar(srcString[num]))
				{
					num++;
				}
				int num2 = 0;
				int num3 = -1;
				for (int i = 0; i < _switches.Length; i++)
				{
					int length2 = switchForms[i].IDString.Length;
					if (length2 > num3 && num + length2 <= length && string.Compare(switchForms[i].IDString, 0, srcString, num, length2, ignoreCase: true) == 0)
					{
						num2 = i;
						num3 = length2;
					}
				}
				if (num3 == -1)
				{
					throw new Exception("maxLen == kNoLen");
				}
				SwitchResult switchResult = _switches[num2];
				SwitchForm switchForm = switchForms[num2];
				if (!switchForm.Multi && switchResult.ThereIs)
				{
					throw new Exception("switch must be single");
				}
				switchResult.ThereIs = true;
				num += num3;
				int num4 = length - num;
				SwitchType type = switchForm.Type;
				switch (type)
				{
				case SwitchType.PostMinus:
					if (num4 == 0)
					{
						switchResult.WithMinus = false;
						break;
					}
					switchResult.WithMinus = srcString[num] == '-';
					if (switchResult.WithMinus)
					{
						num++;
					}
					break;
				case SwitchType.PostChar:
				{
					if (num4 < switchForm.MinLen)
					{
						throw new Exception("switch is not full");
					}
					string postCharSet = switchForm.PostCharSet;
					if (num4 == 0)
					{
						switchResult.PostCharIndex = -1;
						break;
					}
					int num6 = postCharSet.IndexOf(srcString[num]);
					if (num6 < 0)
					{
						switchResult.PostCharIndex = -1;
						break;
					}
					switchResult.PostCharIndex = num6;
					num++;
					break;
				}
				case SwitchType.LimitedPostString:
				case SwitchType.UnLimitedPostString:
				{
					int minLen = switchForm.MinLen;
					if (num4 < minLen)
					{
						throw new Exception("switch is not full");
					}
					if (type == SwitchType.UnLimitedPostString)
					{
						switchResult.PostStrings.Add(srcString.Substring(num));
						return true;
					}
					string text = srcString.Substring(num, minLen);
					num += minLen;
					int num5 = minLen;
					while (num5 < switchForm.MaxLen && num < length)
					{
						char c = srcString[num];
						if (IsItSwitchChar(c))
						{
							break;
						}
						text += c;
						num5++;
						num++;
					}
					switchResult.PostStrings.Add(text);
					break;
				}
				}
			}
			return true;
		}

		public void ParseStrings(SwitchForm[] switchForms, string[] commandStrings)
		{
			int num = commandStrings.Length;
			bool flag = false;
			for (int i = 0; i < num; i++)
			{
				string text = commandStrings[i];
				if (flag)
				{
					NonSwitchStrings.Add(text);
				}
				else if (text == "--")
				{
					flag = true;
				}
				else if (!ParseString(text, switchForms))
				{
					NonSwitchStrings.Add(text);
				}
			}
		}

		public static int ParseCommand(CommandForm[] commandForms, string commandString, out string postString)
		{
			for (int i = 0; i < commandForms.Length; i++)
			{
				string iDString = commandForms[i].IDString;
				if (commandForms[i].PostStringMode)
				{
					if (commandString.IndexOf(iDString) == 0)
					{
						postString = commandString.Substring(iDString.Length);
						return i;
					}
				}
				else if (commandString == iDString)
				{
					postString = "";
					return i;
				}
			}
			postString = "";
			return -1;
		}

		private static bool ParseSubCharsCommand(int numForms, CommandSubCharsSet[] forms, string commandString, ArrayList indices)
		{
			indices.Clear();
			int num = 0;
			for (int i = 0; i < numForms; i++)
			{
				CommandSubCharsSet commandSubCharsSet = forms[i];
				int num2 = -1;
				int length = commandSubCharsSet.Chars.Length;
				for (int j = 0; j < length; j++)
				{
					char value = commandSubCharsSet.Chars[j];
					int num3 = commandString.IndexOf(value);
					if (num3 >= 0)
					{
						if (num2 >= 0)
						{
							return false;
						}
						if (commandString.IndexOf(value, num3 + 1) >= 0)
						{
							return false;
						}
						num2 = j;
						num++;
					}
				}
				if (num2 == -1 && !commandSubCharsSet.EmptyAllowed)
				{
					return false;
				}
				indices.Add(num2);
			}
			return num == commandString.Length;
		}

		private static bool IsItSwitchChar(char c)
		{
			if (c != '-')
			{
				return c == '/';
			}
			return true;
		}
	}
	public class CommandForm
	{
		public string IDString = "";

		public bool PostStringMode;

		public CommandForm(string idString, bool postStringMode)
		{
			IDString = idString;
			PostStringMode = postStringMode;
		}
	}
	internal class CommandSubCharsSet
	{
		public string Chars = "";

		public bool EmptyAllowed;
	}
}
namespace LZ4ps
{
	public static class LZ4Codec
	{
		private class LZ4HC_Data_Structure
		{
			public byte[] src;

			public int src_base;

			public int src_end;

			public int src_LASTLITERALS;

			public byte[] dst;

			public int dst_base;

			public int dst_len;

			public int dst_end;

			public int[] hashTable;

			public ushort[] chainTable;

			public int nextToUpdate;
		}

		private const int MEMORY_USAGE = 14;

		private const int NOTCOMPRESSIBLE_DETECTIONLEVEL = 6;

		private const int BLOCK_COPY_LIMIT = 16;

		private const int MINMATCH = 4;

		private const int SKIPSTRENGTH = 6;

		private const int COPYLENGTH = 8;

		private const int LASTLITERALS = 5;

		private const int MFLIMIT = 12;

		private const int MINLENGTH = 13;

		private const int MAXD_LOG = 16;

		private const int MAXD = 65536;

		private const int MAXD_MASK = 65535;

		private const int MAX_DISTANCE = 65535;

		private const int ML_BITS = 4;

		private const int ML_MASK = 15;

		private const int RUN_BITS = 4;

		private const int RUN_MASK = 15;

		private const int STEPSIZE_64 = 8;

		private const int STEPSIZE_32 = 4;

		private const int LZ4_64KLIMIT = 65547;

		private const int HASH_LOG = 12;

		private const int HASH_TABLESIZE = 4096;

		private const int HASH_ADJUST = 20;

		private const int HASH64K_LOG = 13;

		private const int HASH64K_TABLESIZE = 8192;

		private const int HASH64K_ADJUST = 19;

		private const int HASHHC_LOG = 15;

		private const int HASHHC_TABLESIZE = 32768;

		private const int HASHHC_ADJUST = 17;

		private static readonly int[] DECODER_TABLE_32 = new int[8] { 0, 3, 2, 3, 0, 0, 0, 0 };

		private static readonly int[] DECODER_TABLE_64 = new int[8] { 0, 0, 0, -1, 0, 1, 2, 3 };

		private static readonly int[] DEBRUIJN_TABLE_32 = new int[32]
		{
			0, 0, 3, 0, 3, 1, 3, 0, 3, 2,
			2, 1, 3, 2, 0, 1, 3, 3, 1, 2,
			2, 2, 2, 0, 3, 1, 2, 0, 1, 0,
			1, 1
		};

		private static readonly int[] DEBRUIJN_TABLE_64 = new int[64]
		{
			0, 0, 0, 0, 0, 1, 1, 2, 0, 3,
			1, 3, 1, 4, 2, 7, 0, 2, 3, 6,
			1, 5, 3, 5, 1, 3, 4, 4, 2, 5,
			6, 7, 7, 0, 1, 2, 3, 3, 4, 6,
			2, 6, 5, 5, 3, 4, 5, 6, 7, 1,
			2, 4, 6, 4, 4, 5, 7, 2, 6, 5,
			7, 6, 7, 7
		};

		private const int MAX_NB_ATTEMPTS = 256;

		private const int OPTIMAL_ML = 18;

		public static int MaximumOutputLength(int inputLength)
		{
			return inputLength + inputLength / 255 + 16;
		}

		internal static void CheckArguments(byte[] input, int inputOffset, ref int inputLength, byte[] output, int outputOffset, ref int outputLength)
		{
			if (inputLength < 0)
			{
				inputLength = input.Length - inputOffset;
			}
			if (inputLength == 0)
			{
				outputLength = 0;
				return;
			}
			if (input == null)
			{
				throw new ArgumentNullException("input");
			}
			if (inputOffset < 0 || inputOffset + inputLength > input.Length)
			{
				throw new ArgumentException("inputOffset and inputLength are invalid for given input");
			}
			if (outputLength < 0)
			{
				outputLength = output.Length - outputOffset;
			}
			if (output == null)
			{
				throw new ArgumentNullException("output");
			}
			if (outputOffset >= 0 && outputOffset + outputLength <= output.Length)
			{
				return;
			}
			throw new ArgumentException("outputOffset and outputLength are invalid for given output");
		}

		[Conditional("DEBUG")]
		private static void Assert(bool condition, string errorMessage)
		{
			if (!condition)
			{
				throw new ArgumentException(errorMessage);
			}
		}

		internal static void Poke2(byte[] buffer, int offset, ushort value)
		{
			buffer[offset] = (byte)value;
			buffer[offset + 1] = (byte)(value >> 8);
		}

		internal static ushort Peek2(byte[] buffer, int offset)
		{
			return (ushort)(buffer[offset] | (buffer[offset + 1] << 8));
		}

		internal static uint Peek4(byte[] buffer, int offset)
		{
			return (uint)(buffer[offset] | (buffer[offset + 1] << 8) | (buffer[offset + 2] << 16) | (buffer[offset + 3] << 24));
		}

		private static uint Xor4(byte[] buffer, int offset1, int offset2)
		{
			int num = buffer[offset1] | (buffer[offset1 + 1] << 8) | (buffer[offset1 + 2] << 16) | (buffer[offset1 + 3] << 24);
			uint num2 = (uint)(buffer[offset2] | (buffer[offset2 + 1] << 8) | (buffer[offset2 + 2] << 16) | (buffer[offset2 + 3] << 24));
			return (uint)num ^ num2;
		}

		private static ulong Xor8(byte[] buffer, int offset1, int offset2)
		{
			ulong num = buffer[offset1] | ((ulong)buffer[offset1 + 1] << 8) | ((ulong)buffer[offset1 + 2] << 16) | ((ulong)buffer[offset1 + 3] << 24) | ((ulong)buffer[offset1 + 4] << 32) | ((ulong)buffer[offset1 + 5] << 40) | ((ulong)buffer[offset1 + 6] << 48) | ((ulong)buffer[offset1 + 7] << 56);
			ulong num2 = buffer[offset2] | ((ulong)buffer[offset2 + 1] << 8) | ((ulong)buffer[offset2 + 2] << 16) | ((ulong)buffer[offset2 + 3] << 24) | ((ulong)buffer[offset2 + 4] << 32) | ((ulong)buffer[offset2 + 5] << 40) | ((ulong)buffer[offset2 + 6] << 48) | ((ulong)buffer[offset2 + 7] << 56);
			return num ^ num2;
		}

		private static bool Equal2(byte[] buffer, int offset1, int offset2)
		{
			if (buffer[offset1] != buffer[offset2])
			{
				return false;
			}
			return buffer[offset1 + 1] == buffer[offset2 + 1];
		}

		private static bool Equal4(byte[] buffer, int offset1, int offset2)
		{
			if (buffer[offset1] != buffer[offset2])
			{
				return false;
			}
			if (buffer[offset1 + 1] != buffer[offset2 + 1])
			{
				return false;
			}
			if (buffer[offset1 + 2] != buffer[offset2 + 2])
			{
				return false;
			}
			return buffer[offset1 + 3] == buffer[offset2 + 3];
		}

		private static void Copy4(byte[] buf, int src, int dst)
		{
			buf[dst + 3] = buf[src + 3];
			buf[dst + 2] = buf[src + 2];
			buf[dst + 1] = buf[src + 1];
			buf[dst] = buf[src];
		}

		private static void Copy8(byte[] buf, int src, int dst)
		{
			buf[dst + 7] = buf[src + 7];
			buf[dst + 6] = buf[src + 6];
			buf[dst + 5] = buf[src + 5];
			buf[dst + 4] = buf[src + 4];
			buf[dst + 3] = buf[src + 3];
			buf[dst + 2] = buf[src + 2];
			buf[dst + 1] = buf[src + 1];
			buf[dst] = buf[src];
		}

		private static void BlockCopy(byte[] src, int src_0, byte[] dst, int dst_0, int len)
		{
			if (len >= 16)
			{
				Buffer.BlockCopy(src, src_0, dst, dst_0, len);
				return;
			}
			while (len >= 8)
			{
				dst[dst_0] = src[src_0];
				dst[dst_0 + 1] = src[src_0 + 1];
				dst[dst_0 + 2] = src[src_0 + 2];
				dst[dst_0 + 3] = src[src_0 + 3];
				dst[dst_0 + 4] = src[src_0 + 4];
				dst[dst_0 + 5] = src[src_0 + 5];
				dst[dst_0 + 6] = src[src_0 + 6];
				dst[dst_0 + 7] = src[src_0 + 7];
				len -= 8;
				src_0 += 8;
				dst_0 += 8;
			}
			while (len >= 4)
			{
				dst[dst_0] = src[src_0];
				dst[dst_0 + 1] = src[src_0 + 1];
				dst[dst_0 + 2] = src[src_0 + 2];
				dst[dst_0 + 3] = src[src_0 + 3];
				len -= 4;
				src_0 += 4;
				dst_0 += 4;
			}
			while (len-- > 0)
			{
				dst[dst_0++] = src[src_0++];
			}
		}

		private static int WildCopy(byte[] src, int src_0, byte[] dst, int dst_0, int dst_end)
		{
			int num = dst_end - dst_0;
			if (num >= 16)
			{
				Buffer.BlockCopy(src, src_0, dst, dst_0, num);
			}
			else
			{
				while (num >= 4)
				{
					dst[dst_0] = src[src_0];
					dst[dst_0 + 1] = src[src_0 + 1];
					dst[dst_0 + 2] = src[src_0 + 2];
					dst[dst_0 + 3] = src[src_0 + 3];
					num -= 4;
					src_0 += 4;
					dst_0 += 4;
				}
				while (num-- > 0)
				{
					dst[dst_0++] = src[src_0++];
				}
			}
			return num;
		}

		private static int SecureCopy(byte[] buffer, int src, int dst, int dst_end)
		{
			int num = dst - src;
			int num2 = dst_end - dst;
			int num3 = num2;
			if (num >= 16)
			{
				if (num >= num2)
				{
					Buffer.BlockCopy(buffer, src, buffer, dst, num2);
					return num2;
				}
				do
				{
					Buffer.BlockCopy(buffer, src, buffer, dst, num);
					src += num;
					dst += num;
					num3 -= num;
				}
				while (num3 >= num);
			}
			while (num3 >= 4)
			{
				buffer[dst] = buffer[src];
				buffer[dst + 1] = buffer[src + 1];
				buffer[dst + 2] = buffer[src + 2];
				buffer[dst + 3] = buffer[src + 3];
				dst += 4;
				src += 4;
				num3 -= 4;
			}
			while (num3-- > 0)
			{
				buffer[dst++] = buffer[src++];
			}
			return num2;
		}

		public static int Encode32(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset, int outputLength)
		{
			CheckArguments(input, inputOffset, ref inputLength, output, outputOffset, ref outputLength);
			if (outputLength == 0)
			{
				return 0;
			}
			if (inputLength < 65547)
			{
				return LZ4_compress64kCtx_safe32(new ushort[8192], input, output, inputOffset, outputOffset, inputLength, outputLength);
			}
			return LZ4_compressCtx_safe32(new int[4096], input, output, inputOffset, outputOffset, inputLength, outputLength);
		}

		public static byte[] Encode32(byte[] input, int inputOffset, int inputLength)
		{
			if (inputLength < 0)
			{
				inputLength = input.Length - inputOffset;
			}
			if (input == null)
			{
				throw new ArgumentNullException("input");
			}
			if (inputOffset < 0 || inputOffset + inputLength > input.Length)
			{
				throw new ArgumentException("inputOffset and inputLength are invalid for given input");
			}
			byte[] array = new byte[MaximumOutputLength(inputLength)];
			int num = Encode32(input, inputOffset, inputLength, array, 0, array.Length);
			if (num != array.Length)
			{
				if (num < 0)
				{
					throw new InvalidOperationException("Compression has been corrupted");
				}
				byte[] array2 = new byte[num];
				Buffer.BlockCopy(array, 0, array2, 0, num);
				return array2;
			}
			return array;
		}

		public static int Encode64(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset, int outputLength)
		{
			CheckArguments(input, inputOffset, ref inputLength, output, outputOffset, ref outputLength);
			if (outputLength == 0)
			{
				return 0;
			}
			if (inputLength < 65547)
			{
				return LZ4_compress64kCtx_safe64(new ushort[8192], input, output, inputOffset, outputOffset, inputLength, outputLength);
			}
			return LZ4_compressCtx_safe64(new int[4096], input, output, inputOffset, outputOffset, inputLength, outputLength);
		}

		public static byte[] Encode64(byte[] input, int inputOffset, int inputLength)
		{
			if (inputLength < 0)
			{
				inputLength = input.Length - inputOffset;
			}
			if (input == null)
			{
				throw new ArgumentNullException("input");
			}
			if (inputOffset < 0 || inputOffset + inputLength > input.Length)
			{
				throw new ArgumentException("inputOffset and inputLength are invalid for given input");
			}
			byte[] array = new byte[MaximumOutputLength(inputLength)];
			int num = Encode64(input, inputOffset, inputLength, array, 0, array.Length);
			if (num != array.Length)
			{
				if (num < 0)
				{
					throw new InvalidOperationException("Compression has been corrupted");
				}
				byte[] array2 = new byte[num];
				Buffer.BlockCopy(array, 0, array2, 0, num);
				return array2;
			}
			return array;
		}

		public static int Decode32(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset, int outputLength, bool knownOutputLength)
		{
			CheckArguments(input, inputOffset, ref inputLength, output, outputOffset, ref outputLength);
			if (outputLength == 0)
			{
				return 0;