Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Wesleys Enemies Localize v1.0.0
WesleysEnemiesLocalize.dll
Decompiled 20 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: AssemblyCompany("Midorino")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+b24e3fcb591b98a2e17e6d618a7603905068ceed")] [assembly: AssemblyProduct("WesleysEnemiesLocalize")] [assembly: AssemblyTitle("WesleysEnemiesLocalize")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [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; } } } namespace WesleysEnemiesLocalize { internal sealed class MiniJson { private readonly string _text; private int _pos; private MiniJson(string text) { _text = text; } internal static object? Parse(string text) { MiniJson miniJson = new MiniJson(text); if (miniJson._pos < text.Length && text[miniJson._pos] == '\ufeff') { miniJson._pos++; } object? result = miniJson.ReadValue(); miniJson.SkipWhitespace(); if (miniJson._pos < text.Length) { throw miniJson.Error("unexpected text after the end"); } return result; } private object? ReadValue() { SkipWhitespace(); if (_pos >= _text.Length) { throw Error("unexpected end of file"); } char c = _text[_pos]; switch (c) { case '{': return ReadObject(); case '[': return ReadArray(); case '"': return ReadString(); case 't': ReadWord("true"); return true; case 'f': ReadWord("false"); return false; case 'n': ReadWord("null"); return null; default: switch (c) { case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return ReadNumber(); default: throw Error($"unexpected character '{c}'"); } } } private Dictionary<string, object?> ReadObject() { Dictionary<string, object> dictionary = new Dictionary<string, object>(); _pos++; SkipWhitespace(); if (Peek() == '}') { _pos++; return dictionary; } while (true) { SkipWhitespace(); if (Peek() != '"') { break; } string key = ReadString(); SkipWhitespace(); Expect(':'); dictionary[key] = ReadValue(); SkipWhitespace(); switch (Peek()) { case ',': break; case '}': _pos++; return dictionary; default: throw Error("expected ',' or '}' (a comma may be missing)"); } _pos++; } throw Error("expected a \"name\""); } private List<object?> ReadArray() { List<object> list = new List<object>(); _pos++; SkipWhitespace(); if (Peek() == ']') { _pos++; return list; } while (true) { list.Add(ReadValue()); SkipWhitespace(); switch (Peek()) { case ',': break; case ']': _pos++; return list; default: throw Error("expected ',' or ']'"); } _pos++; } } private string ReadString() { _pos++; StringBuilder stringBuilder = new StringBuilder(); while (_pos < _text.Length) { char c = _text[_pos++]; switch (c) { case '"': return stringBuilder.ToString(); case '\n': throw Error("a string is not closed with \" before the end of the line"); default: stringBuilder.Append(c); break; case '\\': { if (_pos >= _text.Length) { throw Error("unexpected end of file"); } char c2 = _text[_pos++]; switch (c2) { case '"': stringBuilder.Append('"'); break; case '\\': stringBuilder.Append('\\'); break; case '/': stringBuilder.Append('/'); break; case 'b': stringBuilder.Append('\b'); break; case 'f': stringBuilder.Append('\f'); break; case 'n': stringBuilder.Append('\n'); break; case 'r': stringBuilder.Append('\r'); break; case 't': stringBuilder.Append('\t'); break; case 'u': { if (_pos + 4 > _text.Length || !int.TryParse(_text.Substring(_pos, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var result)) { throw Error("bad \\u escape"); } stringBuilder.Append((char)result); _pos += 4; break; } default: throw Error($"bad escape '\\{c2}'"); } break; } } } throw Error("a string is not closed with \""); } private double ReadNumber() { int pos = _pos; while (_pos < _text.Length && "+-0123456789.eE".IndexOf(_text[_pos]) >= 0) { _pos++; } string text = _text.Substring(pos, _pos - pos); if (!double.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { _pos = pos; throw Error("bad number '" + text + "'"); } return result; } private void ReadWord(string word) { if (string.CompareOrdinal(_text, _pos, word, 0, word.Length) != 0) { throw Error("unexpected word (did you forget \"quotes\"?)"); } _pos += word.Length; } private void Expect(char c) { if (Peek() != c) { throw Error($"expected '{c}'"); } _pos++; } private char Peek() { if (_pos >= _text.Length) { return '\0'; } return _text[_pos]; } private void SkipWhitespace() { while (_pos < _text.Length && char.IsWhiteSpace(_text[_pos])) { _pos++; } } private FormatException Error(string message) { int num = 1; int num2 = 1; for (int i = 0; i < _pos && i < _text.Length; i++) { if (_text[i] == '\n') { num++; num2 = 1; } else { num2++; } } return new FormatException($"{message} (line {num}, column {num2})"); } } internal sealed class NameTable { private const float ReloadCheckInterval = 2f; private readonly ManualLogSource _log; private readonly string _path; private readonly string _fileName; private readonly string _label; private DateTime _loadedWriteTime; private float _nextReloadCheck; private Dictionary<string, Dictionary<string, string>> _tables = NewTables(); private string? _cachedLocale; private Dictionary<string, string>? _cachedTable; private readonly HashSet<string> _reported = new HashSet<string>(StringComparer.OrdinalIgnoreCase); internal NameTable(ManualLogSource log, string path, string defaultJson, string label, params string[] legacyPaths) { _log = log; _path = path; _fileName = Path.GetFileName(path); _label = label; try { if (!File.Exists(path)) { string text = Array.Find(legacyPaths, File.Exists); if (text != null) { File.Copy(text, path); _log.LogInfo((object)("Copied " + Path.GetFileName(text) + " to " + _fileName)); } else { File.WriteAllText(path, defaultJson, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); _log.LogInfo((object)("Created " + path)); } } } catch (Exception ex) { _log.LogError((object)("Could not create " + path + ": " + ex.Message)); } Load(); } internal bool TryTranslate(string name, out string translated) { translated = name; try { ReloadIfChanged(); Dictionary<string, string> dictionary = CurrentTable(); if (dictionary != null && dictionary.TryGetValue(name.Trim(), out var value) && !string.IsNullOrEmpty(value)) { translated = value; return true; } } catch (Exception arg) { if (_reported.Add("\0error")) { _log.LogError((object)$"Translate failed: {arg}"); } } return false; } internal void ReportIfMissing(string name) { try { if (!string.IsNullOrEmpty(name)) { Dictionary<string, string> dictionary = CurrentTable(); if (dictionary != null && !dictionary.ContainsKey(name.Trim()) && _reported.Add(name.Trim())) { _log.LogDebug((object)("No translation for \"" + name.Trim() + "\" (language " + SemiFunc.LocaleGetCode() + "). Add it to " + _fileName + " if you want.")); } } } catch (Exception ex) { _log.LogDebug((object)("ReportIfMissing failed: " + ex.Message)); } } private Dictionary<string, string>? CurrentTable() { string text = SemiFunc.LocaleGetCode() ?? ""; if (text == _cachedLocale) { return _cachedTable; } _cachedLocale = text; _cachedTable = null; if (_tables.TryGetValue(text, out Dictionary<string, string> value)) { _cachedTable = value; } else { int num = text.IndexOf('-'); if (num > 0 && _tables.TryGetValue(text.Substring(0, num), out Dictionary<string, string> value2)) { _cachedTable = value2; } } return _cachedTable; } private void ReloadIfChanged() { float realtimeSinceStartup = Time.realtimeSinceStartup; if (!(realtimeSinceStartup < _nextReloadCheck)) { _nextReloadCheck = realtimeSinceStartup + 2f; if (File.Exists(_path) && File.GetLastWriteTimeUtc(_path) != _loadedWriteTime) { Load(); } } } private void Load() { try { if (!File.Exists(_path)) { _log.LogWarning((object)(_path + " not found. No names are translated.")); return; } _loadedWriteTime = File.GetLastWriteTimeUtc(_path); string text = File.ReadAllText(_path, Encoding.UTF8); Dictionary<string, Dictionary<string, string>> dictionary = (_tables = Parse(text)); _cachedLocale = null; _cachedTable = null; _reported.Clear(); List<string> list = new List<string>(); foreach (KeyValuePair<string, Dictionary<string, string>> item in dictionary) { list.Add($"{item.Key}={item.Value.Count}"); } _log.LogInfo((object)(_label + ": loaded " + _fileName + " (" + ((list.Count > 0) ? string.Join(", ", list) : "no languages") + ")")); } catch (Exception ex) { _log.LogError((object)("Could not read " + _path + ": " + ex.Message + " (keeping the previous names)")); } } private Dictionary<string, Dictionary<string, string>> Parse(string text) { Dictionary<string, object> obj = (MiniJson.Parse(text) as Dictionary<string, object>) ?? throw new FormatException("the top level must be { ... }"); Dictionary<string, Dictionary<string, string>> dictionary = NewTables(); foreach (KeyValuePair<string, object> item in obj) { if (item.Key.StartsWith("_", StringComparison.Ordinal) || !(item.Value is Dictionary<string, object> dictionary2)) { continue; } Dictionary<string, string> dictionary3 = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair<string, object> item2 in dictionary2) { if (!item2.Key.StartsWith("_", StringComparison.Ordinal)) { if (item2.Value is string value) { dictionary3[item2.Key.Trim()] = value; } else { _log.LogWarning((object)(item.Key + ".\"" + item2.Key + "\": the value must be a string in \"quotes\" (ignored)")); } } } dictionary[item.Key.Trim()] = dictionary3; } return dictionary; } private static Dictionary<string, Dictionary<string, string>> NewTables() { return new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase); } } [BepInPlugin("Midorino.WesleysEnemiesLocalize", "Wesleys_Enemies_Localize", "1.0.0")] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "Midorino.WesleysEnemiesLocalize"; public const string Name = "Wesleys_Enemies_Localize"; public const string Version = "1.0.0"; public const string ConfigFileName = "Wesleys_Enemies_Localize.json"; private const string LegacyConfigFileName = "WesleysEnemiesJP.json"; private const string DefaultJson = "{\n \"_readme\": \"Enemy display names per game language (ja = Japanese). Left: English enemy name, right: name to show. Add other languages the same way. Keys starting with _ are ignored. Changes are reloaded while the game is running.\",\n \"ja\": {\n \"Gusher\": \"ガッシャー\",\n \"Roaster\": \"ロースター\",\n \"Lost droid\": \"迷えるドロイド\",\n \"Lost Destroyer\": \"喪失の破壊者\",\n \"Soldier\": \"兵士\",\n \"Soldier Ghost\": \"幽霊兵士\",\n \"Voodoo\": \"ブードゥー\"\n }\n}\n"; internal static ManualLogSource Log; internal static NameTable Names; private void Awake() { //IL_0050: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; Names = new NameTable(Log, Path.Combine(Paths.ConfigPath, "Wesleys_Enemies_Localize.json"), "{\n \"_readme\": \"Enemy display names per game language (ja = Japanese). Left: English enemy name, right: name to show. Add other languages the same way. Keys starting with _ are ignored. Changes are reloaded while the game is running.\",\n \"ja\": {\n \"Gusher\": \"ガッシャー\",\n \"Roaster\": \"ロースター\",\n \"Lost droid\": \"迷えるドロイド\",\n \"Lost Destroyer\": \"喪失の破壊者\",\n \"Soldier\": \"兵士\",\n \"Soldier Ghost\": \"幽霊兵士\",\n \"Voodoo\": \"ブードゥー\"\n }\n}\n", "Wesleys_Enemies_Localize 1.0.0", Path.Combine(Paths.ConfigPath, "WesleysEnemiesJP.json")); new Harmony("Midorino.WesleysEnemiesLocalize").PatchAll(typeof(Plugin).Assembly); } } [HarmonyPatch(typeof(ItemInfoUI), "ItemInfoText", new Type[] { typeof(ItemAttributes), typeof(string), typeof(bool) })] internal static class ItemInfoTextPatch { private static void Prefix(ref string message, bool enemy) { if (enemy && !string.IsNullOrEmpty(message) && Plugin.Names.TryTranslate(message, out string translated)) { message = translated; } } } [HarmonyPatch(typeof(EnemyParent), "Awake")] internal static class EnemyParentAwakePatch { private static void Postfix(EnemyParent __instance) { if (!((Object)(object)__instance.enemyNameLocalized != (Object)null)) { Plugin.Names.ReportIfMissing(__instance.enemyName); } } } }