├── ProjectSettings ├── ProjectVersion.txt ├── TagManager.asset ├── TimeManager.asset ├── AudioManager.asset ├── EditorSettings.asset ├── InputManager.asset ├── NavMeshAreas.asset ├── NetworkManager.asset ├── DynamicsManager.asset ├── GraphicsSettings.asset ├── ProjectSettings.asset ├── QualitySettings.asset ├── UnityAdsSettings.asset ├── ClusterInputManager.asset ├── EditorBuildSettings.asset ├── Physics2DSettings.asset └── UnityConnectSettings.asset ├── Assets ├── Untitled.unity ├── PickleTools │ ├── PickleBuilder │ │ ├── Editor │ │ │ ├── build_config_skin.guiskin │ │ │ ├── build_config.json.meta │ │ │ ├── build_config_skin.guiskin.meta │ │ │ ├── BuildScript.cs.meta │ │ │ ├── BuildConfigWindow.cs.meta │ │ │ ├── BuildScript.cs │ │ │ └── BuildConfigWindow.cs │ │ ├── downloadsdk.sh │ │ ├── downloadsdk.sh.meta │ │ └── Editor.meta │ ├── Editor.meta │ ├── Extensions.meta │ ├── PickleBuilder.meta │ ├── Editor │ │ ├── EditorCoroutine.cs.meta │ │ ├── SortableList.cs.meta │ │ ├── EditorCoroutine.cs │ │ └── SortableList.cs │ └── Extensions │ │ ├── ArrayExtensions.cs.meta │ │ └── ArrayExtensions.cs ├── Untitled.unity.meta ├── LitJson.meta ├── PickleTools.meta └── LitJson │ ├── Lexer.cs.meta │ ├── IJsonWrapper.cs.meta │ ├── JsonData.cs.meta │ ├── JsonMapper.cs.meta │ ├── JsonReader.cs.meta │ ├── JsonWriter.cs.meta │ ├── ParserToken.cs.meta │ ├── JsonException.cs.meta │ ├── JsonMockWrapper.cs.meta │ ├── ParserToken.cs │ ├── IJsonWrapper.cs │ ├── JsonException.cs │ ├── JsonMockWrapper.cs │ ├── JsonWriter.cs │ ├── JsonReader.cs │ ├── JsonData.cs │ ├── Lexer.cs │ └── JsonMapper.cs ├── .gitignore ├── LICENSE └── README.md /ProjectSettings/ProjectVersion.txt: -------------------------------------------------------------------------------- 1 | m_EditorVersion: 5.3.5p8 2 | m_StandardAssetsVersion: 0 3 | -------------------------------------------------------------------------------- /Assets/Untitled.unity: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PicklesIIDX/PickleBuilder/HEAD/Assets/Untitled.unity -------------------------------------------------------------------------------- /ProjectSettings/TagManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PicklesIIDX/PickleBuilder/HEAD/ProjectSettings/TagManager.asset -------------------------------------------------------------------------------- /ProjectSettings/TimeManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PicklesIIDX/PickleBuilder/HEAD/ProjectSettings/TimeManager.asset -------------------------------------------------------------------------------- /ProjectSettings/AudioManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PicklesIIDX/PickleBuilder/HEAD/ProjectSettings/AudioManager.asset -------------------------------------------------------------------------------- /ProjectSettings/EditorSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PicklesIIDX/PickleBuilder/HEAD/ProjectSettings/EditorSettings.asset -------------------------------------------------------------------------------- /ProjectSettings/InputManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PicklesIIDX/PickleBuilder/HEAD/ProjectSettings/InputManager.asset -------------------------------------------------------------------------------- /ProjectSettings/NavMeshAreas.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PicklesIIDX/PickleBuilder/HEAD/ProjectSettings/NavMeshAreas.asset -------------------------------------------------------------------------------- /ProjectSettings/NetworkManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PicklesIIDX/PickleBuilder/HEAD/ProjectSettings/NetworkManager.asset -------------------------------------------------------------------------------- /ProjectSettings/DynamicsManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PicklesIIDX/PickleBuilder/HEAD/ProjectSettings/DynamicsManager.asset -------------------------------------------------------------------------------- /ProjectSettings/GraphicsSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PicklesIIDX/PickleBuilder/HEAD/ProjectSettings/GraphicsSettings.asset -------------------------------------------------------------------------------- /ProjectSettings/ProjectSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PicklesIIDX/PickleBuilder/HEAD/ProjectSettings/ProjectSettings.asset -------------------------------------------------------------------------------- /ProjectSettings/QualitySettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PicklesIIDX/PickleBuilder/HEAD/ProjectSettings/QualitySettings.asset -------------------------------------------------------------------------------- /ProjectSettings/UnityAdsSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PicklesIIDX/PickleBuilder/HEAD/ProjectSettings/UnityAdsSettings.asset -------------------------------------------------------------------------------- /ProjectSettings/ClusterInputManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PicklesIIDX/PickleBuilder/HEAD/ProjectSettings/ClusterInputManager.asset -------------------------------------------------------------------------------- /ProjectSettings/EditorBuildSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PicklesIIDX/PickleBuilder/HEAD/ProjectSettings/EditorBuildSettings.asset -------------------------------------------------------------------------------- /ProjectSettings/Physics2DSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PicklesIIDX/PickleBuilder/HEAD/ProjectSettings/Physics2DSettings.asset -------------------------------------------------------------------------------- /ProjectSettings/UnityConnectSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PicklesIIDX/PickleBuilder/HEAD/ProjectSettings/UnityConnectSettings.asset -------------------------------------------------------------------------------- /Assets/PickleTools/PickleBuilder/Editor/build_config_skin.guiskin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PicklesIIDX/PickleBuilder/HEAD/Assets/PickleTools/PickleBuilder/Editor/build_config_skin.guiskin -------------------------------------------------------------------------------- /Assets/PickleTools/PickleBuilder/downloadsdk.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | mkdir steamsdk 3 | cd steamsdk 4 | curl -sqL 'https://steamcdn-a.akamaihd.net/client/installer/steamcmd_osx.tar.gz' | tar zxvf - -------------------------------------------------------------------------------- /Assets/Untitled.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4224a8195abcc4df5b131928d71b26b0 3 | timeCreated: 1477495440 4 | licenseType: Pro 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/LitJson.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 73788568642ac4005b9804b62a1284f5 3 | folderAsset: yes 4 | timeCreated: 1473888182 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/PickleTools/PickleBuilder/downloadsdk.sh.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8b440f510d379401a96742431e7ceda6 3 | timeCreated: 1477456279 4 | licenseType: Pro 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/PickleTools.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ce8cdad53820643a68154bcf5eb61457 3 | folderAsset: yes 4 | timeCreated: 1473888152 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/PickleTools/Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 87403f03b503b409dbdca68f20f3d2ef 3 | folderAsset: yes 4 | timeCreated: 1473888243 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/PickleTools/Extensions.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f6f98bf19f1b64864b2f3f370e795076 3 | folderAsset: yes 4 | timeCreated: 1473888243 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/PickleTools/PickleBuilder/Editor/build_config.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e2ed678ee3f164a5cbe94074b35b7d61 3 | timeCreated: 1473888978 4 | licenseType: Pro 5 | TextScriptImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/PickleTools/PickleBuilder.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 81728b91561bb4a2696d56fa706af1f8 3 | folderAsset: yes 4 | timeCreated: 1473888152 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/PickleTools/PickleBuilder/Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 06af3c60666cd4a73bd6f88649374888 3 | folderAsset: yes 4 | timeCreated: 1473884435 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/PickleTools/PickleBuilder/Editor/build_config_skin.guiskin.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 87a72fb1b519145528678247ab8e7215 3 | timeCreated: 1446317475 4 | licenseType: Free 5 | NativeFormatImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/LitJson/Lexer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3933e03de80dd434d864f40c3fab7908 3 | timeCreated: 1458858357 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/LitJson/IJsonWrapper.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d7f42fb050c2dba468c0e192b0889a6d 3 | timeCreated: 1458858357 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/LitJson/JsonData.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dbd3136aaf24ef84ea9578b8a91b1d33 3 | timeCreated: 1458858357 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/LitJson/JsonMapper.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4b3727d508ff80c409e3fd8fbb15b677 3 | timeCreated: 1458858357 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/LitJson/JsonReader.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 26a7ef01159e85d429951f4b362cfece 3 | timeCreated: 1458858357 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/LitJson/JsonWriter.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 96dd9fce1d55efe49aed0d07987dc315 3 | timeCreated: 1458858357 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/LitJson/ParserToken.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 37f55ab13d3b753428fd6a7c6da144a5 3 | timeCreated: 1458858357 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/LitJson/JsonException.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8fc60666acab7494b8d5274957010412 3 | timeCreated: 1458858357 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/LitJson/JsonMockWrapper.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1a81021dd94526a45b56aa9796edb72a 3 | timeCreated: 1458858357 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/PickleTools/Editor/EditorCoroutine.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f9206be004bca4e3cafcbcd2cbe8672d 3 | timeCreated: 1473888260 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/PickleTools/Editor/SortableList.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3df2e85dad62442d29d6773018ad5788 3 | timeCreated: 1473888243 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/PickleTools/Extensions/ArrayExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9faffd36cb6b042f6be7adc73679f32e 3 | timeCreated: 1473888243 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/PickleTools/PickleBuilder/Editor/BuildScript.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 943c213746e61466088aebd6ca472a7a 3 | timeCreated: 1441736389 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/PickleTools/PickleBuilder/Editor/BuildConfigWindow.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bfbb8a72426254173b94c62aeb442fb9 3 | timeCreated: 1445834738 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /[Ll]ibrary/ 2 | /[Tt]emp/ 3 | /[Oo]bj/ 4 | /[Bb]uild/ 5 | /[Bb]uilds/ 6 | /Assets/AssetStoreTools* 7 | 8 | # Autogenerated VS/MD solution and project files 9 | ExportedObj/ 10 | *.csproj 11 | *.unityproj 12 | *.sln 13 | *.suo 14 | *.tmp 15 | *.user 16 | *.userprefs 17 | *.pidb 18 | *.booproj 19 | *.svd 20 | 21 | 22 | # Unity3D generated meta files 23 | *.pidb.meta 24 | 25 | # Unity3D Generated File On Crash Reports 26 | sysinfo.txt 27 | 28 | # Builds 29 | *.apk 30 | *.unitypackage 31 | -------------------------------------------------------------------------------- /Assets/LitJson/ParserToken.cs: -------------------------------------------------------------------------------- 1 | #region Header 2 | /** 3 | * ParserToken.cs 4 | * Internal representation of the tokens used by the lexer and the parser. 5 | * 6 | * The authors disclaim copyright to this source code. For more details, see 7 | * the COPYING file included with this distribution. 8 | **/ 9 | #endregion 10 | 11 | 12 | namespace LitJson 13 | { 14 | internal enum ParserToken 15 | { 16 | // Lexer tokens (see section A.1.1. of the manual) 17 | None = System.Char.MaxValue + 1, 18 | Number, 19 | True, 20 | False, 21 | Null, 22 | CharSeq, 23 | // Single char 24 | Char, 25 | 26 | // Parser Rules (see section A.2.1 of the manual) 27 | Text, 28 | Object, 29 | ObjectPrime, 30 | Pair, 31 | PairRest, 32 | Array, 33 | ArrayPrime, 34 | Value, 35 | ValueRest, 36 | String, 37 | 38 | // End of input 39 | End, 40 | 41 | // The empty rule 42 | Epsilon 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Assets/PickleTools/Editor/EditorCoroutine.cs: -------------------------------------------------------------------------------- 1 | // Source: https://gist.github.com/benblo/10732554 2 | using UnityEditor; 3 | using System.Collections; 4 | 5 | namespace PickleTools.UnityEditor { 6 | public class EditorCoroutine { 7 | public static EditorCoroutine start(IEnumerator _routine) { 8 | EditorCoroutine coroutine = new EditorCoroutine(_routine); 9 | coroutine.start(); 10 | return coroutine; 11 | } 12 | 13 | readonly IEnumerator routine; 14 | EditorCoroutine(IEnumerator _routine) { 15 | routine = _routine; 16 | } 17 | 18 | void start() { 19 | UnityEngine.Debug.Log("start"); 20 | EditorApplication.update += update; 21 | } 22 | public void stop() { 23 | UnityEngine.Debug.Log("stop"); 24 | EditorApplication.update -= update; 25 | } 26 | 27 | void update() { 28 | /* NOTE: no need to try/catch MoveNext, 29 | * if an IEnumerator throws its next iteration returns false. 30 | * Also, Unity probably catches when calling EditorApplication.update. 31 | */ 32 | 33 | UnityEngine.Debug.Log("update"); 34 | if (!routine.MoveNext()) { 35 | stop(); 36 | } 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Auston Montville 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Assets/PickleTools/Extensions/ArrayExtensions.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace PickleTools.Extensions.ArrayExtensions { 4 | public static class ArrayExtensions { 5 | 6 | public static T[] RemoveAt(this T[] source, int index){ 7 | T[] dest = new T[source.Length - 1]; 8 | if(index > 0){ 9 | System.Array.Copy(source, 0, dest, 0, index); 10 | } 11 | 12 | if(index < source.Length - 1){ 13 | System.Array.Copy(source, index+1, dest, index, source.Length - index - 1); 14 | } 15 | return dest; 16 | } 17 | 18 | public static T[] InsertAt(this T[] source, int index, T item){ 19 | // split the array up 20 | T[] leftHandSide = new T[index]; 21 | System.Array.Copy(source, 0, leftHandSide, 0, index); 22 | T[] rightHandSide = new T[source.Length - index]; 23 | System.Array.Copy(source, index, rightHandSide, 0, source.Length - index); 24 | T[] dest = new T[source.Length + 1]; 25 | System.Array.Copy(leftHandSide, 0, dest, 0, leftHandSide.Length); 26 | dest[leftHandSide.Length] = item; 27 | System.Array.Copy(rightHandSide, 0, dest, leftHandSide.Length + 1, rightHandSide.Length); 28 | 29 | return dest; 30 | } 31 | 32 | } 33 | } -------------------------------------------------------------------------------- /Assets/LitJson/IJsonWrapper.cs: -------------------------------------------------------------------------------- 1 | #region Header 2 | /** 3 | * IJsonWrapper.cs 4 | * Interface that represents a type capable of handling all kinds of JSON 5 | * data. This is mainly used when mapping objects through JsonMapper, and 6 | * it's implemented by JsonData. 7 | * 8 | * The authors disclaim copyright to this source code. For more details, see 9 | * the COPYING file included with this distribution. 10 | **/ 11 | #endregion 12 | 13 | 14 | using System.Collections; 15 | using System.Collections.Specialized; 16 | 17 | namespace LitJson 18 | { 19 | public enum JsonType 20 | { 21 | None, 22 | 23 | Object, 24 | Array, 25 | String, 26 | Int, 27 | Long, 28 | Double, 29 | Boolean 30 | } 31 | 32 | public interface IJsonWrapper : IList, IOrderedDictionary 33 | { 34 | bool IsArray { get; } 35 | 36 | bool IsBoolean { get; } 37 | 38 | bool IsDouble { get; } 39 | 40 | bool IsInt { get; } 41 | 42 | bool IsLong { get; } 43 | 44 | bool IsObject { get; } 45 | 46 | bool IsString { get; } 47 | 48 | bool GetBoolean (); 49 | 50 | double GetDouble (); 51 | 52 | int GetInt (); 53 | 54 | JsonType GetJsonType (); 55 | 56 | long GetLong (); 57 | 58 | string GetString (); 59 | 60 | void SetBoolean (bool val); 61 | 62 | void SetDouble (double val); 63 | 64 | void SetInt (int val); 65 | 66 | void SetJsonType (JsonType type); 67 | 68 | void SetLong (long val); 69 | 70 | void SetString (string val); 71 | 72 | string ToJson (); 73 | 74 | void ToJson (JsonWriter writer); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Assets/LitJson/JsonException.cs: -------------------------------------------------------------------------------- 1 | #region Header 2 | /** 3 | * JsonException.cs 4 | * Base class throwed by LitJSON when a parsing error occurs. 5 | * 6 | * The authors disclaim copyright to this source code. For more details, see 7 | * the COPYING file included with this distribution. 8 | **/ 9 | #endregion 10 | 11 | 12 | using System; 13 | 14 | 15 | namespace LitJson 16 | { 17 | public class JsonException : ApplicationException 18 | { 19 | public JsonException () : base () 20 | { 21 | } 22 | 23 | internal JsonException (ParserToken token) : 24 | base (String.Format ( 25 | "Invalid token '{0}' in input string", token)) 26 | { 27 | } 28 | 29 | internal JsonException (ParserToken token, 30 | Exception inner_exception) : 31 | base (String.Format ( 32 | "Invalid token '{0}' in input string", token), 33 | inner_exception) 34 | { 35 | } 36 | 37 | internal JsonException (int c) : 38 | base (String.Format ( 39 | "Invalid character '{0}' in input string", (char) c)) 40 | { 41 | } 42 | 43 | internal JsonException (int c, Exception inner_exception) : 44 | base (String.Format ( 45 | "Invalid character '{0}' in input string", (char) c), 46 | inner_exception) 47 | { 48 | } 49 | 50 | 51 | public JsonException (string message) : base (message) 52 | { 53 | } 54 | 55 | public JsonException (string message, Exception inner_exception) : 56 | base (message, inner_exception) 57 | { 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Assets/LitJson/JsonMockWrapper.cs: -------------------------------------------------------------------------------- 1 | #region Header 2 | /** 3 | * JsonMockWrapper.cs 4 | * Mock object implementing IJsonWrapper, to facilitate actions like 5 | * skipping data more efficiently. 6 | * 7 | * The authors disclaim copyright to this source code. For more details, see 8 | * the COPYING file included with this distribution. 9 | **/ 10 | #endregion 11 | 12 | 13 | using System; 14 | using System.Collections; 15 | using System.Collections.Specialized; 16 | 17 | 18 | namespace LitJson 19 | { 20 | public class JsonMockWrapper : IJsonWrapper 21 | { 22 | public bool IsArray { get { return false; } } 23 | public bool IsBoolean { get { return false; } } 24 | public bool IsDouble { get { return false; } } 25 | public bool IsInt { get { return false; } } 26 | public bool IsLong { get { return false; } } 27 | public bool IsObject { get { return false; } } 28 | public bool IsString { get { return false; } } 29 | 30 | public bool GetBoolean () { return false; } 31 | public double GetDouble () { return 0.0; } 32 | public int GetInt () { return 0; } 33 | public JsonType GetJsonType () { return JsonType.None; } 34 | public long GetLong () { return 0L; } 35 | public string GetString () { return ""; } 36 | 37 | public void SetBoolean (bool val) {} 38 | public void SetDouble (double val) {} 39 | public void SetInt (int val) {} 40 | public void SetJsonType (JsonType type) {} 41 | public void SetLong (long val) {} 42 | public void SetString (string val) {} 43 | 44 | public string ToJson () { return ""; } 45 | public void ToJson (JsonWriter writer) {} 46 | 47 | 48 | bool IList.IsFixedSize { get { return true; } } 49 | bool IList.IsReadOnly { get { return true; } } 50 | 51 | object IList.this[int index] { 52 | get { return null; } 53 | set {} 54 | } 55 | 56 | int IList.Add (object value) { return 0; } 57 | void IList.Clear () {} 58 | bool IList.Contains (object value) { return false; } 59 | int IList.IndexOf (object value) { return -1; } 60 | void IList.Insert (int i, object v) {} 61 | void IList.Remove (object value) {} 62 | void IList.RemoveAt (int index) {} 63 | 64 | 65 | int ICollection.Count { get { return 0; } } 66 | bool ICollection.IsSynchronized { get { return false; } } 67 | object ICollection.SyncRoot { get { return null; } } 68 | 69 | void ICollection.CopyTo (Array array, int index) {} 70 | 71 | 72 | IEnumerator IEnumerable.GetEnumerator () { return null; } 73 | 74 | 75 | bool IDictionary.IsFixedSize { get { return true; } } 76 | bool IDictionary.IsReadOnly { get { return true; } } 77 | 78 | ICollection IDictionary.Keys { get { return null; } } 79 | ICollection IDictionary.Values { get { return null; } } 80 | 81 | object IDictionary.this[object key] { 82 | get { return null; } 83 | set {} 84 | } 85 | 86 | void IDictionary.Add (object k, object v) {} 87 | void IDictionary.Clear () {} 88 | bool IDictionary.Contains (object key) { return false; } 89 | void IDictionary.Remove (object key) {} 90 | 91 | IDictionaryEnumerator IDictionary.GetEnumerator () { return null; } 92 | 93 | 94 | object IOrderedDictionary.this[int idx] { 95 | get { return null; } 96 | set {} 97 | } 98 | 99 | IDictionaryEnumerator IOrderedDictionary.GetEnumerator () { 100 | return null; 101 | } 102 | void IOrderedDictionary.Insert (int i, object k, object v) {} 103 | void IOrderedDictionary.RemoveAt (int i) {} 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /Assets/PickleTools/Editor/SortableList.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using PickleTools.Extensions.ArrayExtensions; 3 | 4 | namespace PickleTools.UnityEditor { 5 | 6 | public class SortableList { 7 | 8 | T[] listEntries = new T[0]; 9 | public T[] ListEntries { 10 | get { return listEntries; } 11 | } 12 | 13 | T dragEntry; 14 | public T DragEntry { 15 | get { return dragEntry; } 16 | } 17 | 18 | float entryHeight = 24.0f; 19 | public float EntryHeight { 20 | get { return entryHeight; } 21 | set { entryHeight = value; } 22 | } 23 | 24 | float topPadding = 2.0f; 25 | public float TopPadding { 26 | get { return topPadding; } 27 | set { topPadding = value; } 28 | } 29 | 30 | private Rect[] entryRects = new Rect[0]; 31 | private float dragEntryListHeight = 0.0f; 32 | 33 | private bool dragMode = false; 34 | private int insertSlot = 0; 35 | private int removeIndex = 0; 36 | 37 | public SortableList(){ 38 | listEntries = new T[0]; 39 | } 40 | 41 | public SortableList(T[] newEntries, float newEntryHeight = 24.0f, float newTopPadding = 2.0f){ 42 | listEntries = newEntries; 43 | entryHeight = newEntryHeight; 44 | topPadding = newTopPadding; 45 | } 46 | 47 | #region draw 48 | // TODO: how do we display the entries within this draw function, so that 49 | // we can draw anything within the drag entries. 50 | // maybe we do a start and stop...? 51 | public T[] Draw(System.Action callback, System.Action dragCallback, T[] newListEntries = null){ 52 | if(newListEntries != null){ 53 | listEntries = newListEntries; 54 | } 55 | Rect lastRect = GUILayoutUtility.GetLastRect(); 56 | dragEntryListHeight = 0; 57 | entryRects = new Rect[listEntries.Length]; 58 | for(int e = 0; e < listEntries.Length; e ++){ 59 | lastRect = new Rect(lastRect.x, lastRect.y + lastRect.height + topPadding, Screen.width, entryHeight); 60 | dragEntryListHeight += entryHeight + topPadding; 61 | // draw the entry 62 | callback(listEntries[e], entryHeight, e); 63 | // store the rect for input checking 64 | entryRects[e] = lastRect; 65 | } 66 | 67 | HandleInput(dragCallback); 68 | return listEntries; 69 | } 70 | #endregion 71 | 72 | #region input 73 | void HandleInput(System.Action dragCallback){ 74 | 75 | for(int e = 0; e < entryRects.Length; e ++){ 76 | if(entryRects[e].Contains(Event.current.mousePosition) && 77 | Event.current.type == EventType.mouseDown && Event.current.button == 0){ 78 | dragMode = true; 79 | dragEntry = listEntries[e]; 80 | removeIndex = e; 81 | break; 82 | } 83 | } 84 | if(dragMode && Event.current.type == EventType.mouseUp){ 85 | dragMode = false; 86 | // remove the entry from the list 87 | listEntries = listEntries.RemoveAt(removeIndex); 88 | // make sure the slot can be inserted within the range of entries 89 | insertSlot = Mathf.Clamp(insertSlot, 0, listEntries.Length); 90 | // insert the dragged entry 91 | listEntries = listEntries.InsertAt(insertSlot, dragEntry); 92 | // reset values 93 | dragEntry = default(T); 94 | insertSlot = -1; 95 | removeIndex = -1; 96 | } 97 | 98 | if(dragMode && Event.current.type != EventType.layout){ 99 | Rect dragBox = new Rect(0, Event.current.mousePosition.y - entryHeight * 0.5f, 100 | Screen.width, entryHeight); 101 | dragCallback(dragEntry, dragBox); 102 | // show where we are placing the entry 103 | for(int e = 0; e < entryRects.Length; e ++){ 104 | // greater than 0 or entry-1 y + halfWidth 105 | float min = Mathf.Max(entryRects[e].y - entryHeight * 0.5f, 0); 106 | // less than entry y + half width 107 | float max = entryRects[e].y + entryHeight * 0.5f; 108 | 109 | if(Event.current.mousePosition.y >= min && 110 | Event.current.mousePosition.y <= max){ 111 | GUI.color = Color.green; 112 | GUI.Box(new Rect(entryRects[e].x, entryRects[e].y + (topPadding * e - topPadding), Screen.width, 4), ""); 113 | GUI.color = Color.white; 114 | insertSlot = e-1; 115 | break; 116 | } else if(e == entryRects.Length - 1 && Event.current.mousePosition.y > max){ 117 | GUI.color = Color.green; 118 | GUI.Box(new Rect(entryRects[e].x, entryRects[e].y + entryHeight + (topPadding * e - topPadding), Screen.width, 4), ""); 119 | GUI.color = Color.white; 120 | insertSlot = e; 121 | } 122 | } 123 | } 124 | } 125 | #endregion 126 | 127 | public void DefaultDragDraw(T entryType, Rect dragRect){ 128 | GUI.Box(dragRect, entryType.ToString()); 129 | } 130 | 131 | } 132 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #PickleBuilder 2 | 3 | A Unity tool for building to multiple platforms and uploading to steam. 4 | 5 | #Builder Window 6 | 7 | ![Unity Build Config Tool Wind](https://github.com/PicklesIIDX/PickleBuilder/raw/master/build_config_tool.png "Unity Build Config Tool Window") 8 | 9 | 10 | This is the build window which you can open by selecting Build > Build Config Window in the Unity Menu Bar. It allows you to define how you want to build your game for different platforms. 11 | 12 | **Build Path:** This is the folder all of your builds will be organized into. When performing a build a new folder will be created inside this folder that is named with the version and date of the build. For example, a folder named 0_1_20__10_26_2016_06_44_27 means that the folder contains version 0.1.20 and was built at 6:44 and 27 seconds GMT on October 26th, 2016. 13 | 14 | **File Name:** This is the name of the application file. The .app on OSX and the .exe on Windows. 15 | 16 | **Version Number Major:** The first number used to describe the version. 17 | 18 | **Version Number Minor:** The second number used to describe the version. 19 | 20 | **Version Number Patch:** The third number used to describe the version. This is incremented automatically every time you build. 21 | 22 | **New Major Version:** Click this button to increase the major version by 1. 23 | 24 | **New Minor Version:** Click this button to increase the minor version by 1. 25 | 26 | **Preview build:** If this is checked the game will not actually be built. But the process will run and the build folders will be created. The Patch version will not be incremented. 27 | 28 | **Platform:** This is a list of all platforms to build for. Use the dropdown list to select your platform. Some platforms, although in the list, will only be able to be built if you have the right licenses (consoles for example). You can remove a platform to build by pressing the "x" button on the right side. When built, the files for this platform will be placed in the versioned build folder under a sub folder that has the name of the build target. 29 | 30 | **Add a platform:** Press this button to add a new platform to build to. 31 | 32 | **Scenes:** This is a list of all scenes in your Build Settings. If a scene is checked, it will be included in the build. You can reorder scenes by clicking and dragging the name of the scene. 33 | 34 | **Push to Steam:** If this toggle is selected, after the builds are made they will be pushed to your Steam app repository. To use the upload to steam function, you must be a steam developer, have an app ID, have set up your app depots, and have the steam sdk. Look below to see how to get your computer ready to upload to steam. 35 | 36 | **Steam SDK Content Builder Path:** Click this button to select the path to the ContentBuilder folder, which is in the steam sdk: /sdk/tools/ContentBuilder/. Follow the instructions below to download the steam SDK 37 | 38 | **Steam CMD Path:** Click this button to select the path to the steamCMD application. Follow the instructions below to download the steamCMD. 39 | 40 | **Use Existing Builds:** Check this box to use an existing build folder instead of the build folder created for this build. 41 | 42 | **Existing Builds Path:** This is only visible if Use Existing Builds is checked. This is the root build folder which contains your individual platform builds. 43 | 44 | **App Block:** This is a group which describes the settings for uploading to steam. 45 | 46 | **App ID:** The app ID of your game. This is given to you by Valve and can be seen on your steamworks app page. 47 | 48 | **Description:** This is a message for this particular upload that you will see in the builds list in your app admin on steamworks. 49 | 50 | **Preview:** Check this box to veryify that the steam process works, but prevent actually uploading to your steam repository. 51 | 52 | **Depot Block:** This is a group which describes the settings for each of your depots. 53 | 54 | **Depot ID:** The ID of the depot which has been setup on your steamworks account. 55 | 56 | **Build Type:** The files to include in this depot. It will grab all the files in the specified build folder. 57 | 58 | **Ignore Files:** Any files listed in here will not be included from the build folder. Accepts wildcards. You can remove the entry with the "x" button. 59 | 60 | **Add file to ignore:** Adds another entry in the Ignore Files list. 61 | 62 | **Add Depot:** Select this button to add a new depot. 63 | 64 | **Save Changes:** This will save your build settings to a file called build_config.json, which will be located in this project at Assets/PickleTools/PickleBuilder/Editor 65 | 66 | **BUILD!:** This will perform the build as you have specified in this window. 67 | 68 | 69 | 70 | 71 | #Steam SDK Setup 72 | 73 | ##OSX 74 | 75 | Download the steamCMD 76 | 77 | 1. open terminal 78 | 79 | 2. create a go to the directory you want the steam sdk to be located 80 | 81 | 2. run curl -sqL 'https://steamcdn-a.akamaihd.net/client/installer/steamcmd_osx.tar.gz' | tar zxvf - 82 | 83 | Update the steam mac client 84 | 85 | 3. open steamCMD by running ./osx32/steamcmd in terminal 86 | 87 | 4. it will open and update steam 88 | 89 | 5. close the steam client 90 | 91 | Download the SDK 92 | 93 | 6. You will find the latest SDK to download on the right side of this page: https://partner.steamgames.com/home 94 | 95 | 7. Unip the steam sdk 96 | 97 | 98 | ##Windows 99 | 100 | Download the steamCMD 101 | 102 | 1. download the steamCMD from here: https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip 103 | 104 | 2. unzip the folder 105 | 106 | Update the steam windows client 107 | 108 | 3. run steamCMD.exe to update steam 109 | 110 | 4. close the steam client 111 | 112 | Download the SDK 113 | 114 | 5. You will find the latest SDK to download on the right side of this page: https://partner.steamgames.com/home 115 | 116 | 6. Unzip the steam sdk 117 | 118 | 119 | #Steamworks setup 120 | 121 | Next you will need to prepare you steam repository. This page (https://partner.steamgames.com/documentation/steampipe) contains a full tutorial on how to setup your steam repository. You only really need to follow the first 4 minutes of this tutorial: https://www.youtube.com/watch?v=SoNH-v6aU9Q. Particularly, you need to set up demos for your game's platforms. 122 | 123 | Launch Options: Create a launch option for each platform. The executable will be the name set in the File Name property in the Build Config Window. Make sure to use .exe for Windows and .app for OSX. 124 | 125 | Depots: You will need to setup an individual depot for each platform. This Build Config Tool uploads a complete version of the game to each depot. 126 | 127 | Make sure to publish your changes! 128 | 129 | #Build from the Command Line: 130 | The BuildScript is accessible from the command line. This is useful if you want to build from Unity without the hassle of opening Unity up and using a GUI interface. Use this command to build using your current config from the command line: 131 | 132 | Windows: 133 | 134 | C:\program files\Unity\Editor\Unity.exe -quit -batchmode -executeMethod BuildScript.PerformBuild steam_login_name steam_login_password 135 | 136 | OSX: 137 | 138 | /Applications/Unity/Unity.app/Contents/MacOS/Unity -quit -batchmode -executeMethod BuildScript.PerformBuild steam_login_name steam_login_password 139 | 140 | Note that the path to the Unity application is where you have installed Unity. Listed above is the default. 141 | Also note that steam_login_name and steam_login_password are optional parameters that should be replaced with your steam username and password if you want to upload the build to steam. If not, just leave those arguments out. 142 | 143 | #Common Issues 144 | 145 | Unity locks up when I hit BUILD!: 146 | 147 | When uploading to Steam we start an external process and wait for it to complete. Just wait a bit. The process is running. The two things that take the most time are checking for steam updates and actually uploading the game content. After the process is complete you will see a log in your console of how the steam upload went. 148 | 149 | I get a depot build error: 150 | 151 | you may have added a new depot in the PickleBuilder, but not in steamworks. You need to add the new depot and publish the changes. 152 | 153 | I only see Reload Config and Open Config File in the Build Config Window: 154 | 155 | The build config window is not serialized so when you recompile your code it will unload the build config. Select the Reload Config button to load the build_config.json that is in your project. Select Open Config File to open that json file with a text editor. 156 | 157 | -------------------------------------------------------------------------------- /Assets/LitJson/JsonWriter.cs: -------------------------------------------------------------------------------- 1 | //#define UNITY3D 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Globalization; 5 | using System.IO; 6 | using System.Text; 7 | 8 | namespace LitJson 9 | { 10 | internal enum Condition 11 | { 12 | InArray, 13 | InObject, 14 | NotAProperty, 15 | Property, 16 | Value 17 | } 18 | 19 | internal class WriterContext 20 | { 21 | public int Count; 22 | public bool InArray; 23 | public bool InObject; 24 | public bool ExpectingValue; 25 | public int Padding; 26 | } 27 | 28 | /// 29 | /// Stream-like facility to output JSON text. 30 | /// 31 | public class JsonWriter 32 | { 33 | #region Fields 34 | 35 | private static NumberFormatInfo number_format; 36 | 37 | private WriterContext context; 38 | private Stack ctx_stack; 39 | private bool has_reached_end; 40 | private char[] hex_seq; 41 | private int indentation; 42 | private int indent_value; 43 | private StringBuilder inst_string_builder; 44 | private bool pretty_print; 45 | private bool validate; 46 | private TextWriter writer; 47 | 48 | #endregion 49 | 50 | 51 | #region Properties 52 | 53 | public int IndentValue { 54 | get { return indent_value; } 55 | set { 56 | indentation = (indentation / indent_value) * value; 57 | indent_value = value; 58 | } 59 | } 60 | 61 | public bool PrettyPrint { 62 | get { return pretty_print; } 63 | set { pretty_print = value; } 64 | } 65 | 66 | public TextWriter TextWriter { 67 | get { return writer; } 68 | } 69 | 70 | public bool Validate { 71 | get { return validate; } 72 | set { validate = value; } 73 | } 74 | 75 | #endregion 76 | 77 | 78 | #region Constructors 79 | 80 | static JsonWriter () 81 | { 82 | number_format = NumberFormatInfo.InvariantInfo; 83 | } 84 | 85 | public JsonWriter () 86 | { 87 | inst_string_builder = new StringBuilder (); 88 | writer = new StringWriter (inst_string_builder); 89 | 90 | Init (); 91 | } 92 | 93 | public JsonWriter (StringBuilder sb) : 94 | this (new StringWriter (sb)) 95 | { 96 | } 97 | 98 | public JsonWriter (TextWriter writer) 99 | { 100 | if (writer == null) 101 | throw new ArgumentNullException ("writer"); 102 | 103 | this.writer = writer; 104 | 105 | Init (); 106 | } 107 | 108 | #endregion 109 | 110 | 111 | #region Private Methods 112 | 113 | private void DoValidation (Condition cond) 114 | { 115 | if (!context.ExpectingValue) 116 | context.Count++; 117 | 118 | if (!validate) 119 | return; 120 | 121 | if (has_reached_end) 122 | throw new JsonException ( 123 | "A complete JSON symbol has already been written"); 124 | 125 | switch (cond) { 126 | case Condition.InArray: 127 | if (!context.InArray) 128 | throw new JsonException ( 129 | "Can't close an array here"); 130 | break; 131 | 132 | case Condition.InObject: 133 | if (!context.InObject || context.ExpectingValue) 134 | throw new JsonException ( 135 | "Can't close an object here"); 136 | break; 137 | 138 | case Condition.NotAProperty: 139 | if (context.InObject && !context.ExpectingValue) 140 | throw new JsonException ( 141 | "Expected a property"); 142 | break; 143 | 144 | case Condition.Property: 145 | if (!context.InObject || context.ExpectingValue) 146 | throw new JsonException ( 147 | "Can't add a property here"); 148 | break; 149 | 150 | case Condition.Value: 151 | if (!context.InArray && 152 | (!context.InObject || !context.ExpectingValue)) 153 | throw new JsonException ( 154 | "Can't add a value here"); 155 | 156 | break; 157 | } 158 | } 159 | 160 | private void Init () 161 | { 162 | has_reached_end = false; 163 | hex_seq = new char[4]; 164 | indentation = 0; 165 | indent_value = 4; 166 | pretty_print = false; 167 | validate = true; 168 | 169 | ctx_stack = new Stack (); 170 | context = new WriterContext (); 171 | ctx_stack.Push (context); 172 | } 173 | 174 | private static void IntToHex (int n, char[] hex) 175 | { 176 | int num; 177 | 178 | for (int i = 0; i < 4; i++) { 179 | num = n % 16; 180 | 181 | if (num < 10) 182 | hex [3 - i] = (char)('0' + num); 183 | else 184 | hex [3 - i] = (char)('A' + (num - 10)); 185 | 186 | n >>= 4; 187 | } 188 | } 189 | 190 | private void Indent () 191 | { 192 | if (pretty_print) 193 | indentation += indent_value; 194 | } 195 | 196 | 197 | private void Put (string str, bool indent = true) 198 | { 199 | if (pretty_print && !context.ExpectingValue && indent) 200 | for (int i = 0; i < indentation; i++) 201 | writer.Write (' '); 202 | 203 | writer.Write (str); 204 | } 205 | 206 | private void PutNewline () 207 | { 208 | PutNewline (true); 209 | } 210 | 211 | private void PutNewline (bool add_comma) 212 | { 213 | if (add_comma && !context.ExpectingValue && 214 | context.Count > 1) 215 | writer.Write (','); 216 | 217 | if (pretty_print && !context.ExpectingValue) 218 | writer.Write ('\n'); 219 | } 220 | 221 | private void PutString (string str) 222 | { 223 | Put (String.Empty); 224 | 225 | writer.Write ('"'); 226 | 227 | int n = str.Length; 228 | for (int i = 0; i < n; i++) { 229 | switch (str [i]) { 230 | case '\n': 231 | writer.Write ("\\n"); 232 | continue; 233 | 234 | case '\r': 235 | writer.Write ("\\r"); 236 | continue; 237 | 238 | case '\t': 239 | writer.Write ("\\t"); 240 | continue; 241 | 242 | case '"': 243 | case '\\': 244 | writer.Write ('\\'); 245 | writer.Write (str [i]); 246 | continue; 247 | 248 | case '\f': 249 | writer.Write ("\\f"); 250 | continue; 251 | 252 | case '\b': 253 | writer.Write ("\\b"); 254 | continue; 255 | } 256 | 257 | if ((int)str [i] >= 32 && (int)str [i] <= 126) { 258 | writer.Write (str [i]); 259 | continue; 260 | } 261 | 262 | // Default, turn into a \uXXXX sequence 263 | IntToHex ((int)str [i], hex_seq); 264 | writer.Write ("\\u"); 265 | writer.Write (hex_seq); 266 | } 267 | 268 | writer.Write ('"'); 269 | } 270 | 271 | private void Unindent () 272 | { 273 | if (pretty_print) 274 | indentation -= indent_value; 275 | } 276 | 277 | #endregion 278 | 279 | 280 | public override string ToString () 281 | { 282 | if (inst_string_builder == null) 283 | return String.Empty; 284 | 285 | return inst_string_builder.ToString (); 286 | } 287 | 288 | public void Reset () 289 | { 290 | has_reached_end = false; 291 | 292 | ctx_stack.Clear (); 293 | context = new WriterContext (); 294 | ctx_stack.Push (context); 295 | 296 | if (inst_string_builder != null) 297 | inst_string_builder.Remove (0, inst_string_builder.Length); 298 | } 299 | 300 | public void Write (bool boolean) 301 | { 302 | DoValidation (Condition.Value); 303 | PutNewline (); 304 | 305 | Put (boolean ? "true" : "false"); 306 | 307 | context.ExpectingValue = false; 308 | } 309 | 310 | public void Write (decimal number) 311 | { 312 | DoValidation (Condition.Value); 313 | PutNewline (); 314 | 315 | int intTest = 0; 316 | if(int.TryParse(number.ToString(), out intTest)) { 317 | Put(Convert.ToString(intTest)); 318 | } else { 319 | Put(Convert.ToString(number, number_format)); 320 | } 321 | 322 | context.ExpectingValue = false; 323 | } 324 | 325 | public void Write (double number) 326 | { 327 | DoValidation (Condition.Value); 328 | PutNewline (); 329 | 330 | int intTest = 0; 331 | if(int.TryParse(number.ToString(), out intTest)) { 332 | Put(Convert.ToString(intTest)); 333 | } else { 334 | string str = Convert.ToString(number, number_format); 335 | Put(str); 336 | 337 | if(str.IndexOf('.') == -1 && 338 | str.IndexOf('E') == -1) 339 | writer.Write(".0"); 340 | } 341 | 342 | context.ExpectingValue = false; 343 | } 344 | 345 | public void Write (int number) 346 | { 347 | DoValidation (Condition.Value); 348 | PutNewline (); 349 | 350 | int intTest = 0; 351 | if(int.TryParse(number.ToString(), out intTest)) { 352 | Put(Convert.ToString(intTest)); 353 | } else { 354 | Put(Convert.ToString(number, number_format)); 355 | } 356 | 357 | context.ExpectingValue = false; 358 | } 359 | 360 | public void Write (long number) 361 | { 362 | DoValidation (Condition.Value); 363 | PutNewline (); 364 | int intTest = 0; 365 | if(int.TryParse(number.ToString(), out intTest)) { 366 | Put(Convert.ToString(intTest)); 367 | } else { 368 | Put(Convert.ToString(number, number_format)); 369 | } 370 | context.ExpectingValue = false; 371 | } 372 | 373 | public void Write (string str) 374 | { 375 | DoValidation (Condition.Value); 376 | PutNewline (); 377 | 378 | if (str == null) 379 | Put ("null"); 380 | else 381 | PutString (str); 382 | 383 | context.ExpectingValue = false; 384 | } 385 | 386 | // [CLSCompliant (false)] 387 | public void Write (ulong number) 388 | { 389 | DoValidation (Condition.Value); 390 | PutNewline (); 391 | 392 | int intTest = 0; 393 | if(int.TryParse(number.ToString(), out intTest)) { 394 | Put(Convert.ToString(intTest)); 395 | } else { 396 | Put(Convert.ToString(number, number_format)); 397 | } 398 | 399 | context.ExpectingValue = false; 400 | } 401 | 402 | public void WriteArrayEnd (bool hadData) 403 | { 404 | DoValidation (Condition.InArray); 405 | 406 | 407 | ctx_stack.Pop (); 408 | if(ctx_stack.Count == 1) { 409 | has_reached_end = true; 410 | } 411 | else { 412 | context = ctx_stack.Peek (); 413 | context.ExpectingValue = false; 414 | } 415 | 416 | if(hadData) { 417 | PutNewline(false); 418 | } else { 419 | context.ExpectingValue = false; 420 | } 421 | 422 | Unindent(); 423 | Put ("]", hadData); 424 | } 425 | 426 | public void WriteArrayStart () 427 | { 428 | DoValidation (Condition.NotAProperty); 429 | PutNewline (); 430 | 431 | Put ("["); 432 | 433 | context = new WriterContext (); 434 | context.InArray = true; 435 | ctx_stack.Push (context); 436 | 437 | Indent (); 438 | } 439 | 440 | public void WriteObjectEnd () 441 | { 442 | DoValidation (Condition.InObject); 443 | PutNewline (false); 444 | 445 | ctx_stack.Pop (); 446 | if (ctx_stack.Count == 1) 447 | has_reached_end = true; 448 | else { 449 | context = ctx_stack.Peek (); 450 | context.ExpectingValue = false; 451 | } 452 | 453 | Unindent (); 454 | Put ("}"); 455 | } 456 | 457 | public void WriteObjectStart () 458 | { 459 | DoValidation (Condition.NotAProperty); 460 | PutNewline (); 461 | 462 | Put ("{"); 463 | 464 | context = new WriterContext (); 465 | context.InObject = true; 466 | ctx_stack.Push (context); 467 | 468 | Indent (); 469 | } 470 | 471 | public void WritePropertyName (string property_name) 472 | { 473 | DoValidation (Condition.Property); 474 | PutNewline (); 475 | 476 | PutString (property_name); 477 | 478 | if (pretty_print) { 479 | if (property_name.Length > context.Padding) 480 | context.Padding = property_name.Length; 481 | 482 | //for (int i = context.Padding - property_name.Length; 483 | // i >= 1; i--) 484 | // writer.Write (' '); 485 | 486 | writer.Write (": "); 487 | } else 488 | writer.Write (':'); 489 | 490 | context.ExpectingValue = true; 491 | } 492 | 493 | #region Unity specific 494 | 495 | public void Write (UnityEngine.Vector2 v2) 496 | { 497 | // if (v2 == null) 498 | // return; 499 | 500 | WriteObjectStart (); 501 | Put (string.Format ("\"x\":{0},\"y\":{1}", v2.x, v2.y)); 502 | WriteObjectEnd (); 503 | } 504 | 505 | public void Write (UnityEngine.Vector3 v3) 506 | { 507 | // if (v3 == null) 508 | // return; 509 | 510 | WriteObjectStart (); 511 | Put (string.Format ("\"x\":{0},\"y\":{1},\"z\":{2}", v3.x, v3.y, v3.z)); 512 | WriteObjectEnd (); 513 | } 514 | 515 | public void Write (UnityEngine.Vector4 v4) 516 | { 517 | // if (v4 == null) 518 | // return; 519 | 520 | WriteObjectStart (); 521 | Put (string.Format ("\"x\":{0},\"y\":{1},\"z\":{2},\"w\":{3}", v4.x, v4.y, v4.z, v4.w)); 522 | WriteObjectEnd (); 523 | } 524 | 525 | public void Write (UnityEngine.Quaternion q) 526 | { 527 | // if (q == null) 528 | // return; 529 | 530 | WriteObjectStart (); 531 | Put (string.Format ("\"x\":{0},\"y\":{1},\"z\":{2},\"w\":{3}", q.x, q.y, q.z, q.w)); 532 | WriteObjectEnd (); 533 | } 534 | 535 | public void Write (UnityEngine.Matrix4x4 m) 536 | { 537 | // if (m == null) 538 | // return; 539 | 540 | WriteObjectStart (); 541 | Put (string.Format ("\"m00\":{0},\"m33\":{1},\"m23\":{2},\"m13\":{3},\"m03\":{4},\"m32\":{5},\"m12\":{6},\"m02\":{7},\"m22\":{8},\"m21\":{9},\"m11\":{10},\"m01\":{11},\"m30\":{12},\"m20\":{13},\"m10\":{14},\"m31\":{15}", 542 | m.m00, m.m33, m.m23, m.m13, m.m03, m.m32, m.m12, m.m02, m.m22, m.m21, m.m11, m.m01, m.m30, m.m20, m.m10, m.m31)); 543 | 544 | WriteObjectEnd (); 545 | } 546 | 547 | public void Write (UnityEngine.Ray r) 548 | { 549 | WriteObjectStart (); 550 | WritePropertyName ("origin"); 551 | Write (r.origin); 552 | WritePropertyName ("direction"); 553 | Write (r.direction); 554 | WriteObjectEnd (); 555 | } 556 | 557 | public void Write (UnityEngine.RaycastHit r) 558 | { 559 | WriteObjectStart (); 560 | WritePropertyName ("barycentricCoordinate"); 561 | Write (r.barycentricCoordinate); 562 | WritePropertyName ("distance"); 563 | Write (r.distance); 564 | WritePropertyName ("normal"); 565 | Write (r.normal); 566 | WritePropertyName ("point"); 567 | Write (r.point); 568 | WriteObjectEnd (); 569 | } 570 | 571 | public void Write (UnityEngine.Color c) 572 | { 573 | // if (c == null) 574 | // return; 575 | 576 | WriteObjectStart (); 577 | Put (string.Format ("\"r\":{0},\"g\":{1},\"b\":{2},\"a\":{3}", c.r, c.g, c.b, c.a)); 578 | WriteObjectEnd (); 579 | } 580 | 581 | #endregion 582 | } 583 | } 584 | -------------------------------------------------------------------------------- /Assets/LitJson/JsonReader.cs: -------------------------------------------------------------------------------- 1 | #region Header 2 | /** 3 | * JsonReader.cs 4 | * Stream-like access to JSON text. 5 | * 6 | * The authors disclaim copyright to this source code. For more details, see 7 | * the COPYING file included with this distribution. 8 | **/ 9 | #endregion 10 | 11 | 12 | using System; 13 | using System.Collections.Generic; 14 | using System.IO; 15 | using System.Text; 16 | 17 | 18 | namespace LitJson 19 | { 20 | public enum JsonToken 21 | { 22 | None, 23 | 24 | ObjectStart, 25 | PropertyName, 26 | ObjectEnd, 27 | 28 | ArrayStart, 29 | ArrayEnd, 30 | 31 | Int, 32 | Long, 33 | Double, 34 | 35 | String, 36 | 37 | Boolean, 38 | Null 39 | } 40 | 41 | 42 | public class JsonReader 43 | { 44 | #region Fields 45 | private static IDictionary> parse_table; 46 | 47 | private Stack automaton_stack; 48 | private int current_input; 49 | private int current_symbol; 50 | private bool end_of_json; 51 | private bool end_of_input; 52 | private Lexer lexer; 53 | private bool parser_in_string; 54 | private bool parser_return; 55 | private bool read_started; 56 | private TextReader reader; 57 | private bool reader_is_owned; 58 | private bool skip_non_members; 59 | private object token_value; 60 | private JsonToken token; 61 | #endregion 62 | 63 | 64 | #region Public Properties 65 | public bool AllowComments { 66 | get { return lexer.AllowComments; } 67 | set { lexer.AllowComments = value; } 68 | } 69 | 70 | public bool AllowSingleQuotedStrings { 71 | get { return lexer.AllowSingleQuotedStrings; } 72 | set { lexer.AllowSingleQuotedStrings = value; } 73 | } 74 | 75 | public bool SkipNonMembers { 76 | get { return skip_non_members; } 77 | set { skip_non_members = value; } 78 | } 79 | 80 | public bool EndOfInput { 81 | get { return end_of_input; } 82 | } 83 | 84 | public bool EndOfJson { 85 | get { return end_of_json; } 86 | } 87 | 88 | public JsonToken Token { 89 | get { return token; } 90 | } 91 | 92 | public object Value { 93 | get { return token_value; } 94 | } 95 | #endregion 96 | 97 | 98 | #region Constructors 99 | static JsonReader () 100 | { 101 | PopulateParseTable (); 102 | } 103 | 104 | public JsonReader (string json_text) : 105 | this (new StringReader (json_text), true) 106 | { 107 | } 108 | 109 | public JsonReader (TextReader reader) : 110 | this (reader, false) 111 | { 112 | } 113 | 114 | private JsonReader (TextReader reader, bool owned) 115 | { 116 | if (reader == null) 117 | throw new ArgumentNullException ("reader"); 118 | 119 | parser_in_string = false; 120 | parser_return = false; 121 | 122 | read_started = false; 123 | automaton_stack = new Stack (); 124 | automaton_stack.Push ((int) ParserToken.End); 125 | automaton_stack.Push ((int) ParserToken.Text); 126 | 127 | lexer = new Lexer (reader); 128 | 129 | end_of_input = false; 130 | end_of_json = false; 131 | 132 | skip_non_members = true; 133 | 134 | this.reader = reader; 135 | reader_is_owned = owned; 136 | } 137 | #endregion 138 | 139 | 140 | #region Static Methods 141 | private static void PopulateParseTable () 142 | { 143 | // See section A.2. of the manual for details 144 | parse_table = new Dictionary> (); 145 | 146 | TableAddRow (ParserToken.Array); 147 | TableAddCol (ParserToken.Array, '[', 148 | '[', 149 | (int) ParserToken.ArrayPrime); 150 | 151 | TableAddRow (ParserToken.ArrayPrime); 152 | TableAddCol (ParserToken.ArrayPrime, '"', 153 | (int) ParserToken.Value, 154 | 155 | (int) ParserToken.ValueRest, 156 | ']'); 157 | TableAddCol (ParserToken.ArrayPrime, '[', 158 | (int) ParserToken.Value, 159 | (int) ParserToken.ValueRest, 160 | ']'); 161 | TableAddCol (ParserToken.ArrayPrime, ']', 162 | ']'); 163 | TableAddCol (ParserToken.ArrayPrime, '{', 164 | (int) ParserToken.Value, 165 | (int) ParserToken.ValueRest, 166 | ']'); 167 | TableAddCol (ParserToken.ArrayPrime, (int) ParserToken.Number, 168 | (int) ParserToken.Value, 169 | (int) ParserToken.ValueRest, 170 | ']'); 171 | TableAddCol (ParserToken.ArrayPrime, (int) ParserToken.True, 172 | (int) ParserToken.Value, 173 | (int) ParserToken.ValueRest, 174 | ']'); 175 | TableAddCol (ParserToken.ArrayPrime, (int) ParserToken.False, 176 | (int) ParserToken.Value, 177 | (int) ParserToken.ValueRest, 178 | ']'); 179 | TableAddCol (ParserToken.ArrayPrime, (int) ParserToken.Null, 180 | (int) ParserToken.Value, 181 | (int) ParserToken.ValueRest, 182 | ']'); 183 | 184 | TableAddRow (ParserToken.Object); 185 | TableAddCol (ParserToken.Object, '{', 186 | '{', 187 | (int) ParserToken.ObjectPrime); 188 | 189 | TableAddRow (ParserToken.ObjectPrime); 190 | TableAddCol (ParserToken.ObjectPrime, '"', 191 | (int) ParserToken.Pair, 192 | (int) ParserToken.PairRest, 193 | '}'); 194 | TableAddCol (ParserToken.ObjectPrime, '}', 195 | '}'); 196 | 197 | TableAddRow (ParserToken.Pair); 198 | TableAddCol (ParserToken.Pair, '"', 199 | (int) ParserToken.String, 200 | ':', 201 | (int) ParserToken.Value); 202 | 203 | TableAddRow (ParserToken.PairRest); 204 | TableAddCol (ParserToken.PairRest, ',', 205 | ',', 206 | (int) ParserToken.Pair, 207 | (int) ParserToken.PairRest); 208 | TableAddCol (ParserToken.PairRest, '}', 209 | (int) ParserToken.Epsilon); 210 | 211 | TableAddRow (ParserToken.String); 212 | TableAddCol (ParserToken.String, '"', 213 | '"', 214 | (int) ParserToken.CharSeq, 215 | '"'); 216 | 217 | TableAddRow (ParserToken.Text); 218 | TableAddCol (ParserToken.Text, '[', 219 | (int) ParserToken.Array); 220 | TableAddCol (ParserToken.Text, '{', 221 | (int) ParserToken.Object); 222 | 223 | TableAddRow (ParserToken.Value); 224 | TableAddCol (ParserToken.Value, '"', 225 | (int) ParserToken.String); 226 | TableAddCol (ParserToken.Value, '[', 227 | (int) ParserToken.Array); 228 | TableAddCol (ParserToken.Value, '{', 229 | (int) ParserToken.Object); 230 | TableAddCol (ParserToken.Value, (int) ParserToken.Number, 231 | (int) ParserToken.Number); 232 | TableAddCol (ParserToken.Value, (int) ParserToken.True, 233 | (int) ParserToken.True); 234 | TableAddCol (ParserToken.Value, (int) ParserToken.False, 235 | (int) ParserToken.False); 236 | TableAddCol (ParserToken.Value, (int) ParserToken.Null, 237 | (int) ParserToken.Null); 238 | 239 | TableAddRow (ParserToken.ValueRest); 240 | TableAddCol (ParserToken.ValueRest, ',', 241 | ',', 242 | (int) ParserToken.Value, 243 | (int) ParserToken.ValueRest); 244 | TableAddCol (ParserToken.ValueRest, ']', 245 | (int) ParserToken.Epsilon); 246 | } 247 | 248 | private static void TableAddCol (ParserToken row, int col, 249 | params int[] symbols) 250 | { 251 | parse_table[(int) row].Add (col, symbols); 252 | } 253 | 254 | private static void TableAddRow (ParserToken rule) 255 | { 256 | parse_table.Add ((int) rule, new Dictionary ()); 257 | } 258 | #endregion 259 | 260 | 261 | #region Private Methods 262 | private void ProcessNumber (string number) 263 | { 264 | if (number.IndexOf ('.') != -1 || 265 | number.IndexOf ('e') != -1 || 266 | number.IndexOf ('E') != -1) { 267 | 268 | double n_double; 269 | if (Double.TryParse (number, out n_double)) { 270 | token = JsonToken.Double; 271 | token_value = n_double; 272 | 273 | return; 274 | } 275 | } 276 | 277 | int n_int32; 278 | if (Int32.TryParse (number, out n_int32)) { 279 | token = JsonToken.Int; 280 | token_value = n_int32; 281 | 282 | return; 283 | } 284 | 285 | long n_int64; 286 | if (Int64.TryParse (number, out n_int64)) { 287 | token = JsonToken.Long; 288 | token_value = n_int64; 289 | 290 | return; 291 | } 292 | 293 | ulong n_uint64; 294 | if (UInt64.TryParse(number, out n_uint64)) 295 | { 296 | token = JsonToken.Long; 297 | token_value = n_uint64; 298 | 299 | return; 300 | } 301 | 302 | // Shouldn't happen, but just in case, return something 303 | token = JsonToken.Int; 304 | token_value = 0; 305 | } 306 | 307 | private void ProcessSymbol () 308 | { 309 | if (current_symbol == '[') { 310 | token = JsonToken.ArrayStart; 311 | parser_return = true; 312 | 313 | } else if (current_symbol == ']') { 314 | token = JsonToken.ArrayEnd; 315 | parser_return = true; 316 | 317 | } else if (current_symbol == '{') { 318 | token = JsonToken.ObjectStart; 319 | parser_return = true; 320 | 321 | } else if (current_symbol == '}') { 322 | token = JsonToken.ObjectEnd; 323 | parser_return = true; 324 | 325 | } else if (current_symbol == '"') { 326 | if (parser_in_string) { 327 | parser_in_string = false; 328 | 329 | parser_return = true; 330 | 331 | } else { 332 | if (token == JsonToken.None) 333 | token = JsonToken.String; 334 | 335 | parser_in_string = true; 336 | } 337 | 338 | } else if (current_symbol == (int) ParserToken.CharSeq) { 339 | token_value = lexer.StringValue; 340 | 341 | } else if (current_symbol == (int) ParserToken.False) { 342 | token = JsonToken.Boolean; 343 | token_value = false; 344 | parser_return = true; 345 | 346 | } else if (current_symbol == (int) ParserToken.Null) { 347 | token = JsonToken.Null; 348 | parser_return = true; 349 | 350 | } else if (current_symbol == (int) ParserToken.Number) { 351 | ProcessNumber (lexer.StringValue); 352 | 353 | parser_return = true; 354 | 355 | } else if (current_symbol == (int) ParserToken.Pair) { 356 | token = JsonToken.PropertyName; 357 | 358 | } else if (current_symbol == (int) ParserToken.True) { 359 | token = JsonToken.Boolean; 360 | token_value = true; 361 | parser_return = true; 362 | 363 | } 364 | } 365 | 366 | private bool ReadToken () 367 | { 368 | if (end_of_input) 369 | return false; 370 | 371 | lexer.NextToken (); 372 | 373 | if (lexer.EndOfInput) { 374 | Close (); 375 | 376 | return false; 377 | } 378 | 379 | current_input = lexer.Token; 380 | 381 | return true; 382 | } 383 | #endregion 384 | 385 | 386 | public void Close () 387 | { 388 | if (end_of_input) 389 | return; 390 | 391 | end_of_input = true; 392 | end_of_json = true; 393 | 394 | if (reader_is_owned) 395 | reader.Close (); 396 | 397 | reader = null; 398 | } 399 | 400 | public bool Read () 401 | { 402 | if (end_of_input) 403 | return false; 404 | 405 | if (end_of_json) { 406 | end_of_json = false; 407 | automaton_stack.Clear (); 408 | automaton_stack.Push ((int) ParserToken.End); 409 | automaton_stack.Push ((int) ParserToken.Text); 410 | } 411 | 412 | parser_in_string = false; 413 | parser_return = false; 414 | 415 | token = JsonToken.None; 416 | token_value = null; 417 | 418 | if (! read_started) { 419 | read_started = true; 420 | 421 | if (! ReadToken ()) 422 | return false; 423 | } 424 | 425 | 426 | int[] entry_symbols; 427 | 428 | while (true) { 429 | if (parser_return) { 430 | if (automaton_stack.Peek () == (int) ParserToken.End) 431 | end_of_json = true; 432 | 433 | return true; 434 | } 435 | 436 | current_symbol = automaton_stack.Pop (); 437 | 438 | ProcessSymbol (); 439 | 440 | if (current_symbol == current_input) { 441 | if (! ReadToken ()) { 442 | if (automaton_stack.Peek () != (int) ParserToken.End) 443 | throw new JsonException ( 444 | "Input doesn't evaluate to proper JSON text"); 445 | 446 | if (parser_return) 447 | return true; 448 | 449 | return false; 450 | } 451 | 452 | continue; 453 | } 454 | 455 | try { 456 | 457 | entry_symbols = 458 | parse_table[current_symbol][current_input]; 459 | 460 | } catch (KeyNotFoundException e) { 461 | throw new JsonException ((ParserToken) current_input, e); 462 | } 463 | 464 | if (entry_symbols[0] == (int) ParserToken.Epsilon) 465 | continue; 466 | 467 | for (int i = entry_symbols.Length - 1; i >= 0; i--) 468 | automaton_stack.Push (entry_symbols[i]); 469 | } 470 | } 471 | 472 | } 473 | } 474 | -------------------------------------------------------------------------------- /Assets/PickleTools/PickleBuilder/Editor/BuildScript.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor; 2 | using System.IO; 3 | using LitJson; 4 | using System.Diagnostics; 5 | using System.Collections; 6 | using PickleTools.UnityEditor; 7 | 8 | namespace PickleTools.PickleBuilder { 9 | 10 | public enum BuildType { 11 | none = 0, 12 | win_x86 = 1, 13 | win_x64 = 2, 14 | mac = 3, 15 | max_x86 = 4, 16 | mac_x64 = 5, 17 | linux = 6, 18 | linux_x86 = 7, 19 | linux_x64 = 8, 20 | android = 9, 21 | blackberry = 10, 22 | ios = 11, 23 | ps3 = 12, 24 | ps4 = 13, 25 | vita = 14, 26 | xbox_360 = 15, 27 | xbone = 16, 28 | win_phone_8 = 17, 29 | win_store_app = 18, 30 | web = 19, 31 | samsung_tv = 20, 32 | tizen = 21, 33 | tv_os = 22, 34 | web_gl = 23, 35 | wiiu = 24, 36 | new_3ds = 25, 37 | } 38 | 39 | public class ConfigData{ 40 | public string BuildPath = ""; 41 | public string FileName = ""; 42 | public int VersionNumberMajor = 0; 43 | public int VersionNumberMinor = 0; 44 | public int VersionNumberPatch = 0; 45 | public bool AutoIncrement = true; 46 | public string[] Platforms = new string[0]; 47 | public string[] Scenes = new string[0]; 48 | public bool Preview = false; 49 | public SteamData SteamData = new SteamData(); 50 | } 51 | 52 | public class SteamData{ 53 | public bool BuildToSteam = false; 54 | public string SDKPath = "C:\\steamworks_sdk\\tools\\ContentBuilder\\"; 55 | public string SteamCMDPath = "builder\\steamcmd.exe"; 56 | public DepotData[] Depots = new DepotData[0]; 57 | public AppBuildData AppBuild = new AppBuildData(); 58 | public BatchData BatchData = new BatchData(); 59 | public bool UseExistingBuilds = false; 60 | public string ExistingBuildsPath = "C:\\"; 61 | } 62 | 63 | public class DepotData { 64 | public string DepotID = "000001"; 65 | public string BuildType = "win_x86"; 66 | public string[] IgnoreFiles = new string[1]{"*.pdb"}; 67 | } 68 | 69 | public class AppBuildData { 70 | public string AppID = "000000"; 71 | public string Description = "Description of this build."; 72 | public bool Preview = true; 73 | } 74 | 75 | public class BatchData { 76 | 77 | } 78 | 79 | public class BuildScript { 80 | 81 | EditorCoroutine editorCoroutine; 82 | 83 | public static readonly string CONFIG_FILENAME = "build_config.json"; 84 | 85 | static string configPath = ""; 86 | public static string ConfigPath { 87 | get { 88 | configPath = UnityEngine.Application.dataPath + 89 | "/PickleTools/PickleBuilder/Editor/" + 90 | CONFIG_FILENAME; 91 | System.Console.WriteLine("Config Path: " + configPath); 92 | return configPath; 93 | } 94 | } 95 | 96 | static public void EditConfig(){ 97 | if(!System.IO.File.Exists(ConfigPath)){ 98 | ConfigData newData = new ConfigData(); 99 | WriteData(newData); 100 | } 101 | 102 | Process.Start(ConfigPath); 103 | } 104 | 105 | public static void PerformBuildCommand(){ 106 | string accountName = GetArg("-account"); 107 | string password = GetArg("-password"); 108 | PerformBuild(accountName, password); 109 | } 110 | 111 | private static string GetArg(string name){ 112 | var args = System.Environment.GetCommandLineArgs(); 113 | for(int i = 0; i < args.Length; i ++){ 114 | if(args[i] == name && args.Length > i + 1){ 115 | return args[i + 1]; 116 | } 117 | } 118 | return null; 119 | } 120 | 121 | [MenuItem("Build/Build From Config")] 122 | public static void PerformBuild(string accountName = "", string password = ""){ 123 | if(!System.IO.File.Exists(ConfigPath)){ 124 | ConfigData newData = new ConfigData(); 125 | WriteData(newData); 126 | } 127 | 128 | ConfigData data = JsonMapper.ToObject(File.ReadAllText(ConfigPath)); 129 | 130 | int versionNumberMajor = 0; 131 | int versionNumberMinor = 0; 132 | int versionNumberPatch = 1; 133 | if(data.VersionNumberMajor != 0){ 134 | versionNumberMajor = data.VersionNumberMajor; 135 | } else { 136 | data.VersionNumberMajor = versionNumberMajor; 137 | } 138 | if(data.VersionNumberMinor != 0){ 139 | versionNumberMinor = data.VersionNumberMinor; 140 | } else { 141 | data.VersionNumberMinor = versionNumberMinor; 142 | } 143 | if(data.VersionNumberPatch != 1){ 144 | versionNumberPatch = data.VersionNumberPatch; 145 | } else { 146 | data.VersionNumberPatch = versionNumberPatch; 147 | } 148 | 149 | bool autoIncrement = true; 150 | if(data.AutoIncrement != true){ 151 | autoIncrement = data.AutoIncrement; 152 | } else { 153 | data.AutoIncrement = autoIncrement; 154 | } 155 | //if(autoIncrement){ 156 | // data.VersionNumberPatch = versionNumberPatch = versionNumberPatch + 1; 157 | //} 158 | 159 | string completeVersionString = versionNumberMajor + "_" + versionNumberMinor + "_" + versionNumberPatch + "__" + 160 | System.DateTime.UtcNow.ToString().Replace('/', '_').Replace(' ', '_').Replace(':', '_'); 161 | string path = System.Environment.CurrentDirectory + "/build" + "/" + completeVersionString + "/"; 162 | if(data.BuildPath != ""){ 163 | path = data.BuildPath + "/" + completeVersionString + "/"; 164 | } else { 165 | data.BuildPath = path; 166 | } 167 | string fileName = "Game Name"; 168 | if(data.FileName != ""){ 169 | fileName = data.FileName; 170 | } else { 171 | data.FileName = fileName; 172 | } 173 | 174 | string[] platforms = new string[1]{"win_x86"}; 175 | if(data.Platforms.Length > 0){ 176 | platforms = data.Platforms; 177 | } else { 178 | data.Platforms = platforms; 179 | } 180 | 181 | string[] scenes = { 182 | }; 183 | if(data.Scenes.Length > 0){ 184 | scenes = data.Scenes; 185 | } else { 186 | data.Scenes = scenes; 187 | } 188 | 189 | 190 | WriteData(data); 191 | 192 | for(int p = 0; p < platforms.Length; p ++){ 193 | string platformPath = path + platforms[p]; 194 | if(!System.IO.Directory.Exists(platformPath)){ 195 | System.IO.Directory.CreateDirectory(platformPath); 196 | } 197 | string buildPath = platformPath + "/" + fileName; 198 | UnityEngine.Debug.LogWarning("Building at: " + buildPath); 199 | // skip the actual build if this is just a preview. 200 | if(data.Preview){ 201 | UnityEngine.Debug.LogWarning("but skipping the build due to this being a preview..."); 202 | continue; 203 | } 204 | switch(platforms[p]){ 205 | case "win_x86": 206 | buildPath += ".exe"; 207 | BuildPipeline.BuildPlayer(scenes, buildPath, BuildTarget.StandaloneWindows, BuildOptions.None); 208 | break; 209 | case "win_x64": 210 | buildPath += ".exe"; 211 | BuildPipeline.BuildPlayer(scenes, buildPath, BuildTarget.StandaloneWindows64, BuildOptions.None); 212 | break; 213 | case "mac": 214 | BuildPipeline.BuildPlayer(scenes, buildPath, BuildTarget.StandaloneOSXUniversal, BuildOptions.None); 215 | break; 216 | case "mac_x86": 217 | BuildPipeline.BuildPlayer(scenes, buildPath, BuildTarget.StandaloneOSXIntel, BuildOptions.None); 218 | break; 219 | case "mac_x64": 220 | BuildPipeline.BuildPlayer(scenes, buildPath, BuildTarget.StandaloneOSXIntel64, BuildOptions.None); 221 | break; 222 | case "linux": 223 | BuildPipeline.BuildPlayer(scenes, buildPath, BuildTarget.StandaloneLinuxUniversal, BuildOptions.None); 224 | break; 225 | case "linux_x86": 226 | BuildPipeline.BuildPlayer(scenes, buildPath, BuildTarget.StandaloneLinux, BuildOptions.None); 227 | break; 228 | case "linux_x64": 229 | BuildPipeline.BuildPlayer(scenes, buildPath, BuildTarget.StandaloneLinux64, BuildOptions.None); 230 | break; 231 | case "android": 232 | BuildPipeline.BuildPlayer(scenes, buildPath, BuildTarget.Android, BuildOptions.None); 233 | break; 234 | case "blackberry": 235 | BuildPipeline.BuildPlayer(scenes, buildPath, BuildTarget.BlackBerry, BuildOptions.None); 236 | break; 237 | case "ios": 238 | BuildPipeline.BuildPlayer(scenes, buildPath, BuildTarget.iOS, BuildOptions.None); 239 | break; 240 | case "ps3": 241 | BuildPipeline.BuildPlayer(scenes, buildPath, BuildTarget.PS3, BuildOptions.None); 242 | break; 243 | case "ps4": 244 | BuildPipeline.BuildPlayer(scenes, buildPath, BuildTarget.PS4, BuildOptions.None); 245 | break; 246 | case "vita": 247 | BuildPipeline.BuildPlayer(scenes, buildPath, BuildTarget.PSP2, BuildOptions.None); 248 | break; 249 | case "360": 250 | BuildPipeline.BuildPlayer(scenes, buildPath, BuildTarget.XBOX360, BuildOptions.None); 251 | break; 252 | case "xbone": 253 | BuildPipeline.BuildPlayer(scenes, buildPath, BuildTarget.XboxOne, BuildOptions.None); 254 | break; 255 | case "win_phone_8": 256 | BuildPipeline.BuildPlayer(scenes, buildPath, BuildTarget.WP8Player, BuildOptions.None); 257 | break; 258 | case "win_store_app": 259 | BuildPipeline.BuildPlayer(scenes, buildPath, BuildTarget.WSAPlayer, BuildOptions.None); 260 | break; 261 | case "web": 262 | data.FileName = fileName; 263 | BuildPipeline.BuildPlayer(scenes, buildPath, BuildTarget.WebPlayer, BuildOptions.None); 264 | break; 265 | case "new3ds": 266 | BuildPipeline.BuildPlayer(scenes, buildPath, BuildTarget.Nintendo3DS, BuildOptions.None); 267 | break; 268 | case "samsung_tv": 269 | BuildPipeline.BuildPlayer(scenes, buildPath, BuildTarget.SamsungTV, BuildOptions.None); 270 | break; 271 | case "tizen": 272 | BuildPipeline.BuildPlayer(scenes, buildPath, BuildTarget.Tizen, BuildOptions.None); 273 | break; 274 | case "tv_os": 275 | BuildPipeline.BuildPlayer(scenes, buildPath, BuildTarget.tvOS, BuildOptions.None); 276 | break; 277 | case "web_gl": 278 | BuildPipeline.BuildPlayer(scenes, buildPath, BuildTarget.WebGL, BuildOptions.None); 279 | break; 280 | case "wiiu": 281 | BuildPipeline.BuildPlayer(scenes, buildPath, BuildTarget.WiiU, BuildOptions.None); 282 | break; 283 | default: 284 | BuildPipeline.BuildPlayer(scenes, buildPath, BuildTarget.StandaloneWindows, BuildOptions.None); 285 | break; 286 | } 287 | } 288 | 289 | // TODO: Make sure if we aren't on windows, we just create the appropriate files 290 | 291 | SteamData steamData = new SteamData(); 292 | if(data.SteamData.SDKPath != ""){ 293 | steamData = data.SteamData; 294 | } else { 295 | data.SteamData = steamData; 296 | } 297 | 298 | if(steamData.BuildToSteam){ 299 | PushToSteam(accountName, password, completeVersionString); 300 | } 301 | } 302 | 303 | static void PushToSteam(string accountName, string password, string completeVersionString){ 304 | 305 | if(!System.IO.File.Exists(ConfigPath)){ 306 | ConfigData newData = new ConfigData(); 307 | WriteData(newData); 308 | } 309 | 310 | ConfigData data = JsonMapper.ToObject(File.ReadAllText(ConfigPath)); 311 | SteamData steamData = new SteamData(); 312 | if(data.SteamData.SDKPath != ""){ 313 | steamData = data.SteamData; 314 | } else { 315 | data.SteamData = steamData; 316 | } 317 | // get builds path 318 | string path = ""; 319 | if(data.SteamData.UseExistingBuilds){ 320 | path = data.SteamData.ExistingBuildsPath; 321 | } else { 322 | int versionNumberMajor = 0; 323 | int versionNumberMinor = 0; 324 | int versionNumberPatch = 1; 325 | if(data.VersionNumberMajor != 0){ 326 | versionNumberMajor = data.VersionNumberMajor; 327 | } else { 328 | data.VersionNumberMajor = versionNumberMajor; 329 | } 330 | if(data.VersionNumberMinor != 0){ 331 | versionNumberMinor = data.VersionNumberMinor; 332 | } else { 333 | data.VersionNumberMinor = versionNumberMinor; 334 | } 335 | if(data.VersionNumberPatch != 1){ 336 | versionNumberPatch = data.VersionNumberPatch; 337 | } else { 338 | data.VersionNumberPatch = versionNumberPatch; 339 | } 340 | path = System.Environment.CurrentDirectory + "/build" + "/" + completeVersionString + "/"; 341 | if(data.BuildPath != ""){ 342 | path = data.BuildPath + "/" + completeVersionString + "/"; 343 | } else { 344 | data.BuildPath = path; 345 | } 346 | } 347 | 348 | // create content directories for each build 349 | string scriptsPath = steamData.SDKPath + "/scripts/"; 350 | if(!System.IO.Directory.Exists(scriptsPath)){ 351 | UnityEngine.Debug.LogError("[BuildScript.cs]: Could not find the scripts folder of the Steamworks SDK. " + 352 | "Please make sure your SDK path is correct and that this path exists: \n" + scriptsPath); 353 | 354 | } else { 355 | string textFile = ""; 356 | byte[] textBytes = new byte[0]; 357 | // 1. generate a vdf file for each depot 358 | for(int d = 0; d < steamData.Depots.Length; d ++){ 359 | string contentPath = ".\\" + steamData.Depots[d].BuildType + "\\*"; 360 | 361 | FileStream depotStream = System.IO.File.Create(scriptsPath + "depot_build_" + steamData.Depots[d].DepotID + ".vdf"); 362 | textFile = 363 | "\"DepotBuildConfig\"\n" + 364 | "{\n" + 365 | "\t\"DepotID\" \"" + steamData.Depots[d].DepotID + "\"\n\n" + 366 | 367 | "\t\"ContentRoot\" \"\"\n\n" + 368 | 369 | "\t\"FileMapping\"\n"+ 370 | "\t{\n" + 371 | "\t\t\"LocalPath\" \"" + contentPath + "\"\n\n" + 372 | 373 | "\t\t\"DepotPath\" \".\"\n\n" + 374 | 375 | "\t\t\"recursive\" \"1\"\n" + 376 | "\t}\n\n"; 377 | 378 | for(int e = 0; e < steamData.Depots[d].IgnoreFiles.Length; e ++){ 379 | textFile += "\t\"FileExclusion\" \"" + steamData.Depots[d].IgnoreFiles[e] + "\"\n"; 380 | } 381 | textFile += "}"; 382 | 383 | textBytes = System.Text.UTF8Encoding.UTF8.GetBytes(textFile); 384 | depotStream.Write(textBytes, 0, textBytes.Length); 385 | depotStream.Flush(); 386 | depotStream.Close(); 387 | } 388 | 389 | // 2. generate a vdf file for the appdata 390 | int previewValue = 0; 391 | if(steamData.AppBuild.Preview){ 392 | previewValue = 1; 393 | } 394 | FileStream appDataStream = System.IO.File.Create(scriptsPath + "app_build_" + steamData.AppBuild.AppID + ".vdf"); 395 | textFile = 396 | "\"appbuild\"\n" + 397 | "{\n" + 398 | "\t\"appid\" \"" + steamData.AppBuild.AppID + "\"\n" + 399 | "\t\"desc\" \"" + steamData.AppBuild.Description + "\"\n" + 400 | "\t\"buildoutput\" \"..\\output\\\"\n" + 401 | "\t\"contentroot\" \"" + path + "\"\n" + 402 | "\t\"setlive\" \"\"\n" + 403 | "\t\"preview\" \"" + previewValue + "\"\n" + 404 | "\t\"local\" \"\"\n\n" + 405 | 406 | "\t\"depots\"\n" + 407 | "\t{\n"; 408 | for(int d = 0; d < steamData.Depots.Length; d ++){ 409 | textFile += "\t\t\"" + steamData.Depots[d].DepotID + "\" \"" + ( "depot_build_" + steamData.Depots[d].DepotID + ".vdf") + "\"\n"; 410 | } 411 | textFile += "\t}\n"; 412 | textFile += "}"; 413 | 414 | textBytes = System.Text.UTF8Encoding.UTF8.GetBytes(textFile); 415 | appDataStream.Write(textBytes, 0, textBytes.Length); 416 | appDataStream.Flush(); 417 | appDataStream.Close(); 418 | 419 | // 3. run the sdk build function 420 | UnityEngine.Debug.LogWarning("Starting steam publishing process..."); 421 | string appBuildFilePath = steamData.SDKPath + "/scripts/app_build_" + steamData.AppBuild.AppID + ".vdf"; 422 | ProcessStartInfo startInfo = new ProcessStartInfo() 423 | { 424 | FileName = steamData.SteamCMDPath, 425 | Arguments = "+login " + 426 | accountName + " " + 427 | password + " " + 428 | "+run_app_build_http " + appBuildFilePath + " +quit", 429 | UseShellExecute = false, 430 | RedirectStandardOutput = true, 431 | }; 432 | Process proc = new Process() 433 | { 434 | StartInfo = startInfo, 435 | }; 436 | proc.Start(); 437 | // Editor coroutine is being exited early by unity, so we'll just skip it for now 438 | //EditorCoroutine.start(DisplaySteamLog(proc, steamData.SDKPath + "/scripts/")); 439 | while (!proc.StandardOutput.EndOfStream) { 440 | UnityEngine.Debug.Log(proc.StandardOutput.ReadLine()); 441 | } 442 | UnityEngine.Debug.Log("[BuildScript.cs]: Steam upload process exited."); 443 | Process.Start(steamData.SDKPath + "/scripts/"); 444 | } 445 | } 446 | 447 | static IEnumerator DisplaySteamLog(Process proc, string scriptPath){ 448 | while(!proc.StandardOutput.EndOfStream){ 449 | UnityEngine.Debug.Log (proc.StandardOutput.ReadLine ()); 450 | yield return null; 451 | } 452 | UnityEngine.Debug.Log ("[BuildScript.cs]: Steam upload process exited."); 453 | // 4. open the scripts folder for validation 454 | Process.Start(scriptPath); 455 | // 5. open steamworks app page for validation 456 | } 457 | 458 | public static void WriteData(ConfigData newData){ 459 | FileStream configStream = File.Create(ConfigPath); 460 | JsonWriter writer = new JsonWriter(); 461 | writer.PrettyPrint = true; 462 | JsonMapper.ToJson(newData, writer); 463 | string newDataJson = writer.TextWriter.ToString(); 464 | byte[] jsonBytes = System.Text.UTF8Encoding.UTF8.GetBytes(newDataJson); 465 | configStream.Write(jsonBytes, 0, jsonBytes.Length); 466 | configStream.Flush(); 467 | configStream.Close(); 468 | string importPath = ConfigPath.Substring(ConfigPath.IndexOf("Assets/")); 469 | AssetDatabase.ImportAsset(importPath); 470 | } 471 | } 472 | 473 | } -------------------------------------------------------------------------------- /Assets/PickleTools/PickleBuilder/Editor/BuildConfigWindow.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEditor; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using LitJson; 6 | using PickleTools.Extensions.ArrayExtensions; 7 | using PickleTools.UnityEditor; 8 | using System.Diagnostics; 9 | using System.Collections; 10 | 11 | namespace PickleTools.PickleBuilder { 12 | public class BuildConfigWindow : EditorWindow { 13 | 14 | // TODO: 15 | // [ ] test on windows 16 | // [ ] add developer name 17 | // [ ] set unity build settings of name, developer, and version from this config 18 | 19 | ConfigData data; 20 | string helpInfo = ""; 21 | Vector2 scrollPosition = Vector2.zero; 22 | 23 | SortableList sceneList; 24 | 25 | GUISkin skin; 26 | private static readonly string BUILD_CONFIG_SKIN = "Assets/PickleTools/PickleBuilder/Editor/build_config_skin.guiskin"; 27 | // private static readonly string SOURCE = "source"; 28 | 29 | [MenuItem("Build/Build Config Window")] 30 | public static void ShowEditor(){ 31 | BuildConfigWindow window = GetWindow(); 32 | window.titleContent = new GUIContent("Build Config"); 33 | window.Show(); 34 | window.Initialize(); 35 | } 36 | 37 | // Object[] scenes = new Object[0]; 38 | 39 | void Initialize(){ 40 | if(!System.IO.File.Exists(BuildScript.ConfigPath)){ 41 | ConfigData newData = new ConfigData(); 42 | BuildScript.WriteData(newData); 43 | } 44 | 45 | data = JsonMapper.ToObject(File.ReadAllText(BuildScript.ConfigPath)); 46 | skin = AssetDatabase.LoadAssetAtPath(BUILD_CONFIG_SKIN); 47 | if(skin == null){ 48 | skin = CreateInstance(); 49 | } 50 | 51 | sceneList = new SortableList(); 52 | } 53 | 54 | void DrawSceneListEntry(EditorBuildSettingsScene entry, float entryHeight, int entryNumber){ 55 | GUILayout.BeginVertical(skin.box, GUILayout.Height(entryHeight)); 56 | GUILayout.BeginHorizontal(); 57 | entry.enabled = GUILayout.Toggle(entry.enabled, ""); 58 | GUILayout.Label(entry.path.ToString()); 59 | GUILayout.EndHorizontal(); 60 | GUILayout.EndVertical(); 61 | } 62 | 63 | void DrawSceneListEntryDrag(EditorBuildSettingsScene entry, Rect dragRect){ 64 | GUI.color = new Color(1.0f, 1.0f, 1.0f, 0.5f); 65 | GUI.Box(dragRect, entry.path); 66 | GUI.color = Color.white; 67 | Repaint(); 68 | } 69 | 70 | void OnGUI(){ 71 | if(data == null){ 72 | if(GUILayout.Button("Reload Config", GUILayout.Height(80))){ 73 | Initialize(); 74 | } 75 | if(GUILayout.Button("Open Config File", GUILayout.Height(80))){ 76 | BuildScript.EditConfig(); 77 | } 78 | } else { 79 | if(helpInfo == ""){ 80 | helpInfo = "Click on an object to learn more about it!"; 81 | } 82 | GUI.SetNextControlName("help box"); 83 | 84 | scrollPosition = GUILayout.BeginScrollView(scrollPosition); 85 | 86 | GUILayout.Box(helpInfo, GUILayout.Height(60), GUILayout.Width(position.width - 8)); 87 | GUI.SetNextControlName("build path"); 88 | GUILayout.BeginHorizontal(); 89 | EditorGUILayout.PrefixLabel(new GUIContent("Build Path", "The location your built game will be saved.")); 90 | int buttonNameLength = 40; 91 | string buttonName = data.BuildPath.Substring(Mathf.Max(0, data.BuildPath.Length - 92 | buttonNameLength), Mathf.Min(buttonNameLength, data.BuildPath.Length)); 93 | if(buttonName.Length == buttonNameLength){ 94 | buttonName = "..." + buttonName; 95 | } 96 | if(GUILayout.Button(buttonName)){ 97 | string selectedPath = EditorUtility.OpenFolderPanel("Build Path", data.BuildPath, data.BuildPath); 98 | if(selectedPath != ""){ 99 | data.BuildPath = selectedPath; 100 | } 101 | } 102 | GUILayout.EndHorizontal(); 103 | 104 | GUILayout.BeginHorizontal(); 105 | EditorGUILayout.PrefixLabel(new GUIContent("File Name", "The name of the file"), skin.label); 106 | GUI.SetNextControlName("file name"); 107 | data.FileName = GUILayout.TextField(data.FileName, skin.textArea); 108 | GUILayout.EndHorizontal(); 109 | GUI.SetNextControlName("version number"); 110 | data.VersionNumberMajor = EditorGUILayout.IntField("Version Number Major", data.VersionNumberMajor); 111 | data.VersionNumberMinor = EditorGUILayout.IntField("Version Number Minor", data.VersionNumberMinor); 112 | data.VersionNumberPatch = EditorGUILayout.IntField("Version Number Patch", data.VersionNumberPatch); 113 | GUILayout.BeginHorizontal(); 114 | if(GUILayout.Button("New Major Version")){ 115 | data.VersionNumberMajor ++; 116 | data.VersionNumberMinor = 0; 117 | data.VersionNumberPatch = 0; 118 | } 119 | if(GUILayout.Button("New Minor Version")){ 120 | data.VersionNumberMinor ++; 121 | data.VersionNumberPatch = 0; 122 | } 123 | GUILayout.EndHorizontal(); 124 | GUI.SetNextControlName("preview build"); 125 | data.Preview = EditorGUILayout.Toggle("Preview build", data.Preview); 126 | 127 | GUILayout.Box("", GUILayout.Height(1), GUILayout.Width(position.width - 8)); 128 | EditorGUI.indentLevel ++; 129 | 130 | int deletePlatform = -1; 131 | for(int i = 0; i < data.Platforms.Length; i ++){ 132 | GUILayout.BeginHorizontal(); 133 | GUI.SetNextControlName("platform " + i); 134 | BuildType platformType = (BuildType)System.Enum.Parse(typeof(BuildType), data.Platforms[i]); 135 | data.Platforms[i] = EditorGUILayout.EnumPopup(new GUIContent("Platform " + i, "Path relative to the Unity Project that " + 136 | "you want to place builds. Suggested to use '/build'"), 137 | platformType, 138 | GUILayout.Width(position.width - 40)) 139 | .ToString(); 140 | if(GUILayout.Button("X", GUILayout.Width(20))){ 141 | deletePlatform = i; 142 | break; 143 | } 144 | GUILayout.EndHorizontal(); 145 | } 146 | if(deletePlatform > -1){ 147 | data.Platforms = data.Platforms.RemoveAt(deletePlatform); 148 | } 149 | 150 | GUILayout.BeginHorizontal(); 151 | if(GUILayout.Button("Add a platform")){ 152 | System.Array.Resize(ref data.Platforms, data.Platforms.Length + 1); 153 | data.Platforms[data.Platforms.Length - 1] = BuildType.win_x86.ToString(); 154 | } 155 | EditorGUI.indentLevel --; 156 | GUILayout.EndHorizontal(); 157 | 158 | GUILayout.Box("", GUILayout.Height(1), GUILayout.Width(position.width - 8)); 159 | 160 | // SCENES 161 | GUILayout.Label("Scenes"); 162 | EditorBuildSettingsScene[] scenes = EditorBuildSettings.scenes; 163 | EditorGUI.indentLevel ++; 164 | scenes = sceneList.Draw(DrawSceneListEntry, DrawSceneListEntryDrag, scenes); 165 | EditorGUI.indentLevel --; 166 | EditorBuildSettings.scenes = scenes; 167 | List enabledScenes = new List(); 168 | for(int s = 0; s < scenes.Length; s ++){ 169 | if(scenes[s].enabled){ 170 | enabledScenes.Add(scenes[s].path); 171 | } 172 | } 173 | data.Scenes = enabledScenes.ToArray(); 174 | 175 | GUILayout.Box("", GUILayout.Height(1), GUILayout.Width(position.width - 8)); 176 | 177 | #region steam 178 | // steam settings 179 | GUILayout.BeginHorizontal(); 180 | data.SteamData.BuildToSteam = EditorGUILayout.Toggle(new GUIContent("Push to Steam", 181 | "Check this button to enable packing a build to Steamworks."), data.SteamData.BuildToSteam, EditorStyles.miniButtonMid); 182 | GUILayout.EndHorizontal(); 183 | if(data.SteamData.BuildToSteam){ 184 | // we should do the cool expanding thing 185 | GUILayout.BeginHorizontal(); 186 | EditorGUILayout.PrefixLabel(new GUIContent("Steam SDK Content Builder Path", "The location of the ContentBuilder folder in" + 187 | " the Steam SDK")); 188 | buttonNameLength = 40; 189 | buttonName = data.SteamData.SDKPath.Substring(Mathf.Max(0, data.SteamData.SDKPath.Length - 190 | buttonNameLength), Mathf.Min(buttonNameLength, data.SteamData.SDKPath.Length)); 191 | if(buttonName.Length == buttonNameLength){ 192 | buttonName = "..." + buttonName; 193 | } 194 | if(GUILayout.Button(buttonName)){ 195 | string selectedPath = EditorUtility.OpenFolderPanel("Steam SDK Path", data.SteamData.SDKPath, data.SteamData.SDKPath); 196 | if(selectedPath != ""){ 197 | data.SteamData.SDKPath = selectedPath; 198 | } 199 | } 200 | GUILayout.EndHorizontal(); 201 | //if(GUILayout.Button("Download Steam SDK")){ 202 | // string selectedPath = EditorUtility.OpenFolderPanel("Steam SDK Path", data.SteamData.SDKPath, data.SteamData.SDKPath); 203 | // if (selectedPath != "") { 204 | // DownloadSteamSDK(selectedPath); 205 | // } 206 | //} 207 | 208 | GUILayout.BeginHorizontal(); 209 | EditorGUILayout.PrefixLabel(new GUIContent("Steam CMD Path", "The location of the command line interface" + 210 | " for steam. This is steamcmd.exe on windows and steamcmd.sh on mac.")); 211 | buttonName = data.SteamData.SteamCMDPath.Substring(Mathf.Max(0, data.SteamData.SteamCMDPath.Length - 212 | buttonNameLength), 213 | Mathf.Min(buttonNameLength, 214 | data.SteamData.SteamCMDPath.Length)); 215 | if(buttonName.Length == buttonNameLength){ 216 | buttonName = "..." + buttonName; 217 | } 218 | if(GUILayout.Button(buttonName)){ 219 | string selectedPath = ""; 220 | selectedPath = EditorUtility.OpenFilePanel("steamcmd Path", data.SteamData.SteamCMDPath, ""); 221 | if(selectedPath != ""){ 222 | data.SteamData.SteamCMDPath = selectedPath; 223 | } 224 | } 225 | GUILayout.EndHorizontal(); 226 | 227 | GUILayout.BeginHorizontal(); 228 | EditorGUILayout.PrefixLabel(new GUIContent("Use Existing Builds", "Check this box to select a folder" + 229 | " that contains builds not made during this build process to push to Steam.")); 230 | data.SteamData.UseExistingBuilds = GUILayout.Toggle(data.SteamData.UseExistingBuilds, ""); 231 | GUILayout.EndHorizontal(); 232 | 233 | if(data.SteamData.UseExistingBuilds){ 234 | GUILayout.BeginHorizontal(); 235 | EditorGUILayout.PrefixLabel(new GUIContent("Existing Builds Path", "The location of your builds" + 236 | " folder that contains builds separated by platform.")); 237 | buttonName = data.SteamData.ExistingBuildsPath.Substring( 238 | Mathf.Max(0, data.SteamData.ExistingBuildsPath.Length - buttonNameLength), 239 | Mathf.Min(buttonNameLength, data.SteamData.ExistingBuildsPath.Length)); 240 | if(buttonName.Length == buttonNameLength){ 241 | buttonName = "..." + buttonName; 242 | } 243 | if(GUILayout.Button(buttonName)){ 244 | string selectedPath = ""; 245 | selectedPath = EditorUtility.OpenFolderPanel("Existing Builds Path", data.SteamData.ExistingBuildsPath, 246 | data.SteamData.ExistingBuildsPath); 247 | if(selectedPath != ""){ 248 | data.SteamData.ExistingBuildsPath = selectedPath; 249 | } 250 | } 251 | GUILayout.EndHorizontal(); 252 | } 253 | 254 | // app build info 255 | GUILayout.BeginVertical(skin.box); 256 | 257 | GUILayout.BeginHorizontal(); 258 | EditorGUILayout.PrefixLabel(new GUIContent("App ID", "This game's steamworks App ID")); 259 | data.SteamData.AppBuild.AppID = GUILayout.TextField(data.SteamData.AppBuild.AppID); 260 | GUILayout.EndHorizontal(); 261 | 262 | GUILayout.BeginHorizontal(); 263 | EditorGUILayout.PrefixLabel(new GUIContent("Description", "Details to identify this build in steamworks.")); 264 | data.SteamData.AppBuild.Description = GUILayout.TextField(data.SteamData.AppBuild.Description); 265 | GUILayout.EndHorizontal(); 266 | 267 | data.SteamData.AppBuild.Preview = GUILayout.Toggle(data.SteamData.AppBuild.Preview, 268 | new GUIContent("Preview", "Check this if you don't want" + 269 | " to actually send the builds up to steam and just want to test" + 270 | " if the correct files were packaged.")); 271 | 272 | GUILayout.EndVertical(); 273 | 274 | // depot list 275 | int deleteDepot = -1; 276 | for(int d = 0; d < data.SteamData.Depots.Length; d ++){ 277 | GUILayout.BeginVertical(skin.box); 278 | GUILayout.BeginHorizontal(); 279 | GUILayout.FlexibleSpace(); 280 | if(GUILayout.Button(new GUIContent("Remove Depot", "Removes this depot from the list."), GUILayout.Width(100))){ 281 | deleteDepot = d; 282 | break; 283 | } 284 | GUILayout.EndHorizontal(); 285 | GUILayout.BeginHorizontal(); 286 | EditorGUILayout.PrefixLabel(new GUIContent("Depot ID", "This refers to the Steamworks depot that the chosen build's" + 287 | " files will be packaged into. Depots are normally used to create packages for players on a specifc platform.")); 288 | data.SteamData.Depots[d].DepotID = GUILayout.TextField(data.SteamData.Depots[d].DepotID); 289 | GUILayout.EndHorizontal(); 290 | GUILayout.BeginHorizontal(); 291 | EditorGUILayout.PrefixLabel(new GUIContent("Build Type", "The platform this depot is for.")); 292 | BuildType depotBuildType = (BuildType)System.Enum.Parse(typeof(BuildType), data.SteamData.Depots[d].BuildType); 293 | data.SteamData.Depots[d].BuildType = EditorGUILayout.EnumPopup(depotBuildType).ToString(); 294 | GUILayout.EndHorizontal(); 295 | EditorGUILayout.PrefixLabel(new GUIContent("Ignore Files", "Any files created through the build process that you don.'t" + 296 | " want to upload to steam. You can use wildcards ('/*') to indicate all files in a directory.")); 297 | int deleteIgnore = -1; 298 | for(int i = 0; i < data.SteamData.Depots[d].IgnoreFiles.Length; i ++){ 299 | GUILayout.BeginHorizontal(); 300 | data.SteamData.Depots[d].IgnoreFiles[i] = GUILayout.TextField(data.SteamData.Depots[d].IgnoreFiles[i]); 301 | if(GUILayout.Button(new GUIContent("X", "Removes this file from the ignore list."), GUILayout.Width(20))){ 302 | deleteIgnore = i; 303 | break; 304 | } 305 | GUILayout.EndHorizontal(); 306 | } 307 | if(deleteIgnore > -1){ 308 | data.SteamData.Depots[d].IgnoreFiles = data.SteamData.Depots[d].IgnoreFiles.RemoveAt(deleteIgnore); 309 | } 310 | GUILayout.BeginHorizontal(); 311 | if(GUILayout.Button("Add file to ignore")){ 312 | System.Array.Resize(ref data.SteamData.Depots[d].IgnoreFiles, 313 | data.SteamData.Depots[d].IgnoreFiles.Length + 1); 314 | data.SteamData.Depots[d].IgnoreFiles[data.SteamData.Depots[d].IgnoreFiles.Length - 1] = ""; 315 | } 316 | GUILayout.EndHorizontal(); 317 | GUILayout.EndVertical(); 318 | } 319 | if(deleteDepot > -1){ 320 | data.SteamData.Depots = data.SteamData.Depots.RemoveAt(deleteDepot); 321 | } 322 | GUILayout.BeginHorizontal(); 323 | if(GUILayout.Button("Add Depot", GUILayout.Height(30))){ 324 | System.Array.Resize(ref data.SteamData.Depots, 325 | data.SteamData.Depots.Length + 1); 326 | data.SteamData.Depots[data.SteamData.Depots.Length - 1] = new DepotData(); 327 | } 328 | GUILayout.EndHorizontal(); 329 | 330 | GUILayout.Box("", GUILayout.Height(1), GUILayout.Width(Screen.width - 12)); 331 | } 332 | #endregion 333 | 334 | GUILayout.EndScrollView(); 335 | 336 | GUILayout.FlexibleSpace(); 337 | if(GUILayout.Button("Save Changes", GUILayout.Height(60))){ 338 | BuildScript.WriteData(data); 339 | } 340 | 341 | if(GUILayout.Button("BUILD!", GUILayout.Height(80))){ 342 | // popup to ask for password 343 | if(data.SteamData.BuildToSteam){ 344 | SteamLoginWindow.DoEnterLogin("Login to Steam", BuildWithLogin); 345 | } else { 346 | if(data.AutoIncrement && !data.Preview){ 347 | data.VersionNumberPatch += 1; 348 | BuildScript.WriteData(data); 349 | } 350 | BuildScript.PerformBuild(); 351 | } 352 | } 353 | 354 | // Help info section 355 | switch(GUI.GetNameOfFocusedControl()){ 356 | case "help box": 357 | helpInfo = "This is the help info box! Mouse over other things to learn more about them :3"; 358 | break; 359 | case "build path": 360 | helpInfo = "Path relative to the Unity Project that you want to place builds. Suggested to use '/build'"; 361 | break; 362 | case "file name": 363 | helpInfo = "The name of the application."; 364 | break; 365 | case "version number": 366 | helpInfo = "Which version you are building. It is suggested to go with XX_YY_ZZ where XX is major version," + 367 | " YY is minor version, and ZZ is patch version. The current time is always appended to the end of the version"; 368 | break; 369 | case "preview build": 370 | helpInfo = "Check this if you want to test out the build. It'll log out where builds are going but it won't actually build them."; 371 | break; 372 | default: 373 | helpInfo = "Please select an item below to discover more about it ^.^"; 374 | break; 375 | } 376 | if(GUI.GetNameOfFocusedControl().Contains("platform")){ 377 | helpInfo = "A list of every platform you are building to. Acceptable platforms are:\n" + 378 | "win_x86, win_x64, mac, mac_x86, max_x64, linux, linux_x86, linux_x64, android, blackberry, ios," + 379 | "ps3, ps4, vita, 360, xbone, win_phone_8, win_store_app, web"; 380 | } 381 | if(GUI.GetNameOfFocusedControl().Contains("scene")){ 382 | helpInfo = "A list of unity scenes to build into the project. Order is important here! This will override your build settings."; 383 | } 384 | } 385 | } 386 | 387 | static void BuildWithLogin(string accountName, string password){ 388 | BuildScript.PerformBuild(accountName, password); 389 | } 390 | 391 | 392 | static void DownloadSteamSDK(string path) { 393 | if (!Directory.Exists(path)) { 394 | Directory.CreateDirectory(path); 395 | } 396 | 397 | ProcessStartInfo downloadProcess; 398 | 399 | #if UNITY_EDITOR_OSX 400 | string downloadScriptPath = Application.dataPath + "/PickleTools/PickleBuilder/downloadsdk.sh"; 401 | UnityEngine.Debug.Log(downloadScriptPath); 402 | downloadProcess = new ProcessStartInfo { 403 | FileName = downloadScriptPath, 404 | UseShellExecute = false, 405 | RedirectStandardOutput = true, 406 | RedirectStandardError = true, 407 | CreateNoWindow = false 408 | }; 409 | Process proc = new Process { 410 | StartInfo = downloadProcess 411 | }; 412 | UnityEngine.Debug.Log(downloadProcess.FileName.ToString()); 413 | proc.Start(); 414 | 415 | EditorCoroutine.start(DisplayDownloadLog(proc)); 416 | #else 417 | // TODO: download file and unzip it 418 | #endif 419 | } 420 | 421 | static IEnumerator DisplayDownloadLog(Process proc) { 422 | UnityEngine.Debug.Log("STarting download..."); 423 | while (!proc.StandardOutput.EndOfStream) { 424 | UnityEngine.Debug.Log("log downloading..."); 425 | System.Console.WriteLine("downloading..."); 426 | UnityEngine.Debug.Log(proc.StandardOutput.ReadLine()); 427 | yield return null; 428 | } 429 | yield return null; 430 | UnityEngine.Debug.Log("[BuildConfigWindow.cs]: Download Complete."); 431 | } 432 | } 433 | } 434 | 435 | public delegate void LoginHandler (string accountName, string password); 436 | 437 | public class SteamLoginWindow : EditorWindow { 438 | 439 | LoginHandler callback; 440 | 441 | string accountName = ""; 442 | string password = ""; 443 | 444 | void OnGUI(){ 445 | GUILayout.BeginHorizontal(); 446 | GUILayout.Label("Steam account name"); 447 | accountName = GUILayout.TextField(accountName); 448 | GUILayout.EndHorizontal(); 449 | GUILayout.BeginHorizontal(); 450 | GUILayout.Label("password"); 451 | password = GUILayout.PasswordField(password, '*'); 452 | GUILayout.EndHorizontal(); 453 | if(GUILayout.Button("Confirm")){ 454 | this.Close(); 455 | } 456 | } 457 | 458 | void OnDisable(){ 459 | CloseWindow(); 460 | } 461 | 462 | void CloseWindow(){ 463 | callback(accountName, password); 464 | } 465 | 466 | public static void DoEnterLogin(string title, LoginHandler callback){ 467 | SteamLoginWindow popup = EditorWindow.GetWindow(typeof(SteamLoginWindow), true, title, true) as SteamLoginWindow; 468 | popup.callback = callback; 469 | } 470 | 471 | } -------------------------------------------------------------------------------- /Assets/LitJson/JsonData.cs: -------------------------------------------------------------------------------- 1 | #region Header 2 | /** 3 | * JsonData.cs 4 | * Generic type to hold JSON data (objects, arrays, and so on). This is 5 | * the default type returned by JsonMapper.ToObject(). 6 | * 7 | * The authors disclaim copyright to this source code. For more details, see 8 | * the COPYING file included with this distribution. 9 | **/ 10 | #endregion 11 | 12 | 13 | using System; 14 | using System.Collections; 15 | using System.Collections.Generic; 16 | using System.Collections.Specialized; 17 | using System.IO; 18 | 19 | using UnityEngine; 20 | 21 | 22 | namespace LitJson 23 | { 24 | public class JsonData : IJsonWrapper, IEquatable 25 | { 26 | #region Fields 27 | 28 | private IList inst_array; 29 | private bool inst_boolean; 30 | private double inst_double; 31 | private int inst_int; 32 | private long inst_long; 33 | private IDictionary inst_object; 34 | private string inst_string; 35 | private string json; 36 | private JsonType type; 37 | 38 | // Used to implement the IOrderedDictionary interface 39 | private IList> object_list; 40 | 41 | #endregion 42 | 43 | 44 | #region Properties 45 | 46 | public int Count { 47 | get { return EnsureCollection ().Count; } 48 | } 49 | 50 | public bool IsArray { 51 | get { return type == JsonType.Array; } 52 | } 53 | 54 | public bool IsBoolean { 55 | get { return type == JsonType.Boolean; } 56 | } 57 | 58 | public bool IsDouble { 59 | get { return type == JsonType.Double; } 60 | } 61 | 62 | public bool IsInt { 63 | get { return type == JsonType.Int; } 64 | } 65 | 66 | public bool IsLong { 67 | get { return type == JsonType.Long; } 68 | } 69 | 70 | public bool IsObject { 71 | get { return type == JsonType.Object; } 72 | } 73 | 74 | public bool IsString { 75 | get { return type == JsonType.String; } 76 | } 77 | 78 | public ICollection Keys { 79 | get { 80 | EnsureDictionary (); 81 | return inst_object.Keys; 82 | } 83 | } 84 | 85 | #endregion 86 | 87 | 88 | #region ICollection Properties 89 | 90 | int ICollection.Count { 91 | get { 92 | return Count; 93 | } 94 | } 95 | 96 | bool ICollection.IsSynchronized { 97 | get { 98 | return EnsureCollection ().IsSynchronized; 99 | } 100 | } 101 | 102 | object ICollection.SyncRoot { 103 | get { 104 | return EnsureCollection ().SyncRoot; 105 | } 106 | } 107 | 108 | #endregion 109 | 110 | 111 | #region IDictionary Properties 112 | 113 | bool IDictionary.IsFixedSize { 114 | get { 115 | return EnsureDictionary ().IsFixedSize; 116 | } 117 | } 118 | 119 | bool IDictionary.IsReadOnly { 120 | get { 121 | return EnsureDictionary ().IsReadOnly; 122 | } 123 | } 124 | 125 | ICollection IDictionary.Keys { 126 | get { 127 | EnsureDictionary (); 128 | IList keys = new List (); 129 | 130 | foreach (KeyValuePair entry in 131 | object_list) { 132 | keys.Add (entry.Key); 133 | } 134 | 135 | return (ICollection)keys; 136 | } 137 | } 138 | 139 | ICollection IDictionary.Values { 140 | get { 141 | EnsureDictionary (); 142 | IList values = new List (); 143 | 144 | foreach (KeyValuePair entry in 145 | object_list) { 146 | values.Add (entry.Value); 147 | } 148 | 149 | return (ICollection)values; 150 | } 151 | } 152 | 153 | #endregion 154 | 155 | 156 | #region IJsonWrapper Properties 157 | 158 | bool IJsonWrapper.IsArray { 159 | get { return IsArray; } 160 | } 161 | 162 | bool IJsonWrapper.IsBoolean { 163 | get { return IsBoolean; } 164 | } 165 | 166 | bool IJsonWrapper.IsDouble { 167 | get { return IsDouble; } 168 | } 169 | 170 | bool IJsonWrapper.IsInt { 171 | get { return IsInt; } 172 | } 173 | 174 | bool IJsonWrapper.IsLong { 175 | get { return IsLong; } 176 | } 177 | 178 | bool IJsonWrapper.IsObject { 179 | get { return IsObject; } 180 | } 181 | 182 | bool IJsonWrapper.IsString { 183 | get { return IsString; } 184 | } 185 | 186 | #endregion 187 | 188 | 189 | #region IList Properties 190 | 191 | bool IList.IsFixedSize { 192 | get { 193 | return EnsureList ().IsFixedSize; 194 | } 195 | } 196 | 197 | bool IList.IsReadOnly { 198 | get { 199 | return EnsureList ().IsReadOnly; 200 | } 201 | } 202 | 203 | #endregion 204 | 205 | 206 | #region IDictionary Indexer 207 | 208 | object IDictionary.this [object key] { 209 | get { 210 | return EnsureDictionary () [key]; 211 | } 212 | 213 | set { 214 | if (!(key is String)) 215 | throw new ArgumentException ( 216 | "The key has to be a string"); 217 | 218 | JsonData data = ToJsonData (value); 219 | 220 | this [(string)key] = data; 221 | } 222 | } 223 | 224 | #endregion 225 | 226 | 227 | #region IOrderedDictionary Indexer 228 | 229 | object IOrderedDictionary.this [int idx] { 230 | get { 231 | EnsureDictionary (); 232 | return object_list [idx].Value; 233 | } 234 | 235 | set { 236 | EnsureDictionary (); 237 | JsonData data = ToJsonData (value); 238 | 239 | KeyValuePair old_entry = object_list [idx]; 240 | 241 | inst_object [old_entry.Key] = data; 242 | 243 | KeyValuePair entry = 244 | new KeyValuePair (old_entry.Key, data); 245 | 246 | object_list [idx] = entry; 247 | } 248 | } 249 | 250 | #endregion 251 | 252 | 253 | #region IList Indexer 254 | 255 | object IList.this [int index] { 256 | get { 257 | return EnsureList () [index]; 258 | } 259 | 260 | set { 261 | EnsureList (); 262 | JsonData data = ToJsonData (value); 263 | 264 | this [index] = data; 265 | } 266 | } 267 | 268 | #endregion 269 | 270 | 271 | #region Public Indexers 272 | 273 | public JsonData this [string prop_name] { 274 | get { 275 | EnsureDictionary (); 276 | return inst_object [prop_name]; 277 | } 278 | 279 | set { 280 | EnsureDictionary (); 281 | 282 | KeyValuePair entry = 283 | new KeyValuePair (prop_name, value); 284 | 285 | if (inst_object.ContainsKey (prop_name)) { 286 | for (int i = 0; i < object_list.Count; i++) { 287 | if (object_list [i].Key == prop_name) { 288 | object_list [i] = entry; 289 | break; 290 | } 291 | } 292 | } else 293 | object_list.Add (entry); 294 | 295 | inst_object [prop_name] = value; 296 | 297 | json = null; 298 | } 299 | } 300 | 301 | public JsonData this [int index] { 302 | get { 303 | EnsureCollection (); 304 | 305 | if (type == JsonType.Array) 306 | return inst_array [index]; 307 | 308 | return object_list [index].Value; 309 | } 310 | 311 | set { 312 | EnsureCollection (); 313 | 314 | if (type == JsonType.Array) 315 | inst_array [index] = value; 316 | else { 317 | KeyValuePair entry = object_list [index]; 318 | KeyValuePair new_entry = 319 | new KeyValuePair (entry.Key, value); 320 | 321 | object_list [index] = new_entry; 322 | inst_object [entry.Key] = value; 323 | } 324 | 325 | json = null; 326 | } 327 | } 328 | 329 | #endregion 330 | 331 | 332 | #region Constructors 333 | 334 | public JsonData () 335 | { 336 | } 337 | 338 | public JsonData (bool boolean) 339 | { 340 | type = JsonType.Boolean; 341 | inst_boolean = boolean; 342 | } 343 | 344 | public JsonData (double number) 345 | { 346 | type = JsonType.Double; 347 | inst_double = number; 348 | } 349 | 350 | public JsonData (int number) 351 | { 352 | type = JsonType.Int; 353 | inst_int = number; 354 | } 355 | 356 | public JsonData (long number) 357 | { 358 | type = JsonType.Long; 359 | inst_long = number; 360 | } 361 | 362 | public JsonData (object obj) 363 | { 364 | if (obj is Boolean) { 365 | type = JsonType.Boolean; 366 | inst_boolean = (bool)obj; 367 | return; 368 | } 369 | 370 | if (obj is Double) { 371 | type = JsonType.Double; 372 | inst_double = (double)obj; 373 | return; 374 | } 375 | 376 | if (obj is Int32) { 377 | type = JsonType.Int; 378 | inst_int = (int)obj; 379 | return; 380 | } 381 | 382 | if (obj is Int64) { 383 | type = JsonType.Long; 384 | inst_long = (long)obj; 385 | return; 386 | } 387 | 388 | if (obj is String) { 389 | type = JsonType.String; 390 | inst_string = (string)obj; 391 | return; 392 | } 393 | 394 | if (obj is Vector2) { 395 | SetJsonType (JsonType.Object); 396 | this ["x"] = ((Vector2)obj).x; 397 | this ["y"] = ((Vector2)obj).y; 398 | return; 399 | } 400 | 401 | if (obj is Vector3) { 402 | SetJsonType (JsonType.Object); 403 | this ["x"] = ((Vector3)obj).x; 404 | this ["y"] = ((Vector3)obj).y; 405 | this ["z"] = ((Vector3)obj).z; 406 | return; 407 | } 408 | 409 | if (obj is Vector4) { 410 | SetJsonType (JsonType.Object); 411 | this ["x"] = ((Vector4)obj).x; 412 | this ["y"] = ((Vector4)obj).y; 413 | this ["z"] = ((Vector4)obj).z; 414 | this ["w"] = ((Vector4)obj).w; 415 | return; 416 | } 417 | 418 | if (obj is Quaternion) { 419 | SetJsonType (JsonType.Object); 420 | this ["x"] = ((Quaternion)obj).x; 421 | this ["y"] = ((Quaternion)obj).y; 422 | this ["z"] = ((Quaternion)obj).z; 423 | this ["w"] = ((Quaternion)obj).w; 424 | return; 425 | } 426 | 427 | if (obj is Matrix4x4) { 428 | SetJsonType (JsonType.Object); 429 | this ["m00"] = ((Matrix4x4)obj).m00; 430 | this ["m33"] = ((Matrix4x4)obj).m33; 431 | this ["m23"] = ((Matrix4x4)obj).m23; 432 | this ["m13"] = ((Matrix4x4)obj).m13; 433 | this ["m03"] = ((Matrix4x4)obj).m03; 434 | this ["m32"] = ((Matrix4x4)obj).m32; 435 | this ["m12"] = ((Matrix4x4)obj).m12; 436 | this ["m02"] = ((Matrix4x4)obj).m02; 437 | this ["m22"] = ((Matrix4x4)obj).m22; 438 | this ["m21"] = ((Matrix4x4)obj).m21; 439 | this ["m11"] = ((Matrix4x4)obj).m11; 440 | this ["m01"] = ((Matrix4x4)obj).m01; 441 | this ["m30"] = ((Matrix4x4)obj).m30; 442 | this ["m20"] = ((Matrix4x4)obj).m20; 443 | this ["m10"] = ((Matrix4x4)obj).m10; 444 | this ["m31"] = ((Matrix4x4)obj).m31; 445 | return; 446 | } 447 | 448 | throw new ArgumentException ( 449 | "Unable to wrap the given object with JsonData"); 450 | } 451 | 452 | public JsonData (string str) 453 | { 454 | type = JsonType.String; 455 | inst_string = str; 456 | } 457 | 458 | #endregion 459 | 460 | 461 | #region Implicit Conversions 462 | 463 | public static implicit operator JsonData (Boolean data) 464 | { 465 | return new JsonData (data); 466 | } 467 | 468 | public static implicit operator JsonData (Double data) 469 | { 470 | return new JsonData (data); 471 | } 472 | 473 | public static implicit operator JsonData (Int32 data) 474 | { 475 | return new JsonData (data); 476 | } 477 | 478 | public static implicit operator JsonData (Int64 data) 479 | { 480 | return new JsonData (data); 481 | } 482 | 483 | public static implicit operator JsonData (String data) 484 | { 485 | return new JsonData (data); 486 | } 487 | 488 | #endregion 489 | 490 | 491 | #region Explicit Conversions 492 | 493 | public static explicit operator Boolean (JsonData data) 494 | { 495 | if (data.type != JsonType.Boolean) 496 | throw new InvalidCastException ( 497 | "Instance of JsonData doesn't hold a double"); 498 | 499 | return data.inst_boolean; 500 | } 501 | 502 | public static explicit operator Double (JsonData data) 503 | { 504 | if (data.type != JsonType.Double) 505 | throw new InvalidCastException ( 506 | "Instance of JsonData doesn't hold a double"); 507 | 508 | return data.inst_double; 509 | } 510 | 511 | public static explicit operator Int32 (JsonData data) 512 | { 513 | if (data.type != JsonType.Int) 514 | throw new InvalidCastException ( 515 | "Instance of JsonData doesn't hold an int"); 516 | 517 | return data.inst_int; 518 | } 519 | 520 | public static explicit operator Int64 (JsonData data) 521 | { 522 | if (data.type != JsonType.Long) 523 | throw new InvalidCastException ( 524 | "Instance of JsonData doesn't hold an int"); 525 | 526 | return data.inst_long; 527 | } 528 | 529 | public static explicit operator String (JsonData data) 530 | { 531 | if (data.type != JsonType.String) 532 | throw new InvalidCastException ( 533 | "Instance of JsonData doesn't hold a string"); 534 | 535 | return data.inst_string; 536 | } 537 | 538 | #endregion 539 | 540 | 541 | #region ICollection Methods 542 | 543 | void ICollection.CopyTo (Array array, int index) 544 | { 545 | EnsureCollection ().CopyTo (array, index); 546 | } 547 | 548 | #endregion 549 | 550 | 551 | #region IDictionary Methods 552 | 553 | void IDictionary.Add (object key, object value) 554 | { 555 | JsonData data = ToJsonData (value); 556 | 557 | EnsureDictionary ().Add (key, data); 558 | 559 | KeyValuePair entry = 560 | new KeyValuePair ((string)key, data); 561 | object_list.Add (entry); 562 | 563 | json = null; 564 | } 565 | 566 | void IDictionary.Clear () 567 | { 568 | EnsureDictionary ().Clear (); 569 | object_list.Clear (); 570 | json = null; 571 | } 572 | 573 | bool IDictionary.Contains (object key) 574 | { 575 | return EnsureDictionary ().Contains (key); 576 | } 577 | 578 | IDictionaryEnumerator IDictionary.GetEnumerator () 579 | { 580 | return ((IOrderedDictionary)this).GetEnumerator (); 581 | } 582 | 583 | void IDictionary.Remove (object key) 584 | { 585 | EnsureDictionary ().Remove (key); 586 | 587 | for (int i = 0; i < object_list.Count; i++) { 588 | if (object_list [i].Key == (string)key) { 589 | object_list.RemoveAt (i); 590 | break; 591 | } 592 | } 593 | 594 | json = null; 595 | } 596 | 597 | #endregion 598 | 599 | 600 | #region IEnumerable Methods 601 | 602 | IEnumerator IEnumerable.GetEnumerator () 603 | { 604 | return EnsureCollection ().GetEnumerator (); 605 | } 606 | 607 | #endregion 608 | 609 | 610 | #region IJsonWrapper Methods 611 | 612 | bool IJsonWrapper.GetBoolean () 613 | { 614 | if (type != JsonType.Boolean) 615 | throw new InvalidOperationException ( 616 | "JsonData instance doesn't hold a boolean"); 617 | 618 | return inst_boolean; 619 | } 620 | 621 | double IJsonWrapper.GetDouble () 622 | { 623 | if (type != JsonType.Double) 624 | throw new InvalidOperationException ( 625 | "JsonData instance doesn't hold a double"); 626 | 627 | return inst_double; 628 | } 629 | 630 | int IJsonWrapper.GetInt () 631 | { 632 | if (type != JsonType.Int) 633 | throw new InvalidOperationException ( 634 | "JsonData instance doesn't hold an int"); 635 | 636 | return inst_int; 637 | } 638 | 639 | long IJsonWrapper.GetLong () 640 | { 641 | if (type != JsonType.Long) 642 | throw new InvalidOperationException ( 643 | "JsonData instance doesn't hold a long"); 644 | 645 | return inst_long; 646 | } 647 | 648 | string IJsonWrapper.GetString () 649 | { 650 | if (type != JsonType.String) 651 | throw new InvalidOperationException ( 652 | "JsonData instance doesn't hold a string"); 653 | 654 | return inst_string; 655 | } 656 | 657 | void IJsonWrapper.SetBoolean (bool val) 658 | { 659 | type = JsonType.Boolean; 660 | inst_boolean = val; 661 | json = null; 662 | } 663 | 664 | void IJsonWrapper.SetDouble (double val) 665 | { 666 | type = JsonType.Double; 667 | inst_double = val; 668 | json = null; 669 | } 670 | 671 | void IJsonWrapper.SetInt (int val) 672 | { 673 | type = JsonType.Int; 674 | inst_int = val; 675 | json = null; 676 | } 677 | 678 | void IJsonWrapper.SetLong (long val) 679 | { 680 | type = JsonType.Long; 681 | inst_long = val; 682 | json = null; 683 | } 684 | 685 | void IJsonWrapper.SetString (string val) 686 | { 687 | type = JsonType.String; 688 | inst_string = val; 689 | json = null; 690 | } 691 | 692 | string IJsonWrapper.ToJson () 693 | { 694 | return ToJson (); 695 | } 696 | 697 | void IJsonWrapper.ToJson (JsonWriter writer) 698 | { 699 | ToJson (writer); 700 | } 701 | 702 | #endregion 703 | 704 | 705 | #region IList Methods 706 | 707 | int IList.Add (object value) 708 | { 709 | return Add (value); 710 | } 711 | 712 | void IList.Clear () 713 | { 714 | EnsureList ().Clear (); 715 | json = null; 716 | } 717 | 718 | bool IList.Contains (object value) 719 | { 720 | return EnsureList ().Contains (value); 721 | } 722 | 723 | int IList.IndexOf (object value) 724 | { 725 | return EnsureList ().IndexOf (value); 726 | } 727 | 728 | void IList.Insert (int index, object value) 729 | { 730 | EnsureList ().Insert (index, value); 731 | json = null; 732 | } 733 | 734 | void IList.Remove (object value) 735 | { 736 | EnsureList ().Remove (value); 737 | json = null; 738 | } 739 | 740 | void IList.RemoveAt (int index) 741 | { 742 | EnsureList ().RemoveAt (index); 743 | json = null; 744 | } 745 | 746 | #endregion 747 | 748 | 749 | #region IOrderedDictionary Methods 750 | 751 | IDictionaryEnumerator IOrderedDictionary.GetEnumerator () 752 | { 753 | EnsureDictionary (); 754 | 755 | return new OrderedDictionaryEnumerator ( 756 | object_list.GetEnumerator ()); 757 | } 758 | 759 | void IOrderedDictionary.Insert (int idx, object key, object value) 760 | { 761 | string property = (string)key; 762 | JsonData data = ToJsonData (value); 763 | 764 | this [property] = data; 765 | 766 | KeyValuePair entry = 767 | new KeyValuePair (property, data); 768 | 769 | object_list.Insert (idx, entry); 770 | } 771 | 772 | void IOrderedDictionary.RemoveAt (int idx) 773 | { 774 | EnsureDictionary (); 775 | 776 | inst_object.Remove (object_list [idx].Key); 777 | object_list.RemoveAt (idx); 778 | } 779 | 780 | #endregion 781 | 782 | 783 | #region Private Methods 784 | 785 | private ICollection EnsureCollection () 786 | { 787 | if (type == JsonType.Array) 788 | return (ICollection)inst_array; 789 | 790 | if (type == JsonType.Object) 791 | return (ICollection)inst_object; 792 | 793 | throw new InvalidOperationException ( 794 | "The JsonData instance has to be initialized first"); 795 | } 796 | 797 | private IDictionary EnsureDictionary () 798 | { 799 | if (type == JsonType.Object) 800 | return (IDictionary)inst_object; 801 | 802 | if (type != JsonType.None) 803 | throw new InvalidOperationException ( 804 | "Instance of JsonData is not a dictionary"); 805 | 806 | type = JsonType.Object; 807 | inst_object = new Dictionary (); 808 | object_list = new List> (); 809 | 810 | return (IDictionary)inst_object; 811 | } 812 | 813 | private IList EnsureList () 814 | { 815 | if (type == JsonType.Array) 816 | return (IList)inst_array; 817 | 818 | if (type != JsonType.None) 819 | throw new InvalidOperationException ( 820 | "Instance of JsonData is not a list"); 821 | 822 | type = JsonType.Array; 823 | inst_array = new List (); 824 | 825 | return (IList)inst_array; 826 | } 827 | 828 | private JsonData ToJsonData (object obj) 829 | { 830 | if (obj == null) 831 | return null; 832 | 833 | if (obj is JsonData) 834 | return (JsonData)obj; 835 | 836 | return new JsonData (obj); 837 | } 838 | 839 | private static void WriteJson (IJsonWrapper obj, JsonWriter writer) 840 | { 841 | if (obj == null) { 842 | writer.Write (null); 843 | return; 844 | } 845 | 846 | if (obj.IsString) { 847 | writer.Write (obj.GetString ()); 848 | return; 849 | } 850 | 851 | if (obj.IsBoolean) { 852 | writer.Write (obj.GetBoolean ()); 853 | return; 854 | } 855 | 856 | if (obj.IsDouble) { 857 | writer.Write (obj.GetDouble ()); 858 | return; 859 | } 860 | 861 | if (obj.IsInt) { 862 | writer.Write (obj.GetInt ()); 863 | return; 864 | } 865 | 866 | if (obj.IsLong) { 867 | writer.Write (obj.GetLong ()); 868 | return; 869 | } 870 | 871 | if (obj.IsArray) { 872 | writer.WriteArrayStart (); 873 | foreach (object elem in (IList) obj) 874 | WriteJson ((JsonData)elem, writer); 875 | writer.WriteArrayEnd (obj.Count > 0); 876 | 877 | return; 878 | } 879 | 880 | if (obj.IsObject) { 881 | writer.WriteObjectStart (); 882 | 883 | foreach (DictionaryEntry entry in ((IDictionary) obj)) { 884 | writer.WritePropertyName ((string)entry.Key); 885 | WriteJson ((JsonData)entry.Value, writer); 886 | } 887 | writer.WriteObjectEnd (); 888 | 889 | return; 890 | } 891 | } 892 | 893 | #endregion 894 | 895 | 896 | public int Add (object value) 897 | { 898 | JsonData data = ToJsonData (value); 899 | 900 | json = null; 901 | 902 | return EnsureList ().Add (data); 903 | } 904 | 905 | public void Clear () 906 | { 907 | if (IsObject) { 908 | ((IDictionary)this).Clear (); 909 | return; 910 | } 911 | 912 | if (IsArray) { 913 | ((IList)this).Clear (); 914 | return; 915 | } 916 | } 917 | 918 | public bool Equals (JsonData x) 919 | { 920 | if (x == null) 921 | return false; 922 | 923 | if (x.type != this.type) 924 | return false; 925 | 926 | switch (this.type) { 927 | case JsonType.None: 928 | return true; 929 | 930 | case JsonType.Object: 931 | return this.inst_object.Equals (x.inst_object); 932 | 933 | case JsonType.Array: 934 | return this.inst_array.Equals (x.inst_array); 935 | 936 | case JsonType.String: 937 | return this.inst_string.Equals (x.inst_string); 938 | 939 | case JsonType.Int: 940 | return this.inst_int.Equals (x.inst_int); 941 | 942 | case JsonType.Long: 943 | return this.inst_long.Equals (x.inst_long); 944 | 945 | case JsonType.Double: 946 | return this.inst_double.Equals (x.inst_double); 947 | 948 | case JsonType.Boolean: 949 | return this.inst_boolean.Equals (x.inst_boolean); 950 | } 951 | 952 | return false; 953 | } 954 | 955 | public JsonType GetJsonType () 956 | { 957 | return type; 958 | } 959 | 960 | public void SetJsonType (JsonType type) 961 | { 962 | if (this.type == type) 963 | return; 964 | 965 | switch (type) { 966 | case JsonType.None: 967 | break; 968 | 969 | case JsonType.Object: 970 | inst_object = new Dictionary (); 971 | object_list = new List> (); 972 | break; 973 | 974 | case JsonType.Array: 975 | inst_array = new List (); 976 | break; 977 | 978 | case JsonType.String: 979 | inst_string = default (String); 980 | break; 981 | 982 | case JsonType.Int: 983 | inst_int = default (Int32); 984 | break; 985 | 986 | case JsonType.Long: 987 | inst_long = default (Int64); 988 | break; 989 | 990 | case JsonType.Double: 991 | inst_double = default (Double); 992 | break; 993 | 994 | case JsonType.Boolean: 995 | inst_boolean = default (Boolean); 996 | break; 997 | } 998 | 999 | this.type = type; 1000 | } 1001 | 1002 | public string ToJson () 1003 | { 1004 | if (json != null) 1005 | return json; 1006 | 1007 | StringWriter sw = new StringWriter (); 1008 | JsonWriter writer = new JsonWriter (sw); 1009 | writer.Validate = false; 1010 | 1011 | WriteJson (this, writer); 1012 | json = sw.ToString (); 1013 | 1014 | return json; 1015 | } 1016 | 1017 | public void ToJson (JsonWriter writer) 1018 | { 1019 | bool old_validate = writer.Validate; 1020 | 1021 | writer.Validate = false; 1022 | 1023 | WriteJson (this, writer); 1024 | 1025 | writer.Validate = old_validate; 1026 | } 1027 | 1028 | public override string ToString () 1029 | { 1030 | switch (type) { 1031 | case JsonType.Array: 1032 | return "JsonData array"; 1033 | 1034 | case JsonType.Boolean: 1035 | return inst_boolean.ToString (); 1036 | 1037 | case JsonType.Double: 1038 | return inst_double.ToString (); 1039 | 1040 | case JsonType.Int: 1041 | return inst_int.ToString (); 1042 | 1043 | case JsonType.Long: 1044 | return inst_long.ToString (); 1045 | 1046 | case JsonType.Object: 1047 | return "JsonData object"; 1048 | 1049 | case JsonType.String: 1050 | return inst_string; 1051 | } 1052 | 1053 | return "Uninitialized JsonData"; 1054 | } 1055 | } 1056 | 1057 | 1058 | internal class OrderedDictionaryEnumerator : IDictionaryEnumerator 1059 | { 1060 | IEnumerator> list_enumerator; 1061 | 1062 | 1063 | public object Current { 1064 | get { return Entry; } 1065 | } 1066 | 1067 | public DictionaryEntry Entry { 1068 | get { 1069 | KeyValuePair curr = list_enumerator.Current; 1070 | return new DictionaryEntry (curr.Key, curr.Value); 1071 | } 1072 | } 1073 | 1074 | public object Key { 1075 | get { return list_enumerator.Current.Key; } 1076 | } 1077 | 1078 | public object Value { 1079 | get { return list_enumerator.Current.Value; } 1080 | } 1081 | 1082 | 1083 | public OrderedDictionaryEnumerator ( 1084 | IEnumerator> enumerator) 1085 | { 1086 | list_enumerator = enumerator; 1087 | } 1088 | 1089 | 1090 | public bool MoveNext () 1091 | { 1092 | return list_enumerator.MoveNext (); 1093 | } 1094 | 1095 | public void Reset () 1096 | { 1097 | list_enumerator.Reset (); 1098 | } 1099 | } 1100 | } 1101 | -------------------------------------------------------------------------------- /Assets/LitJson/Lexer.cs: -------------------------------------------------------------------------------- 1 | #region Header 2 | /** 3 | * Lexer.cs 4 | * JSON lexer implementation based on a finite state machine. 5 | * 6 | * The authors disclaim copyright to this source code. For more details, see 7 | * the COPYING file included with this distribution. 8 | **/ 9 | #endregion 10 | 11 | 12 | using System; 13 | using System.Collections.Generic; 14 | using System.IO; 15 | using System.Text; 16 | 17 | 18 | namespace LitJson 19 | { 20 | internal class FsmContext 21 | { 22 | public bool Return; 23 | public int NextState; 24 | public Lexer L; 25 | public int StateStack; 26 | } 27 | 28 | 29 | internal class Lexer 30 | { 31 | #region Fields 32 | private delegate bool StateHandler (FsmContext ctx); 33 | 34 | private static int[] fsm_return_table; 35 | private static StateHandler[] fsm_handler_table; 36 | 37 | private bool allow_comments; 38 | private bool allow_single_quoted_strings; 39 | private bool end_of_input; 40 | private FsmContext fsm_context; 41 | private int input_buffer; 42 | private int input_char; 43 | private TextReader reader; 44 | private int state; 45 | private StringBuilder string_buffer; 46 | private string string_value; 47 | private int token; 48 | private int unichar; 49 | #endregion 50 | 51 | 52 | #region Properties 53 | public bool AllowComments { 54 | get { return allow_comments; } 55 | set { allow_comments = value; } 56 | } 57 | 58 | public bool AllowSingleQuotedStrings { 59 | get { return allow_single_quoted_strings; } 60 | set { allow_single_quoted_strings = value; } 61 | } 62 | 63 | public bool EndOfInput { 64 | get { return end_of_input; } 65 | } 66 | 67 | public int Token { 68 | get { return token; } 69 | } 70 | 71 | public string StringValue { 72 | get { return string_value; } 73 | } 74 | #endregion 75 | 76 | 77 | #region Constructors 78 | static Lexer () 79 | { 80 | PopulateFsmTables (); 81 | } 82 | 83 | public Lexer (TextReader reader) 84 | { 85 | allow_comments = true; 86 | allow_single_quoted_strings = true; 87 | 88 | input_buffer = 0; 89 | string_buffer = new StringBuilder (128); 90 | state = 1; 91 | end_of_input = false; 92 | this.reader = reader; 93 | 94 | fsm_context = new FsmContext (); 95 | fsm_context.L = this; 96 | } 97 | #endregion 98 | 99 | 100 | #region Static Methods 101 | private static int HexValue (int digit) 102 | { 103 | switch (digit) { 104 | case 'a': 105 | case 'A': 106 | return 10; 107 | 108 | case 'b': 109 | case 'B': 110 | return 11; 111 | 112 | case 'c': 113 | case 'C': 114 | return 12; 115 | 116 | case 'd': 117 | case 'D': 118 | return 13; 119 | 120 | case 'e': 121 | case 'E': 122 | return 14; 123 | 124 | case 'f': 125 | case 'F': 126 | return 15; 127 | 128 | default: 129 | return digit - '0'; 130 | } 131 | } 132 | 133 | private static void PopulateFsmTables () 134 | { 135 | // See section A.1. of the manual for details of the finite 136 | // state machine. 137 | fsm_handler_table = new StateHandler[28] { 138 | State1, 139 | State2, 140 | State3, 141 | State4, 142 | State5, 143 | State6, 144 | State7, 145 | State8, 146 | State9, 147 | State10, 148 | State11, 149 | State12, 150 | State13, 151 | State14, 152 | State15, 153 | State16, 154 | State17, 155 | State18, 156 | State19, 157 | State20, 158 | State21, 159 | State22, 160 | State23, 161 | State24, 162 | State25, 163 | State26, 164 | State27, 165 | State28 166 | }; 167 | 168 | fsm_return_table = new int[28] { 169 | (int) ParserToken.Char, 170 | 0, 171 | (int) ParserToken.Number, 172 | (int) ParserToken.Number, 173 | 0, 174 | (int) ParserToken.Number, 175 | 0, 176 | (int) ParserToken.Number, 177 | 0, 178 | 0, 179 | (int) ParserToken.True, 180 | 0, 181 | 0, 182 | 0, 183 | (int) ParserToken.False, 184 | 0, 185 | 0, 186 | (int) ParserToken.Null, 187 | (int) ParserToken.CharSeq, 188 | (int) ParserToken.Char, 189 | 0, 190 | 0, 191 | (int) ParserToken.CharSeq, 192 | (int) ParserToken.Char, 193 | 0, 194 | 0, 195 | 0, 196 | 0 197 | }; 198 | } 199 | 200 | private static char ProcessEscChar (int esc_char) 201 | { 202 | switch (esc_char) { 203 | case '"': 204 | case '\'': 205 | case '\\': 206 | case '/': 207 | return Convert.ToChar (esc_char); 208 | 209 | case 'n': 210 | return '\n'; 211 | 212 | case 't': 213 | return '\t'; 214 | 215 | case 'r': 216 | return '\r'; 217 | 218 | case 'b': 219 | return '\b'; 220 | 221 | case 'f': 222 | return '\f'; 223 | 224 | default: 225 | // Unreachable 226 | return '?'; 227 | } 228 | } 229 | 230 | private static bool State1 (FsmContext ctx) 231 | { 232 | while (ctx.L.GetChar ()) { 233 | if (ctx.L.input_char == ' ' || 234 | ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') 235 | continue; 236 | 237 | if (ctx.L.input_char >= '1' && ctx.L.input_char <= '9') { 238 | ctx.L.string_buffer.Append ((char) ctx.L.input_char); 239 | ctx.NextState = 3; 240 | return true; 241 | } 242 | 243 | switch (ctx.L.input_char) { 244 | case '"': 245 | ctx.NextState = 19; 246 | ctx.Return = true; 247 | return true; 248 | 249 | case ',': 250 | case ':': 251 | case '[': 252 | case ']': 253 | case '{': 254 | case '}': 255 | ctx.NextState = 1; 256 | ctx.Return = true; 257 | return true; 258 | 259 | case '-': 260 | ctx.L.string_buffer.Append ((char) ctx.L.input_char); 261 | ctx.NextState = 2; 262 | return true; 263 | 264 | case '0': 265 | ctx.L.string_buffer.Append ((char) ctx.L.input_char); 266 | ctx.NextState = 4; 267 | return true; 268 | 269 | case 'f': 270 | ctx.NextState = 12; 271 | return true; 272 | 273 | case 'n': 274 | ctx.NextState = 16; 275 | return true; 276 | 277 | case 't': 278 | ctx.NextState = 9; 279 | return true; 280 | 281 | case '\'': 282 | if (! ctx.L.allow_single_quoted_strings) 283 | return false; 284 | 285 | ctx.L.input_char = '"'; 286 | ctx.NextState = 23; 287 | ctx.Return = true; 288 | return true; 289 | 290 | case '/': 291 | if (! ctx.L.allow_comments) 292 | return false; 293 | 294 | ctx.NextState = 25; 295 | return true; 296 | 297 | default: 298 | return false; 299 | } 300 | } 301 | 302 | return true; 303 | } 304 | 305 | private static bool State2 (FsmContext ctx) 306 | { 307 | ctx.L.GetChar (); 308 | 309 | if (ctx.L.input_char >= '1' && ctx.L.input_char<= '9') { 310 | ctx.L.string_buffer.Append ((char) ctx.L.input_char); 311 | ctx.NextState = 3; 312 | return true; 313 | } 314 | 315 | switch (ctx.L.input_char) { 316 | case '0': 317 | ctx.L.string_buffer.Append ((char) ctx.L.input_char); 318 | ctx.NextState = 4; 319 | return true; 320 | 321 | default: 322 | return false; 323 | } 324 | } 325 | 326 | private static bool State3 (FsmContext ctx) 327 | { 328 | while (ctx.L.GetChar ()) { 329 | if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9') { 330 | ctx.L.string_buffer.Append ((char) ctx.L.input_char); 331 | continue; 332 | } 333 | 334 | if (ctx.L.input_char == ' ' || 335 | ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') { 336 | ctx.Return = true; 337 | ctx.NextState = 1; 338 | return true; 339 | } 340 | 341 | switch (ctx.L.input_char) { 342 | case ',': 343 | case ']': 344 | case '}': 345 | ctx.L.UngetChar (); 346 | ctx.Return = true; 347 | ctx.NextState = 1; 348 | return true; 349 | 350 | case '.': 351 | ctx.L.string_buffer.Append ((char) ctx.L.input_char); 352 | ctx.NextState = 5; 353 | return true; 354 | 355 | case 'e': 356 | case 'E': 357 | ctx.L.string_buffer.Append ((char) ctx.L.input_char); 358 | ctx.NextState = 7; 359 | return true; 360 | 361 | default: 362 | return false; 363 | } 364 | } 365 | return true; 366 | } 367 | 368 | private static bool State4 (FsmContext ctx) 369 | { 370 | ctx.L.GetChar (); 371 | 372 | if (ctx.L.input_char == ' ' || 373 | ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') { 374 | ctx.Return = true; 375 | ctx.NextState = 1; 376 | return true; 377 | } 378 | 379 | switch (ctx.L.input_char) { 380 | case ',': 381 | case ']': 382 | case '}': 383 | ctx.L.UngetChar (); 384 | ctx.Return = true; 385 | ctx.NextState = 1; 386 | return true; 387 | 388 | case '.': 389 | ctx.L.string_buffer.Append ((char) ctx.L.input_char); 390 | ctx.NextState = 5; 391 | return true; 392 | 393 | case 'e': 394 | case 'E': 395 | ctx.L.string_buffer.Append ((char) ctx.L.input_char); 396 | ctx.NextState = 7; 397 | return true; 398 | 399 | default: 400 | return false; 401 | } 402 | } 403 | 404 | private static bool State5 (FsmContext ctx) 405 | { 406 | ctx.L.GetChar (); 407 | 408 | if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9') { 409 | ctx.L.string_buffer.Append ((char) ctx.L.input_char); 410 | ctx.NextState = 6; 411 | return true; 412 | } 413 | 414 | return false; 415 | } 416 | 417 | private static bool State6 (FsmContext ctx) 418 | { 419 | while (ctx.L.GetChar ()) { 420 | if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9') { 421 | ctx.L.string_buffer.Append ((char) ctx.L.input_char); 422 | continue; 423 | } 424 | 425 | if (ctx.L.input_char == ' ' || 426 | ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') { 427 | ctx.Return = true; 428 | ctx.NextState = 1; 429 | return true; 430 | } 431 | 432 | switch (ctx.L.input_char) { 433 | case ',': 434 | case ']': 435 | case '}': 436 | ctx.L.UngetChar (); 437 | ctx.Return = true; 438 | ctx.NextState = 1; 439 | return true; 440 | 441 | case 'e': 442 | case 'E': 443 | ctx.L.string_buffer.Append ((char) ctx.L.input_char); 444 | ctx.NextState = 7; 445 | return true; 446 | 447 | default: 448 | return false; 449 | } 450 | } 451 | 452 | return true; 453 | } 454 | 455 | private static bool State7 (FsmContext ctx) 456 | { 457 | ctx.L.GetChar (); 458 | 459 | if (ctx.L.input_char >= '0' && ctx.L.input_char<= '9') { 460 | ctx.L.string_buffer.Append ((char) ctx.L.input_char); 461 | ctx.NextState = 8; 462 | return true; 463 | } 464 | 465 | switch (ctx.L.input_char) { 466 | case '+': 467 | case '-': 468 | ctx.L.string_buffer.Append ((char) ctx.L.input_char); 469 | ctx.NextState = 8; 470 | return true; 471 | 472 | default: 473 | return false; 474 | } 475 | } 476 | 477 | private static bool State8 (FsmContext ctx) 478 | { 479 | while (ctx.L.GetChar ()) { 480 | if (ctx.L.input_char >= '0' && ctx.L.input_char<= '9') { 481 | ctx.L.string_buffer.Append ((char) ctx.L.input_char); 482 | continue; 483 | } 484 | 485 | if (ctx.L.input_char == ' ' || 486 | ctx.L.input_char >= '\t' && ctx.L.input_char<= '\r') { 487 | ctx.Return = true; 488 | ctx.NextState = 1; 489 | return true; 490 | } 491 | 492 | switch (ctx.L.input_char) { 493 | case ',': 494 | case ']': 495 | case '}': 496 | ctx.L.UngetChar (); 497 | ctx.Return = true; 498 | ctx.NextState = 1; 499 | return true; 500 | 501 | default: 502 | return false; 503 | } 504 | } 505 | 506 | return true; 507 | } 508 | 509 | private static bool State9 (FsmContext ctx) 510 | { 511 | ctx.L.GetChar (); 512 | 513 | switch (ctx.L.input_char) { 514 | case 'r': 515 | ctx.NextState = 10; 516 | return true; 517 | 518 | default: 519 | return false; 520 | } 521 | } 522 | 523 | private static bool State10 (FsmContext ctx) 524 | { 525 | ctx.L.GetChar (); 526 | 527 | switch (ctx.L.input_char) { 528 | case 'u': 529 | ctx.NextState = 11; 530 | return true; 531 | 532 | default: 533 | return false; 534 | } 535 | } 536 | 537 | private static bool State11 (FsmContext ctx) 538 | { 539 | ctx.L.GetChar (); 540 | 541 | switch (ctx.L.input_char) { 542 | case 'e': 543 | ctx.Return = true; 544 | ctx.NextState = 1; 545 | return true; 546 | 547 | default: 548 | return false; 549 | } 550 | } 551 | 552 | private static bool State12 (FsmContext ctx) 553 | { 554 | ctx.L.GetChar (); 555 | 556 | switch (ctx.L.input_char) { 557 | case 'a': 558 | ctx.NextState = 13; 559 | return true; 560 | 561 | default: 562 | return false; 563 | } 564 | } 565 | 566 | private static bool State13 (FsmContext ctx) 567 | { 568 | ctx.L.GetChar (); 569 | 570 | switch (ctx.L.input_char) { 571 | case 'l': 572 | ctx.NextState = 14; 573 | return true; 574 | 575 | default: 576 | return false; 577 | } 578 | } 579 | 580 | private static bool State14 (FsmContext ctx) 581 | { 582 | ctx.L.GetChar (); 583 | 584 | switch (ctx.L.input_char) { 585 | case 's': 586 | ctx.NextState = 15; 587 | return true; 588 | 589 | default: 590 | return false; 591 | } 592 | } 593 | 594 | private static bool State15 (FsmContext ctx) 595 | { 596 | ctx.L.GetChar (); 597 | 598 | switch (ctx.L.input_char) { 599 | case 'e': 600 | ctx.Return = true; 601 | ctx.NextState = 1; 602 | return true; 603 | 604 | default: 605 | return false; 606 | } 607 | } 608 | 609 | private static bool State16 (FsmContext ctx) 610 | { 611 | ctx.L.GetChar (); 612 | 613 | switch (ctx.L.input_char) { 614 | case 'u': 615 | ctx.NextState = 17; 616 | return true; 617 | 618 | default: 619 | return false; 620 | } 621 | } 622 | 623 | private static bool State17 (FsmContext ctx) 624 | { 625 | ctx.L.GetChar (); 626 | 627 | switch (ctx.L.input_char) { 628 | case 'l': 629 | ctx.NextState = 18; 630 | return true; 631 | 632 | default: 633 | return false; 634 | } 635 | } 636 | 637 | private static bool State18 (FsmContext ctx) 638 | { 639 | ctx.L.GetChar (); 640 | 641 | switch (ctx.L.input_char) { 642 | case 'l': 643 | ctx.Return = true; 644 | ctx.NextState = 1; 645 | return true; 646 | 647 | default: 648 | return false; 649 | } 650 | } 651 | 652 | private static bool State19 (FsmContext ctx) 653 | { 654 | while (ctx.L.GetChar ()) { 655 | switch (ctx.L.input_char) { 656 | case '"': 657 | ctx.L.UngetChar (); 658 | ctx.Return = true; 659 | ctx.NextState = 20; 660 | return true; 661 | 662 | case '\\': 663 | ctx.StateStack = 19; 664 | ctx.NextState = 21; 665 | return true; 666 | 667 | default: 668 | ctx.L.string_buffer.Append ((char) ctx.L.input_char); 669 | continue; 670 | } 671 | } 672 | 673 | return true; 674 | } 675 | 676 | private static bool State20 (FsmContext ctx) 677 | { 678 | ctx.L.GetChar (); 679 | 680 | switch (ctx.L.input_char) { 681 | case '"': 682 | ctx.Return = true; 683 | ctx.NextState = 1; 684 | return true; 685 | 686 | default: 687 | return false; 688 | } 689 | } 690 | 691 | private static bool State21 (FsmContext ctx) 692 | { 693 | ctx.L.GetChar (); 694 | 695 | switch (ctx.L.input_char) { 696 | case 'u': 697 | ctx.NextState = 22; 698 | return true; 699 | 700 | case '"': 701 | case '\'': 702 | case '/': 703 | case '\\': 704 | case 'b': 705 | case 'f': 706 | case 'n': 707 | case 'r': 708 | case 't': 709 | ctx.L.string_buffer.Append ( 710 | ProcessEscChar (ctx.L.input_char)); 711 | ctx.NextState = ctx.StateStack; 712 | return true; 713 | 714 | default: 715 | return false; 716 | } 717 | } 718 | 719 | private static bool State22 (FsmContext ctx) 720 | { 721 | int counter = 0; 722 | int mult = 4096; 723 | 724 | ctx.L.unichar = 0; 725 | 726 | while (ctx.L.GetChar ()) { 727 | 728 | if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9' || 729 | ctx.L.input_char >= 'A' && ctx.L.input_char <= 'F' || 730 | ctx.L.input_char >= 'a' && ctx.L.input_char <= 'f') { 731 | 732 | ctx.L.unichar += HexValue (ctx.L.input_char) * mult; 733 | 734 | counter++; 735 | mult /= 16; 736 | 737 | if (counter == 4) { 738 | ctx.L.string_buffer.Append ( 739 | Convert.ToChar (ctx.L.unichar)); 740 | ctx.NextState = ctx.StateStack; 741 | return true; 742 | } 743 | 744 | continue; 745 | } 746 | 747 | return false; 748 | } 749 | 750 | return true; 751 | } 752 | 753 | private static bool State23 (FsmContext ctx) 754 | { 755 | while (ctx.L.GetChar ()) { 756 | switch (ctx.L.input_char) { 757 | case '\'': 758 | ctx.L.UngetChar (); 759 | ctx.Return = true; 760 | ctx.NextState = 24; 761 | return true; 762 | 763 | case '\\': 764 | ctx.StateStack = 23; 765 | ctx.NextState = 21; 766 | return true; 767 | 768 | default: 769 | ctx.L.string_buffer.Append ((char) ctx.L.input_char); 770 | continue; 771 | } 772 | } 773 | 774 | return true; 775 | } 776 | 777 | private static bool State24 (FsmContext ctx) 778 | { 779 | ctx.L.GetChar (); 780 | 781 | switch (ctx.L.input_char) { 782 | case '\'': 783 | ctx.L.input_char = '"'; 784 | ctx.Return = true; 785 | ctx.NextState = 1; 786 | return true; 787 | 788 | default: 789 | return false; 790 | } 791 | } 792 | 793 | private static bool State25 (FsmContext ctx) 794 | { 795 | ctx.L.GetChar (); 796 | 797 | switch (ctx.L.input_char) { 798 | case '*': 799 | ctx.NextState = 27; 800 | return true; 801 | 802 | case '/': 803 | ctx.NextState = 26; 804 | return true; 805 | 806 | default: 807 | return false; 808 | } 809 | } 810 | 811 | private static bool State26 (FsmContext ctx) 812 | { 813 | while (ctx.L.GetChar ()) { 814 | if (ctx.L.input_char == '\n') { 815 | ctx.NextState = 1; 816 | return true; 817 | } 818 | } 819 | 820 | return true; 821 | } 822 | 823 | private static bool State27 (FsmContext ctx) 824 | { 825 | while (ctx.L.GetChar ()) { 826 | if (ctx.L.input_char == '*') { 827 | ctx.NextState = 28; 828 | return true; 829 | } 830 | } 831 | 832 | return true; 833 | } 834 | 835 | private static bool State28 (FsmContext ctx) 836 | { 837 | while (ctx.L.GetChar ()) { 838 | if (ctx.L.input_char == '*') 839 | continue; 840 | 841 | if (ctx.L.input_char == '/') { 842 | ctx.NextState = 1; 843 | return true; 844 | } 845 | 846 | ctx.NextState = 27; 847 | return true; 848 | } 849 | 850 | return true; 851 | } 852 | #endregion 853 | 854 | 855 | private bool GetChar () 856 | { 857 | if ((input_char = NextChar ()) != -1) 858 | return true; 859 | 860 | end_of_input = true; 861 | return false; 862 | } 863 | 864 | private int NextChar () 865 | { 866 | if (input_buffer != 0) { 867 | int tmp = input_buffer; 868 | input_buffer = 0; 869 | 870 | return tmp; 871 | } 872 | 873 | return reader.Read (); 874 | } 875 | 876 | public bool NextToken () 877 | { 878 | StateHandler handler; 879 | fsm_context.Return = false; 880 | 881 | while (true) { 882 | handler = fsm_handler_table[state - 1]; 883 | 884 | if (! handler (fsm_context)) 885 | throw new JsonException (input_char); 886 | 887 | if (end_of_input) 888 | return false; 889 | 890 | if (fsm_context.Return) { 891 | string_value = string_buffer.ToString (); 892 | string_buffer.Remove (0, string_buffer.Length); 893 | token = fsm_return_table[state - 1]; 894 | 895 | if (token == (int) ParserToken.Char) 896 | token = input_char; 897 | 898 | state = fsm_context.NextState; 899 | 900 | return true; 901 | } 902 | 903 | state = fsm_context.NextState; 904 | } 905 | } 906 | 907 | private void UngetChar () 908 | { 909 | input_buffer = input_char; 910 | } 911 | } 912 | } 913 | -------------------------------------------------------------------------------- /Assets/LitJson/JsonMapper.cs: -------------------------------------------------------------------------------- 1 | //#define UNITY3D 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.Globalization; 6 | using System.IO; 7 | using System.Reflection; 8 | 9 | namespace LitJson 10 | { 11 | internal struct PropertyMetadata 12 | { 13 | public MemberInfo Info; 14 | public bool IsField; 15 | public Type Type; 16 | } 17 | 18 | 19 | internal struct ArrayMetadata 20 | { 21 | private Type element_type; 22 | private bool is_array; 23 | private bool is_list; 24 | 25 | 26 | public Type ElementType { 27 | get { 28 | if (element_type == null) 29 | return typeof(JsonData); 30 | 31 | return element_type; 32 | } 33 | 34 | set { element_type = value; } 35 | } 36 | 37 | public bool IsArray { 38 | get { return is_array; } 39 | set { is_array = value; } 40 | } 41 | 42 | public bool IsList { 43 | get { return is_list; } 44 | set { is_list = value; } 45 | } 46 | } 47 | 48 | 49 | internal struct ObjectMetadata 50 | { 51 | private Type element_type; 52 | private bool is_dictionary; 53 | 54 | private IDictionary properties; 55 | 56 | 57 | public Type ElementType { 58 | get { 59 | if (element_type == null) 60 | return typeof(JsonData); 61 | 62 | return element_type; 63 | } 64 | 65 | set { element_type = value; } 66 | } 67 | 68 | public bool IsDictionary { 69 | get { return is_dictionary; } 70 | set { is_dictionary = value; } 71 | } 72 | 73 | public IDictionary Properties { 74 | get { return properties; } 75 | set { properties = value; } 76 | } 77 | } 78 | 79 | 80 | internal delegate void ExporterFunc (object obj, JsonWriter writer); 81 | public delegate void ExporterFunc (T obj, JsonWriter writer); 82 | 83 | internal delegate object ImporterFunc (object input); 84 | public delegate TValue ImporterFunc (TJson input); 85 | 86 | public delegate IJsonWrapper WrapperFactory (); 87 | 88 | /// 89 | /// JSON to .Net object and object to JSON conversions. 90 | /// 91 | public class JsonMapper 92 | { 93 | #region Fields 94 | 95 | private static int max_nesting_depth; 96 | 97 | private static IFormatProvider datetime_format; 98 | 99 | private static IDictionary base_exporters_table; 100 | private static IDictionary custom_exporters_table; 101 | 102 | private static IDictionary> 104 | base_importers_table; 105 | private static IDictionary> 107 | custom_importers_table; 108 | 109 | private static IDictionary array_metadata; 110 | private static readonly object array_metadata_lock = new Object (); 111 | 112 | private static IDictionary> 114 | conv_ops; 115 | private static readonly object conv_ops_lock = new Object (); 116 | 117 | private static IDictionary object_metadata; 118 | private static readonly object object_metadata_lock = new Object (); 119 | 120 | private static IDictionary> 122 | type_properties; 123 | private static readonly object type_properties_lock = new Object (); 124 | 125 | private static JsonWriter static_writer; 126 | private static readonly object static_writer_lock = new Object (); 127 | 128 | #endregion 129 | 130 | 131 | #region Constructors 132 | 133 | static JsonMapper () 134 | { 135 | max_nesting_depth = 100; 136 | 137 | array_metadata = new Dictionary (); 138 | conv_ops = new Dictionary> (); 139 | object_metadata = new Dictionary (); 140 | type_properties = new Dictionary> (); 142 | 143 | static_writer = new JsonWriter (); 144 | 145 | datetime_format = DateTimeFormatInfo.InvariantInfo; 146 | 147 | base_exporters_table = new Dictionary (); 148 | custom_exporters_table = new Dictionary (); 149 | 150 | base_importers_table = new Dictionary> (); 152 | custom_importers_table = new Dictionary> (); 154 | 155 | RegisterBaseExporters (); 156 | RegisterBaseImporters (); 157 | } 158 | 159 | #endregion 160 | 161 | 162 | #region Private Methods 163 | 164 | private static void AddArrayMetadata (Type type) 165 | { 166 | if (array_metadata.ContainsKey (type)) 167 | return; 168 | 169 | ArrayMetadata data = new ArrayMetadata (); 170 | 171 | data.IsArray = type.IsArray; 172 | 173 | if (type.GetInterface ("System.Collections.IList") != null) 174 | data.IsList = true; 175 | 176 | foreach (PropertyInfo p_info in type.GetProperties ()) { 177 | if (p_info.Name != "Item") 178 | continue; 179 | 180 | ParameterInfo[] parameters = p_info.GetIndexParameters (); 181 | 182 | if (parameters.Length != 1) 183 | continue; 184 | 185 | if (parameters [0].ParameterType == typeof(int)) 186 | data.ElementType = p_info.PropertyType; 187 | } 188 | 189 | lock (array_metadata_lock) { 190 | try { 191 | array_metadata.Add (type, data); 192 | } catch (ArgumentException) { 193 | return; 194 | } 195 | } 196 | } 197 | 198 | private static void AddObjectMetadata (Type type) 199 | { 200 | if (object_metadata.ContainsKey (type)) 201 | return; 202 | 203 | ObjectMetadata data = new ObjectMetadata (); 204 | 205 | if (type.GetInterface ("System.Collections.IDictionary") != null) 206 | data.IsDictionary = true; 207 | 208 | data.Properties = new Dictionary (); 209 | 210 | foreach (PropertyInfo p_info in type.GetProperties ()) { 211 | if (p_info.Name == "Item") { 212 | ParameterInfo[] parameters = p_info.GetIndexParameters (); 213 | 214 | if (parameters.Length != 1) 215 | continue; 216 | 217 | if (parameters [0].ParameterType == typeof(string)) 218 | data.ElementType = p_info.PropertyType; 219 | 220 | continue; 221 | } 222 | 223 | PropertyMetadata p_data = new PropertyMetadata (); 224 | p_data.Info = p_info; 225 | p_data.Type = p_info.PropertyType; 226 | 227 | data.Properties.Add (p_info.Name, p_data); 228 | } 229 | 230 | foreach (FieldInfo f_info in type.GetFields ()) { 231 | PropertyMetadata p_data = new PropertyMetadata (); 232 | p_data.Info = f_info; 233 | p_data.IsField = true; 234 | p_data.Type = f_info.FieldType; 235 | 236 | data.Properties.Add (f_info.Name, p_data); 237 | } 238 | 239 | lock (object_metadata_lock) { 240 | try { 241 | object_metadata.Add (type, data); 242 | } catch (ArgumentException) { 243 | return; 244 | } 245 | } 246 | } 247 | 248 | private static void AddTypeProperties (Type type) 249 | { 250 | if (type_properties.ContainsKey (type)) 251 | return; 252 | 253 | IList props = new List (); 254 | 255 | foreach (PropertyInfo p_info in type.GetProperties ()) { 256 | if (p_info.Name == "Item") 257 | continue; 258 | 259 | PropertyMetadata p_data = new PropertyMetadata (); 260 | p_data.Info = p_info; 261 | p_data.IsField = false; 262 | props.Add (p_data); 263 | } 264 | 265 | foreach (FieldInfo f_info in type.GetFields ()) { 266 | PropertyMetadata p_data = new PropertyMetadata (); 267 | p_data.Info = f_info; 268 | p_data.IsField = true; 269 | 270 | props.Add (p_data); 271 | } 272 | 273 | lock (type_properties_lock) { 274 | try { 275 | type_properties.Add (type, props); 276 | } catch (ArgumentException) { 277 | return; 278 | } 279 | } 280 | } 281 | 282 | private static MethodInfo GetConvOp (Type t1, Type t2) 283 | { 284 | lock (conv_ops_lock) { 285 | if (!conv_ops.ContainsKey (t1)) 286 | conv_ops.Add (t1, new Dictionary ()); 287 | } 288 | 289 | if (conv_ops [t1].ContainsKey (t2)) 290 | return conv_ops [t1] [t2]; 291 | 292 | MethodInfo op = t1.GetMethod ( 293 | "op_Implicit", new Type[] { t2 }); 294 | 295 | lock (conv_ops_lock) { 296 | try { 297 | conv_ops [t1].Add (t2, op); 298 | } catch (ArgumentException) { 299 | return conv_ops [t1] [t2]; 300 | } 301 | } 302 | 303 | return op; 304 | } 305 | 306 | private static object ReadValue (Type inst_type, JsonReader reader) 307 | { 308 | reader.Read (); 309 | 310 | if (reader.Token == JsonToken.ArrayEnd) 311 | return null; 312 | 313 | Type underlying_type = Nullable.GetUnderlyingType (inst_type); 314 | Type value_type = underlying_type ?? inst_type; 315 | 316 | if (reader.Token == JsonToken.Null) { 317 | if (inst_type.IsClass || underlying_type != null) { 318 | return null; 319 | } 320 | 321 | throw new JsonException (String.Format ( 322 | "Can't assign null to an instance of type {0}", 323 | inst_type)); 324 | } 325 | 326 | if (reader.Token == JsonToken.Double || 327 | reader.Token == JsonToken.Int || 328 | reader.Token == JsonToken.Long || 329 | reader.Token == JsonToken.String || 330 | reader.Token == JsonToken.Boolean) { 331 | 332 | Type json_type = reader.Value.GetType (); 333 | 334 | if (value_type.IsAssignableFrom (json_type)) 335 | return reader.Value; 336 | 337 | // If there's a custom importer that fits, use it 338 | if (custom_importers_table.ContainsKey (json_type) && 339 | custom_importers_table [json_type].ContainsKey ( 340 | value_type)) { 341 | 342 | ImporterFunc importer = 343 | custom_importers_table [json_type] [value_type]; 344 | 345 | return importer (reader.Value); 346 | } 347 | 348 | // Maybe there's a base importer that works 349 | if (base_importers_table.ContainsKey (json_type) && 350 | base_importers_table [json_type].ContainsKey ( 351 | value_type)) { 352 | 353 | ImporterFunc importer = 354 | base_importers_table [json_type] [value_type]; 355 | 356 | return importer (reader.Value); 357 | } 358 | 359 | // Maybe it's an enum 360 | if (value_type.IsEnum) 361 | return Enum.ToObject (value_type, reader.Value); 362 | 363 | // Try using an implicit conversion operator 364 | MethodInfo conv_op = GetConvOp (value_type, json_type); 365 | 366 | if (conv_op != null) 367 | return conv_op.Invoke (null, 368 | new object[] { reader.Value }); 369 | 370 | // No luck 371 | throw new JsonException (String.Format ( 372 | "Can't assign value '{0}' (type {1}) to type {2}", 373 | reader.Value, json_type, inst_type)); 374 | } 375 | 376 | object instance = null; 377 | 378 | if (reader.Token == JsonToken.ArrayStart) { 379 | 380 | AddArrayMetadata (inst_type); 381 | ArrayMetadata t_data = array_metadata [inst_type]; 382 | 383 | if (!t_data.IsArray && !t_data.IsList) 384 | throw new JsonException (String.Format ( 385 | "Type {0} can't act as an array", 386 | inst_type)); 387 | 388 | IList list; 389 | Type elem_type; 390 | 391 | if (!t_data.IsArray) { 392 | list = (IList)Activator.CreateInstance (inst_type); 393 | elem_type = t_data.ElementType; 394 | } else { 395 | list = new ArrayList (); 396 | elem_type = inst_type.GetElementType (); 397 | } 398 | 399 | while (true) { 400 | object item = ReadValue (elem_type, reader); 401 | if (item == null && reader.Token == JsonToken.ArrayEnd) 402 | break; 403 | 404 | list.Add (item); 405 | } 406 | 407 | if (t_data.IsArray) { 408 | int n = list.Count; 409 | instance = Array.CreateInstance (elem_type, n); 410 | 411 | for (int i = 0; i < n; i++) 412 | ((Array)instance).SetValue (list [i], i); 413 | } else 414 | instance = list; 415 | 416 | } else if (reader.Token == JsonToken.ObjectStart) { 417 | AddObjectMetadata (value_type); 418 | ObjectMetadata t_data = object_metadata [value_type]; 419 | 420 | instance = Activator.CreateInstance (value_type); 421 | 422 | while (true) { 423 | reader.Read (); 424 | 425 | if (reader.Token == JsonToken.ObjectEnd) 426 | break; 427 | 428 | string property = (string)reader.Value; 429 | 430 | if (t_data.Properties.ContainsKey (property)) { 431 | PropertyMetadata prop_data = 432 | t_data.Properties [property]; 433 | 434 | if (prop_data.IsField) { 435 | ((FieldInfo)prop_data.Info).SetValue ( 436 | instance, ReadValue (prop_data.Type, reader)); 437 | } else { 438 | PropertyInfo p_info = 439 | (PropertyInfo)prop_data.Info; 440 | 441 | if (p_info.CanWrite) 442 | p_info.SetValue ( 443 | instance, 444 | ReadValue (prop_data.Type, reader), 445 | null); 446 | else 447 | ReadValue (prop_data.Type, reader); 448 | } 449 | 450 | } else { 451 | if (!t_data.IsDictionary) { 452 | 453 | if (!reader.SkipNonMembers) { 454 | throw new JsonException (String.Format ( 455 | "The type {0} doesn't have the " + 456 | "property '{1}'", 457 | inst_type, property)); 458 | } else { 459 | ReadSkip (reader); 460 | continue; 461 | } 462 | } 463 | 464 | ((IDictionary)instance).Add ( 465 | property, ReadValue ( 466 | t_data.ElementType, reader)); 467 | } 468 | 469 | } 470 | 471 | } 472 | 473 | return instance; 474 | } 475 | 476 | private static IJsonWrapper ReadValue (WrapperFactory factory, 477 | JsonReader reader) 478 | { 479 | reader.Read (); 480 | 481 | if (reader.Token == JsonToken.ArrayEnd || 482 | reader.Token == JsonToken.Null) 483 | return null; 484 | 485 | IJsonWrapper instance = factory (); 486 | 487 | if (reader.Token == JsonToken.String) { 488 | instance.SetString ((string)reader.Value); 489 | return instance; 490 | } 491 | 492 | if (reader.Token == JsonToken.Double) { 493 | instance.SetDouble ((double)reader.Value); 494 | return instance; 495 | } 496 | 497 | if (reader.Token == JsonToken.Int) { 498 | instance.SetInt ((int)reader.Value); 499 | return instance; 500 | } 501 | 502 | if (reader.Token == JsonToken.Long) { 503 | instance.SetLong ((long)reader.Value); 504 | return instance; 505 | } 506 | 507 | if (reader.Token == JsonToken.Boolean) { 508 | instance.SetBoolean ((bool)reader.Value); 509 | return instance; 510 | } 511 | 512 | if (reader.Token == JsonToken.ArrayStart) { 513 | instance.SetJsonType (JsonType.Array); 514 | 515 | while (true) { 516 | IJsonWrapper item = ReadValue (factory, reader); 517 | if (item == null && reader.Token == JsonToken.ArrayEnd) 518 | break; 519 | 520 | ((IList)instance).Add (item); 521 | } 522 | } else if (reader.Token == JsonToken.ObjectStart) { 523 | instance.SetJsonType (JsonType.Object); 524 | 525 | while (true) { 526 | reader.Read (); 527 | 528 | if (reader.Token == JsonToken.ObjectEnd) 529 | break; 530 | 531 | string property = (string)reader.Value; 532 | 533 | ((IDictionary)instance) [property] = ReadValue ( 534 | factory, reader); 535 | } 536 | 537 | } 538 | 539 | return instance; 540 | } 541 | 542 | private static void ReadSkip (JsonReader reader) 543 | { 544 | ToWrapper ( 545 | delegate { 546 | return new JsonMockWrapper (); 547 | }, reader); 548 | } 549 | 550 | private static void RegisterBaseExporters () 551 | { 552 | base_exporters_table [typeof(byte)] = 553 | delegate (object obj, JsonWriter writer) { 554 | writer.Write (Convert.ToInt32 ((byte)obj)); 555 | }; 556 | 557 | base_exporters_table [typeof(char)] = 558 | delegate (object obj, JsonWriter writer) { 559 | writer.Write (Convert.ToString ((char)obj)); 560 | }; 561 | 562 | base_exporters_table [typeof(DateTime)] = 563 | delegate (object obj, JsonWriter writer) { 564 | writer.Write (Convert.ToString ((DateTime)obj, 565 | datetime_format)); 566 | }; 567 | 568 | base_exporters_table [typeof(decimal)] = 569 | delegate (object obj, JsonWriter writer) { 570 | writer.Write ((decimal)obj); 571 | }; 572 | 573 | base_exporters_table [typeof(sbyte)] = 574 | delegate (object obj, JsonWriter writer) { 575 | writer.Write (Convert.ToInt32 ((sbyte)obj)); 576 | }; 577 | 578 | base_exporters_table [typeof(short)] = 579 | delegate (object obj, JsonWriter writer) { 580 | writer.Write (Convert.ToInt32 ((short)obj)); 581 | }; 582 | 583 | base_exporters_table [typeof(ushort)] = 584 | delegate (object obj, JsonWriter writer) { 585 | writer.Write (Convert.ToInt32 ((ushort)obj)); 586 | }; 587 | 588 | base_exporters_table [typeof(uint)] = 589 | delegate (object obj, JsonWriter writer) { 590 | writer.Write (Convert.ToUInt64 ((uint)obj)); 591 | }; 592 | 593 | base_exporters_table [typeof(ulong)] = 594 | delegate (object obj, JsonWriter writer) { 595 | writer.Write ((ulong)obj); 596 | }; 597 | 598 | base_exporters_table [typeof(float)] = 599 | delegate(object obj, JsonWriter writer) { 600 | writer.Write (Convert.ToDouble ((float)obj)); 601 | }; 602 | 603 | base_exporters_table [typeof(Int64)] = 604 | delegate(object obj, JsonWriter writer) { 605 | writer.Write ((Int64)obj); 606 | }; 607 | } 608 | 609 | private static void RegisterBaseImporters () 610 | { 611 | ImporterFunc importer; 612 | 613 | importer = delegate (object input) { 614 | return Convert.ToByte ((int)input); 615 | }; 616 | RegisterImporter (base_importers_table, typeof(int), 617 | typeof(byte), importer); 618 | 619 | importer = delegate (object input) { 620 | return Convert.ToUInt64 ((int)input); 621 | }; 622 | RegisterImporter (base_importers_table, typeof(int), 623 | typeof(ulong), importer); 624 | 625 | importer = delegate (object input) { 626 | return Convert.ToSByte ((int)input); 627 | }; 628 | RegisterImporter (base_importers_table, typeof(int), 629 | typeof(sbyte), importer); 630 | 631 | importer = delegate (object input) { 632 | return Convert.ToInt16 ((int)input); 633 | }; 634 | RegisterImporter (base_importers_table, typeof(int), 635 | typeof(short), importer); 636 | 637 | importer = delegate (object input) { 638 | return Convert.ToUInt16 ((int)input); 639 | }; 640 | RegisterImporter (base_importers_table, typeof(int), 641 | typeof(ushort), importer); 642 | 643 | importer = delegate (object input) { 644 | return Convert.ToUInt32 ((int)input); 645 | }; 646 | RegisterImporter (base_importers_table, typeof(int), 647 | typeof(uint), importer); 648 | 649 | importer = delegate (object input) { 650 | return Convert.ToSingle ((int)input); 651 | }; 652 | RegisterImporter (base_importers_table, typeof(int), 653 | typeof(float), importer); 654 | 655 | importer = delegate (object input) { 656 | return Convert.ToDouble ((int)input); 657 | }; 658 | RegisterImporter (base_importers_table, typeof(int), 659 | typeof(double), importer); 660 | 661 | importer = delegate (object input) { 662 | return Convert.ToDecimal ((double)input); 663 | }; 664 | RegisterImporter (base_importers_table, typeof(double), 665 | typeof(decimal), importer); 666 | 667 | importer = delegate(object input) { 668 | return Convert.ToSingle ((float)(double)input); 669 | }; 670 | RegisterImporter (base_importers_table, typeof(double), 671 | typeof(float), importer); 672 | 673 | importer = delegate (object input) { 674 | return Convert.ToUInt32 ((long)input); 675 | }; 676 | RegisterImporter (base_importers_table, typeof(long), 677 | typeof(uint), importer); 678 | 679 | importer = delegate (object input) { 680 | return Convert.ToChar ((string)input); 681 | }; 682 | RegisterImporter (base_importers_table, typeof(string), 683 | typeof(char), importer); 684 | 685 | importer = delegate (object input) { 686 | return Convert.ToDateTime ((string)input, datetime_format); 687 | }; 688 | RegisterImporter (base_importers_table, typeof(string), 689 | typeof(DateTime), importer); 690 | 691 | importer = delegate(object input) { 692 | return Convert.ToInt64 ((Int32)input); 693 | }; 694 | RegisterImporter (base_importers_table, typeof(Int32), 695 | typeof(Int64), importer); 696 | } 697 | 698 | private static void RegisterImporter ( 699 | IDictionary> table, 700 | Type json_type, Type value_type, ImporterFunc importer) 701 | { 702 | if (!table.ContainsKey (json_type)) 703 | table.Add (json_type, new Dictionary ()); 704 | 705 | table [json_type] [value_type] = importer; 706 | } 707 | 708 | private static void WriteValue (object obj, JsonWriter writer, 709 | bool writer_is_private, 710 | int depth) 711 | { 712 | if (depth > max_nesting_depth) 713 | throw new JsonException ( 714 | String.Format ("Max allowed object depth reached while " + 715 | "trying to export from type {0}", 716 | obj.GetType ())); 717 | 718 | if (obj == null) { 719 | writer.Write (null); 720 | return; 721 | } 722 | 723 | if (obj is IJsonWrapper) { 724 | if (writer_is_private) 725 | writer.TextWriter.Write (((IJsonWrapper)obj).ToJson ()); 726 | else 727 | ((IJsonWrapper)obj).ToJson (writer); 728 | 729 | return; 730 | } 731 | 732 | #region UnityEngine specific 733 | 734 | if (obj is UnityEngine.Vector2) { 735 | writer.Write ((UnityEngine.Vector2)obj); 736 | return; 737 | } 738 | 739 | if (obj is UnityEngine.Vector3) { 740 | writer.Write ((UnityEngine.Vector3)obj); 741 | return; 742 | } 743 | 744 | if (obj is UnityEngine.Vector4) { 745 | writer.Write ((UnityEngine.Vector4)obj); 746 | return; 747 | } 748 | 749 | if (obj is UnityEngine.Quaternion) { 750 | writer.Write ((UnityEngine.Quaternion)obj); 751 | return; 752 | } 753 | 754 | if (obj is UnityEngine.Matrix4x4) { 755 | writer.Write ((UnityEngine.Matrix4x4)obj); 756 | return; 757 | } 758 | 759 | if (obj is UnityEngine.Ray) { 760 | writer.Write ((UnityEngine.Ray)obj); 761 | return; 762 | } 763 | 764 | if (obj is UnityEngine.RaycastHit) { 765 | writer.Write ((UnityEngine.RaycastHit)obj); 766 | return; 767 | } 768 | 769 | if (obj is UnityEngine.Color) { 770 | writer.Write ((UnityEngine.Color)obj); 771 | return; 772 | } 773 | 774 | #endregion 775 | 776 | if (obj is String) { 777 | writer.Write ((string)obj); 778 | return; 779 | } 780 | 781 | if (obj is Double) { 782 | writer.Write ((double)obj); 783 | return; 784 | } 785 | 786 | if (obj is Int32) { 787 | writer.Write ((int)obj); 788 | return; 789 | } 790 | 791 | if (obj is Boolean) { 792 | writer.Write ((bool)obj); 793 | return; 794 | } 795 | 796 | if (obj is Int64) { 797 | writer.Write ((long)obj); 798 | return; 799 | } 800 | 801 | if (obj is Array) { 802 | writer.WriteArrayStart (); 803 | 804 | foreach (object elem in (Array) obj) 805 | WriteValue (elem, writer, writer_is_private, depth + 1); 806 | 807 | writer.WriteArrayEnd(((Array)obj).Length > 0); 808 | 809 | return; 810 | } 811 | 812 | if (obj is IList) { 813 | writer.WriteArrayStart (); 814 | foreach (object elem in (IList) obj) 815 | WriteValue (elem, writer, writer_is_private, depth + 1); 816 | writer.WriteArrayEnd (((IList)obj).Count > 0); 817 | 818 | return; 819 | } 820 | 821 | if (obj is IDictionary) { 822 | writer.WriteObjectStart (); 823 | foreach (DictionaryEntry entry in (IDictionary) obj) { 824 | writer.WritePropertyName ((string)entry.Key); 825 | WriteValue (entry.Value, writer, writer_is_private, 826 | depth + 1); 827 | } 828 | writer.WriteObjectEnd (); 829 | 830 | return; 831 | } 832 | 833 | Type obj_type = obj.GetType (); 834 | 835 | // See if there's a custom exporter for the object 836 | if (custom_exporters_table.ContainsKey (obj_type)) { 837 | ExporterFunc exporter = custom_exporters_table [obj_type]; 838 | exporter (obj, writer); 839 | 840 | return; 841 | } 842 | 843 | // If not, maybe there's a base exporter 844 | if (base_exporters_table.ContainsKey (obj_type)) { 845 | ExporterFunc exporter = base_exporters_table [obj_type]; 846 | exporter (obj, writer); 847 | 848 | return; 849 | } 850 | 851 | // Last option, let's see if it's an enum 852 | if (obj is Enum) { 853 | Type e_type = Enum.GetUnderlyingType (obj_type); 854 | 855 | if (e_type == typeof(long) 856 | || e_type == typeof(uint) 857 | || e_type == typeof(ulong)) 858 | writer.Write ((ulong)obj); 859 | else 860 | writer.Write ((int)obj); 861 | 862 | return; 863 | } 864 | 865 | // Okay, so it looks like the input should be exported as an 866 | // object 867 | AddTypeProperties (obj_type); 868 | IList props = type_properties [obj_type]; 869 | 870 | writer.WriteObjectStart (); 871 | foreach (PropertyMetadata p_data in props) { 872 | if (p_data.IsField) { 873 | writer.WritePropertyName (p_data.Info.Name); 874 | WriteValue (((FieldInfo)p_data.Info).GetValue (obj), 875 | writer, writer_is_private, depth + 1); 876 | } else { 877 | PropertyInfo p_info = (PropertyInfo)p_data.Info; 878 | 879 | if (p_info.CanRead) { 880 | writer.WritePropertyName (p_data.Info.Name); 881 | WriteValue (p_info.GetValue (obj, null), 882 | writer, writer_is_private, depth + 1); 883 | } 884 | } 885 | } 886 | writer.WriteObjectEnd (); 887 | } 888 | 889 | #endregion 890 | 891 | 892 | public static string ToJson (object obj) 893 | { 894 | lock (static_writer_lock) { 895 | static_writer.Reset (); 896 | 897 | WriteValue (obj, static_writer, true, 0); 898 | 899 | return static_writer.ToString (); 900 | } 901 | } 902 | 903 | public static void ToJson (object obj, JsonWriter writer) 904 | { 905 | WriteValue (obj, writer, false, 0); 906 | } 907 | 908 | public static JsonData ToObject (JsonReader reader) 909 | { 910 | return (JsonData)ToWrapper ( 911 | delegate { 912 | return new JsonData (); 913 | }, reader); 914 | } 915 | 916 | public static JsonData ToObject (TextReader reader) 917 | { 918 | JsonReader json_reader = new JsonReader (reader); 919 | 920 | return (JsonData)ToWrapper ( 921 | delegate { 922 | return new JsonData (); 923 | }, json_reader); 924 | } 925 | 926 | public static JsonData ToObject (string json) 927 | { 928 | return (JsonData)ToWrapper ( 929 | delegate { 930 | return new JsonData (); 931 | }, json); 932 | } 933 | 934 | public static T ToObject (JsonReader reader) 935 | { 936 | return (T)ReadValue (typeof(T), reader); 937 | } 938 | 939 | public static T ToObject (TextReader reader) 940 | { 941 | JsonReader json_reader = new JsonReader (reader); 942 | 943 | return (T)ReadValue (typeof(T), json_reader); 944 | } 945 | 946 | public static T ToObject (string json) 947 | { 948 | JsonReader reader = new JsonReader (json); 949 | 950 | return (T)ReadValue (typeof(T), reader); 951 | } 952 | 953 | public static IJsonWrapper ToWrapper (WrapperFactory factory, 954 | JsonReader reader) 955 | { 956 | return ReadValue (factory, reader); 957 | } 958 | 959 | public static IJsonWrapper ToWrapper (WrapperFactory factory, 960 | string json) 961 | { 962 | JsonReader reader = new JsonReader (json); 963 | 964 | return ReadValue (factory, reader); 965 | } 966 | 967 | public static void RegisterExporter (ExporterFunc exporter) 968 | { 969 | ExporterFunc exporter_wrapper = 970 | delegate (object obj, JsonWriter writer) { 971 | exporter ((T)obj, writer); 972 | }; 973 | 974 | custom_exporters_table [typeof(T)] = exporter_wrapper; 975 | } 976 | 977 | public static void RegisterImporter ( 978 | ImporterFunc importer) 979 | { 980 | ImporterFunc importer_wrapper = 981 | delegate (object input) { 982 | return importer ((TJson)input); 983 | }; 984 | 985 | RegisterImporter (custom_importers_table, typeof(TJson), 986 | typeof(TValue), importer_wrapper); 987 | } 988 | 989 | public static void UnregisterExporters () 990 | { 991 | custom_exporters_table.Clear (); 992 | } 993 | 994 | public static void UnregisterImporters () 995 | { 996 | custom_importers_table.Clear (); 997 | } 998 | } 999 | } 1000 | --------------------------------------------------------------------------------