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 RepoRPG RU v0.2.1
patchers/RepoRPG_RU.Patcher.dll
Decompiled an hour ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using BepInEx; using BepInEx.Logging; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Collections.Generic; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("RepoRPG RU - patcher")] [assembly: AssemblyVersion("0.2.1.0")] namespace RepoRPG_RU; internal enum Use { Unknown, Display, Keep, Id } internal struct Consumer { public Instruction Ins; public int FromTop; public int Pops; public bool IsCall { get { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Invalid comparison between Unknown and I4 //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Invalid comparison between Unknown and I4 //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Invalid comparison between Unknown and I4 if (Ins == null) { return false; } OpCode opCode = Ins.OpCode; Code code = ((OpCode)(ref opCode)).Code; if ((int)code != 39 && (int)code != 110) { return (int)code == 114; } return true; } } public MethodReference Method { get { if (!IsCall) { return null; } object operand = Ins.Operand; return (MethodReference)((operand is MethodReference) ? operand : null); } } public int Param { get { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Invalid comparison between Unknown and I4 MethodReference method = Method; if (method == null) { return -2; } int num = Pops - 1 - FromTop; if (method.HasThis && !method.ExplicitThis) { OpCode opCode = Ins.OpCode; if ((int)((OpCode)(ref opCode)).Code != 114) { return num - 1; } } return num; } } } internal enum ProbeKind { Prefix, Suffix, Inside } internal sealed class Probe { public string Text; public ProbeKind Kind; public bool IgnoreCase; public string Where; public string Key { get { int kind = (int)Kind; return kind + (IgnoreCase ? "i" : "c") + Text; } } public bool Hits(string s) { if (s == null || s.Length < Text.Length) { return false; } StringComparison comparisonType = (IgnoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); return Kind switch { ProbeKind.Prefix => s.StartsWith(Text, comparisonType), ProbeKind.Suffix => s.EndsWith(Text, comparisonType), _ => s.IndexOf(Text, comparisonType) >= 0, }; } public override string ToString() { return ((Kind == ProbeKind.Prefix) ? "StartsWith" : ((Kind == ProbeKind.Suffix) ? "EndsWith" : "Contains")) + "(\"" + Builder.Escape(Text) + "\")"; } } internal static class Analysis { public static bool IsProbe(Consumer c, out ProbeKind kind, out bool ignoreCase) { kind = ProbeKind.Inside; ignoreCase = false; MethodReference method = c.Method; if (method == null || c.Param != 0 || method.Parameters.Count == 0) { return false; } if (TypeName(((MemberReference)method).DeclaringType) != "System.String" || ((MemberReference)((ParameterReference)method.Parameters[0]).ParameterType).FullName != "System.String") { return false; } switch (((MemberReference)method).Name) { case "StartsWith": kind = ProbeKind.Prefix; break; case "EndsWith": kind = ProbeKind.Suffix; break; case "Contains": case "IndexOf": case "LastIndexOf": kind = ProbeKind.Inside; break; case "Replace": kind = ProbeKind.Inside; break; default: return false; } if (!method.HasThis) { return false; } for (int i = 1; i < method.Parameters.Count; i++) { string fullName = ((MemberReference)((ParameterReference)method.Parameters[i]).ParameterType).FullName; if (fullName == "System.StringComparison" && i == method.Parameters.Count - 1) { if (c.Ins != null && Constant(c.Ins.Previous, out var value)) { if ((value & 1) != 0) { ignoreCase = true; } } else { ignoreCase = true; } } else { switch (fullName) { case "System.StringComparison": case "System.Boolean": case "System.Globalization.CultureInfo": ignoreCase = true; break; } } } return true; } private static bool Constant(Instruction ins, out int value) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected I4, but got Unknown value = 0; if (ins == null) { return false; } OpCode opCode = ins.OpCode; Code code = ((OpCode)(ref opCode)).Code; switch (code - 21) { case 0: value = -1; return true; case 1: value = 0; return true; case 2: value = 1; return true; case 3: value = 2; return true; case 4: value = 3; return true; case 5: value = 4; return true; case 6: value = 5; return true; case 7: value = 6; return true; case 8: value = 7; return true; case 9: value = 8; return true; case 10: value = (sbyte)ins.Operand; return true; case 11: value = (int)ins.Operand; return true; default: return false; } } public static bool ProbeMatters(Probe p, Rules rules) { if (rules.ProbeIgnore.Contains(p.Text)) { return false; } if (rules.ProbeProtect.Contains(p.Text)) { return true; } if (p.Text.Length >= 3 && p.Text[0] == '{' && p.Text[p.Text.Length - 1] == '}') { return false; } bool flag = false; bool flag2 = true; string text = p.Text; foreach (char c in text) { if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { flag = true; } else if (c < '0' || c > '9') { flag2 = false; } } if (!flag) { return false; } if (p.Kind == ProbeKind.Inside && flag2) { return false; } return true; } public static string TypeKey(TypeDefinition t) { List<string> list = new List<string>(); for (TypeDefinition val = t; val != null; val = val.DeclaringType) { string text = ((MemberReference)val).Name; if (text.Length <= 0 || text[0] != '<') { int num = text.IndexOf('`'); if (num > 0) { text = text.Substring(0, num); } list.Add(text); } } list.Reverse(); return string.Join(".", list.ToArray()); } public static string MemberName(MethodDefinition m) { string text = Angle(((MemberReference)m).Name); if (text == null) { TypeDefinition declaringType = m.DeclaringType; while (declaringType != null && ((MemberReference)declaringType).Name.Length > 0 && ((MemberReference)declaringType).Name[0] == '<') { string text2 = Angle(((MemberReference)declaringType).Name); if (!string.IsNullOrEmpty(text2)) { text = text2; break; } declaringType = declaringType.DeclaringType; } } if (text == null) { text = ((MemberReference)m).Name; } if (text != ".ctor" && text != ".cctor") { int num = text.LastIndexOf('.'); if (num >= 0 && num + 1 < text.Length) { text = text.Substring(num + 1); } } if (text.StartsWith("get_", StringComparison.Ordinal) || text.StartsWith("set_", StringComparison.Ordinal)) { text = text.Substring(4); } else if (text.StartsWith("add_", StringComparison.Ordinal)) { text = text.Substring(4); } else if (text.StartsWith("remove_", StringComparison.Ordinal)) { text = text.Substring(7); } return text; } private static string Angle(string n) { if (n.Length < 3 || n[0] != '<') { return null; } int num = n.IndexOf('>'); if (num <= 1) { return null; } return n.Substring(1, num - 1); } public static string Scope(MethodDefinition m) { return TypeKey(m.DeclaringType) + "::" + MemberName(m); } public static string TypeName(TypeReference t) { if (t == null) { return ""; } return ((MemberReference)t.GetElementType()).FullName.Replace('/', '.'); } private static bool Effect(MethodDefinition owner, Instruction ins, out int pop, out int push) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: 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) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Expected I4, but got Unknown //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Invalid comparison between Unknown and I4 //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Expected I4, but got Unknown //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Invalid comparison between Unknown and I4 //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Invalid comparison between Unknown and I4 //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Invalid comparison between Unknown and I4 OpCode opCode = ins.OpCode; pop = 0; push = 0; StackBehaviour stackBehaviourPop = ((OpCode)(ref opCode)).StackBehaviourPop; switch ((int)stackBehaviourPop) { case 0: pop = 0; break; case 1: case 3: case 10: pop = 1; break; case 2: case 4: case 5: case 6: case 8: case 9: case 11: case 12: pop = 2; break; case 7: case 13: case 14: case 15: case 16: case 17: pop = 3; break; case 18: return false; case 27: { if ((int)((OpCode)(ref opCode)).Code == 41) { pop = ((!(((MemberReference)((MethodReference)owner).ReturnType).FullName == "System.Void")) ? 1 : 0); break; } if ((int)((OpCode)(ref opCode)).Code == 40) { object operand = ins.Operand; CallSite val = (CallSite)((operand is CallSite) ? operand : null); if (val == null) { return false; } pop = val.Parameters.Count + 1 + ((val.HasThis && !val.ExplicitThis) ? 1 : 0); break; } object operand2 = ins.Operand; MethodReference val2 = (MethodReference)((operand2 is MethodReference) ? operand2 : null); if (val2 == null) { return false; } pop = val2.Parameters.Count; if (val2.HasThis && !val2.ExplicitThis && (int)((OpCode)(ref opCode)).Code != 114) { pop++; } break; } default: return false; } stackBehaviourPop = ((OpCode)(ref opCode)).StackBehaviourPush; switch (stackBehaviourPop - 19) { case 0: push = 0; break; case 1: case 3: case 4: case 5: case 6: case 7: push = 1; break; case 2: push = 2; break; case 9: if ((int)((OpCode)(ref opCode)).Code == 40) { object operand3 = ins.Operand; CallSite val3 = (CallSite)((operand3 is CallSite) ? operand3 : null); if (val3 == null) { return false; } push = ((!(((MemberReference)val3.ReturnType).FullName == "System.Void")) ? 1 : 0); } else { object operand4 = ins.Operand; MethodReference val4 = (MethodReference)((operand4 is MethodReference) ? operand4 : null); if (val4 == null) { return false; } push = ((!(((MemberReference)val4.ReturnType).FullName == "System.Void")) ? 1 : 0); } break; default: return false; } return true; } public static Consumer Find(MethodDefinition owner, Instruction start) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Invalid comparison between Unknown and I4 //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Invalid comparison between Unknown and I4 //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Invalid comparison between Unknown and I4 //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Invalid comparison between Unknown and I4 //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Invalid comparison between Unknown and I4 //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Invalid comparison between Unknown and I4 //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Invalid comparison between Unknown and I4 //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) Consumer result = default(Consumer); Instruction val = start; int num = 0; int num2 = 0; while (val != null && num2 < 4000) { OpCode opCode = val.OpCode; Code code = ((OpCode)(ref opCode)).Code; if ((int)code == 187 || (int)code == 188 || (int)code == 186 || (int)code == 206 || (int)code == 215) { return result; } if (!Effect(owner, val, out var pop, out var push)) { return result; } if (num < pop) { return new Consumer { Ins = val, FromTop = num, Pops = pop }; } num += push - pop; if ((int)code == 41 || (int)code == 119) { return result; } opCode = val.OpCode; val = (Instruction)(((int)((OpCode)(ref opCode)).FlowControl != 0) ? ((object)val.Next) : ((object)/*isinst with value type is only supported in some contexts*/)); num2++; } return result; } public static Consumer Final(MethodDefinition owner, Instruction afterValue, Rules rules) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Invalid comparison between Unknown and I4 //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Invalid comparison between Unknown and I4 //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Invalid comparison between Unknown and I4 //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Invalid comparison between Unknown and I4 Consumer result = Find(owner, afterValue); for (int i = 0; i < 12; i++) { if (result.Ins == null) { break; } OpCode opCode = result.Ins.OpCode; Code code = ((OpCode)(ref opCode)).Code; if ((int)code == 115 || (int)code == 116) { result = Find(owner, result.Ins.Next); continue; } if ((int)code != 39 && (int)code != 110) { break; } MethodReference method = result.Method; if (method == null || !(((MemberReference)method.ReturnType).FullName != "System.Void") || !IsThrough(method, result.Param, rules)) { break; } result = Find(owner, result.Ins.Next); } return result; } private static bool IsThrough(MethodReference m, int param, Rules rules) { string text = TypeName(((MemberReference)m).DeclaringType); string name = ((MemberReference)m).Name; if (text == "System.String") { switch (name) { case "Concat": case "Format": case "Join": return true; case "ToUpper": case "ToUpperInvariant": case "ToLower": case "ToLowerInvariant": case "Trim": case "TrimStart": case "TrimEnd": case "PadLeft": case "PadRight": case "Substring": case "Insert": case "Remove": case "Replace": case "ToString": case "Normalize": return param == -1; default: return false; } } if (text == "System.Object" && name == "ToString") { return param == -1; } for (int i = 0; i < rules.Through.Count; i++) { if (Match(rules.Through[i], m, text, name, param)) { return true; } } return false; } public static bool Match(CallRule rule, MethodReference m, string dt, string name, int param) { if (rule.Args != null && Array.IndexOf(rule.Args, param) < 0) { return false; } if (!Rules.Glob(rule.Method, name)) { return false; } if (rule.External) { return Rules.Glob(rule.Type, dt); } if (!(((MemberReference)m).DeclaringType.GetElementType() is TypeDefinition)) { return false; } return TypeMatches(rule.Type, dt); } public static bool TypeMatches(string pattern, string fullName) { if (pattern == "*") { return true; } if (Rules.Glob(pattern, fullName)) { return true; } if (pattern.IndexOf('*') < 0) { if (fullName.Length > pattern.Length && fullName.EndsWith(pattern, StringComparison.Ordinal)) { return fullName[fullName.Length - pattern.Length - 1] == '.'; } return false; } return Rules.Glob("*." + pattern, fullName); } public static Use Classify(MethodDefinition owner, Consumer c, Rules rules, Dictionary<MethodDefinition, HashSet<int>> hooked, int depth, out string why) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0224: Unknown result type (might be due to invalid IL or missing references) why = null; if (c.Ins == null) { return Use.Unknown; } OpCode opCode = c.Ins.OpCode; Code code = ((OpCode)(ref opCode)).Code; int num = LocalIndex(c.Ins, store: true); if (num >= 0) { if (depth > 0) { return Use.Unknown; } bool flag = false; bool flag2 = true; string text = null; Enumerator<Instruction> enumerator = owner.Body.Instructions.GetEnumerator(); try { while (enumerator.MoveNext()) { Instruction current = enumerator.Current; if (LocalIndex(current, store: false) != num) { continue; } flag = true; string why2; Use use = Classify(owner, Final(owner, current.Next, rules), rules, hooked, depth + 1, out why2); switch (use) { case Use.Id: why = "local -> " + why2; return Use.Id; case Use.Keep: if (text == null) { text = why2; } break; } if (use != Use.Display) { flag2 = false; } } } finally { ((IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } if (text != null) { why = "local -> " + text; return Use.Keep; } if (!(flag && flag2)) { return Use.Unknown; } return Use.Display; } if (!c.IsCall) { return Use.Unknown; } MethodReference method = c.Method; if (method == null) { return Use.Unknown; } string text2 = TypeName(((MemberReference)method).DeclaringType); string name = ((MemberReference)method).Name; int param = c.Param; MethodReference elementMethod = method.GetElementMethod(); MethodDefinition val = (MethodDefinition)(object)((elementMethod is MethodDefinition) ? elementMethod : null); if (val != null && hooked != null && hooked.TryGetValue(val, out var value) && value.Contains(param)) { return Use.Display; } for (int i = 0; i < rules.Display.Count; i++) { if (Match(rules.Display[i], method, text2, name, param)) { return Use.Display; } } for (int j = 0; j < rules.Keep.Count; j++) { if (Match(rules.Keep[j], method, text2, name, param)) { why = text2 + "::" + name; return Use.Keep; } } for (int k = 0; k < rules.Veto.Count; k++) { if (Match(rules.Veto[k], method, text2, name, param)) { why = text2 + "::" + name; return Use.Id; } } Use use2 = Builtin(method, text2, name, param, code); if (use2 == Use.Id || use2 == Use.Keep) { why = text2 + "::" + name; } return use2; } private static int LocalIndex(Instruction ins, bool store) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: 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) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected I4, but got Unknown //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Invalid comparison between Unknown and I4 //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Invalid comparison between Unknown and I4 OpCode opCode = ins.OpCode; Code code = ((OpCode)(ref opCode)).Code; switch (code - 6) { default: if ((int)code != 202) { if ((int)code != 204) { break; } goto case 13; } goto case 11; case 4: if (!store) { return -1; } return 0; case 5: if (!store) { return -1; } return 1; case 6: if (!store) { return -1; } return 2; case 7: if (!store) { return -1; } return 3; case 13: if (!store) { return -1; } return Index(ins.Operand); case 0: if (!store) { return 0; } return -1; case 1: if (!store) { return 1; } return -1; case 2: if (!store) { return 2; } return -1; case 3: if (!store) { return 3; } return -1; case 11: if (!store) { return Index(ins.Operand); } return -1; case 8: case 9: case 10: case 12: break; } return -1; } private static int Index(object operand) { VariableDefinition val = (VariableDefinition)((operand is VariableDefinition) ? operand : null); if (val != null) { return ((VariableReference)val).Index; } return -1; } private static Use Builtin(MethodReference m, string dt, string name, int param, Code code) { //IL_0530: Unknown result type (might be due to invalid IL or missing references) //IL_0534: Invalid comparison between Unknown and I4 if (dt == "System.String") { switch (name) { case "op_Equality": case "op_Inequality": case "Equals": case "StartsWith": case "EndsWith": case "Contains": case "IndexOf": case "LastIndexOf": case "IndexOfAny": case "Compare": case "CompareOrdinal": case "CompareTo": case "Split": case "IsNullOrEmpty": case "IsNullOrWhiteSpace": case "Intern": return Use.Id; case "Replace": if (param != 0) { return Use.Unknown; } return Use.Id; case "Trim": case "TrimStart": case "TrimEnd": if (param < 0) { return Use.Unknown; } return Use.Id; default: return Use.Unknown; } } if (dt.StartsWith("System.Collections.", StringComparison.Ordinal)) { switch (name) { case "ContainsKey": case "TryGetValue": case "get_Item": case "Contains": case "IndexOf": case "LastIndexOf": case "Remove": case "TryRemove": case "GetValueOrDefault": case "ContainsValue": return Use.Id; case "set_Item": case "Add": case "TryAdd": case ".ctor": if (dt.IndexOf("Dictionary", StringComparison.Ordinal) >= 0 || dt.IndexOf("SortedList", StringComparison.Ordinal) >= 0 || dt.IndexOf("Hashtable", StringComparison.Ordinal) >= 0 || dt.IndexOf("KeyValuePair", StringComparison.Ordinal) >= 0) { if (param != 0) { return Use.Unknown; } return Use.Id; } if (dt.IndexOf("Set`", StringComparison.Ordinal) >= 0) { return Use.Id; } return Use.Unknown; default: return Use.Unknown; } } if (dt.StartsWith("System.", StringComparison.Ordinal)) { if ((int)code == 114 && dt.EndsWith("Exception", StringComparison.Ordinal)) { return Use.Keep; } switch (dt) { case "System.Array": case "System.Linq.Enumerable": switch (name) { default: return Use.Unknown; case "IndexOf": case "LastIndexOf": case "Contains": case "BinarySearch": return Use.Id; } case "System.Console": return Use.Keep; case "System.Enum": case "System.Type": case "System.Activator": case "System.Convert": case "System.Environment": case "System.Guid": case "System.Version": case "System.Uri": case "System.AppDomain": case "System.Text.Encoding": case "System.StringComparer": case "System.Int16": case "System.Int32": case "System.Int64": case "System.UInt16": case "System.UInt32": case "System.UInt64": case "System.Byte": case "System.SByte": case "System.Single": case "System.Double": case "System.Decimal": case "System.Boolean": case "System.Char": case "System.DateTime": case "System.TimeSpan": case "System.Math": return Use.Id; case "System.Text.StringBuilder": if (!(name == "Replace") || param != 0) { return Use.Unknown; } return Use.Id; default: if (dt.StartsWith("System.Diagnostics.", StringComparison.Ordinal)) { return Use.Keep; } if (dt.StartsWith("System.Reflection.", StringComparison.Ordinal) || dt.StartsWith("System.IO.", StringComparison.Ordinal) || dt.StartsWith("System.Text.RegularExpressions.", StringComparison.Ordinal) || dt.StartsWith("System.Net.", StringComparison.Ordinal) || dt.StartsWith("System.Threading.", StringComparison.Ordinal) || dt.StartsWith("System.Globalization.", StringComparison.Ordinal) || dt.StartsWith("System.Runtime.", StringComparison.Ordinal) || dt.StartsWith("System.Security.", StringComparison.Ordinal) || dt.StartsWith("System.Xml.", StringComparison.Ordinal)) { return Use.Id; } return Use.Unknown; } } if (dt.StartsWith("BepInEx.Configuration.", StringComparison.Ordinal)) { if (name == "Bind") { if (m.Parameters.Count != 4 || param != 3 || !(((MemberReference)((ParameterReference)m.Parameters[3]).ParameterType).FullName == "System.String")) { return Use.Id; } return Use.Display; } if (dt == "BepInEx.Configuration.ConfigDescription" && name == ".ctor") { if (param != 0) { return Use.Id; } return Use.Display; } return Use.Id; } if (dt.StartsWith("BepInEx.Logging.", StringComparison.Ordinal)) { return Use.Keep; } if (dt.StartsWith("BepInEx.", StringComparison.Ordinal)) { return Use.Id; } if (dt.StartsWith("HarmonyLib.", StringComparison.Ordinal) || dt.StartsWith("MonoMod.", StringComparison.Ordinal) || dt.StartsWith("Mono.", StringComparison.Ordinal) || dt.StartsWith("Photon.", StringComparison.Ordinal) || dt.StartsWith("ExitGames.", StringComparison.Ordinal) || dt.StartsWith("Steamworks.", StringComparison.Ordinal)) { return Use.Id; } if (dt.StartsWith("UnityEngine.", StringComparison.Ordinal)) { if (IsUnityText(dt, name)) { return Use.Display; } switch (dt) { case "UnityEngine.Debug": case "UnityEngine.Logger": return Use.Keep; case "UnityEngine.GUIUtility": if (name == "set_systemCopyBuffer") { return Use.Keep; } break; } return Use.Id; } if (dt.StartsWith("TMPro.", StringComparison.Ordinal)) { if (!(name == "set_text") && !(name == "SetText")) { return Use.Id; } return Use.Display; } return Use.Unknown; } private static bool IsUnityText(string dt, string name) { switch (dt) { case "UnityEngine.GUI": case "UnityEngine.GUILayout": return true; case "UnityEngine.GUIContent": if (!(name == ".ctor") && !(name == "set_text")) { return name == "set_tooltip"; } return true; case "UnityEngine.TextMesh": case "UnityEngine.UI.Text": return name == "set_text"; default: return false; } } public static bool IsTokenLike(string s) { if (s.Length == 0 || s.Length > 40) { return false; } bool result = false; foreach (char c in s) { if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { result = true; } else if ((c < '0' || c > '9') && c != '_' && c != '.' && c != '-' && c != '+' && c != '/' && c != ':' && c != '#' && c != '%') { return false; } } return result; } public static string Describe(Consumer c) { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) if (c.Ins == null) { return "?"; } MethodReference method = c.Method; if (method != null) { return TypeName(((MemberReference)method).DeclaringType) + "::" + ((MemberReference)method).Name + "#" + c.Param; } OpCode opCode = c.Ins.OpCode; return ((OpCode)(ref opCode)).Name; } } internal sealed class Report { public string OriginalVersion = "?"; public string NewVersion = "?"; public int Methods; public int Literals; public int Replaced; public int ReplacedScoped; public int SkippedVeto; public int SkippedToken; public int SkippedType; public int SkippedProbe; public int SkippedSentence; public int Probes; public int HooksApplied; public int CallHooks; public int HookRulesMissed; public int FontHooks; public int FontRulesMissed; public int DataReplaced; public int DataFiles; public int EnumsKnown; public int EnumsAnswered; public int AssemblyRequests; public readonly HashSet<string> UsedGlobal = new HashSet<string>(StringComparer.Ordinal); public readonly HashSet<string> UsedScoped = new HashSet<string>(StringComparer.Ordinal); public readonly HashSet<string> UsedData = new HashSet<string>(StringComparer.Ordinal); public readonly List<string> Notes = new List<string>(); public readonly List<string> Dump = new List<string>(); public readonly List<string> DataDump = new List<string>(); public readonly Dictionary<string, byte[]> Resources = new Dictionary<string, byte[]>(StringComparer.Ordinal); } internal sealed class BuildOptions { public Settings Settings; public Lang Lang; public Rules Rules; public string RuntimeAssemblyName = "RepoRPG_RU"; public Version RuntimeVersion = new Version(0, 0, 0, 0); public bool HaveRuntime; public bool Dump; public string Stamp; public bool RequirePlugin = true; public List<string> SearchDirs = new List<string>(); } internal static class Builder { private sealed class Edit { public int Index; public string Value; } private sealed class Plan { public readonly Dictionary<uint, List<Edit>> Edits = new Dictionary<uint, List<Edit>>(); public readonly Dictionary<uint, List<int>> Hooks = new Dictionary<uint, List<int>>(); public readonly Dictionary<uint, List<int>> Shims = new Dictionary<uint, List<int>>(); public readonly Dictionary<uint, int> Fonts = new Dictionary<uint, int>(); public readonly Dictionary<uint, int> FontAdded = new Dictionary<uint, int>(); public readonly Dictionary<uint, int> Counts = new Dictionary<uint, int>(); public readonly Dictionary<uint, string> Names = new Dictionary<uint, string>(); public readonly List<Probe> Probes = new List<Probe>(); public HashSet<string> Free; } private sealed class Occurrence { public uint Token; public int Index; public string Scope; public string TypeKey; public string Value; public string Translated; public string Why; public bool Scoped; public bool Skipped; public Use Use; } private sealed class Expect { public int Count; public int Shift; public int Font; public List<int> Shims; public List<Edit> Edits; public bool Seen; public int Moved(int index) { int num = index + Shift; if (Shims != null) { for (int i = 0; i < Shims.Count; i++) { if (Shims[i] < index) { num++; } } } return num; } } private const string GuardType = "<RepoRPG_RU>"; public static byte[] Build(byte[] original, BuildOptions o, Report report) { Plan plan = MakePlan(original, o, report); byte[] result = Apply(original, o, plan, report); Verify(result, plan, report); return result; } private static AssemblyDefinition Read(byte[] bytes) { return Read(bytes, null, null); } private static AssemblyDefinition Read(byte[] bytes, IAssemblyResolver assemblies, IMetadataResolver metadata) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ReaderParameters val = new ReaderParameters(); val.ReadingMode = (ReadingMode)2; val.InMemory = true; val.ReadSymbols = false; if (assemblies != null) { val.AssemblyResolver = assemblies; } if (metadata != null) { val.MetadataResolver = metadata; } return AssemblyDefinition.ReadAssembly((Stream)new MemoryStream(bytes, writable: false), val); } private static IEnumerable<TypeDefinition> AllTypes(ModuleDefinition module) { Stack<TypeDefinition> todo = new Stack<TypeDefinition>(); for (int num = module.Types.Count - 1; num >= 0; num--) { todo.Push(module.Types[num]); } while (todo.Count > 0) { TypeDefinition t = todo.Pop(); yield return t; if (t.HasNestedTypes) { for (int num2 = t.NestedTypes.Count - 1; num2 >= 0; num2--) { todo.Push(t.NestedTypes[num2]); } } } } private static Plan MakePlan(byte[] original, BuildOptions o, Report report) { //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_029c: Unknown result type (might be due to invalid IL or missing references) //IL_02a1: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_032b: Unknown result type (might be due to invalid IL or missing references) //IL_0330: Unknown result type (might be due to invalid IL or missing references) //IL_0504: Unknown result type (might be due to invalid IL or missing references) //IL_0509: Unknown result type (might be due to invalid IL or missing references) //IL_053e: Unknown result type (might be due to invalid IL or missing references) //IL_0543: Unknown result type (might be due to invalid IL or missing references) //IL_068e: Unknown result type (might be due to invalid IL or missing references) //IL_0693: Unknown result type (might be due to invalid IL or missing references) //IL_0697: Unknown result type (might be due to invalid IL or missing references) //IL_069e: Invalid comparison between Unknown and I4 //IL_05bc: Unknown result type (might be due to invalid IL or missing references) //IL_05c1: Unknown result type (might be due to invalid IL or missing references) //IL_05c5: Unknown result type (might be due to invalid IL or missing references) //IL_05cc: Invalid comparison between Unknown and I4 //IL_03bb: Unknown result type (might be due to invalid IL or missing references) //IL_03c0: Unknown result type (might be due to invalid IL or missing references) //IL_03d8: Unknown result type (might be due to invalid IL or missing references) //IL_03dd: Unknown result type (might be due to invalid IL or missing references) //IL_05d0: Unknown result type (might be due to invalid IL or missing references) //IL_05d5: Unknown result type (might be due to invalid IL or missing references) //IL_05d9: Unknown result type (might be due to invalid IL or missing references) //IL_05e0: Invalid comparison between Unknown and I4 //IL_0606: Unknown result type (might be due to invalid IL or missing references) //IL_060b: Unknown result type (might be due to invalid IL or missing references) //IL_060f: Unknown result type (might be due to invalid IL or missing references) //IL_0615: Invalid comparison between Unknown and I4 Plan plan = new Plan(); plan.Free = ((o.Rules != null) ? o.Rules.ProbeFree : null); AssemblyDefinition val = Read(original); try { ModuleDefinition mainModule = val.MainModule; Rules rules = o.Rules; Lang lang = o.Lang; Dictionary<MethodDefinition, HashSet<int>> dictionary = new Dictionary<MethodDefinition, HashSet<int>>(); foreach (HookRule hook in rules.Hooks) { int num = 0; foreach (TypeDefinition item in AllTypes(mainModule)) { if (!Analysis.TypeMatches(hook.Type, ((MemberReference)item).FullName.Replace('/', '.'))) { continue; } Enumerator<MethodDefinition> enumerator3 = item.Methods.GetEnumerator(); try { while (enumerator3.MoveNext()) { MethodDefinition current3 = enumerator3.Current; if (((MemberReference)current3).Name != hook.Method || !current3.HasBody) { continue; } Enumerator<ParameterDefinition> enumerator4 = ((MethodReference)current3).Parameters.GetEnumerator(); try { while (enumerator4.MoveNext()) { ParameterDefinition current4 = enumerator4.Current; if (Array.IndexOf(hook.Params, ((ParameterReference)current4).Name) >= 0 && !(((MemberReference)((ParameterReference)current4).ParameterType).FullName != "System.String")) { if (!dictionary.TryGetValue(current3, out var value)) { value = (dictionary[current3] = new HashSet<int>()); } if (value.Add(((ParameterReference)current4).Index)) { num++; } } } } finally { ((IDisposable)enumerator4/*cast due to .constrained prefix*/).Dispose(); } } } finally { ((IDisposable)enumerator3/*cast due to .constrained prefix*/).Dispose(); } } if (num == 0) { report.HookRulesMissed++; report.Notes.Add("hook not found: " + hook.Type + "::" + hook.Method + "(" + string.Join(", ", hook.Params) + ") [" + hook.Source + "]"); } } bool flag = o.Settings.DisplayHooks && o.HaveRuntime; MetadataToken metadataToken; if (flag) { foreach (KeyValuePair<MethodDefinition, HashSet<int>> item2 in dictionary) { List<int> list = new List<int>(item2.Value); list.Sort(); Dictionary<uint, List<int>> hooks = plan.Hooks; metadataToken = ((MemberReference)item2.Key).MetadataToken; hooks[((MetadataToken)(ref metadataToken)).ToUInt32()] = list; } } foreach (HookRule font in rules.Fonts) { int num2 = 0; foreach (TypeDefinition item3 in AllTypes(mainModule)) { if (!Analysis.TypeMatches(font.Type, ((MemberReference)item3).FullName.Replace('/', '.'))) { continue; } Enumerator<MethodDefinition> enumerator3 = item3.Methods.GetEnumerator(); try { while (enumerator3.MoveNext()) { MethodDefinition current8 = enumerator3.Current; if (!(((MemberReference)current8).Name != font.Method) && current8.HasBody && !((MethodReference)current8).HasParameters && current8.Body.Instructions.Count != 0 && !(((MemberReference)((MethodReference)current8).ReturnType).FullName != "UnityEngine.Font")) { num2++; if (flag) { int num3 = FontSetting(current8.Body); Dictionary<uint, int> fonts = plan.Fonts; metadataToken = ((MemberReference)current8).MetadataToken; fonts[((MetadataToken)(ref metadataToken)).ToUInt32()] = num3; Dictionary<uint, int> fontAdded = plan.FontAdded; metadataToken = ((MemberReference)current8).MetadataToken; fontAdded[((MetadataToken)(ref metadataToken)).ToUInt32()] = FontAdded(current8.Body, 0, num3); } } } } finally { ((IDisposable)enumerator3/*cast due to .constrained prefix*/).Dispose(); } } if (num2 == 0) { report.FontRulesMissed++; report.Notes.Add("font method not found: " + font.Type + "::" + font.Method + " [" + font.Source + "]"); } } bool translateStrings = o.Settings.TranslateStrings; List<Occurrence> list2 = new List<Occurrence>(); HashSet<string> hashSet2 = new HashSet<string>(StringComparer.Ordinal); HashSet<string> hashSet3 = new HashSet<string>(StringComparer.Ordinal); foreach (TypeDefinition item4 in AllTypes(mainModule)) { string text = Analysis.TypeKey(item4); Enumerator<MethodDefinition> enumerator3 = item4.Methods.GetEnumerator(); try { while (enumerator3.MoveNext()) { MethodDefinition current10 = enumerator3.Current; if (!current10.HasBody) { continue; } report.Methods++; MethodBody body = current10.Body; metadataToken = ((MemberReference)current10).MetadataToken; uint num4 = ((MetadataToken)(ref metadataToken)).ToUInt32(); plan.Counts[num4] = body.Instructions.Count; plan.Names[num4] = ((MemberReference)current10).FullName; string text2 = null; Dictionary<string, string> value2 = null; Dictionary<string, string> value3 = null; bool skipped = false; OpCode opCode; if (flag && rules.Calls.Count > 0) { List<int> list3 = null; for (int i = 0; i < body.Instructions.Count; i++) { Instruction val2 = body.Instructions[i]; opCode = val2.OpCode; if ((int)((OpCode)(ref opCode)).Code != 39) { opCode = val2.OpCode; if ((int)((OpCode)(ref opCode)).Code != 110) { continue; } } object operand = val2.Operand; if (!IsCallHook(rules, (MethodReference)((operand is MethodReference) ? operand : null))) { continue; } if (val2.Previous != null) { opCode = val2.Previous.OpCode; if ((int)((OpCode)(ref opCode)).OpCodeType == 4) { continue; } } if (list3 == null) { if (IsSkipped(rules, text, text + "::" + Analysis.MemberName(current10))) { break; } list3 = new List<int>(); } list3.Add(i); } if (list3 != null) { plan.Shims[num4] = list3; } } for (int j = 0; j < body.Instructions.Count; j++) { Instruction val3 = body.Instructions[j]; opCode = val3.OpCode; if ((int)((OpCode)(ref opCode)).Code != 113 || !(val3.Operand is string text3)) { continue; } report.Literals++; if (text3.Length == 0) { continue; } if (text2 == null) { text2 = text + "::" + Analysis.MemberName(current10); lang.Scoped.TryGetValue(text2, out value2); lang.Scoped.TryGetValue(text + "::*", out value3); skipped = IsSkipped(rules, text, text2); } string value4 = null; bool flag2 = (value2 != null && value2.TryGetValue(text3, out value4)) || (value3?.TryGetValue(text3, out value4) ?? false); bool flag3 = !flag2 && lang.Global.TryGetValue(text3, out value4); Consumer c = Analysis.Final(current10, val3.Next, rules); if (Analysis.IsProbe(c, out var kind, out var ignoreCase)) { Probe probe = new Probe(); probe.Text = text3; probe.Kind = kind; probe.IgnoreCase = ignoreCase; probe.Where = text2; if (hashSet3.Add(probe.Key) && Analysis.ProbeMatters(probe, rules)) { plan.Probes.Add(probe); } } if (o.Dump || flag2 || flag3 || lang.Terms.ContainsKey(text3)) { string why; Use use = Analysis.Classify(current10, c, rules, dictionary, 0, out why); if (use == Use.Id) { hashSet2.Add(text3); } if (o.Dump) { report.Dump.Add(text2 + "\t" + use.ToString() + "\t" + Analysis.Describe(c) + "\t" + Escape(text3)); } if (flag2 || flag3) { Occurrence occurrence = new Occurrence(); occurrence.Token = num4; occurrence.Index = j; occurrence.Scope = text2; occurrence.TypeKey = text; occurrence.Value = text3; occurrence.Translated = value4; occurrence.Scoped = flag2; occurrence.Skipped = skipped; occurrence.Use = use; occurrence.Why = why ?? Analysis.Describe(c); list2.Add(occurrence); } } } } } finally { ((IDisposable)enumerator3/*cast due to .constrained prefix*/).Dispose(); } } report.Probes = plan.Probes.Count; if (!translateStrings) { return plan; } List<Occurrence> list4 = new List<Occurrence>(); Dictionary<string, HashSet<string>> dictionary2 = new Dictionary<string, HashSet<string>>(StringComparer.Ordinal); foreach (KeyValuePair<string, HashSet<string>> item5 in lang.Unsafe) { dictionary2[item5.Key] = new HashSet<string>(item5.Value, StringComparer.Ordinal); } foreach (Occurrence item6 in list2) { string text4 = Refusal(item6, plan, hashSet2, report); if (text4 == null) { list4.Add(item6); continue; } report.Notes.Add(text4 + ": " + item6.Scope + " \"" + Escape(item6.Value) + "\""); if (item6.Scoped) { if (!dictionary2.TryGetValue(item6.Scope, out var value5)) { value5 = new HashSet<string>(StringComparer.Ordinal); dictionary2[item6.Scope] = value5; } value5.Add(item6.Value); } } foreach (KeyValuePair<string, HashSet<string>> item7 in dictionary2) { if (!lang.Sentences.TryGetValue(item7.Key, out var value6)) { continue; } HashSet<string> value7 = item7.Value; bool flag4 = true; while (flag4) { flag4 = false; foreach (List<string> value9 in value6.Values) { bool flag5 = false; foreach (string item8 in value9) { if (value7.Contains(item8)) { flag5 = true; break; } } if (!flag5) { continue; } foreach (string item9 in value9) { if (value7.Add(item9)) { flag4 = true; } } } } } foreach (Occurrence item10 in list4) { if (item10.Scoped && dictionary2.TryGetValue(item10.Scope, out var value8) && value8.Contains(item10.Value)) { report.SkippedSentence++; report.Notes.Add("left to display time (another piece of the same sentence stays as it is): " + item10.Scope + " \"" + Escape(item10.Value) + "\""); } else { AddEdit(plan, item10.Token, item10.Index, item10.Translated); report.Replaced++; if (item10.Scoped) { report.ReplacedScoped++; report.UsedScoped.Add(item10.Scope + "\n" + item10.Value); report.UsedScoped.Add(item10.TypeKey + "::*\n" + item10.Value); } else { report.UsedGlobal.Add(item10.Value); } } } return plan; } finally { ((IDisposable)val)?.Dispose(); } } private static string Refusal(Occurrence occ, Plan plan, HashSet<string> usedAsId, Report report) { if (occ.Skipped) { report.SkippedType++; return "skipped (excluded type)"; } if (occ.Use == Use.Id || occ.Use == Use.Keep) { report.SkippedVeto++; return (occ.Scoped ? "scoped entry ignored, " : "kept, ") + ((occ.Use == Use.Id) ? "used as id" : "log/network") + " (" + occ.Why + ")"; } if (!occ.Scoped && occ.Use != Use.Display) { bool flag = Analysis.IsTokenLike(occ.Value); if (flag || usedAsId.Contains(occ.Value)) { report.SkippedToken++; return "left to display time (" + (flag ? "single word" : "also used as id") + ", " + occ.Why + ")"; } } Probe probe = BrokenProbe(plan.Probes, plan.Free, occ.Value, occ.Translated); if (probe != null) { report.SkippedProbe++; return "left to display time (the mod tests texts with " + probe?.ToString() + " in " + probe.Where + ")"; } return null; } private static Probe BrokenProbe(List<Probe> probes, HashSet<string> free, string original, string translated) { if (free != null && free.Contains(original)) { return null; } for (int i = 0; i < probes.Count; i++) { Probe probe = probes[i]; if (probe.Hits(original) != probe.Hits(translated)) { return probe; } } return null; } private static bool IsCallHook(Rules rules, MethodReference target) { if (target == null || !target.HasParameters) { return false; } int num = target.Parameters.Count - 1; if (((MemberReference)((ParameterReference)target.Parameters[num]).ParameterType).FullName != "System.String") { return false; } string dt = Analysis.TypeName(((MemberReference)target).DeclaringType); for (int i = 0; i < rules.Calls.Count; i++) { if (Analysis.Match(rules.Calls[i], target, dt, ((MemberReference)target).Name, num)) { return true; } } return false; } private static bool IsSkipped(Rules rules, string typeKey, string scope) { for (int i = 0; i < rules.Skip.Count; i++) { string text = rules.Skip[i]; if (text.IndexOf("::", StringComparison.Ordinal) >= 0) { if (Rules.Glob(text, scope)) { return true; } } else if (Rules.Glob(text, typeKey) || typeKey.StartsWith(text + ".", StringComparison.Ordinal)) { return true; } } return false; } private static void AddEdit(Plan plan, uint token, int index, string value) { if (!plan.Edits.TryGetValue(token, out var value2)) { value2 = new List<Edit>(); plan.Edits[token] = value2; } Edit edit = new Edit(); edit.Index = index; edit.Value = value; value2.Add(edit); } private static byte[] Apply(byte[] original, BuildOptions o, Plan plan, Report report) { DirectoryResolver directoryResolver = new DirectoryResolver(o.SearchDirs); EnumResolver enumResolver = (EnumResolver)(object)(directoryResolver.Metadata = (IMetadataResolver)(object)new EnumResolver((IAssemblyResolver)(object)directoryResolver)); try { return Apply(original, o, plan, report, directoryResolver, enumResolver); } finally { report.EnumsKnown = enumResolver.Count; report.EnumsAnswered = enumResolver.Answered; report.AssemblyRequests = directoryResolver.Requests; directoryResolver.Dispose(); } } private static byte[] Apply(byte[] original, BuildOptions o, Plan plan, Report report, DirectoryResolver directories, EnumResolver enums) { //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0828: Unknown result type (might be due to invalid IL or missing references) //IL_082d: Unknown result type (might be due to invalid IL or missing references) //IL_0818: Unknown result type (might be due to invalid IL or missing references) //IL_0822: Expected O, but got Unknown //IL_0534: Unknown result type (might be due to invalid IL or missing references) //IL_0539: Unknown result type (might be due to invalid IL or missing references) //IL_053d: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Invalid comparison between Unknown and I4 //IL_05b6: Unknown result type (might be due to invalid IL or missing references) //IL_05d4: Unknown result type (might be due to invalid IL or missing references) //IL_05e4: Unknown result type (might be due to invalid IL or missing references) //IL_0601: Unknown result type (might be due to invalid IL or missing references) //IL_061b: Unknown result type (might be due to invalid IL or missing references) //IL_062e: Unknown result type (might be due to invalid IL or missing references) //IL_0643: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Unknown result type (might be due to invalid IL or missing references) //IL_022a: Unknown result type (might be due to invalid IL or missing references) //IL_0231: Invalid comparison between Unknown and I4 //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_023a: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Unknown result type (might be due to invalid IL or missing references) //IL_0245: Invalid comparison between Unknown and I4 //IL_028c: Unknown result type (might be due to invalid IL or missing references) //IL_029c: Unknown result type (might be due to invalid IL or missing references) //IL_03db: Unknown result type (might be due to invalid IL or missing references) //IL_03f2: Unknown result type (might be due to invalid IL or missing references) //IL_0408: Unknown result type (might be due to invalid IL or missing references) AssemblyDefinition val = Read(original, (IAssemblyResolver)(object)directories, (IMetadataResolver)(object)enums); try { ModuleDefinition mainModule = val.MainModule; OpCode opCode; foreach (KeyValuePair<uint, List<Edit>> edit in plan.Edits) { IMetadataTokenProvider obj = mainModule.LookupToken((int)edit.Key); MethodDefinition val2 = (MethodDefinition)(object)((obj is MethodDefinition) ? obj : null); if (val2 == null || !val2.HasBody) { throw new InvalidOperationException("method token " + edit.Key.ToString("x8") + " vanished"); } MethodBody body = val2.Body; foreach (Edit item in edit.Value) { Instruction obj2 = body.Instructions[item.Index]; opCode = obj2.OpCode; if ((int)((OpCode)(ref opCode)).Code != 113) { throw new InvalidOperationException("instruction mismatch in " + ((MemberReference)val2).FullName); } obj2.Operand = item.Value; } } HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); Enumerator<AssemblyNameReference> enumerator3 = mainModule.AssemblyReferences.GetEnumerator(); try { while (enumerator3.MoveNext()) { AssemblyNameReference current3 = enumerator3.Current; hashSet.Add(current3.Name); } } finally { ((IDisposable)enumerator3/*cast due to .constrained prefix*/).Dispose(); } MethodDefinition val3 = null; foreach (KeyValuePair<uint, List<int>> shim in plan.Shims) { IMetadataTokenProvider obj3 = mainModule.LookupToken((int)shim.Key); MethodDefinition val4 = (MethodDefinition)(object)((obj3 is MethodDefinition) ? obj3 : null); if (val4 == null || !val4.HasBody) { throw new InvalidOperationException("call hook target " + shim.Key.ToString("x8") + " vanished"); } MethodBody body2 = val4.Body; ILProcessor iLProcessor = body2.GetILProcessor(); int num = shim.Value.Count - 1; while (num >= 0) { Instruction val5 = body2.Instructions[shim.Value[num]]; object operand = val5.Operand; MethodReference val6 = (MethodReference)((operand is MethodReference) ? operand : null); if (val6 != null) { opCode = val5.OpCode; if ((int)((OpCode)(ref opCode)).Code != 39) { opCode = val5.OpCode; if ((int)((OpCode)(ref opCode)).Code != 110) { goto IL_0247; } } if (val3 == null) { val3 = AddGuard(mainModule, o, ((ParameterReference)val6.Parameters[val6.Parameters.Count - 1]).ParameterType); } Instruction val7 = iLProcessor.Create(val5.OpCode, val6); val5.OpCode = OpCodes.Call; val5.Operand = val3; iLProcessor.InsertAfter(val5, val7); report.CallHooks++; num--; continue; } goto IL_0247; IL_0247: throw new InvalidOperationException("instruction mismatch in " + ((MemberReference)val4).FullName); } } if (plan.Hooks.Count > 0) { foreach (KeyValuePair<uint, List<int>> hook in plan.Hooks) { IMetadataTokenProvider obj4 = mainModule.LookupToken((int)hook.Key); MethodDefinition val8 = (MethodDefinition)(object)((obj4 is MethodDefinition) ? obj4 : null); if (val8 == null || !val8.HasBody) { throw new InvalidOperationException("hook target " + hook.Key.ToString("x8") + " vanished"); } MethodBody body3 = val8.Body; if (body3.Instructions.Count == 0) { continue; } ILProcessor iLProcessor2 = body3.GetILProcessor(); Instruction val9 = body3.Instructions[0]; foreach (int item2 in hook.Value) { ParameterDefinition val10 = ((MethodReference)val8).Parameters[item2]; if (val3 == null) { val3 = AddGuard(mainModule, o, ((ParameterReference)val10).ParameterType); } iLProcessor2.InsertBefore(val9, iLProcessor2.Create(OpCodes.Ldarg, val10)); iLProcessor2.InsertBefore(val9, iLProcessor2.Create(OpCodes.Call, (MethodReference)(object)val3)); iLProcessor2.InsertBefore(val9, iLProcessor2.Create(OpCodes.Starg, val10)); report.HooksApplied++; } } } foreach (KeyValuePair<uint, int> item3 in new List<KeyValuePair<uint, int>>(plan.Fonts)) { IMetadataTokenProvider obj5 = mainModule.LookupToken((int)item3.Key); MethodDefinition val11 = (MethodDefinition)(object)((obj5 is MethodDefinition) ? obj5 : null); if (val11 == null || !val11.HasBody) { throw new InvalidOperationException("font method " + item3.Key.ToString("x8") + " vanished"); } MethodBody body4 = val11.Body; ILProcessor iLProcessor3 = body4.GetILProcessor(); List<Instruction> list = new List<Instruction>(); Instruction val12; try { int num2 = 0; if (plan.Hooks.TryGetValue(item3.Key, out var value)) { num2 = value.Count * 3; } val12 = body4.Instructions[num2]; TypeReference val13 = null; for (int i = 0; i < item3.Value; i++) { Instruction val14 = body4.Instructions[num2 + i]; opCode = val14.OpCode; if ((int)((OpCode)(ref opCode)).Code != 0) { list.Add(Copy(iLProcessor3, val14)); object operand2 = val14.Operand; MethodReference val15 = (MethodReference)((operand2 is MethodReference) ? operand2 : null); if (val15 != null && ((MemberReference)((MemberReference)val15).DeclaringType).FullName == "System.String") { val13 = ((MemberReference)val15).DeclaringType; } } } if (val13 == null) { val13 = mainModule.TypeSystem.String; } if (list.Count == 0) { list.Add(iLProcessor3.Create(OpCodes.Ldnull)); } if (val3 == null) { val3 = AddGuard(mainModule, o, val13); } Instruction val16 = iLProcessor3.Create(OpCodes.Pop); list.Add(iLProcessor3.Create(OpCodes.Call, (MethodReference)(object)AddFontGuard(mainModule, o, val3, val13))); list.Add(iLProcessor3.Create(OpCodes.Isinst, ((MethodReference)val11).ReturnType)); list.Add(iLProcessor3.Create(OpCodes.Dup)); list.Add(iLProcessor3.Create(OpCodes.Brfalse, val16)); list.Add(iLProcessor3.Create(OpCodes.Ret)); list.Add(val16); if (list.Count != FontAdded(body4, num2, item3.Value)) { throw new InvalidOperationException("plan and result differ"); } } catch (Exception ex) { plan.Fonts.Remove(item3.Key); plan.FontAdded.Remove(item3.Key); report.FontRulesMissed++; report.Notes.Add("font method " + ((MemberReference)val11.DeclaringType).Name + "::" + ((MemberReference)val11).Name + " left as it is: " + ex.Message); continue; } foreach (Instruction item4 in list) { iLProcessor3.InsertBefore(val12, item4); } report.FontHooks++; if (item3.Value == 0) { report.Notes.Add("font method " + ((MemberReference)val11.DeclaringType).Name + "::" + ((MemberReference)val11).Name + ": does not begin by reading its setting the way 1.0.100 does - the add-on reads the setting from the mod's config file instead"); } } if (o.RequirePlugin) { BumpVersion(mainModule, report); } if (o.Settings.TranslateData || o.Dump) { TranslateData(mainModule, o, report, plan.Probes); } if (o.Stamp != null) { mainModule.Resources.Add((Resource)new EmbeddedResource("RepoRPG_RU.stamp", (ManifestResourceAttributes)2, Encoding.UTF8.GetBytes(o.Stamp))); } enumerator3 = mainModule.AssemblyReferences.GetEnumerator(); try { while (enumerator3.MoveNext()) { AssemblyNameReference current9 = enumerator3.Current; if (!hashSet.Contains(current9.Name) && current9.Name != o.RuntimeAssemblyName) { throw new InvalidOperationException("unexpected new assembly reference: " + current9.Name); } } } finally { ((IDisposable)enumerator3/*cast due to .constrained prefix*/).Dispose(); } enums.Learn(mainModule, AllTypes(mainModule)); MemoryStream memoryStream = new MemoryStream(original.Length + 65536); val.Write((Stream)memoryStream); return memoryStream.ToArray(); } finally { ((IDisposable)val)?.Dispose(); } } private static int FontSetting(MethodBody body) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Invalid comparison between Unknown and I4 //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Invalid comparison between Unknown and I4 //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Invalid comparison between Unknown and I4 //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Invalid comparison between Unknown and I4 //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Invalid comparison between Unknown and I4 //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Invalid comparison between Unknown and I4 //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Invalid comparison between Unknown and I4 //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Invalid comparison between Unknown and I4 //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Invalid comparison between Unknown and I4 //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Invalid comparison between Unknown and I4 //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Invalid comparison between Unknown and I4 //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Invalid comparison between Unknown and I4 for (int i = 0; i < body.Instructions.Count && i < 10; i++) { Instruction val = body.Instructions[i]; OpCode opCode = val.OpCode; Code code = ((OpCode)(ref opCode)).Code; if ((int)code <= 39) { if ((int)code == 0 || (int)code == 2) { continue; } if ((int)code == 39) { goto IL_0059; } } else { if ((int)code == 110) { goto IL_0059; } if ((int)code == 120 || (int)code == 123) { continue; } } return 0; IL_0059: object operand = val.Operand; MethodReference val2 = (MethodReference)((operand is MethodReference) ? operand : null); if (val2 == null) { return 0; } if (((MemberReference)val2).Name == "Trim" && !val2.HasParameters && ((MemberReference)((MemberReference)val2).DeclaringType).FullName == "System.String") { Instruction next = val.Next; if (next == null) { return 0; } opCode = next.OpCode; Code code2 = ((OpCode)(ref opCode)).Code; if ((int)code2 != 10 && (int)code2 != 11 && (int)code2 != 12 && (int)code2 != 13 && (int)code2 != 19 && (int)code2 != 204) { return 0; } return i + 1; } if (val2.HasParameters || ((MemberReference)val2.ReturnType).FullName == "System.Void") { return 0; } } return 0; } private static int FontAdded(MethodBody body, int at, int setting) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) int num = 0; for (int i = 0; i < setting; i++) { OpCode opCode = body.Instructions[at + i].OpCode; if ((int)((OpCode)(ref opCode)).Code != 0) { num++; } } return ((num == 0) ? 1 : num) + 6; } private static Instruction Copy(ILProcessor il, Instruction ins) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) if (ins.Operand == null) { return il.Create(ins.OpCode); } object operand = ins.Operand; FieldReference val = (FieldReference)((operand is FieldReference) ? operand : null); if (val != null) { return il.Create(ins.OpCode, val); } object operand2 = ins.Operand; MethodReference val2 = (MethodReference)((operand2 is MethodReference) ? operand2 : null); if (val2 != null) { return il.Create(ins.OpCode, val2); } OpCode opCode = ins.OpCode; throw new InvalidOperationException("font method: cannot repeat " + ((OpCode)(ref opCode)).Name); } private static MethodDefinition AddFontGuard(ModuleDefinition module, BuildOptions o, MethodDefinition guard, TypeReference stringType) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Expected O, but got Unknown //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Expected O, but got Unknown //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Expected O, but got Unknown //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Expected O, but got Unknown //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Expected O, but got Unknown //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Expected O, but got Unknown //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Expected O, but got Unknown //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_021f: Expected O, but got Unknown //IL_0233: Unknown result type (might be due to invalid IL or missing references) //IL_0240: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Unknown result type (might be due to invalid IL or missing references) //IL_025a: Unknown result type (might be due to invalid IL or missing references) //IL_0268: Unknown result type (might be due to invalid IL or missing references) //IL_027b: Unknown result type (might be due to invalid IL or missing references) //IL_0296: Unknown result type (might be due to invalid IL or missing references) //IL_02a9: Unknown result type (might be due to invalid IL or missing references) //IL_02ba: Unknown result type (might be due to invalid IL or missing references) //IL_02d5: Unknown result type (might be due to invalid IL or missing references) //IL_02e6: Unknown result type (might be due to invalid IL or missing references) //IL_02f9: Unknown result type (might be due to invalid IL or missing references) //IL_0314: Unknown result type (might be due to invalid IL or missing references) //IL_032c: Unknown result type (might be due to invalid IL or missing references) //IL_033c: Unknown result type (might be due to invalid IL or missing references) //IL_0343: Expected O, but got Unknown TypeDefinition declaringType = guard.DeclaringType; Enumerator<MethodDefinition> enumerator = declaringType.Methods.GetEnumerator(); try { while (enumerator.MoveNext()) { MethodDefinition current = enumerator.Current; if (((MemberReference)current).Name == "F") { return current; } } } finally { ((IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } TypeReference val = module.TypeSystem.Object; TypeReference val2 = null; enumerator = declaringType.Methods.GetEnumerator(); try { while (enumerator.MoveNext()) { MethodDefinition current2 = enumerator.Current; if (((MemberReference)current2).Name != "Call") { continue; } Enumerator<Instruction> enumerator2 = current2.Body.Instructions.GetEnumerator(); try { while (enumerator2.MoveNext()) { object operand = enumerator2.Current.Operand; MethodReference val3 = (MethodReference)((operand is MethodReference) ? operand : null); if (val3 != null) { val2 = ((MemberReference)val3).DeclaringType; } } } finally { ((IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose(); } } } finally { ((IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } if (val2 == null) { throw new InvalidOperationException("the guard class has no reference to the runtime"); } MethodReference val4 = new MethodReference("MenuFont", val, val2); val4.HasThis = false; val4.Parameters.Add(new ParameterDefinition(stringType)); FieldDefinition val5 = new FieldDefinition("offF", (FieldAttributes)17, module.TypeSystem.Boolean); declaringType.Fields.Add(val5); MethodDefinition val6 = new MethodDefinition("CallF", (MethodAttributes)145, val); val6.ImplAttributes = (MethodImplAttributes)8; ((MethodReference)val6).Parameters.Add(new ParameterDefinition("s", (ParameterAttributes)0, stringType)); declaringType.Methods.Add(val6); ILProcessor iLProcessor = val6.Body.GetILProcessor(); iLProcessor.Append(iLProcessor.Create(OpCodes.Ldarg_0)); iLProcessor.Append(iLProcessor.Create(OpCodes.Call, val4)); iLProcessor.Append(iLProcessor.Create(OpCodes.Ret)); MethodDefinition val7 = new MethodDefinition("F", (MethodAttributes)147, val); val7.ImplAttributes = (MethodImplAttributes)0; ((MethodReference)val7).Parameters.Add(new ParameterDefinition("s", (ParameterAttributes)0, stringType)); declaringType.Methods.Add(val7); MethodBody body = val7.Body; body.InitLocals = true; VariableDefinition val8 = new VariableDefinition(val); body.Variables.Add(val8); ILProcessor iLProcessor2 = body.GetILProcessor(); Instruction val9 = iLProcessor2.Create(OpCodes.Ldnull); Instruction val10 = iLProcessor2.Create(OpCodes.Ldarg_0); Instruction val11 = iLProcessor2.Create(OpCodes.Pop); Instruction val12 = iLProcessor2.Create(OpCodes.Ldloc_0); iLProcessor2.Append(iLProcessor2.Create(OpCodes.Ldsfld, (FieldReference)(object)val5)); iLProcessor2.Append(iLProcessor2.Create(OpCodes.Brtrue, val9)); iLProcessor2.Append(val10); iLProcessor2.Append(iLProcessor2.Create(OpCodes.Call, (MethodReference)(object)val6)); iLProcessor2.Append(iLProcessor2.Create(OpCodes.Stloc_0)); iLProcessor2.Append(iLProcessor2.Create(OpCodes.Leave, val12)); iLProcessor2.Append(val11); iLProcessor2.Append(iLProcessor2.Create(OpCodes.Ldc_I4_1)); iLProcessor2.Append(iLProcessor2.Create(OpCodes.Stsfld, (FieldReference)(object)val5)); iLProcessor2.Append(iLProcessor2.Create(OpCodes.Leave, val9)); iLProcessor2.Append(val12); iLProcessor2.Append(iLProcessor2.Create(OpCodes.Ret)); iLProcessor2.Append(val9); iLProcessor2.Append(iLProcessor2.Create(OpCodes.Ret)); ExceptionHandler val13 = new ExceptionHandler((ExceptionHandlerType)0); val13.CatchType = val; val13.TryStart = val10; val13.TryEnd = val11; val13.HandlerStart = val11; val13.HandlerEnd = val12; body.ExceptionHandlers.Add(val13); return val7; } private static MethodDefinition AddGuard(ModuleDefinition module, BuildOptions o, TypeReference stringType) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Expected O, but got Unknown //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Expected O, but got Unknown //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Expected O, but got Unknown //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Expected O, but got Unknown //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Expected O, but got Unknown //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Expected O, but got Unknown //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Expected O, but got Unknown //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Expected O, but got Unknown //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Expected O, but got Unknown //IL_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Expected O, but got Unknown //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_01f7: Unknown result type (might be due to invalid IL or missing references) //IL_0204: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_021f: Unknown result type (might be due to invalid IL or missing references) //IL_0232: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Unknown result type (might be due to invalid IL or missing references) //IL_0260: Unknown result type (might be due to invalid IL or missing references) //IL_0271: Unknown result type (might be due to invalid IL or missing references) //IL_028c: Unknown result type (might be due to invalid IL or missing references) //IL_029d: Unknown result type (might be due to invalid IL or missing references) //IL_02b0: Unknown result type (might be due to invalid IL or missing references) //IL_02cb: Unknown result type (might be due to invalid IL or missing references) //IL_02e3: Unknown result type (might be due to invalid IL or missing references) //IL_02f3: Unknown result type (might be due to invalid IL or missing references) //IL_02fa: Expected O, but got Unknown //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Expected O, but got Unknown TypeReference val = module.TypeSystem.Object; TypeReference boolean = module.TypeSystem.Boolean; AssemblyNameReference val2 = null; Enumerator<AssemblyNameReference> enumerator = module.AssemblyReferences.GetEnumerator(); try { while (enumerator.MoveNext()) { AssemblyNameReference current = enumerator.Current; if (current.Name == o.RuntimeAssemblyName) { val2 = current; break; } } } finally { ((IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } if (val2 == null) { val2 = new AssemblyNameReference(o.RuntimeAssemblyName, o.RuntimeVersion); module.AssemblyReferences.Add(val2); } TypeReference val3 = new TypeReference("RepoRPG_RU", "Loc", module, (IMetadataScope)(object)val2); MethodReference val4 = new MethodReference("T", stringType, val3); val4.HasThis = false; val4.Parameters.Add(new ParameterDefinition(stringType)); TypeDefinition val5 = new TypeDefinition("", "<RepoRPG_RU>", (TypeAttributes)1048960, val); module.Types.Add(val5); FieldDefinition val6 = new FieldDefinition("off", (FieldAttributes)17, boolean); val5.Fields.Add(val6); MethodDefinition val7 = new MethodDefinition("Call", (MethodAttributes)145, stringType); val7.ImplAttributes = (MethodImplAttributes)8; ((MethodReference)val7).Parameters.Add(new ParameterDefinition("s", (ParameterAttributes)0, stringType)); val5.Methods.Add(val7); ILProcessor iLProcessor = val7.Body.GetILProcessor(); iLProcessor.Append(iLProcessor.Create(OpCodes.Ldarg_0)); iLProcessor.Append(iLProcessor.Create(OpCodes.Call, val4)); iLProcessor.Append(iLProcessor.Create(OpCodes.Ret)); MethodDefinition val8 = new MethodDefinition("T", (MethodAttributes)147, stringType); val8.ImplAttributes = (MethodImplAttributes)0; ((MethodReference)val8).Parameters.Add(new ParameterDefinition("s", (ParameterAttributes)0, stringType)); val5.Methods.Add(val8); MethodBody body = val8.Body; body.InitLocals = true; VariableDefinition val9 = new VariableDefinition(stringType); body.Variables.Add(val9); ILProcessor iLProcessor2 = body.GetILProcessor(); Instruction val10 = iLProcessor2.Create(OpCodes.Ldarg_0); Instruction val11 = iLProcessor2.Create(OpCodes.Ldarg_0); Instruction val12 = iLProcessor2.Create(OpCodes.Pop); Instruction val13 = iLProcessor2.Create(OpCodes.Ldloc_0); iLProcessor2.Append(iLProcessor2.Create(OpCodes.Ldsfld, (FieldReference)(object)val6)); iLProcessor2.Append(iLProcessor2.Create(OpCodes.Brtrue, val10)); iLProcessor2.Append(val11); iLProcessor2.Append(iLProcessor2.Create(OpCodes.Call, (MethodReference)(object)val7)); iLProcessor2.Append(iLProcessor2.Create(OpCodes.Stloc_0)); iLProcessor2.Append(iLProcessor2.Create(OpCodes.Leave, val13)); iLProcessor2.Append(val12); iLProcessor2.Append(iLProcessor2.Create(OpCodes.Ldc_I4_1)); iLProcessor2.Append(iLProcessor2.Create(OpCodes.Stsfld, (FieldReference)(object)val6)); iLProcessor2.Append(iLProcessor2.Create(OpCodes.Leave, val10)); iLProcessor2.Append(val13); iLProcessor2.Append(iLProcessor2.Create(OpCodes.Ret)); iLProcessor2.Append(val10); iLProcessor2.Append(iLProcessor2.Create(OpCodes.Ret)); ExceptionHandler val14 = new ExceptionHandler((ExceptionHandlerType)0); val14.CatchType = val; val14.TryStart = val11; val14.TryEnd = val12; val14.HandlerStart = val12; val14.HandlerEnd = val13; body.ExceptionHandlers.Add(val14); return val8; } private static void BumpVersion(ModuleDefinition module, Report report) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) foreach (TypeDefinition item in AllTypes(module)) { if (!item.HasCustomAttributes) { continue; } Enumerator<CustomAttribute> enumerator2 = item.CustomAttributes.GetEnumerator(); try { while (enumerator2.MoveNext()) { CustomAttribute current2 = enumerator2.Current; if (!(((MemberReference)current2.AttributeType).FullName != "BepInEx.BepInPlugin") && current2.ConstructorArguments.Count == 3) { CustomAttributeArgument val = current2.ConstructorArguments[0]; if (string.Equals(((CustomAttributeArgument)(ref val)).Value as string, "rpgmod.semibotrpg", StringComparison.Ordinal)) { val = current2.ConstructorArguments[2]; string text = ((CustomAttributeArgument)(ref val)).Value as string; string text2 = Bump(text); Collection<CustomAttributeArgument> constructorArguments = current2.ConstructorArguments; val = current2.ConstructorArguments[2]; constructorArguments[2] = new CustomAttributeArgument(((CustomAttributeArgument)(ref val)).Type, (object)text2); report.OriginalVersion = text; report.NewVersion = text2; return; } } } } finally { ((IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose(); } } throw new InvalidOperationException("[BepInPlugin(\"rpgmod.semibotrpg\", ...)] not found - is this really RepoRPG?"); } internal static string Bump(string version) { Version version2 = new Version(version); int num = ((version2.Build >= 0) ? version2.Build : 0); int num2 = ((version2.Revision < 0) ? 1 : (version2.Revision + 1)); return version2.Major + "." + version2.Minor + "." + num + "." + num2; } public static string ReadPluginVersion(ModuleDefinition module) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) foreach (TypeDefinition item in AllTypes(module)) { if (!item.HasCustomAttributes) { continue; } Enumerator<CustomAttribute> enumerator2 = item.CustomAttributes.GetEnumerator(); try { while (enumerator2.MoveNext()) { CustomAttribute current2 = enumerator2.Current; if (!(((MemberReference)current2.AttributeType).FullName != "BepInEx.BepInPlugin") && current2.ConstructorArguments.Count == 3) { CustomAttributeArgument val = current2.ConstructorArguments[0]; if (string.Equals(((CustomAttributeArgument)(ref val)).Value as string, "rpgmod.semibotrpg", StringComparison.Ordinal)) { val = current2.ConstructorArguments[2]; return ((CustomAttributeArgument)(ref val)).Value as string; } } } } finally { ((IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose(); } } return null; } private static void TranslateData(ModuleDefinition module, BuildOptions o, Report report, List<Probe> probes) { //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Expected O, but got Unknown for (int i = 0; i < module.Resources.Count; i++) { Resource obj = module.Resources[i]; EmbeddedResource val = (EmbeddedResource)(object)((obj is EmbeddedResource) ? obj : null); if (val == null || !((Resource)val).Name.EndsWith(".json", StringComparison.OrdinalIgnoreCase)) { continue; } byte[] resourceData = val.GetResourceData(); if (o.Dump) { report.Resources[((Resource)val).Name] = resourceData; } bool translate = o.Settings.TranslateData; string text = ((Resource)val).Name.Substring(0, ((Resource)val).Name.Length - 5); int num = text.LastIndexOf('.'); if (num >= 0) { text = text.Substring(num + 1); } bool flag = resourceData.Length >= 3 && resourceData[0] == 239 && resourceData[1] == 187 && resourceData[2] == 191; string json = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: false).GetString(resourceData, flag ? 3 : 0, resourceData.Length - (flag ? 3 : 0)); o.Lang.Data.TryGetValue(text, out var own); o.Lang.Data.TryGetValue("", out var any); string stemCopy = text; Rules rules = o.Rules; bool dump = o.Dump; int replaced; string s = JsonText.Translate(json, delegate(string path, string property, string value) { bool flag2 = rules.DataAllowed(stemCopy, property, path); if (dump) { report.DataDump.Add(stemCopy + "\t" + path + "\t" + (flag2 ? "+" : "-") + "\t" + Escape(value)); } if (!flag2 || !translate) { return (string)null; } string value2; bool flag3 = own != null && own.TryGetValue(value, out value2); if (!flag3) { if (any == null || !any.TryGetValue(value, out value2)) { return (string)null; } } else { value2 = own[value]; } Probe probe = (rules.ProbeFreeData.Contains(stemCopy) ? null : BrokenProbe(probes, rules.ProbeFree, value, value2)); if (probe != null) { report.SkippedProbe++; report.Notes.Add("data left as is (the mod tests texts with " + probe?.ToString() + " in " + probe.Where + "): " + stemCopy + " " + path + " \"" + Escape(value) + "\""); return (string)null; } report.UsedData.Add((flag3 ? stemCopy : "") + "\n" + value); return value2; }, out replaced); if (replaced != 0) { byte[] bytes = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false).GetBytes(s); byte[] array = bytes; if (flag) { array = new byte[bytes.Length + 3]; array[0] = 239; array[1] = 187; array[2] = 191; Buffer.BlockCopy(bytes, 0, array, 3, bytes.Length); } module.Resources[i] = (Resource)new EmbeddedResource(((Resource)val).Name, ((Resource)val).Attributes, array); report.DataReplaced += replaced; report.DataFiles++; } } } private static void Verify(byte[] result, Plan plan, Report report) { //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_055a: Unknown result type (might be due to invalid IL or missing references) //IL_055f: Unknown result type (might be due to invalid IL or missing references) //IL_0274: Unknown result type (might be due to invalid IL or missing references) //IL_0279: Unknown result type (might be due to invalid IL or missing references) //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_0284: Invalid comparison between Unknown and I4 //IL_0341: Unknown result type (might be due to invalid IL or missing references) //IL_0346: Unknown result type (might be due to invalid IL or missing references) //IL_034a: Unknown result type (might be due to invalid IL or missing references) //IL_0351: Invalid comparison between Unknown and I4 //IL_041b: Unknown result type (might be due to invalid IL or missing references) //IL_0420: Unknown result type (might be due to invalid IL or missing references) //IL_0424: Unknown result type (might be due to invalid IL or missing references) //IL_042b: Invalid comparison between Unknown and I4 //IL_037f: Unknown result type (might be due to invalid IL or missing references) //IL_0384: Unknown result type (might be due to invalid IL or missing references) //IL_0388: Unknown result type (might be due to invalid IL or missing references) //IL_038f: Invalid comparison between Unknown and I4 //IL_0398: Unknown result type (might be due to invalid IL or missing references) //IL_039d: Unknown result type (might be due to invalid IL or missing references) //IL_03a1: Unknown result type (might be due to invalid IL or missing references) //IL_03a8: Invalid comparison between Unknown and I4 Dictionary<string, Expect> dictionary = new Dictionary<string, Expect>(StringComparer.Ordinal); HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); foreach (KeyValuePair<uint, int> count in plan.Counts) { string text = plan.Names[count.Key]; Expect expect = new Expect(); expect.Count = count.Value; expect.Shift = (plan.Hooks.TryGetValue(count.Key, out var value) ? (value.Count * 3) : 0); if (plan.FontAdded.TryGetValue(count.Key, out expect.Font)) { expect.Shift += expect.Font; } plan.Shims.TryGetValue(count.Key, out expect.Shims); plan.Edits.TryGetValue(count.Key, out expect.Edits); if (dictionary.ContainsKey(text)) { hashSet.Add(text); } else { dictionary[text] = expect; } } AssemblyDefinition val = Read(result); try { ModuleDefinition mainModule = val.MainModule; int num = 0; foreach (TypeDefinition item in AllTypes(mainModule)) { Enumerator<MethodDefinition> enumerator3 = item.Methods.GetEnumerator(); try { while (enumerator3.MoveNext()) { MethodDefinition current3 = enumerator3.Current; if (!current3.HasBody) { continue; } MethodBody body = current3.Body; num++; string fullName = ((MemberReference)current3).FullName; if (((MemberReference)item).Name == "<RepoRPG_RU>" && ((TypeReference)item).Namespace.Length == 0) { num--; } else { if (hashSet.Contains(fullName)) { continue; } if (!dictionary.TryGetValue(fullName, out var value2)) { throw new InvalidOperationException("verification: unexpected method " + fullName); } value2.Seen = true; if (body.Instructions.Count != value2.Count + value2.Shift + ((value2.Shims != null) ? value2.Shims.Count : 0)) { throw new InvalidOperationException("verification: instruction count changed in " + fullName); } OpCode opCode; if (value2.Font > 0) { bool flag = false; for (int i = 0; i < value2.Shift && i < body.Instructions.Count; i++) { object operand = body.Instructions[i].Operand; MethodReference val2 = (MethodReference)((operand is MethodReference) ? operand : null); opCode = body.Instructions[i].OpCode; if ((int)((OpCode)(ref opCode)).Code == 39 && val2 != null && ((MemberReference)((MemberReference)val2).DeclaringType).Name == "<RepoRPG_RU>" && ((MemberReference)val2).Name == "F") { flag = true; } } if (!flag) { throw new InvalidOperationException("verification: font hook not written in " + fullName); } } if (value2.Shims != null) { foreach (int shim in value2.Shims) { Instruction val3 = body.Instructions[value2.Moved(shim)]; object operand2 = val3.Operand; MethodReference val4 = (MethodReference)((operand2 is MethodReference) ? operand2 : null); opCode = val3.OpCode; if ((int)((OpCode)(ref opCode)).Code == 39 && val4 != null && !(((MemberReference)((MemberReference)val4).DeclaringType).Name != "<RepoRPG_RU>") && val3.Next != null) { opCode = val3.Next.OpCode; if ((int)((OpCode)(ref opCode)).Code == 39) { continue; } opCode = val3.Next.OpCode; if ((int)((OpCode)(ref opCode)).Code == 110) { continue; } } throw new InvalidOperationException("verification: call hook not written in " + fullName); } } if (value2.Edits == null) { continue; } foreach (Edit edit in value2.Edits) { Instruction val5 = body.Instructions[value2.Moved(edit.Index)]; opCode = val5.OpCode; if ((int)((OpCode)(ref opCode)).Code != 113 || !string.Equals(val5.Operand as string, edit.Value, StringComparison.Ordinal)) { throw new InvalidOperationException("verification: literal not written in " + fullName); } } } } } finally { ((IDisposable)enumerator3/*cast due to .constrained prefix*/).Dispose(); } } if (num != plan.Counts.Count) { throw new InvalidOperationException("verification: method count changed (" + num + " vs " + plan.Counts.Count + ")"); } foreach (KeyValuePair<string, Expect> item2 in dictionary) { if (!item2.Value.Seen) { throw new InvalidOperationException("verification: method lost: " + item2.Key); } } Enumerator<Resource> enumerator7 = mainModule.Resources.GetEnumerator(); try { while (enumerator7.MoveNext()) { Resource current7 = enumerator7.Current; EmbeddedResource val6 = (EmbeddedResource)(object)((current7 is EmbeddedResource) ? current7 : null); if (val6 != null) { val6.GetResourceData(); } } } finally { ((IDisposable)enumerator7/*cast due to .constrained prefix*/).Dispose(); } } finally { ((IDisposable)val)?.Dispose(); } } internal static string Escape(string s) { StringBuilder stringBuilder = new StringBuilder(s.Length + 8); foreach (char c in s) { switch (c) { case '\\': stringBuilder.Append("\\\\"); break; case '\n': stringBuilder.Append("\\n"); break; case '\r': stringBuilder.Append("\\r"); break; case '\t': stringBuilder.Append("\\t"); break; default: stringBuilder.Append(c); break; } } return stringBuilder.ToString(); } } public static class Patcher { private static readonly string[] None = new string[0]; public static IEnumerable<string> TargetDLLs => None; public static void Patch(AssemblyDefinition assembly) { } private static void AddPath(List<string> list, string property) { try { PropertyInfo property2 = typeof(Paths).GetProperty(property, BindingFlags.Static | BindingFlags.Public); if (!(property2 == null)) { object value = property2.GetValue(null, null); if (value is string item) { list.Add(item); } else if (value is string[] collection) { list.AddRange(collection); } } } catch { } } public static void Initialize() { ManualLogSource val = null; try { val = Logger.CreateLogSource("RepoRPG RU"); Env env = new Env(); env.PluginPath = Paths.PluginPath; env.ConfigPath = Paths.ConfigPath; AddPath(env.SearchDirs, "DllSearchPaths"); AddPath(env.SearchDirs, "ManagedPath"); AddPath(env.SearchDirs, "BepInExAssemblyDirectory"); try { env.SearchDirs.Add(Path.GetDirectoryName(typeof(object).Assembly.Location)); } catch { } env.SearchDirs.Add(Paths.PluginPath); env.SearchDirs.Add(Paths.PatcherPluginPath); ManualLogSource sink = val; env.Info = delegate(string s) { sink.LogInfo((object)s); }; env.Warn = delegate(string s) { sink.LogWarning((object)s); }; env.Error = delegate(string s) { sink.LogError((object)s); }; Worker.Run(env); } catch (Exception ex) { try { if (val != null) { val.LogError((object)("Русификатор не запустился, мод RepoRPG останется английским: " + ex)); } } catch { } } } } internal sealed class Env { public string PluginPath; public string ConfigPath; public readonly List<string> SearchDirs = new List<string>(); public Action<string> Info; public Action<string> Warn; public Action<string> Error; } internal static class Worker { internal sealed class Candidate { public string Path; public byte[] Bytes; public Version Version = new Version(0, 0); public string VersionText = "?"; public bool Generated; public string Stamp; } public const string StampResource = "RepoRPG_RU.stamp"; private const string OutputName = "RepoRPG.dll"; public static void Run(Env env) { if (string.IsNullOrEmpty(env.PluginPath) || !Directory.Exists(env.PluginPath)) { return; } Settings settings = Settings.Load(Path.Combine(env.ConfigPath ?? "", "RepoRPG_RU.cfg"), createIfMissing: true); string text = FindFile(env.PluginPath, "RepoRPG_RU.dll"); string text2 = ((text != null) ? Path.GetDirectoryName(text) : null); string text3 = ((text2 != null) ? Path.Combine(text2, "RepoRPG.dll") : null); Candidate candidate = null; foreach (string item in FindAll(env.PluginPath, "RepoRPG.dll")) { Candidate candidate2 = Inspect(item); if (candidate2 == null) { continue; } if (candidate2.Generated) { if (text3 == null || !SamePath(item, text3)) { TryDelete(item, env, "устаревшая копия"); } continue; } if (text3 != null && SamePath(item, text3)) { env.Warn("Оригинальный RepoRPG.dll лежит в папке русификатора (" + item + "). Перенесите его в отдельную папку, иначе перевод не применяется."); return; } if (candidate == null || candidate2.Version > candidate.Version) { candidate = candidate2; } } if (text2 == null) { env.Warn("Не найден RepoRPG_RU.dll в BepInEx/plugins - русификатор установлен не полностью, перевод не применяется."); return; } if (!settings.Enabled) { TryDelete(text3, env, "перевод выключен в RepoRPG_RU.cfg"); env.Info("Перевод выключен (Enabled = false)."); return; } if (candidate == null) { TryDelete(text3, env, "оригинальный мод не найден"); env.Warn("Мод RepoRPG (RepoRPG.dll) не найден или выключен - переводить нечего."); return; } try { Translate(env, settings, candidate, text2, text3); } catch (Exception ex) { TryDelete(text3, env, "ошибка при создании"); env.Error("Не удалось создать переведённую копию RepoRPG " + candidate.VersionText + " - мод загрузится на английском. Причина: " + ex); } } private static void Translate(Env env, Sett
plugins/RepoRPG_RU.dll
Decompiled an hour ago
The result has been truncated due to the large size, download it to view full contents!
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.Text; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("RepoRPG RU - runtime")] [assembly: AssemblyVersion("0.2.1.0")] namespace RepoRPG_RU; internal static class Fonts { private sealed class Expect { public char Char; public int Size; public int Advance; } private sealed class Unity { private static Unity found; public string Problem; private Type fontType; private Type objectType; private ConstructorInfo create; private MethodInfo request; private MethodInfo info; private MethodInfo destroy; private MethodInfo exists; private MethodInfo system; private MethodInfo findAll; private PropertyInfo advance; private PropertyInfo hideFlags; private PropertyInfo objectName; private PropertyInfo fontSize; private PropertyInfo lineHeight; private PropertyInfo ascent; private PropertyInfo isDynamic; private object normal; public bool CanAskSystem => system != null; public static Unity Find() { if (found != null && found.Problem == null) { return found; } Unity unity = new Unity(); try { unity.Bind(); } catch (Exception ex) { unity.Problem = "Unity: " + ex.GetType().Name + ": " + ex.Message; } found = unity; return unity; } private static Type Lookup(string name, string module) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); Assembly[] array = assemblies; foreach (Assembly assembly in array) { if (!(assembly.GetName().Name != module)) { Type type = assembly.GetType(name, throwOnError: false); if (type != null) { return type; } } } array = assemblies; foreach (Assembly assembly2 in array) { if (assembly2.GetName().Name.StartsWith("UnityEngine", StringComparison.Ordinal)) { Type type2 = assembly2.GetType(name, throwOnError: false); if (type2 != null) { return type2; } } } try { return Type.GetType(name + ", " + module, throwOnError: false); } catch { return null; } } private void Bind() { fontType = Lookup("UnityEngine.Font", "UnityEngine.TextRenderingModule"); Type type = Lookup("UnityEngine.FontStyle", "UnityEngine.TextRenderingModule"); Type type2 = Lookup("UnityEngine.CharacterInfo", "UnityEngine.TextRenderingModule"); objectType = Lookup("UnityEngine.Object", "UnityEngine.CoreModule"); if (fontType == null || type == null || type2 == null) { Problem = "в игре не найден тип UnityEngine.Font"; return; } create = fontType.GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, new Type[1] { typeof(string) }, null); request = fontType.GetMethod("RequestCharactersInTexture", BindingFlags.Instance | BindingFlags.Public, null, new Type[3] { typeof(string), typeof(int), type }, null); info = fontType.GetMethod("GetCharacterInfo", BindingFlags.Instance | BindingFlags.Public, null, new Type[4] { typeof(char), type2.MakeByRefType(), typeof(int), type }, null); advance = type2.GetProperty("advance", BindingFlags.Instance | BindingFlags.Public); if (create == null || request == null || info == null || advance == null || advance.PropertyType != typeof(int)) { Problem = "у UnityEngine.Font в этой версии игры другие методы (" + ((create == null) ? "конструктор " : "") + ((request == null) ? "RequestCharactersInTexture " : "") + ((info == null) ? "GetCharacterInfo " : "") + ((advance == null) ? "CharacterInfo.advance" : "").TrimEnd(Array.Empty<char>()) + ")"; return; } normal = Enum.ToObject(type, 0); try { system = fontType.GetMethod("CreateDynamicFontFromOSFont", BindingFlags.Static | BindingFlags.Public, null, new Type[2] { typeof(string), typeof(int) }, null); } catch { } try { fontSize = fontType.GetProperty("fontSize", BindingFlags.Instance | BindingFlags.Public); } catch { } try { lineHeight = fontType.GetProperty("lineHeight", BindingFlags.Instance | BindingFlags.Public); } catch { } try { ascent = fontType.GetProperty("ascent", BindingFlags.Instance | BindingFlags.Public); } catch { } try { isDynamic = fontType.GetProperty("dynamic", BindingFlags.Instance | BindingFlags.Public); } catch { } try { Type type3 = Lookup("UnityEngine.Resources", "UnityEngine.CoreModule"); if (type3 != null) { findAll = type3.GetMethod("FindObjectsOfTypeAll", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(Type) }, null); } } catch { } if (objectType != null) { try { objectName = objectType.GetProperty("name", BindingFlags.Instance | BindingFlags.Public); } catch { } try { hideFlags = objectType.GetProperty("hideFlags", BindingFlags.Instance | BindingFlags.Public); } catch { } try { destroy = objectType.GetMethod("Destroy", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { objectType }, null); } catch { } try { exists = objectType.GetMethod("op_Implicit", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { objectType }, null); } catch { } if (exists != null && exists.ReturnType != typeof(bool)) { exists = null; } } } public object Create(string path) { return create.Invoke(new object[1] { path }); } public List<object> AllFonts() { if (findAll == null) { return null; } if (!(findAll.Invoke(null, new object[1] { fontType }) is Array array)) { return null; } List<object> list = new List<object>(); foreach (object item in array) { if (item != null && Exists(item)) { list.Add(item); } } return list; } public string Describe(object f) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(Read(objectName, f) ?? "?").Append(": ").Append(Read(fontSize, f) ?? "?") .Append(" / ") .Append(Read(lineHeight, f) ?? "?") .Append(" / ") .Append(Read(ascent, f) ?? "?"); object obj = null; try { if (isDynamic != null) { obj = isDynamic.GetValue(f, null); } } catch { } if (obj is bool && !(bool)obj) { return stringBuilder.Append("; не динамический").ToString(); } try { Request(f, "HW→★", 100); stringBuilder.Append(';'); string text = "HW→★"; foreach (char c in text) { stringBuilder.Append(' ').Append(c).Append(' ') .Append(Advance(f, c, 100, out var pixels) ? pixels.ToString(CultureInfo.InvariantCulture) : "-"); } } catch { stringBuilder.Append("; не измерен"); } return stringBuilder.ToString(); } private static string Read(PropertyInfo p, object o) { try { if (p == null) { return null; } object value = p.GetValue(o, null); return (value == null) ? null : Convert.ToString(value, CultureInfo.InvariantCulture); } catch { return null; } } public object CreateSystem(string family) { return system.Invoke(null, new object[2] { family, 32 }); } public void Request(object f, string characters, int size) { request.Invoke(f, new object[3] { characters, size, normal }); } public bool Advance(object f, char c, int size, out int pixels) { object[] array = new object[4] { c, null, size, normal }; pixels = 0; if (!(bool)info.Invoke(f, array) || array[1] == null) { return false; } pixels = (int)advance.GetValue(array[1], null); return true; } public void Keep(object f) { try { if (hideFlags != null && hideFlags.CanWrite) { hideFlags.SetValue(f, Enum.ToObject(hideFlags.PropertyType, 61), null); } } catch { } } public void Destroy(object f) { try { if (destroy != null) { destroy.Invoke(null, new object[1] { f }); } } catch { } } public bool Exists(object f) { if (exists == null) { return true; } return (bool)exists.Invoke(null, new object[1] { f }); } } public const string FileName = "RepoRPG_RU.ttf"; private const string ModConfigFile = "rpgmod.semibotrpg.cfg"; private const int Tolerance = 3; private static readonly object Gate = new object(); private static object font; private static bool refused; private static string made; private static string madeSummary; private static readonly List<string> decisions = new List<string>(); private static string written; private static string said; private static string home; private static string config; private static bool placed; private static object tried; private static object logSource; private static MethodInfo logInfo; public static object Pick(string setting) { lock (Gate) { StringBuilder stringBuilder = new StringBuilder(); object obj = null; string summary; try { obj = Decide(setting, stringBuilder, out summary); } catch (Exception ex) { obj = null; refused = true; summary = (madeSummary = "шрифт не подключён: " + ex.GetType().Name + ": " + ex.Message); } Report(stringBuilder.ToString(), summary); return obj; } } private static object Decide(string setting, StringBuilder log, out string summary) { if (!placed) { try { home = Path.GetDirectoryName(typeof(Fonts).Assembly.Location); } catch { } if (!string.IsNullOrEmpty(home)) { config = Loc.FindConfigPath(home); } placed = true; } if (string.IsNullOrEmpty(home)) { summary = "шрифт не подключён: неизвестно, где лежит русификатор"; return null; } Settings settings = ((config == null) ? new Settings() : Settings.Load(Path.Combine(config, "RepoRPG_RU.cfg"), createIfMissing: false)); if (!settings.Enabled || !settings.DisplayHooks || !settings.MenuFont) { summary = "шрифт не подключён: выключено в RepoRPG_RU.cfg (MenuFont = false)"; return null; } if (setting == null) { setting = ModSetting(); log.Append("настройка мода Controls/UiFont (из файла ").Append("rpgmod.semibotrpg.cfg").Append("): ") .Append(setting ?? "не найдена, считаем auto") .Append('\n'); } else { log.Append("настройка мода Controls/UiFont: ").Append((setting.Length == 0) ? "(пусто)" : setting).Append('\n'); } if (setting != null) { setting = setting.Trim(); } if (!string.IsNullOrEmpty(setting) && !string.Equals(setting, "auto", StringComparison.OrdinalIgnoreCase)) { summary = "шрифт не подключён: в настройках мода выбран свой шрифт («" + setting + "») - он и остаётся. Чтобы вернуть шрифт игры с русскими буквами, поставьте там auto"; return null; } if (font != null) { if (Alive(font)) { summary = madeSummary; return font; } font = null; log.Append("шрифт был выгружен игрой - создаём заново\n"); } if (refused) { summary = madeSummary; return null; } StringBuilder stringBuilder = new StringBuilder(); object obj2 = Make(stringBuilder, out summary); if (obj2 == null) { refused = true; } font = obj2; made = stringBuilder.ToString(); madeSummary = summary; return obj2; } private static object Make(StringBuilder log, out string summary) { string text = Find(home); if (text == null) { summary = "шрифт не подключён: файл RepoRPG_RU.ttf не найден в папке " + home; return null; } string family; List<Expect> list = ReadCheck(text + ".check", out family); if (list.Count == 0) { summary = "шрифт не подключён: рядом с RepoRPG_RU.ttf нет файла с контрольными ширинами (RepoRPG_RU.ttf.check)"; return null; } long value = 0L; try { value = new FileInfo(text).Length; } catch { } log.Append("файл: ").Append(text).Append(" (") .Append(value) .Append(" байт)\n"); Unity unity = Unity.Find(); if (unity.Problem != null) { summary = "шрифт не подключён: " + unity.Problem; return null; } List<string> list2 = new List<string>(); list2.Add(text); int num = 0; for (int i = 0; i < list2.Count; i++) { string text2 = list2[i]; log.Append("попытка ").Append(++num).Append(": файл ") .Append(text2) .Append('\n'); if (Try(unity, text2, system: false, list, log)) { Survey(unity, tried, log); summary = "шрифт подключён: шрифт игры (Teko) с русскими буквами" + ((i > 0) ? (", прочитан по пути " + text2) : ""); return tried; } if (i == 0 && !Ascii(text)) { string text3 = ShortPath(text); if (text3 != null) { list2.Add(text3); } string text4 = Copy(text, log); if (text4 != null) { list2.Add(text4); } if (list2.Count == 1) { log.Append(" в пути к файлу есть буквы не из латиницы, а другого пути к нему нет\n"); } } } if (family != null && unity.CanAskSystem) { log.Append("попытка ").Append(++num).Append(": шрифт «") .Append(family) .Append("», установленный в системе\n"); if (Try(unity, family, system: true, list, log)) { Survey(unity, tried, log); summary = "шрифт подключён: шрифт игры (Teko) с русскими буквами, установленный в системе («" + family + "»)"; return tried; } } Survey(unity, null, log); summary = "шрифт не подключён: игра не прочитала файл шрифта (подробности выше) - мод рисует своим шрифтом, как раньше. Что можно сделать: установить этот файл в Windows (правый щелчок по " + text + " - «Установить для всех пользователей») и перезапустить игру: русификатор найдёт шрифт по имени" + ((family != null) ? (" «" + family + "»") : ""); return null; } private static void Survey(Unity unity, object ours, StringBuilder log) { try { List<object> list = unity.AllFonts(); if (list == null || list.Count == 0) { return; } log.Append("шрифты в игре (размер / высота строки / подъём; ширина H, W и знаков → ★ при 100 пикс.):\n"); int num = 0; foreach (object item in list) { if (num++ >= 16) { log.Append(" ... всего ").Append(list.Count).Append('\n'); break; } log.Append(" ").Append((item == ours) ? "* " : "").Append(unity.Describe(item)) .Append('\n'); } } catch { } } private static bool Try(Unity unity, string what, bool system, List<Expect> expect, StringBuilder log) { object obj = null; tried = null; string text; try { obj = (system ? unity.CreateSystem(what) : unity.Create(what)); text = ((obj == null) ? "игра не создала шрифт" : Measure(unity, obj, expect, log)); } catch (Exception ex) { Exception ex2 = ((ex is TargetInvocationException && ex.InnerException != null) ? ex.InnerException : ex); text = ex2.GetType().Name + ": " + ex2.Message; } if (text == null) { unity.Keep(obj); tried = obj; return true; } log.Append(" не подошло: ").Append(text).Append('\n'); if (obj != null) { unity.Destroy(obj); } return false; } private static string Find(string dir) { try { string text = Path.Combine(Path.Combine(dir, "font"), "RepoRPG_RU.ttf"); if (File.Exists(text)) { return text; } text = Path.Combine(dir, "RepoRPG_RU.ttf"); if (File.Exists(text)) { return text; } string[] files = Directory.GetFiles(dir, "RepoRPG_RU.ttf", SearchOption.AllDirectories); if (files.Length != 0) { Array.Sort(files, (IComparer<string>?)StringComparer.OrdinalIgnoreCase); return files[0]; } } catch { } return null; } private static List<Expect> ReadCheck(string path, out string family) { List<Expect> list = new List<Expect>(); family = null; try { if (!File.Exists(path)) { return list; } string[] array = File.ReadAllLines(path, Encoding.UTF8); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Trim().Split(new char[1] { '\t' }); int result; int result2; int result3; if (array2.Length == 2 && array2[0] == "family" && array2[1].Trim().Length > 0) { family = array2[1].Trim(); } else if (array2.Length == 3 && array2[0].StartsWith("U+", StringComparison.Ordinal) && int.TryParse(array2[0].Substring(2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out result) && int.TryParse(array2[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out result2) && int.TryParse(array2[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out result3) && result > 0 && result <= 65535 && result2 >= 8 && result2 <= 400 && result3 > 0) { Expect expect = new Expect(); expect.Char = (char)result; expect.Size = result2; expect.Advance = result3; list.Add(expect); } } } catch { list.Clear(); } return list; } private static string Measure(Unity unity, object f, List<Expect> expect, StringBuilder log) { Dictionary<int, StringBuilder> dictionary = new Dictionary<int, StringBuilder>(); foreach (Expect item in expect) { if (!dictionary.TryGetValue(item.Size, out var value)) { value = new StringBuilder(); dictionary[item.Size] = value; } value.Append(item.Char); } foreach (KeyValuePair<int, StringBuilder> item2 in dictionary) { unity.Request(f, item2.Value.ToString(), item2.Key); } string text = null; foreach (Expect item3 in expect) { int pixels; bool flag = unity.Advance(f, item3.Char, item3.Size, out pixels); log.Append(" ").Append(item3.Char).Append(" (") .Append(item3.Size) .Append(" пикс.): по файлу ") .Append(item3.Advance) .Append(", в игре ") .Append(flag ? pixels.ToString(CultureInfo.InvariantCulture) : "нет такого знака") .Append('\n'); if (!flag) { if (text == null) { text = "в шрифте, который создал Unity, нет знака «" + item3.Char + "»"; } } else if (Math.Abs(pixels - item3.Advance) > 3 && text == null) { text = "ширина «" + item3.Char + "» " + pixels + " вместо " + item3.Advance + " - игра подставила другой шрифт"; } } return text; } private static bool Ascii(string s) { for (int i = 0; i < s.Length; i++) { if (s[i] > '~') { return false; } } return true; } [DllImport("kernel32.dll", CharSet = CharSet.Unicode, EntryPoint = "GetShortPathNameW", SetLastError = true)] private static extern uint GetShortPathName(string longPath, StringBuilder shortPath, uint size); private static string ShortPath(string file) { try { if (Path.DirectorySeparatorChar != '\\') { return null; } StringBuilder stringBuilder = new StringBuilder(1024); uint shortPathName = GetShortPathName(file, stringBuilder, (uint)stringBuilder.Capacity); if (shortPathName == 0 || shortPathName >= stringBuilder.Capacity) { return null; } string text = stringBuilder.ToString(); if (text.Length == 0 || !Ascii(text) || string.Equals(text, file, StringComparison.OrdinalIgnoreCase) || !File.Exists(text)) { return null; } return text; } catch { return null; } } private static string Copy(string file, StringBuilder log) { List<string> list = new List<string>(); try { list.Add(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)); } catch { } list.Add(BepInExPath("GameRootPath")); try { list.Add(Path.GetTempPath()); } catch { } byte[] array = null; foreach (string item in list) { if (string.IsNullOrEmpty(item) || !Ascii(item)) { continue; } try { if (!Directory.Exists(item)) { continue; } string text = Path.Combine(item, "RepoRPG_RU"); string text2 = Path.Combine(text, "RepoRPG_RU.ttf"); if (array == null) { array = File.ReadAllBytes(file); } if (!Same(text2, array)) { Directory.CreateDirectory(text); File.WriteAllBytes(text2, array); } return text2; } catch (Exception ex) { log.Append(" копия в ").Append(item).Append(" не удалась: ") .Append(ex.Message) .Append('\n'); } } return null; } private static string BepInExPath(string property) { try { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { if (assembly.GetName().Name != "BepInEx") { continue; } Type type = assembly.GetType("BepInEx.Paths", throwOnError: false); if (!(type == null)) { PropertyInfo property2 = type.GetProperty(property, BindingFlags.Static | BindingFlags.Public); if (!(property2 == null)) { return property2.GetValue(null, null) as string; } } } } catch { } return null; } private static bool Same(string path, byte[] bytes) { try { if (!File.Exists(path)) { return false; } byte[] array = File.ReadAllBytes(path); if (array.Length != bytes.Length) { return false; } for (int i = 0; i < array.Length; i++) { if (array[i] != bytes[i]) { return false; } } return true; } catch { return false; } } private static string ModSetting() { try { if (config == null) { return null; } string path = Path.Combine(config, "rpgmod.semibotrpg.cfg"); if (!File.Exists(path)) { return null; } string text = ""; string[] array = File.ReadAllLines(path, Encoding.UTF8); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length == 0 || text2[0] == '#' || text2[0] == ';') { continue; } if (text2[0] == '[' && text2[text2.Length - 1] == ']') { text = text2.Substring(1, text2.Length - 2).Trim(); continue; } int num = text2.IndexOf('='); if (num > 0 && !(text != "Controls") && text2.Substring(0, num).Trim() == "UiFont") { return text2.Substring(num + 1).Trim(); } } } catch { } return null; } private static void Report(string log, string summary) { if (summary == null) { return; } string item = log + "итог: " + summary + "\n"; if (!decisions.Contains(item) && decisions.Count < 16) { decisions.Add(item); } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("RepoRPG RU ").Append("0.2.1").Append(" - шрифт меню\n"); if (!string.IsNullOrEmpty(made)) { stringBuilder.Append('\n').Append(made); } foreach (string decision in decisions) { stringBuilder.Append('\n').Append(decision); } string text = stringBuilder.ToString(); if (text == written) { return; } written = text; if (summary != said) { said = summary; Say("Шрифт меню: " + summary + "."); } try { if (config != null) { string text2 = Path.Combine(config, "RepoRPG_RU"); Directory.CreateDirectory(text2); File.WriteAllText(Path.Combine(text2, "font.txt"), text.Replace("\n", Environment.NewLine), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); } } catch { } } private static void Say(string message) { try { if (logInfo == null) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { if (assembly.GetName().Name != "BepInEx") { continue; } Type type = assembly.GetType("BepInEx.Logging.Logger", throwOnError: false); if (type == null) { continue; } MethodInfo method = type.GetMethod("CreateLogSource", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null); if (method == null) { continue; } object obj = method.Invoke(null, new object[1] { "RepoRPG RU" }); if (obj != null) { MethodInfo method2 = obj.GetType().GetMethod("LogInfo", BindingFlags.Instance | BindingFlags.Public, null, new Type[1] { typeof(object) }, null); if (!(method2 == null)) { logSource = obj; logInfo = method2; break; } } } } if (logInfo != null) { logInfo.Invoke(logSource, new object[1] { message }); } } catch { } } private static bool Alive(object o) { try { return Unity.Find().Exists(o); } catch { return true; } } } public static class Loc { private sealed class Thing { public int Gender; public string Name; } private sealed class CaseEntry { public string Key; public string Value; } private sealed class Pattern { public string[] Lits; public int[] Slots; public string Template; public int Weight; public int[] Ghosts; public bool Tight; public bool[] Glued; public byte[] Kind; public bool[] Numeric; public bool Counter; public bool[] Dropped; public bool[] Roomy; public bool[] Framed; public bool[] Known; public int[] Form; public bool[] Rarity; private const byte Value = 0; private const byte Clause = 1; private const byte Line = 2; public static Pattern Parse(string original, string translation, bool explicitPattern, int[] numbers, int[] words, int[] forms) { List<string> list = new List<string>(); List<int> list2 = new List<int>(); List<int> list3 = null; List<bool> list4 = new List<bool>(); List<bool> list5 = new List<bool>(); string text = null; StringBuilder stringBuilder = new StringBuilder(); int num = 0; int num2 = 0; List<int> list6 = new List<int>(); for (int i = 0; i < original.Length; i++) { char c = original[i]; switch (c) { case '{': { if (i + 1 < original.Length && original[i + 1] == '{') { stringBuilder.Append('{'); i++; text = null; continue; } int num4 = original.IndexOf('}', i); if (num4 < 0) { return null; } string text2 = original.Substring(i, num4 - i + 1); int num5 = Fmt.FormatIndex(text2); if (num5 < 0) { return null; } if (text != null && stringBuilder.Length == 0) { if (translation.IndexOf(text + text2, StringComparison.Ordinal) < 0 || list2.Contains(num5)) { return null; } if (list3 == null) { list3 = new List<int>(); } list3.Add(num5); text = text2; i = num4; } else { text = text2; list5.Add(text2.IndexOf(':') > 0 || (numbers != null && Array.IndexOf(numbers, num5) >= 0)); list4.Add(stringBuilder.Length > 0 && IsLatinLetter(stringBuilder[stringBuilder.Length - 1])); list.Add(stringBuilder.ToString()); list6.Add(num2); num2 = 0; stringBuilder.Length = 0; list2.Add(num5); i = num4; } continue; } case '}': if (i + 1 < original.Length && original[i + 1] == '}') { stringBuilder.Append('}'); i++; continue; } return null; case '<': { int num3 = original.IndexOf('>', i); if (num3 > i && num3 - i <= 40 && original.IndexOf('{', i, num3 - i) < 0) { stringBuilder.Append(original, i, num3 - i + 1); i = num3; text = null; continue; } break; } } if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { num++; num2++; } stringBuilder.Append(c); text = null; } list.Add(stringBuilder.ToString()); list6.Add(num2); if (list2.Count == 0) { return null; } if (num < (explicitPattern ? 1 : 3)) { return null; } Pattern pattern = new Pattern(); pattern.Lits = list.ToArray(); pattern.Slots = list2.ToArray(); pattern.Template = translation; pattern.Ghosts = list3?.ToArray(); pattern.Tight = num < 14; pattern.Glued = list4.ToArray(); pattern.Numeric = list5.ToArray(); string[] lits; if (pattern.Tight && pattern.Slots.Length == 1 && num > 0) { bool counter = true; lits = pattern.Lits; foreach (string text3 in lits) { for (int k = 0; k < text3.Length; k++) { switch (text3[k]) { case ' ': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': continue; } counter = false; } } pattern.Counter = counter; } pattern.Dropped = new bool[pattern.Slots.Length]; List<string> list7 = Fmt.Holes(translation); for (int l = 0; l < pattern.Slots.Length; l++) { bool flag = false; foreach (string item in list7) { if (Fmt.FormatIndex(item) == pattern.Slots[l]) { flag = true; } } pattern.Dropped[l] = !flag; } pattern.Kind = new byte[pattern.Slots.Length]; pattern.Roomy = new bool[pattern.Slots.Length]; pattern.Framed = new bool[pattern.Slots.Length]; pattern.Known = new bool[pattern.Slots.Length]; pattern.Form = new int[pattern.Slots.Length]; pattern.Rarity = new bool[pattern.Slots.Length]; for (int m = 0; m < pattern.Slots.Length; m++) { int num6 = ((words == null) ? (-1) : Array.IndexOf(words, pattern.Slots[m])); int num7 = ((num6 >= 0 && forms != null && num6 < forms.Length) ? forms[num6] : (-1)); pattern.Known[m] = num6 >= 0; pattern.Form[m] = ((num7 < 0) ? (-1) : (num7 & 3)); pattern.Rarity[m] = num7 >= 4; } for (int n = 0; n < pattern.Slots.Length; n++) { pattern.Framed[n] = num >= 30 && list6[n] >= 3 && list6[n + 1] >= 3; string text4 = pattern.Lits[n]; string text5 = pattern.Lits[n + 1]; int length = text4.Length; bool flag2 = n == 0 && length == 0; bool num8 = n == pattern.Slots.Length - 1 && text5.Length == 0; bool flag3 = flag2 || text4[length - 1] == '\n'; bool flag4 = num8 || text5[0] == '\n'; bool flag5 = flag3 || text4[length - 1] == '>' || (length > 1 && text4[length - 1] == ' ' && (text4[length - 2] == '.' || text4[length - 2] == '!' || text4[length - 2] == '?' || text4[length - 2] == ':' || text4[length - 2] == '-' || text4[length - 2] == '>')); bool flag6 = flag4 || text5[0] == '.' || text5[0] == '!' || text5[0] == '?' || text5[0] == ':' || text5[0] == '<' || (text5.Length > 1 && text5[0] == ' ' && ((text5[1] >= 'A' && text5[1] <= 'Z') || text5[1] == '<' || text5[1] == '-' || text5[1] == '(')) || (text5.Length > 2 && text5[0] == ' ' && text5[1] == ' ' && (text5[2] == '(' || text5[2] == '-' || text5[2] == '<')); pattern.Kind[n] = (byte)((flag3 && flag4) ? 2 : ((flag5 && flag6) ? 1 : 0)); bool flag7 = length > 1 && text4[length - 1] == ' ' && (text4[length - 2] == '–' || text4[length - 2] == '—'); pattern.Roomy[n] = (flag5 || flag7) && flag6; } lits = pattern.Lits; foreach (string text6 in lits) { pattern.Weight += text6.Length; } return pattern; } private static bool Several(string v) { for (int i = 0; i < v.Length; i++) { switch (v[i]) { case '\n': return true; case '!': case '.': case ';': case '?': if (i + 2 < v.Length && v[i + 1] == ' ') { return true; } break; } } return false; } internal static bool Simple(string v) { if (v.Length <= 80 && NameLike(v, lenient: true)) { return Whole(v); } return false; } private static bool Short(string v) { if (v.Length > 32 || !Whole(v)) { return false; } int num = 0; bool flag = false; for (int i = 0; i < v.Length; i++) { if (v[i] == '<' || v[i] == '>') { return false; } bool num2 = !IsBlank(v[i]); if (num2 && !flag) { num++; } flag = num2; } return num <= 4; } private static bool Whole(string v) { if (v.Length > 0 && (IsBlank(v[0]) || IsBlank(v[v.Length - 1]))) { return false; } int num = 0; for (int i = 0; i < v.Length; i++) { char c = v[i]; switch (c) { case '\n': return false; case '<': { int num2 = v.IndexOf('>', i); if (num2 <= i || num2 - i > 40 || i + 1 >= v.Length) { continue; } if (v[i + 1] == '/') { if (--num < 0) { return false; } } else { if (!IsLatinLetter(v[i + 1])) { continue; } if (num == 0 && i > 0) { return false; } num++; } if (num == 0 && num2 + 1 < v.Length) { return false; } i = num2; continue; } default: if (i + 1 < v.Length) { char c2 = v[i + 1]; if (c2 == ' ' && (c == ':' || c == ',' || c == '.' || c == '!' || c == '?' || c == ';') && i + 2 < v.Length) { return false; } if (c == ' ' && (c2 == '·' || c2 == '|')) { return false; } if (c != ' ' || c2 != ' ') { continue; } return false; } break; } break; } return num == 0; } public string Apply(string s, int depth) { int num = Lits.Length - 1; string text = Lits[0]; string text2 = Lits[num]; if (s.Length < Weight) { return null; } if (text.Length > 0 && string.CompareOrdinal(s, 0, text, 0, text.Length) != 0) { return null; } int num2 = s.Length - text2.Length; if (text2.Length > 0 && string.CompareOrdinal(s, num2, text2, 0, text2.Length) != 0) { return null; } int num3 = text.Length; if (num3 > num2) { return null; } string[] array = new string[Slots.Length]; for (int i = 1; i < num; i++) { int num4 = s.IndexOf(Lits[i], num3, num2 - num3, StringComparison.Ordinal); if (num4 < 0) { return null; } array[i - 1] = s.Substring(num3, num4 - num3); num3 = num4 + Lits[i].Length; } array[num - 1] = s.Substring(num3, num2 - num3); int num5 = 0; for (int j = 0; j < array.Length; j++) { string text3 = array[j]; if (Numeric[j]) { if (text3.Length > 0 && (IsBlank(text3[0]) || IsBlank(text3[text3.Length - 1]))) { return null; } bool flag = false; for (int k = 0; k < text3.Length; k++) { if (char.IsLetter(text3[k])) { return null; } if (text3[k] >= '0' && text3[k] <= '9') { flag = true; } } if (!flag) { return null; } continue; } if (Known[j]) { if (Exact(text3) == null && !HasCyrillic(text3)) { return null; } if (Rarity[j] && InForm(text3, 0, inside: false) == null) { return null; } } else if (Tight && !Simple(text3) && (things == null || Built(text3) == null)) { if (!Roomy[j] || j >= 30 || Dropped[j]) { return null; } if ((text3.Length > 80 || !Whole(text3)) && Exact(text3) == null && (shapes == null || Shape(text3) == null)) { return null; } num5 |= 1 << j; } if (Kind[j] == 0) { if (Several(text3)) { return null; } if (j == 0 && Lits[0].Length == 0 && text3.Length > 0 && text3[0] == ' ') { return null; } if (j == 0 && Lits[0].Length == 0 && text3.IndexOf(": ", StringComparison.Ordinal) >= 0) { return null; } if (j == array.Length - 1 && Lits[j + 1].Length == 0 && text3.Length > 0 && text3[text3.Length - 1] == ' ') { return null; } } if (Dropped[j]) { if (text3.Length > 3) { return null; } for (int l = 0; l < text3.Length; l++) { if (!IsLatinLetter(text3[l])) { return null; } } } else if (Glued[j] && text3.Length > 0 && IsLatinLetter(text3[0])) { return null; } } StringBuilder stringBuilder = new StringBuilder(Template.Length + s.Length); int num6 = -1; int num7 = -1; for (int m = 0; m < Template.Length; m++) { char c = Template[m]; switch (c) { case '{': { if (m + 1 < Template.Length && Template[m + 1] == '{') { stringBuilder.Append('{'); m++; continue; } int num8 = Template.IndexOf('}', m); if (num8 < 0) { return null; } int num9 = Fmt.FormatIndex(Template.Substring(m, num8 - m + 1)); int num10 = ((num9 < 0) ? (-1) : Array.IndexOf(Slots, num9)); if (num10 < 0) { if (num9 >= 0 && Ghosts != null && Array.IndexOf(Ghosts, num9) >= 0) { m = num8; continue; } return null; } string text4 = array[num10]; if (things != null && num10 + 1 < array.Length && Lits[num10 + 1] == " " && num8 + 2 < Template.Length && Template[num8 + 1] == ' ' && Template[num8 + 2] == '{') { int num11 = Template.IndexOf('}', num8 + 2); if (num11 > 0 && Fmt.FormatIndex(Template.Substring(num8 + 2, num11 - num8 - 1)) == Slots[num10 + 1]) { string text5 = array[num10 + 1]; string text6 = Built(text4 + " " + text5); if (text6 == null) { int num12 = text5.IndexOf(", ", StringComparison.Ordinal); string text7 = ((num12 > 0) ? Built(text4 + " " + text5.Substring(0, num12)) : null); if (text7 != null) { bool unknown = false; string text8 = Piece(text5.Substring(num12), depth, ref unknown); if (text8 == null && !unknown) { text8 = Piece(text5.Substring(num12 + 2), depth, ref unknown); if (text8 != null) { text8 = ", " + text8; } } if (unknown) { return null; } if (text8 != null) { text6 = text7 + text8; } } } if (text6 != null) { stringBuilder.Append(text6); m = num11; continue; } } } if (Form[num10] >= 0 && things != null) { string text9 = InForm(text4, Form[num10], Worded(stringBuilder)); if (text9 != null) { stringBuilder.Append(text9); num6 = num10; num7 = num8 + 1; m = num8; continue; } } bool unknown2 = false; string text10 = null; if (num10 == num6 + 1 && num10 > 0 && m - num7 == 2 && Template[num7] == ' ' && Template[num7 + 1] == '(' && Lits[num10] == " (") { text10 = Agreed(array[num10 - 1], text4, depth, ref unknown2); } if (text10 == null) { text10 = Piece(text4, depth, ref unknown2); } if (unknown2) { return null; } if (text10 == null && (num5 & (1 << num10)) != 0) { return null; } bool flag2 = text10 == null && Framed[num10] && text4.Length <= 200 && text4.IndexOf('<') < 0 && !Several(text4); if (text10 == null && Kind[num10] == 1 && HasLatin(text4) && !Simple(text4) && !flag2) { return null; } if (text10 == null && Counter && HasLatin(text4)) { return null; } if (text10 == null && Kind[num10] == 0 && HasLatin(text4) && !Simple(text4) && !Short(text4) && !flag2) { return null; } stringBuilder.Append(text10 ?? text4); num6 = num10; num7 = num8 + 1; m = num8; continue; } case '}': if (m + 1 < Template.Length && Template[m + 1] == '}') { stringBuilder.Append('}'); m++; continue; } break; } stringBuilder.Append(c); } return stringBuilder.ToString(); } } private static readonly object Gate = new object(); private static volatile bool ready; private static Dictionary<string, string> map = new Dictionary<string, string>(StringComparer.Ordinal); private static int minLength = int.MaxValue; private static int maxLength = -1; private static bool fallbacks; private static Dictionary<string, CaseEntry> upper; private static Dictionary<string, Pattern[]> byHead; private static Dictionary<string, Pattern[]> byTail; private static Dictionary<string, string> shapes; private static Dictionary<string, string[]> kinds; private static Dictionary<string, Thing> things; private static Dictionary<string, string> owners; private static Pattern[] loose; private static KeyValuePair<string, string>[] prefixes = new KeyValuePair<string, string>[0]; private static KeyValuePair<string, string>[] suffixes = new KeyValuePair<string, string>[0]; private static Dictionary<string, string> memo; private const int MemoLimit = 6000; private static bool logging; private static HashSet<string> seen; private static List<string> pending; private static string logPath; private static int nextFlush; private const int MaxLogged = 4000; private const int MaxDepth = 6; private const int MaxSteps = 4000; [ThreadStatic] private static int cuts; [ThreadStatic] private static int steps; private const char NumberMark = '\u0001'; [ThreadStatic] private static List<int> runStart; [ThreadStatic] private static List<int> runEnd; [ThreadStatic] private static List<string> runText; private const int MaxKinds = 4; private const int Lines = 0; private const int Cells = 1; private const int Phrase = 2; private const int Label = 3; public static int Count { get { Init(); return map.Count; } } public static object MenuFont(string setting) { try { return Fonts.Pick(setting); } catch { return null; } } public static string T(string text) { if (text == null) { return null; } int length = text.Length; if (length == 0) { return text; } if (!ready) { Init(); } if (length >= minLength && length <= maxLength && map.TryGetValue(text, out var value)) { return value; } if (!fallbacks) { return text; } try { return Slow(text); } catch { fallbacks = false; return text; } } private static string Slow(string text) { if (!HasLatin(text)) { return text; } string value; lock (Gate) { if (memo.TryGetValue(text, out value)) { return value ?? text; } } steps = 0; value = Derive(text, 0); lock (Gate) { if (memo.Count >= 6000) { memo.Clear(); } memo[text] = value; if (logging) { if (value == null) { Note(LooksEnglish(text) ? text : (Leftover(text) ? ("~ " + text) : null)); } else if (Leftover(value)) { Note("~ " + text + " => " + value); } } } return value ?? text; } private static string Sub(string text, int depth) { if (text.Length < 2 || !HasLatin(text)) { return null; } string value; lock (Gate) { if (memo.TryGetValue(text, out value)) { return value; } } int num = cuts; value = Derive(text, depth); if (value == null && cuts != num) { return null; } lock (Gate) { if (memo.Count < 6000) { memo[text] = value; } } return value; } private static string Piece(string text, int depth, ref bool unknown) { int num = cuts; string text2 = Sub(text, depth + 1); if (text2 == null && cuts != num) { unknown = true; } return text2; } private static string Exact(string text) { if (map.TryGetValue(text, out var value)) { return value; } return ByCase(text); } private static string Derive(string text, int depth) { string text2 = Exact(text); if (text2 != null) { return text2; } if (things != null && (text2 = Built(text)) != null) { return text2; } if (shapes != null && (text2 = Shape(text)) != null) { return text2; } if (depth >= 6 || ++steps > 4000) { cuts++; return null; } text2 = Compose(text, depth); if (text2 != null && HasLatin(text2)) { text2 = Islands(text2, depth) ?? text2; } return text2; } private static bool IsDigit(char c) { if (c >= '0') { return c <= '9'; } return false; } private static int NumberEnd(string s, int i) { int length = s.Length; int j; for (j = i; j < length && IsDigit(s[j]); j++) { } for (; j + 3 < length && (s[j] == '\u00a0' || s[j] == '\u202f') && IsDigit(s[j + 1]) && IsDigit(s[j + 2]) && IsDigit(s[j + 3]); j += 4) { } if (j + 1 < length && (s[j] == '.' || s[j] == ',') && IsDigit(s[j + 1])) { for (j += 2; j < length && IsDigit(s[j]); j++) { } } return j; } private static string Shape(string text) { int length = text.Length; int i; for (i = 0; i < length && !IsDigit(text[i]); i++) { } if (i == length) { return null; } StringBuilder stringBuilder = new StringBuilder(length); List<string> list = new List<string>(4); int num = 0; while (i < length) { if (!IsDigit(text[i])) { i++; continue; } int num2 = NumberEnd(text, i); stringBuilder.Append(text, num, i - num).Append('\u0001'); list.Add(text.Substring(i, num2 - i)); num = (i = num2); } stringBuilder.Append(text, num, length - num); if (!shapes.TryGetValue(stringBuilder.ToString(), out var value)) { return null; } StringBuilder stringBuilder2 = new StringBuilder(value.Length + 16); for (int j = 0; j < value.Length; j++) { char c = value[j]; if (c == '{') { int num3 = value.IndexOf('}', j); int num4 = ((num3 < 0) ? (-1) : Fmt.FormatIndex(value.Substring(j, num3 - j + 1))); if (num4 < 0 || num4 >= list.Count) { return null; } stringBuilder2.Append(list[num4]); j = num3; } else { stringBuilder2.Append(c); } } return stringBuilder2.ToString(); } private static string Compose(string text, int depth) { string result; if (text.Length >= 2) { if (byHead.TryGetValue(text.Substring(0, 2), out var value) && (result = Try(value, text, depth)) != null) { return result; } if (byTail.TryGetValue(text.Substring(text.Length - 2), out value) && (result = Try(value, text, depth)) != null) { return result; } if (loose.Length != 0 && (result = Try(loose, text, depth)) != null) { return result; } } int i = 0; int num; for (num = text.Length; i < num && IsBlank(text[i]); i++) { } while (num > i && IsBlank(text[num - 1])) { num--; } if (num - i >= 2 && (i > 0 || num < text.Length)) { string text2 = Sub(text.Substring(i, num - i), depth + 1); if (text2 != null) { return text.Substring(0, i) + text2 + text.Substring(num); } } int num2 = 0; int num3 = text.Length; while (num2 < num3 && text[num2] == '<') { int num4 = text.IndexOf('>', num2); if (num4 < 0 || num4 - num2 > 40 || text[num2 + 1] == '/') { break; } num2 = num4 + 1; } while (num3 > num2 && text[num3 - 1] == '>') { int num5 = text.LastIndexOf('<', num3 - 1); if (num5 < num2 || num3 - num5 > 40 || num5 + 1 >= num3 || text[num5 + 1] != '/') { break; } num3 = num5; } if ((num2 > 0 || num3 < text.Length) && num3 > num2) { string text3 = Sub(text.Substring(num2, num3 - num2), depth + 1); if (text3 != null) { return text.Substring(0, num2) + text3 + text.Substring(num3); } } if ((result = Parts(text, "\n", 0, depth)) != null) { return result; } if ((result = Quoted(text, depth)) != null) { return result; } if ((result = Edge(text, depth)) != null) { return result; } if ((result = Runs(text, depth)) != null) { return result; } if ((result = Parts(text, " · ", 1, depth)) != null) { return result; } if ((result = Parts(text, " · ", 1, depth)) != null) { return result; } if ((result = Parts(text, " | ", 1, depth)) != null) { return result; } if ((result = Parts(text, " ", 1, depth)) != null) { return result; } if ((result = Sentences(text, depth)) != null) { return result; } if ((result = Parts(text, " + ", 2, depth)) != null) { return result; } if ((result = Parts(text, " – ", 2, depth)) != null) { return result; } if ((result = Parts(text, " - ", 2, depth)) != null) { return result; } if ((result = Parts(text, ": ", 3, depth)) != null) { return result; } if ((result = Parts(text, ", ", 2, depth)) != null) { return result; } if ((result = Bracket(text, depth)) != null) { return result; } if ((result = Peel(text, depth)) != null) { return result; } if ((result = Counted(text)) != null) { return result; } return Islands(text, depth); } private static string Agreed(string outer, string inner, int depth, ref bool unknown) { if (things == null) { return null; } int num = inner.IndexOf(", ", StringComparison.Ordinal); string text = ((num < 0) ? inner : inner.Substring(0, num)); if (text.Length > 24 || !kinds.TryGetValue(text.ToUpperInvariant(), out var value)) { return null; } if (string.Equals(text, text.ToUpperInvariant(), StringComparison.Ordinal)) { return null; } int i = outer.Length; while (i > 0 && (IsLatinLetter(outer[i - 1]) || outer[i - 1] == ' ' || outer[i - 1] == '\'' || outer[i - 1] == '-')) { i--; } for (; i < outer.Length && outer[i] == ' '; i++) { } if (i >= outer.Length) { return null; } int num2 = Gender(outer.Substring(i)); if (num2 < 0) { return null; } string text2 = LowerFirst(value[num2]); if (num < 0) { return text2; } string text3 = inner.Substring(num + 2); string text4 = Piece(text3, depth, ref unknown); if (text4 == null && (unknown || (HasLatin(text3) && !NameLike(text3, lenient: false)))) { return null; } return text2 + ", " + (text4 ?? text3); } private static string Bracket(string text, int depth) { int length = text.Length; if (length < 6) { return null; } char c = text[length - 1]; char c2 = c switch { ']' => '[', ')' => '(', _ => '\0', }; if (c2 == '\0') { return null; } int num = 0; int num2 = -1; for (int num3 = length - 1; num3 >= 0; num3--) { char c3 = text[num3]; if (c3 == c) { num++; } else if (c3 == c2 && --num == 0) { num2 = num3; break; } } if (num2 < 2 || text[num2 - 1] != ' ' || num2 + 1 >= length - 1) { return null; } string text2 = text.Substring(0, num2 - 1); string text3 = text.Substring(num2 + 1, length - num2 - 2); bool unknown = false; string text4 = Piece(text2, depth, ref unknown); string text5 = ((c2 == '(') ? Agreed(text2, text3, depth, ref unknown) : null) ?? Piece(text3, depth, ref unknown); if (unknown || (text4 == null && text5 == null)) { return null; } if (text4 == null && HasLatin(text2) && !NameLike(text2, lenient: false)) { return null; } if (text5 == null && HasLatin(text3) && !NameLike(text3, lenient: false)) { return null; } return (text4 ?? text2) + " " + c2 + (text5 ?? text3) + c; } private static string Quoted(string text, int depth) { int num = text.Length - 1; while (num > 0 && text[num] != '"') { char c = text[num]; if (IsBlank(c)) { num--; continue; } if (c != '>') { return null; } int num2 = text.LastIndexOf('<', num); if (num2 < 0 || num - num2 > 40) { return null; } num = num2 - 1; } if (num <= 0) { return null; } int num3 = text.IndexOf('"'); if (num3 >= num - 2) { return null; } bool unknown = false; string text2 = Piece(text.Substring(num3 + 1, num - num3 - 1), depth, ref unknown); if (text2 == null) { return null; } int num4 = num3; while (num4 > 0) { char c2 = text[num4 - 1]; if (IsBlank(c2)) { num4--; continue; } if (c2 != '>') { break; } int num5 = text.LastIndexOf('<', num4 - 1); if (num5 < 0 || num4 - num5 > 41 || num5 + 1 >= num4 || text[num5 + 1] == '/') { break; } num4 = num5; } string text3 = text.Substring(0, num4); string text4 = null; if (text3.Length > 0 && HasLatin(text3)) { text4 = Piece(text3, depth, ref unknown); if (unknown || (text4 == null && !NameLike(text3, lenient: false))) { return null; } } return (text4 ?? text3) + text.Substring(num4, num3 - num4) + "«" + text2 + "»" + text.Substring(num + 1); } private static string Counted(string text) { int num = text.LastIndexOf(' '); if (num < 2 || num == text.Length - 1 || text.Length - num > 12) { return null; } bool flag = false; for (int i = num + 1; i < text.Length; i++) { char c = text[i]; if (c >= '0' && c <= '9') { flag = true; } else if ((i != num + 1 || (c != 'x' && c != '×')) && c != '.' && c != ',' && c != '%' && c != '+' && c != '-' && c != '/' && c != '\u00a0') { return null; } } if (!flag) { return null; } string text2 = Named(text.Substring(0, num)); if (text2 != null) { return text2 + text.Substring(num); } return null; } private static string Islands(string text, int depth) { if (!HasCyrillic(text)) { return null; } StringBuilder sb = null; int length = text.Length; int num = 0; int copied = 0; while (num < length) { int i = num; bool russian = false; for (; i < length; i++) { char c = text[i]; if (c == '\n') { break; } if (c == '<') { int num2 = text.IndexOf('>', i); if (num2 > i && num2 - i <= 40) { break; } } if (c >= 'Ѐ' && c <= 'ӿ') { russian = true; } } Stretch(text, num, i, russian, depth, ref sb, ref copied); if (i >= length) { break; } num = ((text[i] == '\n') ? (i + 1) : (text.IndexOf('>', i) + 1)); } if (sb == null) { return null; } sb.Append(text, copied, length - copied); return sb.ToString(); } private static void Stretch(string text, int from, int to, bool russian, int depth, ref StringBuilder sb, ref int copied) { List<int> list = runStart ?? (runStart = new List<int>(8)); List<int> list2 = runEnd ?? (runEnd = new List<int>(8)); List<string> list3 = runText ?? (runText = new List<string>(8)); list.Clear(); list2.Clear(); list3.Clear(); int length = text.Length; int num = from; bool flag = false; while (num < to) { if (!IsLatinLetter(text[num]) || (num > 0 && IsWordChar(text[num - 1]))) { num++; continue; } int num2 = num; int num3 = num; int i = num; while (i < to && IsLatinLetter(text[i]) && (i <= num2 || text[i] != 'x' || i + 1 >= to || !IsDigit(text[i + 1]))) { for (; i < to && (IsWordChar(text[i]) || ((text[i] == '\'' || text[i] == '-') && i + 1 < to && IsWordChar(text[i + 1]))); i++) { } num3 = i; if (i + 1 < to && (text[i] == ' ' || text[i] == '/') && IsLatinLetter(text[i + 1])) { i++; continue; } if (i + 3 >= to || text[i] != ' ' || text[i + 1] != '&' || text[i + 2] != ' ' || !IsLatinLetter(text[i + 3])) { break; } i += 3; } if (num3 > num2) { string text2 = null; if (num3 - num2 >= 2 && (num2 != 0 || num3 != length)) { string text3 = text.Substring(num2, num3 - num2); text2 = Named(text3) ?? Names(text3); } if ((list.Count > 0 && OnlyNumber(text, list2[list2.Count - 1], num2)) || (text2 == null && (!russian || !NameLike(text.Substring(num2, num3 - num2), lenient: true)))) { return; } if (text2 != null) { int num4 = num2; if (num4 > from && text[num4 - 1] == ' ') { num4--; } int num5 = num4; while (num5 > from && (IsDigit(text[num5 - 1]) || ((text[num5 - 1] == '.' || text[num5 - 1] == ',' || text[num5 - 1] == '\u00a0') && num5 - 1 > from && IsDigit(text[num5 - 2]) && num5 < num4 && IsDigit(text[num5])))) { num5--; } if (num5 < num4 && num4 < num2 && (num5 == from || !IsWordChar(text[num5 - 1])) && depth < 6) { string text4 = text.Substring(num5, num3 - num5); string text5 = Sub(text4, depth + 1); if (text5 != null && !string.Equals(text5, text4, StringComparison.Ordinal)) { num2 = num5; text2 = text5; } } } list.Add(num2); list2.Add(num3); list3.Add(text2); if (text2 != null) { flag = true; } } num = ((num3 > num2) ? num3 : (num2 + 1)); } if (!flag) { return; } for (int j = 0; j < list.Count; j++) { if (list3[j] != null) { if (sb == null) { sb = new StringBuilder(length + 16); } sb.Append(text, copied, list[j] - copied).Append(list3[j]); copied = list2[j]; } } } private static bool OnlyNumber(string text, int from, int to) { bool result = false; for (int i = from; i < to; i++) { char c = text[i]; if (IsDigit(c)) { result = true; } else if (c != ' ' && c != '\u00a0' && ((c != '.' && c != ',') || i <= from || !IsDigit(text[i - 1]) || i + 1 >= to || !IsDigit(text[i + 1]))) { return false; } } return result; } private static string InForm(string word, int gender, bool inside) { if (things == null || word.Length < 2 || word.Length > 24 || !kinds.TryGetValue(word.ToUpperInvariant(), out var value)) { return null; } if (string.Equals(word, word.ToUpperInvariant(), StringComparison.Ordinal)) { return Upper(value[gender]); } if (!inside) { return value[gender]; } return LowerFirst(value[gender]); } private static string Beside(int gender, string word) { if (gender >= 0) { return InForm(word, gender, inside: false); } return null; } private static bool Worded(StringBuilder sb) { bool flag = false; for (int i = 0; i < sb.Length; i++) { char c = sb[i]; switch (c) { case '<': flag = true; continue; case '>': flag = false; continue; } if (!flag && char.IsLetter(c)) { return true; } } return false; } private static string Named(string text) { string text2 = Exact(text); if (text2 == null) { if (things == null) { return null; } text2 = Built(text); } return text2; } private static string Built(string text) { string[][] array = new string[4][]; int count; string owner; bool lower; Thing thing = Parse(text, array, out count, out owner, out lower); if (thing == null || (count == 0 && owner == null)) { return null; } StringBuilder stringBuilder = new StringBuilder(text.Length + 24); for (int i = 0; i < count; i++) { string text2 = array[i][thing.Gender]; stringBuilder.Append((i == 0) ? text2 : LowerFirst(text2)).Append(' '); } stringBuilder.Append((count == 0) ? thing.Name : LowerFirst(thing.Name)); if (owner != null) { stringBuilder.Append(' ').Append(owner); } string s = stringBuilder.ToString(); if (!lower) { return Upper(s); } if (text[0] < 'a' || text[0] > 'z') { return UpperFirst(s); } return LowerFirst(s); } private static int Gender(string text) { int count; string owner; bool lower; return Parse(text, new string[4][], out count, out owner, out lower)?.Gender ?? (-1); } private static Thing Parse(string text, string[][] found, out int count, out string owner, out bool lower) { count = 0; owner = null; lower = false; int length = text.Length; if (length < 2 || length > 120) { return null; } for (int i = 0; i < length; i++) { char c = text[i]; switch (c) { case ' ': if (i == 0 || i == length - 1 || text[i - 1] == ' ') { return null; } break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': lower = true; break; default: if ((c < 'A' || c > 'Z') && c != '\'' && c != '-') { return null; } break; } } string text2 = (lower ? text.ToUpperInvariant() : text); int num = text2.LastIndexOf(" OF THE ", StringComparison.Ordinal); if (num > 0 && owners.TryGetValue(text2.Substring(num + 8), out owner)) { text2 = text2.Substring(0, num); } else { owner = null; } return Kinds(text2, 0, found, ref count); } private static Thing Kinds(string u, int from, string[][] found, ref int count) { if (things.TryGetValue((from == 0) ? u : u.Substring(from), out var value)) { return value; } if (count == 4) { return null; } int num = from; for (int i = 0; i < 3; i++) { num = u.IndexOf(' ', num + 1); if (num < 0) { break; } if (kinds.TryGetValue(u.Substring(from, num - from), out var value2)) { found[count++] = value2; value = Kinds(u, num + 1, found, ref count); if (value != null) { return value; } count--; } } return null; } private static string Names(string run) { if (run.Length > 120 || run.IndexOf(' ') < 0 || !IsUpperLetter(run[0])) { return null; } StringBuilder stringBuilder = new StringBuilder(run.Length + 16); int budget = 200; if (!NamesFrom(run, 0, stringBuilder, ref budget)) { return null; } return stringBuilder.ToString(); } private static bool NamesFrom(string run, int from, StringBuilder sb, ref int budget) { int length = sb.Length; for (int num = run.Length; num > from; num--) { if ((num >= run.Length || (run[num] == ' ' && num + 1 < run.Length && IsUpperLetter(run[num + 1]))) && (from != 0 || num != run.Length)) { if (--budget < 0) { return false; } string text = Exact(run.Substring(from, num - from)); if (text != null) { if (from > 0) { sb.Append(' '); } sb.Append(text); if (num == run.Length || NamesFrom(run, num + 1, sb, ref budget)) { return true; } sb.Length = length; } } } return false; } private static bool IsWordChar(char c) { if (!IsLatinLetter(c) && (c < '0' || c > '9')) { return c == '_'; } return true; } private static bool IsBlank(char c) { if (c != ' ' && c != '\n' && c != '\r') { return c == '\t'; } return true; } private static bool IsLatinLetter(char c) { if (c < 'a' || c > 'z') { if (c >= 'A') { return c <= 'Z'; } return false; } return true; } private static string Edge(string text, int depth) { for (int i = 0; i < suffixes.Length; i++) { string key = suffixes[i].Key; if (text.Length > key.Length && string.CompareOrdinal(text, text.Length - key.Length, key, 0, key.Length) == 0) { string text2 = text.Substring(0, text.Length - key.Length); bool unknown = false; string text3 = Piece(text2, depth, ref unknown); if (text3 != null || (!unknown && Pattern.Simple(text2) && NameLike(text2, lenient: false))) { return (text3 ?? text2) + suffixes[i].Value; } } } if (!IsLatinLetter(text[text.Length - 1])) { for (int j = 0; j < suffixes.Length; j++) { string value = suffixes[j].Value; if (value.Length >= 3 && text.Length > value.Length && HasCyrillic(value) && string.CompareOrdinal(text, text.Length - value.Length, value, 0, value.Length) == 0) { string text4 = Sub(text.Substring(0, text.Length - value.Length), depth + 1); if (text4 != null) { return text4 + value; } } } } for (int k = 0; k < prefixes.Length; k++) { string key2 = prefixes[k].Key; if (text.Length > key2.Length && string.CompareOrdinal(text, 0, key2, 0, key2.Length) == 0) { string text5 = text.Substring(key2.Length); bool unknown2 = false; string text6 = Piece(text5, depth, ref unknown2); if (text6 != null || (!unknown2 && Pattern.Simple(text5) && NameLike(text5, lenient: false))) { return prefixes[k].Value + (text6 ?? text5); } } } return null; } internal static bool NameLike(string v, bool lenient) { int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; int num5 = 0; bool flag = false; bool flag2 = true; for (int i = 0; i < v.Length; i++) { char c = v[i]; switch (c) { case '\n': return false; case '<': { int num6 = v.IndexOf('>', i); if (num6 > i && num6 - i <= 40) { i = num6; flag = false; continue; } break; } } num3++; switch (c) { case '(': num4++; break; case ')': if (--num4 < 0) { return false; } break; case '[': num5++; break; case ']': if (--num5 < 0) { return false; } break; } bool flag3 = IsLatinLetter(c); if (flag3 || (c >= '0' && c <= '9') || c == '\'' || c == '_') { if (flag) { continue; } flag = true; num2++; if (!flag3) { continue; } num++; if (c < 'A' || c > 'Z') { int j; for (j = 1; i + j < v.Length && IsLatinLetter(v[i + j]); j++) { } if (num == 1 || j >= 4) { flag2 = false; } } } else { flag = false; } } if (num4 != 0 || num5 != 0) { return false; } if (num == 0) { return true; } if (num3 > 60) { return false; } if (lenient && num2 == 1) { return true; } if (flag2) { return num <= 4; } return false; } private static string Parts(string text, string sep, int mode, int depth) { int num = text.IndexOf(sep, StringComparison.Ordinal); if (num < 0) { return null; } StringBuilder stringBuilder = null; int num2 = 0; int num3 = 0; int num4 = 0; int num5 = 0; string text2 = null; while (true) { int num6 = ((num < 0) ? text.Length : num); string text3 = text.Substring(num2, num6 - num2); bool unknown = false; string text4 = ((mode == 1 && text2 != null) ? Beside(Gender(text2), text3) : null) ?? Piece(text3, depth, ref unknown); text2 = text3; if (unknown) { return null; } if (text4 != null) { num4++; } else if (mode != 0 && HasLatin(text3)) { if (mode == 3 && num3 == 0) { return null; } if (!NameLike(text3, lenient: false)) { if (mode != 1) { return null; } num5++; } } if (stringBuilder == null) { stringBuilder = new StringBuilder(text.Length + 16); } stringBuilder.Append(text4 ?? text3); if (num < 0) { break; } stringBuilder.Append(sep); num2 = num + sep.Length; num3++; num = ((mode == 3) ? (-1) : text.IndexOf(sep, num2, StringComparison.Ordinal)); } if (num4 <= 0 || num4 < num5) { return null; } return stringBuilder.ToString(); } private static string Sentences(string text, int depth) { StringBuilder stringBuilder = null; int num = 0; int num2 = 0; for (int i = 0; i + 2 < text.Length; i++) { char c = text[i]; if ((c != '.' && c != '!' && c != '?') || text[i + 1] != ' ') { continue; } char c2 = text[i + 2]; if (c2 < 'A' || c2 > 'Z') { switch (c2) { case '(': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '<': case '[': break; default: continue; } } string text2 = text.Substring(num, i + 1 - num); string text3 = Sub(text2, depth + 1); if (text3 == null && HasLatin(text2)) { return null; } if (stringBuilder == null) { stringBuilder = new StringBuilder(text.Length + 16); } stringBuilder.Append(text3 ?? text2).Append(' '); num = i + 2; num2++; } if (num2 == 0) { return null; } string text4 = text.Substring(num); string text5 = Sub(text4, depth + 1); if (text5 == null && HasLatin(text4)) { return null; } return stringBuilder.Append(text5 ?? text4).ToString(); } private static string Runs(string text, int depth) { int num = text.IndexOf('<'); if (num < 0) { return null; } StringBuilder stringBuilder = new StringBuilder(text.Length + 16); int num2 = 0; int num3 = num; int num4 = 0; bool flag = false; bool unknown = false; int gender = -1; while (num3 >= 0 && num3 < text.Length) { int num5 = text.IndexOf('>', num3); if (num5 <= num3 || num5 - num3 > 40 || num3 + 1 >= text.Length || (text[num3 + 1] != '/' && (text[num3 + 1] < 'a' || text[num3 + 1] > 'z') && (text[num3 + 1] < 'A' || text[num3 + 1] > 'Z')) || text.IndexOf('<', num3 + 1, num5 - num3 - 1) >= 0) { num3 = text.IndexOf('<', num3 + 1); continue; } if (num3 > num2) { string text2 = text.Substring(num2, num3 - num2); string text3 = Beside(gender, text2) ?? Piece(text2, depth, ref unknown); if (unknown) { return null; } if (things != null && text2.Length <= 120 && IsLatinLetter(text2[0])) { int num6 = Gender(text2); if (num6 >= 0) { gender = num6; } } if (text3 != null) { flag = true; } stringBuilder.Append(text3 ?? text2); } stringBuilder.Append(text, num3, num5 - num3 + 1); num4++; num2 = num5 + 1; num3 = text.IndexOf('<', num2); } if (num4 == 0) { return null; } if (num2 < text.Length) { string text4 = text.Substring(num2); string text5 = Beside(gender, text4) ?? Piece(text4, depth, ref unknown); if (unknown) { return null; } if (text5 != null) { flag = true; } stringBuilder.Append(text5 ?? text4); } if (!flag) { return null; } return stringBuilder.ToString(); } private static string Peel(string text, int depth) { int i = 0; int num = text.Length; if (num > 2 && text[0] == '>' && text[1] == ' ') { i = 2; } for (; i < num && Peelable(text[i]); i++) { } while (num > i && Peelable(text[num - 1])) { num--; } int num2 = 0; for (int j = i; j < num; j++) { if (text[j] == '(') { num2++; } else if (text[j] == ')' && num2 > 0) { num2--; } } while (num2 > 0 && num < text.Length && text[num] == ')') { num++; num2--; } if (text.Length > 5 && text[0] == 'x' && text[1] >= '0' && text[1] <= '9') { int k; for (k = 2; k < text.Length && k < 5 && text[k] >= '0' && text[k] <= '9'; k++) { } if (k + 2 < text.Length && text[k] == ':' && text[k + 1] == ' ') { string text2 = Sub(text.Substring(k + 2), depth + 1); if (text2 != null) { return text.Substring(0, k + 2) + text2; } } } if (text.Length > 5 && text[0] == '[') { int num3 = text.IndexOf(']'); if (num3 > 1 && num3 <= 14 && num3 + 2 < text.Length && text[num3 + 1] == ' ') { string text3 = Sub(text.Substring(num3 + 2), depth + 1); if (text3 != null) { return text.Substring(0, num3 + 2) + text3; } } } if ((i == 0 && num == text.Length) || num - i < 2) { return null; } string text4 = Sub(text.Substring(i, num - i), depth + 1); if (text4 != null) { return text.Substring(0, i) + text4 + text.Substring(num); } int length = text.Length; if (i == 0 && length - num >= 2 && (text[length - 1] == '.' || text[length - 1] == '!' || text[length - 1] == '?' || text[length - 1] == ':' || text[length - 1] == ',') && (text[length - 2] == ')' || text[length - 2] == ']')) { text4 = Sub(text.Substring(0, length - 1), depth + 1); if (text4 != null) { return text4 + text.Substring(length - 1); } } return null; } private static bool Peelable(char c) { if (c <= ' ') { return true; } if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '<' || c == '>' || c == '{' || c == '}' || c == '%') { return false; } if (c < '\u0080') { return true; } if ((c >= '←' && c <= '⇿') || (c >= '■' && c <= '◿') || (c >= '☀' && c <= '➿')) { return true; } if (c != '•' && c != '·' && c != '…' && c != '–' && c != '—' && c != '×' && c != '‘' && c != '’' && c != '“' && c != '”' && c != '‹' && c != '›') { return c == '\u00a0'; } return true; } private static string ByCase(string text) { if (text.Length > 120) { return null; } string text2 = text.ToUpperInvariant(); if (!upper.TryGetValue(text2, out var value)) { return null; } if (string.Equals(text, text2, StringComparison.Ordinal)) { return Upper(value.Value); } bool flag = string.Equals(value.Key, text2, StringComparison.Ordinal); if (string.Equals(text, text.ToLowerInvariant(), StringComparison.Ordinal)) { if (!flag) { return LowerFirst(value.Value); } return Lower(value.Value); } if (flag) { return Sentence(value.Value); } if (char.IsUpper(text[0]) && char.IsLower(value.Key[0])) { return UpperFirst(value.Value); } return value.Value; } private static string LowerFirst(string s) { if (s.Length == 0) { return s; } if (s.Length > 1 && IsUpperLetter(s[1])) { return s; } return Lower(s.Substring(0, 1)) + s.Substring(1); } private static string UpperFirst(string s) { for (int i = 0; i < s.Length; i++) { char c = s[i]; if (c == '<') { int num = s.IndexOf('>', i); if (num > i && num - i <= 40) { i = num; continue; } } if (char.IsLetter(c)) { return s.Substring(0, i) + Upper(s.Substring(i, 1)) + s.Substring(i + 1); } if (char.IsDigit(c)) { return s; } } return s; } private static string Sentence(string s) { if (s.Length == 0) { return s; } return Upper(s.Substring(0, 1)) + Lower(s.Substring(1)); } private static bool IsUpperLetter(char c) { if ((c < 'A' || c > 'Z') && (c < 'А' || c > 'Я')) { return c == 'Ё'; } return true; } private static string Lower(string s) { char[] array = s.ToLowerInvariant().ToCharArray(); for (int i = 0; i < array.Length; i++) { char c = array[i]; if (c >= 'А' && c <= 'Я') { array[i] = (char)(c + 32); } else if (c >= 'Ѐ' && c <= 'Џ') { array[i] = (char)(c + 80); } } return new string(array); } private static string Try(Pattern[] bucket, string text, int depth) { for (int i = 0; i < bucket.Length; i++) { string text2 = bucket[i].Apply(text, depth); if (text2 != null) { return text2; } } return null; } private static string Upper(string s) { char[] array = s.ToUpperInvariant().ToCharArray(); for (int i = 0; i < array.Length; i++) { char c = array[i]; if (c >= 'а' && c <= 'я') { array[i] = (char)(c - 32); } else if (c >= 'ѐ' && c <= 'џ') { array[i] = (char)(c - 80); } } return new string(array); } private static bool HasCyrillic(string s) { for (int i = 0; i < s.Length; i++) { if (s[i] >= 'Ѐ' && s[i] <= 'ӿ') { return true; } } return false; } private static bool HasLatin(string s) { foreach (char c in s) { if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { return true; } } return false; } private static void Init() { if (ready) { return; } lock (Gate) { if (!ready) { try { Load(); } catch { } ready = true; } } } private static void Load() { string text = null; try { text = Path.GetDirectoryName(typeof(Loc).Assembly.Location); } catch { } if (string.IsNullOrEmpty(text)) { return; } string text2 = FindConfigPath(text); Settings settings = ((text2 == null) ? new Settings() : Settings.Load(Path.Combine(text2, "RepoRPG_RU.cfg"), createIfMissing: false)); if (!settings.Enabled || !settings.DisplayHooks) { return; } Lang lang = Lang.Load(text); Dictionary<string, string> dictionary = new Dictionary<string, string>(lang.Global.Count + lang.Terms.Count, StringComparer.Ordinal); foreach (KeyValuePair<string, string> item in lang.Global) { dictionary[item.Key] = item.Value; } foreach (KeyValuePair<string, string> term in lang.Terms) { dictionary[term.Key] = term.Value; } int num = int.MaxValue; int num2 = -1; foreach (string key2 in dictionary.Keys) { if (key2.Length < num) { num = key2.Length; } if (key2.Length > num2) { num2 = key2.Length; } } Dictionary<string, CaseEntry> dictionary2 = new Dictionary<string, CaseEntry>(dictionary.Count, StringComparer.Ordinal); foreach (KeyValuePair<string, string> item2 in dictionary) { if (item2.Key.Length > 120 || item2.Key.IndexOf('{') >= 0) { continue; } string text3 = item2.Key.ToUpperInvariant(); if (dictionary2.TryGetValue(text3, out var value)) { bool num3 = !string.Equals(value.Key, text3, StringComparison.Ordinal) && !string.Equals(value.Key, value.Key.ToLowerInvariant(), StringComparison.Ordinal); bool flag = !string.Equals(item2.Key, text3, StringComparison.Ordinal) && !string.Equals(item2.Key, item2.Key.ToLowerInvariant(), StringComparison.Ordinal); if (num3 || !flag) { continue; } } CaseEntry caseEntry = new CaseEntry(); caseEntry.Key = item2.Key; caseEntry.Value = item2.Value; dictionary2[text3] = caseEntry; } List<KeyValuePair<string, string>> list = new List<KeyValuePair<string, string>>(); List<KeyValuePair<string, string>> list2 = new List<KeyValuePair<string, string>>(); foreach (KeyValuePair<string, string> item3 in dictionary) { string key = item3.Key; if (key.Length >= 3 && key.Length <= 60 && key.IndexOf('{') < 0 && HasLatin(key)) { if (key[key.Length - 1] == ' ') { list.Add(item3); } else if (key[0] == ' ' || key[0] == ',') { list2.Add(item3); } } } Comparison<KeyValuePair<string, string>> comparison = delegate(KeyValuePair<string, string> a, KeyValuePair<string, string> b) { int num8 = b.Key.Length.CompareTo(a.Key.Length); return (num8 == 0) ? string.CompareOrdinal(a.Key, b.Key) : num8; }; list.Sort(comparison); list2.Sort(comparison); List<Pattern> list3 = new List<Pattern>(); foreach (KeyValuePair<string, string> pattern3 in lang.Patterns) { lang.Numbers.TryGetValue(pattern3.Key, out var value2); lang.Words.TryGetValue(pattern3.Key, out var value3); lang.WordForms.TryGetValue(pattern3.Key, out var value4); Pattern pattern = Pattern.Parse(pattern3.Key, pattern3.Value, explicitPattern: true, value2, value3, value4); if (pattern != null) { list3.Add(pattern); } } foreach (KeyValuePair<string, string> item4 in dictionary) { if (item4.Key.IndexOf('{') >= 0 && !lang.Patterns.ContainsKey(item4.Key)) { lang.Numbers.TryGetValue(item4.Key, out var value5); lang.Words.TryGetValue(item4.Key, out var value6); lang.WordForms.TryGetValue(item4.Key, out var value7); Pattern pattern2 = Pattern.Parse(item4.Key, item4.Value, explicitPattern: false, value5, value6, value7); if (pattern2 != null) { list3.Add(pattern2); } } } list3.Sort(delegate(Pattern a, Pattern b) { int num8 = b.Weight.CompareTo(a.Weight); return (num8 == 0) ? string.CompareOrdinal(a.Template, b.Template) : num8; }); Dictionary<string, List<Pattern>> table = new Dictionary<string, List<Pattern>>(StringComparer.Ordinal); Dictionary<string, List<Pattern>> table2 = new Dictionary<string, List<Pattern>>(StringComparer.Ordinal); List<Pattern> list4 = new List<Pattern>(); foreach (Pattern item5 in list3) { string text4 = item5.Lits[0]; string text5 = item5.Lits[item5.Lits.Length - 1]; if (text4.Length >= 2) { Bucket(table, text4.Substring(0, 2), item5); } else if (text5.Length >= 2) { Bucket(table2, text5.Substring(text5.Length - 2), item5); } else { list4.Add(item5); } } Dictionary<string, string> dictionary3 = null; if (lang.Shapes.Count > 0) { dictionary3 = new Dictionary<string, string>(lang.Shapes.Count, StringComparer.Ordinal); foreach (KeyValuePair<string, string> shape in lang.Shapes) { StringBuilder stringBuilder = new StringBuilder(shape.Key.Length); bool flag2 = true; for (int num4 = 0; num4 < shape.Key.Length && flag2; num4++) { char c = shape.Key[num4]; if (c != '{') { stringBuilder.Append(c); continue; } int num5 = shape.Key.IndexOf('}', num4); if (num5 < 0 || Fmt.FormatIndex(shape.Key.Substring(num4, num5 - num4 + 1)) < 0) { flag2 = false; break; } stringBuilder.Append('\u0001'); num4 = num5; } if (flag2) { dictionary3[stringBuilder.ToString()] = shape.Value; } } } Dictionary<string, string[]> dictionary4 = null; Dictionary<string, Thing> dictionary5 = null; Dictionary<string, string> dictionary6 = null; if (lang.Nouns.Count > 0) { dictionary4 = new Dictionary<string, string[]>(lang.Adjectives.Count, StringComparer.Ordinal); foreach (KeyValuePair<string, string[]> adjective in lang.Adjectives) { dictionary4[adjective.Key.ToUpperInvariant()] = adjective.Value; } dictionary6 = new Dictionary<string, string>(lang.Owners.Count, StringComparer.Ordinal); foreach (KeyValuePair<string, string> owner in lang.Owners) { dictionary6[owner.Key.ToUpperInvariant()] = owner.Value; } dictionary5 = new Dictionary<string, Thing>(lang.Nouns.Count, StringComparer.Ordinal); foreach (KeyValuePair<string, string> noun in lang.Nouns) { Thing thing = new Thing(); thing.Gender = "mfnp".IndexOf(noun.Value[0]); int num6 = noun.Value.IndexOf('|'); if (num6 > 0) { thing.Name = noun.Value.Substring(num6 + 1); } else if (!dictionary.TryGetValue(noun.Key, out thing.Name)) { continue; } if (thing.Gender >= 0 && !string.IsNullOrEmpty(thing.Name)) { dictionary5[noun.Key.ToUpperInvariant()] = thing; } } if (dictionary5.Count == 0) { dictionary5 = null; } } minLength = num; maxLength = num2; kinds = dictionary4; owners = dictionary6; things = dictionary5; shapes = dictionary3; upper = dictionary2; prefixes = list.ToArray(); suffixes = list2.ToArray(); byHead = Freeze(table); byTail = Freeze(table2); loose = list4.ToArray(); memo = new Dictionary<string, string>(StringComparer.Ordinal); map = dictionary; fallbacks = dictionary.Count > 0; if (!settings.LogUntranslated || text2 == null) { return; } string text6 = Path.Combine(text2, "RepoRPG_RU"); Directory.CreateDirectory(text6); logPath = Path.Combine(text6, "untranslated.txt"); seen = new HashSet<string>(StringComparer.Ordinal); pending = new List<string>(); try { if (File.Exists(logPath)) { string[] array = File.ReadAllLines(logPath, Encoding.UTF8); foreach (string text7 in array) { if (text7.Length > 0) { seen.Add(text7); } } } } catch { } nextFlush = Environment.TickCount; try { AppDomain.CurrentDomain.ProcessExit += delegate { Flush(); }; } catch { } logging = true; } private static void Bucket(Dictionary<string, List<Pattern>> table, string key, Pattern p) { if (!table.TryGetValue(key, out var value)) { value = (table[key] = new List<Pattern>()); } value.Add(p); } private static Dictionary<string, Pattern[]> Freeze(Dictionary<string, List<Pattern>> table) { Dictionary<string, Pattern[]> dictionary = new Dictionary<string, Pattern[]>(table.Count, StringComparer.Ordinal); foreach (KeyValuePair<string, List<Pattern>> item in table) { dictionary[item.Key] = item.Value.ToArray(); } return dictionary; } internal static string FindConfigPath(string home) { try { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { if (assembly.GetName().Name != "BepInEx") { continue; } Type type = assembly.GetType("BepInEx.Paths", throwOnError: false); if (type == null) { continue; } PropertyInfo property = type.GetProperty("ConfigPath", BindingFlags.Static | BindingFlags.Public); if (!(property == null)) { string text = property.GetValue(null, null) as string; if (!string.IsNullOrEmpty(text)) { return text; } } } } catch { } try { DirectoryInfo directoryInfo = new DirectoryInfo(home); int num = 0; while (num < 6 && directoryInfo != null) { string text2 = Path.Combine(directoryInfo.FullName, "config"); if (string.Equals(directoryInfo.Name, "BepInEx", StringComparison.OrdinalIgnoreCase) && Directory.Exists(text2)) { return text2; } num++; directoryInfo = directoryInfo.Parent; } } catch { } return null; } private static bool Leftover(string s) { bool flag = false; int num = 0; int num2 = 0; for (int i = 0; i < s.Length; i++) { char c = s[i]; if (c >= 'Ѐ' && c <= 'ӿ') { flag = true; num = 0; continue; } if (c == '<') { int num3 = s.IndexOf('>', i); if (num3 > i && num3 - i < 24) { i = num3; num = 0; continue; } } if (c >= 'a' && c <= 'z') { if (num > 0 || i == 0 || !IsLatinLetter(s[i - 1])) { num++; } if (num > num2) { num2 = num; } } else { num = 0; } } if (flag) { return num2 >= 4; } return false; } private static void Note(string text) { try { if (text != null && seen.Count < 4000 && text.Length <= 400) { string item = Flatten(text); if (seen.Add(item)) { pending.Add(item); } } if (pending.Count != 0) { int tickCount = Environment.TickCount; if (tickCount - nextFlush >= 0) { nextFlush = tickCount + 3000; Flush(); } } } catch { logging = false; } } private static void Flush() { try { lock (Gate) { if (pending != null && pending.Count != 0) { File.AppendAllText(logPath, string.Join("\n", pending.ToArray()) + "\n", new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); pending.Clear(); } } } catch { logging = false; } } private static bool LooksEnglish(string s) { int num = 0; int num2 = 0; for (int i = 0; i < s.Length; i++) { char c = s[i]; if (c >= 'Ѐ' && c <= 'ӿ') { return false; } if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { num++; if (num > num2) { num2 = num; } } else if (c == '<') { int num3 = s.IndexOf('>', i); if (num3 > i && num3 - i < 24) { i = num3; } num = 0; } else { num = 0; } } return num2 >= 3; } private static string Flatten(string s) { if (s.IndexOf('\n') < 0 && s.IndexOf('\r') < 0 && s.IndexOf('\\') < 0) { return s; } return s.Replace("\\", "\\\\").Replace("\r", "\\r").Replace("\n", "\\n"); } } internal sealed class Lang { public readonly Dictionary<string, string> Global = new Dictionary<string, string>(StringComparer.Ordinal); public readonly Dictionary<string, Dictionary<string, string>> Scoped = new Dictionary<string, Dictionary<string, string>>(StringComparer.Ordinal); public readonly Dictionary<string, Dictionary<string, List<string>>> Sentences = new Dictionary<string, Dictionary<string, List<string>>>(StringComparer.Ordinal); public readonly Dictionary<string, HashSet<string>> Unsafe = new Dictionary<string, HashSet<string>>(StringComparer.Ordinal); public readonly Dictionary<string, string> Terms = new Dictionary<string, string>(StringComparer.Ordinal); public readonly Dictionary<string, int[]> Numbers = new Dictionary<string, int[]>(StringComparer.Ordinal); public readonly Dictionary<string, int[]> Words = new Dictionary<string, int[]>(StringComparer.Ordinal); public readonly Dictionary<string, int[]> WordForms = new Dictionary<string, int[]>(StringComparer.Ordinal); public readonly Dictionary<string, string> Patterns = new Dictionary<string, string>(StringComparer.Ordinal); public readonly Dictionary<string, Dictionary<string, string>> Data = new Dictionary<string, Dictionary<string, string>>(StringComparer.Ordinal); public readonly Dictionary<string, string> Shapes = new Dictionary<string, string>(StringComparer.Ordinal); public readonly Dictionary<string, string[]> Adjectives = new Dictionary<string, string[]>(StringComparer.Ordinal); public readonly Dictionary<string, string> Nouns = new Dictionary<string, string>(StringComparer.Ordinal); public readonly Dictionary<string, string> Owners = new Dictionary<string, string>(StringComparer.Ordinal); public readonly List<string> Problems = new List<string>(); public readonly List<string> Files = new List<string>(); public int Entries; public int Rejected; public ulong Fingerprint = 14695981039346656037uL; public static Lang Load(string dir) { Lang lang = new Lang(); if (string.IsNullOrEmpty(dir) || !Directory.Exists(dir)) { return lang; } string[] files = Directory.GetFiles(dir, "*.po", SearchOption.AllDirectories); Array.Sort(files, (IComparer<string>?)StringComparer.OrdinalIgnoreCase); string[] array = files; foreach (string text in array) { try { byte[] array2 = File.ReadAllBytes(text); lang.Fingerprint = Hash.Fnv(lang.Fingerprint, Path.GetFileName(text)); lang.Fingerprint = Hash.Fnv(lang.Fingerprint, array2); lang.Files.Add(text); List<PoEntry> list = new List<PoEntry>(); Po.Parse(Encoding.UTF8.GetString(array2), Path.GetFileName(text), list, lang.Problems); foreach (PoEntry item in list) { lang.Add(item); } } catch (Exception ex) { lang.Problems.Add(Path.GetFileName(text) + ": " + ex.Message); } } return lang; } private void Add(PoEntry e) { if (e.Ctx == "numbers" || e.Ctx == "words") { List<int> list = new List<int>(); List<int> list2 = new List<int>(); bool flag = false; string[] array = e.Str.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); int num = -1; if (e.Ctx == "words" && text.Length > 1) { num = "mfnpMFNP".IndexOf(text[text.Length - 1]); if (num >= 0) { text = text.Substring(0, text.Length - 1); } } if (int.TryParse(text, out var result) && result >= 0) { list.Add(result); list2.Add(num); if (num >= 0) { flag = true; } } } if (list.Count > 0) { ((e.Ctx == "numbers") ? Numbers : Words)[e.Id] = list.ToArray(); if (flag) { WordForms[e.Id] = list2.ToArray(); } } return; } if ((e.Ctx != null && (e.Ctx == "data" || e.Ctx.StartsWith("data:", StringComparison.Ordinal))) ? (!Fmt.SameTokens(e.Id, e.Str)) : (!Fmt.SameHoles(e.Id, e.Str))) { Rejected++; Problems.Add(e.File + ":" + e.Line + ": {placeholders} differ from the original, entry ignored"); return; } Entries++; if (e.Ctx == "shape") { Shapes[e.Id] = e.Str; } else if (e.Ctx == null) { Global[e.Id] = e.Str; } else if (e.Ctx == "term") { Terms[e.Id] = e.Str; } else if (e.Ctx == "adj" || e.Ctx == "noun" || e.Ctx == "of") { string[] array2 = e.Str.Split(new char[1] { '|' }); bool flag2 = e.Id.Trim().Length > 0; string[] array = array2; for (int i = 0; i < array.Length; i++) { if (array[i].Trim().Length == 0) { flag2 = false; } } if (!((e.Ctx == "adj") ? (flag2 && array2.Length == 4) : ((!(e.Ctx == "noun")) ? (flag2 && array2.Length == 1) : (flag2 && array2.Length <= 2 && array2[0].Length == 1 && "mfnp".IndexOf(array2[0][0]) >= 0)))) { Entries--; Rejected++; Problems.Add(e.File + ":" + e.Line + ": " + ((e.Ctx == "adj") ? "an adjective needs four forms (m|f|n|p)" : ((e.Ctx == "noun") ? "a noun needs its gender (m, f, n or p)" : "one word form expected")) + ", entry ignored"); } else if (e.Ctx == "adj") { Adjectives[e.Id] = array2; } else if (e.Ctx == "noun") { Nouns[e.Id] = e.Str; } else { Owners[e.Id] = e.Str; } } else if (e.Ctx == "pattern") { bool flag3 = false; foreach (string item in Fmt.Holes(e.Id)) { if (Fmt.FormatIndex(item) >= 0) { flag3 = true; } } if (!flag3) { Entries--; Rejected++; Problems.Add(e.File + ":" + e.Line + ": a pattern needs at least one {0} placeholder, entry ignored"); } else { Patterns[e.Id] = e.Str; } } else if (e.Ctx[0] == '@') { string text2 = e.Ctx.Substring(1).Trim(); int num2 = text2.LastIndexOf('#'); if (num2 > 0) { string key = text2.Substring(num2 + 1).Trim(); text2 = text2.Substring(0, num2).Trim(); if (!Sentences.TryGetValue(text2, out var value)) { value = new Dictionary<string, List<string>>(StringComparer.Ordinal); Sentences[text2] = value; } if (!value.TryGetValue(key, out var value2)) { value2 = (value[key] = new List<string>()); } value2.Add(e.Id); } if (Scoped.TryGetValue(text2, out var value3) && value3.TryGetValue(e.Id, out var value4) && !string.Equals(value4, e.Str, StringComparison.Ordinal)) { if (!Unsafe.TryGetValue(text2, out var value5)) { value5 = new HashSet<string>(StringComparer.Ordinal); Unsafe[text2] = value5; } value5.Add(e.Id); Problems.Add(e.File + ":" + e.Line + ": two different translations of one text inside " + text2 + ", it stays untouched in the code"); } else { Put(Scoped, text2, e); } } else if (e.Ctx == "data") { Put(Data, "", e); } else if (e.Ctx.StartsWith("data:", StringComparison.Ordinal)) { Put(Data, e.Ctx.Substring(5).Trim(), e); } else { Entries--; Rejected++; Problems.Add(e.File + ":" + e.Line + ": unknown msgctxt \"" + e.Ctx + "\", entry ignored"); } } private static void Put(Dictionary<string, Dictionary<string, string>> table, string key, PoEntry e) { if (!table.TryGetValue(key, out var value)) { value = (table[key] = new Dictionary<string, string>(StringComparer.Ordinal)); } value[e.Id] = e.Str; } } internal sealed class PoEntry { public string Ctx; public string Id; public string Str; public string File; public int Line; } internal static class Po { public static void Parse(string text, string file, List<PoEntry> output, List<string> problems) { string ctx = null; string id = null; string str = null; int num = 0; int line = 0; int num2 = 0; int num3 = 0; int length = text.Length; while (num3 <= length) { int num4 = text.IndexOf('\n', num3); if (num4 < 0) { num4 = length; } string text2 = text.Substring(num3, num4 - num3).Trim(); num3 = num4 + 1; num2++; if (num2 == 1 && text2.Length > 0 && text2[0] == '\ufeff') { text2 = text2.Substring(1).Trim(); } if (text2.Length == 0) { Flush(ref ctx, ref id, ref str, file, line, output); num = 0; } else { if (text2[0] == '#') { continue; } if (text2[0] == '"') { string text3 = Unquote(text2, file, num2, problems); switch (num) { case 1: ctx += text3; break; case 2: id += text3; break; case 3: str += text3; break; } } else if (text2.StartsWith("msgctxt", StringComparison.Ordinal)) { if (num == 3) { Flush(ref ctx, ref id, ref str, file, line, output); } ctx = Unquote(text2, file, num2, problems); num = 1; line = num2; } else if (text2.StartsWith("msgid_plural", StringComparison.Ordinal)) { num = 0; } else if (text2.StartsWith("msgid", StringComparison.Ordinal)) { if (num == 3) { Flush(ref ctx, ref id, ref str, file, line, output); } id = Unquote(text2, file, num2, problems); if (num != 1) { line = num2; } num = 2; } else if (text2.StartsWith("msgstr[", StringComparison.Ordinal)) { if (text2.StartsWith("msgstr[0]", StringComparison.Ordinal)) { str = Unquote(text2, file, num2, problems); num = 3; } else { num = 0; } } else if (text2.StartsWith("msgstr", StringComparison.Ordinal)) { str = Unquote(text2, file, num2, problems); num = 3; } else { problems?.Add(file + ":" + num2 + ": unrecognised line"); num = 0; } } } Flush(ref ctx, ref id, ref str, file, line, output); } private static void Flush(ref string ctx, ref string id, ref string str, string file, int line, List<PoEntry> output) { if (!string.IsNullOrEmpty(id) && !string.IsNullOrEmpty(str)) { PoEntry poEntry = new PoEntry(); poEntry.Ctx = (string.IsNullOrEmpty(ctx) ? null : ctx); poEntry.Id = id; poEntry.Str = str; poEntry.File = file; poEntry.Line = line; output.Add(poEntry); } ctx = null; id = null; str = null; } private static string Unquote(string line, string file, int n, List<string> problems) { int num = line.IndexOf('"'); int num2 = line.LastIndexOf('"'); if (num < 0 || num2 <= num) { problems?.Add(file + ":" + n + ": missing quotes"); return ""; } StringBuilder stringBuilder = new StringBuilder(num2 - num); for (int i = num + 1; i < num2; i++) { char c = line[i]; if (c != '\\' || i + 1 >= num2) { stringBuilder.Append(c); continue; } char c2 = line[++i]; switch (c2) { case 'n': stringBuilder.Append('\n'); break; case 't': stringBuilder.Append('\t'); break; case 'r': stringBuilder.Append('\r'); break; case '0': stringBuilder.Append('\0'); break; case 'u': if (i + 4 < num2 && IsHex4(line, i + 1)) { stringBuilder.Append((char)Convert.ToInt32(line.Substring(i + 1, 4), 16)); i += 4; } else { stringBuilder.Append('u'); } break; default: stringBuilder.Append(c2); break; } } return stringBuilder.ToString(); } private static bool IsHex4(string s, int at) { if (at + 4 > s.Length) { return false; } for (int i = at; i < at + 4; i++) { char c = s[i]; if ((c < '0' || c > '9') && (c < 'a' || c > 'f') && (c < 'A' || c > 'F')) { return false; } } return true; } } internal static class Fmt { public static bool SameHoles(string a, string b) { if (a.IndexOf('{') < 0 && a.IndexOf('}') < 0 && b.IndexOf('{') < 0 && b.IndexOf('}') < 0) { return true; } List<string> list = Holes(a); List<string> list2 = Holes(b); HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); List<string> list3 = new List<string>(); List<string> list4 = new List<string>(); foreach (string item in list) { if (IsNamed(item) || FormatIndex(item) >= 0) { hashSet.Add(item); } else { list3.Add(item); } } foreach (string item2 in list2) { if (IsNamed(item2) || FormatIndex(item2) >= 0) { if (!hashSet.Contains(item2)) { return false; } } else { list4.Add(item2); } } if (list3.Count != list4.Count) { return false; } list3.Sort(string.CompareOrdinal); list4.Sort(string.CompareOrdinal); for (int i = 0; i < list3.Count; i++) { if (!string.Equals(list3[i], list4[i], StringComparison.Ordinal)) { return false; } } return true; } public static bool SameTokens(string a, string b) { if (a.IndexOf('{') < 0 && a.IndexOf('}') < 0 && b.IndexOf('{') < 0 && b.IndexOf('}') < 0) { return true; } List<string> list = Holes(a); List<string> list2 = Holes(b); if (list.Count != list2.Count) { return false; } for (int i = 0; i < list.Count; i++) { list[i] = TokenHead(list[i]); } for (int j = 0; j < list2.Count; j++) { list2[j] = TokenHead(list2[j]); } list.Sort(string.CompareOrdinal); list2.Sort(string.CompareOrdinal); for (int k = 0; k < list.Count; k++) { if (!string.Equals(list[k], list2[k], StringComparison.Ordinal)) { return false; } } return true; } private static string TokenHead(string hole) { int num = hole.IndexOf('?'); if (num <= 0 || hole.IndexOf(':') <= 0 || hole.IndexOf(':') >= num) { return hole; } return hole.Substring(0, num); } private static bool IsNamed(string hole) { if (hole.Length >= 3 && hole[0] == '{' && hole[hole.Length - 1] == '}') { if (hole[1] < 'a' || hole[1] > 'z') { if (hole[1] >= 'A') { return hole[1] <= 'Z'; } return false; } return true; } return false; } public static int FormatIndex(string hole) { if (hole.Length < 3 || hole[0] != '{' || hole[hole.L