| 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    }}
 |