Decompiled source of GK2LocalizationAPI v1.0.0

BepInEx/plugins/GraveyardKeeper2LocalizationAPI/GraveyardKeeper2LocalizationAPI.dll

Decompiled 7 hours 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.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GraveyardKeeper2LocalizationAPI.Api;
using HarmonyLib;
using LazyBearTechnology;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("GraveyardKeeper2LocalizationAPI")]
[assembly: AssemblyDescription("Graveyard Keeper 2 Localization API mod for Graveyard Keeper 2 by Ice Box Studio")]
[assembly: AssemblyCompany("Ice Box Studio")]
[assembly: AssemblyProduct("GraveyardKeeper2LocalizationAPI")]
[assembly: AssemblyCopyright("Copyright © 2026 Ice Box Studio All rights reserved.")]
[assembly: ComVisible(false)]
[assembly: Guid("b1a645ec-4739-4dbd-86a4-2861a957efb5")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace GraveyardKeeper2LocalizationAPI
{
	[BepInPlugin("IceBoxStudio.GraveyardKeeper2.LocalizationAPI", "GraveyardKeeper2LocalizationAPI", "1.0.0")]
	public sealed class GraveyardKeeper2LocalizationAPI : BaseUnityPlugin
	{
		internal static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			Log = ((BaseUnityPlugin)this).Logger;
			LocalizationApi.LoadSavedLanguage();
			GameSettings.OnLanguageChanged += OnLanguageChanged;
			Log.LogInfo((object)"GraveyardKeeper2LocalizationAPI 1.0.0 initialized.");
		}

		private static void OnLanguageChanged()
		{
			LocalizationApi.NotifyLanguageChanged(LLBase.CurrentLang);
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "IceBoxStudio.GraveyardKeeper2.LocalizationAPI";

		public const string PLUGIN_NAME = "GraveyardKeeper2LocalizationAPI";

		public const string PLUGIN_VERSION = "1.0.0";
	}
}
namespace GraveyardKeeper2LocalizationAPI.Api
{
	internal sealed class ConfigSectionTag
	{
		private readonly ModLocalizer _localizer;

		private readonly string _key;

		public string Section { get; }

		public string DisplayName => _localizer.GetLocalizedText(_key);

		public int? Order { get; }

		internal ConfigSectionTag(ModLocalizer localizer, string section, string key, int order)
		{
			_localizer = localizer;
			_key = key;
			Section = section;
			Order = order;
		}

		internal bool TryGetDisplayName(out string displayName)
		{
			displayName = _localizer.GetLocalizedText(_key);
			return !string.Equals(displayName, _key, StringComparison.Ordinal);
		}
	}
	internal sealed class ConfigEntryTag
	{
		private readonly ModLocalizer _localizer;

		private readonly string _key;

		public string DisplayName => _localizer.GetLocalizedText(_key + ".name");

		public string Description => _localizer.GetLocalizedText(_key + ".description");

		public int? Order { get; }

		public double? SliderStep { get; }

		internal ConfigEntryTag(ModLocalizer localizer, string key, int order, double? sliderStep = null)
		{
			_localizer = localizer;
			_key = key;
			Order = order;
			SliderStep = sliderStep;
		}

		internal bool TryGetText(string propertyName, out string text)
		{
			string text2 = (string.Equals(propertyName, "DisplayName", StringComparison.Ordinal) ? ".name" : ".description");
			string text3 = _key + text2;
			text = _localizer.GetLocalizedText(text3);
			return !string.Equals(text, text3, StringComparison.Ordinal);
		}

		internal bool TryGetDisplayValue(object value, out string displayValue)
		{
			displayValue = null;
			string text = NormalizeValue(value);
			if (text.Length == 0)
			{
				return false;
			}
			string text2 = _key + "." + text;
			string localizedText = _localizer.GetLocalizedText(text2);
			if (string.Equals(localizedText, text2, StringComparison.Ordinal))
			{
				return false;
			}
			displayValue = localizedText;
			return true;
		}

		private static string NormalizeValue(object value)
		{
			string text = Convert.ToString(value, CultureInfo.InvariantCulture);
			if (string.IsNullOrWhiteSpace(text))
			{
				return string.Empty;
			}
			StringBuilder stringBuilder = new StringBuilder(text.Length + 4);
			for (int i = 0; i < text.Length; i++)
			{
				char c = text[i];
				if (!char.IsLetterOrDigit(c))
				{
					if (stringBuilder.Length > 0 && stringBuilder[stringBuilder.Length - 1] != '_')
					{
						stringBuilder.Append('_');
					}
					continue;
				}
				if (char.IsUpper(c) && i > 0 && char.IsLower(text[i - 1]) && stringBuilder.Length > 0 && stringBuilder[stringBuilder.Length - 1] != '_')
				{
					stringBuilder.Append('_');
				}
				stringBuilder.Append(char.ToLowerInvariant(c));
			}
			return stringBuilder.ToString().Trim(new char[1] { '_' });
		}
	}
	public static class LocalizationApi
	{
		public const string DefaultLanguage = "en";

		private static readonly Dictionary<string, ModLocalizer> Localizers = new Dictionary<string, ModLocalizer>(StringComparer.OrdinalIgnoreCase);

		private static readonly HashSet<string> GameLanguages = new HashSet<string>(new string[11]
		{
			"en", "de", "fr", "pt-br", "es", "ru", "pl", "ja", "zh_cn", "ko",
			"tr"
		}, StringComparer.OrdinalIgnoreCase);

		private static string _currentLanguage = "en";

		public static string CurrentLanguage => _currentLanguage;

		public static event Action<string> LanguageChanged;

		public static ModLocalizer For(string pluginGuid)
		{
			if (string.IsNullOrWhiteSpace(pluginGuid))
			{
				throw new ArgumentException("Plugin GUID cannot be empty.", "pluginGuid");
			}
			if (!Localizers.TryGetValue(pluginGuid, out var value))
			{
				value = new ModLocalizer();
				Localizers.Add(pluginGuid, value);
			}
			return value;
		}

		public static bool TryGetCurrentGameLanguage(out string language)
		{
			language = CurrentLanguage;
			return true;
		}

		internal static void LoadSavedLanguage()
		{
			try
			{
				string text = PlayerPrefs.GetString("settings");
				if (!string.IsNullOrWhiteSpace(text))
				{
					string text2 = (string)JObject.Parse(text)["language"];
					if (!string.IsNullOrWhiteSpace(text2) && GameLanguages.Contains(text2))
					{
						_currentLanguage = text2;
					}
				}
			}
			catch
			{
			}
		}

		public static string GetConfigDescription(ConfigEntryBase entry)
		{
			string entryTagText = GetEntryTagText(entry, "Description");
			if (!string.IsNullOrWhiteSpace(entryTagText))
			{
				return entryTagText;
			}
			if (entry != null)
			{
				return entry.Description.Description;
			}
			return null;
		}

		public static string GetConfigDisplayName(ConfigEntryBase entry)
		{
			string entryTagText = GetEntryTagText(entry, "DisplayName");
			if (!string.IsNullOrWhiteSpace(entryTagText))
			{
				return entryTagText;
			}
			if (entry != null)
			{
				return entry.Definition.Key;
			}
			return null;
		}

		public static string GetConfigSection(string section, ConfigFile configFile)
		{
			ConfigEntryBase[] obj = ((configFile != null) ? configFile.GetConfigEntries() : null) ?? Array.Empty<ConfigEntryBase>();
			string text = null;
			ConfigEntryBase[] array = obj;
			foreach (ConfigEntryBase val in array)
			{
				if (!string.Equals(val.Definition.Section, section, StringComparison.Ordinal))
				{
					continue;
				}
				ConfigDescription description = val.Description;
				object[] array2 = ((description != null) ? description.Tags : null) ?? Array.Empty<object>();
				foreach (object obj2 in array2)
				{
					if (!string.Equals(ReadString(obj2, "Section"), section, StringComparison.Ordinal))
					{
						continue;
					}
					if (obj2 is ConfigSectionTag configSectionTag)
					{
						if (configSectionTag.TryGetDisplayName(out var displayName))
						{
							text = displayName;
						}
						continue;
					}
					string text2 = ReadString(obj2, "DisplayName");
					if (!string.IsNullOrWhiteSpace(text2))
					{
						text = text2;
					}
				}
			}
			if (!string.IsNullOrWhiteSpace(text))
			{
				return text;
			}
			return section;
		}

		public static bool TryGetConfigDisplayValue(ConfigEntryBase entry, object value, out string displayValue)
		{
			displayValue = null;
			if (entry == null || value == null)
			{
				return false;
			}
			ConfigDescription description = entry.Description;
			object[] array = ((description != null) ? description.Tags : null) ?? Array.Empty<object>();
			for (int i = 0; i < array.Length; i++)
			{
				if (array[i] is ConfigEntryTag configEntryTag && configEntryTag.TryGetDisplayValue(value, out displayValue))
				{
					return true;
				}
			}
			return false;
		}

		internal static void NotifyLanguageChanged(string code)
		{
			string obj = (_currentLanguage = GetLanguage(code));
			Action<string> languageChanged = LocalizationApi.LanguageChanged;
			if (languageChanged == null)
			{
				return;
			}
			Delegate[] invocationList = languageChanged.GetInvocationList();
			foreach (Delegate obj2 in invocationList)
			{
				try
				{
					((Action<string>)obj2)(obj);
				}
				catch (Exception ex)
				{
					ManualLogSource log = GraveyardKeeper2LocalizationAPI.Log;
					if (log != null)
					{
						log.LogError((object)("Language change callback failed: " + ex));
					}
				}
			}
		}

		private static string GetLanguage(string code)
		{
			if (string.IsNullOrEmpty(code) || !GameLanguages.Contains(code))
			{
				return "en";
			}
			return code;
		}

		private static string GetEntryTagText(ConfigEntryBase entry, string propertyName)
		{
			if (entry == null)
			{
				return null;
			}
			string result = null;
			ConfigDescription description = entry.Description;
			object[] array = ((description != null) ? description.Tags : null) ?? Array.Empty<object>();
			foreach (object obj in array)
			{
				if (ReadProperty(obj, "Section") != null)
				{
					continue;
				}
				if (obj is ConfigEntryTag configEntryTag)
				{
					if (configEntryTag.TryGetText(propertyName, out var text))
					{
						result = text;
					}
					continue;
				}
				string text2 = ReadString(obj, propertyName);
				if (!string.IsNullOrWhiteSpace(text2))
				{
					result = text2;
				}
			}
			return result;
		}

		private static string ReadString(object value, string propertyName)
		{
			return ReadProperty(value, propertyName) as string;
		}

		private static object ReadProperty(object value, string propertyName)
		{
			try
			{
				if (value == null)
				{
					return null;
				}
				return AccessTools.FirstProperty(value.GetType(), (Func<PropertyInfo, bool>)((PropertyInfo property) => string.Equals(property.Name, propertyName, StringComparison.Ordinal)))?.GetValue(value, null);
			}
			catch
			{
				return null;
			}
		}
	}
	public sealed class ModLocalizer
	{
		private readonly Dictionary<string, Dictionary<string, string>> _localizations = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);

		internal ModLocalizer()
		{
		}

		public void RegisterJson(string jsonPath)
		{
			RegisterJson(jsonPath, requireDefaultLanguage: true);
		}

		public void RegisterJson(string jsonPath, bool requireDefaultLanguage)
		{
			if (string.IsNullOrWhiteSpace(jsonPath))
			{
				throw new ArgumentException("Localization file path cannot be empty.", "jsonPath");
			}
			if (!File.Exists(jsonPath))
			{
				throw new FileNotFoundException("Localization file not found.", jsonPath);
			}
			Dictionary<string, Dictionary<string, string>> dictionary = JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, string>>>(File.ReadAllText(jsonPath));
			if (dictionary == null)
			{
				throw new InvalidDataException("Localization JSON must contain an object.");
			}
			if (requireDefaultLanguage && !dictionary.ContainsKey("en"))
			{
				throw new InvalidDataException("Localization file must contain a en section.");
			}
			_localizations.Clear();
			foreach (KeyValuePair<string, Dictionary<string, string>> item in dictionary)
			{
				if (!string.IsNullOrWhiteSpace(item.Key) && item.Value != null)
				{
					_localizations[item.Key] = item.Value;
				}
			}
		}

		public string GetLocalizedText(string key, params object[] args)
		{
			if (string.IsNullOrEmpty(key))
			{
				return string.Empty;
			}
			string currentLanguage = LocalizationApi.CurrentLanguage;
			if (TryGetText(currentLanguage, key, out var text))
			{
				return Format(text, args);
			}
			if (!string.Equals(currentLanguage, "en", StringComparison.OrdinalIgnoreCase) && TryGetText("en", key, out text))
			{
				return Format(text, args);
			}
			return key;
		}

		public ConfigDescription Config(string key, int entryOrder, string section, string sectionKey, int sectionOrder, AcceptableValueBase acceptableValues = null)
		{
			return CreateConfig(key, entryOrder, section, sectionKey, sectionOrder, acceptableValues, null);
		}

		public ConfigDescription Config(string key, int entryOrder, string section, string sectionKey, int sectionOrder, double sliderStep)
		{
			return CreateConfig(key, entryOrder, section, sectionKey, sectionOrder, null, sliderStep);
		}

		public ConfigDescription Config(string key, int entryOrder, string section, string sectionKey, int sectionOrder, AcceptableValueBase acceptableValues, double sliderStep)
		{
			return CreateConfig(key, entryOrder, section, sectionKey, sectionOrder, acceptableValues, sliderStep);
		}

		private ConfigDescription CreateConfig(string key, int entryOrder, string section, string sectionKey, int sectionOrder, AcceptableValueBase acceptableValues, double? sliderStep)
		{
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Expected O, but got Unknown
			if (string.IsNullOrWhiteSpace(key))
			{
				throw new ArgumentException("Localization key cannot be empty.", "key");
			}
			if (string.IsNullOrWhiteSpace(section))
			{
				throw new ArgumentException("Config section cannot be empty.", "section");
			}
			if (string.IsNullOrWhiteSpace(sectionKey))
			{
				throw new ArgumentException("Section localization key cannot be empty.", "sectionKey");
			}
			if (sliderStep.HasValue && (sliderStep.Value <= 0.0 || double.IsNaN(sliderStep.Value) || double.IsInfinity(sliderStep.Value)))
			{
				throw new ArgumentOutOfRangeException("sliderStep");
			}
			string text = key + ".description";
			string text2 = GetLocalizedText(text);
			if (string.Equals(text2, text, StringComparison.Ordinal))
			{
				text2 = string.Empty;
			}
			return new ConfigDescription(text2, acceptableValues, new object[2]
			{
				new ConfigSectionTag(this, section, sectionKey, sectionOrder),
				new ConfigEntryTag(this, key, entryOrder, sliderStep)
			});
		}

		private bool TryGetText(string language, string key, out string text)
		{
			text = null;
			if (_localizations.TryGetValue(language, out var value))
			{
				return value.TryGetValue(key, out text);
			}
			return false;
		}

		private static string Format(string text, object[] args)
		{
			if (args == null || args.Length == 0)
			{
				return text;
			}
			try
			{
				return string.Format(text, args);
			}
			catch (FormatException)
			{
				return text;
			}
		}
	}
}