123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728 |
- /**
- * Copyright(c) Live2D Inc. All rights reserved.
- *
- * Use of this source code is governed by the Live2D Open Software license
- * that can be found at http: //live2d.com/eula/live2d-open-software-license-agreement_en.html.
- */
- using System;
- using System.Collections.Generic;
- using System.Text;
- namespace Live2D.Cubism.Framework.Json
- {
- /// <summary>
- /// Cubism json parser for loading the configuration file etc.
- ///
- /// Minimal lightweight JSON parser that only supports Ascii characters.
- /// Specification is a subset of JSON.
- ///
- /// Unsupported item.
- /// - Non-ASCII characters such as Japanese.
- /// - Exponential representation by e.
- /// </summary>
- public class CubismJsonParser
- {
- #region variable
- /// <summary>
- /// Array of buffer.
- /// </summary>
- private char[] buffer;
- /// <summary>
- /// Length of buffer.
- /// </summary>
- private int length;
- /// <summary>
- /// For error message.
- /// </summary>
- private int line_count = 0;
- /// <summary>
- /// Root node.
- /// </summary>
- private Value root;
- #endregion
- /// <summary>
- /// Constructor.
- /// </summary>
- /// <param name"jsonBytes">Byte data.</param>
- public CubismJsonParser(char[] jsonBytes)
- {
- this.buffer = jsonBytes;
- this.length = jsonBytes.Length;
- }
- #region Parse Functionn
- /// <summary>
- /// Parse JSON.
- /// </summary>
- /// <returns>Value of parsed from JSON.</returns>
- public Value Parse()
- // throws Exception.
- {
- try
- {
- var ret = new int[1];
- root = ParseValue(buffer, length, 0, ret);
- return root;
- }
- catch (Exception e)
- {
- throw new Exception("json error " + "@line:" + line_count + " / " + e.Message, e);
- }
- }
- /// <summary>
- /// Parse JSON from byte data.
- /// </summary>
- /// <param name="jsonBytes">Byte data.</param>
- /// <returns>Value of parsed from JSON.</returns>
- public static Value ParseFromBytes(char[] jsonBytes)
- // throws Exception.
- {
- var jp = new CubismJsonParser(jsonBytes);
- var ret = jp.Parse();
- return ret;
- }
- /// <summary>
- /// Parse JSON from string data.
- /// </summary>
- /// <param name="jsonString">String data.</param>
- /// <returns>Value of parsed from JSON.</returns>
- public static Value ParseFromString(String jsonString)
- // throws Exception.
- {
- var buffer = jsonString.ToCharArray();
- var jp = new CubismJsonParser(buffer);
- var ret = jp.Parse();
- return ret;
- }
- /// <summary>
- /// Parse till next.
- /// </summary>
- /// <param name="buffer">json data buffer.</param>
- /// <param name="length">json data buffer length.</param>
- /// <param name="pos">Parse position.</param>
- /// <param name="endPos">End position.</param>
- /// <returns>String of parsed from JSON.</returns>
- private static String ParseString(char[] str, int length, int pos, int[] endPos)
- // throws Exception.
- {
- char c, c2;
- StringBuilder stringBuffer = null;
- var startPos = pos; // start pos of the word which is not in sbuf
- for (var i = pos; i < length; i++)
- {
- c = (char)(str[i] & 0xFF);
- switch (c)
- {
- case '\"': // end " , escape char never comes here.
- endPos[0] = i + 1; // next word of "
- if (stringBuffer != null)
- {
- if (i - 1 > startPos) stringBuffer.Append(new string(str, startPos, i - 1 - startPos)); // regist till prev char
- return stringBuffer.ToString();
- }
- else
- {
- return new string(str, pos, i - pos);
- }
- case '\\': // escape
- if (stringBuffer == null)
- {
- stringBuffer = new StringBuilder();
- }
- if (i > startPos) stringBuffer.Append(new string(str, startPos, i - startPos)); // regist till prev char
- i++; // 2 chars
- if (i < length)
- {
- c2 = (char)(str[i] & 0xFF);
- switch (c2)
- {
- case '\\': stringBuffer.Append('\\'); break;
- case '\"': stringBuffer.Append('\"'); break;
- case '/': stringBuffer.Append('/'); break;
- case 'b': stringBuffer.Append('\b'); break;
- case 'f': stringBuffer.Append('\f'); break;
- case 'n': stringBuffer.Append('\n'); break;
- case 'r': stringBuffer.Append('\r'); break;
- case 't': stringBuffer.Append('\t'); break;
- case 'u':
- throw new Exception("parse string/unicode escape not supported");
- }
- }
- else
- {
- throw new Exception("parse string/escape error");
- }
- startPos = i + 1; // after next to escape char (2chars)
- break;
- }
- }
- throw new Exception("parse string/illegal end");
- }
- /// <summary>
- /// Parse object, not include { at pos.
- /// </summary>
- /// <param name="buffer">json data buffer.</param>
- /// <param name="length">json data buffer length.</param>
- /// <param name="pos">Parse position.</param>
- /// <param name="endPos">End position.</param>
- /// <returns>Value of parsed from JSON.</returns>
- private Value ParseObject(char[] buffer, int length, int pos, int[] endPos)
- // throws Exception.
- {
- var ret = new Dictionary<String, Value>();
- // key : value ,
- String key = null;
- char c;
- var i = pos;
- var ret_endPos = new int[1];
- var ok = false;
- // loop till , is lasting
- for (; i < length; i++)
- {
- // FOR_LOOP1:
- for (; i < length; i++)
- {
- c = (char)(buffer[i] & 0xFF);
- switch (c)
- {
- case '\"':
- key = ParseString(buffer, length, i + 1, ret_endPos);
- i = ret_endPos[0];
- ok = true;
- goto EXIT_FOR_LOOP1;
- case '}': endPos[0] = i + 1; return new Value(ret); // empty
- case ':': throw new Exception("illegal ':' position");
- default: break; // skip char
- }
- }
- EXIT_FOR_LOOP1:
- if (!ok)
- {
- throw new Exception("key not found");
- }
- ok = false;
- // check :
- // FOR_LOOP2:
- for (; i < length; i++)
- {
- c = (char)(buffer[i] & 0xFF);
- switch (c)
- {
- case ':': ok = true; i++; goto EXIT_FOR_LOOP2;
- case '}': throw new Exception("illegal '}' position");
- case '\n': line_count++; break;
- default: break; // skip char
- }
- }
- EXIT_FOR_LOOP2:
- if (!ok)
- {
- throw new Exception("':' not found");
- }
- // check :
- Value value = ParseValue(buffer, length, i, ret_endPos);
- i = ret_endPos[0];
- ret.Add(key, value);
- // FOR_LOOP3:
- for (; i < length; i++)
- {
- c = (char)(buffer[i] & 0xFF);
- switch (c)
- {
- case ',': goto EXIT_FOR_LOOP3; // next key, value
- case '}': endPos[0] = i + 1; return new Value(ret); //finished
- case '\n': line_count++; break;
- default: break; // skip
- }
- }
- EXIT_FOR_LOOP3: ;
- }
- throw new Exception("illegal end of ParseObject");
- }
- /// <summary>
- /// Parse Array, not include first[ at pos.
- /// </summary>
- /// <param name="buffer">json data buffer.</param>
- /// <param name="length">json data buffer length.</param>
- /// <param name="pos">Parse position.</param>
- /// <param name="endPos">End position.</param>
- /// <returns>Value of parsed from JSON.</returns>
- private Value ParseArray(char[] buffer, int length, int pos, int[] endPos)
- // throws Exception.
- {
- var ret = new List<Value>();
- var i = pos;
- char c;
- var ret_endPos = new int[1];
- // loop till, is lasting
- for (; i < length; i++)
- {
- // check :
- var value = ParseValue(buffer, length, i, ret_endPos);
- i = ret_endPos[0];
- if (value != null)
- {
- ret.Add(value);
- }
- // FOR_LOOP3:
- for (; i < length; i++)
- {
- c = (char)(buffer[i] & 0xFF);
- switch (c)
- {
- case ',': goto EXIT_FOR_LOOP3; // next key value
- case ']': endPos[0] = i + 1; return new Value(ret); // finish
- case '\n': line_count++; break;
- default: break; // skip
- }
- }
- EXIT_FOR_LOOP3: ;
- }
- throw new Exception("illegal end of ParseObject");
- }
- /// <summary>
- /// Parse double.
- /// </summary>
- /// <param name="buffer">json data buffer.</param>
- /// <param name="length">json data buffer length.</param>
- /// <param name="pos">Parse position.</param>
- /// <param name="endPos">End position.</param>
- /// <returns>Double of parsed from JSON.</returns>
- public static double strToDouble(char[] str, int length, int pos, int[] endPos)
- {
- // int length = str.length ;
- var i = pos;
- var minus = false; // minus flag
- var period = false;
- var v1 = 0.0;
- // check minus
- var c = (char)(str[i] & 0xFF);
- if(c == '-')
- {
- minus = true;
- i++;
- }
- // check integer part
- // FOR_LOOP:
- for (; i < length; i++)
- {
- c = (char)(str[i] & 0xFF);
- switch (c)
- {
- case '0': v1 = v1 * 10; break;
- case '1': v1 = v1 * 10 + 1; break;
- case '2': v1 = v1 * 10 + 2; break;
- case '3': v1 = v1 * 10 + 3; break;
- case '4': v1 = v1 * 10 + 4; break;
- case '5': v1 = v1 * 10 + 5; break;
- case '6': v1 = v1 * 10 + 6; break;
- case '7': v1 = v1 * 10 + 7; break;
- case '8': v1 = v1 * 10 + 8; break;
- case '9': v1 = v1 * 10 + 9; break;
- case '.':
- period = true;
- i++;
- goto EXIT_FOR_LOOP;
- default: // new line code , and delim
- goto EXIT_FOR_LOOP;
- }
- }
- EXIT_FOR_LOOP:
- // check floating point part
- if (period)
- {
- var mul = 0.1;
- // FOR_LOOP2:
- for (; i < length; i++)
- {
- c = (char)(str[i] & 0xFF);
- switch (c)
- {
- case '0': break;
- case '1': v1 += mul * 1; break;
- case '2': v1 += mul * 2; break;
- case '3': v1 += mul * 3; break;
- case '4': v1 += mul * 4; break;
- case '5': v1 += mul * 5; break;
- case '6': v1 += mul * 6; break;
- case '7': v1 += mul * 7; break;
- case '8': v1 += mul * 8; break;
- case '9': v1 += mul * 9; break;
- default: // new line code, and delim
- goto EXIT_FOR_LOOP2;
- }
- mul *= 0.1;
- }
- EXIT_FOR_LOOP2:;
- }
- if (minus)
- {
- v1 = -v1;
- }
- endPos[0] = i;
- return v1;
- }
- /// <summary>
- /// Parse one Value(float, String, Object, Array, null, true, false).
- /// </summary>
- /// <param name="buffer">json data buffer.</param>
- /// <param name="length">json data buffer length.</param>
- /// <param name="pos">Parse position.</param>
- /// <param name="endPos">End position.</param>
- /// <returns>Value of parsed from JSON.</returns>
- private Value ParseValue(char[] buffer, int length, int pos, int[] endPos)
- // throws Exception.
- {
- Value obj;
- var i = pos;
- for (; i < length; i++)
- {
- var c = (char)(buffer[i] & 0xFF);
- switch (c)
- {
- case '-':
- case '.':
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- var f = strToDouble(buffer, length, i, endPos);
- return new Value(f);
- case '\"':
- obj = new Value(ParseString(buffer, length, i + 1, endPos)); // next to \"
- return obj;
- case '[':
- obj = ParseArray(buffer, length, i + 1, endPos);
- return obj;
- case ']': // It is illegal } but skip it. There seems to be unnecessary at the end of the array
- // obj = null;
- endPos[0] = i; // Reprocess the same letters
- return null;
- case '{':
- obj = ParseObject(buffer, length, i + 1, endPos);
- return obj;
- case 'n': // null
- if (i + 3 < length) obj = null;
- else throw new Exception("parse null");
- return obj;
- case 't': // true
- if (i + 3 < length) obj = new Value(true);
- else throw new Exception("parse true");
- return obj;
- case 'f': // false
- if (i + 4 < length) obj = new Value(false);
- else throw new Exception("parse false");
- return obj;
- case ',': // Array separator
- throw new Exception("illegal ',' position");
- case '\n': line_count++;
- break;
- case ' ':
- case '\t':
- case '\r':
- default: // skip
- break;
- }
- }
- // throw new Exception("illegal end of value");
- return null;
- }
- #endregion
- }
- /// <summary>
- /// Json value.
- /// </summary>
- public class Value
- {
- private Object _object;
- /// <summary>
- /// Get value.
- /// </summary>
- /// <returns>The JSON value.</returns>
- public Value(Object obj)
- {
- this._object = obj;
- }
- #region toString
- /// <summary>
- /// Value to string.
- /// </summary>
- /// <returns>Value of string type.</returns>
- public string toString()
- {
- return toString("");
- }
- /// <summary>
- /// Value to string.
- /// </summary>
- /// <returns>Value of string type.</returns>
- public string toString(string indent)
- {
- if (_object is string)
- {
- return (string)_object;
- }
- //------------ List ------------
- else if (_object is List<Value>)
- {
- string ret = indent + "[\n";
- foreach (Value v in ((List<Value>)_object))
- {
- ret += indent + " " + v.toString(indent + " ") + "\n";
- }
- ret += indent + "]\n";
- return ret;
- }
- //------------ Dictionary ------------
- else if (_object is Dictionary<string, Value>)
- {
- string ret = indent + "{\n";
- Dictionary<string, Value> vmap = (Dictionary<string, Value>)_object;
- foreach (KeyValuePair<string, Value> pair in vmap)
- {
- Value v = pair.Value;
- ret += indent + " " + pair.Key + " : " + v.toString(indent + " ") + "\n";
- }
- ret += indent + "}\n";
- return ret;
- }
- else
- {
- return "" + _object;
- }
- }
- #endregion
- #region toInt
- /// <summary>
- /// Value to int.
- /// </summary>
- /// <returns>Value of int type.</returns>
- public int toInt()
- {
- return toInt(0);
- }
- /// <summary>
- /// Value to int.
- /// </summary>
- /// <param name="defaultValue">Default value.</param>
- /// <returns>Value of int type.</returns>
- public int toInt(int defaultValue)
- {
- return (_object is Double) ? (int)((Double)_object) : defaultValue;
- }
- #endregion
- #region ToFloat
- /// <summary>
- /// Value to float.
- /// </summary>
- /// <returns>Value of float type.</returns>
- public float ToFloat()
- {
- return ToFloat(0);
- }
- /// <summary>
- /// Value to float.
- /// </summary>
- /// <param name="defaultValue">Default value.</param>
- /// <returns>Value of float type.</returns>
- public float ToFloat(float defaultValue)
- {
- return (_object is Double) ? (float)((Double)_object) : defaultValue;
- }
- #endregion
- #region ToDouble
- /// <summary>
- /// Value to double.
- /// </summary>
- /// <returns>Value of double type.</returns>
- public double ToDouble()
- {
- return ToDouble(0);
- }
- /// <summary>
- /// Value to double.
- /// </summary>
- /// <param name="defaultValue">Default value.</param>
- /// <returns>Value of double type.</returns>
- public double ToDouble(double defaultValue)
- {
- return (_object is Double) ? ((Double)_object) : defaultValue;
- }
- #endregion
- #region toArray
- /// <summary>
- /// Get list.
- /// </summary>
- /// <param name="defaultValue">Default value.</param>
- /// <returns>Value list.</returns>
- public List<Value> GetVector(List<Value> defalutV)
- {
- return (_object is List<Value>) ? (List<Value>)_object : defalutV;
- }
- /// <summary>
- /// Get from list.
- /// </summary>
- /// <param name="index">Value index in list.</param>
- /// <returns>Value from list.</returns>
- public Value Get(int index)
- {
- return (_object is List<Value>) ? (Value)((List<Value>)_object)[index] : null;
- }
- #endregion
- #region toDictionary
- /// <summary>
- /// Get Value of dictionary type.
- /// </summary>
- /// <param name="defaultValue">Default value.</param>
- /// <returns>Value of dictionary type.</returns>
- public Dictionary<string, Value> GetMap(Dictionary<string, Value> defalutV)
- {
- return (_object is Dictionary<string, Value>) ? (Dictionary<string, Value>)_object : defalutV;
- }
- /// <summary>
- /// Get data from dictionary.
- /// </summary>
- /// <param name="key">key.</param>
- /// <returns>Key value from dictionary.</returns>
- public Value Get(string key)
- {
- if(_object is Dictionary<string, Value>)
- {
- if (((Dictionary<string, Value>)_object).ContainsKey(key)) return (Value)((Dictionary<string, Value>)_object)[key];
- }
- return null;
- }
- /// <summary>
- /// Get key list from dictionary.
- /// </summary>
- /// <returns>Key list.</returns>
- public List<string> KeySet()
- {
- return (_object is Dictionary<string, Value>) ? new List<string>(((Dictionary<string, Value>)_object).Keys) : null;
- }
- /// <summary>
- /// Get dictionary.
- /// </summary>
- /// <returns>Value of dictionary type.</returns>
- public Dictionary<string, Value> ToMap()
- {
- return (_object is Dictionary<string, Value>) ? (Dictionary<string, Value>)_object: null;
- }
- #endregion
- #region check type
- /// <summary>
- /// Confirm the type.
- /// </summary>
- public bool isNull() { return _object == null; }
- public bool isBoolean() { return _object is Boolean; }
- public bool isDouble() { return _object is Double; }
- public bool isString() { return _object is string; }
- public bool isArray() { return _object is List<Value>; }
- public bool isMap() { return _object is Dictionary<string, Value>; }
- #endregion
- }
- }
|