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.InteropServices;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Necro.UI;
using SimpleJSON;
using UnityEngine;
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("JSONLocalizeAPI")]
[assembly: ComVisible(false)]
[assembly: AssemblyDescription("JSON-based localization library for Necropolis mods")]
[assembly: AssemblyTrademark("")]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCopyright("Copyright © KoMiKoZa 2025")]
[assembly: CompilationRelaxations(8)]
[assembly: Guid("71311014-fbb4-4e33-9234-e39e359a9e78")]
[assembly: AssemblyCompany("KoMiKoZa")]
[assembly: AssemblyProduct("JSONLocalizeAPI")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace KoMiKoZa.Necropolis.JSONLocalizeAPI
{
internal class TranslationCache
{
public Dictionary<string, List<Dictionary<string, string>>> ArrayTranslations;
public Dictionary<string, Dictionary<string, string>> ObjectTranslations;
public Dictionary<string, int> LastIndexByArray;
public string CurrentLang;
public TranslationCache()
{
ArrayTranslations = new Dictionary<string, List<Dictionary<string, string>>>();
ObjectTranslations = new Dictionary<string, Dictionary<string, string>>();
LastIndexByArray = new Dictionary<string, int>();
CurrentLang = "en";
}
}
public static class JSONLocalizeAPI
{
private static Dictionary<Assembly, TranslationCache> translationsByAssembly = new Dictionary<Assembly, TranslationCache>();
public static string GetRandomFromArray(Assembly callingAssembly, string arrayKey, string fallback)
{
try
{
if ((object)callingAssembly == null)
{
LogWarning("GetRandomFromArray: callingAssembly is null");
return fallback;
}
TranslationCache orLoadTranslations = GetOrLoadTranslations(callingAssembly);
if (orLoadTranslations == null || !orLoadTranslations.ArrayTranslations.ContainsKey(arrayKey))
{
if (JSONLocalizePlugin.DebugMode.Value)
{
LogInfo($"Array key '{arrayKey}' not found in {callingAssembly.GetName().Name}");
}
return fallback;
}
List<Dictionary<string, string>> list = orLoadTranslations.ArrayTranslations[arrayKey];
if (list.Count == 0)
{
LogWarning($"Array '{arrayKey}' is empty in {callingAssembly.GetName().Name}");
return fallback;
}
int num = (orLoadTranslations.LastIndexByArray.ContainsKey(arrayKey) ? orLoadTranslations.LastIndexByArray[arrayKey] : (-1));
int num2;
if (list.Count > 1 && num >= 0)
{
do
{
num2 = Random.Range(0, list.Count);
}
while (num2 == num);
}
else
{
num2 = Random.Range(0, list.Count);
}
orLoadTranslations.LastIndexByArray[arrayKey] = num2;
string localizedString = GetLocalizedString(orLoadTranslations, list[num2], fallback);
if (JSONLocalizePlugin.DebugMode.Value)
{
LogInfo($"GetRandomFromArray({arrayKey}): index={num2}, result={localizedString}");
}
return localizedString;
}
catch (Exception arg)
{
LogError($"GetRandomFromArray error: {arg}");
return fallback;
}
}
public static string GetFromObject(Assembly callingAssembly, string objectKey, string fallback)
{
try
{
if ((object)callingAssembly == null)
{
LogWarning("GetFromObject: callingAssembly is null");
return fallback;
}
TranslationCache orLoadTranslations = GetOrLoadTranslations(callingAssembly);
if (orLoadTranslations == null || !orLoadTranslations.ObjectTranslations.ContainsKey(objectKey))
{
if (JSONLocalizePlugin.DebugMode.Value)
{
LogInfo($"Object key '{objectKey}' not found in {callingAssembly.GetName().Name}");
}
return fallback;
}
string localizedString = GetLocalizedString(orLoadTranslations, orLoadTranslations.ObjectTranslations[objectKey], fallback);
if (JSONLocalizePlugin.DebugMode.Value)
{
LogInfo($"GetFromObject({objectKey}): result={localizedString}");
}
return localizedString;
}
catch (Exception arg)
{
LogError($"GetFromObject error: {arg}");
return fallback;
}
}
public static string GetFromArrayIndex(Assembly callingAssembly, string arrayKey, int index, string fallback)
{
try
{
if ((object)callingAssembly == null)
{
LogWarning("GetFromArrayIndex: callingAssembly is null");
return fallback;
}
TranslationCache orLoadTranslations = GetOrLoadTranslations(callingAssembly);
if (orLoadTranslations == null || !orLoadTranslations.ArrayTranslations.ContainsKey(arrayKey))
{
if (JSONLocalizePlugin.DebugMode.Value)
{
LogInfo($"Array key '{arrayKey}' not found in {callingAssembly.GetName().Name}");
}
return fallback;
}
List<Dictionary<string, string>> list = orLoadTranslations.ArrayTranslations[arrayKey];
if (index < 0 || index >= list.Count)
{
LogWarning($"Index {index} out of range for array '{arrayKey}' (count: {list.Count}) in {callingAssembly.GetName().Name}");
return fallback;
}
string localizedString = GetLocalizedString(orLoadTranslations, list[index], fallback);
if (JSONLocalizePlugin.DebugMode.Value)
{
LogInfo($"GetFromArrayIndex({arrayKey}, {index}): result={localizedString}");
}
return localizedString;
}
catch (Exception arg)
{
LogError($"GetFromArrayIndex error: {arg}");
return fallback;
}
}
private static TranslationCache GetOrLoadTranslations(Assembly assembly)
{
if ((object)assembly == null)
{
return null;
}
if (translationsByAssembly.ContainsKey(assembly))
{
return translationsByAssembly[assembly];
}
TranslationCache translationCache = new TranslationCache();
translationsByAssembly[assembly] = translationCache;
try
{
string location = assembly.Location;
string directoryName = Path.GetDirectoryName(location);
string text = Path.Combine(directoryName, "translations.json");
string name = assembly.GetName().Name;
if (JSONLocalizePlugin.DebugMode.Value)
{
LogInfo($"Looking for translations at: {text}");
}
if (!File.Exists(text))
{
LogWarning($"No translations.json found for {name}, using fallbacks");
return translationCache;
}
string text2 = File.ReadAllText(text);
if (JSONLocalizePlugin.DebugMode.Value)
{
LogInfo($"JSON content length for {name}: {text2.Length} chars");
}
JSONNode jSONNode = JSON.Parse(text2);
if (jSONNode == null)
{
LogError($"Failed to parse translations.json for {name}");
return translationCache;
}
LogInfo($"Successfully parsed translations.json for {name}");
JSONNode.Enumerator enumerator = jSONNode.GetEnumerator();
while (enumerator.MoveNext())
{
KeyValuePair<string, JSONNode> current = enumerator.Current;
if (current.Value.IsArray)
{
List<Dictionary<string, string>> list = new List<Dictionary<string, string>>();
JSONNode.Enumerator enumerator2 = current.Value.AsArray.GetEnumerator();
while (enumerator2.MoveNext())
{
JSONNode jSONNode2 = enumerator2.Current;
Dictionary<string, string> dictionary = new Dictionary<string, string>();
JSONNode.Enumerator enumerator3 = jSONNode2.GetEnumerator();
while (enumerator3.MoveNext())
{
KeyValuePair<string, JSONNode> current2 = enumerator3.Current;
dictionary[current2.Key] = current2.Value.Value;
}
list.Add(dictionary);
}
translationCache.ArrayTranslations[current.Key] = list;
if (JSONLocalizePlugin.DebugMode.Value)
{
LogInfo($"Loaded array '{current.Key}' with {list.Count} items for {name}");
}
}
else if (current.Value.IsObject)
{
Dictionary<string, string> dictionary = new Dictionary<string, string>();
JSONNode.Enumerator enumerator2 = current.Value.GetEnumerator();
while (enumerator2.MoveNext())
{
KeyValuePair<string, JSONNode> current2 = enumerator2.Current;
dictionary[current2.Key] = current2.Value.Value;
}
translationCache.ObjectTranslations[current.Key] = dictionary;
if (JSONLocalizePlugin.DebugMode.Value)
{
LogInfo($"Loaded object '{current.Key}' with {dictionary.Count} languages for {name}");
}
}
}
if (JSONLocalizePlugin.DebugMode.Value)
{
LogInfo($"Loaded translations for {name}: {translationCache.ArrayTranslations.Count} arrays, {translationCache.ObjectTranslations.Count} objects");
}
}
catch (Exception arg)
{
LogError($"Error loading translations for {assembly.GetName().Name}: {arg}");
}
return translationCache;
}
private static string GetLocalizedString(TranslationCache cache, Dictionary<string, string> dict, string fallback)
{
if (dict == null || dict.Count == 0)
{
return fallback;
}
string currentLanguage = GetCurrentLanguage();
if (currentLanguage != cache.CurrentLang)
{
cache.CurrentLang = currentLanguage;
if (JSONLocalizePlugin.DebugMode.Value)
{
LogInfo($"Language changed to: {currentLanguage}");
}
}
if (dict.ContainsKey(currentLanguage))
{
string text = dict[currentLanguage];
if (!string.IsNullOrEmpty(text))
{
return text;
}
}
if (dict.ContainsKey("en"))
{
string text = dict["en"];
if (!string.IsNullOrEmpty(text))
{
return text;
}
}
foreach (KeyValuePair<string, string> item in dict)
{
if (!string.IsNullOrEmpty(item.Value))
{
return item.Value;
}
}
return fallback;
}
private static string GetCurrentLanguage()
{
try
{
string currentLang = Strings.CurrentLang;
if (string.IsNullOrEmpty(currentLang))
{
return "en";
}
return currentLang;
}
catch
{
return "en";
}
}
private static void LogInfo(string message)
{
if (JSONLocalizePlugin.Logger != null)
{
JSONLocalizePlugin.Logger.LogInfo((object)$"[LOCALIZE] {message}");
}
}
private static void LogWarning(string message)
{
if (JSONLocalizePlugin.Logger != null)
{
JSONLocalizePlugin.Logger.LogWarning((object)$"[LOCALIZE] {message}");
}
}
private static void LogError(string message)
{
if (JSONLocalizePlugin.Logger != null)
{
JSONLocalizePlugin.Logger.LogError((object)$"[LOCALIZE] {message}");
}
}
}
[BepInPlugin("komikoza.necropolis.jsonlocalizeapi", "JSONLocalizeAPI", "1.0.0")]
public class JSONLocalizePlugin : BaseUnityPlugin
{
private const string GUID = "komikoza.necropolis.jsonlocalizeapi";
private const string NAME = "JSONLocalizeAPI";
private const string VERSION = "1.0.0";
internal static ManualLogSource Logger;
internal static ConfigEntry<bool> DebugMode;
public static JSONLocalizePlugin Instance { get; private set; }
private void Awake()
{
Instance = this;
Logger = ((BaseUnityPlugin)this).Logger;
DebugMode = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugMode", false, "Enable debug logging for translation loading and lookups");
Logger.LogInfo((object)"================================================");
Logger.LogInfo((object)string.Format("[{0}] v{1} loaded!", "JSONLocalizeAPI", "1.0.0"));
Logger.LogInfo((object)" - JSON-based localization for mod content");
Logger.LogInfo((object)"================================================");
}
}
}
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 string 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 = false;
public static bool longAsString = false;
public static bool allowLineComments = true;
[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 C in Children)
{
foreach (JSONNode deepChild in C.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, NumberStyles.Float, CultureInfo.InvariantCulture, out result))
{
return result;
}
return 0.0;
}
set
{
Value = value.ToString(CultureInfo.InvariantCulture);
}
}
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 long AsLong
{
get
{
long result = 0L;
if (long.TryParse(Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result))
{
return result;
}
return 0L;
}
set
{
Value = value.ToString(CultureInfo.InvariantCulture);
}
}
public virtual ulong AsULong
{
get
{
ulong result = 0uL;
if (ulong.TryParse(Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result))
{
return result;
}
return 0uL;
}
set
{
Value = value.ToString(CultureInfo.InvariantCulture);
}
}
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 virtual void Clear()
{
}
public virtual JSONNode Clone()
{
return null;
}
public virtual bool HasKey(string aKey)
{
return false;
}
public virtual JSONNode GetValueOrDefault(string aKey, JSONNode aDefault)
{
return aDefault;
}
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 (s == null) ? ((JSONNode)JSONNull.CreateOrGet()) : ((JSONNode)new JSONString(s));
}
public static implicit operator string(JSONNode d)
{
return (d == null) ? null : d.Value;
}
public static implicit operator JSONNode(double n)
{
return new JSONNumber(n);
}
public static implicit operator double(JSONNode d)
{
return (d == null) ? 0.0 : d.AsDouble;
}
public static implicit operator JSONNode(float n)
{
return new JSONNumber(n);
}
public static implicit operator float(JSONNode d)
{
return (d == null) ? 0f : d.AsFloat;
}
public static implicit operator JSONNode(int n)
{
return new JSONNumber(n);
}
public static implicit operator int(JSONNode d)
{
return (!(d == null)) ? d.AsInt : 0;
}
public static implicit operator JSONNode(long n)
{
if (longAsString)
{
return new JSONString(n.ToString(CultureInfo.InvariantCulture));
}
return new JSONNumber(n);
}
public static implicit operator long(JSONNode d)
{
return (d == null) ? 0 : d.AsLong;
}
public static implicit operator JSONNode(ulong n)
{
if (longAsString)
{
return new JSONString(n.ToString(CultureInfo.InvariantCulture));
}
return new JSONNumber(n);
}
public static implicit operator ulong(JSONNode d)
{
return (d == null) ? 0 : d.AsULong;
}
public static implicit operator JSONNode(bool b)
{
return new JSONBool(b);
}
public static implicit operator bool(JSONNode d)
{
return !(d == null) && d.AsBool;
}
public static implicit operator JSONNode(KeyValuePair<string, JSONNode> aKeyValue)
{
return aKeyValue.Value;
}
public static bool operator ==(JSONNode a, object b)
{
if (object.ReferenceEquals(a, b))
{
return true;
}
bool flag = a is JSONNull || object.ReferenceEquals(a, null) || a is JSONLazyCreator;
bool flag2 = b is JSONNull || object.ReferenceEquals(b, null) || b is JSONLazyCreator;
if (flag && flag2)
{
return true;
}
return !flag && a.Equals(b);
}
public static bool operator !=(JSONNode a, object b)
{
return !(a == b);
}
public override bool Equals(object obj)
{
return object.ReferenceEquals(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 JSONNode ParseElement(string token, bool quoted)
{
if (quoted)
{
return token;
}
if (token.Length <= 5)
{
string text = token.ToLower();
if (text == "false" || text == "true")
{
return text == "true";
}
if (text == "null")
{
return JSONNull.CreateOrGet();
}
}
if (double.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
{
return result;
}
return token;
}
public static JSONNode Parse(string aJSON)
{
Stack<JSONNode> stack = new Stack<JSONNode>();
JSONNode jSONNode = null;
int i = 0;
StringBuilder stringBuilder = new StringBuilder();
string aKey = "";
bool flag = false;
bool flag2 = false;
bool flag3 = 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(aKey, stack.Peek());
}
aKey = "";
stringBuilder.Length = 0;
jSONNode = stack.Peek();
flag3 = false;
break;
case '[':
if (flag)
{
stringBuilder.Append(aJSON[i]);
break;
}
stack.Push(new JSONArray());
if (jSONNode != null)
{
jSONNode.Add(aKey, stack.Peek());
}
aKey = "";
stringBuilder.Length = 0;
jSONNode = stack.Peek();
flag3 = false;
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)
{
jSONNode.Add(aKey, ParseElement(stringBuilder.ToString(), flag2));
}
if (jSONNode != null)
{
jSONNode.Inline = !flag3;
}
flag2 = false;
aKey = "";
stringBuilder.Length = 0;
if (stack.Count > 0)
{
jSONNode = stack.Peek();
}
break;
case ':':
if (flag)
{
stringBuilder.Append(aJSON[i]);
break;
}
aKey = 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)
{
jSONNode.Add(aKey, ParseElement(stringBuilder.ToString(), flag2));
}
flag2 = false;
aKey = "";
stringBuilder.Length = 0;
flag2 = false;
break;
case '\n':
case '\r':
flag3 = true;
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;
case '/':
if (allowLineComments && !flag && i + 1 < aJSON.Length && aJSON[i + 1] == '/')
{
while (++i < aJSON.Length && aJSON[i] != '\n' && aJSON[i] != '\r')
{
}
}
else
{
stringBuilder.Append(aJSON[i]);
}
break;
default:
stringBuilder.Append(aJSON[i]);
break;
case '\ufeff':
break;
}
}
if (flag)
{
throw new Exception("JSON Parse: Quotation marks seems to be messed up.");
}
if (jSONNode == null)
{
return ParseElement(stringBuilder.ToString(), flag2);
}
return jSONNode;
}
}
public class JSONArray : JSONNode
{
private List<JSONNode> m_List = new List<JSONNode>();
private bool inline = false;
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;
}
public override void Clear()
{
m_List.Clear();
}
public override JSONNode Clone()
{
JSONArray jSONArray = new JSONArray();
jSONArray.m_List.Capacity = m_List.Capacity;
foreach (JSONNode item in m_List)
{
if (item != null)
{
jSONArray.Add(item.Clone());
}
else
{
jSONArray.Add(null);
}
}
return jSONArray;
}
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 = false;
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.TryGetValue(aKey, out var value))
{
return value;
}
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> N in m_Dict)
{
KeyValuePair<string, JSONNode> keyValuePair = N;
yield return keyValuePair.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 (aKey != null)
{
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;
}
}
public override void Clear()
{
m_Dict.Clear();
}
public override JSONNode Clone()
{
JSONObject jSONObject = new JSONObject();
foreach (KeyValuePair<string, JSONNode> item in m_Dict)
{
jSONObject.Add(item.Key, item.Value.Clone());
}
return jSONObject;
}
public override bool HasKey(string aKey)
{
return m_Dict.ContainsKey(aKey);
}
public override JSONNode GetValueOrDefault(string aKey, JSONNode aDefault)
{
if (m_Dict.TryGetValue(aKey, out var value))
{
return value;
}
return aDefault;
}
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;
}
public override JSONNode Clone()
{
return new JSONString(m_Data);
}
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 override void Clear()
{
m_Data = "";
}
}
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(CultureInfo.InvariantCulture);
}
set
{
if (double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
{
m_Data = result;
}
}
}
public override double AsDouble
{
get
{
return m_Data;
}
set
{
m_Data = value;
}
}
public override long AsLong
{
get
{
return (long)m_Data;
}
set
{
m_Data = value;
}
}
public override ulong AsULong
{
get
{
return (ulong)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;
}
public override JSONNode Clone()
{
return new JSONNumber(m_Data);
}
internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode)
{
aSB.Append(Value.ToString(CultureInfo.InvariantCulture));
}
private static bool IsNumeric(object value)
{
return 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 || value is byte;
}
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 override void Clear()
{
m_Data = 0.0;
}
}
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;
}
public override JSONNode Clone()
{
return new JSONBool(m_Data);
}
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 override void Clear()
{
m_Data = false;
}
}
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 JSONNode Clone()
{
return CreateOrGet();
}
public override bool Equals(object obj)
{
if (object.ReferenceEquals(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 = null;
private string m_Key = null;
public override JSONNodeType Tag => JSONNodeType.None;
public override JSONNode this[int aIndex]
{
get
{
return new JSONLazyCreator(this);
}
set
{
Set(new JSONArray()).Add(value);
}
}
public override JSONNode this[string aKey]
{
get
{
return new JSONLazyCreator(this, aKey);
}
set
{
Set(new JSONObject()).Add(aKey, value);
}
}
public override int AsInt
{
get
{
Set(new JSONNumber(0.0));
return 0;
}
set
{
Set(new JSONNumber(value));
}
}
public override float AsFloat
{
get
{
Set(new JSONNumber(0.0));
return 0f;
}
set
{
Set(new JSONNumber(value));
}
}
public override double AsDouble
{
get
{
Set(new JSONNumber(0.0));
return 0.0;
}
set
{
Set(new JSONNumber(value));
}
}
public override long AsLong
{
get
{
if (JSONNode.longAsString)
{
Set(new JSONString("0"));
}
else
{
Set(new JSONNumber(0.0));
}
return 0L;
}
set
{
if (JSONNode.longAsString)
{
Set(new JSONString(value.ToString(CultureInfo.InvariantCulture)));
}
else
{
Set(new JSONNumber(value));
}
}
}
public override ulong AsULong
{
get
{
if (JSONNode.longAsString)
{
Set(new JSONString("0"));
}
else
{
Set(new JSONNumber(0.0));
}
return 0uL;
}
set
{
if (JSONNode.longAsString)
{
Set(new JSONString(value.ToString(CultureInfo.InvariantCulture)));
}
else
{
Set(new JSONNumber(value));
}
}
}
public override bool AsBool
{
get
{
Set(new JSONBool(aData: false));
return false;
}
set
{
Set(new JSONBool(value));
}
}
public override JSONArray AsArray => Set(new JSONArray());
public override JSONObject AsObject => Set(new 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 T Set<T>(T aVal) where T : JSONNode
{
if (m_Key == null)
{
m_Node.Add(aVal);
}
else
{
m_Node.Add(m_Key, aVal);
}
m_Node = null;
return aVal;
}
public override void Add(JSONNode aItem)
{
Set(new JSONArray()).Add(aItem);
}
public override void Add(string aKey, JSONNode aItem)
{
Set(new JSONObject()).Add(aKey, aItem);
}
public static bool operator ==(JSONLazyCreator a, object b)
{
if (b == null)
{
return true;
}
return object.ReferenceEquals(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.ReferenceEquals(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);
}
}
}