using System;
using System.Collections;
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.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Logging;
using CommonAPI;
using CommonAPI.Systems.ModLocalization;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("LanguageLoaderPlugin")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("LanguageLoaderPlugin")]
[assembly: AssemblyTitle("LanguageLoaderPlugin")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace LanguageLoaderPlugin
{
[BepInPlugin("org.kremnev8.LanguageLoaderPlugin", "Language Loader Plugin", "1.0.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[CommonAPISubmoduleDependency(new string[] { "LocalizationModule" })]
public class LoaderPlugin : BaseUnityPlugin
{
public const string GUID = "org.kremnev8.LanguageLoaderPlugin";
public const string DISPNAME = "Language Loader Plugin";
public const string VERSION = "1.0.0";
public static ManualLogSource logger;
private void Awake()
{
logger = ((BaseUnityPlugin)this).Logger;
Assembly executingAssembly = Assembly.GetExecutingAssembly();
string directoryName = Path.GetDirectoryName(executingAssembly.Location);
string fullName = Directory.GetParent(directoryName).FullName;
LoadLanguages(fullName, executingAssembly);
logger.LogInfo((object)"Finished loading all JSON mods");
}
private static void LoadLanguages(string pluginsDir, Assembly assembly)
{
foreach (string item in Directory.EnumerateDirectories(pluginsDir))
{
string path = Path.Combine(item, assembly.GetName().Name + ".dll");
if (File.Exists(path))
{
try
{
LoadDirectory(item);
}
catch (Exception arg)
{
logger.LogError((object)$"Exception while loading folder {item} language data!\n{arg}");
}
}
}
}
internal static void LoadDirectory(string directory)
{
//IL_0064: Unknown result type (might be due to invalid IL or missing references)
//IL_0069: Unknown result type (might be due to invalid IL or missing references)
//IL_0072: Unknown result type (might be due to invalid IL or missing references)
//IL_007b: Unknown result type (might be due to invalid IL or missing references)
//IL_0086: Expected O, but got Unknown
//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
//IL_00db: Unknown result type (might be due to invalid IL or missing references)
string path = Path.Combine(directory, "language.tsv");
if (!File.Exists(path))
{
logger.LogError((object)("Failed to load language folder " + directory + ", because manifest file is missing!"));
return;
}
string text = File.ReadAllText(path);
string[] array = text.Split(new char[1] { '\t' });
string text2 = array[3];
string text3 = array[4];
string value = array[5];
Language val = new Language
{
name = array[0],
abbr = array[1],
abbr2 = array[2]
};
if (int.TryParse(text2, out var result))
{
val.fallback = result;
}
else
{
result = LocalizationModule.GetLanguageId(text2);
if (result == 0)
{
result = 1033;
}
val.fallback = result;
}
if (Enum.TryParse<EGlyph>(value, ignoreCase: true, out EGlyph result2))
{
val.glyph = result2;
}
int num = LocalizationModule.AddLanguage(val);
if (!string.IsNullOrEmpty(text3))
{
if (text3 == "$bundle")
{
string text4 = Path.Combine(directory, "font_bundle");
LocalizationModule.RegisterFontForLanguageFromBundle(num, text4);
}
else
{
LocalizationModule.RegisterFontForLanguage(num, text3);
}
}
string text5 = Path.Combine(directory, "Locale");
LocalizationModule.LoadTranslationsFromFolder(text5);
logger.LogInfo((object)("Done adding custom language " + val.name + "!"));
}
}
}
namespace LanguageLoaderPlugin.SimpleJson
{
public enum JsonOption
{
FAIL_SILENTLY,
ALLOW_COMMENTS,
ALLOW_TRAILING_COMMA,
ALLOW_SINGLE_QUOTES,
ALLOW_NON_QUOTED_KEYS,
ALLOW_NON_OBJECT_AS_ROOT,
ALLOW_DECIMAL_AS_FLOAT_FIRST_CHAR,
OVERWRITE_DUPLICATE_KEYS,
ALLOW_PYTHON_PRIMITIVES
}
public class JsonParser
{
private class Token
{
public string Value { get; set; }
public int Line { get; set; }
public int Column { get; set; }
public override string ToString()
{
return "TOKEN: " + Value;
}
public Token(string value, int line, int column)
{
Value = value;
Line = line;
Column = column;
}
public Exception Throw(string message)
{
throw new JsonParserException("Line " + Line + ", Column " + Column + ": " + message);
}
}
private class TokenStream
{
private enum TokenizerState
{
NONE,
COMMENT,
STRING,
WORD
}
private int index;
private int length;
private Token[] tokens;
public int Length => length;
public Token Last => tokens[length - 1];
public TokenStream(string text, bool allowComments)
{
tokens = Init(text, allowComments);
index = 0;
length = tokens.Length;
}
public void Reset()
{
index = 0;
}
private static Token[] Init(string text, bool allowComments)
{
List<Token> list = new List<Token>();
text += "\n";
int num = text.Length;
TokenizerState tokenizerState = TokenizerState.NONE;
List<string> list2 = new List<string>();
int num2 = 0;
int[] array = new int[num];
int[] array2 = new int[num];
int num3 = 1;
int num4 = 1;
for (int i = 0; i < num; i++)
{
array[i] = num3;
array2[i] = num4++;
if (text[i] == '\n')
{
num3++;
num4 = 1;
}
}
char c = '\0';
for (int j = 0; j < num; j++)
{
char c2 = text[j];
switch (tokenizerState)
{
case TokenizerState.NONE:
switch (c2)
{
case '"':
case '\'':
c = c2;
num2 = j;
tokenizerState = TokenizerState.STRING;
break;
case '/':
if (allowComments && j + 1 < num && text[j + 1] == '*')
{
tokenizerState = TokenizerState.COMMENT;
j++;
}
else
{
list.Add(new Token("/", array[j], array2[j]));
}
break;
default:
if ((c2 >= '0' && c2 <= '9') || (c2 >= 'a' && c2 <= 'z') || (c2 >= 'A' && c2 <= 'Z') || c2 == '_' || c2 == '.' || c2 == '-')
{
tokenizerState = TokenizerState.WORD;
num2 = j;
}
else
{
list.Add(new Token(c2.ToString(), array[j], array2[j]));
}
break;
case '\t':
case '\n':
case '\r':
case ' ':
break;
}
break;
case TokenizerState.COMMENT:
if (c2 == '*' && j + 1 < num && text[j + 1] == '/')
{
j++;
tokenizerState = TokenizerState.NONE;
}
break;
case TokenizerState.STRING:
if (c2 == c)
{
list.Add(new Token(text.Substring(num2, j - num2 + 1), array[num2], array2[num2]));
tokenizerState = TokenizerState.NONE;
}
else if (c2 == '\\')
{
j++;
}
break;
case TokenizerState.WORD:
if ((c2 < '0' || c2 > '9') && (c2 < 'a' || c2 > 'z') && (c2 < 'A' || c2 > 'Z') && c2 != '_' && c2 != '.' && c2 != '-')
{
list.Add(new Token(text.Substring(num2, j - num2), array[num2], array2[num2]));
j--;
tokenizerState = TokenizerState.NONE;
}
break;
}
}
return tokenizerState switch
{
TokenizerState.COMMENT => throw new JsonParserException("Unexpected EOF detected. A comment seems to be left unclosed."),
TokenizerState.NONE => list.ToArray(),
_ => throw new JsonParserException("Unexpected EOF detected. A string seems to be left unclosed."),
};
}
public Token Pop()
{
if (index == length)
{
throw new InvalidOperationException();
}
return tokens[index++];
}
public Token Peek()
{
if (index < length)
{
return tokens[index];
}
throw Last.Throw("Unexpected EOF");
}
public string PeekValue()
{
if (index < length)
{
return tokens[index].Value;
}
throw Last.Throw("Unexpected EOF");
}
public void PopExpected(string value)
{
if (index < length)
{
Token token = tokens[index];
if (token.Value != value)
{
token.Throw("Unexpected token. Expected '" + value + "' but found '" + token.Value + "' instead.");
}
index++;
}
else
{
Last.Throw("Unexpected EOF. Expected '" + value + "'.");
}
}
public bool PopIfPresent(string value)
{
if (index < length && tokens[index].Value == value)
{
index++;
return true;
}
return false;
}
}
public class JsonParserException : Exception
{
public JsonParserException(string message)
: base(message)
{
}
}
private class ReadOnlyOrderedDictionary : IDictionary<string, object>, ICollection<KeyValuePair<string, object>>, IEnumerable<KeyValuePair<string, object>>, IEnumerable
{
private Dictionary<string, object> lookup = new Dictionary<string, object>();
private string[] keyOrder;
public object this[string key]
{
get
{
return lookup[key];
}
set
{
throw new NotImplementedException();
}
}
public int Count => keyOrder.Length;
public bool IsReadOnly => true;
public ICollection<string> Keys => new List<string>(keyOrder);
public ICollection<object> Values => new List<object>(keyOrder.Select((string key) => lookup[key]));
public ReadOnlyOrderedDictionary(Dictionary<string, object> lookup, List<string> keyOrder)
{
this.lookup = lookup;
this.keyOrder = keyOrder.ToArray();
}
public bool Contains(KeyValuePair<string, object> item)
{
return lookup.Contains(item);
}
public bool ContainsKey(string key)
{
return lookup.ContainsKey(key);
}
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{
return lookup.GetEnumerator();
}
public bool TryGetValue(string key, out object value)
{
return lookup.TryGetValue(key, out value);
}
IEnumerator IEnumerable.GetEnumerator()
{
return lookup.GetEnumerator();
}
public void CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
{
Dictionary<string, KeyValuePair<string, object>> dictionary = new Dictionary<string, KeyValuePair<string, object>>();
foreach (KeyValuePair<string, object> item in lookup)
{
dictionary.Add(item.Key, item);
}
string[] array2 = keyOrder;
foreach (string key in array2)
{
array[arrayIndex++] = dictionary[key];
}
}
public void Add(KeyValuePair<string, object> item)
{
throw new NotImplementedException();
}
public void Add(string key, object value)
{
throw new NotImplementedException();
}
public void Clear()
{
throw new NotImplementedException();
}
public bool Remove(KeyValuePair<string, object> item)
{
throw new NotImplementedException();
}
public bool Remove(string key)
{
throw new NotImplementedException();
}
}
private string text;
private HashSet<JsonOption> options = new HashSet<JsonOption>();
private static readonly IFormatProvider EN_US = CultureInfo.GetCultureInfo("en-us");
private static readonly NumberStyles DOUBLE_FLAG = NumberStyles.Float;
public JsonParser(string text)
{
this.text = text;
}
public JsonParser AddOption(JsonOption option)
{
options.Add(option);
return this;
}
public IDictionary<string, object> ParseAsDictionary()
{
if (options.Contains(JsonOption.ALLOW_NON_OBJECT_AS_ROOT))
{
throw new InvalidOperationException("Cannot use ParseAsDictionary if ALLOW_NON_OBJECT_AS_ROOT option is enabled.");
}
return (IDictionary<string, object>)Parse();
}
public object Parse()
{
try
{
TokenStream tokenStream = new TokenStream(text, options.Contains(JsonOption.ALLOW_COMMENTS));
if (tokenStream.Length == 0)
{
throw new JsonParserException("Content is blank.");
}
object obj = ParseThing(tokenStream);
if (!options.Contains(JsonOption.ALLOW_NON_OBJECT_AS_ROOT) && !(obj is IDictionary<string, object>))
{
tokenStream.Reset();
tokenStream.PopExpected("{");
}
return obj;
}
catch (JsonParserException ex)
{
if (options.Contains(JsonOption.FAIL_SILENTLY))
{
return null;
}
throw ex;
}
}
private object ParseThing(TokenStream tokens)
{
string text = tokens.PeekValue();
switch (text)
{
case "true":
case "false":
tokens.Pop();
return text == "true";
case "null":
tokens.Pop();
return null;
default:
{
if (options.Contains(JsonOption.ALLOW_PYTHON_PRIMITIVES))
{
switch (text)
{
case "True":
case "False":
tokens.Pop();
return text == "True";
case "None":
tokens.Pop();
return null;
}
}
char c = text[0];
switch (c)
{
case '"':
case '\'':
return ParseString(tokens.Pop(), allowUnquoted: false);
case '{':
return ParseDictionary(tokens);
case '[':
return ParseList(tokens);
default:
if (c >= '0' && c <= '9')
{
if (Enumerable.Contains(text, '.'))
{
return ParseFloat(tokens.Pop());
}
return ParseInteger(tokens.Pop());
}
if (c == '.')
{
return ParseFloat(tokens.Pop());
}
throw tokens.Pop().Throw("Unrecognized/unexpected value: '" + text + "'");
}
}
}
}
private int ParseInteger(Token token)
{
if (int.TryParse(token.Value, out var result))
{
return result;
}
throw token.Throw("Unrecognized integer value: '" + result + "'");
}
private double ParseFloat(Token token)
{
if (token.Value[0] == '.' && !options.Contains(JsonOption.ALLOW_DECIMAL_AS_FLOAT_FIRST_CHAR))
{
token.Throw("Invalid format for decimal. Use ALLOW_DECIMAL_AS_FLOAT_FIRST_CHAR or add '0' prefix before decimal.");
}
if (double.TryParse(token.Value, DOUBLE_FLAG, EN_US, out var result))
{
return result;
}
throw token.Throw("Unrecognized float value: '" + token.Value + "'");
}
private char GetUnicodeChar(string fourDigitHex)
{
if (int.TryParse(fourDigitHex, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var result))
{
return (char)result;
}
throw new JsonParserException("Could not parse unicode escape sequence: \"\\u" + fourDigitHex + "\"");
}
private string ParseString(Token token, bool allowUnquoted)
{
string value = token.Value;
char c = value[0];
if (c == '"' || c == '\'')
{
StringBuilder stringBuilder = new StringBuilder();
int num = value.Length - 1;
for (int i = 1; i < num; i++)
{
char c2 = value[i];
if (c2 == '\\')
{
if (i + 1 >= num)
{
throw token.Throw("Trailing backslash in string.");
}
switch (value[i + 1])
{
case '\\':
stringBuilder.Append('\\');
break;
case 'r':
stringBuilder.Append('\r');
break;
case 'n':
stringBuilder.Append('\n');
break;
case '0':
stringBuilder.Append('\0');
break;
case 't':
stringBuilder.Append('\t');
break;
case '\'':
stringBuilder.Append('\'');
break;
case '"':
stringBuilder.Append('"');
break;
case 'u':
if (i + 5 >= num)
{
throw token.Throw("Unicode escape sequence is invalid.");
}
stringBuilder.Append(GetUnicodeChar(value.Substring(i + 2, 4)));
i += 4;
break;
default:
throw token.Throw("Unrecognized escape sequence: '\\" + value[i + 1] + "'.");
}
i++;
}
else
{
stringBuilder.Append(c2);
}
}
return stringBuilder.ToString();
}
if (allowUnquoted)
{
return value;
}
throw token.Throw("Found unquoted string: " + value);
}
private object[] ParseList(TokenStream tokens)
{
List<object> list = new List<object>();
tokens.PopExpected("[");
bool flag = true;
Token token = null;
while (!tokens.PopIfPresent("]"))
{
if (!flag)
{
tokens.PopExpected(",");
}
list.Add(ParseThing(tokens));
token = tokens.Peek();
flag = tokens.PopIfPresent(",");
}
if (flag && list.Count > 0 && !options.Contains(JsonOption.ALLOW_TRAILING_COMMA))
{
throw token.Throw("Unexpected comma at end of list. Remove or use ALLOW_TRAILING_COMMA option.");
}
return list.ToArray();
}
private ReadOnlyOrderedDictionary ParseDictionary(TokenStream tokens)
{
List<string> list = new List<string>();
Dictionary<string, object> dictionary = new Dictionary<string, object>();
tokens.PopExpected("{");
bool flag = true;
bool allowUnquoted = options.Contains(JsonOption.ALLOW_NON_QUOTED_KEYS);
Token token = null;
while (!tokens.PopIfPresent("}"))
{
if (!flag)
{
tokens.PopExpected(",");
}
Token token2 = tokens.Peek();
string text = ParseString(tokens.Pop(), allowUnquoted);
tokens.PopExpected(":");
object value = ParseThing(tokens);
if (dictionary.ContainsKey(text))
{
if (options.Contains(JsonOption.OVERWRITE_DUPLICATE_KEYS))
{
dictionary[text] = value;
}
else
{
token2.Throw("Duplicate object key declaration: '" + text + "'.");
}
}
else
{
list.Add(text);
dictionary[text] = value;
}
token = tokens.Peek();
flag = tokens.PopIfPresent(",");
}
if (flag && list.Count > 0 && !options.Contains(JsonOption.ALLOW_TRAILING_COMMA))
{
throw token.Throw("Unexpected comma at end of object. Remove or use ALLOW_TRAILING_COMMA option.");
}
return new ReadOnlyOrderedDictionary(dictionary, list);
}
}
public class JsonLookup
{
private IDictionary<string, object> root;
public JsonLookup(IDictionary<string, object> root)
{
this.root = root;
}
public string GetAsString(string path, string defaultValue)
{
return GetAsString(path) ?? defaultValue;
}
public string GetAsString(string path)
{
return Get(path) as string;
}
public int GetAsInteger(string path, int defaultValue)
{
object obj = Get(path);
return (obj == null) ? defaultValue : ((int)obj);
}
public int GetAsInteger(string path)
{
return GetAsInteger(path, 0);
}
public double GetAsDouble(string path, double defaultValue)
{
object obj = Get(path);
return (obj == null) ? defaultValue : ((double)obj);
}
public double GetAsDouble(string path)
{
return GetAsDouble(path, 0.0);
}
public bool GetAsBoolean(string path, bool defaultValue)
{
object obj = Get(path);
return (obj == null) ? defaultValue : ((bool)obj);
}
public bool GetAsBoolean(string path)
{
return GetAsBoolean(path, defaultValue: false);
}
public object[] GetAsList(string path)
{
return (Get(path) as object[]) ?? new object[0];
}
public IDictionary<string, object> GetAsDictionary(string path)
{
return (Get(path) as IDictionary<string, object>) ?? new Dictionary<string, object>();
}
public JsonLookup GetAsLookup(string path)
{
return new JsonLookup(GetAsDictionary(path));
}
public JsonLookup[] GetAsListOfLookups(string path)
{
return (from d in GetAsList(path).OfType<IDictionary<string, object>>()
select new JsonLookup(d)).ToArray();
}
public object Get(string path)
{
string[] array = path.Split(new char[1] { '.' });
IDictionary<string, object> dictionary = root;
for (int i = 0; i < array.Length; i++)
{
if (dictionary.ContainsKey(array[i]))
{
if (i == array.Length - 1)
{
return dictionary[array[i]];
}
if (dictionary[array[i]] is IDictionary<string, object>)
{
dictionary = (IDictionary<string, object>)dictionary[array[i]];
continue;
}
return null;
}
return null;
}
return null;
}
}
}