Decompiled source of SunkenlandLocalizationAPI v1.2.0

BepInEx/plugins/SunkenlandLocalizationAPI/SunkenlandLocalizationAPI.dll

Decompiled 5 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.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SunkenlandLocalizationAPI.Api;
using UnityEngine;
using UnityEngine.Localization;
using UnityEngine.Localization.Settings;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("SunkenlandLocalizationAPI")]
[assembly: AssemblyDescription("Sunkenland Localization API mod for Sunkenland by Ice Box Studio")]
[assembly: AssemblyCompany("Ice Box Studio")]
[assembly: AssemblyProduct("SunkenlandLocalizationAPI")]
[assembly: AssemblyCopyright("Copyright © 2026 Ice Box Studio All rights reserved.")]
[assembly: ComVisible(false)]
[assembly: Guid("79870812-2aca-43d5-b058-5844d23fb3b4")]
[assembly: AssemblyFileVersion("1.2.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.2.0.0")]
namespace SunkenlandLocalizationAPI
{
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "IceBoxStudio.Sunkenland.LocalizationAPI";

		public const string PLUGIN_NAME = "SunkenlandLocalizationAPI";

		public const string PLUGIN_VERSION = "1.2.0";
	}
	[BepInPlugin("IceBoxStudio.Sunkenland.LocalizationAPI", "SunkenlandLocalizationAPI", "1.2.0")]
	public sealed class SunkenlandLocalizationAPI : BaseUnityPlugin
	{
		private Harmony _harmony;

		internal static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			LocalizationApi.LoadSavedLanguage();
			LocalizationSettings.SelectedLocaleChanged += OnLanguageChanged;
			_harmony = new Harmony("IceBoxStudio.Sunkenland.LocalizationAPI");
			_harmony.PatchAll(Assembly.GetExecutingAssembly());
			Log.LogInfo((object)"SunkenlandLocalizationAPI 1.2.0 initialized.");
		}

		private static void OnLanguageChanged(Locale locale)
		{
			LocalizationApi.NotifyLanguageChanged(locale);
		}
	}
}
namespace SunkenlandLocalizationAPI.Patches
{
	[HarmonyPatch(typeof(GameInitialization), "OnBeforeSplashScreen")]
	internal static class GameInitializationPatch
	{
		[HarmonyPostfix]
		private static void Postfix()
		{
			LocalizationApi.NotifyLanguageChanged(LocalizationSettings.SelectedLocale);
		}
	}
}
namespace SunkenlandLocalizationAPI.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[9] { "en", "zh-Hans", "fr", "de", "ja", "ko", "ru", "es", "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 path = Path.Combine(Application.persistentDataPath, "Setting.json");
				if (File.Exists(path))
				{
					JToken obj = JObject.Parse(File.ReadAllText(path))["SettingProfile"];
					object obj2;
					if (obj == null)
					{
						obj2 = null;
					}
					else
					{
						JToken obj3 = obj[(object)"value"];
						obj2 = ((obj3 != null) ? obj3[(object)"localeCode"] : null);
					}
					string text = (string)(JToken)obj2;
					if (!string.IsNullOrWhiteSpace(text) && GameLanguages.Contains(text))
					{
						_currentLanguage = text;
					}
				}
			}
			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(Locale locale)
		{
			string obj = (_currentLanguage = GetLanguage(locale));
			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 = SunkenlandLocalizationAPI.Log;
					if (log != null)
					{
						log.LogError((object)("Language change callback failed: " + ex));
					}
				}
			}
		}

		private static string GetLanguage(Locale locale)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			object obj;
			if (!((Object)(object)locale == (Object)null))
			{
				LocaleIdentifier identifier = locale.Identifier;
				obj = ((LocaleIdentifier)(ref identifier)).Code;
			}
			else
			{
				obj = null;
			}
			string text = (string)obj;
			if (string.IsNullOrEmpty(text) || !GameLanguages.Contains(text))
			{
				return "en";
			}
			return text;
		}

		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
			{
				return (value == null) ? null : AccessTools.Property(value.GetType(), propertyName)?.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)
		{
			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 || !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;
			}
		}
	}
}