summaryrefslogtreecommitdiff
path: root/Assets/ProFlares/Editor/FlareJson.cs
diff options
context:
space:
mode:
authorchai <chaifix@163.com>2021-04-21 21:52:56 +0800
committerchai <chaifix@163.com>2021-04-21 21:52:56 +0800
commit6d0c9a214dc0fda264e8588fa02aaa19b0b2cc5f (patch)
tree6a853a0b0cfb5818c864c3794cb58075876c07b0 /Assets/ProFlares/Editor/FlareJson.cs
+init
Diffstat (limited to 'Assets/ProFlares/Editor/FlareJson.cs')
-rw-r--r--Assets/ProFlares/Editor/FlareJson.cs1163
1 files changed, 1163 insertions, 0 deletions
diff --git a/Assets/ProFlares/Editor/FlareJson.cs b/Assets/ProFlares/Editor/FlareJson.cs
new file mode 100644
index 0000000..f49d17f
--- /dev/null
+++ b/Assets/ProFlares/Editor/FlareJson.cs
@@ -0,0 +1,1163 @@
+using System;
+using System.Collections;
+using System.Text;
+using System.Collections.Generic;
+using UnityEngine;
+
+// Source: UIToolkit -- https://github.com/prime31/UIToolkit/blob/master/Assets/Plugins/MiniJSON.cs
+
+// Based on the JSON parser from
+// http://techblog.procurios.nl/k/618/news/view/14605/14863/How-do-I-write-my-own-parser-for-JSON.html
+
+/// <summary>
+/// This class encodes and decodes JSON strings.
+/// Spec. details, see http://www.json.org/
+///
+/// JSON uses Arrays and Objects. These correspond here to the datatypes ArrayList and Hashtable.
+/// All numbers are parsed to doubles.
+/// </summary>
+
+public class FlareJson
+{
+ private const int TOKEN_NONE = 0;
+ private const int TOKEN_CURLY_OPEN = 1;
+ private const int TOKEN_CURLY_CLOSE = 2;
+ private const int TOKEN_SQUARED_OPEN = 3;
+ private const int TOKEN_SQUARED_CLOSE = 4;
+ private const int TOKEN_COLON = 5;
+ private const int TOKEN_COMMA = 6;
+ private const int TOKEN_STRING = 7;
+ private const int TOKEN_NUMBER = 8;
+ private const int TOKEN_TRUE = 9;
+ private const int TOKEN_FALSE = 10;
+ private const int TOKEN_NULL = 11;
+ private const int BUILDER_CAPACITY = 2000;
+
+ /// <summary>
+ /// On decoding, this value holds the position at which the parse failed (-1 = no error).
+ /// </summary>
+ protected static int lastErrorIndex = -1;
+ protected static string lastDecode = "";
+
+ public static Color decodeColor(Hashtable table){
+
+ float r = float.Parse(table["r"].ToString());
+ float g = float.Parse(table["g"].ToString());
+ float b = float.Parse(table["b"].ToString());
+ float a = float.Parse(table["a"].ToString());
+
+ return new Color(r,g,b,a);
+ }
+
+ public static Vector4 decodeVector4(Hashtable table){
+
+ float r = float.Parse(table["r"].ToString());
+ float g = float.Parse(table["g"].ToString());
+ float b = float.Parse(table["b"].ToString());
+ float a = float.Parse(table["a"].ToString());
+
+ return new Vector4(r,g,b,a);
+ }
+
+ public static Vector3 decodeVector3(Hashtable table){
+
+ float r = float.Parse(table["r"].ToString());
+ float g = float.Parse(table["g"].ToString());
+ float b = float.Parse(table["b"].ToString());
+
+ return new Vector4(r,g,b);
+ }
+
+ public static Vector2 decodeVector2(Hashtable table){
+
+ float x = float.Parse(table["x"].ToString());
+ float y = float.Parse(table["y"].ToString());
+
+ return new Vector2(x,y);
+ }
+
+
+ public static AnimationCurve decodeAnimCurve(Hashtable table){
+
+ AnimationCurve curve = new AnimationCurve ();
+
+
+ foreach (System.Collections.DictionaryEntry key in table) {
+
+
+ Hashtable keyTable = (Hashtable)table [key.Key];
+
+
+ float time = float.Parse(keyTable["time"].ToString());
+ float value = float.Parse(keyTable["value"].ToString());
+ float _in = float.Parse(keyTable["in"].ToString());
+ float _out = float.Parse(keyTable["out"].ToString());
+// float tangentMode = float.Parse(keyTable["tangentMode"].ToString());
+
+ Keyframe newKey = new Keyframe(time,value,_in,_out);
+ // newKey.tangentMode = tangentMode;
+
+ curve.AddKey(newKey);
+
+ }
+
+ return curve;
+ }
+
+ public static bool decodeBool(string str){
+
+ int num = int.Parse(str);
+ if(num == 1)
+ return true;
+ else
+ return false;
+ }
+
+ public static void LoadFlareData (ProFlare flare, TextAsset asset)
+ {
+
+ string jsonString = asset.text;
+
+// Debug.Log ("LoadFlareData");
+
+ Hashtable decodedHash = jsonDecode (jsonString) as Hashtable;
+
+ if (decodedHash == null) {
+ Debug.LogWarning ("Unable to parse Json file: " + asset.name);
+ return;
+ }
+
+ Hashtable meta = (Hashtable)decodedHash["meta"];
+
+ flare.GlobalScale = float.Parse(meta["GlobalScale"].ToString());
+
+ flare.GlobalBrightness = float.Parse(meta["GlobalBrightness"].ToString());
+
+ flare.GlobalTintColor = FlareJson.decodeColor((Hashtable)meta["GlobalTintColor"]);
+
+ flare.MultiplyScaleByTransformScale = FlareJson.decodeBool(meta["MultiplyScaleByTransformScale"].ToString());
+
+ //Distance Fall off
+ flare.useMaxDistance = FlareJson.decodeBool(meta["useMaxDistance"].ToString());
+
+ flare.useDistanceScale = FlareJson.decodeBool(meta["useDistanceScale"].ToString());
+
+ flare.useDistanceFade = FlareJson.decodeBool(meta["useDistanceFade"].ToString());
+
+ flare.GlobalMaxDistance = float.Parse(meta["GlobalMaxDistance"].ToString());
+
+
+ //Angle Culling Properties
+ flare.UseAngleLimit = FlareJson.decodeBool(meta["UseAngleLimit"].ToString());
+
+ flare.maxAngle = float.Parse(meta["maxAngle"].ToString());
+
+ flare.UseAngleScale = FlareJson.decodeBool(meta["UseAngleScale"].ToString());
+
+ flare.UseAngleBrightness = FlareJson.decodeBool(meta["UseAngleBrightness"].ToString());
+
+ flare.UseAngleCurve = FlareJson.decodeBool(meta["UseAngleCurve"].ToString());
+
+ flare.AngleCurve = FlareJson.decodeAnimCurve ((Hashtable)meta ["AngleCurve"]);
+
+ // public LayerMask mask = 1;
+
+ flare.RaycastPhysics = FlareJson.decodeBool(meta["RaycastPhysics"].ToString());
+
+ flare.OffScreenFadeDist = float.Parse(meta["OffScreenFadeDist"].ToString());
+
+ flare.useDynamicEdgeBoost = FlareJson.decodeBool(meta["useDynamicEdgeBoost"].ToString());
+
+ flare.DynamicEdgeBoost = float.Parse(meta["DynamicEdgeBoost"].ToString());
+
+ flare.DynamicEdgeBrightness = float.Parse(meta["DynamicEdgeBrightness"].ToString());
+
+ flare.DynamicEdgeRange = float.Parse(meta["DynamicEdgeRange"].ToString());
+
+ flare.DynamicEdgeBias = float.Parse(meta["DynamicEdgeBias"].ToString());
+
+ flare.DynamicEdgeCurve = FlareJson.decodeAnimCurve ((Hashtable)meta ["DynamicEdgeCurve"]);
+
+ flare.useDynamicCenterBoost = FlareJson.decodeBool(meta["useDynamicCenterBoost"].ToString());
+
+ flare.DynamicCenterBoost = float.Parse(meta["DynamicCenterBoost"].ToString());
+
+ flare.DynamicCenterBrightness = float.Parse(meta["DynamicCenterBrightness"].ToString());
+
+ flare.DynamicCenterRange = float.Parse(meta["DynamicCenterRange"].ToString());
+
+ flare.DynamicCenterBias = float.Parse(meta["DynamicCenterBias"].ToString());
+
+ flare.neverCull = FlareJson.decodeBool(meta["neverCull"].ToString());
+
+ flare.Elements.Clear ();
+
+ Hashtable elements = (Hashtable)meta["Elements"];
+
+ foreach (System.Collections.DictionaryEntry item in elements) {
+
+ Hashtable element = (Hashtable)elements[item.Key];
+
+ ProFlareElement elementNew = new ProFlareElement();
+
+ elementNew.Editing = FlareJson.decodeBool(element["Editing"].ToString());
+
+ elementNew.Visible = FlareJson.decodeBool(element["Visible"].ToString());
+
+ elementNew.SpriteName = element["SpriteName"].ToString();
+
+ elementNew.flare = flare;
+
+ elementNew.flareAtlas = flare._Atlas;
+
+ elementNew.Brightness = float.Parse(element["Brightness"].ToString());
+
+ elementNew.Scale = float.Parse(element["Scale"].ToString());
+
+ elementNew.ScaleRandom = float.Parse(element["ScaleRandom"].ToString());
+
+ elementNew.ScaleFinal = float.Parse(element["ScaleFinal"].ToString());
+
+ elementNew.RandomColorAmount = FlareJson.decodeVector4((Hashtable)element["RandomColorAmount"]);
+
+// //Element OffSet Properties
+ elementNew.position = float.Parse(element["position"].ToString());
+
+ elementNew.useRangeOffset = FlareJson.decodeBool(element["useRangeOffset"].ToString());
+
+ elementNew.SubElementPositionRange_Min = float.Parse(element["SubElementPositionRange_Min"].ToString());
+
+ elementNew.SubElementPositionRange_Max = float.Parse(element["SubElementPositionRange_Max"].ToString());
+
+ elementNew.SubElementAngleRange_Min = float.Parse(element["SubElementAngleRange_Min"].ToString());
+
+ elementNew.SubElementAngleRange_Max = float.Parse(element["SubElementAngleRange_Max"].ToString());
+
+ elementNew.OffsetPosition = FlareJson.decodeVector3((Hashtable)element["OffsetPosition"]);
+
+ elementNew.Anamorphic = FlareJson.decodeVector3((Hashtable)element["Anamorphic"]);
+
+ elementNew.OffsetPostion = FlareJson.decodeVector3((Hashtable)element["OffsetPostion"]);
+
+// //Element Rotation Properties
+ elementNew.angle = float.Parse(element["angle"].ToString());
+
+ elementNew.useRandomAngle = FlareJson.decodeBool(element["useRandomAngle"].ToString());
+
+ elementNew.useStarRotation = FlareJson.decodeBool(element["useStarRotation"].ToString());
+
+ elementNew.AngleRandom_Min = float.Parse(element["AngleRandom_Min"].ToString());
+
+ elementNew.AngleRandom_Max = float.Parse(element["AngleRandom_Max"].ToString());
+
+ elementNew.OrientToSource = FlareJson.decodeBool(element["OrientToSource"].ToString());
+
+ elementNew.rotateToFlare = FlareJson.decodeBool(element["rotateToFlare"].ToString());
+
+ elementNew.rotationSpeed = float.Parse(element["rotationSpeed"].ToString());
+
+ elementNew.rotationOverTime = float.Parse(element["rotationOverTime"].ToString());
+
+// //Colour Properties,
+ elementNew.useColorRange = FlareJson.decodeBool(element["useColorRange"].ToString());
+
+ elementNew.OffsetPosition = FlareJson.decodeVector3((Hashtable)element["OffsetPosition"]);
+
+ elementNew.ElementTint = FlareJson.decodeColor((Hashtable)element["ElementTint"]);
+
+ elementNew.SubElementColor_Start = FlareJson.decodeColor((Hashtable)element["SubElementColor_Start"]);
+
+ elementNew.SubElementColor_End = FlareJson.decodeColor((Hashtable)element["SubElementColor_End"]);
+
+// //Scale Curve
+ elementNew.useScaleCurve = FlareJson.decodeBool(element["useScaleCurve"].ToString());
+
+ elementNew.ScaleCurve = FlareJson.decodeAnimCurve ((Hashtable)element ["ScaleCurve"]);
+
+// //Override Properties
+ elementNew.OverrideDynamicEdgeBoost = FlareJson.decodeBool(element["OverrideDynamicEdgeBoost"].ToString());
+
+ elementNew.DynamicEdgeBoostOverride = float.Parse(element["DynamicEdgeBoostOverride"].ToString());
+
+ elementNew.OverrideDynamicCenterBoost = FlareJson.decodeBool(element["OverrideDynamicCenterBoost"].ToString());
+
+ elementNew.DynamicCenterBoostOverride = float.Parse(element["DynamicCenterBoostOverride"].ToString());
+
+ elementNew.OverrideDynamicEdgeBrightness = FlareJson.decodeBool(element["OverrideDynamicEdgeBrightness"].ToString());
+
+ elementNew.DynamicEdgeBrightnessOverride = float.Parse(element["DynamicEdgeBrightnessOverride"].ToString());
+
+ elementNew.OverrideDynamicCenterBrightness = FlareJson.decodeBool(element["OverrideDynamicCenterBrightness"].ToString());
+
+ elementNew.DynamicCenterBrightnessOverride = float.Parse(element["DynamicCenterBrightnessOverride"].ToString());
+
+ elementNew.type = (ProFlareElement.Type)(int.Parse(element["type"].ToString()));
+
+
+ elementNew.size = FlareJson.decodeVector2((Hashtable)element["size"]);
+
+ Hashtable subElements = (Hashtable)element["subElements"];
+
+ if(subElements != null)
+ foreach (System.Collections.DictionaryEntry subItem in subElements) {
+
+ Hashtable subElement = (Hashtable)subElements[subItem.Key];
+
+ SubElement subElementNew = new SubElement();
+
+ subElementNew.color = FlareJson.decodeColor((Hashtable)subElement["color"]);
+
+ subElementNew.position = float.Parse(subElement["position"].ToString());
+
+ subElementNew.offset = FlareJson.decodeVector3((Hashtable)subElement["offset"]);
+
+ subElementNew.angle = float.Parse(subElement["angle"].ToString());
+
+ subElementNew.scale = float.Parse(subElement["scale"].ToString());
+
+ subElementNew.random = float.Parse(subElement["random"].ToString());
+
+ subElementNew.random2 = float.Parse(subElement["random2"].ToString());
+
+ subElementNew.RandomScaleSeed = float.Parse(subElement["RandomScaleSeed"].ToString());
+
+ subElementNew.RandomColorSeedR = float.Parse(subElement["RandomColorSeedR"].ToString());
+
+ subElementNew.RandomColorSeedG = float.Parse(subElement["RandomColorSeedG"].ToString());
+
+ subElementNew.RandomColorSeedB = float.Parse(subElement["RandomColorSeedB"].ToString());
+
+ subElementNew.RandomColorSeedA = float.Parse(subElement["RandomColorSeedA"].ToString());
+
+ elementNew.subElements.Add(subElementNew);
+ }
+
+ bool Found = false;
+
+ for(int i2 = 0; i2 < flare._Atlas.elementsList.Count; i2++){
+ if(elementNew.SpriteName == flare._Atlas.elementsList[i2].name){
+ Found = true;
+ elementNew.elementTextureID = i2;
+ }
+ }
+
+ if(Found)
+ flare.Elements.Add(elementNew);
+ else
+ Debug.LogWarning("ProFlares - Flare Element Missing From Atlas Not Adding - "+elementNew.SpriteName);
+
+ }
+
+ foreach (ProFlareBatch batch in flare.FlareBatches) {
+ batch.dirty = true;
+ }
+ }
+ /// <summary>
+ /// Parse the specified JSon file, loading sprite information for the specified atlas.
+ /// </summary>
+
+ public static void LoadSpriteData (ProFlareAtlas atlas, TextAsset asset)
+ {
+ if (asset == null || atlas == null) return;
+
+ string jsonString = asset.text;
+
+ Hashtable decodedHash = jsonDecode(jsonString) as Hashtable;
+
+ if (decodedHash == null)
+ {
+ Debug.LogWarning("Unable to parse Json file: " + asset.name);
+ return;
+ }
+ List<ProFlareAtlas.Element> oldElements = atlas.elementsList;
+
+ atlas.elementsList = new List<ProFlareAtlas.Element>();
+
+ Vector2 TextureScale = Vector2.one;
+
+ //Find Texture Size
+ Hashtable meta = (Hashtable)decodedHash["meta"];
+ foreach (System.Collections.DictionaryEntry item in meta)
+ {
+ if(item.Key.ToString() == "size"){
+ Hashtable sizeTable = (Hashtable)item.Value;
+
+ TextureScale.x = int.Parse(sizeTable["w"].ToString());
+ TextureScale.y = int.Parse(sizeTable["h"].ToString());
+ }
+ }
+
+ //Debug.LogError(TextureScale);
+
+ Hashtable frames = (Hashtable)decodedHash["frames"];
+ foreach (System.Collections.DictionaryEntry item in frames)
+ {
+ ProFlareAtlas.Element newElement = new ProFlareAtlas.Element();
+ newElement.name = item.Key.ToString();
+
+ bool exists = false;
+
+ // Check to see if this sprite exists
+ foreach (ProFlareAtlas.Element oldSprite in oldElements)
+ {
+ if (oldSprite.name.Equals(newElement.name, StringComparison.OrdinalIgnoreCase))
+ {
+ exists = true;
+ break;
+ }
+ }
+
+ if (!exists)
+ {
+ newElement.name = newElement.name.Replace(".png", "");
+ newElement.name = newElement.name.Replace(".tga", "");
+ newElement.name = newElement.name.Replace(".psd", "");
+ newElement.name = newElement.name.Replace(".PSD", "");
+ }
+
+ Hashtable table = (Hashtable)item.Value;
+ Hashtable frame = (Hashtable)table["frame"];
+
+ int frameX = int.Parse(frame["x"].ToString());
+ int frameY = int.Parse(frame["y"].ToString());
+ int frameW = int.Parse(frame["w"].ToString());
+ int frameH = int.Parse(frame["h"].ToString());
+
+ Rect finalUVs = new Rect(frameX, frameY, frameW, frameH);
+
+ Rect rect = new Rect(frameX, frameY, frameW, frameH);
+
+ float width = TextureScale.x;
+ float height = TextureScale.y;
+
+ if (width != 0f && height != 0f)
+ {
+ finalUVs.xMin = rect.xMin / width;
+ finalUVs.xMax = rect.xMax / width;
+ finalUVs.yMin = 1f - rect.yMax / height;
+ finalUVs.yMax = 1f - rect.yMin / height;
+ }
+
+ newElement.UV = finalUVs;
+ newElement.Imported = true;
+
+
+
+ atlas.elementsList.Add(newElement);
+ }
+
+ foreach (ProFlareAtlas.Element oldSprite in oldElements)
+ {
+ if (!oldSprite.Imported)
+ {
+ atlas.elementsList.Add(oldSprite);
+ }
+ }
+
+ // Sort imported sprites alphabetically
+
+ atlas.elementsList.Sort(CompareSprites);
+
+ Debug.Log("PROFLARES - Imported " + atlas.elementsList.Count + " Elements");
+
+ // Unload the asset
+ asset = null;
+ Resources.UnloadUnusedAssets();
+ }
+
+ /// <summary>
+ /// Sprite comparison function for sorting.
+ /// </summary>
+
+ static int CompareSprites (ProFlareAtlas.Element a, ProFlareAtlas.Element b) { return a.name.CompareTo(b.name); }
+
+ /// <summary>
+ /// Copy the inner rectangle from one sprite to another.
+ /// </summary>
+ /*
+ static void CopyInnerRect (ProFlareAtlas.Element oldSprite, ProFlareAtlas.Element newElement)
+ {
+ float offsetX = oldSprite.inner.xMin - oldSprite.outer.xMin;
+ float offsetY = oldSprite.inner.yMin - oldSprite.outer.yMin;
+ float sizeX = oldSprite.inner.width;
+ float sizeY = oldSprite.inner.height;
+
+ if (Mathf.Approximately(newElement.outer.width, oldSprite.outer.width))
+ {
+ // The sprite has not been rotated or it's a square
+ newElement.inner = new Rect(newElement.outer.xMin + offsetX, newElement.outer.yMin + offsetY, sizeX, sizeY);
+ }
+ else if (Mathf.Approximately(newElement.outer.width, oldSprite.outer.height))
+ {
+ // The sprite was rotated since the last time it was imported
+ newElement.inner = new Rect(newElement.outer.xMin + offsetY, newElement.outer.yMin + offsetX, sizeY, sizeX);
+ }
+ }
+ */
+ /// <summary>
+ /// Parses the string json into a value
+ /// </summary>
+ /// <param name="json">A JSON string.</param>
+ /// <returns>An ArrayList, a Hashtable, a double, a string, null, true, or false</returns>
+ public static object jsonDecode( string json )
+ {
+ // save the string for debug information
+ FlareJson.lastDecode = json;
+
+ if( json != null )
+ {
+ char[] charArray = json.ToCharArray();
+ int index = 0;
+ bool success = true;
+ object value = FlareJson.parseValue( charArray, ref index, ref success );
+
+ if( success ){
+ Debug.Log("jsonDecode success");
+ FlareJson.lastErrorIndex = -1;
+ }
+ else{
+
+ FlareJson.lastErrorIndex = index;
+ }
+ return value;
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+
+ /// <summary>
+ /// Converts a Hashtable / ArrayList / Dictionary(string,string) object into a JSON string
+ /// </summary>
+ /// <param name="json">A Hashtable / ArrayList</param>
+ /// <returns>A JSON encoded string, or null if object 'json' is not serializable</returns>
+ public static string jsonEncode( object json )
+ {
+ var builder = new StringBuilder( BUILDER_CAPACITY );
+ var success = FlareJson.serializeValue( json, builder );
+
+ return ( success ? builder.ToString() : null );
+ }
+
+
+ /// <summary>
+ /// On decoding, this function returns the position at which the parse failed (-1 = no error).
+ /// </summary>
+ /// <returns></returns>
+ public static bool lastDecodeSuccessful()
+ {
+ return ( FlareJson.lastErrorIndex == -1 );
+ }
+
+
+ /// <summary>
+ /// On decoding, this function returns the position at which the parse failed (-1 = no error).
+ /// </summary>
+ /// <returns></returns>
+ public static int getLastErrorIndex()
+ {
+ return FlareJson.lastErrorIndex;
+ }
+
+
+ /// <summary>
+ /// If a decoding error occurred, this function returns a piece of the JSON string
+ /// at which the error took place. To ease debugging.
+ /// </summary>
+ /// <returns></returns>
+ public static string getLastErrorSnippet()
+ {
+ if( FlareJson.lastErrorIndex == -1 )
+ {
+ return "";
+ }
+ else
+ {
+ int startIndex = FlareJson.lastErrorIndex - 5;
+ int endIndex = FlareJson.lastErrorIndex + 15;
+ if( startIndex < 0 )
+ startIndex = 0;
+
+ if( endIndex >= FlareJson.lastDecode.Length )
+ endIndex = FlareJson.lastDecode.Length - 1;
+
+ return FlareJson.lastDecode.Substring( startIndex, endIndex - startIndex + 1 );
+ }
+ }
+
+
+ #region Parsing
+
+ protected static Hashtable parseObject( char[] json, ref int index )
+ {
+ Hashtable table = new Hashtable();
+ int token;
+
+ // {
+ nextToken( json, ref index );
+
+ bool done = false;
+ while( !done )
+ {
+ token = lookAhead( json, index );
+ if( token == FlareJson.TOKEN_NONE )
+ {
+ return null;
+ }
+ else if( token == FlareJson.TOKEN_COMMA )
+ {
+ nextToken( json, ref index );
+ }
+ else if( token == FlareJson.TOKEN_CURLY_CLOSE )
+ {
+ nextToken( json, ref index );
+ return table;
+ }
+ else
+ {
+ // name
+ string name = parseString( json, ref index );
+ if( name == null )
+ {
+ return null;
+ }
+
+ // :
+ token = nextToken( json, ref index );
+ if( token != FlareJson.TOKEN_COLON )
+ return null;
+
+ // value
+ bool success = true;
+ object value = parseValue( json, ref index, ref success );
+ if( !success )
+ return null;
+
+ table[name] = value;
+ }
+ }
+
+ return table;
+ }
+
+
+ protected static ArrayList parseArray( char[] json, ref int index )
+ {
+ ArrayList array = new ArrayList();
+
+ // [
+ nextToken( json, ref index );
+
+ bool done = false;
+ while( !done )
+ {
+ int token = lookAhead( json, index );
+ if( token == FlareJson.TOKEN_NONE )
+ {
+ return null;
+ }
+ else if( token == FlareJson.TOKEN_COMMA )
+ {
+ nextToken( json, ref index );
+ }
+ else if( token == FlareJson.TOKEN_SQUARED_CLOSE )
+ {
+ nextToken( json, ref index );
+ break;
+ }
+ else
+ {
+ bool success = true;
+ object value = parseValue( json, ref index, ref success );
+ if( !success )
+ return null;
+
+ array.Add( value );
+ }
+ }
+
+ return array;
+ }
+
+
+ protected static object parseValue( char[] json, ref int index, ref bool success )
+ {
+ switch( lookAhead( json, index ) )
+ {
+ case FlareJson.TOKEN_STRING:
+ return parseString( json, ref index );
+ case FlareJson.TOKEN_NUMBER:
+ return parseNumber( json, ref index );
+ case FlareJson.TOKEN_CURLY_OPEN:
+ return parseObject( json, ref index );
+ case FlareJson.TOKEN_SQUARED_OPEN:
+ return parseArray( json, ref index );
+ case FlareJson.TOKEN_TRUE:
+ nextToken( json, ref index );
+ return Boolean.Parse( "TRUE" );
+ case FlareJson.TOKEN_FALSE:
+ nextToken( json, ref index );
+ return Boolean.Parse( "FALSE" );
+ case FlareJson.TOKEN_NULL:
+ nextToken( json, ref index );
+ return null;
+ case FlareJson.TOKEN_NONE:
+ break;
+ }
+
+ success = false;
+ return null;
+ }
+
+
+ protected static string parseString( char[] json, ref int index )
+ {
+ string s = "";
+ char c;
+
+ eatWhitespace( json, ref index );
+
+ // "
+ c = json[index++];
+
+ bool complete = false;
+ while( !complete )
+ {
+ if( index == json.Length )
+ break;
+
+ c = json[index++];
+ if( c == '"' )
+ {
+ complete = true;
+ break;
+ }
+ else if( c == '\\' )
+ {
+ if( index == json.Length )
+ break;
+
+ c = json[index++];
+ if( c == '"' )
+ {
+ s += '"';
+ }
+ else if( c == '\\' )
+ {
+ s += '\\';
+ }
+ else if( c == '/' )
+ {
+ s += '/';
+ }
+ else if( c == 'b' )
+ {
+ s += '\b';
+ }
+ else if( c == 'f' )
+ {
+ s += '\f';
+ }
+ else if( c == 'n' )
+ {
+ s += '\n';
+ }
+ else if( c == 'r' )
+ {
+ s += '\r';
+ }
+ else if( c == 't' )
+ {
+ s += '\t';
+ }
+ else if( c == 'u' )
+ {
+ int remainingLength = json.Length - index;
+ if( remainingLength >= 4 )
+ {
+ char[] unicodeCharArray = new char[4];
+ Array.Copy( json, index, unicodeCharArray, 0, 4 );
+
+ // Drop in the HTML markup for the unicode character
+ s += "&#x" + new string( unicodeCharArray ) + ";";
+
+ /*
+uint codePoint = UInt32.Parse(new string(unicodeCharArray), NumberStyles.HexNumber);
+// convert the integer codepoint to a unicode char and add to string
+s += Char.ConvertFromUtf32((int)codePoint);
+*/
+
+ // skip 4 chars
+ index += 4;
+ }
+ else
+ {
+ break;
+ }
+
+ }
+ }
+ else
+ {
+ s += c;
+ }
+
+ }
+
+ if( !complete )
+ return null;
+
+ return s;
+ }
+
+
+ protected static double parseNumber( char[] json, ref int index )
+ {
+ eatWhitespace( json, ref index );
+
+ int lastIndex = getLastIndexOfNumber( json, index );
+ int charLength = ( lastIndex - index ) + 1;
+ char[] numberCharArray = new char[charLength];
+
+ Array.Copy( json, index, numberCharArray, 0, charLength );
+ index = lastIndex + 1;
+ return Double.Parse( new string( numberCharArray ) ); // , CultureInfo.InvariantCulture);
+ }
+
+
+ protected static int getLastIndexOfNumber( char[] json, int index )
+ {
+ int lastIndex;
+ for( lastIndex = index; lastIndex < json.Length; lastIndex++ )
+ if( "0123456789+-.eE".IndexOf( json[lastIndex] ) == -1 )
+ {
+ break;
+ }
+ return lastIndex - 1;
+ }
+
+
+ protected static void eatWhitespace( char[] json, ref int index )
+ {
+ for( ; index < json.Length; index++ )
+ if( " \t\n\r".IndexOf( json[index] ) == -1 )
+ {
+ break;
+ }
+ }
+
+
+ protected static int lookAhead( char[] json, int index )
+ {
+ int saveIndex = index;
+ return nextToken( json, ref saveIndex );
+ }
+
+
+ protected static int nextToken( char[] json, ref int index )
+ {
+ eatWhitespace( json, ref index );
+
+ if( index == json.Length )
+ {
+ return FlareJson.TOKEN_NONE;
+ }
+
+ char c = json[index];
+ index++;
+ switch( c )
+ {
+ case '{':
+ return FlareJson.TOKEN_CURLY_OPEN;
+ case '}':
+ return FlareJson.TOKEN_CURLY_CLOSE;
+ case '[':
+ return FlareJson.TOKEN_SQUARED_OPEN;
+ case ']':
+ return FlareJson.TOKEN_SQUARED_CLOSE;
+ case ',':
+ return FlareJson.TOKEN_COMMA;
+ case '"':
+ return FlareJson.TOKEN_STRING;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '-':
+ return FlareJson.TOKEN_NUMBER;
+ case ':':
+ return FlareJson.TOKEN_COLON;
+ }
+ index--;
+
+ int remainingLength = json.Length - index;
+
+ // false
+ if( remainingLength >= 5 )
+ {
+ if( json[index] == 'f' &&
+ json[index + 1] == 'a' &&
+ json[index + 2] == 'l' &&
+ json[index + 3] == 's' &&
+ json[index + 4] == 'e' )
+ {
+ index += 5;
+ return FlareJson.TOKEN_FALSE;
+ }
+ }
+
+ // true
+ if( remainingLength >= 4 )
+ {
+ if( json[index] == 't' &&
+ json[index + 1] == 'r' &&
+ json[index + 2] == 'u' &&
+ json[index + 3] == 'e' )
+ {
+ index += 4;
+ return FlareJson.TOKEN_TRUE;
+ }
+ }
+
+ // null
+ if( remainingLength >= 4 )
+ {
+ if( json[index] == 'n' &&
+ json[index + 1] == 'u' &&
+ json[index + 2] == 'l' &&
+ json[index + 3] == 'l' )
+ {
+ index += 4;
+ return FlareJson.TOKEN_NULL;
+ }
+ }
+
+ return FlareJson.TOKEN_NONE;
+ }
+
+ #endregion
+
+
+ #region Serialization
+
+ protected static bool serializeObjectOrArray( object objectOrArray, StringBuilder builder )
+ {
+ if( objectOrArray is Hashtable )
+ {
+ return serializeObject( (Hashtable)objectOrArray, builder );
+ }
+ else if( objectOrArray is ArrayList )
+ {
+ return serializeArray( (ArrayList)objectOrArray, builder );
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+
+ protected static bool serializeObject( Hashtable anObject, StringBuilder builder )
+ {
+ builder.Append( "{" );
+
+ IDictionaryEnumerator e = anObject.GetEnumerator();
+ bool first = true;
+ while( e.MoveNext() )
+ {
+ string key = e.Key.ToString();
+ object value = e.Value;
+
+ if( !first )
+ {
+ builder.Append( ", " );
+ }
+
+ serializeString( key, builder );
+ builder.Append( ":" );
+ if( !serializeValue( value, builder ) )
+ {
+ return false;
+ }
+
+ first = false;
+ }
+
+ builder.Append( "}" );
+ return true;
+ }
+
+
+ protected static bool serializeDictionary( Dictionary<string,string> dict, StringBuilder builder )
+ {
+ builder.Append( "{" );
+
+ bool first = true;
+ foreach( var kv in dict )
+ {
+ if( !first )
+ builder.Append( ", " );
+
+ serializeString( kv.Key, builder );
+ builder.Append( ":" );
+ serializeString( kv.Value, builder );
+
+ first = false;
+ }
+
+ builder.Append( "}" );
+ return true;
+ }
+
+
+ protected static bool serializeArray( ArrayList anArray, StringBuilder builder )
+ {
+ builder.Append( "[" );
+
+ bool first = true;
+ for( int i = 0; i < anArray.Count; i++ )
+ {
+ object value = anArray[i];
+
+ if( !first )
+ {
+ builder.Append( ", " );
+ }
+
+ if( !serializeValue( value, builder ) )
+ {
+ return false;
+ }
+
+ first = false;
+ }
+
+ builder.Append( "]" );
+ return true;
+ }
+
+
+ protected static bool serializeValue( object value, StringBuilder builder )
+ {
+ // Type t = value.GetType();
+ // Debug.Log("type: " + t.ToString() + " isArray: " + t.IsArray);
+
+ if( value == null )
+ {
+ builder.Append( "null" );
+ }
+ else if( value.GetType().IsArray )
+ {
+ serializeArray( new ArrayList( (ICollection)value ), builder );
+ }
+ else if( value is string )
+ {
+ serializeString( (string)value, builder );
+ }
+ else if( value is Char )
+ {
+ serializeString( Convert.ToString( (char)value ), builder );
+ }
+ else if( value is Hashtable )
+ {
+ serializeObject( (Hashtable)value, builder );
+ }
+ else if( value is Dictionary<string,string> )
+ {
+ serializeDictionary( (Dictionary<string,string>)value, builder );
+ }
+ else if( value is ArrayList )
+ {
+ serializeArray( (ArrayList)value, builder );
+ }
+ else if( ( value is Boolean ) && ( (Boolean)value == true ) )
+ {
+ builder.Append( "true" );
+ }
+ else if( ( value is Boolean ) && ( (Boolean)value == false ) )
+ {
+ builder.Append( "false" );
+ }
+ else if( value.GetType().IsPrimitive )
+ {
+ serializeNumber( Convert.ToDouble( value ), builder );
+ }
+ else
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+
+ protected static void serializeString( string aString, StringBuilder builder )
+ {
+ builder.Append( "\"" );
+
+ char[] charArray = aString.ToCharArray();
+ for( int i = 0; i < charArray.Length; i++ )
+ {
+ char c = charArray[i];
+ if( c == '"' )
+ {
+ builder.Append( "\\\"" );
+ }
+ else if( c == '\\' )
+ {
+ builder.Append( "\\\\" );
+ }
+ else if( c == '\b' )
+ {
+ builder.Append( "\\b" );
+ }
+ else if( c == '\f' )
+ {
+ builder.Append( "\\f" );
+ }
+ else if( c == '\n' )
+ {
+ builder.Append( "\\n" );
+ }
+ else if( c == '\r' )
+ {
+ builder.Append( "\\r" );
+ }
+ else if( c == '\t' )
+ {
+ builder.Append( "\\t" );
+ }
+ else
+ {
+ int codepoint = Convert.ToInt32( c );
+ if( ( codepoint >= 32 ) && ( codepoint <= 126 ) )
+ {
+ builder.Append( c );
+ }
+ else
+ {
+ builder.Append( "\\u" + Convert.ToString( codepoint, 16 ).PadLeft( 4, '0' ) );
+ }
+ }
+ }
+
+ builder.Append( "\"" );
+ }
+
+
+ protected static void serializeNumber( double number, StringBuilder builder )
+ {
+ builder.Append( Convert.ToString( number ) ); // , CultureInfo.InvariantCulture));
+ }
+
+ #endregion
+
+}