using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text.Json; using BepInEx; using BepInEx.Configuration; using BepInEx.Core.Logging.Interpolation; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using HarmonyLib; using ShiftAtMidnightLocalizationAPI.Api; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("ShiftAtMidnightLocalizationAPI")] [assembly: AssemblyDescription("Localization API for Shift At Midnight mods")] [assembly: AssemblyProduct("ShiftAtMidnightLocalizationAPI")] [assembly: ComVisible(false)] [assembly: Guid("79870812-2aca-43d5-b058-5844d23fb3b4")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyVersion("1.0.0.0")] namespace ShiftAtMidnightLocalizationAPI { public static class PluginInfo { public const string PLUGIN_GUID = "IceBoxStudio.ShiftAtMidnight.LocalizationAPI"; public const string PLUGIN_NAME = "ShiftAtMidnightLocalizationAPI"; public const string PLUGIN_VERSION = "1.0.0"; } [BepInPlugin("IceBoxStudio.ShiftAtMidnight.LocalizationAPI", "ShiftAtMidnightLocalizationAPI", "1.0.0")] public sealed class ShiftAtMidnightLocalizationAPI : BasePlugin { internal static ManualLogSource Logger { get; private set; } public override void Load() { //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected O, but got Unknown //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown Logger = ((BasePlugin)this).Log; bool flag = default(bool); try { new Harmony("IceBoxStudio.ShiftAtMidnight.LocalizationAPI").PatchAll(Assembly.GetExecutingAssembly()); ManualLogSource logger = Logger; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(14, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted("ShiftAtMidnightLocalizationAPI"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted("1.0.0"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" initialized."); } logger.LogInfo(val); } catch (Exception ex) { ManualLogSource logger2 = Logger; BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(43, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Failed to initialize the localization API: "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(ex); } logger2.LogError(val2); } } } } namespace ShiftAtMidnightLocalizationAPI.Patches { [HarmonyPatch(typeof(LanguageManager), "OnLanguageLoaded")] public static class LanguageManagerOnLanguageLoadedPatch { [HarmonyPostfix] public static void Postfix() { LocalizationApi.NotifyLanguageChanged(); } } } namespace ShiftAtMidnightLocalizationAPI.Api { public static class LocalizationApi { private sealed class LocalizedConfigDescription { public ModLocalizer Localizer; public string Key; public object[] Arguments; } public const string DefaultLanguage = "ENGLISH"; private const string LanguagePreferenceKey = "Language"; private static readonly Dictionary Localizers = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary ConfigDescriptions = new Dictionary(); public static string CurrentLanguage { get { TryGetCurrentGameLanguage(out var language); return language; } } public static event Action 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(pluginGuid); Localizers.Add(pluginGuid, value); } return value; } public static bool TryGetCurrentGameLanguage(out string language) { try { string text = (PlayerPrefs.HasKey("Language") ? PlayerPrefs.GetString("Language", "ENGLISH") : "ENGLISH"); language = (string.IsNullOrWhiteSpace(text) ? "ENGLISH" : text); return true; } catch { language = "ENGLISH"; return false; } } public static string GetConfigDescription(ConfigEntryBase entry) { if (entry != null && ConfigDescriptions.TryGetValue(entry, out var value)) { return value.Localizer.GetLocalizedText(value.Key, value.Arguments); } if (entry == null) { return null; } ConfigDescription description = entry.Description; if (description == null) { return null; } return description.Description; } internal static void RegisterConfigDescription(ConfigEntryBase entry, ModLocalizer localizer, string key, object[] arguments) { if (entry == null) { throw new ArgumentNullException("entry"); } if (localizer == null) { throw new ArgumentNullException("localizer"); } if (string.IsNullOrWhiteSpace(key)) { throw new ArgumentException("Localization key cannot be empty.", "key"); } ConfigDescriptions[entry] = new LocalizedConfigDescription { Localizer = localizer, Key = key, Arguments = arguments }; } internal static void NotifyLanguageChanged() { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown Action languageChanged = LocalizationApi.LanguageChanged; if (languageChanged == null) { return; } string currentLanguage = CurrentLanguage; Delegate[] invocationList = languageChanged.GetInvocationList(); bool flag = default(bool); foreach (Delegate obj in invocationList) { try { ((Action)obj)(currentLanguage); } catch (Exception ex) { ManualLogSource logger = ShiftAtMidnightLocalizationAPI.Logger; if (logger != null) { BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(33, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Language change callback failed: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex); } logger.LogError(val); } } } } } public sealed class ModLocalizer { private readonly Dictionary> _localizations = new Dictionary>(StringComparer.OrdinalIgnoreCase); public string PluginGuid { get; } internal ModLocalizer(string pluginGuid) { PluginGuid = pluginGuid; } 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> dictionary = new Dictionary>(StringComparer.OrdinalIgnoreCase); using (JsonDocument jsonDocument = JsonDocument.Parse(File.ReadAllText(jsonPath))) { if (jsonDocument.RootElement.ValueKind != JsonValueKind.Object) { throw new InvalidDataException("Localization file root must be a JSON object."); } foreach (JsonProperty item in jsonDocument.RootElement.EnumerateObject()) { if (item.Value.ValueKind != JsonValueKind.Object) { continue; } Dictionary dictionary2 = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (JsonProperty item2 in item.Value.EnumerateObject()) { if (item2.Value.ValueKind == JsonValueKind.String) { dictionary2[item2.Name] = item2.Value.GetString() ?? string.Empty; } } if (dictionary2.Count > 0) { dictionary[item.Name] = dictionary2; } } } if (!dictionary.ContainsKey("ENGLISH")) { throw new InvalidDataException("Localization file must contain a ENGLISH section."); } _localizations.Clear(); foreach (KeyValuePair> item3 in dictionary) { _localizations.Add(item3.Key, item3.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, "ENGLISH", StringComparison.OrdinalIgnoreCase) && TryGetText("ENGLISH", key, out var text2)) { return Format(text2, args); } return key; } public void RegisterConfigDescription(ConfigEntryBase entry, string key, params object[] args) { LocalizationApi.RegisterConfigDescription(entry, this, key, args); } 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; } } } }