Decompiled source of Aicho Better Bloom v1.0.0

Aicho_BetterBloom.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.PostProcessing;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Aicho_Bloom")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Configurable bloom for Valheim.")]
[assembly: AssemblyFileVersion("0.4.0.0")]
[assembly: AssemblyInformationalVersion("0.4.0")]
[assembly: AssemblyProduct("Aicho Bloom")]
[assembly: AssemblyTitle("Aicho_Bloom")]
[assembly: AssemblyVersion("0.4.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace AichoBloom
{
	public enum BloomMode
	{
		Game,
		ForceOn,
		ForceOff
	}
	public enum ThresholdSpace
	{
		Gamma,
		Linear
	}
	internal sealed class ContextCfg
	{
		public ConfigEntry<float> Intensity;

		public ConfigEntry<float> ThresholdOffset;

		public ConfigEntry<float> Dirt;

		public static ContextCfg Bind(ConfigFile cfg, string section, string what, float intensity = 1f, float thresholdOffset = 0f, float dirt = 1f)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Expected O, but got Unknown
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Expected O, but got Unknown
			return new ContextCfg
			{
				Intensity = cfg.Bind<float>(section, "IntensityMultiplier", intensity, new ConfigDescription("Multiplicador de la intensidad del bloom " + what + ". 1 = sin cambio.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>())),
				ThresholdOffset = cfg.Bind<float>(section, "ThresholdOffset", thresholdOffset, new ConfigDescription("Se suma al umbral (en espacio gamma) " + what + ". Positivo = solo brilla lo mas claro; negativo = brilla casi todo. 0 = sin cambio.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 2f), Array.Empty<object>())),
				Dirt = cfg.Bind<float>(section, "LensDirtMultiplier", dirt, new ConfigDescription("Multiplicador de la suciedad de lente " + what + ". 1 = sin cambio.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()))
			};
		}
	}
	internal sealed class ConfigurationManagerAttributes
	{
		public int? Order;

		public bool? HideDefaultButton;

		public string DispName;

		public Action<ConfigEntryBase> CustomDrawer;
	}
	[BepInPlugin("aicho.bloom", "Aicho Bloom", "0.4.0")]
	public class AichoBloomPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "aicho.bloom";

		public const string PluginName = "Aicho Bloom";

		public const string PluginVersion = "0.4.0";

		private const string TestedGameVersion = "1.0.20";

		private const int MaxFailStreak = 5;

		private const string MasterSection = "0. Mod";

		internal static AichoBloomPlugin Instance;

		private ConfigEntry<bool> _enabled;

		private ConfigEntry<bool> _reset;

		private volatile bool _resetRequested;

		private static float _resetConfirmUntil;

		private ConfigEntry<KeyboardShortcut> _toggleKey;

		private ConfigEntry<KeyboardShortcut> _dumpKey;

		private ConfigEntry<bool> _autoReload;

		private ConfigEntry<float> _smoothing;

		private ConfigEntry<bool> _debugLog;

		private ConfigEntry<BloomMode> _mode;

		private ConfigEntry<float> _intensity;

		private ConfigEntry<ThresholdSpace> _thresholdSpace;

		private ConfigEntry<float> _threshold;

		private ConfigEntry<float> _softKnee;

		private ConfigEntry<float> _radius;

		private ConfigEntry<bool> _antiFlicker;

		private ConfigEntry<bool> _normalize;

		private ConfigEntry<int> _referenceLevels;

		private ConfigEntry<float> _dirtIntensity;

		private ConfigEntry<string> _dirtTexture;

		private ConfigEntry<bool> _modEnabled;

		private ContextCfg _day;

		private ContextCfg _night;

		private ContextCfg _twilight;

		private ContextCfg _wet;

		private ContextCfg _freezing;

		private ContextCfg _interior;

		private bool _bypass;

		private bool _dead;

		private int _failStreak;

		private float _fWet;

		private float _fFreezing;

		private float _fInterior;

		private Texture2D _customTex;

		private string _customTexKey = "\0";

		private FileSystemWatcher _watcher;

		private long _reloadAtTicks;

		private long _lastSettingChangeTicks;

		private bool _reloading;

		private AppliedInfo _info;

		private bool _hasInfo;

		private float _lastWDay;

		private float _lastWNight;

		private float _lastWTwi;

		private float _nextDebug;

		private static readonly HashSet<string> LoggedOnce = new HashSet<string>();

		internal static void LogInfo(string msg)
		{
			if ((Object)(object)Instance != (Object)null)
			{
				((BaseUnityPlugin)Instance).Logger.LogInfo((object)msg);
			}
		}

		internal static void LogWarning(string msg)
		{
			if ((Object)(object)Instance != (Object)null)
			{
				((BaseUnityPlugin)Instance).Logger.LogWarning((object)msg);
			}
		}

		internal static void LogOnce(string key, string msg)
		{
			if ((Object)(object)Instance == (Object)null)
			{
				return;
			}
			lock (LoggedOnce)
			{
				if (!LoggedOnce.Add(key))
				{
					return;
				}
			}
			((BaseUnityPlugin)Instance).Logger.LogWarning((object)msg);
		}

		private void Awake()
		{
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Expected O, but got Unknown
			Instance = this;
			BindConfig();
			_reset.SettingChanged += delegate
			{
				if (_reset.Value)
				{
					_resetRequested = true;
				}
			};
			if (_reset.Value)
			{
				_resetRequested = true;
			}
			LogInfo("Aicho Bloom 0.4.0 cargado. Mod " + (_enabled.Value ? "activado" : "DESACTIVADO (bloom vanilla)") + " ('Enabled' en la seccion '0. Mod' del cfg).");
			LogGameVersion();
			try
			{
				BloomDriver.InstallPatch(new Harmony("aicho.bloom"));
			}
			catch (Exception ex)
			{
				LogWarning("No se pudo instalar el parche opcional (" + ex.GetType().Name + "); el mod funciona igual aplicando cada fotograma.");
			}
			SetupWatcher();
		}

		private void LogGameVersion()
		{
			try
			{
				string text = BloomDriver.GameVersion();
				if (text == "1.0.20")
				{
					LogInfo("Valheim " + text + " (version con la que se probo el mod).");
				}
				else
				{
					LogInfo("Valheim " + text + "; el mod se probo con 1.0.20. Si una actualizacion cambio algo, el mod se apaga solo y el bloom queda en vanilla.");
				}
			}
			catch (Exception ex)
			{
				LogWarning("No pude leer la version del juego (" + ex.GetType().Name + ").");
			}
		}

		private void OnDestroy()
		{
			try
			{
				BloomDriver.Restore();
			}
			catch
			{
			}
			if (_watcher != null)
			{
				_watcher.EnableRaisingEvents = false;
				_watcher.Dispose();
				_watcher = null;
			}
		}

		private void BindConfig()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Expected O, but got Unknown
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Expected O, but got Unknown
			//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Expected O, but got Unknown
			//IL_0255: Unknown result type (might be due to invalid IL or missing references)
			//IL_025f: Expected O, but got Unknown
			//IL_0293: Unknown result type (might be due to invalid IL or missing references)
			//IL_029d: Expected O, but got Unknown
			//IL_02d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02db: Expected O, but got Unknown
			//IL_0346: Unknown result type (might be due to invalid IL or missing references)
			//IL_0350: Expected O, but got Unknown
			//IL_0384: Unknown result type (might be due to invalid IL or missing references)
			//IL_038e: Expected O, but got Unknown
			_enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("0. Mod", "Enabled", true, new ConfigDescription("Activa o desactiva TODO el mod. En false el bloom vuelve a ser exactamente el de vanilla y las demas opciones se ignoran. Se aplica en vivo.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 100
				}
			}));
			_reset = ((BaseUnityPlugin)this).Config.Bind<bool>("0. Mod", "ResetToDefaults", false, new ConfigDescription("Restablece TODAS las opciones del mod (incluidas las teclas y Enabled) a sus valores por defecto. En ConfigurationManager aparece como un boton (pulsalo dos veces para confirmar). En el archivo cfg: ponlo en true y guarda; se aplica solo y vuelve a false. Antes de restablecer se guarda una copia del cfg con el sufijo .antes-de-restablecer.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 90,
					HideDefaultButton = true,
					DispName = "Restablecer todo a los valores por defecto",
					CustomDrawer = DrawResetButton
				}
			}));
			_toggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "ToggleKey", new KeyboardShortcut((KeyCode)98, (KeyCode[])(object)new KeyCode[1] { (KeyCode)307 }), "Atajo para alternar entre tu configuracion y el bloom vanilla (sirve para comparar). No modifica el archivo cfg.");
			_dumpKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "DumpKey", new KeyboardShortcut((KeyCode)110, (KeyCode[])(object)new KeyCode[1] { (KeyCode)307 }), "Atajo que escribe en el log de BepInEx los valores que se estan aplicando ahora mismo (intensidad real, niveles de la piramide, pesos de contexto, resolucion, espacio de color).");
			_autoReload = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "AutoReloadConfigFile", true, "Recarga el archivo cfg en cuanto lo guardas desde un editor de texto, sin reiniciar el juego. (Con ConfigurationManager los cambios ya son en vivo.)");
			_smoothing = ((BaseUnityPlugin)this).Config.Bind<float>("General", "SmoothingSeconds", 1.5f, new ConfigDescription("Segundos que tarda en mezclarse un cambio de contexto (entrar a una cueva, empezar a llover...). 0 = instantaneo.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
			_debugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DebugLog", false, "Escribe en el log los valores aplicados cada pocos segundos.");
			_mode = ((BaseUnityPlugin)this).Config.Bind<BloomMode>("Bloom", "Mode", BloomMode.Game, "Game = respeta el interruptor 'Bloom' del menu de graficos del juego. ForceOn = bloom siempre activo (incluso en el preset Very Low). ForceOff = siempre apagado.");
			_intensity = ((BaseUnityPlugin)this).Config.Bind<float>("Bloom", "Intensity", 0.4f, new ConfigDescription("Fuerza del bloom. Vanilla 0.30. El bloom de Valheim es aditivo y suma varios niveles, asi que sube de a poco.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
			_thresholdSpace = ((BaseUnityPlugin)this).Config.Bind<ThresholdSpace>("Bloom", "ThresholdSpace", ThresholdSpace.Gamma, "En que espacio esta escrito el valor de Threshold. Gamma = como lo guarda el juego (0.70 vanilla). Linear = brillo lineal real (0.70 gamma equivale a 0.448 lineal).");
			_threshold = ((BaseUnityPlugin)this).Config.Bind<float>("Bloom", "Threshold", 0.9f, new ConfigDescription("Brillo minimo para que un pixel produzca bloom. Vanilla 0.70 (gamma). Subirlo reduce el velo en zonas claras (nieve, cielo, arena) y deja brillar solo luces y fuego.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 20f), Array.Empty<object>()));
			_softKnee = ((BaseUnityPlugin)this).Config.Bind<float>("Bloom", "SoftKnee", 0f, new ConfigDescription("Suavidad de la transicion bajo/sobre el umbral. 0 = corte seco, 1 = muy gradual. Vanilla 0.70.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			_radius = ((BaseUnityPlugin)this).Config.Bind<float>("Bloom", "Radius", 1f, new ConfigDescription("Tamano del halo, independiente de la resolucion. Vanilla 5. Ojo: cambia tambien cuantos niveles tiene la piramide (ver seccion Resolution).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 7f), Array.Empty<object>()));
			_antiFlicker = ((BaseUnityPlugin)this).Config.Bind<bool>("Bloom", "AntiFlicker", true, "Filtro de mediana + promedio de Karis que evita parpadeos, pero tambien apaga el brillo de luces muy pequenas (chispas, fuegos lejanos). Vanilla true. Ponlo en false si quieres que las luces pequenas brillen mas (puede parpadear).");
			_normalize = ((BaseUnityPlugin)this).Config.Bind<bool>("Resolution", "NormalizeGain", false, "El bloom vanilla suma N niveles y N depende de la altura de render (5 a ~720p, 6 a 1080p-1440p, 7 a 4K), por eso en 4K brilla mas que en 1080p. En true se escala la intensidad por ReferenceLevels/N para que se vea igual en cualquier resolucion o render scale.");
			_referenceLevels = ((BaseUnityPlugin)this).Config.Bind<int>("Resolution", "ReferenceLevels", 6, new ConfigDescription("Niveles de piramide con los que la intensidad se ve 'como esta escrita'. 6 = lo que obtienes en 1080p/1440p con Radius 5.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10), Array.Empty<object>()));
			_dirtIntensity = ((BaseUnityPlugin)this).Config.Bind<float>("LensDirt", "Intensity", 15f, new ConfigDescription("Intensidad de la suciedad de lente (manchas que se iluminan con el bloom). Vanilla 10.4. 0 = sin suciedad. Es un multiplicador sobre el bloom: con 10.4 las manchas mas claras llegan a x8.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			_dirtTexture = ((BaseUnityPlugin)this).Config.Bind<string>("LensDirt", "CustomTexture", "", "Imagen PNG/JPG propia para la suciedad de lente. Vacio = la de vanilla. Ruta absoluta, o relativa a la carpeta del plugin o a BepInEx/config. Se estira a toda la pantalla, asi que usa una proporcion 16:9 o 2:1. Se aplica en vivo al cambiarla.");
			_modEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Modulation", "Enabled", false, "Activa la modulacion por contexto (los ajustes de las secciones Context.*). Con false se ignoran todos. Cada contexto multiplica la intensidad, desplaza el umbral y multiplica la suciedad; los de hora del dia se mezclan entre si y los de clima/interior se superponen con transicion suave.");
			_day = ContextCfg.Bind(((BaseUnityPlugin)this).Config, "Context.Day", "a pleno dia", 1f, 0.1f);
			_night = ContextCfg.Bind(((BaseUnityPlugin)this).Config, "Context.Night", "de noche", 2.357f, -0.5f);
			_twilight = ContextCfg.Bind(((BaseUnityPlugin)this).Config, "Context.Twilight", "al amanecer y al atardecer");
			_wet = ContextCfg.Bind(((BaseUnityPlugin)this).Config, "Context.Wet", "cuando llueve (EnvMan.IsWet)");
			_freezing = ContextCfg.Bind(((BaseUnityPlugin)this).Config, "Context.Freezing", "en clima helado o nevando (EnvMan.IsFreezing)");
			_interior = ContextCfg.Bind(((BaseUnityPlugin)this).Config, "Context.Interior", "dentro de mazmorras y cuevas (Character.InInterior)");
		}

		private void SetupWatcher()
		{
			((BaseUnityPlugin)this).Config.SettingChanged += delegate
			{
				if (!_reloading)
				{
					Interlocked.Exchange(ref _lastSettingChangeTicks, DateTime.UtcNow.Ticks);
				}
			};
			try
			{
				string configFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath;
				_watcher = new FileSystemWatcher(Path.GetDirectoryName(configFilePath), Path.GetFileName(configFilePath))
				{
					NotifyFilter = (NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite),
					EnableRaisingEvents = true
				};
				FileSystemEventHandler value = delegate
				{
					Interlocked.Exchange(ref _reloadAtTicks, DateTime.UtcNow.Ticks);
				};
				_watcher.Changed += value;
				_watcher.Created += value;
				_watcher.Renamed += delegate
				{
					Interlocked.Exchange(ref _reloadAtTicks, DateTime.UtcNow.Ticks);
				};
			}
			catch (Exception ex)
			{
				LogWarning("No se pudo vigilar el archivo cfg (la recarga automatica queda desactivada): " + ex.Message);
			}
		}

		private void LateUpdate()
		{
			try
			{
				if (_resetRequested)
				{
					PerformReset();
				}
				HandleConfigReload();
				HandleHotkeys();
			}
			catch (Exception ex)
			{
				LogOnce("input", "Error en atajos/recarga de cfg (" + ex.GetType().Name + ": " + ex.Message + "). El bloom sigue funcionando.");
			}
			TickSafe(Time.unscaledDeltaTime);
		}

		private void HandleConfigReload()
		{
			long num = Interlocked.Read(in _reloadAtTicks);
			if (num == 0L || DateTime.UtcNow.Ticks - num <= 4000000)
			{
				return;
			}
			Interlocked.Exchange(ref _reloadAtTicks, 0L);
			long num2 = Interlocked.Read(in _lastSettingChangeTicks);
			bool flag = num2 != 0L && Math.Abs(num - num2) < 20000000;
			if (!_autoReload.Value || flag)
			{
				return;
			}
			try
			{
				_reloading = true;
				((BaseUnityPlugin)this).Config.Reload();
				LogInfo("Archivo cfg editado desde fuera: recargado.");
			}
			catch (Exception ex)
			{
				LogWarning("Error al recargar el cfg: " + ex.Message);
			}
			finally
			{
				_reloading = false;
			}
		}

		private void HandleHotkeys()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			KeyboardShortcut value = _toggleKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				_bypass = !_bypass;
				Toast(_bypass ? "Aicho Bloom: bloom vanilla" : "Aicho Bloom: tu configuracion");
			}
			value = _dumpKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				Dump();
			}
		}

		private void PerformReset()
		{
			_resetRequested = false;
			try
			{
				string text = BackupConfigFile();
				int num = ConfigReset.ResetAll(((BaseUnityPlugin)this).Config, (ConfigEntryBase)(object)_reset);
				_reset.Value = false;
				if (!((BaseUnityPlugin)this).Config.SaveOnConfigSet)
				{
					((BaseUnityPlugin)this).Config.Save();
				}
				_bypass = false;
				LogInfo("Configuracion restablecida a los valores por defecto (" + num + " opciones cambiadas)." + ((text != null) ? (" Copia del cfg anterior: " + Path.GetFileName(text)) : string.Empty));
				Toast("Aicho Bloom: configuracion restablecida");
			}
			catch (Exception ex)
			{
				LogWarning("No se pudo restablecer la configuracion: " + ex.Message);
				try
				{
					_reset.Value = false;
				}
				catch
				{
				}
			}
		}

		private string BackupConfigFile()
		{
			try
			{
				string configFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath;
				string text = configFilePath + ".antes-de-restablecer";
				if (File.Exists(configFilePath))
				{
					File.Copy(configFilePath, text, overwrite: true);
					return text;
				}
			}
			catch (Exception ex)
			{
				LogWarning("No pude guardar la copia del cfg antes de restablecer: " + ex.Message);
			}
			return null;
		}

		internal static void DrawResetButton(ConfigEntryBase entry)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				bool flag = Time.realtimeSinceStartup < _resetConfirmUntil;
				Color backgroundColor = GUI.backgroundColor;
				if (flag)
				{
					GUI.backgroundColor = new Color(1f, 0.55f, 0.45f);
				}
				if (GUILayout.Button(flag ? "Seguro? Pulsa otra vez para restablecer TODO" : "Restablecer todo a los valores por defecto", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }))
				{
					if (flag)
					{
						_resetConfirmUntil = 0f;
						entry.BoxedValue = true;
					}
					else
					{
						_resetConfirmUntil = Time.realtimeSinceStartup + 6f;
					}
				}
				GUI.backgroundColor = backgroundColor;
			}
			catch
			{
			}
		}

		private static void Toast(string msg)
		{
			try
			{
				BloomDriver.Toast(msg);
			}
			catch
			{
			}
		}

		internal static void OnGameSettingsApplied()
		{
			try
			{
				if ((Object)(object)Instance != (Object)null)
				{
					Instance.TickSafe(0f);
				}
			}
			catch
			{
			}
		}

		private void TickSafe(float dt)
		{
			if (_dead)
			{
				return;
			}
			try
			{
				Tick(dt);
				_failStreak = 0;
			}
			catch (Exception ex)
			{
				_failStreak++;
				LogOnce("tick-" + ex.GetType().Name + ex.Message, "Error al aplicar el bloom (" + ex.GetType().Name + ": " + ex.Message + ").");
				if (_failStreak >= 5)
				{
					EnterSafeMode(ex);
				}
			}
		}

		private void EnterSafeMode(Exception ex)
		{
			_dead = true;
			try
			{
				BloomDriver.Restore();
			}
			catch
			{
			}
			((BaseUnityPlugin)this).Logger.LogError((object)("Aicho Bloom se desactivo solo para no afectar al juego: fallo " + 5 + " fotogramas seguidos. El bloom vuelve a vanilla hasta que reinicies el juego. Lo mas probable es que una actualizacion de Valheim haya cambiado algo; busca una version nueva del mod.\n" + ex));
		}

		private void Tick(float dt)
		{
			if (!_enabled.Value || _bypass)
			{
				BloomDriver.Restore();
				return;
			}
			float intensityMul = 1f;
			float thresholdAdd = 0f;
			float dirtMul = 1f;
			if (_modEnabled.Value)
			{
				ComputeModulation(BloomDriver.SampleContext(), dt, ref intensityMul, ref thresholdAdd, ref dirtMul);
			}
			else
			{
				_fWet = (_fFreezing = (_fInterior = 0f));
			}
			float num = Fin(_threshold.Value, 0.7f);
			float num2 = ((_thresholdSpace.Value == ThresholdSpace.Gamma) ? num : Mathf.LinearToGammaSpace(Mathf.Max(0f, num)));
			BloomRequest r = new BloomRequest
			{
				Mode = _mode.Value,
				Intensity = Fin(_intensity.Value, 0.3f) * intensityMul,
				ThresholdGamma = Mathf.Max(0f, num2 + thresholdAdd),
				SoftKnee = Fin(_softKnee.Value, 0.7f),
				Radius = Fin(_radius.Value, 5f),
				AntiFlicker = _antiFlicker.Value,
				DirtIntensity = Fin(_dirtIntensity.Value, 10.4f) * dirtMul,
				CustomDirt = GetCustomDirtTexture(),
				Normalize = _normalize.Value,
				ReferenceLevels = _referenceLevels.Value
			};
			if (BloomDriver.Apply(ref r, out _info) == ApplyResult.Applied)
			{
				_hasInfo = true;
			}
			if (_debugLog.Value && Time.unscaledTime >= _nextDebug)
			{
				_nextDebug = Time.unscaledTime + 5f;
				Dump();
			}
		}

		private static float Fin(float v, float fallback)
		{
			if (!float.IsNaN(v) && !float.IsInfinity(v))
			{
				return v;
			}
			return fallback;
		}

		private void ComputeModulation(GameContext ctx, float dt, ref float intensityMul, ref float thresholdAdd, ref float dirtMul)
		{
			if (ctx.HasTime)
			{
				float wDay = ctx.WDay;
				float wNight = ctx.WNight;
				float wTwilight = ctx.WTwilight;
				intensityMul = wDay * _day.Intensity.Value + wNight * _night.Intensity.Value + wTwilight * _twilight.Intensity.Value;
				thresholdAdd = wDay * _day.ThresholdOffset.Value + wNight * _night.ThresholdOffset.Value + wTwilight * _twilight.ThresholdOffset.Value;
				dirtMul = wDay * _day.Dirt.Value + wNight * _night.Dirt.Value + wTwilight * _twilight.Dirt.Value;
				_lastWDay = wDay;
				_lastWNight = wNight;
				_lastWTwi = wTwilight;
			}
			float num = ((_smoothing.Value <= 0.001f) ? 1f : (dt / _smoothing.Value));
			_fWet = Mathf.MoveTowards(_fWet, (ctx.HasWeather && ctx.Wet) ? 1f : 0f, num);
			_fFreezing = Mathf.MoveTowards(_fFreezing, (ctx.HasWeather && ctx.Freezing) ? 1f : 0f, num);
			_fInterior = Mathf.MoveTowards(_fInterior, (ctx.HasInterior && ctx.Interior) ? 1f : 0f, num);
			Overlay(_wet, _fWet, ref intensityMul, ref thresholdAdd, ref dirtMul);
			Overlay(_freezing, _fFreezing, ref intensityMul, ref thresholdAdd, ref dirtMul);
			Overlay(_interior, _fInterior, ref intensityMul, ref thresholdAdd, ref dirtMul);
		}

		private static void Overlay(ContextCfg c, float w, ref float intensityMul, ref float thresholdAdd, ref float dirtMul)
		{
			if (!(w <= 0f))
			{
				intensityMul *= Mathf.Lerp(1f, c.Intensity.Value, w);
				thresholdAdd += c.ThresholdOffset.Value * w;
				dirtMul *= Mathf.Lerp(1f, c.Dirt.Value, w);
			}
		}

		private Texture GetCustomDirtTexture()
		{
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Expected O, but got Unknown
			string text = (_dirtTexture.Value ?? string.Empty).Trim();
			if (text == _customTexKey)
			{
				return (Texture)(object)_customTex;
			}
			_customTexKey = text;
			if ((Object)(object)_customTex != (Object)null)
			{
				Object.Destroy((Object)(object)_customTex);
				_customTex = null;
			}
			if (text.Length == 0)
			{
				return null;
			}
			string text2 = ResolveFile(text);
			if (text2 == null)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("No encuentro la textura de suciedad '" + text + "'. Uso la de vanilla."));
				return null;
			}
			try
			{
				Texture2D val = new Texture2D(2, 2, (TextureFormat)4, true, false);
				if (!ImageConversion.LoadImage(val, File.ReadAllBytes(text2), false))
				{
					Object.Destroy((Object)(object)val);
					((BaseUnityPlugin)this).Logger.LogWarning((object)("No pude leer la imagen '" + Path.GetFileName(text2) + "' (usa PNG o JPG). Uso la de vanilla."));
					return null;
				}
				((Texture)val).wrapMode = (TextureWrapMode)1;
				((Texture)val).filterMode = (FilterMode)1;
				((Object)val).name = "AichoBloom_LensDirt";
				_customTex = val;
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Textura de suciedad cargada: " + Path.GetFileName(text2) + " (" + ((Texture)val).width + "x" + ((Texture)val).height + ")"));
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Error al cargar la textura de suciedad: " + ex.Message));
			}
			return (Texture)(object)_customTex;
		}

		private string ResolveFile(string path)
		{
			if (Path.IsPathRooted(path))
			{
				if (!File.Exists(path))
				{
					return null;
				}
				return path;
			}
			string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location);
			string[] array = new string[2]
			{
				Path.Combine(directoryName ?? string.Empty, path),
				Path.Combine(Paths.ConfigPath, path)
			};
			foreach (string text in array)
			{
				if (File.Exists(text))
				{
					return text;
				}
			}
			return null;
		}

		private void Dump()
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("--- Aicho Bloom 0.4.0: estado actual ---");
			stringBuilder.AppendLine("Modo: " + (_dead ? "APAGADO por modo seguro" : ((!_enabled.Value) ? "desactivado en el cfg" : (_bypass ? "bypass (vanilla por atajo)" : "activo"))));
			if (_hasInfo)
			{
				stringBuilder.AppendLine($"Intensidad aplicada {_info.Intensity:0.###} (cfg {_intensity.Value:0.###}, normalizacion x{_info.Norm:0.###}) | umbral gamma {_info.ThresholdGamma:0.###} = lineal {Mathf.GammaToLinearSpace(_info.ThresholdGamma):0.###} | suciedad {_info.Dirt:0.###}");
			}
			try
			{
				stringBuilder.AppendLine(BloomDriver.DescribeCamera(_info.Levels));
			}
			catch (Exception ex)
			{
				stringBuilder.AppendLine("Camara: no disponible (" + ex.GetType().Name + ")");
			}
			stringBuilder.AppendLine($"Hora del dia: dia {_lastWDay:0.00} noche {_lastWNight:0.00} crepusculo {_lastWTwi:0.00} | lluvia {_fWet:0.00} helado {_fFreezing:0.00} interior {_fInterior:0.00}");
			((BaseUnityPlugin)this).Logger.LogInfo((object)stringBuilder.ToString());
		}
	}
	internal enum ApplyResult
	{
		NotReady,
		Applied
	}
	internal struct BloomRequest
	{
		public BloomMode Mode;

		public float Intensity;

		public float ThresholdGamma;

		public float SoftKnee;

		public float Radius;

		public bool AntiFlicker;

		public float DirtIntensity;

		public Texture CustomDirt;

		public bool Normalize;

		public int ReferenceLevels;
	}
	internal struct AppliedInfo
	{
		public float Intensity;

		public float ThresholdGamma;

		public float Dirt;

		public float Norm;

		public int Levels;
	}
	internal struct GameContext
	{
		public bool HasTime;

		public float WDay;

		public float WNight;

		public float WTwilight;

		public bool HasWeather;

		public bool Wet;

		public bool Freezing;

		public bool HasInterior;

		public bool Interior;
	}
	internal static class BloomDriver
	{
		private static PostProcessingProfile _profile;

		private static Settings _vanilla;

		private static bool _vanillaEnabled;

		private static Camera _camera;

		private static bool _applied;

		private static bool _probed;

		private static bool _patchInstalled;

		private static CameraEffects _ceCached;

		private static PostProcessingBehaviour _ppCached;

		[MethodImpl(MethodImplOptions.NoInlining)]
		public static string GameVersion()
		{
			return Version.GetVersionString(false);
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		public static void InstallPatch(Harmony harmony)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(typeof(CameraEffects), "ApplySettings", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				AichoBloomPlugin.LogWarning("CameraEffects.ApplySettings ya no existe en esta version del juego; se aplica solo por fotograma (funciona igual).");
				return;
			}
			harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(BloomDriver), "ApplySettingsPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			_patchInstalled = true;
		}

		private static void ApplySettingsPostfix()
		{
			AichoBloomPlugin.OnGameSettingsApplied();
		}

		private static PostProcessingBehaviour FindBehaviour()
		{
			PostProcessingBehaviour val = null;
			try
			{
				val = FindViaCameraEffects();
			}
			catch (Exception ex)
			{
				AichoBloomPlugin.LogOnce("find-ce", "CameraEffects no esta disponible (" + ex.GetType().Name + "); busco el post-proceso en la camara principal.");
			}
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			Camera main = Camera.main;
			if (!((Object)(object)main != (Object)null))
			{
				return null;
			}
			return ((Component)main).GetComponent<PostProcessingBehaviour>();
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static PostProcessingBehaviour FindViaCameraEffects()
		{
			CameraEffects instance = CameraEffects.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return null;
			}
			if (instance != _ceCached)
			{
				_ceCached = instance;
				_ppCached = null;
			}
			if ((Object)(object)_ppCached == (Object)null)
			{
				_ppCached = ((Component)instance).GetComponent<PostProcessingBehaviour>();
			}
			return _ppCached;
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static bool? GameBloomFlag()
		{
			try
			{
				GraphicsSettingsManager instance = GraphicsSettingsManager.Instance;
				if ((Object)(object)instance != (Object)null)
				{
					return ((GraphicsSettingsState)instance.ActiveSettings).m_bloom;
				}
			}
			catch (Exception ex)
			{
				AichoBloomPlugin.LogOnce("gsm", "No puedo leer el interruptor de bloom del menu (" + ex.GetType().Name + "); en modo Game dejo el valor como esta.");
			}
			return null;
		}

		public static ApplyResult Apply(ref BloomRequest r, out AppliedInfo info)
		{
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			info = default(AppliedInfo);
			PostProcessingBehaviour val = FindBehaviour();
			if ((Object)(object)val == (Object)null || (Object)(object)val.profile == (Object)null)
			{
				return ApplyResult.NotReady;
			}
			PostProcessingProfile profile = val.profile;
			BloomModel bloom = profile.bloom;
			if (bloom == null)
			{
				return ApplyResult.NotReady;
			}
			if (profile != _profile)
			{
				Bind(val, profile, bloom);
			}
			bool? flag = GameBloomFlag();
			float radius = Mathf.Clamp(r.Radius, 1f, 7f);
			int num = PyramidLevels(radius);
			float num2 = 1f;
			if (r.Normalize && num > 0)
			{
				num2 = (float)Mathf.Clamp(r.ReferenceLevels, 1, 16) / (float)num;
			}
			float intensity = Mathf.Clamp(r.Intensity * num2, 0f, 100f);
			float num3 = Mathf.Clamp(r.ThresholdGamma, 0f, 50f);
			float num4 = Mathf.Clamp(r.DirtIntensity, 0f, 500f);
			Settings vanilla = _vanilla;
			vanilla.bloom.intensity = intensity;
			vanilla.bloom.threshold = num3;
			vanilla.bloom.softKnee = Mathf.Clamp01(r.SoftKnee);
			vanilla.bloom.radius = radius;
			vanilla.bloom.antiFlicker = r.AntiFlicker;
			vanilla.lensDirt.intensity = num4;
			vanilla.lensDirt.texture = (((Object)(object)r.CustomDirt != (Object)null) ? r.CustomDirt : _vanilla.lensDirt.texture);
			bloom.settings = vanilla;
			bool flag2 = r.Mode switch
			{
				BloomMode.ForceOn => true, 
				BloomMode.ForceOff => false, 
				_ => flag ?? ((PostProcessingModel)bloom).enabled, 
			};
			if (((PostProcessingModel)bloom).enabled != flag2)
			{
				((PostProcessingModel)bloom).enabled = flag2;
			}
			_applied = true;
			info.Intensity = intensity;
			info.ThresholdGamma = num3;
			info.Dirt = num4;
			info.Norm = num2;
			info.Levels = num;
			return ApplyResult.Applied;
		}

		public static void Restore()
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			if (!_applied)
			{
				return;
			}
			_applied = false;
			if (!((Object)(object)_profile == (Object)null))
			{
				BloomModel bloom = _profile.bloom;
				if (bloom != null)
				{
					bloom.settings = _vanilla;
					((PostProcessingModel)bloom).enabled = GameBloomFlag() ?? _vanillaEnabled;
				}
			}
		}

		private static void Bind(PostProcessingBehaviour pp, PostProcessingProfile profile, BloomModel model)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			_profile = profile;
			_vanilla = model.settings;
			_vanillaEnabled = ((PostProcessingModel)model).enabled;
			_camera = ((Component)pp).GetComponent<Camera>();
			_applied = false;
			if (!_probed)
			{
				_probed = true;
				BloomSettings bloom = _vanilla.bloom;
				AichoBloomPlugin.LogInfo($"Perfil de post-proceso '{((Object)profile).name}' encontrado. Valores vanilla: intensity {bloom.intensity:0.###}, threshold {bloom.threshold:0.###}, softKnee {bloom.softKnee:0.###}, radius {bloom.radius:0.###}, antiFlicker {bloom.antiFlicker}, dirt {_vanilla.lensDirt.intensity:0.###}. Espacio de color: {QualitySettings.activeColorSpace}. Camara HDR: {(Object)(object)_camera != (Object)null && _camera.allowHDR}.");
				AichoBloomPlugin.LogInfo("Comprobacion de compatibilidad: " + Probe());
			}
		}

		private static int PyramidLevels(float radius)
		{
			if ((Object)(object)_camera == (Object)null)
			{
				return 0;
			}
			return Mathf.Clamp((int)(Mathf.Log((float)Mathf.Max(1, _camera.pixelHeight / 2), 2f) + radius - 8f), 1, 16);
		}

		public static GameContext SampleContext()
		{
			GameContext c = default(GameContext);
			try
			{
				SampleTime(ref c);
			}
			catch (Exception ex)
			{
				AichoBloomPlugin.LogOnce("ctx-time", "Hora del dia no disponible (" + ex.GetType().Name + "); ignoro Day/Night/Twilight.");
			}
			try
			{
				SampleWeather(ref c);
			}
			catch (Exception ex2)
			{
				AichoBloomPlugin.LogOnce("ctx-weather", "Clima no disponible (" + ex2.GetType().Name + "); ignoro Wet/Freezing.");
			}
			try
			{
				SampleInterior(ref c);
			}
			catch (Exception ex3)
			{
				AichoBloomPlugin.LogOnce("ctx-interior", "Deteccion de interiores no disponible (" + ex3.GetType().Name + "); ignoro Interior.");
			}
			return c;
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void SampleTime(ref GameContext c)
		{
			EnvMan instance = EnvMan.instance;
			if (!((Object)(object)instance == (Object)null))
			{
				float dayFraction = instance.GetDayFraction();
				float num = Mathf.Pow(Mathf.Max(1f - Mathf.Clamp01(dayFraction / 0.25f), Mathf.Clamp01((dayFraction - 0.75f) / 0.25f)), 0.5f);
				float num2 = Mathf.Pow(Mathf.Clamp01(1f - Mathf.Abs(dayFraction - 0.5f) / 0.25f), 0.5f);
				float num3 = Mathf.Min(Mathf.Clamp01(1f - (dayFraction - 0.26f) / (0f - instance.m_sunHorizonTransitionL)), Mathf.Clamp01(1f - (dayFraction - 0.26f) / instance.m_sunHorizonTransitionH));
				float num4 = Mathf.Min(Mathf.Clamp01(1f - (dayFraction - 0.74f) / (0f - instance.m_sunHorizonTransitionH)), Mathf.Clamp01(1f - (dayFraction - 0.74f) / instance.m_sunHorizonTransitionL));
				float num5 = num + num2 + num3 + num4;
				if (num5 > 0.0001f && !float.IsNaN(num5))
				{
					c.WNight = num / num5;
					c.WDay = num2 / num5;
					c.WTwilight = (num3 + num4) / num5;
					c.HasTime = true;
				}
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void SampleWeather(ref GameContext c)
		{
			if (!((Object)(object)EnvMan.instance == (Object)null))
			{
				c.Wet = EnvMan.IsWet();
				c.Freezing = EnvMan.IsFreezing();
				c.HasWeather = true;
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void SampleInterior(ref GameContext c)
		{
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null))
			{
				c.Interior = ((Character)localPlayer).InInterior();
				c.HasInterior = true;
			}
		}

		public static string DescribeCamera(int levels)
		{
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			string text = "?";
			try
			{
				bool? flag = GameBloomFlag();
				text = (flag.HasValue ? flag.Value.ToString() : "desconocido");
			}
			catch
			{
			}
			if ((Object)(object)_camera == (Object)null)
			{
				return "Camara: no encontrada | interruptor Bloom del menu: " + text;
			}
			return $"Camara: {_camera.pixelWidth}x{_camera.pixelHeight} | niveles de piramide N = {levels} | HDR {_camera.allowHDR} | espacio de color {QualitySettings.activeColorSpace} | interruptor Bloom del menu: {text}";
		}

		private static string Probe()
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("CameraEffects=").Append(Check(ProbeCameraEffects));
			stringBuilder.Append(" | GraphicsSettingsManager=").Append(Check(ProbeGraphics));
			stringBuilder.Append(" | EnvMan=").Append(Check(ProbeEnv));
			stringBuilder.Append(" | Player=").Append(Check(ProbePlayer));
			stringBuilder.Append(" | parche Harmony=").Append(_patchInstalled ? "OK" : "no (se aplica por fotograma)");
			return stringBuilder.ToString();
		}

		private static string Check(Func<bool> test)
		{
			try
			{
				return test() ? "OK" : "aun no disponible";
			}
			catch (Exception ex)
			{
				return "FALLA (" + ex.GetType().Name + ")";
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static bool ProbeCameraEffects()
		{
			return (Object)(object)CameraEffects.instance != (Object)null;
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static bool ProbeGraphics()
		{
			GraphicsSettingsManager instance = GraphicsSettingsManager.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			bool bloom = ((GraphicsSettingsState)instance.ActiveSettings).m_bloom;
			if (!bloom)
			{
				return !bloom;
			}
			return true;
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static bool ProbeEnv()
		{
			if ((Object)(object)EnvMan.instance != (Object)null)
			{
				return EnvMan.instance.GetDayFraction() >= 0f;
			}
			return false;
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static bool ProbePlayer()
		{
			return typeof(Character).GetMethod("InInterior", Type.EmptyTypes) != null;
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		public static void Toast(string msg)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null)
			{
				((Character)localPlayer).Message((MessageType)1, msg, 0, (Sprite)null, false);
			}
		}
	}
	internal static class ConfigReset
	{
		public static int ResetAll(ConfigFile cfg, ConfigEntryBase exclude)
		{
			int num = 0;
			bool saveOnConfigSet = cfg.SaveOnConfigSet;
			cfg.SaveOnConfigSet = false;
			try
			{
				ConfigDefinition[] array = cfg.Keys.ToArray();
				foreach (ConfigDefinition val in array)
				{
					ConfigEntryBase val2 = cfg[val];
					if (val2 != exclude && !object.Equals(val2.BoxedValue, val2.DefaultValue))
					{
						val2.BoxedValue = val2.DefaultValue;
						num++;
					}
				}
				return num;
			}
			finally
			{
				cfg.SaveOnConfigSet = saveOnConfigSet;
			}
		}
	}
}