Decompiled source of H3VR Chinese Localization v0.1.4
H3VR_Chinese_Localization/plugins/XUnity.AutoTranslator/ExIni.dll
Decompiled 4 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using System.Text.RegularExpressions; using Microsoft.Win32; [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyInformationalVersion("GIT master [cf85ca6f3fc361cf48bf56107e4cec9102b7f5eb] ")] [assembly: AssemblyTitle("ExIni")] [assembly: Guid("b8adf1ac-aade-485a-8997-14c4b42e0a8b")] [assembly: AssemblyDescription("Extended INI File Handler")] [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: AssemblyFileVersion("1.0.2.1")] [assembly: AssemblyCompany("Usagirei")] [assembly: AssemblyProduct("ExIni")] [assembly: ComVisible(false)] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCopyright("Copyright © Usagirei 2015")] [assembly: AssemblyTrademark("")] [assembly: AssemblyVersion("1.0.2.1")] namespace ExIni; public class IniComment { public List<string> Comments { get; set; } public IniComment() { Comments = new List<string>(); } public override string ToString() { StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < Comments.Count; i++) { string text = Comments[i]; string value = ((i < Comments.Count - 1) ? (";" + text + Environment.NewLine) : (";" + text)); stringBuilder.Append(value); } return stringBuilder.ToString(); } public void Append(params string[] comments) { Comments.AddRange(comments); } } public class IniFile { private readonly IniComment _comments; private readonly List<IniSection> _sections; public IniSection this[string sec] => CreateSection(sec); public IniComment Comments => _comments; public List<IniSection> Sections => _sections; public IniFile() { _comments = new IniComment(); _sections = new List<IniSection>(); } public override string ToString() { StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < Sections.Count; i++) { IniSection iniSection = Sections[i]; if (iniSection.Comments.Comments.Any()) { stringBuilder.AppendLine(iniSection.Comments.ToString()); } stringBuilder.AppendLine(iniSection.ToString()); foreach (IniKey key in iniSection.Keys) { if (key.Comments.Comments.Any()) { stringBuilder.AppendLine(key.Comments.ToString()); } stringBuilder.AppendLine(key.ToString()); } if (i < Sections.Count - 1) { stringBuilder.AppendLine(); } } if (Comments.Comments.Any()) { stringBuilder.AppendLine(); stringBuilder.AppendLine(Comments.ToString()); } return stringBuilder.ToString(); } public IniSection CreateSection(string section) { IniSection section2 = GetSection(section); if (section2 != null) { return section2; } IniSection iniSection = new IniSection(section); _sections.Add(iniSection); return iniSection; } public bool DeleteSection(string section) { if (!HasSection(section)) { return false; } Sections.Remove(GetSection(section)); return true; } public IniSection GetSection(string section) { if (!HasSection(section)) { return null; } return _sections.FirstOrDefault((IniSection iniSection) => iniSection.Section == section); } public bool HasSection(string section) { return _sections.Any((IniSection iniSection) => iniSection.Section == section); } public void Merge(IniFile ini) { Comments.Append(ini.Comments.Comments.ToArray()); foreach (IniSection section in ini.Sections) { IniSection iniSection = this[section.Section]; iniSection.Comments.Append(section.Comments.Comments.ToArray()); foreach (IniKey key in section.Keys) { IniKey iniKey = iniSection[key.Key]; iniKey.Comments.Append(key.Comments.Comments.ToArray()); iniKey.Value = key.Value; } } } public void Save(string filePath) { string directoryName = Path.GetDirectoryName(filePath); if (!string.IsNullOrEmpty(directoryName)) { Directory.CreateDirectory(directoryName); } File.WriteAllText(filePath, ToString(), Encoding.UTF8); } public static IniFile FromFile(string iniString) { return IniParser.Parse(File.ReadAllText(iniString)); } public static IniFile FromString(string iniString) { return IniParser.Parse(iniString); } } public class IniKey { private readonly IniComment _comments; public IniComment Comments => _comments; public string Key { get; set; } public string RawValue { get; set; } public string Value { get { return Resolve(RawValue); } set { RawValue = value; } } public IniKey(string key, string value = null) { Key = key; Value = value; _comments = new IniComment(); } public override string ToString() { return $"{Key}={RawValue}"; } private static string GetEnvironment(string env) { return Environment.ExpandEnvironmentVariables(env); } private static string GetRegistry(string path) { string directoryName = Path.GetDirectoryName(path); string fileName = Path.GetFileName(path); if (string.IsNullOrEmpty(directoryName)) { return null; } return Registry.GetValue(directoryName, fileName, string.Empty)?.ToString(); } private static string Resolve(string value) { if (value == null) { return null; } Regex regex = new Regex("\\$\\((?<reg>.*)\\)"); Regex regex2 = new Regex("%.*%"); while (regex.IsMatch(value) || regex2.IsMatch(value)) { value = regex.Replace(value, (Match match) => GetRegistry(match.Groups["reg"].Value)); value = GetEnvironment(value); } return value; } } internal class IniParser { private static readonly Regex CommentRegex = new Regex("^;(?<com>.*)"); private static readonly Regex KeyRegex = new Regex("^(?<key>[\\w\\s]+)=(?<val>.*)$"); private static readonly Regex SectionRegex = new Regex("^\\[(?<sec>[\\w\\s]+)\\]$"); private static readonly Regex VarRegex = new Regex("^\\@(?<key>[\\w\\s]+)=(?<val>.*)$"); public static IniFile Parse(string iniString) { IniFile iniFile = new IniFile(); string[] array = (from line in iniString.Split(new char[1] { '\n' }) let trimmed = line.Trim() select trimmed.TrimEnd(new char[1] { '\r' })).ToArray(); List<string> list = new List<string>(); IniSection iniSection = null; bool flag = false; for (int i = 0; i < array.Length; i++) { string text = array[i]; if (string.IsNullOrEmpty(text)) { continue; } if (IsComment(text)) { string comment = GetComment(text); list.Add(comment); if (IsVariable(comment)) { string[] variable = GetVariable(comment); string variable2 = variable[0]; string value = variable[1]; Environment.SetEnvironmentVariable(variable2, value); } flag = true; } else if (IsSection(text)) { iniSection = iniFile[GetSection(text)]; if (flag) { iniSection.Comments.Append(list.ToArray()); list.Clear(); flag = false; } } else if (IsKey(text)) { if (iniSection == null) { throw new Exception($"{i}: Sectionless Key Value Pair"); } string[] key = GetKey(text); string key2 = key[0]; string value2 = key[1]; if (flag) { iniSection[key2].Comments.Append(list.ToArray()); list.Clear(); flag = false; } iniSection[key2].Value = value2; } } if (flag) { iniFile.Comments.Append(list.ToArray()); } return iniFile; } private static string GetComment(string line) { return CommentRegex.Match(line).Groups["com"].Value; } private static string[] GetKey(string line) { Match match = KeyRegex.Match(line); return new string[2] { match.Groups["key"].Value, match.Groups["val"].Value }; } private static string GetSection(string line) { return SectionRegex.Match(line).Groups["sec"].Value; } private static string[] GetVariable(string line) { Match match = VarRegex.Match(line); return new string[2] { match.Groups["key"].Value, match.Groups["val"].Value }; } private static bool IsComment(string line) { return CommentRegex.IsMatch(line); } private static bool IsKey(string line) { return KeyRegex.IsMatch(line); } private static bool IsSection(string line) { return SectionRegex.IsMatch(line); } private static bool IsVariable(string line) { return VarRegex.IsMatch(line); } } public class IniSection { private readonly IniComment _comments; private readonly List<IniKey> _keys; public IniKey this[string key] => CreateKey(key); public IniComment Comments => _comments; public List<IniKey> Keys => _keys; public string Section { get; set; } public IniSection(string section) { Section = section; _comments = new IniComment(); _keys = new List<IniKey>(); } public override string ToString() { return $"[{Section}]"; } public IniKey CreateKey(string key) { IniKey key2 = GetKey(key); if (key2 != null) { return key2; } IniKey iniKey = new IniKey(key); _keys.Add(iniKey); return iniKey; } public IniKey GetKey(string key) { if (!HasKey(key)) { return null; } return _keys.FirstOrDefault((IniKey iniKey) => iniKey.Key == key); } public bool HasKey(string key) { return _keys.Any((IniKey iniKey) => iniKey.Key == key); } public bool DeleteKey(string key) { if (!HasKey(key)) { return false; } Keys.Remove(GetKey(key)); return true; } }
H3VR_Chinese_Localization/plugins/XUnity.AutoTranslator/XUnity.AutoTranslator.Plugin.BepIn-5x.dll
Decompiled 4 days agousing System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using ExIni; using XUnity.AutoTranslator.Plugin.Core; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyCompany("XUnity.AutoTranslator.Plugin.BepIn-5x")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("4.21.0.0")] [assembly: AssemblyInformationalVersion("4.21.0")] [assembly: AssemblyProduct("XUnity.AutoTranslator.Plugin.BepIn-5x")] [assembly: AssemblyTitle("XUnity.AutoTranslator.Plugin.BepIn-5x")] [assembly: AssemblyVersion("4.21.0.0")] namespace XUnity.AutoTranslator.Plugin.BepIn_5x; [BepInPlugin("gravydevsupreme.xunity.autotranslator", "XUnity Auto Translator", "4.21.0")] public class AutoTranslatorPlugin : BaseUnityPlugin, IPluginEnvironment { private IniFile _file; private string _configPath; public IniFile Preferences => _file ?? (_file = ReloadConfig()); public string ConfigPath { get; } public string TranslationPath { get; } public bool AllowDefaultInitializeHarmonyDetourBridge => false; public AutoTranslatorPlugin() { ConfigPath = Paths.ConfigPath; TranslationPath = Paths.BepInExRootPath; _configPath = Path.Combine(ConfigPath, "AutoTranslatorConfig.ini"); } public IniFile ReloadConfig() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) if (!File.Exists(_configPath)) { return (IniFile)(((object)_file) ?? ((object)new IniFile())); } IniFile val = IniFile.FromFile(_configPath); if (_file == null) { return _file = val; } _file.Merge(val); return _file; } public void SaveConfig() { _file.Save(_configPath); } private void Awake() { PluginLoader.LoadWithConfig((IPluginEnvironment)(object)this); } }
H3VR_Chinese_Localization/plugins/XUnity.AutoTranslator/XUnity.AutoTranslator.Plugin.Core.dll
Decompiled 4 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.CodeDom.Compiler; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Linq.Expressions; using System.Net; using System.Net.Cache; using System.Net.Security; using System.Reflection; using System.Reflection.Emit; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Security; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using DynamicLinq; using ExIni; using Harmony; using ICSharpCode.SharpZipLib.Checksums; using ICSharpCode.SharpZipLib.Core; using ICSharpCode.SharpZipLib.Encryption; using ICSharpCode.SharpZipLib.Zip; using ICSharpCode.SharpZipLib.Zip.Compression; using ICSharpCode.SharpZipLib.Zip.Compression.Streams; using Microsoft.CodeAnalysis; using MonoMod.RuntimeDetour; using UnityEngine; using UnityEngine.SceneManagement; using XUnity.AutoTranslator.Plugin.Core; using XUnity.AutoTranslator.Plugin.Core.AssetRedirection; using XUnity.AutoTranslator.Plugin.Core.Configuration; using XUnity.AutoTranslator.Plugin.Core.Debugging; using XUnity.AutoTranslator.Plugin.Core.Endpoints; using XUnity.AutoTranslator.Plugin.Core.Extensions; using XUnity.AutoTranslator.Plugin.Core.Fonts; using XUnity.AutoTranslator.Plugin.Core.Hooks; using XUnity.AutoTranslator.Plugin.Core.Hooks.FairyGUI; using XUnity.AutoTranslator.Plugin.Core.Hooks.IMGUI; using XUnity.AutoTranslator.Plugin.Core.Hooks.NGUI; using XUnity.AutoTranslator.Plugin.Core.Hooks.TextGetterCompat; using XUnity.AutoTranslator.Plugin.Core.Hooks.TextMeshPro; using XUnity.AutoTranslator.Plugin.Core.Hooks.UGUI; using XUnity.AutoTranslator.Plugin.Core.Parsing; using XUnity.AutoTranslator.Plugin.Core.Properties; using XUnity.AutoTranslator.Plugin.Core.Shim; using XUnity.AutoTranslator.Plugin.Core.Text; using XUnity.AutoTranslator.Plugin.Core.UI; using XUnity.AutoTranslator.Plugin.Core.UIResize; using XUnity.AutoTranslator.Plugin.Core.Utilities; using XUnity.AutoTranslator.Plugin.Core.Web; using XUnity.AutoTranslator.Plugin.Core.Web.Internal; using XUnity.AutoTranslator.Plugin.ExtProtocol; using XUnity.AutoTranslator.Plugin.Utilities; using XUnity.Common.Constants; using XUnity.Common.Extensions; using XUnity.Common.Harmony; using XUnity.Common.Logging; using XUnity.Common.MonoMod; using XUnity.Common.Utilities; using XUnity.ResourceRedirector; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: InternalsVisibleTo("XUnity.AutoTranslator.Plugin.Core.Tests")] [assembly: AssemblyCompany("gravydevsupreme")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Main development dependency for XUnity Auto Translator.")] [assembly: AssemblyFileVersion("4.21.0.0")] [assembly: AssemblyInformationalVersion("4.21.0")] [assembly: AssemblyProduct("XUnity.AutoTranslator.Plugin.Core")] [assembly: AssemblyTitle("XUnity.AutoTranslator.Plugin.Core")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("4.21.0.0")] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class IsReadOnlyAttribute : Attribute { } } namespace DynamicLinq { internal static class DynamicQueryable { public static IQueryable<T> Where<T>(this IQueryable<T> source, string predicate, params object[] values) { return (IQueryable<T>)((IQueryable)source).Where(predicate, values); } public static IQueryable Where(this IQueryable source, string predicate, params object[] values) { if (source == null) { throw new ArgumentNullException("source"); } if (predicate == null) { throw new ArgumentNullException("predicate"); } LambdaExpression expression = DynamicExpression.ParseLambda(source.ElementType, typeof(bool), predicate, values); return source.Provider.CreateQuery(Expression.Call(typeof(Queryable), "Where", new Type[1] { source.ElementType }, source.Expression, Expression.Quote(expression))); } public static IQueryable Select(this IQueryable source, string selector, params object[] values) { if (source == null) { throw new ArgumentNullException("source"); } if (selector == null) { throw new ArgumentNullException("selector"); } LambdaExpression lambdaExpression = DynamicExpression.ParseLambda(source.ElementType, null, selector, values); return source.Provider.CreateQuery(Expression.Call(typeof(Queryable), "Select", new Type[2] { source.ElementType, lambdaExpression.Body.Type }, source.Expression, Expression.Quote(lambdaExpression))); } public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string ordering, params object[] values) { return (IQueryable<T>)((IQueryable)source).OrderBy(ordering, values); } public static IQueryable OrderBy(this IQueryable source, string ordering, params object[] values) { if (source == null) { throw new ArgumentNullException("source"); } if (ordering == null) { throw new ArgumentNullException("ordering"); } ParameterExpression[] parameters = new ParameterExpression[1] { Expression.Parameter(source.ElementType, "") }; IEnumerable<DynamicOrdering> enumerable = new ExpressionParser(parameters, ordering, values).ParseOrdering(); Expression expression = source.Expression; string text = "OrderBy"; string text2 = "OrderByDescending"; foreach (DynamicOrdering item in enumerable) { expression = Expression.Call(typeof(Queryable), item.Ascending ? text : text2, new Type[2] { source.ElementType, item.Selector.Type }, expression, Expression.Quote(Expression.Lambda(item.Selector, parameters))); text = "ThenBy"; text2 = "ThenByDescending"; } return source.Provider.CreateQuery(expression); } public static IQueryable Take(this IQueryable source, int count) { if (source == null) { throw new ArgumentNullException("source"); } return source.Provider.CreateQuery(Expression.Call(typeof(Queryable), "Take", new Type[1] { source.ElementType }, source.Expression, Expression.Constant(count))); } public static IQueryable Skip(this IQueryable source, int count) { if (source == null) { throw new ArgumentNullException("source"); } return source.Provider.CreateQuery(Expression.Call(typeof(Queryable), "Skip", new Type[1] { source.ElementType }, source.Expression, Expression.Constant(count))); } public static IQueryable GroupBy(this IQueryable source, string keySelector, string elementSelector, params object[] values) { if (source == null) { throw new ArgumentNullException("source"); } if (keySelector == null) { throw new ArgumentNullException("keySelector"); } if (elementSelector == null) { throw new ArgumentNullException("elementSelector"); } LambdaExpression lambdaExpression = DynamicExpression.ParseLambda(source.ElementType, null, keySelector, values); LambdaExpression lambdaExpression2 = DynamicExpression.ParseLambda(source.ElementType, null, elementSelector, values); return source.Provider.CreateQuery(Expression.Call(typeof(Queryable), "GroupBy", new Type[3] { source.ElementType, lambdaExpression.Body.Type, lambdaExpression2.Body.Type }, source.Expression, Expression.Quote(lambdaExpression), Expression.Quote(lambdaExpression2))); } public static bool Any(this IQueryable source) { if (source == null) { throw new ArgumentNullException("source"); } return (bool)source.Provider.Execute(Expression.Call(typeof(Queryable), "Any", new Type[1] { source.ElementType }, source.Expression)); } public static int Count(this IQueryable source) { if (source == null) { throw new ArgumentNullException("source"); } return (int)source.Provider.Execute(Expression.Call(typeof(Queryable), "Count", new Type[1] { source.ElementType }, source.Expression)); } } internal abstract class DynamicClass { public override string ToString() { PropertyInfo[] properties = GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("{"); for (int i = 0; i < properties.Length; i++) { if (i > 0) { stringBuilder.Append(", "); } stringBuilder.Append(properties[i].Name); stringBuilder.Append("="); stringBuilder.Append(properties[i].GetValue(this, null)); } stringBuilder.Append("}"); return stringBuilder.ToString(); } } internal class DynamicProperty { private string name; private Type type; public string Name => name; public Type Type => type; public DynamicProperty(string name, Type type) { if (name == null) { throw new ArgumentNullException("name"); } if ((object)type == null) { throw new ArgumentNullException("type"); } this.name = name; this.type = type; } } internal static class DynamicExpression { public static Expression Parse(Type resultType, string expression, params object[] values) { return new ExpressionParser(null, expression, values).Parse(resultType); } public static LambdaExpression ParseLambda(Type itType, Type resultType, string expression, params object[] values) { return ParseLambda(new ParameterExpression[1] { Expression.Parameter(itType, "") }, resultType, expression, values); } public static LambdaExpression ParseLambda(ParameterExpression[] parameters, Type resultType, string expression, params object[] values) { return Expression.Lambda(new ExpressionParser(parameters, expression, values).Parse(resultType), parameters); } public static Expression<Func<T, S>> ParseLambda<T, S>(string expression, params object[] values) { return (Expression<Func<T, S>>)ParseLambda(typeof(T), typeof(S), expression, values); } public static Type CreateClass(params DynamicProperty[] properties) { throw new NotImplementedException(); } public static Type CreateClass(IEnumerable<DynamicProperty> properties) { throw new NotImplementedException(); } } internal class DynamicOrdering { public Expression Selector; public bool Ascending; } internal class Signature : IEquatable<Signature> { public DynamicProperty[] properties; public int hashCode; public Signature(IEnumerable<DynamicProperty> properties) { this.properties = properties.ToArray(); hashCode = 0; foreach (DynamicProperty property in properties) { hashCode ^= property.Name.GetHashCode() ^ property.Type.GetHashCode(); } } public override int GetHashCode() { return hashCode; } public override bool Equals(object obj) { if (!(obj is Signature)) { return false; } return Equals((Signature)obj); } public bool Equals(Signature other) { if (properties.Length != other.properties.Length) { return false; } for (int i = 0; i < properties.Length; i++) { if (properties[i].Name != other.properties[i].Name || (object)properties[i].Type != other.properties[i].Type) { return false; } } return true; } } internal sealed class ParseException : Exception { private int position; public int Position => position; public ParseException(string message, int position) : base(message) { this.position = position; } public override string ToString() { return $"{Message} (at index {position})"; } } internal class ExpressionParser { private struct Token { public TokenId id; public string text; public int pos; } private enum TokenId { Unknown, End, Identifier, StringLiteral, IntegerLiteral, RealLiteral, Exclamation, Percent, Amphersand, OpenParen, CloseParen, Asterisk, Plus, Comma, Minus, Dot, Slash, Colon, LessThan, Equal, GreaterThan, Question, OpenBracket, CloseBracket, Bar, ExclamationEqual, DoubleAmphersand, LessThanEqual, LessGreater, DoubleEqual, GreaterThanEqual, DoubleBar } private interface ILogicalSignatures { void F(bool x, bool y); void F(bool? x, bool? y); } private interface IArithmeticSignatures { void F(int x, int y); void F(uint x, uint y); void F(long x, long y); void F(ulong x, ulong y); void F(float x, float y); void F(double x, double y); void F(decimal x, decimal y); void F(int? x, int? y); void F(uint? x, uint? y); void F(long? x, long? y); void F(ulong? x, ulong? y); void F(float? x, float? y); void F(double? x, double? y); void F(decimal? x, decimal? y); } private interface IRelationalSignatures : IArithmeticSignatures { void F(string x, string y); void F(char x, char y); void F(DateTime x, DateTime y); void F(TimeSpan x, TimeSpan y); void F(char? x, char? y); void F(DateTime? x, DateTime? y); void F(TimeSpan? x, TimeSpan? y); } private interface IEqualitySignatures : IRelationalSignatures, IArithmeticSignatures { void F(bool x, bool y); void F(bool? x, bool? y); } private interface IAddSignatures : IArithmeticSignatures { void F(DateTime x, TimeSpan y); void F(TimeSpan x, TimeSpan y); void F(DateTime? x, TimeSpan? y); void F(TimeSpan? x, TimeSpan? y); } private interface ISubtractSignatures : IAddSignatures, IArithmeticSignatures { void F(DateTime x, DateTime y); void F(DateTime? x, DateTime? y); } private interface INegationSignatures { void F(int x); void F(long x); void F(float x); void F(double x); void F(decimal x); void F(int? x); void F(long? x); void F(float? x); void F(double? x); void F(decimal? x); } private interface INotSignatures { void F(bool x); void F(bool? x); } private interface IEnumerableSignatures { void Where(bool predicate); void Any(); void Any(bool predicate); void All(bool predicate); void Count(); void Count(bool predicate); void Min(object selector); void Max(object selector); void Sum(int selector); void Sum(int? selector); void Sum(long selector); void Sum(long? selector); void Sum(float selector); void Sum(float? selector); void Sum(double selector); void Sum(double? selector); void Sum(decimal selector); void Sum(decimal? selector); void Average(int selector); void Average(int? selector); void Average(long selector); void Average(long? selector); void Average(float selector); void Average(float? selector); void Average(double selector); void Average(double? selector); void Average(decimal selector); void Average(decimal? selector); } private class MethodData { public MethodBase MethodBase; public ParameterInfo[] Parameters; public Expression[] Args; } private static readonly Type[] predefinedTypes = new Type[20] { typeof(object), typeof(bool), typeof(char), typeof(string), typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal), typeof(DateTime), typeof(TimeSpan), typeof(Guid), typeof(Math), typeof(Convert) }; private static readonly Expression trueLiteral = Expression.Constant(true); private static readonly Expression falseLiteral = Expression.Constant(false); private static readonly Expression nullLiteral = Expression.Constant(null); private static readonly string keywordIt = "it"; private static readonly string keywordIif = "iif"; private static readonly string keywordNew = "new"; private static Dictionary<string, object> keywords; private Dictionary<string, object> symbols; private IDictionary<string, object> externals; private Dictionary<Expression, string> literals; private ParameterExpression it; private string text; private int textPos; private int textLen; private char ch; private Token token; public ExpressionParser(ParameterExpression[] parameters, string expression, object[] values) { if (expression == null) { throw new ArgumentNullException("expression"); } if (keywords == null) { keywords = CreateKeywords(); } symbols = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); literals = new Dictionary<Expression, string>(); if (parameters != null) { ProcessParameters(parameters); } if (values != null) { ProcessValues(values); } text = expression; textLen = text.Length; SetTextPos(0); NextToken(); } private void ProcessParameters(ParameterExpression[] parameters) { foreach (ParameterExpression parameterExpression in parameters) { if (!string.IsNullOrEmpty(parameterExpression.Name)) { AddSymbol(parameterExpression.Name, parameterExpression); } } if (parameters.Length == 1 && string.IsNullOrEmpty(parameters[0].Name)) { it = parameters[0]; } } private void ProcessValues(object[] values) { for (int i = 0; i < values.Length; i++) { object obj = values[i]; if (i == values.Length - 1 && obj is IDictionary<string, object>) { externals = (IDictionary<string, object>)obj; } else { AddSymbol("@" + i.ToString(CultureInfo.InvariantCulture), obj); } } } private void AddSymbol(string name, object value) { if (symbols.ContainsKey(name)) { throw ParseError("The identifier '{0}' was defined more than once", name); } symbols.Add(name, value); } public Expression Parse(Type resultType) { int pos = token.pos; Expression expression = ParseExpression(); if ((object)resultType != null && (expression = PromoteExpression(expression, resultType, exact: true)) == null) { throw ParseError(pos, "Expression of type '{0}' expected", GetTypeName(resultType)); } ValidateToken(TokenId.End, "Syntax error"); return expression; } public IEnumerable<DynamicOrdering> ParseOrdering() { List<DynamicOrdering> list = new List<DynamicOrdering>(); while (true) { Expression selector = ParseExpression(); bool ascending = true; if (TokenIdentifierIs("asc") || TokenIdentifierIs("ascending")) { NextToken(); } else if (TokenIdentifierIs("desc") || TokenIdentifierIs("descending")) { NextToken(); ascending = false; } list.Add(new DynamicOrdering { Selector = selector, Ascending = ascending }); if (token.id != TokenId.Comma) { break; } NextToken(); } ValidateToken(TokenId.End, "Syntax error"); return list; } private Expression ParseExpression() { int pos = token.pos; Expression expression = ParseLogicalOr(); if (token.id == TokenId.Question) { NextToken(); Expression expr = ParseExpression(); ValidateToken(TokenId.Colon, "':' expected"); NextToken(); Expression expr2 = ParseExpression(); expression = GenerateConditional(expression, expr, expr2, pos); } return expression; } private Expression ParseLogicalOr() { Expression left = ParseLogicalAnd(); while (this.token.id == TokenId.DoubleBar || TokenIdentifierIs("or")) { Token token = this.token; NextToken(); Expression right = ParseLogicalAnd(); CheckAndPromoteOperands(typeof(ILogicalSignatures), token.text, ref left, ref right, token.pos); left = Expression.OrElse(left, right); } return left; } private Expression ParseLogicalAnd() { Expression left = ParseComparison(); while (this.token.id == TokenId.DoubleAmphersand || TokenIdentifierIs("and")) { Token token = this.token; NextToken(); Expression right = ParseComparison(); CheckAndPromoteOperands(typeof(ILogicalSignatures), token.text, ref left, ref right, token.pos); left = Expression.AndAlso(left, right); } return left; } private Expression ParseComparison() { Expression left = ParseAdditive(); while (this.token.id == TokenId.Equal || this.token.id == TokenId.DoubleEqual || this.token.id == TokenId.ExclamationEqual || this.token.id == TokenId.LessGreater || this.token.id == TokenId.GreaterThan || this.token.id == TokenId.GreaterThanEqual || this.token.id == TokenId.LessThan || this.token.id == TokenId.LessThanEqual) { Token token = this.token; NextToken(); Expression right = ParseAdditive(); bool flag = token.id == TokenId.Equal || token.id == TokenId.DoubleEqual || token.id == TokenId.ExclamationEqual || token.id == TokenId.LessGreater; if (flag && !left.Type.IsValueType && !right.Type.IsValueType) { if ((object)left.Type != right.Type) { if (left.Type.IsAssignableFrom(right.Type)) { right = Expression.Convert(right, left.Type); } else { if (!right.Type.IsAssignableFrom(left.Type)) { throw IncompatibleOperandsError(token.text, left, right, token.pos); } left = Expression.Convert(left, right.Type); } } } else if (IsEnumType(left.Type) || IsEnumType(right.Type)) { if ((object)left.Type != right.Type) { Expression expression; if ((expression = PromoteExpression(right, left.Type, exact: true)) != null) { right = expression; } else { if ((expression = PromoteExpression(left, right.Type, exact: true)) == null) { throw IncompatibleOperandsError(token.text, left, right, token.pos); } left = expression; } } } else { CheckAndPromoteOperands(flag ? typeof(IEqualitySignatures) : typeof(IRelationalSignatures), token.text, ref left, ref right, token.pos); } switch (token.id) { case TokenId.Equal: case TokenId.DoubleEqual: left = GenerateEqual(left, right); break; case TokenId.ExclamationEqual: case TokenId.LessGreater: left = GenerateNotEqual(left, right); break; case TokenId.GreaterThan: left = GenerateGreaterThan(left, right); break; case TokenId.GreaterThanEqual: left = GenerateGreaterThanEqual(left, right); break; case TokenId.LessThan: left = GenerateLessThan(left, right); break; case TokenId.LessThanEqual: left = GenerateLessThanEqual(left, right); break; } } return left; } private Expression ParseAdditive() { Expression left = ParseMultiplicative(); while (this.token.id == TokenId.Plus || this.token.id == TokenId.Minus || this.token.id == TokenId.Amphersand) { Token token = this.token; NextToken(); Expression right = ParseMultiplicative(); TokenId id = token.id; if (id != TokenId.Amphersand) { if (id != TokenId.Plus) { if (id == TokenId.Minus) { CheckAndPromoteOperands(typeof(ISubtractSignatures), token.text, ref left, ref right, token.pos); left = GenerateSubtract(left, right); } continue; } if ((object)left.Type != typeof(string) && (object)right.Type != typeof(string)) { CheckAndPromoteOperands(typeof(IAddSignatures), token.text, ref left, ref right, token.pos); left = GenerateAdd(left, right); continue; } } left = GenerateStringConcat(left, right); } return left; } private Expression ParseMultiplicative() { Expression left = ParseUnary(); while (this.token.id == TokenId.Asterisk || this.token.id == TokenId.Slash || this.token.id == TokenId.Percent || TokenIdentifierIs("mod")) { Token token = this.token; NextToken(); Expression right = ParseUnary(); CheckAndPromoteOperands(typeof(IArithmeticSignatures), token.text, ref left, ref right, token.pos); switch (token.id) { case TokenId.Asterisk: left = Expression.Multiply(left, right); break; case TokenId.Slash: left = Expression.Divide(left, right); break; case TokenId.Identifier: case TokenId.Percent: left = Expression.Modulo(left, right); break; } } return left; } private Expression ParseUnary() { if (this.token.id == TokenId.Minus || this.token.id == TokenId.Exclamation || TokenIdentifierIs("not")) { Token token = this.token; NextToken(); if (token.id == TokenId.Minus && (this.token.id == TokenId.IntegerLiteral || this.token.id == TokenId.RealLiteral)) { this.token.text = "-" + this.token.text; this.token.pos = token.pos; return ParsePrimary(); } Expression expr = ParseUnary(); if (token.id == TokenId.Minus) { CheckAndPromoteOperand(typeof(INegationSignatures), token.text, ref expr, token.pos); return Expression.Negate(expr); } CheckAndPromoteOperand(typeof(INotSignatures), token.text, ref expr, token.pos); return Expression.Not(expr); } return ParsePrimary(); } private Expression ParsePrimary() { Expression expression = ParsePrimaryStart(); while (true) { if (token.id == TokenId.Dot) { NextToken(); expression = ParseMemberAccess(null, expression); continue; } if (token.id != TokenId.OpenBracket) { break; } expression = ParseElementAccess(expression); } return expression; } private Expression ParsePrimaryStart() { return token.id switch { TokenId.Identifier => ParseIdentifier(), TokenId.StringLiteral => ParseStringLiteral(), TokenId.IntegerLiteral => ParseIntegerLiteral(), TokenId.RealLiteral => ParseRealLiteral(), TokenId.OpenParen => ParseParenExpression(), _ => throw ParseError("Expression expected"), }; } private Expression ParseStringLiteral() { ValidateToken(TokenId.StringLiteral); char c = token.text[0]; string text = token.text.Substring(1, token.text.Length - 2); int startIndex = 0; while (true) { int num = text.IndexOf(c, startIndex); if (num < 0) { break; } text = text.Remove(num, 1); startIndex = num + 1; } if (c == '\'') { if (text.Length != 1) { throw ParseError("Character literal must contain exactly one character"); } NextToken(); return CreateLiteral(text[0], text); } NextToken(); return CreateLiteral(text, text); } private Expression ParseIntegerLiteral() { ValidateToken(TokenId.IntegerLiteral); string text = token.text; if (text[0] != '-') { if (!ulong.TryParse(text, out var result)) { throw ParseError("Invalid integer literal '{0}'", text); } NextToken(); if (result <= int.MaxValue) { return CreateLiteral((int)result, text); } if (result <= uint.MaxValue) { return CreateLiteral((uint)result, text); } if (result <= long.MaxValue) { return CreateLiteral((long)result, text); } return CreateLiteral(result, text); } if (!long.TryParse(text, out var result2)) { throw ParseError("Invalid integer literal '{0}'", text); } NextToken(); if (result2 >= int.MinValue && result2 <= int.MaxValue) { return CreateLiteral((int)result2, text); } return CreateLiteral(result2, text); } private Expression ParseRealLiteral() { ValidateToken(TokenId.RealLiteral); string text = token.text; object obj = null; char c = text[text.Length - 1]; double result2; if (c == 'F' || c == 'f') { if (float.TryParse(text.Substring(0, text.Length - 1), out var result)) { obj = result; } } else if (double.TryParse(text, out result2)) { obj = result2; } if (obj == null) { throw ParseError("Invalid real literal '{0}'", text); } NextToken(); return CreateLiteral(obj, text); } private Expression CreateLiteral(object value, string text) { ConstantExpression constantExpression = Expression.Constant(value); literals.Add(constantExpression, text); return constantExpression; } private Expression ParseParenExpression() { ValidateToken(TokenId.OpenParen, "'(' expected"); NextToken(); Expression result = ParseExpression(); ValidateToken(TokenId.CloseParen, "')' or operator expected"); NextToken(); return result; } private Expression ParseIdentifier() { ValidateToken(TokenId.Identifier); if (keywords.TryGetValue(token.text, out var value)) { if (value is Type) { return ParseTypeAccess((Type)value); } if (value == keywordIt) { return ParseIt(); } if (value == keywordIif) { return ParseIif(); } if (value == keywordNew) { return ParseNew(); } NextToken(); return (Expression)value; } if (symbols.TryGetValue(token.text, out value) || (externals != null && externals.TryGetValue(token.text, out value))) { Expression expression = value as Expression; if (expression == null) { expression = Expression.Constant(value); } else if (expression is LambdaExpression lambda) { return ParseLambdaInvocation(lambda); } NextToken(); return expression; } if (it != null) { return ParseMemberAccess(null, it); } throw ParseError("Unknown identifier '{0}'", token.text); } private Expression ParseIt() { if (it == null) { throw ParseError("No 'it' is in scope"); } NextToken(); return it; } private Expression ParseIif() { int pos = token.pos; NextToken(); Expression[] array = ParseArgumentList(); if (array.Length != 3) { throw ParseError(pos, "The 'iif' function requires three arguments"); } return GenerateConditional(array[0], array[1], array[2], pos); } private Expression GenerateConditional(Expression test, Expression expr1, Expression expr2, int errorPos) { if ((object)test.Type != typeof(bool)) { throw ParseError(errorPos, "The first expression must be of type 'Boolean'"); } if ((object)expr1.Type != expr2.Type) { Expression expression = ((expr2 != nullLiteral) ? PromoteExpression(expr1, expr2.Type, exact: true) : null); Expression expression2 = ((expr1 != nullLiteral) ? PromoteExpression(expr2, expr1.Type, exact: true) : null); if (expression != null && expression2 == null) { expr1 = expression; } else { if (expression2 == null || expression != null) { string text = ((expr1 != nullLiteral) ? expr1.Type.Name : "null"); string text2 = ((expr2 != nullLiteral) ? expr2.Type.Name : "null"); if (expression != null && expression2 != null) { throw ParseError(errorPos, "Both of the types '{0}' and '{1}' convert to the other", text, text2); } throw ParseError(errorPos, "Neither of the types '{0}' and '{1}' converts to the other", text, text2); } expr2 = expression2; } } return Expression.Condition(test, expr1, expr2); } private Expression ParseNew() { NextToken(); ValidateToken(TokenId.OpenParen, "'(' expected"); NextToken(); List<DynamicProperty> list = new List<DynamicProperty>(); List<Expression> list2 = new List<Expression>(); while (true) { int pos = token.pos; Expression expression = ParseExpression(); string name; if (TokenIdentifierIs("as")) { NextToken(); name = GetIdentifier(); NextToken(); } else { if (!(expression is MemberExpression memberExpression)) { throw ParseError(pos, "Expression is missing an 'as' clause"); } name = memberExpression.Member.Name; } list2.Add(expression); list.Add(new DynamicProperty(name, expression.Type)); if (token.id != TokenId.Comma) { break; } NextToken(); } ValidateToken(TokenId.CloseParen, "')' or ',' expected"); NextToken(); Type type = DynamicExpression.CreateClass(list); MemberBinding[] array = new MemberBinding[list.Count]; for (int i = 0; i < array.Length; i++) { array[i] = Expression.Bind(type.GetProperty(list[i].Name), list2[i]); } return Expression.MemberInit(Expression.New(type), array); } private Expression ParseLambdaInvocation(LambdaExpression lambda) { int pos = token.pos; NextToken(); Expression[] array = ParseArgumentList(); if (FindMethod(lambda.Type, "Invoke", staticAccess: false, array, out var _) != 1) { throw ParseError(pos, "Argument list incompatible with lambda expression"); } return Expression.Invoke(lambda, array); } private Expression ParseTypeAccess(Type type) { int pos = token.pos; NextToken(); if (token.id == TokenId.Question) { if (!type.IsValueType || IsNullableType(type)) { throw ParseError(pos, "Type '{0}' has no nullable form", GetTypeName(type)); } type = typeof(Nullable<>).MakeGenericType(type); NextToken(); } if (token.id == TokenId.OpenParen) { Expression[] array = ParseArgumentList(); MethodBase method; switch (FindBestMethod(type.GetConstructors(), array, out method)) { case 0: if (array.Length == 1) { return GenerateConversion(array[0], type, pos); } throw ParseError(pos, "No matching constructor in type '{0}'", GetTypeName(type)); case 1: return Expression.New((ConstructorInfo)method, array); default: throw ParseError(pos, "Ambiguous invocation of '{0}' constructor", GetTypeName(type)); } } ValidateToken(TokenId.Dot, "'.' or '(' expected"); NextToken(); return ParseMemberAccess(type, null); } private Expression GenerateConversion(Expression expr, Type type, int errorPos) { Type type2 = expr.Type; if ((object)type2 == type) { return expr; } if (type2.IsValueType && type.IsValueType) { if ((IsNullableType(type2) || IsNullableType(type)) && (object)GetNonNullableType(type2) == GetNonNullableType(type)) { return Expression.Convert(expr, type); } if (((IsNumericType(type2) || IsEnumType(type2)) && IsNumericType(type)) || IsEnumType(type)) { return Expression.ConvertChecked(expr, type); } } if (type2.IsAssignableFrom(type) || type.IsAssignableFrom(type2) || type2.IsInterface || type.IsInterface) { return Expression.Convert(expr, type); } throw ParseError(errorPos, "A value of type '{0}' cannot be converted to type '{1}'", GetTypeName(type2), GetTypeName(type)); } private Expression ParseMemberAccess(Type type, Expression instance) { if (instance != null) { type = instance.Type; } int pos = token.pos; string identifier = GetIdentifier(); NextToken(); if (token.id == TokenId.OpenParen) { if (instance != null && (object)type != typeof(string)) { Type type2 = FindGenericType(typeof(IEnumerable<>), type); if ((object)type2 != null) { Type elementType = type2.GetGenericArguments()[0]; return ParseAggregate(instance, elementType, identifier, pos); } } Expression[] array = ParseArgumentList(); MethodBase method; switch (FindMethod(type, identifier, instance == null, array, out method)) { case 0: throw ParseError(pos, "No applicable method '{0}' exists in type '{1}'", identifier, GetTypeName(type)); case 1: { MethodInfo methodInfo = (MethodInfo)method; if (!IsPredefinedType(methodInfo.DeclaringType)) { throw ParseError(pos, "Methods on type '{0}' are not accessible", GetTypeName(methodInfo.DeclaringType)); } if ((object)methodInfo.ReturnType == typeof(void)) { throw ParseError(pos, "Method '{0}' in type '{1}' does not return a value", identifier, GetTypeName(methodInfo.DeclaringType)); } return Expression.Call(instance, methodInfo, array); } default: throw ParseError(pos, "Ambiguous invocation of method '{0}' in type '{1}'", identifier, GetTypeName(type)); } } MemberInfo memberInfo = FindPropertyOrField(type, identifier, instance == null); if ((object)memberInfo == null) { throw ParseError(pos, "No property or field '{0}' exists in type '{1}'", identifier, GetTypeName(type)); } if (!(memberInfo is PropertyInfo)) { return Expression.Field(instance, (FieldInfo)memberInfo); } return Expression.Property(instance, (PropertyInfo)memberInfo); } private static Type FindGenericType(Type generic, Type type) { while ((object)type != null && (object)type != typeof(object)) { if (type.IsGenericType && (object)type.GetGenericTypeDefinition() == generic) { return type; } if (generic.IsInterface) { Type[] interfaces = type.GetInterfaces(); foreach (Type type2 in interfaces) { Type type3 = FindGenericType(generic, type2); if ((object)type3 != null) { return type3; } } } type = type.BaseType; } return null; } private Expression ParseAggregate(Expression instance, Type elementType, string methodName, int errorPos) { ParameterExpression parameterExpression = it; ParameterExpression parameterExpression2 = (it = Expression.Parameter(elementType, "")); Expression[] array = ParseArgumentList(); it = parameterExpression; if (FindMethod(typeof(IEnumerableSignatures), methodName, staticAccess: false, array, out var method) != 1) { throw ParseError(errorPos, "No applicable aggregate method '{0}' exists", methodName); } return Expression.Call(typeArguments: (!(method.Name == "Min") && !(method.Name == "Max")) ? new Type[1] { elementType } : new Type[2] { elementType, array[0].Type }, arguments: (array.Length != 0) ? new Expression[2] { instance, Expression.Lambda(array[0], parameterExpression2) } : new Expression[1] { instance }, type: typeof(Enumerable), methodName: method.Name); } private Expression[] ParseArgumentList() { ValidateToken(TokenId.OpenParen, "'(' expected"); NextToken(); Expression[] result = ((token.id != TokenId.CloseParen) ? ParseArguments() : new Expression[0]); ValidateToken(TokenId.CloseParen, "')' or ',' expected"); NextToken(); return result; } private Expression[] ParseArguments() { List<Expression> list = new List<Expression>(); while (true) { list.Add(ParseExpression()); if (token.id != TokenId.Comma) { break; } NextToken(); } return list.ToArray(); } private Expression ParseElementAccess(Expression expr) { int pos = token.pos; ValidateToken(TokenId.OpenBracket, "'(' expected"); NextToken(); Expression[] array = ParseArguments(); ValidateToken(TokenId.CloseBracket, "']' or ',' expected"); NextToken(); if (expr.Type.IsArray) { if (expr.Type.GetArrayRank() != 1 || array.Length != 1) { throw ParseError(pos, "Indexing of multi-dimensional arrays is not supported"); } Expression expression = PromoteExpression(array[0], typeof(int), exact: true); if (expression == null) { throw ParseError(pos, "Array index must be an integer expression"); } return Expression.ArrayIndex(expr, expression); } MethodBase method; return FindIndexer(expr.Type, array, out method) switch { 0 => throw ParseError(pos, "No applicable indexer exists in type '{0}'", GetTypeName(expr.Type)), 1 => Expression.Call(expr, (MethodInfo)method, array), _ => throw ParseError(pos, "Ambiguous invocation of indexer in type '{0}'", GetTypeName(expr.Type)), }; } private static bool IsPredefinedType(Type type) { Type[] array = predefinedTypes; for (int i = 0; i < array.Length; i++) { if ((object)array[i] == type) { return true; } } return false; } private static bool IsNullableType(Type type) { if (type.IsGenericType) { return (object)type.GetGenericTypeDefinition() == typeof(Nullable<>); } return false; } private static Type GetNonNullableType(Type type) { if (!IsNullableType(type)) { return type; } return type.GetGenericArguments()[0]; } private static string GetTypeName(Type type) { Type nonNullableType = GetNonNullableType(type); string text = nonNullableType.Name; if ((object)type != nonNullableType) { text += "?"; } return text; } private static bool IsNumericType(Type type) { return GetNumericTypeKind(type) != 0; } private static bool IsSignedIntegralType(Type type) { return GetNumericTypeKind(type) == 2; } private static bool IsUnsignedIntegralType(Type type) { return GetNumericTypeKind(type) == 3; } private static int GetNumericTypeKind(Type type) { type = GetNonNullableType(type); if (type.IsEnum) { return 0; } switch (Type.GetTypeCode(type)) { case TypeCode.Char: case TypeCode.Single: case TypeCode.Double: case TypeCode.Decimal: return 1; case TypeCode.SByte: case TypeCode.Int16: case TypeCode.Int32: case TypeCode.Int64: return 2; case TypeCode.Byte: case TypeCode.UInt16: case TypeCode.UInt32: case TypeCode.UInt64: return 3; default: return 0; } } private static bool IsEnumType(Type type) { return GetNonNullableType(type).IsEnum; } private void CheckAndPromoteOperand(Type signatures, string opName, ref Expression expr, int errorPos) { Expression[] array = new Expression[1] { expr }; if (FindMethod(signatures, "F", staticAccess: false, array, out var _) != 1) { throw ParseError(errorPos, "Operator '{0}' incompatible with operand type '{1}'", opName, GetTypeName(array[0].Type)); } expr = array[0]; } private void CheckAndPromoteOperands(Type signatures, string opName, ref Expression left, ref Expression right, int errorPos) { Expression[] array = new Expression[2] { left, right }; if (FindMethod(signatures, "F", staticAccess: false, array, out var _) != 1) { throw IncompatibleOperandsError(opName, left, right, errorPos); } left = array[0]; right = array[1]; } private Exception IncompatibleOperandsError(string opName, Expression left, Expression right, int pos) { return ParseError(pos, "Operator '{0}' incompatible with operand types '{1}' and '{2}'", opName, GetTypeName(left.Type), GetTypeName(right.Type)); } private MemberInfo FindPropertyOrField(Type type, string memberName, bool staticAccess) { BindingFlags bindingAttr = BindingFlags.DeclaredOnly | BindingFlags.Public | (staticAccess ? BindingFlags.Static : BindingFlags.Instance); foreach (Type item in SelfAndBaseTypes(type)) { MemberInfo[] array = item.FindMembers(MemberTypes.Field | MemberTypes.Property, bindingAttr, Type.FilterNameIgnoreCase, memberName); if (array.Length != 0) { return array[0]; } } return null; } private int FindMethod(Type type, string methodName, bool staticAccess, Expression[] args, out MethodBase method) { BindingFlags bindingAttr = BindingFlags.DeclaredOnly | BindingFlags.Public | (staticAccess ? BindingFlags.Static : BindingFlags.Instance); foreach (Type item in SelfAndBaseTypes(type)) { MemberInfo[] source = item.FindMembers(MemberTypes.Method, bindingAttr, Type.FilterNameIgnoreCase, methodName); int num = FindBestMethod(source.Cast<MethodBase>(), args, out method); if (num != 0) { return num; } } method = null; return 0; } private int FindIndexer(Type type, Expression[] args, out MethodBase method) { foreach (Type item in SelfAndBaseTypes(type)) { MemberInfo[] defaultMembers = item.GetDefaultMembers(); if (defaultMembers.Length != 0) { IEnumerable<MethodBase> methods = from m in defaultMembers.OfType<PropertyInfo>().Select((Func<PropertyInfo, MethodBase>)((PropertyInfo p) => p.GetGetMethod())) where (object)m != null select m; int num = FindBestMethod(methods, args, out method); if (num != 0) { return num; } } } method = null; return 0; } private static IEnumerable<Type> SelfAndBaseTypes(Type type) { if (type.IsInterface) { List<Type> list = new List<Type>(); AddInterface(list, type); return list; } return SelfAndBaseClasses(type); } private static IEnumerable<Type> SelfAndBaseClasses(Type type) { while ((object)type != null) { yield return type; type = type.BaseType; } } private static void AddInterface(List<Type> types, Type type) { if (!types.Contains(type)) { types.Add(type); Type[] interfaces = type.GetInterfaces(); foreach (Type type2 in interfaces) { AddInterface(types, type2); } } } private int FindBestMethod(IEnumerable<MethodBase> methods, Expression[] args, out MethodBase method) { MethodData[] applicable = (from m in methods select new MethodData { MethodBase = m, Parameters = m.GetParameters() } into m where IsApplicable(m, args) select m).ToArray(); if (applicable.Length > 1) { applicable = applicable.Where((MethodData m) => applicable.All((MethodData n) => m == n || IsBetterThan(args, m, n))).ToArray(); } if (applicable.Length == 1) { MethodData methodData = applicable[0]; for (int i = 0; i < args.Length; i++) { args[i] = methodData.Args[i]; } method = methodData.MethodBase; } else { method = null; } return applicable.Length; } private bool IsApplicable(MethodData method, Expression[] args) { if (method.Parameters.Length != args.Length) { return false; } Expression[] array = new Expression[args.Length]; for (int i = 0; i < args.Length; i++) { ParameterInfo parameterInfo = method.Parameters[i]; if (parameterInfo.IsOut) { return false; } Expression expression = PromoteExpression(args[i], parameterInfo.ParameterType, exact: false); if (expression == null) { return false; } array[i] = expression; } method.Args = array; return true; } private Expression PromoteExpression(Expression expr, Type type, bool exact) { if ((object)expr.Type == type) { return expr; } if (expr is ConstantExpression) { ConstantExpression constantExpression = (ConstantExpression)expr; string value; if (constantExpression == nullLiteral) { if (!type.IsValueType || IsNullableType(type)) { return Expression.Constant(null, type); } } else if (literals.TryGetValue(constantExpression, out value)) { Type nonNullableType = GetNonNullableType(type); object obj = null; switch (Type.GetTypeCode(constantExpression.Type)) { case TypeCode.Int32: case TypeCode.UInt32: case TypeCode.Int64: case TypeCode.UInt64: obj = ParseNumber(value, nonNullableType); break; case TypeCode.Double: if ((object)nonNullableType == typeof(decimal)) { obj = ParseNumber(value, nonNullableType); } break; case TypeCode.String: obj = ParseEnum(value, nonNullableType); break; } if (obj != null) { return Expression.Constant(obj, type); } } } if (IsCompatibleWith(expr.Type, type)) { if (type.IsValueType || exact) { return Expression.Convert(expr, type); } return expr; } return null; } private static object ParseNumber(string text, Type type) { switch (Type.GetTypeCode(GetNonNullableType(type))) { case TypeCode.SByte: { if (sbyte.TryParse(text, out var result6)) { return result6; } break; } case TypeCode.Byte: { if (byte.TryParse(text, out var result10)) { return result10; } break; } case TypeCode.Int16: { if (short.TryParse(text, out var result2)) { return result2; } break; } case TypeCode.UInt16: { if (ushort.TryParse(text, out var result8)) { return result8; } break; } case TypeCode.Int32: { if (int.TryParse(text, out var result4)) { return result4; } break; } case TypeCode.UInt32: { if (uint.TryParse(text, out var result11)) { return result11; } break; } case TypeCode.Int64: { if (long.TryParse(text, out var result9)) { return result9; } break; } case TypeCode.UInt64: { if (ulong.TryParse(text, out var result7)) { return result7; } break; } case TypeCode.Single: { if (float.TryParse(text, out var result5)) { return result5; } break; } case TypeCode.Double: { if (double.TryParse(text, out var result3)) { return result3; } break; } case TypeCode.Decimal: { if (decimal.TryParse(text, out var result)) { return result; } break; } } return null; } private static object ParseEnum(string name, Type type) { if (type.IsEnum) { MemberInfo[] array = type.FindMembers(MemberTypes.Field, BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public, Type.FilterNameIgnoreCase, name); if (array.Length != 0) { return ((FieldInfo)array[0]).GetValue(null); } } return null; } private static bool IsCompatibleWith(Type source, Type target) { if ((object)source == target) { return true; } if (!target.IsValueType) { return target.IsAssignableFrom(source); } Type nonNullableType = GetNonNullableType(source); Type nonNullableType2 = GetNonNullableType(target); if ((object)nonNullableType != source && (object)nonNullableType2 == target) { return false; } TypeCode typeCode = (nonNullableType.IsEnum ? TypeCode.Object : Type.GetTypeCode(nonNullableType)); TypeCode typeCode2 = (nonNullableType2.IsEnum ? TypeCode.Object : Type.GetTypeCode(nonNullableType2)); switch (typeCode) { case TypeCode.SByte: switch (typeCode2) { case TypeCode.SByte: case TypeCode.Int16: case TypeCode.Int32: case TypeCode.Int64: case TypeCode.Single: case TypeCode.Double: case TypeCode.Decimal: return true; } break; case TypeCode.Byte: if ((uint)(typeCode2 - 6) <= 9u) { return true; } break; case TypeCode.Int16: switch (typeCode2) { case TypeCode.Int16: case TypeCode.Int32: case TypeCode.Int64: case TypeCode.Single: case TypeCode.Double: case TypeCode.Decimal: return true; } break; case TypeCode.UInt16: if ((uint)(typeCode2 - 8) <= 7u) { return true; } break; case TypeCode.Int32: switch (typeCode2) { case TypeCode.Int32: case TypeCode.Int64: case TypeCode.Single: case TypeCode.Double: case TypeCode.Decimal: return true; } break; case TypeCode.UInt32: if ((uint)(typeCode2 - 10) <= 5u) { return true; } break; case TypeCode.Int64: if (typeCode2 == TypeCode.Int64 || (uint)(typeCode2 - 13) <= 2u) { return true; } break; case TypeCode.UInt64: if ((uint)(typeCode2 - 12) <= 3u) { return true; } break; case TypeCode.Single: if ((uint)(typeCode2 - 13) <= 1u) { return true; } break; default: if ((object)nonNullableType == nonNullableType2) { return true; } break; } return false; } private static bool IsBetterThan(Expression[] args, MethodData m1, MethodData m2) { bool result = false; for (int i = 0; i < args.Length; i++) { int num = CompareConversions(args[i].Type, m1.Parameters[i].ParameterType, m2.Parameters[i].ParameterType); if (num < 0) { return false; } if (num > 0) { result = true; } } return result; } private static int CompareConversions(Type s, Type t1, Type t2) { if ((object)t1 == t2) { return 0; } if ((object)s == t1) { return 1; } if ((object)s == t2) { return -1; } bool flag = IsCompatibleWith(t1, t2); bool flag2 = IsCompatibleWith(t2, t1); if (flag && !flag2) { return 1; } if (flag2 && !flag) { return -1; } if (IsSignedIntegralType(t1) && IsUnsignedIntegralType(t2)) { return 1; } if (IsSignedIntegralType(t2) && IsUnsignedIntegralType(t1)) { return -1; } return 0; } private Expression GenerateEqual(Expression left, Expression right) { return Expression.Equal(left, right); } private Expression GenerateNotEqual(Expression left, Expression right) { return Expression.NotEqual(left, right); } private Expression GenerateGreaterThan(Expression left, Expression right) { if ((object)left.Type == typeof(string)) { return Expression.GreaterThan(GenerateStaticMethodCall("Compare", left, right), Expression.Constant(0)); } return Expression.GreaterThan(left, right); } private Expression GenerateGreaterThanEqual(Expression left, Expression right) { if ((object)left.Type == typeof(string)) { return Expression.GreaterThanOrEqual(GenerateStaticMethodCall("Compare", left, right), Expression.Constant(0)); } return Expression.GreaterThanOrEqual(left, right); } private Expression GenerateLessThan(Expression left, Expression right) { if ((object)left.Type == typeof(string)) { return Expression.LessThan(GenerateStaticMethodCall("Compare", left, right), Expression.Constant(0)); } return Expression.LessThan(left, right); } private Expression GenerateLessThanEqual(Expression left, Expression right) { if ((object)left.Type == typeof(string)) { return Expression.LessThanOrEqual(GenerateStaticMethodCall("Compare", left, right), Expression.Constant(0)); } return Expression.LessThanOrEqual(left, right); } private Expression GenerateAdd(Expression left, Expression right) { if ((object)left.Type == typeof(string) && (object)right.Type == typeof(string)) { return GenerateStaticMethodCall("Concat", left, right); } return Expression.Add(left, right); } private Expression GenerateSubtract(Expression left, Expression right) { return Expression.Subtract(left, right); } private Expression GenerateStringConcat(Expression left, Expression right) { return Expression.Call(null, typeof(string).GetMethod("Concat", new Type[2] { typeof(object), typeof(object) }), new Expression[2] { left, right }); } private MethodInfo GetStaticMethod(string methodName, Expression left, Expression right) { return left.Type.GetMethod(methodName, new Type[2] { left.Type, right.Type }); } private Expression GenerateStaticMethodCall(string methodName, Expression left, Expression right) { return Expression.Call(null, GetStaticMethod(methodName, left, right), new Expression[2] { left, right }); } private void SetTextPos(int pos) { textPos = pos; ch = ((textPos < textLen) ? text[textPos] : '\0'); } private void NextChar() { if (textPos < textLen) { textPos++; } ch = ((textPos < textLen) ? text[textPos] : '\0'); } private void NextToken() { while (char.IsWhiteSpace(ch)) { NextChar(); } int num = textPos; TokenId id; switch (ch) { case '!': NextChar(); if (ch == '=') { NextChar(); id = TokenId.ExclamationEqual; } else { id = TokenId.Exclamation; } break; case '%': NextChar(); id = TokenId.Percent; break; case '&': NextChar(); if (ch == '&') { NextChar(); id = TokenId.DoubleAmphersand; } else { id = TokenId.Amphersand; } break; case '(': NextChar(); id = TokenId.OpenParen; break; case ')': NextChar(); id = TokenId.CloseParen; break; case '*': NextChar(); id = TokenId.Asterisk; break; case '+': NextChar(); id = TokenId.Plus; break; case ',': NextChar(); id = TokenId.Comma; break; case '-': NextChar(); id = TokenId.Minus; break; case '.': NextChar(); id = TokenId.Dot; break; case '/': NextChar(); id = TokenId.Slash; break; case ':': NextChar(); id = TokenId.Colon; break; case '<': NextChar(); if (ch == '=') { NextChar(); id = TokenId.LessThanEqual; } else if (ch == '>') { NextChar(); id = TokenId.LessGreater; } else { id = TokenId.LessThan; } break; case '=': NextChar(); if (ch == '=') { NextChar(); id = TokenId.DoubleEqual; } else { id = TokenId.Equal; } break; case '>': NextChar(); if (ch == '=') { NextChar(); id = TokenId.GreaterThanEqual; } else { id = TokenId.GreaterThan; } break; case '?': NextChar(); id = TokenId.Question; break; case '[': NextChar(); id = TokenId.OpenBracket; break; case ']': NextChar(); id = TokenId.CloseBracket; break; case '|': NextChar(); if (ch == '|') { NextChar(); id = TokenId.DoubleBar; } else { id = TokenId.Bar; } break; case '"': case '\'': { char c = ch; do { NextChar(); while (textPos < textLen && ch != c) { NextChar(); } if (textPos == textLen) { throw ParseError(textPos, "Unterminated string literal"); } NextChar(); } while (ch == c); id = TokenId.StringLiteral; break; } default: if (char.IsLetter(ch) || ch == '@' || ch == '_') { do { NextChar(); } while (char.IsLetterOrDigit(ch) || ch == '_'); id = TokenId.Identifier; } else if (char.IsDigit(ch)) { id = TokenId.IntegerLiteral; do { NextChar(); } while (char.IsDigit(ch)); if (ch == '.') { id = TokenId.RealLiteral; NextChar(); ValidateDigit(); do { NextChar(); } while (char.IsDigit(ch)); } if (ch == 'E' || ch == 'e') { id = TokenId.RealLiteral; NextChar(); if (ch == '+' || ch == '-') { NextChar(); } ValidateDigit(); do { NextChar(); } while (char.IsDigit(ch)); } if (ch == 'F' || ch == 'f') { NextChar(); } } else { if (textPos != textLen) { throw ParseError(textPos, "Syntax error '{0}'", ch); } id = TokenId.End; } break; } token.id = id; token.text = text.Substring(num, textPos - num); token.pos = num; } private bool TokenIdentifierIs(string id) { if (token.id == TokenId.Identifier) { return string.Equals(id, token.text, StringComparison.OrdinalIgnoreCase); } return false; } private string GetIdentifier() { ValidateToken(TokenId.Identifier, "Identifier expected"); string text = token.text; if (text.Length > 1 && text[0] == '@') { text = text.Substring(1); } return text; } private void ValidateDigit() { if (!char.IsDigit(ch)) { throw ParseError(textPos, "Digit expected"); } } private void ValidateToken(TokenId t, string errorMessage) { if (token.id != t) { throw ParseError(errorMessage); } } private void ValidateToken(TokenId t) { if (token.id != t) { throw ParseError("Syntax error"); } } private Exception ParseError(string format, params object[] args) { return ParseError(token.pos, format, args); } private Exception ParseError(int pos, string format, params object[] args) { return new ParseException(string.Format(CultureInfo.CurrentCulture, format, args), pos); } private static Dictionary<string, object> CreateKeywords() { Dictionary<string, object> dictionary = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); dictionary.Add("true", trueLiteral); dictionary.Add("false", falseLiteral); dictionary.Add("null", nullLiteral); dictionary.Add(keywordIt, keywordIt); dictionary.Add(keywordIif, keywordIif); dictionary.Add(keywordNew, keywordNew); Type[] array = predefinedTypes; foreach (Type type in array) { dictionary.Add(type.Name, type); } return dictionary; } } internal static class Res { public const string DuplicateIdentifier = "The identifier '{0}' was defined more than once"; public const string ExpressionTypeMismatch = "Expression of type '{0}' expected"; public const string ExpressionExpected = "Expression expected"; public const string InvalidCharacterLiteral = "Character literal must contain exactly one character"; public const string InvalidIntegerLiteral = "Invalid integer literal '{0}'"; public const string InvalidRealLiteral = "Invalid real literal '{0}'"; public const string UnknownIdentifier = "Unknown identifier '{0}'"; public const string NoItInScope = "No 'it' is in scope"; public const string IifRequiresThreeArgs = "The 'iif' function requires three arguments"; public const string FirstExprMustBeBool = "The first expression must be of type 'Boolean'"; public const string BothTypesConvertToOther = "Both of the types '{0}' and '{1}' convert to the other"; public const string NeitherTypeConvertsToOther = "Neither of the types '{0}' and '{1}' converts to the other"; public const string MissingAsClause = "Expression is missing an 'as' clause"; public const string ArgsIncompatibleWithLambda = "Argument list incompatible with lambda expression"; public const string TypeHasNoNullableForm = "Type '{0}' has no nullable form"; public const string NoMatchingConstructor = "No matching constructor in type '{0}'"; public const string AmbiguousConstructorInvocation = "Ambiguous invocation of '{0}' constructor"; public const string CannotConvertValue = "A value of type '{0}' cannot be converted to type '{1}'"; public const string NoApplicableMethod = "No applicable method '{0}' exists in type '{1}'"; public const string MethodsAreInaccessible = "Methods on type '{0}' are not accessible"; public const string MethodIsVoid = "Method '{0}' in type '{1}' does not return a value"; public const string AmbiguousMethodInvocation = "Ambiguous invocation of method '{0}' in type '{1}'"; public const string UnknownPropertyOrField = "No property or field '{0}' exists in type '{1}'"; public const string NoApplicableAggregate = "No applicable aggregate method '{0}' exists"; public const string CannotIndexMultiDimArray = "Indexing of multi-dimensional arrays is not supported"; public const string InvalidIndex = "Array index must be an integer expression"; public const string NoApplicableIndexer = "No applicable indexer exists in type '{0}'"; public const string AmbiguousIndexerInvocation = "Ambiguous invocation of indexer in type '{0}'"; public const string IncompatibleOperand = "Operator '{0}' incompatible with operand type '{1}'"; public const string IncompatibleOperands = "Operator '{0}' incompatible with operand types '{1}' and '{2}'"; public const string UnterminatedStringLiteral = "Unterminated string literal"; public const string InvalidCharacter = "Syntax error '{0}'"; public const string DigitExpected = "Digit expected"; public const string SyntaxError = "Syntax error"; public const string TokenExpected = "{0} expected"; public const string ParseExceptionFormat = "{0} (at index {1})"; public const string ColonExpected = "':' expected"; public const string OpenParenExpected = "'(' expected"; public const string CloseParenOrOperatorExpected = "')' or operator expected"; public const string CloseParenOrCommaExpected = "')' or ',' expected"; public const string DotOrOpenParenExpected = "'.' or '(' expected"; public const string OpenBracketExpected = "'[' expected"; public const string CloseBracketOrCommaExpected = "']' or ',' expected"; public const string IdentifierExpected = "Identifier expected"; } } namespace SimpleJSON { public enum JSONNodeType { Array = 1, Object = 2, String = 3, Number = 4, NullValue = 5, Boolean = 6, None = 7, Custom = 255 } public enum JSONTextMode { Compact, Indent } public abstract class JSONNode { public struct Enumerator { private enum Type { None, Array, Object } private Type type; private Dictionary<string, JSONNode>.Enumerator m_Object; private List<JSONNode>.Enumerator m_Array; public bool IsValid => type != Type.None; public KeyValuePair<string, JSONNode> Current { get { if (type == Type.Array) { return new KeyValuePair<string, JSONNode>(string.Empty, m_Array.Current); } if (type == Type.Object) { return m_Object.Current; } return new KeyValuePair<string, JSONNode>(string.Empty, null); } } public Enumerator(List<JSONNode>.Enumerator aArrayEnum) { type = Type.Array; m_Object = default(Dictionary<string, JSONNode>.Enumerator); m_Array = aArrayEnum; } public Enumerator(Dictionary<string, JSONNode>.Enumerator aDictEnum) { type = Type.Object; m_Object = aDictEnum; m_Array = default(List<JSONNode>.Enumerator); } public bool MoveNext() { if (type == Type.Array) { return m_Array.MoveNext(); } if (type == Type.Object) { return m_Object.MoveNext(); } return false; } } public struct ValueEnumerator { private Enumerator m_Enumerator; public JSONNode Current => m_Enumerator.Current.Value; public ValueEnumerator(List<JSONNode>.Enumerator aArrayEnum) : this(new Enumerator(aArrayEnum)) { } public ValueEnumerator(Dictionary<string, JSONNode>.Enumerator aDictEnum) : this(new Enumerator(aDictEnum)) { } public ValueEnumerator(Enumerator aEnumerator) { m_Enumerator = aEnumerator; } public bool MoveNext() { return m_Enumerator.MoveNext(); } public ValueEnumerator GetEnumerator() { return this; } } public struct KeyEnumerator { private Enumerator m_Enumerator; public JSONNode Current => m_Enumerator.Current.Key; public KeyEnumerator(List<JSONNode>.Enumerator aArrayEnum) : this(new Enumerator(aArrayEnum)) { } public KeyEnumerator(Dictionary<string, JSONNode>.Enumerator aDictEnum) : this(new Enumerator(aDictEnum)) { } public KeyEnumerator(Enumerator aEnumerator) { m_Enumerator = aEnumerator; } public bool MoveNext() { return m_Enumerator.MoveNext(); } public KeyEnumerator GetEnumerator() { return this; } } public class LinqEnumerator : IEnumerator<KeyValuePair<string, JSONNode>>, IDisposable, IEnumerator, IEnumerable<KeyValuePair<string, JSONNode>>, IEnumerable { private JSONNode m_Node; private Enumerator m_Enumerator; public KeyValuePair<string, JSONNode> Current => m_Enumerator.Current; object IEnumerator.Current => m_Enumerator.Current; internal LinqEnumerator(JSONNode aNode) { m_Node = aNode; if (m_Node != null) { m_Enumerator = m_Node.GetEnumerator(); } } public bool MoveNext() { return m_Enumerator.MoveNext(); } public void Dispose() { m_Node = null; m_Enumerator = default(Enumerator); } public IEnumerator<KeyValuePair<string, JSONNode>> GetEnumerator() { return new LinqEnumerator(m_Node); } public void Reset() { if (m_Node != null) { m_Enumerator = m_Node.GetEnumerator(); } } IEnumerator IEnumerable.GetEnumerator() { return new LinqEnumerator(m_Node); } } public static bool forceASCII; [ThreadStatic] private static StringBuilder m_EscapeBuilder; public abstract JSONNodeType Tag { get; } public virtual JSONNode this[int aIndex] { get { return null; } set { } } public virtual JSONNode this[string aKey] { get { return null; } set { } } public virtual string Value { get { return ""; } set { } } public virtual int Count => 0; public virtual bool IsNumber => false; public virtual bool IsString => false; public virtual bool IsBoolean => false; public virtual bool IsNull => false; public virtual bool IsArray => false; public virtual bool IsObject => false; public virtual bool Inline { get { return false; } set { } } public virtual IEnumerable<JSONNode> Children { get { yield break; } } public IEnumerable<JSONNode> DeepChildren { get { foreach (JSONNode child in Children) { foreach (JSONNode deepChild in child.DeepChildren) { yield return deepChild; } } } } public IEnumerable<KeyValuePair<string, JSONNode>> Linq => new LinqEnumerator(this); public KeyEnumerator Keys => new KeyEnumerator(GetEnumerator()); public ValueEnumerator Values => new ValueEnumerator(GetEnumerator()); public virtual double AsDouble { get { double result = 0.0; if (double.TryParse(Value, out result)) { return result; } return 0.0; } set { Value = value.ToString(); } } public virtual int AsInt { get { return (int)AsDouble; } set { AsDouble = value; } } public virtual float AsFloat { get { return (float)AsDouble; } set { AsDouble = value; } } public virtual bool AsBool { get { bool result = false; if (bool.TryParse(Value, out result)) { return result; } return !string.IsNullOrEmpty(Value); } set { Value = (value ? "true" : "false"); } } public virtual JSONArray AsArray => this as JSONArray; public virtual JSONObject AsObject => this as JSONObject; internal static StringBuilder EscapeBuilder { get { if (m_EscapeBuilder == null) { m_EscapeBuilder = new StringBuilder(); } return m_EscapeBuilder; } } public virtual void Add(string aKey, JSONNode aItem) { } public virtual void Add(JSONNode aItem) { Add("", aItem); } public virtual JSONNode Remove(string aKey) { return null; } public virtual JSONNode Remove(int aIndex) { return null; } public virtual JSONNode Remove(JSONNode aNode) { return aNode; } public override string ToString() { StringBuilder stringBuilder = new StringBuilder(); WriteToStringBuilder(stringBuilder, 0, 0, JSONTextMode.Compact); return stringBuilder.ToString(); } public virtual string ToString(int aIndent) { StringBuilder stringBuilder = new StringBuilder(); WriteToStringBuilder(stringBuilder, 0, aIndent, JSONTextMode.Indent); return stringBuilder.ToString(); } internal abstract void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode); public abstract Enumerator GetEnumerator(); public static implicit operator JSONNode(string s) { return new JSONString(s); } public static implicit operator string(JSONNode d) { if (!(d == null)) { return d.Value; } return null; } public static implicit operator JSONNode(double n) { return new JSONNumber(n); } public static implicit operator double(JSONNode d) { if (!(d == null)) { return d.AsDouble; } return 0.0; } public static implicit operator JSONNode(float n) { return new JSONNumber(n); } public static implicit operator float(JSONNode d) { if (!(d == null)) { return d.AsFloat; } return 0f; } public static implicit operator JSONNode(int n) { return new JSONNumber(n); } public static implicit operator int(JSONNode d) { if (!(d == null)) { return d.AsInt; } return 0; } public static implicit operator JSONNode(bool b) { return new JSONBool(b); } public static implicit operator bool(JSONNode d) { if (!(d == null)) { return d.AsBool; } return false; } public static implicit operator JSONNode(KeyValuePair<string, JSONNode> aKeyValue) { return aKeyValue.Value; } public static bool operator ==(JSONNode a, object b) { if ((object)a == b) { return true; } bool flag = a is JSONNull || (object)a == null || a is JSONLazyCreator; bool flag2 = b is JSONNull || b == null || b is JSONLazyCreator; if (flag && flag2) { return true; } if (!flag) { return a.Equals(b); } return false; } public static bool operator !=(JSONNode a, object b) { return !(a == b); } public override bool Equals(object obj) { return (object)this == obj; } public override int GetHashCode() { return base.GetHashCode(); } internal static string Escape(string aText) { StringBuilder escapeBuilder = EscapeBuilder; escapeBuilder.Length = 0; if (escapeBuilder.Capacity < aText.Length + aText.Length / 10) { escapeBuilder.Capacity = aText.Length + aText.Length / 10; } foreach (char c in aText) { switch (c) { case '\\': escapeBuilder.Append("\\\\"); continue; case '"': escapeBuilder.Append("\\\""); continue; case '\n': escapeBuilder.Append("\\n"); continue; case '\r': escapeBuilder.Append("\\r"); continue; case '\t': escapeBuilder.Append("\\t"); continue; case '\b': escapeBuilder.Append("\\b"); continue; case '\f': escapeBuilder.Append("\\f"); continue; } if (c < ' ' || (forceASCII && c > '\u007f')) { ushort num = c; escapeBuilder.Append("\\u").Append(num.ToString("X4")); } else { escapeBuilder.Append(c); } } string result = escapeBuilder.ToString(); escapeBuilder.Length = 0; return result; } private static void ParseElement(JSONNode ctx, string token, string tokenName, bool quoted) { if (quoted) { ctx.Add(tokenName, token); return; } string text = token.ToLower(); switch (text) { case "false": case "true": ctx.Add(tokenName, text == "true"); return; case "null": ctx.Add(tokenName, null); return; } if (double.TryParse(token, out var result)) { ctx.Add(tokenName, result); } else { ctx.Add(tokenName, token); } } public static JSONNode Parse(string aJSON) { Stack<JSONNode> stack = new Stack<JSONNode>(); JSONNode jSONNode = null; int i = 0; StringBuilder stringBuilder = new StringBuilder(); string text = ""; bool flag = false; bool flag2 = false; for (; i < aJSON.Length; i++) { switch (aJSON[i]) { case '{': if (flag) { stringBuilder.Append(aJSON[i]); break; } stack.Push(new JSONObject()); if (jSONNode != null) { jSONNode.Add(text, stack.Peek()); } text = ""; stringBuilder.Length = 0; jSONNode = stack.Peek(); break; case '[': if (flag) { stringBuilder.Append(aJSON[i]); break; } stack.Push(new JSONArray()); if (jSONNode != null) { jSONNode.Add(text, stack.Peek()); } text = ""; stringBuilder.Length = 0; jSONNode = stack.Peek(); break; case ']': case '}': if (flag) { stringBuilder.Append(aJSON[i]); break; } if (stack.Count == 0) { throw new Exception("JSON Parse: Too many closing brackets"); } stack.Pop(); if (stringBuilder.Length > 0 || flag2) { ParseElement(jSONNode, stringBuilder.ToString(), text, flag2); flag2 = false; } text = ""; stringBuilder.Length = 0; if (stack.Count > 0) { jSONNode = stack.Peek(); } break; case ':': if (flag) { stringBuilder.Append(aJSON[i]); break; } text = stringBuilder.ToString(); stringBuilder.Length = 0; flag2 = false; break; case '"': flag = !flag; flag2 = flag2 || flag; break; case ',': if (flag) { stringBuilder.Append(aJSON[i]); break; } if (stringBuilder.Length > 0 || flag2) { ParseElement(jSONNode, stringBuilder.ToString(), text, flag2); flag2 = false; } text = ""; stringBuilder.Length = 0; flag2 = false; break; case '\t': case ' ': if (flag) { stringBuilder.Append(aJSON[i]); } break; case '\\': i++; if (flag) { char c = aJSON[i]; switch (c) { case 't': stringBuilder.Append('\t'); break; case 'r': stringBuilder.Append('\r'); break; case 'n': stringBuilder.Append('\n'); break; case 'b': stringBuilder.Append('\b'); break; case 'f': stringBuilder.Append('\f'); break; case 'u': { string s = aJSON.Substring(i + 1, 4); stringBuilder.Append((char)int.Parse(s, NumberStyles.AllowHexSpecifier)); i += 4; break; } default: stringBuilder.Append(c); break; } } break; default: stringBuilder.Append(aJSON[i]); break; case '\n': case '\r': break; } } if (flag) { throw new Exception("JSON Parse: Quotation marks seems to be messed up."); } return jSONNode; } } public class JSONArray : JSONNode { private List<JSONNode> m_List = new List<JSONNode>(); private bool inline; public override bool Inline { get { return inline; } set { inline = value; } } public override JSONNodeType Tag => JSONNodeType.Array; public override bool IsArray => true; public override JSONNode this[int aIndex] { get { if (aIndex < 0 || aIndex >= m_List.Count) { return new JSONLazyCreator(this); } return m_List[aIndex]; } set { if (value == null) { value = JSONNull.CreateOrGet(); } if (aIndex < 0 || aIndex >= m_List.Count) { m_List.Add(value); } else { m_List[aIndex] = value; } } } public override JSONNode this[string aKey] { get { return new JSONLazyCreator(this); } set { if (value == null) { value = JSONNull.CreateOrGet(); } m_List.Add(value); } } public override int Count => m_List.Count; public override IEnumerable<JSONNode> Children { get { foreach (JSONNode item in m_List) { yield return item; } } } public override Enumerator GetEnumerator() { return new Enumerator(m_List.GetEnumerator()); } public override void Add(string aKey, JSONNode aItem) { if (aItem == null) { aItem = JSONNull.CreateOrGet(); } m_List.Add(aItem); } public override JSONNode Remove(int aIndex) { if (aIndex < 0 || aIndex >= m_List.Count) { return null; } JSONNode result = m_List[aIndex]; m_List.RemoveAt(aIndex); return result; } public override JSONNode Remove(JSONNode aNode) { m_List.Remove(aNode); return aNode; } internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) { aSB.Append('['); int count = m_List.Count; if (inline) { aMode = JSONTextMode.Compact; } for (int i = 0; i < count; i++) { if (i > 0) { aSB.Append(','); } if (aMode == JSONTextMode.Indent) { aSB.AppendLine(); } if (aMode == JSONTextMode.Indent) { aSB.Append(' ', aIndent + aIndentInc); } m_List[i].WriteToStringBuilder(aSB, aIndent + aIndentInc, aIndentInc, aMode); } if (aMode == JSONTextMode.Indent) { aSB.AppendLine().Append(' ', aIndent); } aSB.Append(']'); } } public class JSONObject : JSONNode { private Dictionary<string, JSONNode> m_Dict = new Dictionary<string, JSONNode>(); private bool inline; public override bool Inline { get { return inline; } set { inline = value; } } public override JSONNodeType Tag => JSONNodeType.Object; public override bool IsObject => true; public override JSONNode this[string aKey] { get { if (m_Dict.ContainsKey(aKey)) { return m_Dict[aKey]; } return new JSONLazyCreator(this, aKey); } set { if (value == null) { value = JSONNull.CreateOrGet(); } if (m_Dict.ContainsKey(aKey)) { m_Dict[aKey] = value; } else { m_Dict.Add(aKey, value); } } } public override JSONNode this[int aIndex] { get { if (aIndex < 0 || aIndex >= m_Dict.Count) { return null; } return m_Dict.ElementAt(aIndex).Value; } set { if (value == null) { value = JSONNull.CreateOrGet(); } if (aIndex >= 0 && aIndex < m_Dict.Count) { string key = m_Dict.ElementAt(aIndex).Key; m_Dict[key] = value; } } } public override int Count => m_Dict.Count; public override IEnumerable<JSONNode> Children { get { foreach (KeyValuePair<string, JSONNode> item in m_Dict) { yield return item.Value; } } } public override Enumerator GetEnumerator() { return new Enumerator(m_Dict.GetEnumerator()); } public override void Add(string aKey, JSONNode aItem) { if (aItem == null) { aItem = JSONNull.CreateOrGet(); } if (!string.IsNullOrEmpty(aKey)) { if (m_Dict.ContainsKey(aKey)) { m_Dict[aKey] = aItem; } else { m_Dict.Add(aKey, aItem); } } else { m_Dict.Add(Guid.NewGuid().ToString(), aItem); } } public override JSONNode Remove(string aKey) { if (!m_Dict.ContainsKey(aKey)) { return null; } JSONNode result = m_Dict[aKey]; m_Dict.Remove(aKey); return result; } public override JSONNode Remove(int aIndex) { if (aIndex < 0 || aIndex >= m_Dict.Count) { return null; } KeyValuePair<string, JSONNode> keyValuePair = m_Dict.ElementAt(aIndex); m_Dict.Remove(keyValuePair.Key); return keyValuePair.Value; } public override JSONNode Remove(JSONNode aNode) { try { KeyValuePair<string, JSONNode> keyValuePair = m_Dict.Where((KeyValuePair<string, JSONNode> k) => k.Value == aNode).First(); m_Dict.Remove(keyValuePair.Key); return aNode; } catch { return null; } } internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) { aSB.Append('{'); bool flag = true; if (inline) { aMode = JSONTextMode.Compact; } foreach (KeyValuePair<string, JSONNode> item in m_Dict) { if (!flag) { aSB.Append(','); } flag = false; if (aMode == JSONTextMode.Indent) { aSB.AppendLine(); } if (aMode == JSONTextMode.Indent) { aSB.Append(' ', aIndent + aIndentInc); } aSB.Append('"').Append(JSONNode.Escape(item.Key)).Append('"'); if (aMode == JSONTextMode.Compact) { aSB.Append(':'); } else { aSB.Append(" : "); } item.Value.WriteToStringBuilder(aSB, aIndent + aIndentInc, aIndentInc, aMode); } if (aMode == JSONTextMode.Indent) { aSB.AppendLine().Append(' ', aIndent); } aSB.Append('}'); } } public class JSONString : JSONNode { private string m_Data; public override JSONNodeType Tag => JSONNodeType.String; public override bool IsString => true; public override string Value { get { return m_Data; } set { m_Data = value; } } public override Enumerator GetEnumerator() { return default(Enumerator); } public JSONString(string aData) { m_Data = aData; } internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) { aSB.Append('"').Append(JSONNode.Escape(m_Data)).Append('"'); } public override bool Equals(object obj) { if (base.Equals(obj)) { return true; } if (obj is string text) { return m_Data == text; } JSONString jSONString = obj as JSONString; if (jSONString != null) { return m_Data == jSONString.m_Data; } return false; } public override int GetHashCode() { return m_Data.GetHashCode(); } } public class JSONNumber : JSONNode { private double m_Data; public override JSONNodeType Tag => JSONNodeType.Number; public override bool IsNumber => true; public override string Value { get { return m_Data.ToString(); } set { if (double.TryParse(value, out var result)) { m_Data = result; } } } public override double AsDouble { get { return m_Data; } set { m_Data = value; } } public override Enumerator GetEnumerator() { return default(Enumerator); } public JSONNumber(double aData) { m_Data = aData; } public JSONNumber(string aData) { Value = aData; } internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) { aSB.Append(m_Data); } private static bool IsNumeric(object value) { if (!(value is int) && !(value is uint) && !(value is float) && !(value is double) && !(value is decimal) && !(value is long) && !(value is ulong) && !(value is short) && !(value is ushort) && !(value is sbyte)) { return value is byte; } return true; } public override bool Equals(object obj) { if (obj == null) { return false; } if (base.Equals(obj)) { return true; } JSONNumber jSONNumber = obj as JSONNumber; if (jSONNumber != null) { return m_Data == jSONNumber.m_Data; } if (IsNumeric(obj)) { return Convert.ToDouble(obj) == m_Data; } return false; } public override int GetHashCode() { return m_Data.GetHashCode(); } } public class JSONBool : JSONNode { private bool m_Data; public override JSONNodeType Tag => JSONNodeType.Boolean; public override bool IsBoolean => true; public override string Value { get { return m_Data.ToString(); } set { if (bool.TryParse(value, out var result)) { m_Data = result; } } } public override bool AsBool { get { return m_Data; } set { m_Data = value; } } public override Enumerator GetEnumerator() { return default(Enumerator); } public JSONBool(bool aData) { m_Data = aData; } public JSONBool(string aData) { Value = aData; } internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) { aSB.Append(m_Data ? "true" : "false"); } public override bool Equals(object obj) { if (obj == null) { return false; } if (obj is bool) { return m_Data == (bool)obj; } return false; } public override int GetHashCode() { return m_Data.GetHashCode(); } } public class JSONNull : JSONNode { private static JSONNull m_StaticInstance = new JSONNull(); public static bool reuseSameInstance = true; public override JSONNodeType Tag => JSONNodeType.NullValue; public override bool IsNull => true; public override string Value { get { return "null"; } set { } } public override bool AsBool { get { return false; } set { } } public static JSONNull CreateOrGet() { if (reuseSameInstance) { return m_StaticInstance; } return new JSONNull(); } private JSONNull() { } public override Enumerator GetEnumerator() { return default(Enumerator); } public override bool Equals(object obj) { if ((object)this == obj) { return true; } return obj is JSONNull; } public override int GetHashCode() { return 0; } internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) { aSB.Append("null"); } } internal class JSONLazyCreator : JSONNode { private JSONNode m_Node; private string m_Key; public override JSONNodeType Tag => JSONNodeType.None; public override JSONNode this[int aIndex] { get { return new JSONLazyCreator(this); } set { JSONArray jSONArray = new JSONArray(); jSONArray.Add(value); Set(jSONArray); } } public override JSONNode this[string aKey] { get { return new JSONLazyCreator(this, aKey); } set { JSONObject jSONObject = new JSONObject(); jSONObject.Add(aKey, value); Set(jSONObject); } } public override int AsInt { get { JSONNumber aVal = new JSONNumber(0.0); Set(aVal); return 0; } set { JSONNumber aVal = new JSONNumber(value); Set(aVal); } } public override float AsFloat { get { JSONNumber aVal = new JSONNumber(0.0); Set(aVal); return 0f; } set { JSONNumber aVal = new JSONNumber(value); Set(aVal); } } public override double AsDouble { get { JSONNumber aVal = new JSONNumber(0.0); Set(aVal); return 0.0; } set { JSONNumber aVal = new JSONNumber(value); Set(aVal); } } public override bool AsBool { get { JSONBool aVal = new JSONBool(aData: false); Set(aVal); return false; } set { JSONBool aVal = new JSONBool(value); Set(aVal); } } public override JSONArray AsArray { get { JSONArray jSONArray = new JSONArray(); Set(jSONArray); return jSONArray; } } public override JSONObject AsObject { get { JSONObject jSONObject = new JSONObject(); Set(jSONObject); return jSONObject; } } public override Enumerator GetEnumerator() { return default(Enumerator); } public JSONLazyCreator(JSONNode aNode) { m_Node = aNode; m_Key = null; } public JSONLazyCreator(JSONNode aNode, string aKey) { m_Node = aNode; m_Key = aKey; } private void Set(JSONNode aVal) { if (m_Key == null) { m_Node.Add(aVal); } else { m_Node.Add(m_Key, aVal); } m_Node = null; } public override void Add(JSONNode aItem) { JSONArray jSONArray = new JSONArray(); jSONArray.Add(aItem); Set(jSONArray); } public override void Add(string aKey, JSONNode aItem) { JSONObject jSONObject = new JSONObject(); jSONObject.Add(aKey, aItem); Set(jSONObject); } public static bool operator ==(JSONLazyCreator a, object b) { if (b == null) { return true; } return (object)a == b; } public static bool operator !=(JSONLazyCreator a, object b) { return !(a == b); } public override bool Equals(object obj) { if (obj == null) { return true; } return (object)this == obj; } public override int GetHashCode() { return 0; } internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) { aSB.Append("null"); } } public static class JSON { public static JSONNode Parse(string aJSON) { return JSONNode.Parse(aJSON); } } } namespace XUnity.AutoTranslator.Plugin.Utilities { internal static class SceneManagerHelper { public static int GetActiveSceneId() { if (Features.SupportsSceneManager) { return GetActiveSceneIdBySceneManager(); } return GetActiveSceneIdByApplication(); } private static int GetActiveSceneIdBySceneManager() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) Scene activeScene = SceneManager.GetActiveScene(); return ((Scene)(ref activeScene)).buildIndex; } private static int GetActiveSceneIdByApplication() { return Application.loadedLevel; } } internal class SceneLoadInformation { public SceneInformation ActiveScene { get; set; } public List<SceneInformation> LoadedScenes { get; set; } public SceneLoadInformation() { LoadedScenes = new List<SceneInformation>(); if (Features.SupportsSceneManager) { LoadBySceneManager(); } else { LoadByApplication(); } } public void LoadBySceneManager() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: 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_0029: Unknown result type (might be due to invalid IL or missing references) Scene activeScene = SceneManager.GetActiveScene(); ActiveScene = new SceneInformation(((Scene)(ref activeScene)).buildIndex, ((Scene)(ref activeScene)).name); for (int i = 0; i < SceneManager.sceneCount; i++) { Scene sceneAt = SceneManager.GetSceneAt(i); LoadedScenes.Add(new SceneInformation(((Scene)(ref sceneAt)).buildIndex, ((Scene)(ref sceneAt)).name)); } } public void LoadByApplication() { ActiveScene = new SceneInformation(Application.loadedLevel, Application.loadedLevelName); LoadedScenes.Add(new SceneInformation(Application.loadedLevel, Application.loadedLevelName)); } } internal class SceneInformation { public int Id { get; set; } public string Name { get; set; } public SceneInformation(int id, string name) { Id = id; Name = name; } } internal static class TranslationScopeProvider { public static int GetScope(object ui) { if (Settings.EnableTranslationScoping) { try { Component val = (Component)((ui is Component) ? ui : null); if (val != null) { return GetScopeFromComponent(val); } if (ui is GUIContent) { return -1; } return SceneManagerHelper.GetActiveSceneId(); } catch (MissingMemberException ex) { XuaLogger.AutoTranslator.Error((Exception)ex, "A 'missing member' error occurred while retriving translation scope. Disabling translation scopes."); Settings.EnableTranslationScoping = false; } } return -1; } public static int GetScopeFromComponent(Component component) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) Scene scene = component.gameObject.scene; return ((Scene)(ref scene)).buildIndex; } } internal static class TranslationScopes { public const int None = -1; } } namespace XUnity.AutoTranslator.Plugin.Core { public class AutoTranslationPlugin : MonoBehaviour, IInternalTranslator, ITranslator, ITranslationRegistry { internal static AutoTranslationPlugin Current; private static bool _hasResizedCurrentComponentDuringDiscovery; internal XuaWindow MainWindow; internal TranslationAggregatorWindow TranslationAggregatorWindow; internal TranslationAggregatorOptionsWindow TranslationAggregatorOptionsWindow; internal TranslationManager TranslationManager; internal TextTranslationCache TextCache; internal Dictionary<string, TextTranslationCache> PluginTextCaches = new Dictionary<string, TextTranslationCache>(StringComparer.OrdinalIgnoreCase); internal TextureTranslationCache TextureCache; internal UIResizeCache ResizeCache; internal SpamChecker SpamChecker; private List<Action<ComponentTranslationContext>> _shouldIgnore = new List<Action<ComponentTranslationContext>>(); private List<string> _textsToCopyToClipboardOrdered = new List<string>(); private HashSet<string> _textsToCopyToClipboard = new HashSet<string>(); private float _clipboardUpdated; private HashSet<string> _immediatelyTranslating = new HashSet<string>(); private bool _isInTranslatedMode = true; private bool _textHooksEnabled = true; private float _batchOperationSecondCounter; private bool _hasValidOverrideFont; private bool _hasOverridenFont; private bool _initialized; private bool _temporarilyDisabled; private string _requireSpriteRendererCheckCausedBy; private int _lastSpriteUpdateFrame = -1; private bool _isCalledFromSceneManager; private bool _translationReloadRequest; private static Dictionary<string, UntranslatedText> CachedKeys = new Dictionary<string, UntranslatedText>(StringComparer.Ordinal); public void Initialize() { Current = this; Paths.Initialize(); HarmonyLoader.Load(); Settings.Configure(); DebugConsole.Enable(); InitializeHarmonyDetourBridge(); InitializeTextTranslationCaches(); HooksSetup.InstallTextHooks(); HooksSetup.InstallImageHooks(); HooksSetup.InstallSpriteRendererHooks(); HooksSetup.InstallTextGetterCompatHooks(); HooksSetup.InstallComponentBasedPluginTranslationHooks(); TextureCache = new TextureTranslationCache(); TextureCache.TextureTranslationFileChanged += TextureCache_TextureTranslationFileChanged; ResizeCache = new UIResizeCache(); TranslationManager = new TranslationManager(); TranslationManager.JobCompleted += OnJobCompleted; TranslationManager.JobFailed += OnJobFailed; TranslationManager.InitializeEndpoints(((Component)this).gameObject); SpamChecker = new SpamChecker(TranslationManager); UnityTextParsers.Initialize(); InitializeResourceRedirector(); ValidateConfiguration(); EnableSceneLoadScan(); LoadTranslations(reload: false); InitializeGUI(); XuaLogger.AutoTranslator.Info("Loaded XUnity.AutoTranslator into Unity [" + Application.unityVersion + "] game."); } private static void InitializeHarmonyDetourBridge() { try { if (Settings.InitializeHarmonyDetourBridge) { InitializeHarmonyDetourBridgeSafe(); } } catch (Exception ex) { XuaLogger.AutoTranslator.Error(ex, "An error occurred while initializing harmony detour bridge."); } } private static void InitializeHarmonyDetourBridgeSafe() { HarmonyDetourBridge.Init(true, (Type)0); } private void InitializeTextTranslationCaches() { try { TextCache = new TextTranslationCache(); TextCache.TextTranslationFileChanged += TextCache_TextTranslationFileChanged; DirectoryInfo directoryInfo = new DirectoryInfo(Path.Combine(Settings.TranslationsPath, "plugins")); if (directoryInfo.Exists) { DirectoryInfo[] directories = directoryInfo.GetDirectories(); foreach (DirectoryInfo directoryInfo2 in directories) { TextTranslationCache value = new TextTranslationCache(directoryInfo2); PluginTextCaches.Add(directoryInfo2.Name, value); } } } catch (Exception ex) { XuaLogger.AutoTranslator.Error(ex, "An error occurred while initializing text translation caches."); } } private void TextCache_TextTranslationFileChanged() { _translationReloadRequest = true; } private void TextureCache_TextureTranslationFileChanged() { _translationReloadRequest = true; } private static void EnableLogAllLoadedResources() { ResourceRedirection.LogAllLoadedResources = true; } private void InitializeResourceRedirector() { try { if (Settings.LogAllLoadedResources) { EnableLogAllLoadedResources(); } if (Settings.EnableTextAssetRedirector) { EnableTextAssetLoadedHandler(); } } catch (Exception ex) { XuaLogger.AutoTranslator.Error(ex, "An error occurred while initializing resource redirectors."); } } private void EnableTextAssetLoadedHandler() { new TextAssetLoadedHandler(); } private void InitializeGUI() { try { DisableAutoTranslator(); MainWindow = new XuaWindow(CreateXuaViewModel()); TranslationAggregatorViewModel viewModel = CreateTranslationAggregatorViewModel(); TranslationAggregatorWindow = new TranslationAggregatorWindow(viewModel); TranslationAggregatorOptionsWindow = new TranslationAggregatorOptionsWindow(viewModel); } catch (Exception ex) { XuaLogger.AutoTranslator.Error(ex, "An error occurred while setting up UI."); } finally { EnableAutoTranslator(); } } private TranslationAggregatorViewModel CreateTranslationAggregatorViewModel() { return new TranslationAggregatorViewModel(TranslationManager); } private XuaViewModel CreateXuaViewModel() { return new XuaViewModel(new List<ToggleViewModel> { new ToggleViewModel(" Translated", "<b>TRANSLATED</b>\nThe plugin currently displays translated texts. Disabling this does not mean the plugin will no longer perform translations, just that they will not be displayed.", "<b>NOT TRANSLATED</b>\nThe plugin currently displays untranslated texts.", ToggleTranslation, () => _isInTranslatedMode), new ToggleViewModel(" Silent Logging", "<b>SILENT</b>\nThe plugin will not print out success messages to the log in relation to translations.", "<b>VERBOSE</b>\nThe plugin will print out success messages to the log in relation to translations.", ToggleSilentMode, () => Settings.EnableSilentMode), new ToggleViewModel(" Translation Aggregator", "<b>SHOWN</b>\nThe translation aggregator window is shown.", "<b>HIDDEN</b>\nThe translation aggregator window is not shown.", ToggleTranslationAggregator, () => TranslationAggregatorWindow != null && TranslationAggregatorWindow.IsShown) }, new DropdownViewModel<TranslatorDropdownOptionViewModel,
H3VR_Chinese_Localization/plugins/XUnity.AutoTranslator/XUnity.AutoTranslator.Plugin.ExtProtocol.dll
Decompiled 4 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyCompany("gravydevsupreme")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Package that contains a simple inter-process protocol format used in XUnity.")] [assembly: AssemblyFileVersion("1.0.1.0")] [assembly: AssemblyInformationalVersion("1.0.1")] [assembly: AssemblyProduct("XUnity.AutoTranslator.Plugin.ExtProtocol")] [assembly: AssemblyTitle("XUnity.AutoTranslator.Plugin.ExtProtocol")] [assembly: AssemblyVersion("1.0.1.0")] namespace XUnity.AutoTranslator.Plugin.ExtProtocol; public class ConfigurationMessage : ProtocolMessage { public static readonly string Type = "4"; public string Config { get; set; } internal override void Decode(TextReader reader) { base.Id = new Guid(reader.ReadLine()); Config = reader.ReadToEnd(); } internal override void Encode(TextWriter writer) { writer.WriteLine(base.Id.ToString()); writer.Write(Config); } } public static class ExtProtocolConvert { private static readonly Dictionary<string, Type> IdToType; private static readonly Dictionary<Type, string> TypeToId; static ExtProtocolConvert() { IdToType = new Dictionary<string, Type>(); TypeToId = new Dictionary<Type, string>(); Register(TranslationRequest.Type, typeof(TranslationRequest)); Register(TranslationResponse.Type, typeof(TranslationResponse)); Register(TranslationError.Type, typeof(TranslationError)); Register(ConfigurationMessage.Type, typeof(ConfigurationMessage)); } public static void Register(string id, Type type) { IdToType[id] = type; TypeToId[type] = id; } public static string Encode(ProtocolMessage message) { StringWriter stringWriter = new StringWriter(); string value = TypeToId[message.GetType()]; stringWriter.WriteLine(value); message.Encode(stringWriter); return Convert.ToBase64String(Encoding.UTF8.GetBytes(stringWriter.ToString()), Base64FormattingOptions.None); } public static ProtocolMessage Decode(string message) { StringReader stringReader = new StringReader(Encoding.UTF8.GetString(Convert.FromBase64String(message))); string key = stringReader.ReadLine(); ProtocolMessage obj = (ProtocolMessage)Activator.CreateInstance(IdToType[key]); obj.Decode(stringReader); return obj; } } public abstract class ProtocolMessage { public Guid Id { get; set; } internal abstract void Decode(TextReader reader); internal abstract void Encode(TextWriter writer); } public enum StatusCode { OK = 0, Blocked = 1, Unknown = 1000 } public class TranslationError : ProtocolMessage { public static readonly string Type = "3"; public string Reason { get; set; } public StatusCode FailureCode { get; set; } internal override void Decode(TextReader reader) { base.Id = new Guid(reader.ReadLine()); FailureCode = (StatusCode)int.Parse(reader.ReadLine()); Reason = reader.ReadToEnd(); } internal override void Encode(TextWriter writer) { writer.WriteLine(base.Id.ToString()); writer.WriteLine((int)FailureCode); writer.Write(Reason); } } public class TranslationRequest : ProtocolMessage { public static readonly string Type = "1"; private string[] _untranslatedTexts; public string SourceLanguage { get; set; } public string DestinationLanguage { get; set; } public string[] UntranslatedTexts => _untranslatedTexts ?? (_untranslatedTexts = UntranslatedTextInfos.Select((TransmittableUntranslatedTextInfo x) => x.UntranslatedText).ToArray()); public TransmittableUntranslatedTextInfo[] UntranslatedTextInfos { get; set; } internal override void Decode(TextReader reader) { base.Id = new Guid(reader.ReadLine()); SourceLanguage = reader.ReadLine(); DestinationLanguage = reader.ReadLine(); int num = int.Parse(reader.ReadLine(), CultureInfo.InvariantCulture); TransmittableUntranslatedTextInfo[] array = new TransmittableUntranslatedTextInfo[num]; for (int i = 0; i < num; i++) { TransmittableUntranslatedTextInfo transmittableUntranslatedTextInfo = new TransmittableUntranslatedTextInfo(); transmittableUntranslatedTextInfo.Decode(reader); array[i] = transmittableUntranslatedTextInfo; } UntranslatedTextInfos = array; } internal override void Encode(TextWriter writer) { writer.WriteLine(base.Id.ToString()); writer.WriteLine(SourceLanguage); writer.WriteLine(DestinationLanguage); writer.WriteLine(UntranslatedTextInfos.Length.ToString(CultureInfo.InvariantCulture)); TransmittableUntranslatedTextInfo[] untranslatedTextInfos = UntranslatedTextInfos; for (int i = 0; i < untranslatedTextInfos.Length; i++) { untranslatedTextInfos[i].Encode(writer); } } } public class TranslationResponse : ProtocolMessage { public static readonly string Type = "2"; public string[] TranslatedTexts { get; set; } internal override void Decode(TextReader reader) { base.Id = new Guid(reader.ReadLine()); int num = int.Parse(reader.ReadLine(), CultureInfo.InvariantCulture); string[] array = new string[num]; for (int i = 0; i < num; i++) { string s = reader.ReadLine(); string @string = Encoding.UTF8.GetString(Convert.FromBase64String(s)); array[i] = @string; } TranslatedTexts = array; } internal override void Encode(TextWriter writer) { writer.WriteLine(base.Id.ToString()); writer.WriteLine(TranslatedTexts.Length.ToString(CultureInfo.InvariantCulture)); string[] translatedTexts = TranslatedTexts; foreach (string s in translatedTexts) { string value = Convert.ToBase64String(Encoding.UTF8.GetBytes(s), Base64FormattingOptions.None); writer.WriteLine(value); } } } public class TransmittableUntranslatedTextInfo { public string[] ContextBefore { get; set; } public string UntranslatedText { get; set; } public string[] ContextAfter { get; set; } public TransmittableUntranslatedTextInfo(string[] contextBefore, string untranslatedText, string[] contextAfter) { ContextBefore = contextBefore; UntranslatedText = untranslatedText; ContextAfter = contextAfter; } public TransmittableUntranslatedTextInfo() { } internal void Encode(TextWriter writer) { string[] contextBefore = ContextBefore; writer.WriteLine(((contextBefore != null) ? contextBefore.Length : 0).ToString(CultureInfo.InvariantCulture)); if (ContextBefore != null) { string[] contextBefore2 = ContextBefore; foreach (string s in contextBefore2) { string value = Convert.ToBase64String(Encoding.UTF8.GetBytes(s), Base64FormattingOptions.None); writer.WriteLine(value); } } string[] contextAfter = ContextAfter; writer.WriteLine(((contextAfter != null) ? contextAfter.Length : 0).ToString(CultureInfo.InvariantCulture)); if (ContextAfter != null) { string[] contextBefore2 = ContextAfter; foreach (string s2 in contextBefore2) { string value2 = Convert.ToBase64String(Encoding.UTF8.GetBytes(s2), Base64FormattingOptions.None); writer.WriteLine(value2); } } string value3 = Convert.ToBase64String(Encoding.UTF8.GetBytes(UntranslatedText), Base64FormattingOptions.None); writer.WriteLine(value3); } internal void Decode(TextReader reader) { int num = int.Parse(reader.ReadLine(), CultureInfo.InvariantCulture); string[] array = new string[num]; for (int i = 0; i < num; i++) { string s = reader.ReadLine(); string @string = Encoding.UTF8.GetString(Convert.FromBase64String(s)); array[i] = @string; } ContextBefore = array; int num2 = int.Parse(reader.ReadLine(), CultureInfo.InvariantCulture); string[] array2 = new string[num2]; for (int j = 0; j < num2; j++) { string s2 = reader.ReadLine(); string string2 = Encoding.UTF8.GetString(Convert.FromBase64String(s2)); array2[j] = string2; } ContextAfter = array2; string s3 = reader.ReadLine(); string string3 = Encoding.UTF8.GetString(Convert.FromBase64String(s3)); UntranslatedText = string3; } }