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,