Decompiled source of CineLab v0.4.7

Mods/CineLab.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BoneLib.BoneMenu;
using CineLab;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using MelonLoader;
using MelonLoader.Preferences;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: MelonInfo(typeof(CineLabMod), "CineLab", "0.4.0", "CineLab contributors", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: MelonAdditionalDependencies(new string[] { "BoneLib" })]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("CineLab")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("CineLab")]
[assembly: AssemblyTitle("CineLab")]
[assembly: AssemblyVersion("1.0.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 CineLab
{
	public enum FilterChoice
	{
		Original,
		Cinematic,
		Vintage,
		ColdSteel,
		Noir,
		GoldenHour
	}
	public enum TonemapChoice
	{
		GameDefault,
		Neutral,
		ACES
	}
	public sealed class FilterPreset
	{
		public string Name;

		public Color Tint;

		public Color Shadows;

		public Color Highlights;

		public float Balance;

		public float Saturation;

		public float Contrast;

		public FilterPreset(string n, Color t, Color s, Color h, float b, float sat, float c)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			Name = n;
			Tint = t;
			Shadows = s;
			Highlights = h;
			Balance = b;
			Saturation = sat;
			Contrast = c;
		}
	}
	public sealed class CineLabMod : MelonMod
	{
		internal static MelonPreferences_Category Category;

		internal static MelonPreferences_Entry<bool> Enabled;

		internal static MelonPreferences_Entry<float> Exposure;

		internal static MelonPreferences_Entry<float> Temperature;

		internal static MelonPreferences_Entry<float> BlackBoost;

		internal static MelonPreferences_Entry<float> Contrast;

		internal static MelonPreferences_Entry<float> Saturation;

		internal static MelonPreferences_Entry<float> ShadowLift;

		internal static MelonPreferences_Entry<float> FilterIntensity;

		internal static MelonPreferences_Entry<int> Filter;

		internal static MelonPreferences_Entry<int> Tonemap;

		internal static MelonPreferences_Entry<string> LutFile;

		internal static MelonPreferences_Entry<float> LutStrength;

		internal static MelonPreferences_Entry<float> BloomIntensity;

		internal static MelonPreferences_Entry<float> BloomThreshold;

		internal static MelonPreferences_Entry<float> VignetteIntensity;

		internal static MelonPreferences_Entry<float> GrainIntensity;

		private static readonly Color White = Color.white;

		private static readonly Color Grey = new Color(0.5f, 0.5f, 0.5f, 1f);

		private static readonly FilterPreset[] Presets = new FilterPreset[6]
		{
			new FilterPreset("Original", White, Grey, Grey, 0f, 0f, 0f),
			new FilterPreset("Cinematic", new Color(1f, 0.96f, 0.9f), new Color(0.35f, 0.45f, 0.6f), new Color(0.7f, 0.55f, 0.4f), 10f, -5f, 12f),
			new FilterPreset("Vintage", new Color(1f, 0.92f, 0.78f), new Color(0.45f, 0.38f, 0.3f), new Color(0.75f, 0.62f, 0.45f), 0f, -30f, -10f),
			new FilterPreset("Cold Steel", new Color(0.88f, 0.94f, 1f), new Color(0.25f, 0.4f, 0.65f), new Color(0.55f, 0.65f, 0.8f), -10f, -15f, 10f),
			new FilterPreset("Noir", White, Grey, Grey, 0f, -100f, 30f),
			new FilterPreset("Golden Hour", new Color(1f, 0.88f, 0.7f), new Color(0.45f, 0.35f, 0.35f), new Color(0.95f, 0.65f, 0.3f), 20f, 10f, 5f)
		};

		private Page cineLabPage;

		private Page lutPage;

		private BoolElement eEnabled;

		private FloatElement eExposure;

		private FloatElement eTemp;

		private FloatElement eBlack;

		private FloatElement eContrast;

		private FloatElement eSat;

		private FloatElement eShadow;

		private FloatElement eFilterInt;

		private FloatElement eLutStrength;

		private FloatElement eBloom;

		private FloatElement eBloomThr;

		private FloatElement eVig;

		private FloatElement eGrain;

		private EnumElement eFilter;

		private EnumElement eTonemap;

		private GameObject volumeObj;

		private Volume volume;

		private VolumeProfile profile;

		private ColorAdjustments colorAdj;

		private WhiteBalance wb;

		private LiftGammaGain lgg;

		private SplitToning split;

		private ColorLookup lookup;

		private Tonemapping tonemap;

		private Bloom bloom;

		private Vignette vignette;

		private FilmGrain grain;

		private Texture2D currentLut;

		private readonly Dictionary<string, Texture2D> lutCache = new Dictionary<string, Texture2D>();

		private string lutDir;

		private bool lutRestorePending = true;

		private bool logLutState;

		private float nextApply;

		private float nextCamCheck;

		private bool loggedPipeline;

		public override void OnInitializeMelon()
		{
			Category = MelonPreferences.CreateCategory("CineLab", "CineLab Color Grading");
			Enabled = Category.CreateEntry<bool>("Enabled", false, "Enable effect (starts disabled)", (string)null, false, false, (ValueValidator)null, (string)null);
			Exposure = Category.CreateEntry<float>("ExposureEV", 0f, "Exposure in EV stops", (string)null, false, false, (ValueValidator)null, (string)null);
			Temperature = Category.CreateEntry<float>("Temperature", 0f, "Cool (-1) to warm (+1)", (string)null, false, false, (ValueValidator)null, (string)null);
			BlackBoost = Category.CreateEntry<float>("BlackBoost", 0f, "Lift dark values (0-1)", (string)null, false, false, (ValueValidator)null, (string)null);
			Contrast = Category.CreateEntry<float>("Contrast", 1.1f, "Contrast multiplier", (string)null, false, false, (ValueValidator)null, (string)null);
			Saturation = Category.CreateEntry<float>("Saturation", 1.15f, "Saturation multiplier", (string)null, false, false, (ValueValidator)null, (string)null);
			ShadowLift = Category.CreateEntry<float>("ShadowLift", 0f, "Additional shadow lift (0-0.3)", (string)null, false, false, (ValueValidator)null, (string)null);
			Filter = Category.CreateEntry<int>("Filter", 1, "0 Original, 1 Cinematic, 2 Vintage, 3 Cold Steel, 4 Noir, 5 Golden Hour", (string)null, false, false, (ValueValidator)null, (string)null);
			FilterIntensity = Category.CreateEntry<float>("FilterIntensity", 0.65f, "Filter mix (0-1)", (string)null, false, false, (ValueValidator)null, (string)null);
			LutFile = Category.CreateEntry<string>("LutFile", "", "File name of the selected LUT in UserData/CineLab/LUTs (empty = none)", (string)null, false, false, (ValueValidator)null, (string)null);
			LutStrength = Category.CreateEntry<float>("LutStrength", 1f, "LUT strength (0-1)", (string)null, false, false, (ValueValidator)null, (string)null);
			Tonemap = Category.CreateEntry<int>("Tonemapping", 0, "0 = leave the game's own, 1 = Neutral, 2 = ACES", (string)null, false, false, (ValueValidator)null, (string)null);
			BloomIntensity = Category.CreateEntry<float>("BloomIntensity", 0f, "Bloom (0 = off, leaves the game's own bloom alone)", (string)null, false, false, (ValueValidator)null, (string)null);
			BloomThreshold = Category.CreateEntry<float>("BloomThreshold", 0.5f, "Bloom brightness threshold", (string)null, false, false, (ValueValidator)null, (string)null);
			VignetteIntensity = Category.CreateEntry<float>("VignetteIntensity", 0f, "Vignette (0 = off). Can be uncomfortable in VR.", (string)null, false, false, (ValueValidator)null, (string)null);
			GrainIntensity = Category.CreateEntry<float>("FilmGrain", 0f, "Film grain (0 = off)", (string)null, false, false, (ValueValidator)null, (string)null);
			SetupLutFolder();
			BuildMenu();
		}

		private void BuildMenu()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: 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)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0231: Unknown result type (might be due to invalid IL or missing references)
			//IL_027f: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_031f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0333: Unknown result type (might be due to invalid IL or missing references)
			//IL_0386: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_0439: Unknown result type (might be due to invalid IL or missing references)
			//IL_0462: Unknown result type (might be due to invalid IL or missing references)
			//IL_0476: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_050f: Unknown result type (might be due to invalid IL or missing references)
			//IL_055d: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_05fd: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				cineLabPage = Page.Root.CreatePage("CineLab", Color.white, 0, true);
				eEnabled = cineLabPage.CreateBool("Enable Color Grading", Color.white, Enabled.Value, (Action<bool>)delegate(bool v)
				{
					Enabled.Value = v;
				});
				Page val = cineLabPage.CreatePage("Presets", Color.yellow, 0, true);
				val.CreateFunction("Cinematic", Color.white, (Action)delegate
				{
					ApplyPreset(0);
				});
				val.CreateFunction("Vivid", Color.white, (Action)delegate
				{
					ApplyPreset(1);
				});
				val.CreateFunction("Moody", Color.white, (Action)delegate
				{
					ApplyPreset(2);
				});
				val.CreateFunction("Retro Film", Color.white, (Action)delegate
				{
					ApplyPreset(3);
				});
				val.CreateFunction("Golden Hour", Color.white, (Action)delegate
				{
					ApplyPreset(4);
				});
				val.CreateFunction("Reset / Bypass", Color.red, (Action)ResetSettings);
				Page val2 = cineLabPage.CreatePage("Color", Color.white, 0, true);
				eExposure = val2.CreateFloat("Exposure", Color.white, Exposure.Value, 0.1f, -3f, 3f, (Action<float>)delegate(float v)
				{
					Exposure.Value = v;
				});
				eTemp = val2.CreateFloat("Warm / Cool", Color.white, Temperature.Value, 0.05f, -1f, 1f, (Action<float>)delegate(float v)
				{
					Temperature.Value = v;
				});
				eBlack = val2.CreateFloat("Black Boost", Color.white, BlackBoost.Value, 0.05f, 0f, 1f, (Action<float>)delegate(float v)
				{
					BlackBoost.Value = v;
				});
				eShadow = val2.CreateFloat("Shadow Lift", Color.white, ShadowLift.Value, 0.01f, 0f, 0.3f, (Action<float>)delegate(float v)
				{
					ShadowLift.Value = v;
				});
				eContrast = val2.CreateFloat("Contrast", Color.white, Contrast.Value, 0.05f, 0.5f, 1.8f, (Action<float>)delegate(float v)
				{
					Contrast.Value = v;
				});
				eSat = val2.CreateFloat("Saturation", Color.white, Saturation.Value, 0.05f, 0f, 2f, (Action<float>)delegate(float v)
				{
					Saturation.Value = v;
				});
				Page val3 = cineLabPage.CreatePage("Filter & LUT", Color.cyan, 0, true);
				eFilter = val3.CreateEnum("Filter", Color.cyan, (Enum)(FilterChoice)Mathf.Clamp(Filter.Value, 0, Presets.Length - 1), (Action<Enum>)delegate(Enum v)
				{
					Filter.Value = (int)(FilterChoice)(object)v;
				});
				eFilterInt = val3.CreateFloat("Filter Intensity", Color.cyan, FilterIntensity.Value, 0.05f, 0f, 1f, (Action<float>)delegate(float v)
				{
					FilterIntensity.Value = v;
				});
				lutPage = val3.CreatePage("Choose LUT", Color.cyan, 0, true);
				eLutStrength = val3.CreateFloat("LUT Strength", Color.cyan, LutStrength.Value, 0.05f, 0f, 1f, (Action<float>)delegate(float v)
				{
					LutStrength.Value = v;
				});
				val3.CreateFunction("Rescan LUT Folder", Color.cyan, (Action)RescanLuts);
				RebuildLutMenu();
				Page val4 = cineLabPage.CreatePage("Effects", Color.magenta, 0, true);
				eTonemap = val4.CreateEnum("Tonemapping", Color.white, (Enum)(TonemapChoice)Mathf.Clamp(Tonemap.Value, 0, 2), (Action<Enum>)delegate(Enum v)
				{
					Tonemap.Value = (int)(TonemapChoice)(object)v;
				});
				eBloom = val4.CreateFloat("Bloom", Color.white, BloomIntensity.Value, 0.05f, 0f, 2f, (Action<float>)delegate(float v)
				{
					BloomIntensity.Value = v;
				});
				eBloomThr = val4.CreateFloat("Bloom Threshold", Color.white, BloomThreshold.Value, 0.05f, 0.3f, 2f, (Action<float>)delegate(float v)
				{
					BloomThreshold.Value = v;
				});
				eVig = val4.CreateFloat("Vignette", Color.white, VignetteIntensity.Value, 0.05f, 0f, 0.6f, (Action<float>)delegate(float v)
				{
					VignetteIntensity.Value = v;
				});
				eGrain = val4.CreateFloat("Film Grain", Color.white, GrainIntensity.Value, 0.05f, 0f, 1f, (Action<float>)delegate(float v)
				{
					GrainIntensity.Value = v;
				});
				cineLabPage.CreateFunction("Save Settings", Color.green, (Action)delegate
				{
					MelonPreferences.Save();
					MelonLogger.Msg("CineLab: settings saved.");
				});
				MelonLogger.Msg("CineLab BoneMenu page created.");
			}
			catch (Exception ex)
			{
				MelonLogger.Error("Could not create CineLab BoneMenu page: " + ex);
			}
		}

		private void SyncMenu()
		{
			try
			{
				if (eEnabled != null)
				{
					eEnabled.Value = Enabled.Value;
					eExposure.Value = Exposure.Value;
					eTemp.Value = Temperature.Value;
					eBlack.Value = BlackBoost.Value;
					eShadow.Value = ShadowLift.Value;
					eContrast.Value = Contrast.Value;
					eSat.Value = Saturation.Value;
					eFilter.Value = (FilterChoice)Mathf.Clamp(Filter.Value, 0, Presets.Length - 1);
					eFilterInt.Value = FilterIntensity.Value;
					eLutStrength.Value = LutStrength.Value;
					eTonemap.Value = (TonemapChoice)Mathf.Clamp(Tonemap.Value, 0, 2);
					eBloom.Value = BloomIntensity.Value;
					eBloomThr.Value = BloomThreshold.Value;
					eVig.Value = VignetteIntensity.Value;
					eGrain.Value = GrainIntensity.Value;
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("CineLab: menu sync failed: " + ex.Message);
			}
		}

		public override void OnApplicationQuit()
		{
			MelonPreferences.Save();
		}

		public override void OnSceneWasInitialized(int buildIndex, string sceneName)
		{
			if (!loggedPipeline)
			{
				loggedPipeline = true;
				RenderPipelineAsset currentRenderPipeline = GraphicsSettings.currentRenderPipeline;
				MelonLogger.Msg("CineLab: render pipeline = " + (((Object)(object)currentRenderPipeline == (Object)null) ? "NONE (Built-in)" : (((object)currentRenderPipeline).GetType().Name + " / " + ((Object)currentRenderPipeline).name)));
			}
			EnsureVolume();
			EnableCameraPostProcessing();
			if (lutRestorePending)
			{
				lutRestorePending = false;
				RestoreSavedLut();
			}
		}

		public override void OnUpdate()
		{
			float unscaledTime = Time.unscaledTime;
			if (unscaledTime >= nextCamCheck)
			{
				nextCamCheck = unscaledTime + 2f;
				EnsureVolume();
				EnableCameraPostProcessing();
			}
			if (unscaledTime >= nextApply)
			{
				nextApply = unscaledTime + 0.05f;
				Apply();
			}
		}

		private void EnsureVolume()
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			if (!((Object)(object)volumeObj != (Object)null) || !((Object)(object)volume != (Object)null))
			{
				volumeObj = new GameObject("CineLab_Volume");
				Object.DontDestroyOnLoad((Object)(object)volumeObj);
				((Object)volumeObj).hideFlags = (HideFlags)61;
				volume = volumeObj.AddComponent<Volume>();
				volume.isGlobal = true;
				volume.priority = 1000f;
				volume.weight = 1f;
				profile = ScriptableObject.CreateInstance<VolumeProfile>();
				volume.sharedProfile = profile;
				colorAdj = profile.Add<ColorAdjustments>(true);
				wb = profile.Add<WhiteBalance>(true);
				lgg = profile.Add<LiftGammaGain>(true);
				split = profile.Add<SplitToning>(true);
				lookup = profile.Add<ColorLookup>(true);
				tonemap = profile.Add<Tonemapping>(true);
				bloom = profile.Add<Bloom>(true);
				vignette = profile.Add<Vignette>(true);
				grain = profile.Add<FilmGrain>(true);
			}
		}

		private void EnableCameraPostProcessing()
		{
			try
			{
				foreach (Camera item in (Il2CppArrayBase<Camera>)(object)Camera.allCameras)
				{
					UniversalAdditionalCameraData universalAdditionalCameraData = CameraExtensions.GetUniversalAdditionalCameraData(item);
					if ((Object)(object)universalAdditionalCameraData != (Object)null && !universalAdditionalCameraData.renderPostProcessing)
					{
						universalAdditionalCameraData.renderPostProcessing = true;
					}
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("CineLab: camera post-processing flag failed: " + ex.Message);
			}
		}

		private void Apply()
		{
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)volume == (Object)null)
			{
				return;
			}
			((Behaviour)volume).enabled = Enabled.Value;
			if (!Enabled.Value)
			{
				return;
			}
			FilterPreset filterPreset = Presets[Mathf.Clamp(Filter.Value, 0, Presets.Length - 1)];
			float num = Mathf.Clamp01(FilterIntensity.Value);
			((VolumeParameter<float>)(object)colorAdj.postExposure).Override(Exposure.Value);
			((VolumeParameter<float>)(object)colorAdj.contrast).Override(Mathf.Clamp((Contrast.Value - 1f) * 100f + filterPreset.Contrast * num, -100f, 100f));
			((VolumeParameter<float>)(object)colorAdj.saturation).Override(Mathf.Clamp((Saturation.Value - 1f) * 100f + filterPreset.Saturation * num, -100f, 100f));
			((VolumeParameter<Color>)(object)colorAdj.colorFilter).Override(Color.Lerp(White, filterPreset.Tint, num));
			((VolumeParameter<float>)(object)wb.temperature).Override(Mathf.Clamp(Temperature.Value, -1f, 1f) * 100f);
			float num2 = Mathf.Clamp01(BlackBoost.Value) * 0.15f + Mathf.Clamp(ShadowLift.Value, 0f, 0.3f) * 0.3f;
			((VolumeParameter<Vector4>)(object)lgg.lift).Override(new Vector4(1f, 1f, 1f, num2));
			((VolumeParameter<Color>)(object)split.shadows).Override(Color.Lerp(Grey, filterPreset.Shadows, num));
			((VolumeParameter<Color>)(object)split.highlights).Override(Color.Lerp(Grey, filterPreset.Highlights, num));
			((VolumeParameter<float>)(object)split.balance).Override(filterPreset.Balance * num);
			if ((Object)(object)currentLut != (Object)null)
			{
				((VolumeComponent)lookup).active = true;
				((VolumeParameter<Texture>)(object)lookup.texture).Override((Texture)(object)currentLut);
				((VolumeParameter<float>)(object)lookup.contribution).Override(Mathf.Clamp01(LutStrength.Value));
				if (logLutState)
				{
					logLutState = false;
					MelonLogger.Msg("CineLab: LUT texture " + ((Texture)currentLut).width + "x" + ((Texture)currentLut).height + ", URP asset LUT size " + TargetLutSize() + ", ColorLookup active = " + lookup.IsActive());
				}
			}
			else
			{
				((VolumeComponent)lookup).active = false;
			}
			TonemapChoice tonemapChoice = (TonemapChoice)Mathf.Clamp(Tonemap.Value, 0, 2);
			((VolumeComponent)tonemap).active = tonemapChoice != TonemapChoice.GameDefault;
			if (((VolumeComponent)tonemap).active)
			{
				((VolumeParameter<TonemappingMode>)(object)tonemap.mode).Override((TonemappingMode)((tonemapChoice != TonemapChoice.ACES) ? 1 : 2));
			}
			((VolumeComponent)bloom).active = BloomIntensity.Value > 0.001f;
			if (((VolumeComponent)bloom).active)
			{
				((VolumeParameter<float>)(object)bloom.intensity).Override(BloomIntensity.Value);
				((VolumeParameter<float>)(object)bloom.threshold).Override(BloomThreshold.Value);
				((VolumeParameter<float>)(object)bloom.scatter).Override(0.7f);
			}
			((VolumeComponent)vignette).active = VignetteIntensity.Value > 0.001f;
			if (((VolumeComponent)vignette).active)
			{
				((VolumeParameter<float>)(object)vignette.intensity).Override(VignetteIntensity.Value);
				((VolumeParameter<float>)(object)vignette.smoothness).Override(0.4f);
			}
			((VolumeComponent)grain).active = GrainIntensity.Value > 0.001f;
			if (((VolumeComponent)grain).active)
			{
				((VolumeParameter<FilmGrainLookup>)(object)grain.type).Override((FilmGrainLookup)2);
				((VolumeParameter<float>)(object)grain.intensity).Override(GrainIntensity.Value);
				((VolumeParameter<float>)(object)grain.response).Override(0.8f);
			}
		}

		private void SetLook(float exp, float temp, float black, float contrast, float sat, float shadow, FilterChoice filter, float filterInt, TonemapChoice tm, float bloomI, float grainI)
		{
			Enabled.Value = true;
			Exposure.Value = exp;
			Temperature.Value = temp;
			BlackBoost.Value = black;
			Contrast.Value = contrast;
			Saturation.Value = sat;
			ShadowLift.Value = shadow;
			Filter.Value = (int)filter;
			FilterIntensity.Value = filterInt;
			Tonemap.Value = (int)tm;
			BloomIntensity.Value = bloomI;
			BloomThreshold.Value = 0.5f;
			VignetteIntensity.Value = 0f;
			GrainIntensity.Value = grainI;
			SyncMenu();
		}

		private void ApplyPreset(int id)
		{
			switch (id)
			{
			case 0:
				SetLook(0f, 0.08f, 0.08f, 1.12f, 1.18f, 0.02f, FilterChoice.Cinematic, 0.65f, TonemapChoice.ACES, 0.15f, 0.06f);
				break;
			case 1:
				SetLook(0.1f, 0f, 0f, 1.15f, 1.35f, 0f, FilterChoice.Original, 0f, TonemapChoice.Neutral, 0.1f, 0f);
				break;
			case 2:
				SetLook(-0.3f, -0.1f, 0.05f, 1.2f, 0.85f, 0.02f, FilterChoice.ColdSteel, 0.5f, TonemapChoice.ACES, 0.2f, 0.1f);
				break;
			case 3:
				SetLook(0.1f, 0.15f, 0.15f, 0.95f, 0.9f, 0.04f, FilterChoice.Vintage, 0.7f, TonemapChoice.Neutral, 0.1f, 0.3f);
				break;
			case 4:
				SetLook(0.1f, 0.25f, 0.05f, 1.1f, 1.2f, 0.02f, FilterChoice.GoldenHour, 0.6f, TonemapChoice.ACES, 0.25f, 0.05f);
				break;
			}
			MelonLogger.Msg("CineLab: preset applied.");
		}

		private void ResetSettings()
		{
			Enabled.Value = false;
			Exposure.Value = 0f;
			Temperature.Value = 0f;
			BlackBoost.Value = 0f;
			Contrast.Value = 1f;
			Saturation.Value = 1f;
			ShadowLift.Value = 0f;
			Filter.Value = 0;
			FilterIntensity.Value = 0f;
			Tonemap.Value = 0;
			BloomIntensity.Value = 0f;
			BloomThreshold.Value = 0.5f;
			VignetteIntensity.Value = 0f;
			GrainIntensity.Value = 0f;
			SelectLut(null);
			SyncMenu();
		}

		private void SetupLutFolder()
		{
			try
			{
				lutDir = Path.Combine(MelonEnvironment.UserDataDirectory, "CineLab", "LUTs");
				Directory.CreateDirectory(lutDir);
				string path = Path.Combine(lutDir, "README.txt");
				if (!File.Exists(path))
				{
					File.WriteAllText(path, "Drop LUT files (.cube, or strip images .png/.jpg) in this folder, then use\nCineLab > Rescan LUT Folder in BoneMenu, or restart the game.\n\nImage format: a horizontal strip of N tiles, each N x N pixels, so the image is\nN*N wide and N tall. Common sizes: 256x16, 1024x32, 4096x64.\nThis is the same strip format Unity's URP Color Lookup uses.\n.cube files (3D, LUT_3D_SIZE 2-90) are converted automatically. 1D .cube files and square-grid images are not supported.\n");
				}
				MelonLogger.Msg("CineLab LUT folder: " + lutDir);
			}
			catch (Exception ex)
			{
				MelonLogger.Error("CineLab: could not create LUT folder: " + ex.Message);
			}
		}

		private string[] ListLutFiles()
		{
			List<string> list = new List<string>();
			try
			{
				if (lutDir != null && Directory.Exists(lutDir))
				{
					list.AddRange(Directory.GetFiles(lutDir, "*.cube"));
					list.AddRange(Directory.GetFiles(lutDir, "*.png"));
					list.AddRange(Directory.GetFiles(lutDir, "*.jpg"));
					list.AddRange(Directory.GetFiles(lutDir, "*.jpeg"));
					list.Sort(StringComparer.OrdinalIgnoreCase);
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("CineLab: could not list LUT folder: " + ex.Message);
			}
			return list.ToArray();
		}

		private void RebuildLutMenu()
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			if (lutPage == null)
			{
				return;
			}
			try
			{
				lutPage.RemoveAll();
				lutPage.CreateFunction("None (LUT off)", Color.gray, (Action)delegate
				{
					SelectLut(null);
				});
				string[] array = ListLutFiles();
				foreach (string text in array)
				{
					string path = text;
					lutPage.CreateFunction(Path.GetFileName(path), Color.white, (Action)delegate
					{
						SelectLut(path);
					});
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("CineLab: LUT menu rebuild failed: " + ex.Message);
			}
		}

		private void RescanLuts()
		{
			currentLut = null;
			foreach (KeyValuePair<string, Texture2D> item in lutCache)
			{
				if ((Object)(object)item.Value != (Object)null)
				{
					Object.Destroy((Object)(object)item.Value);
				}
			}
			lutCache.Clear();
			RebuildLutMenu();
			RestoreSavedLut();
			MelonLogger.Msg("CineLab: rescanned LUT folder (" + ListLutFiles().Length + " file(s)).");
		}

		private void RestoreSavedLut()
		{
			string value = LutFile.Value;
			if (!string.IsNullOrEmpty(value) && lutDir != null)
			{
				string path = Path.Combine(lutDir, value);
				if (File.Exists(path))
				{
					SelectLut(path);
				}
				else
				{
					MelonLogger.Warning("CineLab: saved LUT not found: " + value);
				}
			}
		}

		private void SelectLut(string path)
		{
			if (string.IsNullOrEmpty(path))
			{
				currentLut = null;
				LutFile.Value = "";
				MelonLogger.Msg("CineLab: LUT off.");
				return;
			}
			Texture2D lut = GetLut(path);
			if (!((Object)(object)lut == (Object)null))
			{
				currentLut = lut;
				logLutState = true;
				LutFile.Value = Path.GetFileName(path);
				MelonLogger.Msg("CineLab: LUT selected: " + LutFile.Value);
			}
		}

		private static int TargetLutSize()
		{
			try
			{
				UniversalRenderPipelineAsset asset = UniversalRenderPipeline.asset;
				if ((Object)(object)asset != (Object)null)
				{
					return asset.colorGradingLutSize;
				}
			}
			catch (Exception)
			{
			}
			return 32;
		}

		private Texture2D GetLut(string path)
		{
			if (lutCache.TryGetValue(path, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			try
			{
				string fileName = Path.GetFileName(path);
				int n;
				float[] array = (path.EndsWith(".cube", StringComparison.OrdinalIgnoreCase) ? LoadCube(path, out n) : LoadStrip(path, out n));
				if (array == null)
				{
					return null;
				}
				int num = TargetLutSize();
				if (n != num)
				{
					array = Resample(array, n, num);
					MelonLogger.Msg("CineLab: " + fileName + " resampled from size " + n + " to " + num + " (URP asset LUT size).");
				}
				Texture2D val = BuildStrip(array, num);
				((Texture)val).filterMode = (FilterMode)1;
				((Texture)val).wrapMode = (TextureWrapMode)1;
				((Texture)val).anisoLevel = 0;
				((Object)val).hideFlags = (HideFlags)61;
				Object.DontDestroyOnLoad((Object)(object)val);
				lutCache[path] = val;
				return val;
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("CineLab: failed to load LUT " + Path.GetFileName(path) + ": " + ex.Message);
				return null;
			}
		}

		private static int Idx(int n, int r, int g, int b)
		{
			return (r + n * (g + n * b)) * 3;
		}

		private static float[] Resample(float[] src, int n, int t)
		{
			float[] array = new float[t * t * t * 3];
			float num = ((float)n - 1f) / ((float)t - 1f);
			for (int i = 0; i < t; i++)
			{
				for (int j = 0; j < t; j++)
				{
					for (int k = 0; k < t; k++)
					{
						float num2 = (float)k * num;
						float num3 = (float)j * num;
						float num4 = (float)i * num;
						int num5 = Mathf.Min((int)num2, n - 2);
						int num6 = Mathf.Min((int)num3, n - 2);
						int num7 = Mathf.Min((int)num4, n - 2);
						int r = num5 + 1;
						int g = num6 + 1;
						int b = num7 + 1;
						float num8 = num2 - (float)num5;
						float num9 = num3 - (float)num6;
						float num10 = num4 - (float)num7;
						for (int l = 0; l < 3; l++)
						{
							float num11 = Mathf.Lerp(src[Idx(n, num5, num6, num7) + l], src[Idx(n, r, num6, num7) + l], num8);
							float num12 = Mathf.Lerp(src[Idx(n, num5, g, num7) + l], src[Idx(n, r, g, num7) + l], num8);
							float num13 = Mathf.Lerp(src[Idx(n, num5, num6, b) + l], src[Idx(n, r, num6, b) + l], num8);
							float num14 = Mathf.Lerp(src[Idx(n, num5, g, b) + l], src[Idx(n, r, g, b) + l], num8);
							array[Idx(t, k, j, i) + l] = Mathf.Lerp(Mathf.Lerp(num11, num12, num9), Mathf.Lerp(num13, num14, num9), num10);
						}
					}
				}
			}
			return array;
		}

		private static Texture2D BuildStrip(float[] data, int size)
		{
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Expected O, but got Unknown
			int num = size * size;
			byte[] array = new byte[num * size * 4];
			for (int i = 0; i < size; i++)
			{
				for (int j = 0; j < size; j++)
				{
					for (int k = 0; k < size; k++)
					{
						int num2 = Idx(size, k, j, i);
						int num3 = (j * num + i * size + k) * 4;
						array[num3] = ToByte(data[num2]);
						array[num3 + 1] = ToByte(data[num2 + 1]);
						array[num3 + 2] = ToByte(data[num2 + 2]);
						array[num3 + 3] = byte.MaxValue;
					}
				}
			}
			Texture2D val = new Texture2D(num, size, (TextureFormat)4, false, true);
			Il2CppStructArray<byte> val2 = Il2CppStructArray<byte>.op_Implicit(array);
			val.LoadRawTextureData(val2);
			val.Apply(false, false);
			return val;
		}

		private float[] LoadStrip(string path, out int n)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			//IL_012c: 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_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			n = 0;
			byte[] array = File.ReadAllBytes(path);
			Il2CppStructArray<byte> val = Il2CppStructArray<byte>.op_Implicit(array);
			Texture2D val2 = new Texture2D(2, 2, (TextureFormat)4, false, true);
			try
			{
				if (!ImageConversion.LoadImage(val2, val, false))
				{
					MelonLogger.Warning("CineLab: could not decode image: " + Path.GetFileName(path));
					return null;
				}
				if (((Texture)val2).height < 2 || ((Texture)val2).width != ((Texture)val2).height * ((Texture)val2).height)
				{
					MelonLogger.Warning("CineLab: " + Path.GetFileName(path) + " is " + ((Texture)val2).width + "x" + ((Texture)val2).height + ", not a LUT strip (width must equal height x height, e.g. 1024x32). Skipped.");
					return null;
				}
				n = ((Texture)val2).height;
				int width = ((Texture)val2).width;
				Il2CppStructArray<Color32> pixels = val2.GetPixels32();
				float[] array2 = new float[n * n * n * 3];
				for (int i = 0; i < n; i++)
				{
					for (int j = 0; j < n; j++)
					{
						for (int k = 0; k < n; k++)
						{
							Color32 val3 = ((Il2CppArrayBase<Color32>)(object)pixels)[j * width + i * n + k];
							int num = Idx(n, k, j, i);
							array2[num] = (float)(int)val3.r / 255f;
							array2[num + 1] = (float)(int)val3.g / 255f;
							array2[num + 2] = (float)(int)val3.b / 255f;
						}
					}
				}
				return array2;
			}
			finally
			{
				Object.Destroy((Object)(object)val2);
			}
		}

		private static float ParseF(string s)
		{
			return float.Parse(s, NumberStyles.Float, CultureInfo.InvariantCulture);
		}

		private static byte ToByte(float v)
		{
			return (byte)Mathf.RoundToInt(Mathf.Clamp01(v) * 255f);
		}

		private float[] LoadCube(string path, out int n)
		{
			n = 0;
			string fileName = Path.GetFileName(path);
			int num = 0;
			float[] array = null;
			int num2 = 0;
			bool flag = false;
			char[] separator = null;
			foreach (string item in File.ReadLines(path))
			{
				string text = item.Trim();
				if (text.Length == 0 || text[0] == '#')
				{
					continue;
				}
				string[] array2 = text.Split(separator, StringSplitOptions.RemoveEmptyEntries);
				if (char.IsLetter(text[0]))
				{
					string text2 = array2[0].ToUpperInvariant();
					switch (text2)
					{
					case "LUT_3D_SIZE":
					{
						if (array2.Length < 2 || !int.TryParse(array2[1], out var result) || result < 2 || result > 90)
						{
							MelonLogger.Warning("CineLab: " + fileName + " has an unsupported LUT_3D_SIZE (need 2-90). Skipped.");
							return null;
						}
						num = result;
						array = new float[num * num * num * 3];
						break;
					}
					case "LUT_1D_SIZE":
						MelonLogger.Warning("CineLab: " + fileName + " is a 1D LUT, which is not supported. Skipped.");
						return null;
					case "DOMAIN_MIN":
					case "DOMAIN_MAX":
						if (array2.Length >= 4 && !flag)
						{
							float num3 = ((text2 == "DOMAIN_MIN") ? 0f : 1f);
							if (Mathf.Abs(ParseF(array2[1]) - num3) > 0.001f)
							{
								flag = true;
								MelonLogger.Warning("CineLab: " + fileName + " has a non-standard DOMAIN; it is ignored, so colors may look off.");
							}
						}
						break;
					}
					continue;
				}
				if (array == null)
				{
					MelonLogger.Warning("CineLab: " + fileName + " has data before LUT_3D_SIZE. Skipped.");
					return null;
				}
				if (array2.Length >= 3)
				{
					if (num2 >= num * num * num)
					{
						break;
					}
					array[num2 * 3] = ParseF(array2[0]);
					array[num2 * 3 + 1] = ParseF(array2[1]);
					array[num2 * 3 + 2] = ParseF(array2[2]);
					num2++;
				}
			}
			if (array == null || num2 != num * num * num)
			{
				MelonLogger.Warning("CineLab: " + fileName + " is incomplete (expected " + num * num * num + " rows, found " + num2 + "). Skipped.");
				return null;
			}
			n = num;
			return array;
		}
	}
}