Decompiled source of CustomPalettes v1.2.0

plugins/CustomPalettes.dll

Decompiled 2 weeks 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 CustomPalettes.Data;
using GameData;
using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyFileVersion("1.2.0")]
[assembly: AssemblyInformationalVersion("1.2.0")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyVersion("1.2.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;
		}
	}
}
internal class ManifestInfo
{
	internal const string TSName = "CustomPalettes";

	internal const string TSDescription = "Add custom palettes to the game using json files and pngs!";

	internal const string TSVersion = "1.2.0";

	internal const string TSAuthor = "AuriRex";

	internal const string TSWebsite = "https://github.com/AuriRex/GTFO_CustomPalettes/";
}
namespace CustomPalettes
{
	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
	{
		[HarmonyPatch(typeof(AssetShardManager), "Setup")]
		internal static class AssetShardManager_Setup_Patch
		{
			[HarmonyPriority(600)]
			public static void Postfix()
			{
				Plugin.OnAssetShardManagerReady();
			}
		}

		[HarmonyPatch(typeof(GameDataInit), "Initialize")]
		internal static class GameDataInit_Initialize_Patch
		{
			[HarmonyPriority(600)]
			public static void Postfix()
			{
				Plugin.OnGameDataInit();
			}
		}
	}
	[BepInPlugin("dev.aurirex.gtfo.custompalettes", "Custom Palettes", "1.2.0")]
	public class Plugin : BasePlugin
	{
		public const string GUID = "dev.aurirex.gtfo.custompalettes";

		public const string MOD_NAME = "Custom Palettes";

		public const string VERSION = "1.2.0";

		private static readonly Harmony _harmony = new Harmony("dev.aurirex.gtfo.custompalettes");

		private static bool _hasInitedOnce = false;

		public override void Load()
		{
			L.Logger = ((BasePlugin)this).Log;
			_harmony.PatchAll(Assembly.GetExecutingAssembly());
			PaletteManager.Setup();
			PaletteManager.LoadPalettes();
		}

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

		internal static void OnGameDataInit()
		{
			L.Debug("GameDataInit.Initialize called.");
			if (_hasInitedOnce)
			{
				L.Warning("Reloading Custom Palettes ...");
				PaletteManager.LoadPalettes();
				TextureLoader.Setup(PaletteManager.Palettes, doCleanup: true);
				PaletteManager.InjectPalettes(forceRegeneration: true);
				PersistentInventoryManager.m_dirty = true;
			}
			_hasInitedOnce = true;
		}
	}
}
namespace CustomPalettes.Data
{
	public class CustomPalette
	{
		public string Name { get; set; } = "Custom Palette";

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

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

		public uint VanityGroupID { get; set; }

		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
			};
		}
	}
}
namespace CustomPalettes.Core
{
	public static class PaletteManager
	{
		private static string _path;

		private static readonly Dictionary<string, CustomPalette> _nameToPalette = new Dictionary<string, CustomPalette>();

		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 => "CustomPalette_".ToUpper();

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

		public static IEnumerable<CustomPalette> Palettes => _palettes;

		public static bool TryGetPaletteFromBlock(VanityItemsTemplateDataBlock block, out CustomPalette palette)
		{
			return TryGetPaletteFromId(((GameDataBlockBase<VanityItemsTemplateDataBlock>)(object)block).name, out palette);
		}

		public static bool TryGetPaletteFromId(string identifier, out CustomPalette palette)
		{
			return _nameToPalette.TryGetValue(identifier, out palette);
		}

		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"
					}
				}
			};
		}

		private static bool TryGetBlock(string name, out VanityItemsTemplateDataBlock block)
		{
			block = GameDataBlockBase<VanityItemsTemplateDataBlock>.GetBlock(GameDataBlockBase<VanityItemsTemplateDataBlock>.GetBlockID(name));
			return block != null;
		}

		internal static void InjectPalettes(bool forceRegeneration = false)
		{
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: 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}");
			_nameToPalette.Clear();
			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 (TryGetBlock(text, out var block))
					{
						L.Info($"Found existing block for \"{text}\". ({((GameDataBlockBase<VanityItemsTemplateDataBlock>)(object)block).persistentID})");
						block.prefab = GeneratePrefab(text, item.Data, forceRegeneration);
						block.publicName = item.Name;
						((GameDataBlockBase<VanityItemsTemplateDataBlock>)(object)block).internalEnabled = true;
						_nameToPalette[text] = item;
						continue;
					}
					block = new VanityItemsTemplateDataBlock();
					((GameDataBlockBase<VanityItemsTemplateDataBlock>)(object)block).name = text;
					((GameDataBlockBase<VanityItemsTemplateDataBlock>)(object)block).internalEnabled = true;
					block.DropWeight = 1f;
					block.type = (ClothesType)4;
					block.publicName = item.Name;
					block.prefab = GeneratePrefab(text, item.Data, forceRegeneration);
					_nameToPalette[text] = item;
					GameDataBlockBase<VanityItemsTemplateDataBlock>.AddBlock(block, -1);
					L.Info($"Injected new palette \"{text}\". ({((GameDataBlockBase<VanityItemsTemplateDataBlock>)(object)block).persistentID})");
					uint num = item.VanityGroupID;
					if (num == 0)
					{
						num = 36u;
					}
					if (!GameDataBlockBase<VanityItemsGroupDataBlock>.HasBlock(num))
					{
						L.Warning($"Tried to add Palette \"{item?.Name ?? "Unnamed"}\" into group with ID {num} that doesn't exist!");
					}
					else
					{
						GameDataBlockBase<VanityItemsGroupDataBlock>.GetBlock(num).Items.Add(((GameDataBlockBase<VanityItemsTemplateDataBlock>)(object)block).persistentID);
						L.Info($"Added Palette \"{item?.Name ?? "Unnamed"}\" into group with ID {num}.");
					}
				}
				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 static class TextureLoader
	{
		private static readonly Dictionary<string, TextureData> _textures = new Dictionary<string, TextureData>();

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

		private static void CleanupUnusedTextures(IEnumerable<CustomPalette> palettes)
		{
			HashSet<string> hashSet = new HashSet<string>(_textures.Keys);
			foreach (CustomPalette palette in palettes)
			{
				IEnumerable<PaletteData.Tone> enumerable = palette?.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; }
	}
}