Decompiled source of CustomPalettes v1.0.2

plugins/CustomPalettes.dll

Decompiled 3 months 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 AssetShards;
using BepInEx;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using Clonesoft.Json;
using CustomPalettes.Core;
using GameData;
using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyFileVersion("1.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("CustomPalettes")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyProduct("CustomPalettes")]
[assembly: AssemblyTitle("CustomPalettes")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace CustomPalettes
{
	[BepInPlugin("dev.aurirex.gtfo.custompalettes", "Custom Palettes", "1.0.0")]
	public class EntryPoint : BasePlugin
	{
		public const string GUID = "dev.aurirex.gtfo.custompalettes";

		public const string NAME = "Custom Palettes";

		public const string VERSION = "1.0.0";

		private Harmony _harmonyInstance;

		private static bool _hasInitedOnce;

		public override void Load()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			L.Logger = ((BasePlugin)this).Log;
			_harmonyInstance = new Harmony("dev.aurirex.gtfo.custompalettes");
			_harmonyInstance.PatchAll(Assembly.GetExecutingAssembly());
			PaletteManager.Setup();
			PaletteManager.LoadPalettes();
		}

		internal static void OnAssetShardManagerReady()
		{
			L.Debug("AssetShardManager ready, Injecting Palettes");
			TextureLoader.Setup(PaletteManager.Palletes);
			PaletteManager.InjectPalettes();
		}

		internal static void OnGameDataInit()
		{
			L.Debug("GameDataInit.Initialize called.");
			if (_hasInitedOnce)
			{
				L.Warning("Reloading Custom Palettes ...");
				PaletteManager.LoadPalettes();
				TextureLoader.Setup(PaletteManager.Palletes, doCleanup: true);
				PaletteManager.InjectPalettes(forceRegeneration: true);
				PersistentInventoryManager.m_dirty = true;
			}
			_hasInitedOnce = true;
		}
	}
	internal static class L
	{
		internal static ManualLogSource Logger { private get; set; }

		internal static void Info(string msg)
		{
			Logger.LogInfo((object)msg);
		}

		internal static void Msg(string msg)
		{
			Logger.LogMessage((object)msg);
		}

		internal static void Debug(string msg)
		{
			Logger.LogDebug((object)msg);
		}

		internal static void Warning(string msg)
		{
			Logger.LogWarning((object)msg);
		}

		internal static void Error(string msg)
		{
			Logger.LogError((object)msg);
		}

		internal static void Exception(Exception ex)
		{
			Logger.LogError((object)ex.Message);
			Logger.LogWarning((object)("StackTrace:\n" + ex.StackTrace));
		}
	}
	public static class Patches
	{
		[HarmonyPriority(600)]
		[HarmonyPatch(typeof(AssetShardManager), "Setup")]
		internal static class AssetShardManager_Setup_Patch
		{
			public static void Postfix()
			{
				EntryPoint.OnAssetShardManagerReady();
			}
		}

		[HarmonyPriority(600)]
		[HarmonyPatch(typeof(GameDataInit), "Initialize")]
		internal static class GameDataInit_Initialize_Patch
		{
			public static void Postfix()
			{
				EntryPoint.OnGameDataInit();
			}
		}
	}
}
namespace CustomPalettes.Core
{
	public class CustomPalette
	{
		public string Name { get; set; } = "Custom Palette";


		public string Author { get; set; } = string.Empty;


		public string SortingName { get; set; } = "MyCustomPalette";


		public PaletteData Data { get; set; } = new PaletteData();


		[JsonIgnore]
		public string FileName { get; internal set; } = string.Empty;

	}
	public class PaletteData
	{
		public class Tone
		{
			public string HexColor { get; set; } = "#FFFFFF";


			public string TextureFile { get; set; } = string.Empty;


			public int MaterialOverride { get; set; } = -1;

		}

		[JsonIgnore]
		private static readonly Tone _errorTone = new Tone
		{
			m_color = Color.magenta,
			m_texture = Texture2D.whiteTexture,
			m_materialOverride = -1
		};

		public Tone PrimaryTone { get; set; } = new Tone();


		public Tone SecondaryTone { get; set; } = new Tone();


		public Tone TertiaryTone { get; set; } = new Tone();


		public Tone QuaternaryTone { get; set; } = new Tone();


		public Tone QuinaryTone { get; set; } = new Tone();


		public float TextureTiling { get; set; } = 1f;


		[JsonIgnore]
		public IEnumerable<Tone> Tones
		{
			get
			{
				if (PrimaryTone != null)
				{
					yield return PrimaryTone;
				}
				if (SecondaryTone != null)
				{
					yield return SecondaryTone;
				}
				if (TertiaryTone != null)
				{
					yield return TertiaryTone;
				}
				if (QuaternaryTone != null)
				{
					yield return QuaternaryTone;
				}
				if (QuinaryTone != null)
				{
					yield return QuinaryTone;
				}
			}
		}

		internal Tone GetTone(int v)
		{
			return (Tone)(v switch
			{
				2 => GetTone(SecondaryTone), 
				3 => GetTone(TertiaryTone), 
				4 => GetTone(QuaternaryTone), 
				5 => GetTone(QuinaryTone), 
				_ => GetTone(PrimaryTone), 
			});
		}

		private static Tone GetTone(Tone tone)
		{
			//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)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			if (tone == null)
			{
				return _errorTone;
			}
			Color color = default(Color);
			if (!ColorUtility.TryParseHtmlString(tone.HexColor, ref color) && !ColorUtility.TryParseHtmlString("#" + tone.HexColor, ref color))
			{
				return _errorTone;
			}
			Texture2D texture = TextureLoader.GetTexture(tone.TextureFile);
			return new Tone
			{
				m_color = color,
				m_texture = texture,
				m_materialOverride = tone.MaterialOverride
			};
		}
	}
	public class PaletteManager
	{
		private static string _path;

		private static readonly List<CustomPalette> _palettes = new List<CustomPalette>();

		private static readonly JsonSerializerSettings _jsonSettings = new JsonSerializerSettings
		{
			Formatting = (Formatting)1
		};

		private const string TEMPLATE_FILE_NAME = "_template_palette.json";

		public static string CustomPalettesPath => _path ?? (_path = Path.Combine(Paths.BepInExRootPath, "Assets", "CustomPalettes"));

		public static string BLOCK_PREFIX => "dev.aurirex.gtfo.custompalettes_CustomPalette_".ToUpper();

		public static bool DoLoadTemplateFile { get; set; } = false;


		public static IEnumerable<CustomPalette> Palletes => _palettes;

		internal static void Setup()
		{
			if (!Directory.Exists(CustomPalettesPath))
			{
				Directory.CreateDirectory(CustomPalettesPath);
			}
			string path = Path.Combine(CustomPalettesPath, "_template_palette.json");
			if (!File.Exists(path))
			{
				string contents = JsonConvert.SerializeObject((object)GetTemplate(), _jsonSettings);
				File.WriteAllText(path, contents);
			}
		}

		internal static void LoadPalettes()
		{
			_palettes.Clear();
			L.Info("Loading Custom Palettes from [" + CustomPalettesPath + "]!");
			string[] files = Directory.GetFiles(CustomPalettesPath);
			foreach (string path in files)
			{
				string fileName = Path.GetFileName(path);
				if (Path.HasExtension(path) && !(Path.GetExtension(path) != ".json") && (!(fileName == "_template_palette.json") || DoLoadTemplateFile))
				{
					try
					{
						CustomPalette customPalette = JsonConvert.DeserializeObject<CustomPalette>(File.ReadAllText(path), _jsonSettings);
						L.Info($"Loaded Custom Palette: ({customPalette.SortingName}): {customPalette.Author} | {customPalette.Name}");
						customPalette.FileName = fileName;
						Sanitize(customPalette);
						_palettes.Add(customPalette);
					}
					catch (Exception ex)
					{
						L.Exception(ex);
					}
				}
			}
		}

		internal static void Sanitize(CustomPalette pal)
		{
			IEnumerable<PaletteData.Tone> enumerable = pal?.Data?.Tones;
			if (enumerable == null)
			{
				return;
			}
			foreach (PaletteData.Tone item in enumerable)
			{
				item.TextureFile = SanitizePath(item.TextureFile);
			}
		}

		internal static string SanitizePath(string tex)
		{
			char[] invalidPathChars = Path.GetInvalidPathChars();
			foreach (char oldChar in invalidPathChars)
			{
				tex = tex.Replace(oldChar, '_');
			}
			while (tex.Contains(".."))
			{
				tex = tex.Replace("..", "");
			}
			return tex.Replace(":", "");
		}

		private static CustomPalette GetTemplate()
		{
			return new CustomPalette
			{
				Author = "YourNameHere",
				Name = "Template Palette",
				SortingName = "UsedToSort",
				Data = new PaletteData
				{
					PrimaryTone = new PaletteData.Tone
					{
						HexColor = "#FF0000"
					},
					SecondaryTone = new PaletteData.Tone
					{
						HexColor = "#00FF00"
					},
					TertiaryTone = new PaletteData.Tone
					{
						HexColor = "#0000FF"
					},
					QuaternaryTone = new PaletteData.Tone
					{
						HexColor = "#FFFF00"
					},
					QuinaryTone = new PaletteData.Tone
					{
						HexColor = "#00FFFF"
					}
				}
			};
		}

		internal static void InjectPalettes(bool forceRegeneration = false)
		{
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Expected O, but got Unknown
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Expected O, but got Unknown
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Expected O, but got Unknown
			L.Info($"Injecting {_palettes.Count} Custom Palettes ...");
			IOrderedEnumerable<CustomPalette> orderedEnumerable = _palettes.OrderBy((CustomPalette pal) => $"{pal.Author}_{pal.SortingName}_{pal.FileName}");
			foreach (VanityItemsTemplateDataBlock allBlock in GameDataBlockBase<VanityItemsTemplateDataBlock>.GetAllBlocks())
			{
				if (((GameDataBlockBase<VanityItemsTemplateDataBlock>)(object)allBlock).name.StartsWith(BLOCK_PREFIX))
				{
					((GameDataBlockBase<VanityItemsTemplateDataBlock>)(object)allBlock).internalEnabled = false;
				}
			}
			foreach (CustomPalette item in orderedEnumerable)
			{
				try
				{
					string text = BLOCK_PREFIX + item.FileName.ToUpper();
					if (GameDataBlockBase<VanityItemsTemplateDataBlock>.HasBlock(text))
					{
						VanityItemsTemplateDataBlock block = GameDataBlockBase<VanityItemsTemplateDataBlock>.GetBlock(text);
						block.prefab = GeneratePrefab(text, item.Data, forceRegeneration);
						block.publicName = item.Name;
						((GameDataBlockBase<VanityItemsTemplateDataBlock>)(object)block).internalEnabled = true;
						continue;
					}
					VanityItemsTemplateDataBlock val = new VanityItemsTemplateDataBlock();
					((GameDataBlockBase<VanityItemsTemplateDataBlock>)val).name = text;
					((GameDataBlockBase<VanityItemsTemplateDataBlock>)val).internalEnabled = true;
					val.DropWeight = 1f;
					val.type = (ClothesType)4;
					val.publicName = item.Name;
					val.prefab = GeneratePrefab(text, item.Data, forceRegeneration);
					GameDataBlockBase<VanityItemsTemplateDataBlock>.AddBlock(val, -1);
				}
				catch (Exception ex)
				{
					L.Warning($"Failed to load Custom Palette \"{item?.Name ?? "Unnamed"}\" ({item.FileName}).");
					L.Exception(ex);
				}
			}
		}

		private static string GeneratePrefab(string identifier, PaletteData data, bool forceRegeneration = false)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Expected O, but got Unknown
			if (string.IsNullOrWhiteSpace(identifier))
			{
				throw new ArgumentException("Identifier may not be null or whitespace.", "identifier");
			}
			if (data == null)
			{
				throw new ArgumentNullException("data");
			}
			if (AssetShardManager.s_loadedAssetsLookup.ContainsKey(identifier))
			{
				if (!forceRegeneration)
				{
					return identifier;
				}
				L.Debug("Regenerating Palette prefab for \"" + identifier + "\" ...");
				Object.Destroy(AssetShardManager.s_loadedAssetsLookup[identifier]);
			}
			GameObject val = new GameObject(identifier);
			((Object)val).hideFlags = (HideFlags)61;
			Object.DontDestroyOnLoad((Object)(object)val);
			ClothesPalette obj = val.AddComponent<ClothesPalette>();
			obj.m_textureTiling = data.TextureTiling;
			obj.m_primaryTone = data.GetTone(1);
			obj.m_secondaryTone = data.GetTone(2);
			obj.m_tertiaryTone = data.GetTone(3);
			obj.m_quaternaryTone = data.GetTone(4);
			obj.m_quinaryTone = data.GetTone(5);
			AssetShardManager.s_loadedAssetsLookup[identifier] = (Object)(object)val;
			return identifier;
		}
	}
	public class TextureLoader
	{
		private static readonly Dictionary<string, TextureData> _textures = new Dictionary<string, TextureData>();

		public static void Setup(IEnumerable<CustomPalette> palletes, bool doCleanup = false)
		{
			foreach (CustomPalette pallete in palletes)
			{
				if (pallete != null)
				{
					ProcessPaletteData(pallete);
				}
			}
			if (doCleanup)
			{
				CleanupUnusedTextures(palletes);
			}
		}

		private static void CleanupUnusedTextures(IEnumerable<CustomPalette> palletes)
		{
			HashSet<string> hashSet = new HashSet<string>(_textures.Keys);
			foreach (CustomPalette pallete in palletes)
			{
				IEnumerable<PaletteData.Tone> enumerable = pallete?.Data?.Tones;
				if (enumerable == null)
				{
					continue;
				}
				foreach (PaletteData.Tone item in enumerable)
				{
					if (!string.IsNullOrWhiteSpace(item.TextureFile) && hashSet.Contains(item.TextureFile))
					{
						hashSet.Remove(item.TextureFile);
					}
				}
			}
			if (hashSet.Count == 0)
			{
				return;
			}
			L.Debug($"Found {hashSet.Count} unused Textures.");
			foreach (string item2 in hashSet)
			{
				if (_textures.TryGetValue(item2, out var value))
				{
					Object.Destroy((Object)(object)value.Texture);
					_textures.Remove(item2);
					L.Debug("Unloaded \"" + item2 + "\"!");
				}
			}
		}

		private static void ProcessPaletteData(CustomPalette pal)
		{
			PaletteData data = pal.Data;
			if (data == null)
			{
				return;
			}
			foreach (PaletteData.Tone tone in data.Tones)
			{
				string textureFile = tone.TextureFile;
				if (!string.IsNullOrWhiteSpace(textureFile) && AttemptLoad(textureFile, pal))
				{
					L.Msg("Loaded Texture \"" + textureFile + "\" for Palette " + pal.FileName);
				}
			}
		}

		private static bool AttemptLoad(string tex, CustomPalette pal)
		{
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Expected O, but got Unknown
			string path = Path.Combine(PaletteManager.CustomPalettesPath, tex);
			if (!File.Exists(path))
			{
				PrintLoadError("File doesn't exist!", tex, pal);
				return false;
			}
			DateTime lastWriteTimeUtc = File.GetLastWriteTimeUtc(path);
			if (_textures.TryGetValue(tex, out var value))
			{
				if (lastWriteTimeUtc == value.EditedTime)
				{
					return true;
				}
				Object.Destroy((Object)(object)value.Texture);
				_textures.Remove(tex);
			}
			string extension = Path.GetExtension(path);
			if (extension.ToLower() != ".png" && extension.ToLower() != ".jpg")
			{
				PrintLoadError("TextureFile is not a valid image file.", tex, pal);
				return false;
			}
			try
			{
				Texture2D val = new Texture2D(2, 2);
				byte[] array = File.ReadAllBytes(path);
				if (!ImageConversion.LoadImage(val, Il2CppStructArray<byte>.op_Implicit(array), false))
				{
					PrintLoadError("Image data can't be loaded!", tex, pal);
					Object.Destroy((Object)(object)val);
					return false;
				}
				((Object)val).hideFlags = (HideFlags)61;
				Object.DontDestroyOnLoad((Object)(object)val);
				_textures.Add(tex, new TextureData
				{
					Texture = val,
					EditedTime = lastWriteTimeUtc
				});
				return true;
			}
			catch (Exception ex)
			{
				PrintLoadError(ex.Message, tex, pal);
			}
			return false;
		}

		private static void PrintLoadError(string message, string tex, CustomPalette pal)
		{
			L.Error($"Texture \"{tex}\" could not be loaded for Palette \"{pal?.Name}\" ({pal.FileName}): {message}");
		}

		public static Texture2D GetTexture(string textureFile)
		{
			if (string.IsNullOrWhiteSpace(textureFile))
			{
				return Texture2D.whiteTexture;
			}
			if (_textures.TryGetValue(textureFile, out var value))
			{
				return value.Texture;
			}
			return Texture2D.whiteTexture;
		}
	}
	public class TextureData
	{
		public Texture2D Texture { get; internal set; }

		public DateTime EditedTime { get; internal set; }
	}
}