init commit
						commit
						a0532a5ced
					
				|  | @ -0,0 +1,2 @@ | ||||||
|  | # Mac auto-generated system files | ||||||
|  | *.DS_Store* | ||||||
|  | @ -0,0 +1,65 @@ | ||||||
|  | # Guru Template Change Log | ||||||
|  | 
 | ||||||
|  | ## 2.0.3 | ||||||
|  | 
 | ||||||
|  | - Firebase | ||||||
|  |   - 整体升级到 10.1.1. 作为Unity组统一标准 | ||||||
|  | - Max 广告聚合 | ||||||
|  |   - 升级Max聚合的所有渠道, 对齐[中台需求](https://docs.google.com/spreadsheets/d/161UnDimGerqetIYNiMCfUBmJ7qozht8z1baxnxRdCnI/edit#gid=311231520)   | ||||||
|  | - GuruBuildTools | ||||||
|  |   - 新增 Deps Reporter, 用于在Jenkins环境, 上报打包依赖 (Android/iOS). 理论上报告工具不影响本地打包, 不影响打包机打包. 出问题的话也不会中断打包流程. | ||||||
|  |   - 修复 IOS 打包成功后, 上传TF时报Info.plist内没有设置版本号的报错 | ||||||
|  | - GuruAds | ||||||
|  |   - 根据中台的需求, 新增广告属性打点:  `bads_imp`, `bads_loaded` | ||||||
|  |   - 去掉使用三方广告SDK, 需要预先设置 AD_AMAZON 和 AD_PUBMATIC 的流程. 目前默认全部接入  | ||||||
|  | - GuruAnalytics | ||||||
|  |   - 更新和添加 Worker 支持, 预期可让用户在线时长更加精准 | ||||||
|  | - DeviceUtil | ||||||
|  |   - 更新Android端获取系统版本号的的接口, 方便程序判断 Android API 33 以及后继的响应  | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## 2.0.2 | ||||||
|  | 
 | ||||||
|  | 各模块对应的内容更新 | ||||||
|  | 
 | ||||||
|  | - FBService  | ||||||
|  |   - 添加IOS平台打点开启语句 `FB.Mobile.SetAdvertiserTrackingEnabled(true);` | ||||||
|  | - AdjustService  | ||||||
|  |   - 自动创建生命周期对象, 优化部分逻辑 | ||||||
|  | - Firebase | ||||||
|  |   - 修复 Firebase(8.1.0) 无法生成 google-service.json 的问题. 替换了Firebase.Editor.dll | ||||||
|  | - Guru L10n 更新 | ||||||
|  |   - 更新GuruL10N -> 0.4.0 新增单独翻译google sheet的设置 | ||||||
|  |   - 更新插件 backup.csv 缓存路径, 请各个项目将 **`backup.csv`** 提交到代码内进行跟踪! | ||||||
|  |   - 更新了语言编码去重识别, 根据Alpha表重排的功能 | ||||||
|  | - Guru Analytics | ||||||
|  |   - 广告打点中加入了 Waterfall 信息上报 | ||||||
|  |   - 添加了用户属性缓存表 | ||||||
|  | - Guru Build Tools | ||||||
|  |   - 新增 DebugView 联调参数注入, 需要项目安装验证 | ||||||
|  |   - 更新打包管线相关逻辑, IOS Target Version 升级为 13.0 | ||||||
|  | - RemoteConfig | ||||||
|  |   - 修复了AROConfig的初始化逻辑, 避免初始化时报空引发报错 | ||||||
|  | - IAPService | ||||||
|  |   - 添加了本地的订单数据缓存, 防止特殊操作的时候, iOS的订单重复上报的问题. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## 2.0.1 | ||||||
|  | - 更新自打点库原生依赖  | ||||||
|  |   - Android 原生库版本 (0.2.10) | ||||||
|  |   - IOS 原生库版本 (0.2.16)  | ||||||
|  | - 更新 L10n 版本 (3.2.0) | ||||||
|  |   - 更新了导入表格会把code行也导入进来的BUG | ||||||
|  |   - 更新配置多语言时 nb-NO 语言的识别功能 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## 1.6.0 | ||||||
|  | - 添加了 Amazon (1.0.0) 广告扩展,使用说明详见: [GuruAds 扩展 Amazon安装说明](GuruAds/README.md) | ||||||
|  | - 添加了 GuruAnalytics (1.0.3) 广告自打点插件,使用说明详见: [Guru Unity Analytics 自打点插件](GuruAnalytics/README.md) | ||||||
|  | - 添加了 StandardProperties 标准属性点类, 用于记录游戏内的标准事件和属性 | ||||||
|  | - 添加了 GuruBuildTool 模块, 收集和整理了目前为止所有 BuildTool 相关的逻辑 | ||||||
|  | - 扩展了GuruSettings的配置格式(Amazon广告配置相关) | ||||||
|  | - 在 AdjustService 内添加获取 AdjustId 以及 FirebaseID 的逻辑 | ||||||
|  | - 在 AdjustService 内添加上报 DeviceID 的逻辑 | ||||||
|  | - 修复了若干插件显示层以及构建管线的BUG | ||||||
|  | - 调整了框架整体的文件结构 | ||||||
|  | @ -0,0 +1,7 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 6acd89dd2c454676834609a8981c136a | ||||||
|  | TextScriptImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: e3ff74f6837b4366b71c8bfe322a4562 | ||||||
|  | timeCreated: 1671010724 | ||||||
|  | @ -0,0 +1,8 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: c2bda7db652148e7a73fd2179e22be09 | ||||||
|  | folderAsset: yes | ||||||
|  | DefaultImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 347116c7f46946f5945ca097a6e964db | ||||||
|  | timeCreated: 1687241187 | ||||||
|  | @ -0,0 +1,83 @@ | ||||||
|  | #if UNITY_ANDROID | ||||||
|  | namespace Guru | ||||||
|  | { | ||||||
|  |     using System.Collections.Generic; | ||||||
|  |     using System.IO; | ||||||
|  |     using System.Linq; | ||||||
|  |     using UnityEditor; | ||||||
|  |     using UnityEditor.Build; | ||||||
|  |     using UnityEditor.Build.Reporting; | ||||||
|  |     using UnityEngine; | ||||||
|  |      | ||||||
|  |     /// <summary> | ||||||
|  |     /// Android混淆器内容填充 | ||||||
|  |     /// 于应用构建前执行 | ||||||
|  |     /// </summary> | ||||||
|  |     public class UserProguardHelper: IPreprocessBuildWithReport | ||||||
|  |     { | ||||||
|  |         public int callbackOrder { get; } = 0; | ||||||
|  |         public void OnPreprocessBuild(BuildReport report) | ||||||
|  |         { | ||||||
|  |             // 修复ProguardFile | ||||||
|  |             OnApplyProguardFiles(); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         private static void OnApplyProguardFiles() | ||||||
|  |         { | ||||||
|  |             string proguardPath = $"{Application.dataPath}/Plugins/Android/proguard-user.txt"; | ||||||
|  |             if (File.Exists(proguardPath)) | ||||||
|  |             { | ||||||
|  |                 List<string> keeps = new List<string>(); | ||||||
|  |                 DirectoryInfo dir = new DirectoryInfo(Application.dataPath); | ||||||
|  |                 string raw = File.ReadAllText(proguardPath); | ||||||
|  |                  | ||||||
|  |                 if (dir.Exists) | ||||||
|  |                 { | ||||||
|  |                     var editors = dir.GetDirectories("Editor", SearchOption.AllDirectories); | ||||||
|  |                     List<FileInfo> files = new List<FileInfo>(); | ||||||
|  |                     foreach (var e in editors) | ||||||
|  |                     { | ||||||
|  |                         files.AddRange(e.GetFiles("*Proguards.txt", SearchOption.AllDirectories)); | ||||||
|  |                     } | ||||||
|  |                      | ||||||
|  |                     // Debug.Log($"--- Proguard Files: {files.Length}"); | ||||||
|  | 
 | ||||||
|  |                     string[] lens = null; | ||||||
|  |                     string l = ""; | ||||||
|  |                     for (int i = 0; i < files.Count; i++) | ||||||
|  |                     { | ||||||
|  |                         lens = File.ReadAllLines(files[i].FullName); | ||||||
|  |                         foreach (var s in lens) | ||||||
|  |                         { | ||||||
|  |                             l = s.TrimStart(); | ||||||
|  |                             if(string.IsNullOrEmpty(l)) continue; | ||||||
|  |                             if(raw.Contains(l)) continue; | ||||||
|  |                             keeps.Add(l); | ||||||
|  |                             Debug.Log($"--- ✏️ Apply: [ {l} ]"); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                  | ||||||
|  |                 if (keeps.Count == 0) return; // 无注入文件则退出 | ||||||
|  |                  | ||||||
|  |                 List<string> lines = File.ReadAllLines(proguardPath).ToList(); | ||||||
|  |                 lines.Add(""); | ||||||
|  |                 lines.AddRange(keeps); | ||||||
|  |                  | ||||||
|  |                 File.WriteAllLines(proguardPath, lines.ToArray()); | ||||||
|  |                 Debug.Log($"--- Update proguard-user.txt done! ☀️ ---"); | ||||||
|  |                  | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |          | ||||||
|  |         [MenuItem("Tools/Android/Add proguard-user")] | ||||||
|  |         private static void EditorAddProguardUser() | ||||||
|  |         { | ||||||
|  |             OnApplyProguardFiles(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 548800163ca249de8edcfa039836449d | ||||||
|  | timeCreated: 1687241187 | ||||||
|  | @ -0,0 +1,8 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: c4d05b56a8b854c2c93c1db55c5bc13b | ||||||
|  | folderAsset: yes | ||||||
|  | DefaultImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,22 @@ | ||||||
|  | %YAML 1.1 | ||||||
|  | %TAG !u! tag:unity3d.com,2011: | ||||||
|  | --- !u!114 &11400000 | ||||||
|  | MonoBehaviour: | ||||||
|  |   m_ObjectHideFlags: 0 | ||||||
|  |   m_CorrespondingSourceObject: {fileID: 0} | ||||||
|  |   m_PrefabInstance: {fileID: 0} | ||||||
|  |   m_PrefabAsset: {fileID: 0} | ||||||
|  |   m_GameObject: {fileID: 0} | ||||||
|  |   m_Enabled: 1 | ||||||
|  |   m_EditorHideFlags: 0 | ||||||
|  |   m_Script: {fileID: 11500000, guid: a403a09e241a0480a957591ea60fb785, type: 3} | ||||||
|  |   m_Name: settings | ||||||
|  |   m_EditorClassIdentifier:  | ||||||
|  |   usesCleartextTraffic: 0 | ||||||
|  |   writeExternalStorage: 0 | ||||||
|  |   accessFineLocation: 0 | ||||||
|  |   addsKotlin: 0 | ||||||
|  |   addsAndroidBrowser: 0 | ||||||
|  |   enableJetifier: 1 | ||||||
|  |   authCallbackUrls: [] | ||||||
|  |   supportLINELogin: 0 | ||||||
|  | @ -0,0 +1,8 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 0e00e1700888048628304b0c69a1c61e | ||||||
|  | NativeFormatImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   mainObjectFileID: 0 | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,8 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: a99ba0cf57ee14adbb2be18231643025 | ||||||
|  | folderAsset: yes | ||||||
|  | DefaultImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
										
											Binary file not shown.
										
									
								
							|  | @ -0,0 +1,7 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: a2b9d9fa87e634e99b4932bcaaec2f40 | ||||||
|  | DefaultImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 0c5075eff6f44aad872f5c8160b94132 | ||||||
|  | timeCreated: 1703228621 | ||||||
|  | @ -0,0 +1,14 @@ | ||||||
|  | { | ||||||
|  |     "name": "Guru.LitJson", | ||||||
|  |     "rootNamespace": "Guru.LitJson", | ||||||
|  |     "references": [], | ||||||
|  |     "includePlatforms": [], | ||||||
|  |     "excludePlatforms": [], | ||||||
|  |     "allowUnsafeCode": false, | ||||||
|  |     "overrideReferences": false, | ||||||
|  |     "precompiledReferences": [], | ||||||
|  |     "autoReferenced": true, | ||||||
|  |     "defineConstraints": [], | ||||||
|  |     "versionDefines": [], | ||||||
|  |     "noEngineReferences": false | ||||||
|  | } | ||||||
|  | @ -0,0 +1,7 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 2bd307385b74f48c2bdf31bd24d78056 | ||||||
|  | AssemblyDefinitionImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,8 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 63a6db46b50de451a9008227af468125 | ||||||
|  | folderAsset: yes | ||||||
|  | DefaultImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,18 @@ | ||||||
|  | using System; | ||||||
|  | using System.Reflection; | ||||||
|  | using System.Runtime.CompilerServices; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | [assembly: CLSCompliant (true)] | ||||||
|  | 
 | ||||||
|  | [assembly: AssemblyTitle ("LitJson")] | ||||||
|  | [assembly: AssemblyDescription ("LitJSON library")] | ||||||
|  | [assembly: AssemblyConfiguration ("")] | ||||||
|  | [assembly: AssemblyCompany ("")] | ||||||
|  | [assembly: AssemblyProduct ("LitJSON")] | ||||||
|  | [assembly: AssemblyCopyright ( | ||||||
|  |     "The authors disclaim copyright to this source code")] | ||||||
|  | [assembly: AssemblyTrademark ("")] | ||||||
|  | [assembly: AssemblyCulture ("")] | ||||||
|  | 
 | ||||||
|  | [assembly: AssemblyVersion ("@ASSEMBLY_VERSION@")] | ||||||
|  | @ -0,0 +1,7 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: b7cf5dc50b2d64dca9830e9144c06b2c | ||||||
|  | DefaultImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,60 @@ | ||||||
|  | #region Header | ||||||
|  | /** | ||||||
|  |  * IJsonWrapper.cs | ||||||
|  |  *   Interface that represents a type capable of handling all kinds of JSON | ||||||
|  |  *   data. This is mainly used when mapping objects through JsonMapper, and | ||||||
|  |  *   it's implemented by JsonData. | ||||||
|  |  * | ||||||
|  |  * The authors disclaim copyright to this source code. For more details, see | ||||||
|  |  * the COPYING file included with this distribution. | ||||||
|  |  **/ | ||||||
|  | #endregion | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | using System.Collections; | ||||||
|  | using System.Collections.Specialized; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | namespace Guru.LitJson | ||||||
|  | { | ||||||
|  |     public enum JsonType | ||||||
|  |     { | ||||||
|  |         None, | ||||||
|  | 
 | ||||||
|  |         Object, | ||||||
|  |         Array, | ||||||
|  |         String, | ||||||
|  |         Int, | ||||||
|  |         Long, | ||||||
|  |         Double, | ||||||
|  |         Boolean | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public interface IJsonWrapper : IList, IOrderedDictionary | ||||||
|  |     { | ||||||
|  |         bool IsArray   { get; } | ||||||
|  |         bool IsBoolean { get; } | ||||||
|  |         bool IsDouble  { get; } | ||||||
|  |         bool IsInt     { get; } | ||||||
|  |         bool IsLong    { get; } | ||||||
|  |         bool IsObject  { get; } | ||||||
|  |         bool IsString  { get; } | ||||||
|  | 
 | ||||||
|  |         bool     GetBoolean (); | ||||||
|  |         double   GetDouble (); | ||||||
|  |         int      GetInt (); | ||||||
|  |         JsonType GetJsonType (); | ||||||
|  |         long     GetLong (); | ||||||
|  |         string   GetString (); | ||||||
|  | 
 | ||||||
|  |         void SetBoolean  (bool val); | ||||||
|  |         void SetDouble   (double val); | ||||||
|  |         void SetInt      (int val); | ||||||
|  |         void SetJsonType (JsonType type); | ||||||
|  |         void SetLong     (long val); | ||||||
|  |         void SetString   (string val); | ||||||
|  | 
 | ||||||
|  |         string ToJson (); | ||||||
|  |         void   ToJson (JsonWriter writer); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,11 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 394f32e20d93e474487e7f8a07415298 | ||||||
|  | MonoImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   serializedVersion: 2 | ||||||
|  |   defaultReferences: [] | ||||||
|  |   executionOrder: 0 | ||||||
|  |   icon: {instanceID: 0} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -0,0 +1,11 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 5559251be0dd54c669a5b225ad53126e | ||||||
|  | MonoImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   serializedVersion: 2 | ||||||
|  |   defaultReferences: [] | ||||||
|  |   executionOrder: 0 | ||||||
|  |   icon: {instanceID: 0} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,65 @@ | ||||||
|  | #region Header | ||||||
|  | /** | ||||||
|  |  * JsonException.cs | ||||||
|  |  *   Base class throwed by LitJSON when a parsing error occurs. | ||||||
|  |  * | ||||||
|  |  * The authors disclaim copyright to this source code. For more details, see | ||||||
|  |  * the COPYING file included with this distribution. | ||||||
|  |  **/ | ||||||
|  | #endregion | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | using System; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | namespace Guru.LitJson | ||||||
|  | { | ||||||
|  |     public class JsonException : | ||||||
|  | #if NETSTANDARD1_5 | ||||||
|  |         Exception | ||||||
|  | #else | ||||||
|  |         ApplicationException | ||||||
|  | #endif | ||||||
|  |     { | ||||||
|  |         public JsonException () : base () | ||||||
|  |         { | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         internal JsonException (ParserToken token) : | ||||||
|  |             base (String.Format ( | ||||||
|  |                     "Invalid token '{0}' in input string", token)) | ||||||
|  |         { | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         internal JsonException (ParserToken token, | ||||||
|  |                                 Exception inner_exception) : | ||||||
|  |             base (String.Format ( | ||||||
|  |                     "Invalid token '{0}' in input string", token), | ||||||
|  |                 inner_exception) | ||||||
|  |         { | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         internal JsonException (int c) : | ||||||
|  |             base (String.Format ( | ||||||
|  |                     "Invalid character '{0}' in input string", (char) c)) | ||||||
|  |         { | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         internal JsonException (int c, Exception inner_exception) : | ||||||
|  |             base (String.Format ( | ||||||
|  |                     "Invalid character '{0}' in input string", (char) c), | ||||||
|  |                 inner_exception) | ||||||
|  |         { | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         public JsonException (string message) : base (message) | ||||||
|  |         { | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public JsonException (string message, Exception inner_exception) : | ||||||
|  |             base (message, inner_exception) | ||||||
|  |         { | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,11 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 35e97fa5a8f99447a90bffb890cfd55d | ||||||
|  | MonoImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   serializedVersion: 2 | ||||||
|  |   defaultReferences: [] | ||||||
|  |   executionOrder: 0 | ||||||
|  |   icon: {instanceID: 0} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,995 @@ | ||||||
|  | #region Header | ||||||
|  | /** | ||||||
|  |  * JsonMapper.cs | ||||||
|  |  *   JSON to .Net object and object to JSON conversions. | ||||||
|  |  * | ||||||
|  |  * The authors disclaim copyright to this source code. For more details, see | ||||||
|  |  * the COPYING file included with this distribution. | ||||||
|  |  **/ | ||||||
|  | #endregion | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | using System; | ||||||
|  | using System.Collections; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Globalization; | ||||||
|  | using System.IO; | ||||||
|  | using System.Reflection; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | namespace Guru.LitJson | ||||||
|  | { | ||||||
|  |     internal struct PropertyMetadata | ||||||
|  |     { | ||||||
|  |         public MemberInfo Info; | ||||||
|  |         public bool       IsField; | ||||||
|  |         public Type       Type; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     internal struct ArrayMetadata | ||||||
|  |     { | ||||||
|  |         private Type element_type; | ||||||
|  |         private bool is_array; | ||||||
|  |         private bool is_list; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         public Type ElementType { | ||||||
|  |             get { | ||||||
|  |                 if (element_type == null) | ||||||
|  |                     return typeof (JsonData); | ||||||
|  | 
 | ||||||
|  |                 return element_type; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             set { element_type = value; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public bool IsArray { | ||||||
|  |             get { return is_array; } | ||||||
|  |             set { is_array = value; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public bool IsList { | ||||||
|  |             get { return is_list; } | ||||||
|  |             set { is_list = value; } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     internal struct ObjectMetadata | ||||||
|  |     { | ||||||
|  |         private Type element_type; | ||||||
|  |         private bool is_dictionary; | ||||||
|  | 
 | ||||||
|  |         private IDictionary<string, PropertyMetadata> properties; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         public Type ElementType { | ||||||
|  |             get { | ||||||
|  |                 if (element_type == null) | ||||||
|  |                     return typeof (JsonData); | ||||||
|  | 
 | ||||||
|  |                 return element_type; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             set { element_type = value; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public bool IsDictionary { | ||||||
|  |             get { return is_dictionary; } | ||||||
|  |             set { is_dictionary = value; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public IDictionary<string, PropertyMetadata> Properties { | ||||||
|  |             get { return properties; } | ||||||
|  |             set { properties = value; } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     internal delegate void ExporterFunc    (object obj, JsonWriter writer); | ||||||
|  |     public   delegate void ExporterFunc<T> (T obj, JsonWriter writer); | ||||||
|  | 
 | ||||||
|  |     internal delegate object ImporterFunc                (object input); | ||||||
|  |     public   delegate TValue ImporterFunc<TJson, TValue> (TJson input); | ||||||
|  | 
 | ||||||
|  |     public delegate IJsonWrapper WrapperFactory (); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     public class JsonMapper | ||||||
|  |     { | ||||||
|  |         #region Fields | ||||||
|  |         private static readonly int max_nesting_depth; | ||||||
|  | 
 | ||||||
|  |         private static readonly IFormatProvider datetime_format; | ||||||
|  | 
 | ||||||
|  |         private static readonly IDictionary<Type, ExporterFunc> base_exporters_table; | ||||||
|  |         private static readonly IDictionary<Type, ExporterFunc> custom_exporters_table; | ||||||
|  |         private static readonly object custom_exporters_table_lock = new Object(); | ||||||
|  | 
 | ||||||
|  |         private static readonly IDictionary<Type, | ||||||
|  |                 IDictionary<Type, ImporterFunc>> base_importers_table; | ||||||
|  |         private static readonly IDictionary<Type, | ||||||
|  |                 IDictionary<Type, ImporterFunc>> custom_importers_table; | ||||||
|  |         private static readonly object custom_importers_table_lock = new Object(); | ||||||
|  | 
 | ||||||
|  |         private static readonly IDictionary<Type, ArrayMetadata> array_metadata; | ||||||
|  |         private static readonly object array_metadata_lock = new Object (); | ||||||
|  | 
 | ||||||
|  |         private static readonly IDictionary<Type, | ||||||
|  |                 IDictionary<Type, MethodInfo>> conv_ops; | ||||||
|  |         private static readonly object conv_ops_lock = new Object (); | ||||||
|  | 
 | ||||||
|  |         private static readonly IDictionary<Type, ObjectMetadata> object_metadata; | ||||||
|  |         private static readonly object object_metadata_lock = new Object (); | ||||||
|  | 
 | ||||||
|  |         private static readonly IDictionary<Type, | ||||||
|  |                 IList<PropertyMetadata>> type_properties; | ||||||
|  |         private static readonly object type_properties_lock = new Object (); | ||||||
|  | 
 | ||||||
|  |         private static readonly JsonWriter      static_writer; | ||||||
|  |         private static readonly object static_writer_lock = new Object (); | ||||||
|  |         #endregion | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         #region Constructors | ||||||
|  |         static JsonMapper () | ||||||
|  |         { | ||||||
|  |             max_nesting_depth = 100; | ||||||
|  | 
 | ||||||
|  |             array_metadata = new Dictionary<Type, ArrayMetadata> (); | ||||||
|  |             conv_ops = new Dictionary<Type, IDictionary<Type, MethodInfo>> (); | ||||||
|  |             object_metadata = new Dictionary<Type, ObjectMetadata> (); | ||||||
|  |             type_properties = new Dictionary<Type, | ||||||
|  |                             IList<PropertyMetadata>> (); | ||||||
|  | 
 | ||||||
|  |             static_writer = new JsonWriter (); | ||||||
|  | 
 | ||||||
|  |             datetime_format = DateTimeFormatInfo.InvariantInfo; | ||||||
|  | 
 | ||||||
|  |             base_exporters_table   = new Dictionary<Type, ExporterFunc> (); | ||||||
|  |             custom_exporters_table = new Dictionary<Type, ExporterFunc> (); | ||||||
|  | 
 | ||||||
|  |             base_importers_table = new Dictionary<Type, | ||||||
|  |                                  IDictionary<Type, ImporterFunc>> (); | ||||||
|  |             custom_importers_table = new Dictionary<Type, | ||||||
|  |                                    IDictionary<Type, ImporterFunc>> (); | ||||||
|  | 
 | ||||||
|  |             RegisterBaseExporters (); | ||||||
|  |             RegisterBaseImporters (); | ||||||
|  |         } | ||||||
|  |         #endregion | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         #region Private Methods | ||||||
|  |         private static void AddArrayMetadata (Type type) | ||||||
|  |         { | ||||||
|  |             if (array_metadata.ContainsKey (type)) | ||||||
|  |                 return; | ||||||
|  | 
 | ||||||
|  |             ArrayMetadata data = new ArrayMetadata (); | ||||||
|  | 
 | ||||||
|  |             data.IsArray = type.IsArray; | ||||||
|  | 
 | ||||||
|  |             if (type.GetInterface ("System.Collections.IList") != null) | ||||||
|  |                 data.IsList = true; | ||||||
|  | 
 | ||||||
|  |             foreach (PropertyInfo p_info in type.GetProperties ()) { | ||||||
|  |                 if (p_info.Name != "Item") | ||||||
|  |                     continue; | ||||||
|  | 
 | ||||||
|  |                 ParameterInfo[] parameters = p_info.GetIndexParameters (); | ||||||
|  | 
 | ||||||
|  |                 if (parameters.Length != 1) | ||||||
|  |                     continue; | ||||||
|  | 
 | ||||||
|  |                 if (parameters[0].ParameterType == typeof (int)) | ||||||
|  |                     data.ElementType = p_info.PropertyType; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             lock (array_metadata_lock) { | ||||||
|  |                 try { | ||||||
|  |                     array_metadata.Add (type, data); | ||||||
|  |                 } catch (ArgumentException) { | ||||||
|  |                     return; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static void AddObjectMetadata (Type type) | ||||||
|  |         { | ||||||
|  |             if (object_metadata.ContainsKey (type)) | ||||||
|  |                 return; | ||||||
|  | 
 | ||||||
|  |             ObjectMetadata data = new ObjectMetadata (); | ||||||
|  | 
 | ||||||
|  |             if (type.GetInterface ("System.Collections.IDictionary") != null) | ||||||
|  |                 data.IsDictionary = true; | ||||||
|  | 
 | ||||||
|  |             data.Properties = new Dictionary<string, PropertyMetadata> (); | ||||||
|  | 
 | ||||||
|  |             foreach (PropertyInfo p_info in type.GetProperties ()) { | ||||||
|  |                 if (p_info.Name == "Item") { | ||||||
|  |                     ParameterInfo[] parameters = p_info.GetIndexParameters (); | ||||||
|  | 
 | ||||||
|  |                     if (parameters.Length != 1) | ||||||
|  |                         continue; | ||||||
|  | 
 | ||||||
|  |                     if (parameters[0].ParameterType == typeof (string)) | ||||||
|  |                         data.ElementType = p_info.PropertyType; | ||||||
|  | 
 | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 PropertyMetadata p_data = new PropertyMetadata (); | ||||||
|  |                 p_data.Info = p_info; | ||||||
|  |                 p_data.Type = p_info.PropertyType; | ||||||
|  | 
 | ||||||
|  |                 data.Properties.Add (p_info.Name, p_data); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             foreach (FieldInfo f_info in type.GetFields ()) { | ||||||
|  |                 PropertyMetadata p_data = new PropertyMetadata (); | ||||||
|  |                 p_data.Info = f_info; | ||||||
|  |                 p_data.IsField = true; | ||||||
|  |                 p_data.Type = f_info.FieldType; | ||||||
|  | 
 | ||||||
|  |                 data.Properties.Add (f_info.Name, p_data); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             lock (object_metadata_lock) { | ||||||
|  |                 try { | ||||||
|  |                     object_metadata.Add (type, data); | ||||||
|  |                 } catch (ArgumentException) { | ||||||
|  |                     return; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static void AddTypeProperties (Type type) | ||||||
|  |         { | ||||||
|  |             if (type_properties.ContainsKey (type)) | ||||||
|  |                 return; | ||||||
|  | 
 | ||||||
|  |             IList<PropertyMetadata> props = new List<PropertyMetadata> (); | ||||||
|  | 
 | ||||||
|  |             foreach (PropertyInfo p_info in type.GetProperties ()) { | ||||||
|  |                 if (p_info.Name == "Item") | ||||||
|  |                     continue; | ||||||
|  | 
 | ||||||
|  |                 PropertyMetadata p_data = new PropertyMetadata (); | ||||||
|  |                 p_data.Info = p_info; | ||||||
|  |                 p_data.IsField = false; | ||||||
|  |                 props.Add (p_data); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             foreach (FieldInfo f_info in type.GetFields ()) { | ||||||
|  |                 PropertyMetadata p_data = new PropertyMetadata (); | ||||||
|  |                 p_data.Info = f_info; | ||||||
|  |                 p_data.IsField = true; | ||||||
|  | 
 | ||||||
|  |                 props.Add (p_data); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             lock (type_properties_lock) { | ||||||
|  |                 try { | ||||||
|  |                     type_properties.Add (type, props); | ||||||
|  |                 } catch (ArgumentException) { | ||||||
|  |                     return; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static MethodInfo GetConvOp (Type t1, Type t2) | ||||||
|  |         { | ||||||
|  |             lock (conv_ops_lock) { | ||||||
|  |                 if (! conv_ops.ContainsKey (t1)) | ||||||
|  |                     conv_ops.Add (t1, new Dictionary<Type, MethodInfo> ()); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (conv_ops[t1].ContainsKey (t2)) | ||||||
|  |                 return conv_ops[t1][t2]; | ||||||
|  | 
 | ||||||
|  |             MethodInfo op = t1.GetMethod ( | ||||||
|  |                 "op_Implicit", new Type[] { t2 }); | ||||||
|  | 
 | ||||||
|  |             lock (conv_ops_lock) { | ||||||
|  |                 try { | ||||||
|  |                     conv_ops[t1].Add (t2, op); | ||||||
|  |                 } catch (ArgumentException) { | ||||||
|  |                     return conv_ops[t1][t2]; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return op; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static object ReadValue (Type inst_type, JsonReader reader) | ||||||
|  |         { | ||||||
|  |             reader.Read (); | ||||||
|  | 
 | ||||||
|  |             if (reader.Token == JsonToken.ArrayEnd) | ||||||
|  |                 return null; | ||||||
|  | 
 | ||||||
|  |             Type underlying_type = Nullable.GetUnderlyingType(inst_type); | ||||||
|  |             Type value_type = underlying_type ?? inst_type; | ||||||
|  | 
 | ||||||
|  |             if (reader.Token == JsonToken.Null) { | ||||||
|  |                 #if NETSTANDARD1_5 | ||||||
|  |                 if (inst_type.IsClass() || underlying_type != null) { | ||||||
|  |                     return null; | ||||||
|  |                 } | ||||||
|  |                 #else | ||||||
|  |                 if (inst_type.IsClass || underlying_type != null) { | ||||||
|  |                     return null; | ||||||
|  |                 } | ||||||
|  |                 #endif | ||||||
|  | 
 | ||||||
|  |                 throw new JsonException (String.Format ( | ||||||
|  |                             "Can't assign null to an instance of type {0}", | ||||||
|  |                             inst_type)); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (reader.Token == JsonToken.Double || | ||||||
|  |                 reader.Token == JsonToken.Int || | ||||||
|  |                 reader.Token == JsonToken.Long || | ||||||
|  |                 reader.Token == JsonToken.String || | ||||||
|  |                 reader.Token == JsonToken.Boolean) { | ||||||
|  | 
 | ||||||
|  |                 Type json_type = reader.Value.GetType (); | ||||||
|  | 
 | ||||||
|  |                 if (value_type.IsAssignableFrom (json_type)) | ||||||
|  |                     return reader.Value; | ||||||
|  | 
 | ||||||
|  |                 // If there's a custom importer that fits, use it | ||||||
|  |                 lock (custom_importers_table_lock) { | ||||||
|  |                     if (custom_importers_table.TryGetValue(json_type, out IDictionary<Type, ImporterFunc> customImporterTablesValue) && | ||||||
|  |                         customImporterTablesValue.TryGetValue(value_type, out ImporterFunc customImporter)) { | ||||||
|  | 
 | ||||||
|  |                         return customImporter(reader.Value); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 // Maybe there's a base importer that works | ||||||
|  |                 if (base_importers_table.TryGetValue(json_type, out IDictionary<Type, ImporterFunc> baseImporterTablesValue) && | ||||||
|  |                     baseImporterTablesValue.TryGetValue(value_type, out ImporterFunc baseImporter)) { | ||||||
|  | 
 | ||||||
|  |                     return baseImporter(reader.Value); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 // Maybe it's an enum | ||||||
|  |                 #if NETSTANDARD1_5 | ||||||
|  |                 if (value_type.IsEnum()) | ||||||
|  |                     return Enum.ToObject (value_type, reader.Value); | ||||||
|  |                 #else | ||||||
|  |                 if (value_type.IsEnum) | ||||||
|  |                     return Enum.ToObject (value_type, reader.Value); | ||||||
|  |                 #endif | ||||||
|  |                 // Try using an implicit conversion operator | ||||||
|  |                 MethodInfo conv_op = GetConvOp (value_type, json_type); | ||||||
|  | 
 | ||||||
|  |                 if (conv_op != null) | ||||||
|  |                     return conv_op.Invoke (null, | ||||||
|  |                                            new object[] { reader.Value }); | ||||||
|  | 
 | ||||||
|  |                 // No luck | ||||||
|  |                 throw new JsonException (String.Format ( | ||||||
|  |                         "Can't assign value '{0}' (type {1}) to type {2}", | ||||||
|  |                         reader.Value, json_type, inst_type)); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             object instance = null; | ||||||
|  | 
 | ||||||
|  |             if (reader.Token == JsonToken.ArrayStart) { | ||||||
|  | 
 | ||||||
|  |                 AddArrayMetadata (inst_type); | ||||||
|  |                 ArrayMetadata t_data = array_metadata[inst_type]; | ||||||
|  | 
 | ||||||
|  |                 if (! t_data.IsArray && ! t_data.IsList) | ||||||
|  |                     throw new JsonException (String.Format ( | ||||||
|  |                             "Type {0} can't act as an array", | ||||||
|  |                             inst_type)); | ||||||
|  | 
 | ||||||
|  |                 IList list; | ||||||
|  |                 Type elem_type; | ||||||
|  | 
 | ||||||
|  |                 if (! t_data.IsArray) { | ||||||
|  |                     list = (IList) Activator.CreateInstance (inst_type); | ||||||
|  |                     elem_type = t_data.ElementType; | ||||||
|  |                 } else { | ||||||
|  |                     list = new ArrayList (); | ||||||
|  |                     elem_type = inst_type.GetElementType (); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 list.Clear(); | ||||||
|  | 
 | ||||||
|  |                 while (true) { | ||||||
|  |                     object item = ReadValue (elem_type, reader); | ||||||
|  |                     if (item == null && reader.Token == JsonToken.ArrayEnd) | ||||||
|  |                         break; | ||||||
|  | 
 | ||||||
|  |                     list.Add (item); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if (t_data.IsArray) { | ||||||
|  |                     int n = list.Count; | ||||||
|  |                     instance = Array.CreateInstance (elem_type, n); | ||||||
|  | 
 | ||||||
|  |                     for (int i = 0; i < n; i++) | ||||||
|  |                         ((Array) instance).SetValue (list[i], i); | ||||||
|  |                 } else | ||||||
|  |                     instance = list; | ||||||
|  | 
 | ||||||
|  |             } else if (reader.Token == JsonToken.ObjectStart) { | ||||||
|  |                 AddObjectMetadata (value_type); | ||||||
|  |                 ObjectMetadata t_data = object_metadata[value_type]; | ||||||
|  | 
 | ||||||
|  |                 instance = Activator.CreateInstance (value_type); | ||||||
|  | 
 | ||||||
|  |                 while (true) { | ||||||
|  |                     reader.Read (); | ||||||
|  | 
 | ||||||
|  |                     if (reader.Token == JsonToken.ObjectEnd) | ||||||
|  |                         break; | ||||||
|  | 
 | ||||||
|  |                     string property = (string) reader.Value; | ||||||
|  | 
 | ||||||
|  |                     if (t_data.Properties.ContainsKey (property)) { | ||||||
|  |                         PropertyMetadata prop_data = | ||||||
|  |                             t_data.Properties[property]; | ||||||
|  | 
 | ||||||
|  |                         if (prop_data.IsField) { | ||||||
|  |                             ((FieldInfo) prop_data.Info).SetValue ( | ||||||
|  |                                 instance, ReadValue (prop_data.Type, reader)); | ||||||
|  |                         } else { | ||||||
|  |                             PropertyInfo p_info = | ||||||
|  |                                 (PropertyInfo) prop_data.Info; | ||||||
|  | 
 | ||||||
|  |                             if (p_info.CanWrite) | ||||||
|  |                                 p_info.SetValue ( | ||||||
|  |                                     instance, | ||||||
|  |                                     ReadValue (prop_data.Type, reader), | ||||||
|  |                                     null); | ||||||
|  |                             else | ||||||
|  |                                 ReadValue (prop_data.Type, reader); | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|  |                     } else { | ||||||
|  |                         if (! t_data.IsDictionary) { | ||||||
|  | 
 | ||||||
|  |                             if (! reader.SkipNonMembers) { | ||||||
|  |                                 throw new JsonException (String.Format ( | ||||||
|  |                                         "The type {0} doesn't have the " + | ||||||
|  |                                         "property '{1}'", | ||||||
|  |                                         inst_type, property)); | ||||||
|  |                             } else { | ||||||
|  |                                 ReadSkip (reader); | ||||||
|  |                                 continue; | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|  |                         ((IDictionary) instance).Add ( | ||||||
|  |                             property, ReadValue ( | ||||||
|  |                                 t_data.ElementType, reader)); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return instance; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static IJsonWrapper ReadValue (WrapperFactory factory, | ||||||
|  |                                                JsonReader reader) | ||||||
|  |         { | ||||||
|  |             reader.Read (); | ||||||
|  | 
 | ||||||
|  |             if (reader.Token == JsonToken.ArrayEnd || | ||||||
|  |                 reader.Token == JsonToken.Null) | ||||||
|  |                 return null; | ||||||
|  | 
 | ||||||
|  |             IJsonWrapper instance = factory (); | ||||||
|  | 
 | ||||||
|  |             if (reader.Token == JsonToken.String) { | ||||||
|  |                 instance.SetString ((string) reader.Value); | ||||||
|  |                 return instance; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (reader.Token == JsonToken.Double) { | ||||||
|  |                 instance.SetDouble ((double) reader.Value); | ||||||
|  |                 return instance; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (reader.Token == JsonToken.Int) { | ||||||
|  |                 instance.SetInt ((int) reader.Value); | ||||||
|  |                 return instance; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (reader.Token == JsonToken.Long) { | ||||||
|  |                 instance.SetLong ((long) reader.Value); | ||||||
|  |                 return instance; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (reader.Token == JsonToken.Boolean) { | ||||||
|  |                 instance.SetBoolean ((bool) reader.Value); | ||||||
|  |                 return instance; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (reader.Token == JsonToken.ArrayStart) { | ||||||
|  |                 instance.SetJsonType (JsonType.Array); | ||||||
|  | 
 | ||||||
|  |                 while (true) { | ||||||
|  |                     IJsonWrapper item = ReadValue (factory, reader); | ||||||
|  |                     if (item == null && reader.Token == JsonToken.ArrayEnd) | ||||||
|  |                         break; | ||||||
|  | 
 | ||||||
|  |                     ((IList) instance).Add (item); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else if (reader.Token == JsonToken.ObjectStart) { | ||||||
|  |                 instance.SetJsonType (JsonType.Object); | ||||||
|  | 
 | ||||||
|  |                 while (true) { | ||||||
|  |                     reader.Read (); | ||||||
|  | 
 | ||||||
|  |                     if (reader.Token == JsonToken.ObjectEnd) | ||||||
|  |                         break; | ||||||
|  | 
 | ||||||
|  |                     string property = (string) reader.Value; | ||||||
|  | 
 | ||||||
|  |                     ((IDictionary) instance)[property] = ReadValue ( | ||||||
|  |                         factory, reader); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return instance; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static void ReadSkip (JsonReader reader) | ||||||
|  |         { | ||||||
|  |             ToWrapper ( | ||||||
|  |                 delegate { return new JsonMockWrapper (); }, reader); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static void RegisterBaseExporters () | ||||||
|  |         { | ||||||
|  |             // This method is only called from the static initializer, | ||||||
|  |             // so there is no need to explicitly lock any static members here | ||||||
|  |             base_exporters_table[typeof (byte)] = | ||||||
|  |                 delegate (object obj, JsonWriter writer) { | ||||||
|  |                     writer.Write (Convert.ToInt32 ((byte) obj)); | ||||||
|  |                 }; | ||||||
|  | 
 | ||||||
|  |             base_exporters_table[typeof (char)] = | ||||||
|  |                 delegate (object obj, JsonWriter writer) { | ||||||
|  |                     writer.Write (Convert.ToString ((char) obj)); | ||||||
|  |                 }; | ||||||
|  | 
 | ||||||
|  |             base_exporters_table[typeof (DateTime)] = | ||||||
|  |                 delegate (object obj, JsonWriter writer) { | ||||||
|  |                     writer.Write (Convert.ToString ((DateTime) obj, | ||||||
|  |                                                     datetime_format)); | ||||||
|  |                 }; | ||||||
|  | 
 | ||||||
|  |             base_exporters_table[typeof (decimal)] = | ||||||
|  |                 delegate (object obj, JsonWriter writer) { | ||||||
|  |                     writer.Write ((decimal) obj); | ||||||
|  |                 }; | ||||||
|  | 
 | ||||||
|  |             base_exporters_table[typeof (sbyte)] = | ||||||
|  |                 delegate (object obj, JsonWriter writer) { | ||||||
|  |                     writer.Write (Convert.ToInt32 ((sbyte) obj)); | ||||||
|  |                 }; | ||||||
|  | 
 | ||||||
|  |             base_exporters_table[typeof (short)] = | ||||||
|  |                 delegate (object obj, JsonWriter writer) { | ||||||
|  |                     writer.Write (Convert.ToInt32 ((short) obj)); | ||||||
|  |                 }; | ||||||
|  | 
 | ||||||
|  |             base_exporters_table[typeof (ushort)] = | ||||||
|  |                 delegate (object obj, JsonWriter writer) { | ||||||
|  |                     writer.Write (Convert.ToInt32 ((ushort) obj)); | ||||||
|  |                 }; | ||||||
|  | 
 | ||||||
|  |             base_exporters_table[typeof (uint)] = | ||||||
|  |                 delegate (object obj, JsonWriter writer) { | ||||||
|  |                     writer.Write (Convert.ToUInt64 ((uint) obj)); | ||||||
|  |                 }; | ||||||
|  | 
 | ||||||
|  |             base_exporters_table[typeof (ulong)] = | ||||||
|  |                 delegate (object obj, JsonWriter writer) { | ||||||
|  |                     writer.Write ((ulong) obj); | ||||||
|  |                 }; | ||||||
|  | 
 | ||||||
|  |             base_exporters_table[typeof(DateTimeOffset)] = | ||||||
|  |                 delegate (object obj, JsonWriter writer) { | ||||||
|  |                     writer.Write(((DateTimeOffset)obj).ToString("yyyy-MM-ddTHH:mm:ss.fffffffzzz", datetime_format)); | ||||||
|  |                 }; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static void RegisterBaseImporters () | ||||||
|  |         { | ||||||
|  |             // This method is only called from the static initializer, | ||||||
|  |             // so there is no need to explicitly lock any static members here | ||||||
|  |             ImporterFunc importer; | ||||||
|  | 
 | ||||||
|  |             importer = delegate (object input) { | ||||||
|  |                 return Convert.ToByte ((int) input); | ||||||
|  |             }; | ||||||
|  |             RegisterImporter (base_importers_table, typeof (int), | ||||||
|  |                               typeof (byte), importer); | ||||||
|  | 
 | ||||||
|  |             importer = delegate (object input) { | ||||||
|  |                 return Convert.ToUInt64 ((int) input); | ||||||
|  |             }; | ||||||
|  |             RegisterImporter (base_importers_table, typeof (int), | ||||||
|  |                               typeof (ulong), importer); | ||||||
|  | 
 | ||||||
|  |             importer = delegate (object input) { | ||||||
|  |                 return Convert.ToInt64((int)input); | ||||||
|  |             }; | ||||||
|  |             RegisterImporter(base_importers_table, typeof(int), | ||||||
|  |                               typeof(long), importer); | ||||||
|  | 
 | ||||||
|  |             importer = delegate (object input) { | ||||||
|  |                 return Convert.ToSByte ((int) input); | ||||||
|  |             }; | ||||||
|  |             RegisterImporter (base_importers_table, typeof (int), | ||||||
|  |                               typeof (sbyte), importer); | ||||||
|  | 
 | ||||||
|  |             importer = delegate (object input) { | ||||||
|  |                 return Convert.ToInt16 ((int) input); | ||||||
|  |             }; | ||||||
|  |             RegisterImporter (base_importers_table, typeof (int), | ||||||
|  |                               typeof (short), importer); | ||||||
|  | 
 | ||||||
|  |             importer = delegate (object input) { | ||||||
|  |                 return Convert.ToUInt16 ((int) input); | ||||||
|  |             }; | ||||||
|  |             RegisterImporter (base_importers_table, typeof (int), | ||||||
|  |                               typeof (ushort), importer); | ||||||
|  | 
 | ||||||
|  |             importer = delegate (object input) { | ||||||
|  |                 return Convert.ToUInt32 ((int) input); | ||||||
|  |             }; | ||||||
|  |             RegisterImporter (base_importers_table, typeof (int), | ||||||
|  |                               typeof (uint), importer); | ||||||
|  | 
 | ||||||
|  |             importer = delegate (object input) { | ||||||
|  |                 return Convert.ToSingle ((int) input); | ||||||
|  |             }; | ||||||
|  |             RegisterImporter (base_importers_table, typeof (int), | ||||||
|  |                               typeof (float), importer); | ||||||
|  | 
 | ||||||
|  |             importer = delegate (object input) { | ||||||
|  |                 return Convert.ToDouble ((int) input); | ||||||
|  |             }; | ||||||
|  |             RegisterImporter (base_importers_table, typeof (int), | ||||||
|  |                               typeof (double), importer); | ||||||
|  | 
 | ||||||
|  |             importer = delegate (object input) { | ||||||
|  |                 return Convert.ToDecimal ((double) input); | ||||||
|  |             }; | ||||||
|  |             RegisterImporter (base_importers_table, typeof (double), | ||||||
|  |                               typeof (decimal), importer); | ||||||
|  | 
 | ||||||
|  |             importer = delegate (object input) { | ||||||
|  |                 return Convert.ToSingle((double)input); | ||||||
|  |             }; | ||||||
|  |             RegisterImporter(base_importers_table, typeof(double), | ||||||
|  |                 typeof(float), importer); | ||||||
|  | 
 | ||||||
|  |             importer = delegate (object input) { | ||||||
|  |                 return Convert.ToUInt32 ((long) input); | ||||||
|  |             }; | ||||||
|  |             RegisterImporter (base_importers_table, typeof (long), | ||||||
|  |                               typeof (uint), importer); | ||||||
|  | 
 | ||||||
|  |             importer = delegate (object input) { | ||||||
|  |                 return Convert.ToChar ((string) input); | ||||||
|  |             }; | ||||||
|  |             RegisterImporter (base_importers_table, typeof (string), | ||||||
|  |                               typeof (char), importer); | ||||||
|  | 
 | ||||||
|  |             importer = delegate (object input) { | ||||||
|  |                 return Convert.ToDateTime ((string) input, datetime_format); | ||||||
|  |             }; | ||||||
|  |             RegisterImporter (base_importers_table, typeof (string), | ||||||
|  |                               typeof (DateTime), importer); | ||||||
|  | 
 | ||||||
|  |             importer = delegate (object input) { | ||||||
|  |                 return DateTimeOffset.Parse((string)input, datetime_format); | ||||||
|  |             }; | ||||||
|  |             RegisterImporter(base_importers_table, typeof(string), | ||||||
|  |                 typeof(DateTimeOffset), importer); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static void RegisterImporter ( | ||||||
|  |             IDictionary<Type, IDictionary<Type, ImporterFunc>> table, | ||||||
|  |             Type json_type, Type value_type, ImporterFunc importer) | ||||||
|  |         { | ||||||
|  |             if (! table.ContainsKey (json_type)) | ||||||
|  |                 table.Add (json_type, new Dictionary<Type, ImporterFunc> ()); | ||||||
|  | 
 | ||||||
|  |             table[json_type][value_type] = importer; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static void WriteValue (object obj, JsonWriter writer, | ||||||
|  |                                         bool writer_is_private, | ||||||
|  |                                         int depth) | ||||||
|  |         { | ||||||
|  |             if (depth > max_nesting_depth) | ||||||
|  |                 throw new JsonException ( | ||||||
|  |                     String.Format ("Max allowed object depth reached while " + | ||||||
|  |                                    "trying to export from type {0}", | ||||||
|  |                                    obj.GetType ())); | ||||||
|  | 
 | ||||||
|  |             if (obj == null) { | ||||||
|  |                 writer.Write (null); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (obj is IJsonWrapper) { | ||||||
|  |                 if (writer_is_private) | ||||||
|  |                     writer.TextWriter.Write (((IJsonWrapper) obj).ToJson ()); | ||||||
|  |                 else | ||||||
|  |                     ((IJsonWrapper) obj).ToJson (writer); | ||||||
|  | 
 | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (obj is String) { | ||||||
|  |                 writer.Write ((string) obj); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (obj is Double) { | ||||||
|  |                 writer.Write ((double) obj); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (obj is Single) | ||||||
|  |             { | ||||||
|  |                 writer.Write((float)obj); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (obj is Int32) { | ||||||
|  |                 writer.Write ((int) obj); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (obj is Boolean) { | ||||||
|  |                 writer.Write ((bool) obj); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (obj is Int64) { | ||||||
|  |                 writer.Write ((long) obj); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (obj is Array) { | ||||||
|  |                 writer.WriteArrayStart (); | ||||||
|  | 
 | ||||||
|  |                 foreach (object elem in (Array) obj) | ||||||
|  |                     WriteValue (elem, writer, writer_is_private, depth + 1); | ||||||
|  | 
 | ||||||
|  |                 writer.WriteArrayEnd (); | ||||||
|  | 
 | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (obj is IList) { | ||||||
|  |                 writer.WriteArrayStart (); | ||||||
|  |                 foreach (object elem in (IList) obj) | ||||||
|  |                     WriteValue (elem, writer, writer_is_private, depth + 1); | ||||||
|  |                 writer.WriteArrayEnd (); | ||||||
|  | 
 | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (obj is IDictionary dictionary) { | ||||||
|  |                 writer.WriteObjectStart (); | ||||||
|  |                 foreach (DictionaryEntry entry in dictionary) { | ||||||
|  |                     var propertyName = entry.Key is string key ? | ||||||
|  |                         key | ||||||
|  |                         : Convert.ToString(entry.Key, CultureInfo.InvariantCulture); | ||||||
|  |                     writer.WritePropertyName (propertyName); | ||||||
|  |                     WriteValue (entry.Value, writer, writer_is_private, | ||||||
|  |                                 depth + 1); | ||||||
|  |                 } | ||||||
|  |                 writer.WriteObjectEnd (); | ||||||
|  | 
 | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             Type obj_type = obj.GetType (); | ||||||
|  | 
 | ||||||
|  |             // See if there's a custom exporter for the object | ||||||
|  |             lock (custom_exporters_table_lock) { | ||||||
|  |                 if (custom_exporters_table.TryGetValue(obj_type, out ExporterFunc customExporter)) { | ||||||
|  |                     customExporter(obj, writer); | ||||||
|  | 
 | ||||||
|  |                     return; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // If not, maybe there's a base exporter | ||||||
|  |             if (base_exporters_table.TryGetValue(obj_type, out ExporterFunc baseExporter)) { | ||||||
|  |                 baseExporter(obj, writer); | ||||||
|  | 
 | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // Last option, let's see if it's an enum | ||||||
|  |             if (obj is Enum) { | ||||||
|  |                 Type e_type = Enum.GetUnderlyingType (obj_type); | ||||||
|  | 
 | ||||||
|  |                 if (e_type == typeof (long)) | ||||||
|  |                     writer.Write ((long) obj); | ||||||
|  |                 else if (e_type == typeof (uint)) | ||||||
|  |                     writer.Write ((uint) obj); | ||||||
|  |                 else if (e_type == typeof (ulong)) | ||||||
|  |                     writer.Write ((ulong) obj); | ||||||
|  |                 else if (e_type == typeof(ushort)) | ||||||
|  |                     writer.Write ((ushort)obj); | ||||||
|  |                 else if (e_type == typeof(short)) | ||||||
|  |                     writer.Write ((short)obj); | ||||||
|  |                 else if (e_type == typeof(byte)) | ||||||
|  |                     writer.Write ((byte)obj); | ||||||
|  |                 else if (e_type == typeof(sbyte)) | ||||||
|  |                     writer.Write ((sbyte)obj); | ||||||
|  |                 else | ||||||
|  |                     writer.Write ((int) obj); | ||||||
|  | 
 | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // Okay, so it looks like the input should be exported as an | ||||||
|  |             // object | ||||||
|  |             AddTypeProperties (obj_type); | ||||||
|  |             IList<PropertyMetadata> props = type_properties[obj_type]; | ||||||
|  | 
 | ||||||
|  |             writer.WriteObjectStart (); | ||||||
|  |             foreach (PropertyMetadata p_data in props) { | ||||||
|  |                 if (p_data.IsField) { | ||||||
|  |                     writer.WritePropertyName (p_data.Info.Name); | ||||||
|  |                     WriteValue (((FieldInfo) p_data.Info).GetValue (obj), | ||||||
|  |                                 writer, writer_is_private, depth + 1); | ||||||
|  |                 } | ||||||
|  |                 else { | ||||||
|  |                     PropertyInfo p_info = (PropertyInfo) p_data.Info; | ||||||
|  | 
 | ||||||
|  |                     if (p_info.CanRead) { | ||||||
|  |                         writer.WritePropertyName (p_data.Info.Name); | ||||||
|  |                         WriteValue (p_info.GetValue (obj, null), | ||||||
|  |                                     writer, writer_is_private, depth + 1); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             writer.WriteObjectEnd (); | ||||||
|  |         } | ||||||
|  |         #endregion | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         public static string ToJson (object obj) | ||||||
|  |         { | ||||||
|  |             lock (static_writer_lock) { | ||||||
|  |                 static_writer.Reset (); | ||||||
|  | 
 | ||||||
|  |                 WriteValue (obj, static_writer, true, 0); | ||||||
|  | 
 | ||||||
|  |                 return static_writer.ToString (); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static void ToJson (object obj, JsonWriter writer) | ||||||
|  |         { | ||||||
|  |             WriteValue (obj, writer, false, 0); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static JsonData ToObject (JsonReader reader) | ||||||
|  |         { | ||||||
|  |             return (JsonData) ToWrapper ( | ||||||
|  |                 delegate { return new JsonData (); }, reader); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static JsonData ToObject (TextReader reader) | ||||||
|  |         { | ||||||
|  |             JsonReader json_reader = new JsonReader (reader); | ||||||
|  | 
 | ||||||
|  |             return (JsonData) ToWrapper ( | ||||||
|  |                 delegate { return new JsonData (); }, json_reader); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static JsonData ToObject (string json) | ||||||
|  |         { | ||||||
|  |             return (JsonData) ToWrapper ( | ||||||
|  |                 delegate { return new JsonData (); }, json); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static T ToObject<T> (JsonReader reader) | ||||||
|  |         { | ||||||
|  |             return (T) ReadValue (typeof (T), reader); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static T ToObject<T> (TextReader reader) | ||||||
|  |         { | ||||||
|  |             JsonReader json_reader = new JsonReader (reader); | ||||||
|  | 
 | ||||||
|  |             return (T) ReadValue (typeof (T), json_reader); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static T ToObject<T> (string json) | ||||||
|  |         { | ||||||
|  |             JsonReader reader = new JsonReader (json); | ||||||
|  | 
 | ||||||
|  |             return (T) ReadValue (typeof (T), reader); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static object ToObject(string json, Type ConvertType ) | ||||||
|  |         { | ||||||
|  |             JsonReader reader = new JsonReader(json); | ||||||
|  | 
 | ||||||
|  |             return ReadValue(ConvertType, reader); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static IJsonWrapper ToWrapper (WrapperFactory factory, | ||||||
|  |                                               JsonReader reader) | ||||||
|  |         { | ||||||
|  |             return ReadValue (factory, reader); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static IJsonWrapper ToWrapper (WrapperFactory factory, | ||||||
|  |                                               string json) | ||||||
|  |         { | ||||||
|  |             JsonReader reader = new JsonReader (json); | ||||||
|  | 
 | ||||||
|  |             return ReadValue (factory, reader); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static void RegisterExporter<T> (ExporterFunc<T> exporter) | ||||||
|  |         { | ||||||
|  |             ExporterFunc exporter_wrapper = | ||||||
|  |                 delegate (object obj, JsonWriter writer) { | ||||||
|  |                     exporter ((T) obj, writer); | ||||||
|  |                 }; | ||||||
|  | 
 | ||||||
|  |             lock (custom_exporters_table_lock) {  | ||||||
|  |                 custom_exporters_table[typeof (T)] = exporter_wrapper; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static void RegisterImporter<TJson, TValue> ( | ||||||
|  |             ImporterFunc<TJson, TValue> importer) | ||||||
|  |         { | ||||||
|  |             ImporterFunc importer_wrapper = | ||||||
|  |                 delegate (object input) { | ||||||
|  |                     return importer ((TJson) input); | ||||||
|  |                 }; | ||||||
|  | 
 | ||||||
|  |             lock (custom_importers_table_lock) { | ||||||
|  |                 RegisterImporter (custom_importers_table, typeof (TJson), | ||||||
|  |                                  typeof (TValue), importer_wrapper); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static void UnregisterExporters () | ||||||
|  |         { | ||||||
|  |             lock (custom_exporters_table_lock) { | ||||||
|  |                 custom_exporters_table.Clear(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static void UnregisterImporters () | ||||||
|  |         { | ||||||
|  |             lock (custom_importers_table_lock) { | ||||||
|  |                 custom_importers_table.Clear(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,11 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 3e14ed6947460448ea1c1512947ced5d | ||||||
|  | MonoImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   serializedVersion: 2 | ||||||
|  |   defaultReferences: [] | ||||||
|  |   executionOrder: 0 | ||||||
|  |   icon: {instanceID: 0} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,105 @@ | ||||||
|  | #region Header | ||||||
|  | /** | ||||||
|  |  * JsonMockWrapper.cs | ||||||
|  |  *   Mock object implementing IJsonWrapper, to facilitate actions like | ||||||
|  |  *   skipping data more efficiently. | ||||||
|  |  * | ||||||
|  |  * The authors disclaim copyright to this source code. For more details, see | ||||||
|  |  * the COPYING file included with this distribution. | ||||||
|  |  **/ | ||||||
|  | #endregion | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | using System; | ||||||
|  | using System.Collections; | ||||||
|  | using System.Collections.Specialized; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | namespace Guru.LitJson | ||||||
|  | { | ||||||
|  |     public class JsonMockWrapper : IJsonWrapper | ||||||
|  |     { | ||||||
|  |         public bool IsArray   { get { return false; } } | ||||||
|  |         public bool IsBoolean { get { return false; } } | ||||||
|  |         public bool IsDouble  { get { return false; } } | ||||||
|  |         public bool IsInt     { get { return false; } } | ||||||
|  |         public bool IsLong    { get { return false; } } | ||||||
|  |         public bool IsObject  { get { return false; } } | ||||||
|  |         public bool IsString  { get { return false; } } | ||||||
|  | 
 | ||||||
|  |         public bool     GetBoolean ()  { return false; } | ||||||
|  |         public double   GetDouble ()   { return 0.0; } | ||||||
|  |         public int      GetInt ()      { return 0; } | ||||||
|  |         public JsonType GetJsonType () { return JsonType.None; } | ||||||
|  |         public long     GetLong ()     { return 0L; } | ||||||
|  |         public string   GetString ()   { return ""; } | ||||||
|  | 
 | ||||||
|  |         public void SetBoolean  (bool val)      {} | ||||||
|  |         public void SetDouble   (double val)    {} | ||||||
|  |         public void SetInt      (int val)       {} | ||||||
|  |         public void SetJsonType (JsonType type) {} | ||||||
|  |         public void SetLong     (long val)      {} | ||||||
|  |         public void SetString   (string val)    {} | ||||||
|  | 
 | ||||||
|  |         public string ToJson ()                  { return ""; } | ||||||
|  |         public void   ToJson (JsonWriter writer) {} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         bool IList.IsFixedSize { get { return true; } } | ||||||
|  |         bool IList.IsReadOnly  { get { return true; } } | ||||||
|  | 
 | ||||||
|  |         object IList.this[int index] { | ||||||
|  |             get { return null; } | ||||||
|  |             set {} | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         int  IList.Add (object value)       { return 0; } | ||||||
|  |         void IList.Clear ()                 {} | ||||||
|  |         bool IList.Contains (object value)  { return false; } | ||||||
|  |         int  IList.IndexOf (object value)   { return -1; } | ||||||
|  |         void IList.Insert (int i, object v) {} | ||||||
|  |         void IList.Remove (object value)    {} | ||||||
|  |         void IList.RemoveAt (int index)     {} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         int    ICollection.Count          { get { return 0; } } | ||||||
|  |         bool   ICollection.IsSynchronized { get { return false; } } | ||||||
|  |         object ICollection.SyncRoot       { get { return null; } } | ||||||
|  | 
 | ||||||
|  |         void ICollection.CopyTo (Array array, int index) {} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         IEnumerator IEnumerable.GetEnumerator () { return null; } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         bool IDictionary.IsFixedSize { get { return true; } } | ||||||
|  |         bool IDictionary.IsReadOnly  { get { return true; } } | ||||||
|  | 
 | ||||||
|  |         ICollection IDictionary.Keys   { get { return null; } } | ||||||
|  |         ICollection IDictionary.Values { get { return null; } } | ||||||
|  | 
 | ||||||
|  |         object IDictionary.this[object key] { | ||||||
|  |             get { return null; } | ||||||
|  |             set {} | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         void IDictionary.Add (object k, object v) {} | ||||||
|  |         void IDictionary.Clear ()                 {} | ||||||
|  |         bool IDictionary.Contains (object key)    { return false; } | ||||||
|  |         void IDictionary.Remove (object key)      {} | ||||||
|  | 
 | ||||||
|  |         IDictionaryEnumerator IDictionary.GetEnumerator () { return null; } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         object IOrderedDictionary.this[int idx] { | ||||||
|  |             get { return null; } | ||||||
|  |             set {} | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         IDictionaryEnumerator IOrderedDictionary.GetEnumerator () { | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |         void IOrderedDictionary.Insert   (int i, object k, object v) {} | ||||||
|  |         void IOrderedDictionary.RemoveAt (int i) {} | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,11 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: b54da0524de4e4164893c964b0b7b443 | ||||||
|  | MonoImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   serializedVersion: 2 | ||||||
|  |   defaultReferences: [] | ||||||
|  |   executionOrder: 0 | ||||||
|  |   icon: {instanceID: 0} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,478 @@ | ||||||
|  | #region Header | ||||||
|  | /** | ||||||
|  |  * JsonReader.cs | ||||||
|  |  *   Stream-like access to JSON text. | ||||||
|  |  * | ||||||
|  |  * The authors disclaim copyright to this source code. For more details, see | ||||||
|  |  * the COPYING file included with this distribution. | ||||||
|  |  **/ | ||||||
|  | #endregion | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Globalization; | ||||||
|  | using System.IO; | ||||||
|  | using System.Text; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | namespace Guru.LitJson | ||||||
|  | { | ||||||
|  |     public enum JsonToken | ||||||
|  |     { | ||||||
|  |         None, | ||||||
|  | 
 | ||||||
|  |         ObjectStart, | ||||||
|  |         PropertyName, | ||||||
|  |         ObjectEnd, | ||||||
|  | 
 | ||||||
|  |         ArrayStart, | ||||||
|  |         ArrayEnd, | ||||||
|  | 
 | ||||||
|  |         Int, | ||||||
|  |         Long, | ||||||
|  |         Double, | ||||||
|  | 
 | ||||||
|  |         String, | ||||||
|  | 
 | ||||||
|  |         Boolean, | ||||||
|  |         Null | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     public class JsonReader | ||||||
|  |     { | ||||||
|  |         #region Fields | ||||||
|  |         private static readonly IDictionary<int, IDictionary<int, int[]>> parse_table; | ||||||
|  | 
 | ||||||
|  |         private Stack<int>    automaton_stack; | ||||||
|  |         private int           current_input; | ||||||
|  |         private int           current_symbol; | ||||||
|  |         private bool          end_of_json; | ||||||
|  |         private bool          end_of_input; | ||||||
|  |         private Lexer         lexer; | ||||||
|  |         private bool          parser_in_string; | ||||||
|  |         private bool          parser_return; | ||||||
|  |         private bool          read_started; | ||||||
|  |         private TextReader    reader; | ||||||
|  |         private bool          reader_is_owned; | ||||||
|  |         private bool          skip_non_members; | ||||||
|  |         private object        token_value; | ||||||
|  |         private JsonToken     token; | ||||||
|  |         #endregion | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         #region Public Properties | ||||||
|  |         public bool AllowComments { | ||||||
|  |             get { return lexer.AllowComments; } | ||||||
|  |             set { lexer.AllowComments = value; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public bool AllowSingleQuotedStrings { | ||||||
|  |             get { return lexer.AllowSingleQuotedStrings; } | ||||||
|  |             set { lexer.AllowSingleQuotedStrings = value; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public bool SkipNonMembers { | ||||||
|  |             get { return skip_non_members; } | ||||||
|  |             set { skip_non_members = value; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public bool EndOfInput { | ||||||
|  |             get { return end_of_input; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public bool EndOfJson { | ||||||
|  |             get { return end_of_json; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public JsonToken Token { | ||||||
|  |             get { return token; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public object Value { | ||||||
|  |             get { return token_value; } | ||||||
|  |         } | ||||||
|  |         #endregion | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         #region Constructors | ||||||
|  |         static JsonReader () | ||||||
|  |         { | ||||||
|  |             parse_table = PopulateParseTable (); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public JsonReader (string json_text) : | ||||||
|  |             this (new StringReader (json_text), true) | ||||||
|  |         { | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public JsonReader (TextReader reader) : | ||||||
|  |             this (reader, false) | ||||||
|  |         { | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private JsonReader (TextReader reader, bool owned) | ||||||
|  |         { | ||||||
|  |             if (reader == null) | ||||||
|  |                 throw new ArgumentNullException ("reader"); | ||||||
|  | 
 | ||||||
|  |             parser_in_string = false; | ||||||
|  |             parser_return    = false; | ||||||
|  | 
 | ||||||
|  |             read_started = false; | ||||||
|  |             automaton_stack = new Stack<int> (); | ||||||
|  |             automaton_stack.Push ((int) ParserToken.End); | ||||||
|  |             automaton_stack.Push ((int) ParserToken.Text); | ||||||
|  | 
 | ||||||
|  |             lexer = new Lexer (reader); | ||||||
|  | 
 | ||||||
|  |             end_of_input = false; | ||||||
|  |             end_of_json  = false; | ||||||
|  | 
 | ||||||
|  |             skip_non_members = true; | ||||||
|  | 
 | ||||||
|  |             this.reader = reader; | ||||||
|  |             reader_is_owned = owned; | ||||||
|  |         } | ||||||
|  |         #endregion | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         #region Static Methods | ||||||
|  |         private static IDictionary<int, IDictionary<int, int[]>> PopulateParseTable () | ||||||
|  |         { | ||||||
|  |             // See section A.2. of the manual for details | ||||||
|  |             IDictionary<int, IDictionary<int, int[]>> parse_table = new Dictionary<int, IDictionary<int, int[]>> (); | ||||||
|  | 
 | ||||||
|  |             TableAddRow (parse_table, ParserToken.Array); | ||||||
|  |             TableAddCol (parse_table, ParserToken.Array, '[', | ||||||
|  |                             '[', | ||||||
|  |                             (int) ParserToken.ArrayPrime); | ||||||
|  | 
 | ||||||
|  |             TableAddRow (parse_table, ParserToken.ArrayPrime); | ||||||
|  |             TableAddCol (parse_table, ParserToken.ArrayPrime, '"', | ||||||
|  |                             (int) ParserToken.Value, | ||||||
|  | 
 | ||||||
|  |                             (int) ParserToken.ValueRest, | ||||||
|  |                             ']'); | ||||||
|  |             TableAddCol (parse_table, ParserToken.ArrayPrime, '[', | ||||||
|  |                             (int) ParserToken.Value, | ||||||
|  |                             (int) ParserToken.ValueRest, | ||||||
|  |                             ']'); | ||||||
|  |             TableAddCol (parse_table, ParserToken.ArrayPrime, ']', | ||||||
|  |                             ']'); | ||||||
|  |             TableAddCol (parse_table, ParserToken.ArrayPrime, '{', | ||||||
|  |                             (int) ParserToken.Value, | ||||||
|  |                             (int) ParserToken.ValueRest, | ||||||
|  |                             ']'); | ||||||
|  |             TableAddCol (parse_table, ParserToken.ArrayPrime, (int) ParserToken.Number, | ||||||
|  |                             (int) ParserToken.Value, | ||||||
|  |                             (int) ParserToken.ValueRest, | ||||||
|  |                             ']'); | ||||||
|  |             TableAddCol (parse_table, ParserToken.ArrayPrime, (int) ParserToken.True, | ||||||
|  |                             (int) ParserToken.Value, | ||||||
|  |                             (int) ParserToken.ValueRest, | ||||||
|  |                             ']'); | ||||||
|  |             TableAddCol (parse_table, ParserToken.ArrayPrime, (int) ParserToken.False, | ||||||
|  |                             (int) ParserToken.Value, | ||||||
|  |                             (int) ParserToken.ValueRest, | ||||||
|  |                             ']'); | ||||||
|  |             TableAddCol (parse_table, ParserToken.ArrayPrime, (int) ParserToken.Null, | ||||||
|  |                             (int) ParserToken.Value, | ||||||
|  |                             (int) ParserToken.ValueRest, | ||||||
|  |                             ']'); | ||||||
|  | 
 | ||||||
|  |             TableAddRow (parse_table, ParserToken.Object); | ||||||
|  |             TableAddCol (parse_table, ParserToken.Object, '{', | ||||||
|  |                             '{', | ||||||
|  |                             (int) ParserToken.ObjectPrime); | ||||||
|  | 
 | ||||||
|  |             TableAddRow (parse_table, ParserToken.ObjectPrime); | ||||||
|  |             TableAddCol (parse_table, ParserToken.ObjectPrime, '"', | ||||||
|  |                             (int) ParserToken.Pair, | ||||||
|  |                             (int) ParserToken.PairRest, | ||||||
|  |                             '}'); | ||||||
|  |             TableAddCol (parse_table, ParserToken.ObjectPrime, '}', | ||||||
|  |                             '}'); | ||||||
|  | 
 | ||||||
|  |             TableAddRow (parse_table, ParserToken.Pair); | ||||||
|  |             TableAddCol (parse_table, ParserToken.Pair, '"', | ||||||
|  |                             (int) ParserToken.String, | ||||||
|  |                             ':', | ||||||
|  |                             (int) ParserToken.Value); | ||||||
|  | 
 | ||||||
|  |             TableAddRow (parse_table, ParserToken.PairRest); | ||||||
|  |             TableAddCol (parse_table, ParserToken.PairRest, ',', | ||||||
|  |                             ',', | ||||||
|  |                             (int) ParserToken.Pair, | ||||||
|  |                             (int) ParserToken.PairRest); | ||||||
|  |             TableAddCol (parse_table, ParserToken.PairRest, '}', | ||||||
|  |                             (int) ParserToken.Epsilon); | ||||||
|  | 
 | ||||||
|  |             TableAddRow (parse_table, ParserToken.String); | ||||||
|  |             TableAddCol (parse_table, ParserToken.String, '"', | ||||||
|  |                             '"', | ||||||
|  |                             (int) ParserToken.CharSeq, | ||||||
|  |                             '"'); | ||||||
|  | 
 | ||||||
|  |             TableAddRow (parse_table, ParserToken.Text); | ||||||
|  |             TableAddCol (parse_table, ParserToken.Text, '[', | ||||||
|  |                             (int) ParserToken.Array); | ||||||
|  |             TableAddCol (parse_table, ParserToken.Text, '{', | ||||||
|  |                             (int) ParserToken.Object); | ||||||
|  | 
 | ||||||
|  |             TableAddRow (parse_table, ParserToken.Value); | ||||||
|  |             TableAddCol (parse_table, ParserToken.Value, '"', | ||||||
|  |                             (int) ParserToken.String); | ||||||
|  |             TableAddCol (parse_table, ParserToken.Value, '[', | ||||||
|  |                             (int) ParserToken.Array); | ||||||
|  |             TableAddCol (parse_table, ParserToken.Value, '{', | ||||||
|  |                             (int) ParserToken.Object); | ||||||
|  |             TableAddCol (parse_table, ParserToken.Value, (int) ParserToken.Number, | ||||||
|  |                             (int) ParserToken.Number); | ||||||
|  |             TableAddCol (parse_table, ParserToken.Value, (int) ParserToken.True, | ||||||
|  |                             (int) ParserToken.True); | ||||||
|  |             TableAddCol (parse_table, ParserToken.Value, (int) ParserToken.False, | ||||||
|  |                             (int) ParserToken.False); | ||||||
|  |             TableAddCol (parse_table, ParserToken.Value, (int) ParserToken.Null, | ||||||
|  |                             (int) ParserToken.Null); | ||||||
|  | 
 | ||||||
|  |             TableAddRow (parse_table, ParserToken.ValueRest); | ||||||
|  |             TableAddCol (parse_table, ParserToken.ValueRest, ',', | ||||||
|  |                             ',', | ||||||
|  |                             (int) ParserToken.Value, | ||||||
|  |                             (int) ParserToken.ValueRest); | ||||||
|  |             TableAddCol (parse_table, ParserToken.ValueRest, ']', | ||||||
|  |                             (int) ParserToken.Epsilon); | ||||||
|  | 
 | ||||||
|  |             return parse_table; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static void TableAddCol (IDictionary<int, IDictionary<int, int[]>> parse_table, ParserToken row, int col, | ||||||
|  |                                          params int[] symbols) | ||||||
|  |         { | ||||||
|  |             parse_table[(int) row].Add (col, symbols); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static void TableAddRow (IDictionary<int, IDictionary<int, int[]>> parse_table, ParserToken rule) | ||||||
|  |         { | ||||||
|  |             parse_table.Add ((int) rule, new Dictionary<int, int[]> ()); | ||||||
|  |         } | ||||||
|  |         #endregion | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         #region Private Methods | ||||||
|  |         private void ProcessNumber (string number) | ||||||
|  |         { | ||||||
|  |             if (number.IndexOf ('.') != -1 || | ||||||
|  |                 number.IndexOf ('e') != -1 || | ||||||
|  |                 number.IndexOf ('E') != -1) { | ||||||
|  | 
 | ||||||
|  |                 double n_double; | ||||||
|  |                 if (double.TryParse (number, NumberStyles.Any, CultureInfo.InvariantCulture, out n_double)) { | ||||||
|  |                     token = JsonToken.Double; | ||||||
|  |                     token_value = n_double; | ||||||
|  | 
 | ||||||
|  |                     return; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             int n_int32; | ||||||
|  |             if (int.TryParse (number, NumberStyles.Integer, CultureInfo.InvariantCulture, out n_int32)) { | ||||||
|  |                 token = JsonToken.Int; | ||||||
|  |                 token_value = n_int32; | ||||||
|  | 
 | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             long n_int64; | ||||||
|  |             if (long.TryParse (number, NumberStyles.Integer, CultureInfo.InvariantCulture, out n_int64)) { | ||||||
|  |                 token = JsonToken.Long; | ||||||
|  |                 token_value = n_int64; | ||||||
|  | 
 | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             ulong n_uint64; | ||||||
|  |             if (ulong.TryParse(number, NumberStyles.Integer, CultureInfo.InvariantCulture, out n_uint64)) | ||||||
|  |             { | ||||||
|  |                 token = JsonToken.Long; | ||||||
|  |                 token_value = n_uint64; | ||||||
|  | 
 | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // Shouldn't happen, but just in case, return something | ||||||
|  |             token = JsonToken.Int; | ||||||
|  |             token_value = 0; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void ProcessSymbol () | ||||||
|  |         { | ||||||
|  |             if (current_symbol == '[')  { | ||||||
|  |                 token = JsonToken.ArrayStart; | ||||||
|  |                 parser_return = true; | ||||||
|  | 
 | ||||||
|  |             } else if (current_symbol == ']')  { | ||||||
|  |                 token = JsonToken.ArrayEnd; | ||||||
|  |                 parser_return = true; | ||||||
|  | 
 | ||||||
|  |             } else if (current_symbol == '{')  { | ||||||
|  |                 token = JsonToken.ObjectStart; | ||||||
|  |                 parser_return = true; | ||||||
|  | 
 | ||||||
|  |             } else if (current_symbol == '}')  { | ||||||
|  |                 token = JsonToken.ObjectEnd; | ||||||
|  |                 parser_return = true; | ||||||
|  | 
 | ||||||
|  |             } else if (current_symbol == '"')  { | ||||||
|  |                 if (parser_in_string) { | ||||||
|  |                     parser_in_string = false; | ||||||
|  | 
 | ||||||
|  |                     parser_return = true; | ||||||
|  | 
 | ||||||
|  |                 } else { | ||||||
|  |                     if (token == JsonToken.None) | ||||||
|  |                         token = JsonToken.String; | ||||||
|  | 
 | ||||||
|  |                     parser_in_string = true; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |             } else if (current_symbol == (int) ParserToken.CharSeq) { | ||||||
|  |                 token_value = lexer.StringValue; | ||||||
|  | 
 | ||||||
|  |             } else if (current_symbol == (int) ParserToken.False)  { | ||||||
|  |                 token = JsonToken.Boolean; | ||||||
|  |                 token_value = false; | ||||||
|  |                 parser_return = true; | ||||||
|  | 
 | ||||||
|  |             } else if (current_symbol == (int) ParserToken.Null)  { | ||||||
|  |                 token = JsonToken.Null; | ||||||
|  |                 parser_return = true; | ||||||
|  | 
 | ||||||
|  |             } else if (current_symbol == (int) ParserToken.Number)  { | ||||||
|  |                 ProcessNumber (lexer.StringValue); | ||||||
|  | 
 | ||||||
|  |                 parser_return = true; | ||||||
|  | 
 | ||||||
|  |             } else if (current_symbol == (int) ParserToken.Pair)  { | ||||||
|  |                 token = JsonToken.PropertyName; | ||||||
|  | 
 | ||||||
|  |             } else if (current_symbol == (int) ParserToken.True)  { | ||||||
|  |                 token = JsonToken.Boolean; | ||||||
|  |                 token_value = true; | ||||||
|  |                 parser_return = true; | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private bool ReadToken () | ||||||
|  |         { | ||||||
|  |             if (end_of_input) | ||||||
|  |                 return false; | ||||||
|  | 
 | ||||||
|  |             lexer.NextToken (); | ||||||
|  | 
 | ||||||
|  |             if (lexer.EndOfInput) { | ||||||
|  |                 Close (); | ||||||
|  | 
 | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             current_input = lexer.Token; | ||||||
|  | 
 | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |         #endregion | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         public void Close () | ||||||
|  |         { | ||||||
|  |             if (end_of_input) | ||||||
|  |                 return; | ||||||
|  | 
 | ||||||
|  |             end_of_input = true; | ||||||
|  |             end_of_json  = true; | ||||||
|  | 
 | ||||||
|  |             if (reader_is_owned) | ||||||
|  |             { | ||||||
|  |                 using(reader){} | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             reader = null; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public bool Read () | ||||||
|  |         { | ||||||
|  |             if (end_of_input) | ||||||
|  |                 return false; | ||||||
|  | 
 | ||||||
|  |             if (end_of_json) { | ||||||
|  |                 end_of_json = false; | ||||||
|  |                 automaton_stack.Clear (); | ||||||
|  |                 automaton_stack.Push ((int) ParserToken.End); | ||||||
|  |                 automaton_stack.Push ((int) ParserToken.Text); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             parser_in_string = false; | ||||||
|  |             parser_return    = false; | ||||||
|  | 
 | ||||||
|  |             token       = JsonToken.None; | ||||||
|  |             token_value = null; | ||||||
|  | 
 | ||||||
|  |             if (! read_started) { | ||||||
|  |                 read_started = true; | ||||||
|  | 
 | ||||||
|  |                 if (! ReadToken ()) | ||||||
|  |                     return false; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |             int[] entry_symbols; | ||||||
|  | 
 | ||||||
|  |             while (true) { | ||||||
|  |                 if (parser_return) { | ||||||
|  |                     if (automaton_stack.Peek () == (int) ParserToken.End) | ||||||
|  |                         end_of_json = true; | ||||||
|  | 
 | ||||||
|  |                     return true; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 current_symbol = automaton_stack.Pop (); | ||||||
|  | 
 | ||||||
|  |                 ProcessSymbol (); | ||||||
|  | 
 | ||||||
|  |                 if (current_symbol == current_input) { | ||||||
|  |                     if (! ReadToken ()) { | ||||||
|  |                         if (automaton_stack.Peek () != (int) ParserToken.End) | ||||||
|  |                             throw new JsonException ( | ||||||
|  |                                 "Input doesn't evaluate to proper JSON text"); | ||||||
|  | 
 | ||||||
|  |                         if (parser_return) | ||||||
|  |                             return true; | ||||||
|  | 
 | ||||||
|  |                         return false; | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 try { | ||||||
|  | 
 | ||||||
|  |                     entry_symbols = | ||||||
|  |                         parse_table[current_symbol][current_input]; | ||||||
|  | 
 | ||||||
|  |                 } catch (KeyNotFoundException e) { | ||||||
|  |                     throw new JsonException ((ParserToken) current_input, e); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if (entry_symbols[0] == (int) ParserToken.Epsilon) | ||||||
|  |                     continue; | ||||||
|  | 
 | ||||||
|  |                 for (int i = entry_symbols.Length - 1; i >= 0; i--) | ||||||
|  |                     automaton_stack.Push (entry_symbols[i]); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,11 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: a486d94a05da74fb4a2e1d9d1eb53455 | ||||||
|  | MonoImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   serializedVersion: 2 | ||||||
|  |   defaultReferences: [] | ||||||
|  |   executionOrder: 0 | ||||||
|  |   icon: {instanceID: 0} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,484 @@ | ||||||
|  | #region Header | ||||||
|  | /** | ||||||
|  |  * JsonWriter.cs | ||||||
|  |  *   Stream-like facility to output JSON text. | ||||||
|  |  * | ||||||
|  |  * The authors disclaim copyright to this source code. For more details, see | ||||||
|  |  * the COPYING file included with this distribution. | ||||||
|  |  **/ | ||||||
|  | #endregion | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Globalization; | ||||||
|  | using System.IO; | ||||||
|  | using System.Text; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | namespace Guru.LitJson | ||||||
|  | { | ||||||
|  |     internal enum Condition | ||||||
|  |     { | ||||||
|  |         InArray, | ||||||
|  |         InObject, | ||||||
|  |         NotAProperty, | ||||||
|  |         Property, | ||||||
|  |         Value | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     internal class WriterContext | ||||||
|  |     { | ||||||
|  |         public int  Count; | ||||||
|  |         public bool InArray; | ||||||
|  |         public bool InObject; | ||||||
|  |         public bool ExpectingValue; | ||||||
|  |         public int  Padding; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public class JsonWriter | ||||||
|  |     { | ||||||
|  |         #region Fields | ||||||
|  |         private static readonly NumberFormatInfo number_format; | ||||||
|  | 
 | ||||||
|  |         private WriterContext        context; | ||||||
|  |         private Stack<WriterContext> ctx_stack; | ||||||
|  |         private bool                 has_reached_end; | ||||||
|  |         private char[]               hex_seq; | ||||||
|  |         private int                  indentation; | ||||||
|  |         private int                  indent_value; | ||||||
|  |         private StringBuilder        inst_string_builder; | ||||||
|  |         private bool                 pretty_print; | ||||||
|  |         private bool                 validate; | ||||||
|  |         private bool                 lower_case_properties; | ||||||
|  |         private TextWriter           writer; | ||||||
|  |         #endregion | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         #region Properties | ||||||
|  |         public int IndentValue { | ||||||
|  |             get { return indent_value; } | ||||||
|  |             set { | ||||||
|  |                 indentation = (indentation / indent_value) * value; | ||||||
|  |                 indent_value = value; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public bool PrettyPrint { | ||||||
|  |             get { return pretty_print; } | ||||||
|  |             set { pretty_print = value; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public TextWriter TextWriter { | ||||||
|  |             get { return writer; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public bool Validate { | ||||||
|  |             get { return validate; } | ||||||
|  |             set { validate = value; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public bool LowerCaseProperties { | ||||||
|  |             get { return lower_case_properties; } | ||||||
|  |             set { lower_case_properties = value; } | ||||||
|  |         } | ||||||
|  |         #endregion | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         #region Constructors | ||||||
|  |         static JsonWriter () | ||||||
|  |         { | ||||||
|  |             number_format = NumberFormatInfo.InvariantInfo; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public JsonWriter () | ||||||
|  |         { | ||||||
|  |             inst_string_builder = new StringBuilder (); | ||||||
|  |             writer = new StringWriter (inst_string_builder); | ||||||
|  | 
 | ||||||
|  |             Init (); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public JsonWriter (StringBuilder sb) : | ||||||
|  |             this (new StringWriter (sb)) | ||||||
|  |         { | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public JsonWriter (TextWriter writer) | ||||||
|  |         { | ||||||
|  |             if (writer == null) | ||||||
|  |                 throw new ArgumentNullException ("writer"); | ||||||
|  | 
 | ||||||
|  |             this.writer = writer; | ||||||
|  | 
 | ||||||
|  |             Init (); | ||||||
|  |         } | ||||||
|  |         #endregion | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         #region Private Methods | ||||||
|  |         private void DoValidation (Condition cond) | ||||||
|  |         { | ||||||
|  |             if (! context.ExpectingValue) | ||||||
|  |                 context.Count++; | ||||||
|  | 
 | ||||||
|  |             if (! validate) | ||||||
|  |                 return; | ||||||
|  | 
 | ||||||
|  |             if (has_reached_end) | ||||||
|  |                 throw new JsonException ( | ||||||
|  |                     "A complete JSON symbol has already been written"); | ||||||
|  | 
 | ||||||
|  |             switch (cond) { | ||||||
|  |             case Condition.InArray: | ||||||
|  |                 if (! context.InArray) | ||||||
|  |                     throw new JsonException ( | ||||||
|  |                         "Can't close an array here"); | ||||||
|  |                 break; | ||||||
|  | 
 | ||||||
|  |             case Condition.InObject: | ||||||
|  |                 if (! context.InObject || context.ExpectingValue) | ||||||
|  |                     throw new JsonException ( | ||||||
|  |                         "Can't close an object here"); | ||||||
|  |                 break; | ||||||
|  | 
 | ||||||
|  |             case Condition.NotAProperty: | ||||||
|  |                 if (context.InObject && ! context.ExpectingValue) | ||||||
|  |                     throw new JsonException ( | ||||||
|  |                         "Expected a property"); | ||||||
|  |                 break; | ||||||
|  | 
 | ||||||
|  |             case Condition.Property: | ||||||
|  |                 if (! context.InObject || context.ExpectingValue) | ||||||
|  |                     throw new JsonException ( | ||||||
|  |                         "Can't add a property here"); | ||||||
|  |                 break; | ||||||
|  | 
 | ||||||
|  |             case Condition.Value: | ||||||
|  |                 if (! context.InArray && | ||||||
|  |                     (! context.InObject || ! context.ExpectingValue)) | ||||||
|  |                     throw new JsonException ( | ||||||
|  |                         "Can't add a value here"); | ||||||
|  | 
 | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void Init () | ||||||
|  |         { | ||||||
|  |             has_reached_end = false; | ||||||
|  |             hex_seq = new char[4]; | ||||||
|  |             indentation = 0; | ||||||
|  |             indent_value = 4; | ||||||
|  |             pretty_print = false; | ||||||
|  |             validate = true; | ||||||
|  |             lower_case_properties = false; | ||||||
|  | 
 | ||||||
|  |             ctx_stack = new Stack<WriterContext> (); | ||||||
|  |             context = new WriterContext (); | ||||||
|  |             ctx_stack.Push (context); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static void IntToHex (int n, char[] hex) | ||||||
|  |         { | ||||||
|  |             int num; | ||||||
|  | 
 | ||||||
|  |             for (int i = 0; i < 4; i++) { | ||||||
|  |                 num = n % 16; | ||||||
|  | 
 | ||||||
|  |                 if (num < 10) | ||||||
|  |                     hex[3 - i] = (char) ('0' + num); | ||||||
|  |                 else | ||||||
|  |                     hex[3 - i] = (char) ('A' + (num - 10)); | ||||||
|  | 
 | ||||||
|  |                 n >>= 4; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void Indent () | ||||||
|  |         { | ||||||
|  |             if (pretty_print) | ||||||
|  |                 indentation += indent_value; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         private void Put (string str) | ||||||
|  |         { | ||||||
|  |             if (pretty_print && ! context.ExpectingValue) | ||||||
|  |                 for (int i = 0; i < indentation; i++) | ||||||
|  |                     writer.Write (' '); | ||||||
|  | 
 | ||||||
|  |             writer.Write (str); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void PutNewline () | ||||||
|  |         { | ||||||
|  |             PutNewline (true); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void PutNewline (bool add_comma) | ||||||
|  |         { | ||||||
|  |             if (add_comma && ! context.ExpectingValue && | ||||||
|  |                 context.Count > 1) | ||||||
|  |                 writer.Write (','); | ||||||
|  | 
 | ||||||
|  |             if (pretty_print && ! context.ExpectingValue) | ||||||
|  |                 writer.Write (Environment.NewLine); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void PutString (string str) | ||||||
|  |         { | ||||||
|  |             Put (String.Empty); | ||||||
|  | 
 | ||||||
|  |             writer.Write ('"'); | ||||||
|  | 
 | ||||||
|  |             int n = str.Length; | ||||||
|  |             for (int i = 0; i < n; i++) { | ||||||
|  |                 switch (str[i]) { | ||||||
|  |                 case '\n': | ||||||
|  |                     writer.Write ("\\n"); | ||||||
|  |                     continue; | ||||||
|  | 
 | ||||||
|  |                 case '\r': | ||||||
|  |                     writer.Write ("\\r"); | ||||||
|  |                     continue; | ||||||
|  | 
 | ||||||
|  |                 case '\t': | ||||||
|  |                     writer.Write ("\\t"); | ||||||
|  |                     continue; | ||||||
|  | 
 | ||||||
|  |                 case '"': | ||||||
|  |                 case '\\': | ||||||
|  |                     writer.Write ('\\'); | ||||||
|  |                     writer.Write (str[i]); | ||||||
|  |                     continue; | ||||||
|  | 
 | ||||||
|  |                 case '\f': | ||||||
|  |                     writer.Write ("\\f"); | ||||||
|  |                     continue; | ||||||
|  | 
 | ||||||
|  |                 case '\b': | ||||||
|  |                     writer.Write ("\\b"); | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if ((int) str[i] >= 32 && (int) str[i] <= 126) { | ||||||
|  |                     writer.Write (str[i]); | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 // Default, turn into a \uXXXX sequence | ||||||
|  |                 IntToHex ((int) str[i], hex_seq); | ||||||
|  |                 writer.Write ("\\u"); | ||||||
|  |                 writer.Write (hex_seq); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             writer.Write ('"'); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void Unindent () | ||||||
|  |         { | ||||||
|  |             if (pretty_print) | ||||||
|  |                 indentation -= indent_value; | ||||||
|  |         } | ||||||
|  |         #endregion | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         public override string ToString () | ||||||
|  |         { | ||||||
|  |             if (inst_string_builder == null) | ||||||
|  |                 return String.Empty; | ||||||
|  | 
 | ||||||
|  |             return inst_string_builder.ToString (); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void Reset () | ||||||
|  |         { | ||||||
|  |             has_reached_end = false; | ||||||
|  | 
 | ||||||
|  |             ctx_stack.Clear (); | ||||||
|  |             context = new WriterContext (); | ||||||
|  |             ctx_stack.Push (context); | ||||||
|  | 
 | ||||||
|  |             if (inst_string_builder != null) | ||||||
|  |                 inst_string_builder.Remove (0, inst_string_builder.Length); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void Write (bool boolean) | ||||||
|  |         { | ||||||
|  |             DoValidation (Condition.Value); | ||||||
|  |             PutNewline (); | ||||||
|  | 
 | ||||||
|  |             Put (boolean ? "true" : "false"); | ||||||
|  | 
 | ||||||
|  |             context.ExpectingValue = false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void Write (decimal number) | ||||||
|  |         { | ||||||
|  |             DoValidation (Condition.Value); | ||||||
|  |             PutNewline (); | ||||||
|  | 
 | ||||||
|  |             Put (Convert.ToString (number, number_format)); | ||||||
|  | 
 | ||||||
|  |             context.ExpectingValue = false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void Write (double number) | ||||||
|  |         { | ||||||
|  |             DoValidation (Condition.Value); | ||||||
|  |             PutNewline (); | ||||||
|  | 
 | ||||||
|  |             string str = Convert.ToString (number, number_format); | ||||||
|  |             Put (str); | ||||||
|  | 
 | ||||||
|  |             if (str.IndexOf ('.') == -1 && | ||||||
|  |                 str.IndexOf ('E') == -1) | ||||||
|  |                 writer.Write (".0"); | ||||||
|  | 
 | ||||||
|  |             context.ExpectingValue = false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void Write(float number) | ||||||
|  |         { | ||||||
|  |             DoValidation(Condition.Value); | ||||||
|  |             PutNewline(); | ||||||
|  | 
 | ||||||
|  |             string str = Convert.ToString(number, number_format); | ||||||
|  |             Put(str); | ||||||
|  | 
 | ||||||
|  |             context.ExpectingValue = false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void Write (int number) | ||||||
|  |         { | ||||||
|  |             DoValidation (Condition.Value); | ||||||
|  |             PutNewline (); | ||||||
|  | 
 | ||||||
|  |             Put (Convert.ToString (number, number_format)); | ||||||
|  | 
 | ||||||
|  |             context.ExpectingValue = false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void Write (long number) | ||||||
|  |         { | ||||||
|  |             DoValidation (Condition.Value); | ||||||
|  |             PutNewline (); | ||||||
|  | 
 | ||||||
|  |             Put (Convert.ToString (number, number_format)); | ||||||
|  | 
 | ||||||
|  |             context.ExpectingValue = false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void Write (string str) | ||||||
|  |         { | ||||||
|  |             DoValidation (Condition.Value); | ||||||
|  |             PutNewline (); | ||||||
|  | 
 | ||||||
|  |             if (str == null) | ||||||
|  |                 Put ("null"); | ||||||
|  |             else | ||||||
|  |                 PutString (str); | ||||||
|  | 
 | ||||||
|  |             context.ExpectingValue = false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         [CLSCompliant(false)] | ||||||
|  |         public void Write (ulong number) | ||||||
|  |         { | ||||||
|  |             DoValidation (Condition.Value); | ||||||
|  |             PutNewline (); | ||||||
|  | 
 | ||||||
|  |             Put (Convert.ToString (number, number_format)); | ||||||
|  | 
 | ||||||
|  |             context.ExpectingValue = false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void WriteArrayEnd () | ||||||
|  |         { | ||||||
|  |             DoValidation (Condition.InArray); | ||||||
|  |             PutNewline (false); | ||||||
|  | 
 | ||||||
|  |             ctx_stack.Pop (); | ||||||
|  |             if (ctx_stack.Count == 1) | ||||||
|  |                 has_reached_end = true; | ||||||
|  |             else { | ||||||
|  |                 context = ctx_stack.Peek (); | ||||||
|  |                 context.ExpectingValue = false; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             Unindent (); | ||||||
|  |             Put ("]"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void WriteArrayStart () | ||||||
|  |         { | ||||||
|  |             DoValidation (Condition.NotAProperty); | ||||||
|  |             PutNewline (); | ||||||
|  | 
 | ||||||
|  |             Put ("["); | ||||||
|  | 
 | ||||||
|  |             context = new WriterContext (); | ||||||
|  |             context.InArray = true; | ||||||
|  |             ctx_stack.Push (context); | ||||||
|  | 
 | ||||||
|  |             Indent (); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void WriteObjectEnd () | ||||||
|  |         { | ||||||
|  |             DoValidation (Condition.InObject); | ||||||
|  |             PutNewline (false); | ||||||
|  | 
 | ||||||
|  |             ctx_stack.Pop (); | ||||||
|  |             if (ctx_stack.Count == 1) | ||||||
|  |                 has_reached_end = true; | ||||||
|  |             else { | ||||||
|  |                 context = ctx_stack.Peek (); | ||||||
|  |                 context.ExpectingValue = false; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             Unindent (); | ||||||
|  |             Put ("}"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void WriteObjectStart () | ||||||
|  |         { | ||||||
|  |             DoValidation (Condition.NotAProperty); | ||||||
|  |             PutNewline (); | ||||||
|  | 
 | ||||||
|  |             Put ("{"); | ||||||
|  | 
 | ||||||
|  |             context = new WriterContext (); | ||||||
|  |             context.InObject = true; | ||||||
|  |             ctx_stack.Push (context); | ||||||
|  | 
 | ||||||
|  |             Indent (); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void WritePropertyName (string property_name) | ||||||
|  |         { | ||||||
|  |             DoValidation (Condition.Property); | ||||||
|  |             PutNewline (); | ||||||
|  |             string propertyName = (property_name == null || !lower_case_properties) | ||||||
|  |                 ? property_name | ||||||
|  |                 : property_name.ToLowerInvariant(); | ||||||
|  | 
 | ||||||
|  |             PutString (propertyName); | ||||||
|  | 
 | ||||||
|  |             if (pretty_print) { | ||||||
|  |                 if (propertyName.Length > context.Padding) | ||||||
|  |                     context.Padding = propertyName.Length; | ||||||
|  | 
 | ||||||
|  |                 for (int i = context.Padding - propertyName.Length; | ||||||
|  |                      i >= 0; i--) | ||||||
|  |                     writer.Write (' '); | ||||||
|  | 
 | ||||||
|  |                 writer.Write (": "); | ||||||
|  |             } else | ||||||
|  |                 writer.Write (':'); | ||||||
|  | 
 | ||||||
|  |             context.ExpectingValue = true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,11 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: b5c3c60bc9e974025813d454b53835e1 | ||||||
|  | MonoImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   serializedVersion: 2 | ||||||
|  |   defaultReferences: [] | ||||||
|  |   executionOrder: 0 | ||||||
|  |   icon: {instanceID: 0} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,912 @@ | ||||||
|  | #region Header | ||||||
|  | /** | ||||||
|  |  * Lexer.cs | ||||||
|  |  *   JSON lexer implementation based on a finite state machine. | ||||||
|  |  * | ||||||
|  |  * The authors disclaim copyright to this source code. For more details, see | ||||||
|  |  * the COPYING file included with this distribution. | ||||||
|  |  **/ | ||||||
|  | #endregion | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.IO; | ||||||
|  | using System.Text; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | namespace Guru.LitJson | ||||||
|  | { | ||||||
|  |     internal class FsmContext | ||||||
|  |     { | ||||||
|  |         public bool  Return; | ||||||
|  |         public int   NextState; | ||||||
|  |         public Lexer L; | ||||||
|  |         public int   StateStack; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     internal class Lexer | ||||||
|  |     { | ||||||
|  |         #region Fields | ||||||
|  |         private delegate bool StateHandler (FsmContext ctx); | ||||||
|  | 
 | ||||||
|  |         private static readonly int[]          fsm_return_table; | ||||||
|  |         private static readonly StateHandler[] fsm_handler_table; | ||||||
|  | 
 | ||||||
|  |         private bool          allow_comments; | ||||||
|  |         private bool          allow_single_quoted_strings; | ||||||
|  |         private bool          end_of_input; | ||||||
|  |         private FsmContext    fsm_context; | ||||||
|  |         private int           input_buffer; | ||||||
|  |         private int           input_char; | ||||||
|  |         private TextReader    reader; | ||||||
|  |         private int           state; | ||||||
|  |         private StringBuilder string_buffer; | ||||||
|  |         private string        string_value; | ||||||
|  |         private int           token; | ||||||
|  |         private int           unichar; | ||||||
|  |         #endregion | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         #region Properties | ||||||
|  |         public bool AllowComments { | ||||||
|  |             get { return allow_comments; } | ||||||
|  |             set { allow_comments = value; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public bool AllowSingleQuotedStrings { | ||||||
|  |             get { return allow_single_quoted_strings; } | ||||||
|  |             set { allow_single_quoted_strings = value; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public bool EndOfInput { | ||||||
|  |             get { return end_of_input; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public int Token { | ||||||
|  |             get { return token; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public string StringValue { | ||||||
|  |             get { return string_value; } | ||||||
|  |         } | ||||||
|  |         #endregion | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         #region Constructors | ||||||
|  |         static Lexer () | ||||||
|  |         { | ||||||
|  |             PopulateFsmTables (out fsm_handler_table, out fsm_return_table); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public Lexer (TextReader reader) | ||||||
|  |         { | ||||||
|  |             allow_comments = true; | ||||||
|  |             allow_single_quoted_strings = true; | ||||||
|  | 
 | ||||||
|  |             input_buffer = 0; | ||||||
|  |             string_buffer = new StringBuilder (128); | ||||||
|  |             state = 1; | ||||||
|  |             end_of_input = false; | ||||||
|  |             this.reader = reader; | ||||||
|  | 
 | ||||||
|  |             fsm_context = new FsmContext (); | ||||||
|  |             fsm_context.L = this; | ||||||
|  |         } | ||||||
|  |         #endregion | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         #region Static Methods | ||||||
|  |         private static int HexValue (int digit) | ||||||
|  |         { | ||||||
|  |             switch (digit) { | ||||||
|  |             case 'a': | ||||||
|  |             case 'A': | ||||||
|  |                 return 10; | ||||||
|  | 
 | ||||||
|  |             case 'b': | ||||||
|  |             case 'B': | ||||||
|  |                 return 11; | ||||||
|  | 
 | ||||||
|  |             case 'c': | ||||||
|  |             case 'C': | ||||||
|  |                 return 12; | ||||||
|  | 
 | ||||||
|  |             case 'd': | ||||||
|  |             case 'D': | ||||||
|  |                 return 13; | ||||||
|  | 
 | ||||||
|  |             case 'e': | ||||||
|  |             case 'E': | ||||||
|  |                 return 14; | ||||||
|  | 
 | ||||||
|  |             case 'f': | ||||||
|  |             case 'F': | ||||||
|  |                 return 15; | ||||||
|  | 
 | ||||||
|  |             default: | ||||||
|  |                 return digit - '0'; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static void PopulateFsmTables (out StateHandler[] fsm_handler_table, out int[] fsm_return_table) | ||||||
|  |         { | ||||||
|  |             // See section A.1. of the manual for details of the finite | ||||||
|  |             // state machine. | ||||||
|  |             fsm_handler_table = new StateHandler[28] { | ||||||
|  |                 State1, | ||||||
|  |                 State2, | ||||||
|  |                 State3, | ||||||
|  |                 State4, | ||||||
|  |                 State5, | ||||||
|  |                 State6, | ||||||
|  |                 State7, | ||||||
|  |                 State8, | ||||||
|  |                 State9, | ||||||
|  |                 State10, | ||||||
|  |                 State11, | ||||||
|  |                 State12, | ||||||
|  |                 State13, | ||||||
|  |                 State14, | ||||||
|  |                 State15, | ||||||
|  |                 State16, | ||||||
|  |                 State17, | ||||||
|  |                 State18, | ||||||
|  |                 State19, | ||||||
|  |                 State20, | ||||||
|  |                 State21, | ||||||
|  |                 State22, | ||||||
|  |                 State23, | ||||||
|  |                 State24, | ||||||
|  |                 State25, | ||||||
|  |                 State26, | ||||||
|  |                 State27, | ||||||
|  |                 State28 | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             fsm_return_table = new int[28] { | ||||||
|  |                 (int) ParserToken.Char, | ||||||
|  |                 0, | ||||||
|  |                 (int) ParserToken.Number, | ||||||
|  |                 (int) ParserToken.Number, | ||||||
|  |                 0, | ||||||
|  |                 (int) ParserToken.Number, | ||||||
|  |                 0, | ||||||
|  |                 (int) ParserToken.Number, | ||||||
|  |                 0, | ||||||
|  |                 0, | ||||||
|  |                 (int) ParserToken.True, | ||||||
|  |                 0, | ||||||
|  |                 0, | ||||||
|  |                 0, | ||||||
|  |                 (int) ParserToken.False, | ||||||
|  |                 0, | ||||||
|  |                 0, | ||||||
|  |                 (int) ParserToken.Null, | ||||||
|  |                 (int) ParserToken.CharSeq, | ||||||
|  |                 (int) ParserToken.Char, | ||||||
|  |                 0, | ||||||
|  |                 0, | ||||||
|  |                 (int) ParserToken.CharSeq, | ||||||
|  |                 (int) ParserToken.Char, | ||||||
|  |                 0, | ||||||
|  |                 0, | ||||||
|  |                 0, | ||||||
|  |                 0 | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static char ProcessEscChar (int esc_char) | ||||||
|  |         { | ||||||
|  |             switch (esc_char) { | ||||||
|  |             case '"': | ||||||
|  |             case '\'': | ||||||
|  |             case '\\': | ||||||
|  |             case '/': | ||||||
|  |                 return Convert.ToChar (esc_char); | ||||||
|  | 
 | ||||||
|  |             case 'n': | ||||||
|  |                 return '\n'; | ||||||
|  | 
 | ||||||
|  |             case 't': | ||||||
|  |                 return '\t'; | ||||||
|  | 
 | ||||||
|  |             case 'r': | ||||||
|  |                 return '\r'; | ||||||
|  | 
 | ||||||
|  |             case 'b': | ||||||
|  |                 return '\b'; | ||||||
|  | 
 | ||||||
|  |             case 'f': | ||||||
|  |                 return '\f'; | ||||||
|  | 
 | ||||||
|  |             default: | ||||||
|  |                 // Unreachable | ||||||
|  |                 return '?'; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static bool State1 (FsmContext ctx) | ||||||
|  |         { | ||||||
|  |             while (ctx.L.GetChar ()) { | ||||||
|  |                 if (ctx.L.input_char == ' ' || | ||||||
|  |                     ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') | ||||||
|  |                     continue; | ||||||
|  | 
 | ||||||
|  |                 if (ctx.L.input_char >= '1' && ctx.L.input_char <= '9') { | ||||||
|  |                     ctx.L.string_buffer.Append ((char) ctx.L.input_char); | ||||||
|  |                     ctx.NextState = 3; | ||||||
|  |                     return true; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 switch (ctx.L.input_char) { | ||||||
|  |                 case '"': | ||||||
|  |                     ctx.NextState = 19; | ||||||
|  |                     ctx.Return = true; | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 case ',': | ||||||
|  |                 case ':': | ||||||
|  |                 case '[': | ||||||
|  |                 case ']': | ||||||
|  |                 case '{': | ||||||
|  |                 case '}': | ||||||
|  |                     ctx.NextState = 1; | ||||||
|  |                     ctx.Return = true; | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 case '-': | ||||||
|  |                     ctx.L.string_buffer.Append ((char) ctx.L.input_char); | ||||||
|  |                     ctx.NextState = 2; | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 case '0': | ||||||
|  |                     ctx.L.string_buffer.Append ((char) ctx.L.input_char); | ||||||
|  |                     ctx.NextState = 4; | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 case 'f': | ||||||
|  |                     ctx.NextState = 12; | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 case 'n': | ||||||
|  |                     ctx.NextState = 16; | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 case 't': | ||||||
|  |                     ctx.NextState = 9; | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 case '\'': | ||||||
|  |                     if (! ctx.L.allow_single_quoted_strings) | ||||||
|  |                         return false; | ||||||
|  | 
 | ||||||
|  |                     ctx.L.input_char = '"'; | ||||||
|  |                     ctx.NextState = 23; | ||||||
|  |                     ctx.Return = true; | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 case '/': | ||||||
|  |                     if (! ctx.L.allow_comments) | ||||||
|  |                         return false; | ||||||
|  | 
 | ||||||
|  |                     ctx.NextState = 25; | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 default: | ||||||
|  |                     return false; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static bool State2 (FsmContext ctx) | ||||||
|  |         { | ||||||
|  |             ctx.L.GetChar (); | ||||||
|  | 
 | ||||||
|  |             if (ctx.L.input_char >= '1' && ctx.L.input_char<= '9') { | ||||||
|  |                 ctx.L.string_buffer.Append ((char) ctx.L.input_char); | ||||||
|  |                 ctx.NextState = 3; | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             switch (ctx.L.input_char) { | ||||||
|  |             case '0': | ||||||
|  |                 ctx.L.string_buffer.Append ((char) ctx.L.input_char); | ||||||
|  |                 ctx.NextState = 4; | ||||||
|  |                 return true; | ||||||
|  | 
 | ||||||
|  |             default: | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static bool State3 (FsmContext ctx) | ||||||
|  |         { | ||||||
|  |             while (ctx.L.GetChar ()) { | ||||||
|  |                 if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9') { | ||||||
|  |                     ctx.L.string_buffer.Append ((char) ctx.L.input_char); | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if (ctx.L.input_char == ' ' || | ||||||
|  |                     ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') { | ||||||
|  |                     ctx.Return = true; | ||||||
|  |                     ctx.NextState = 1; | ||||||
|  |                     return true; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 switch (ctx.L.input_char) { | ||||||
|  |                 case ',': | ||||||
|  |                 case ']': | ||||||
|  |                 case '}': | ||||||
|  |                     ctx.L.UngetChar (); | ||||||
|  |                     ctx.Return = true; | ||||||
|  |                     ctx.NextState = 1; | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 case '.': | ||||||
|  |                     ctx.L.string_buffer.Append ((char) ctx.L.input_char); | ||||||
|  |                     ctx.NextState = 5; | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 case 'e': | ||||||
|  |                 case 'E': | ||||||
|  |                     ctx.L.string_buffer.Append ((char) ctx.L.input_char); | ||||||
|  |                     ctx.NextState = 7; | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 default: | ||||||
|  |                     return false; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static bool State4 (FsmContext ctx) | ||||||
|  |         { | ||||||
|  |             ctx.L.GetChar (); | ||||||
|  | 
 | ||||||
|  |             if (ctx.L.input_char == ' ' || | ||||||
|  |                 ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') { | ||||||
|  |                 ctx.Return = true; | ||||||
|  |                 ctx.NextState = 1; | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             switch (ctx.L.input_char) { | ||||||
|  |             case ',': | ||||||
|  |             case ']': | ||||||
|  |             case '}': | ||||||
|  |                 ctx.L.UngetChar (); | ||||||
|  |                 ctx.Return = true; | ||||||
|  |                 ctx.NextState = 1; | ||||||
|  |                 return true; | ||||||
|  | 
 | ||||||
|  |             case '.': | ||||||
|  |                 ctx.L.string_buffer.Append ((char) ctx.L.input_char); | ||||||
|  |                 ctx.NextState = 5; | ||||||
|  |                 return true; | ||||||
|  | 
 | ||||||
|  |             case 'e': | ||||||
|  |             case 'E': | ||||||
|  |                 ctx.L.string_buffer.Append ((char) ctx.L.input_char); | ||||||
|  |                 ctx.NextState = 7; | ||||||
|  |                 return true; | ||||||
|  | 
 | ||||||
|  |             default: | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static bool State5 (FsmContext ctx) | ||||||
|  |         { | ||||||
|  |             ctx.L.GetChar (); | ||||||
|  | 
 | ||||||
|  |             if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9') { | ||||||
|  |                 ctx.L.string_buffer.Append ((char) ctx.L.input_char); | ||||||
|  |                 ctx.NextState = 6; | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static bool State6 (FsmContext ctx) | ||||||
|  |         { | ||||||
|  |             while (ctx.L.GetChar ()) { | ||||||
|  |                 if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9') { | ||||||
|  |                     ctx.L.string_buffer.Append ((char) ctx.L.input_char); | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if (ctx.L.input_char == ' ' || | ||||||
|  |                     ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') { | ||||||
|  |                     ctx.Return = true; | ||||||
|  |                     ctx.NextState = 1; | ||||||
|  |                     return true; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 switch (ctx.L.input_char) { | ||||||
|  |                 case ',': | ||||||
|  |                 case ']': | ||||||
|  |                 case '}': | ||||||
|  |                     ctx.L.UngetChar (); | ||||||
|  |                     ctx.Return = true; | ||||||
|  |                     ctx.NextState = 1; | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 case 'e': | ||||||
|  |                 case 'E': | ||||||
|  |                     ctx.L.string_buffer.Append ((char) ctx.L.input_char); | ||||||
|  |                     ctx.NextState = 7; | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 default: | ||||||
|  |                     return false; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static bool State7 (FsmContext ctx) | ||||||
|  |         { | ||||||
|  |             ctx.L.GetChar (); | ||||||
|  | 
 | ||||||
|  |             if (ctx.L.input_char >= '0' && ctx.L.input_char<= '9') { | ||||||
|  |                 ctx.L.string_buffer.Append ((char) ctx.L.input_char); | ||||||
|  |                 ctx.NextState = 8; | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             switch (ctx.L.input_char) { | ||||||
|  |             case '+': | ||||||
|  |             case '-': | ||||||
|  |                 ctx.L.string_buffer.Append ((char) ctx.L.input_char); | ||||||
|  |                 ctx.NextState = 8; | ||||||
|  |                 return true; | ||||||
|  | 
 | ||||||
|  |             default: | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static bool State8 (FsmContext ctx) | ||||||
|  |         { | ||||||
|  |             while (ctx.L.GetChar ()) { | ||||||
|  |                 if (ctx.L.input_char >= '0' && ctx.L.input_char<= '9') { | ||||||
|  |                     ctx.L.string_buffer.Append ((char) ctx.L.input_char); | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if (ctx.L.input_char == ' ' || | ||||||
|  |                     ctx.L.input_char >= '\t' && ctx.L.input_char<= '\r') { | ||||||
|  |                     ctx.Return = true; | ||||||
|  |                     ctx.NextState = 1; | ||||||
|  |                     return true; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 switch (ctx.L.input_char) { | ||||||
|  |                 case ',': | ||||||
|  |                 case ']': | ||||||
|  |                 case '}': | ||||||
|  |                     ctx.L.UngetChar (); | ||||||
|  |                     ctx.Return = true; | ||||||
|  |                     ctx.NextState = 1; | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 default: | ||||||
|  |                     return false; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static bool State9 (FsmContext ctx) | ||||||
|  |         { | ||||||
|  |             ctx.L.GetChar (); | ||||||
|  | 
 | ||||||
|  |             switch (ctx.L.input_char) { | ||||||
|  |             case 'r': | ||||||
|  |                 ctx.NextState = 10; | ||||||
|  |                 return true; | ||||||
|  | 
 | ||||||
|  |             default: | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static bool State10 (FsmContext ctx) | ||||||
|  |         { | ||||||
|  |             ctx.L.GetChar (); | ||||||
|  | 
 | ||||||
|  |             switch (ctx.L.input_char) { | ||||||
|  |             case 'u': | ||||||
|  |                 ctx.NextState = 11; | ||||||
|  |                 return true; | ||||||
|  | 
 | ||||||
|  |             default: | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static bool State11 (FsmContext ctx) | ||||||
|  |         { | ||||||
|  |             ctx.L.GetChar (); | ||||||
|  | 
 | ||||||
|  |             switch (ctx.L.input_char) { | ||||||
|  |             case 'e': | ||||||
|  |                 ctx.Return = true; | ||||||
|  |                 ctx.NextState = 1; | ||||||
|  |                 return true; | ||||||
|  | 
 | ||||||
|  |             default: | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static bool State12 (FsmContext ctx) | ||||||
|  |         { | ||||||
|  |             ctx.L.GetChar (); | ||||||
|  | 
 | ||||||
|  |             switch (ctx.L.input_char) { | ||||||
|  |             case 'a': | ||||||
|  |                 ctx.NextState = 13; | ||||||
|  |                 return true; | ||||||
|  | 
 | ||||||
|  |             default: | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static bool State13 (FsmContext ctx) | ||||||
|  |         { | ||||||
|  |             ctx.L.GetChar (); | ||||||
|  | 
 | ||||||
|  |             switch (ctx.L.input_char) { | ||||||
|  |             case 'l': | ||||||
|  |                 ctx.NextState = 14; | ||||||
|  |                 return true; | ||||||
|  | 
 | ||||||
|  |             default: | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static bool State14 (FsmContext ctx) | ||||||
|  |         { | ||||||
|  |             ctx.L.GetChar (); | ||||||
|  | 
 | ||||||
|  |             switch (ctx.L.input_char) { | ||||||
|  |             case 's': | ||||||
|  |                 ctx.NextState = 15; | ||||||
|  |                 return true; | ||||||
|  | 
 | ||||||
|  |             default: | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static bool State15 (FsmContext ctx) | ||||||
|  |         { | ||||||
|  |             ctx.L.GetChar (); | ||||||
|  | 
 | ||||||
|  |             switch (ctx.L.input_char) { | ||||||
|  |             case 'e': | ||||||
|  |                 ctx.Return = true; | ||||||
|  |                 ctx.NextState = 1; | ||||||
|  |                 return true; | ||||||
|  | 
 | ||||||
|  |             default: | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static bool State16 (FsmContext ctx) | ||||||
|  |         { | ||||||
|  |             ctx.L.GetChar (); | ||||||
|  | 
 | ||||||
|  |             switch (ctx.L.input_char) { | ||||||
|  |             case 'u': | ||||||
|  |                 ctx.NextState = 17; | ||||||
|  |                 return true; | ||||||
|  | 
 | ||||||
|  |             default: | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static bool State17 (FsmContext ctx) | ||||||
|  |         { | ||||||
|  |             ctx.L.GetChar (); | ||||||
|  | 
 | ||||||
|  |             switch (ctx.L.input_char) { | ||||||
|  |             case 'l': | ||||||
|  |                 ctx.NextState = 18; | ||||||
|  |                 return true; | ||||||
|  | 
 | ||||||
|  |             default: | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static bool State18 (FsmContext ctx) | ||||||
|  |         { | ||||||
|  |             ctx.L.GetChar (); | ||||||
|  | 
 | ||||||
|  |             switch (ctx.L.input_char) { | ||||||
|  |             case 'l': | ||||||
|  |                 ctx.Return = true; | ||||||
|  |                 ctx.NextState = 1; | ||||||
|  |                 return true; | ||||||
|  | 
 | ||||||
|  |             default: | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static bool State19 (FsmContext ctx) | ||||||
|  |         { | ||||||
|  |             while (ctx.L.GetChar ()) { | ||||||
|  |                 switch (ctx.L.input_char) { | ||||||
|  |                 case '"': | ||||||
|  |                     ctx.L.UngetChar (); | ||||||
|  |                     ctx.Return = true; | ||||||
|  |                     ctx.NextState = 20; | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 case '\\': | ||||||
|  |                     ctx.StateStack = 19; | ||||||
|  |                     ctx.NextState = 21; | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 default: | ||||||
|  |                     ctx.L.string_buffer.Append ((char) ctx.L.input_char); | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static bool State20 (FsmContext ctx) | ||||||
|  |         { | ||||||
|  |             ctx.L.GetChar (); | ||||||
|  | 
 | ||||||
|  |             switch (ctx.L.input_char) { | ||||||
|  |             case '"': | ||||||
|  |                 ctx.Return = true; | ||||||
|  |                 ctx.NextState = 1; | ||||||
|  |                 return true; | ||||||
|  | 
 | ||||||
|  |             default: | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static bool State21 (FsmContext ctx) | ||||||
|  |         { | ||||||
|  |             ctx.L.GetChar (); | ||||||
|  | 
 | ||||||
|  |             switch (ctx.L.input_char) { | ||||||
|  |             case 'u': | ||||||
|  |                 ctx.NextState = 22; | ||||||
|  |                 return true; | ||||||
|  | 
 | ||||||
|  |             case '"': | ||||||
|  |             case '\'': | ||||||
|  |             case '/': | ||||||
|  |             case '\\': | ||||||
|  |             case 'b': | ||||||
|  |             case 'f': | ||||||
|  |             case 'n': | ||||||
|  |             case 'r': | ||||||
|  |             case 't': | ||||||
|  |                 ctx.L.string_buffer.Append ( | ||||||
|  |                     ProcessEscChar (ctx.L.input_char)); | ||||||
|  |                 ctx.NextState = ctx.StateStack; | ||||||
|  |                 return true; | ||||||
|  | 
 | ||||||
|  |             default: | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static bool State22 (FsmContext ctx) | ||||||
|  |         { | ||||||
|  |             int counter = 0; | ||||||
|  |             int mult    = 4096; | ||||||
|  | 
 | ||||||
|  |             ctx.L.unichar = 0; | ||||||
|  | 
 | ||||||
|  |             while (ctx.L.GetChar ()) { | ||||||
|  | 
 | ||||||
|  |                 if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9' || | ||||||
|  |                     ctx.L.input_char >= 'A' && ctx.L.input_char <= 'F' || | ||||||
|  |                     ctx.L.input_char >= 'a' && ctx.L.input_char <= 'f') { | ||||||
|  | 
 | ||||||
|  |                     ctx.L.unichar += HexValue (ctx.L.input_char) * mult; | ||||||
|  | 
 | ||||||
|  |                     counter++; | ||||||
|  |                     mult /= 16; | ||||||
|  | 
 | ||||||
|  |                     if (counter == 4) { | ||||||
|  |                         ctx.L.string_buffer.Append ( | ||||||
|  |                             Convert.ToChar (ctx.L.unichar)); | ||||||
|  |                         ctx.NextState = ctx.StateStack; | ||||||
|  |                         return true; | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static bool State23 (FsmContext ctx) | ||||||
|  |         { | ||||||
|  |             while (ctx.L.GetChar ()) { | ||||||
|  |                 switch (ctx.L.input_char) { | ||||||
|  |                 case '\'': | ||||||
|  |                     ctx.L.UngetChar (); | ||||||
|  |                     ctx.Return = true; | ||||||
|  |                     ctx.NextState = 24; | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 case '\\': | ||||||
|  |                     ctx.StateStack = 23; | ||||||
|  |                     ctx.NextState = 21; | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 default: | ||||||
|  |                     ctx.L.string_buffer.Append ((char) ctx.L.input_char); | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static bool State24 (FsmContext ctx) | ||||||
|  |         { | ||||||
|  |             ctx.L.GetChar (); | ||||||
|  | 
 | ||||||
|  |             switch (ctx.L.input_char) { | ||||||
|  |             case '\'': | ||||||
|  |                 ctx.L.input_char = '"'; | ||||||
|  |                 ctx.Return = true; | ||||||
|  |                 ctx.NextState = 1; | ||||||
|  |                 return true; | ||||||
|  | 
 | ||||||
|  |             default: | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static bool State25 (FsmContext ctx) | ||||||
|  |         { | ||||||
|  |             ctx.L.GetChar (); | ||||||
|  | 
 | ||||||
|  |             switch (ctx.L.input_char) { | ||||||
|  |             case '*': | ||||||
|  |                 ctx.NextState = 27; | ||||||
|  |                 return true; | ||||||
|  | 
 | ||||||
|  |             case '/': | ||||||
|  |                 ctx.NextState = 26; | ||||||
|  |                 return true; | ||||||
|  | 
 | ||||||
|  |             default: | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static bool State26 (FsmContext ctx) | ||||||
|  |         { | ||||||
|  |             while (ctx.L.GetChar ()) { | ||||||
|  |                 if (ctx.L.input_char == '\n') { | ||||||
|  |                     ctx.NextState = 1; | ||||||
|  |                     return true; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static bool State27 (FsmContext ctx) | ||||||
|  |         { | ||||||
|  |             while (ctx.L.GetChar ()) { | ||||||
|  |                 if (ctx.L.input_char == '*') { | ||||||
|  |                     ctx.NextState = 28; | ||||||
|  |                     return true; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static bool State28 (FsmContext ctx) | ||||||
|  |         { | ||||||
|  |             while (ctx.L.GetChar ()) { | ||||||
|  |                 if (ctx.L.input_char == '*') | ||||||
|  |                     continue; | ||||||
|  | 
 | ||||||
|  |                 if (ctx.L.input_char == '/') { | ||||||
|  |                     ctx.NextState = 1; | ||||||
|  |                     return true; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 ctx.NextState = 27; | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |         #endregion | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         private bool GetChar () | ||||||
|  |         { | ||||||
|  |             if ((input_char = NextChar ()) != -1) | ||||||
|  |                 return true; | ||||||
|  | 
 | ||||||
|  |             end_of_input = true; | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private int NextChar () | ||||||
|  |         { | ||||||
|  |             if (input_buffer != 0) { | ||||||
|  |                 int tmp = input_buffer; | ||||||
|  |                 input_buffer = 0; | ||||||
|  | 
 | ||||||
|  |                 return tmp; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return reader.Read (); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public bool NextToken () | ||||||
|  |         { | ||||||
|  |             StateHandler handler; | ||||||
|  |             fsm_context.Return = false; | ||||||
|  | 
 | ||||||
|  |             while (true) { | ||||||
|  |                 handler = fsm_handler_table[state - 1]; | ||||||
|  | 
 | ||||||
|  |                 if (! handler (fsm_context)) | ||||||
|  |                     throw new JsonException (input_char); | ||||||
|  | 
 | ||||||
|  |                 if (end_of_input) | ||||||
|  |                     return false; | ||||||
|  | 
 | ||||||
|  |                 if (fsm_context.Return) { | ||||||
|  |                     string_value = string_buffer.ToString (); | ||||||
|  |                     string_buffer.Remove (0, string_buffer.Length); | ||||||
|  |                     token = fsm_return_table[state - 1]; | ||||||
|  | 
 | ||||||
|  |                     if (token == (int) ParserToken.Char) | ||||||
|  |                         token = input_char; | ||||||
|  | 
 | ||||||
|  |                     state = fsm_context.NextState; | ||||||
|  | 
 | ||||||
|  |                     return true; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 state = fsm_context.NextState; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void UngetChar () | ||||||
|  |         { | ||||||
|  |             input_buffer = input_char; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,11 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 0e99ecf149fcd4361bafc43771b80d60 | ||||||
|  | MonoImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   serializedVersion: 2 | ||||||
|  |   defaultReferences: [] | ||||||
|  |   executionOrder: 0 | ||||||
|  |   icon: {instanceID: 0} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,87 @@ | ||||||
|  | <Project Sdk="Microsoft.NET.Sdk"> | ||||||
|  | 
 | ||||||
|  |   <PropertyGroup> | ||||||
|  |     <TargetFrameworks>netstandard2.1;netstandard2.0;net45;net48;netstandard1.5;net40;net35;net20;net6.0;net8.0</TargetFrameworks> | ||||||
|  |   </PropertyGroup> | ||||||
|  | 
 | ||||||
|  |   <PropertyGroup> | ||||||
|  |     <GenerateAssemblyInfo>false</GenerateAssemblyInfo> | ||||||
|  |     <DebugType>embedded</DebugType> | ||||||
|  |     <EmbedUntrackedSources>true</EmbedUntrackedSources> | ||||||
|  |     <PublishRepositoryUrl>true</PublishRepositoryUrl> | ||||||
|  |     <SourceLinkCreate Condition="'$(OS)' == 'Windows_NT'">true</SourceLinkCreate> | ||||||
|  |     <PackageReadmeFile>README.md</PackageReadmeFile> | ||||||
|  |   </PropertyGroup> | ||||||
|  | 
 | ||||||
|  |   <ItemGroup Condition="'$(OS)' == 'Windows_NT'"> | ||||||
|  |     <PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="all" /> | ||||||
|  |     <DotNetCliToolReference Include="dotnet-sourcelink-git" Version="2.8.3" /> | ||||||
|  |     <DotNetCliToolReference Include="dotnet-sourcelink" Version="2.8.3" /> | ||||||
|  |   </ItemGroup> | ||||||
|  | 
 | ||||||
|  |   <PropertyGroup> | ||||||
|  |     <PackageId>LitJson</PackageId> | ||||||
|  |     <Description>A .NET library to handle conversions from and to JSON (JavaScript Object Notation) strings. Written in C#, and it’s intended to be small, fast and easy to use. | ||||||
|  | It's quick and lean, without external dependencies.</Description> | ||||||
|  |     <Copyright>The authors disclaim copyright to this source code.</Copyright> | ||||||
|  |     <Authors>Leonardo Boshell, Mattias Karlsson and contributors</Authors> | ||||||
|  |     <Company>Leonardo Boshell, Mattias Karlsson and contributors</Company> | ||||||
|  |     <PackageLicenseExpression>Unlicense</PackageLicenseExpression> | ||||||
|  |     <PackageIcon>litjson.png</PackageIcon> | ||||||
|  |     <RepositoryUrl>https://github.com/LitJSON/litjson</RepositoryUrl> | ||||||
|  |     <RepositoryType>git</RepositoryType> | ||||||
|  |     <PackageTags>JSON;Serializer</PackageTags> | ||||||
|  |     <IncludeSource>true</IncludeSource> | ||||||
|  |   </PropertyGroup> | ||||||
|  | 
 | ||||||
|  |   <ItemGroup> | ||||||
|  |     <None Include="litjson.png" Pack="true" PackagePath="" /> | ||||||
|  |   </ItemGroup> | ||||||
|  | 
 | ||||||
|  |   <PropertyGroup Condition="'$(TargetFramework)' == 'net20' "> | ||||||
|  |     <DefineConstants>$(DefineConstants);LEGACY</DefineConstants> | ||||||
|  |   </PropertyGroup> | ||||||
|  | 
 | ||||||
|  |   <PropertyGroup Condition="'$(TargetFramework)' == 'net35' "> | ||||||
|  |     <DefineConstants>$(DefineConstants);LEGACY</DefineConstants> | ||||||
|  |   </PropertyGroup> | ||||||
|  | 
 | ||||||
|  |   <PropertyGroup Condition="'$(TargetFramework)' == 'net40' "> | ||||||
|  |     <DefineConstants>$(DefineConstants);LEGACY</DefineConstants> | ||||||
|  |   </PropertyGroup> | ||||||
|  | 
 | ||||||
|  |   <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.5' "> | ||||||
|  |     <PackageReference Include="System.Collections.NonGeneric" Version="4.3.0" /> | ||||||
|  |     <PackageReference Include="System.Collections.Specialized" Version="4.3.0" /> | ||||||
|  |     <PackageReference Include="System.Reflection" Version="4.3.0" /> | ||||||
|  |     <PackageReference Include="System.Reflection.TypeExtensions" Version="4.7.0" /> | ||||||
|  |   </ItemGroup> | ||||||
|  | 
 | ||||||
|  |   <ItemGroup> | ||||||
|  |     <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies.net20" Version="1.0.3" Condition="'$(TargetFramework)' == 'net20' "> | ||||||
|  |       <PrivateAssets>all</PrivateAssets> | ||||||
|  |       <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> | ||||||
|  |     </PackageReference> | ||||||
|  |     <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies.net35" Version="1.0.3" Condition="'$(TargetFramework)' == 'net35' "> | ||||||
|  |       <PrivateAssets>all</PrivateAssets> | ||||||
|  |       <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> | ||||||
|  |     </PackageReference> | ||||||
|  |     <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies.net40" Version="1.0.3" Condition="'$(TargetFramework)' == 'net40' "> | ||||||
|  |       <PrivateAssets>all</PrivateAssets> | ||||||
|  |       <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> | ||||||
|  |     </PackageReference> | ||||||
|  |     <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies.net45" Version="1.0.3" Condition="'$(TargetFramework)' == 'net45' "> | ||||||
|  |       <PrivateAssets>all</PrivateAssets> | ||||||
|  |       <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> | ||||||
|  |     </PackageReference> | ||||||
|  |     <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies.net48" Version="1.0.3" Condition="'$(TargetFramework)' == 'net48' "> | ||||||
|  |       <PrivateAssets>all</PrivateAssets> | ||||||
|  |       <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> | ||||||
|  |     </PackageReference> | ||||||
|  |   </ItemGroup> | ||||||
|  | 
 | ||||||
|  |   <ItemGroup> | ||||||
|  |     <None Include="../../README.md" Pack="true" PackagePath="\" /> | ||||||
|  |   </ItemGroup> | ||||||
|  | 
 | ||||||
|  | </Project> | ||||||
|  | @ -0,0 +1,7 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: c5e8519174c484ae1903459114f61f98 | ||||||
|  | DefaultImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,24 @@ | ||||||
|  | #if NETSTANDARD1_5 | ||||||
|  | using System; | ||||||
|  | using System.Reflection; | ||||||
|  | namespace LitJson | ||||||
|  | { | ||||||
|  |     internal static class Netstandard15Polyfill | ||||||
|  |     { | ||||||
|  |         internal static Type GetInterface(this Type type, string name) | ||||||
|  |         { | ||||||
|  |             return type.GetTypeInfo().GetInterface(name);  | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         internal static bool IsClass(this Type type) | ||||||
|  |         { | ||||||
|  |             return type.GetTypeInfo().IsClass; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         internal static bool IsEnum(this Type type) | ||||||
|  |         { | ||||||
|  |             return type.GetTypeInfo().IsEnum; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | @ -0,0 +1,11 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: e212e5f514d9d4ec68eed1251080cce4 | ||||||
|  | MonoImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   serializedVersion: 2 | ||||||
|  |   defaultReferences: [] | ||||||
|  |   executionOrder: 0 | ||||||
|  |   icon: {instanceID: 0} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,44 @@ | ||||||
|  | #region Header | ||||||
|  | /** | ||||||
|  |  * ParserToken.cs | ||||||
|  |  *   Internal representation of the tokens used by the lexer and the parser. | ||||||
|  |  * | ||||||
|  |  * The authors disclaim copyright to this source code. For more details, see | ||||||
|  |  * the COPYING file included with this distribution. | ||||||
|  |  **/ | ||||||
|  | #endregion | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | namespace Guru.LitJson | ||||||
|  | { | ||||||
|  |     internal enum ParserToken | ||||||
|  |     { | ||||||
|  |         // Lexer tokens (see section A.1.1. of the manual) | ||||||
|  |         None = System.Char.MaxValue + 1, | ||||||
|  |         Number, | ||||||
|  |         True, | ||||||
|  |         False, | ||||||
|  |         Null, | ||||||
|  |         CharSeq, | ||||||
|  |         // Single char | ||||||
|  |         Char, | ||||||
|  | 
 | ||||||
|  |         // Parser Rules (see section A.2.1 of the manual) | ||||||
|  |         Text, | ||||||
|  |         Object, | ||||||
|  |         ObjectPrime, | ||||||
|  |         Pair, | ||||||
|  |         PairRest, | ||||||
|  |         Array, | ||||||
|  |         ArrayPrime, | ||||||
|  |         Value, | ||||||
|  |         ValueRest, | ||||||
|  |         String, | ||||||
|  | 
 | ||||||
|  |         // End of input | ||||||
|  |         End, | ||||||
|  | 
 | ||||||
|  |         // The empty rule | ||||||
|  |         Epsilon | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,11 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 6a046907ebdf54072ab870aa49b58f76 | ||||||
|  | MonoImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   serializedVersion: 2 | ||||||
|  |   defaultReferences: [] | ||||||
|  |   executionOrder: 0 | ||||||
|  |   icon: {instanceID: 0} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 3.3 KiB | 
|  | @ -0,0 +1,135 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: f3a96012ad0784fc7893f9b9eb05788e | ||||||
|  | TextureImporter: | ||||||
|  |   internalIDToNameTable: [] | ||||||
|  |   externalObjects: {} | ||||||
|  |   serializedVersion: 12 | ||||||
|  |   mipmaps: | ||||||
|  |     mipMapMode: 0 | ||||||
|  |     enableMipMap: 1 | ||||||
|  |     sRGBTexture: 1 | ||||||
|  |     linearTexture: 0 | ||||||
|  |     fadeOut: 0 | ||||||
|  |     borderMipMap: 0 | ||||||
|  |     mipMapsPreserveCoverage: 0 | ||||||
|  |     alphaTestReferenceValue: 0.5 | ||||||
|  |     mipMapFadeDistanceStart: 1 | ||||||
|  |     mipMapFadeDistanceEnd: 3 | ||||||
|  |   bumpmap: | ||||||
|  |     convertToNormalMap: 0 | ||||||
|  |     externalNormalMap: 0 | ||||||
|  |     heightScale: 0.25 | ||||||
|  |     normalMapFilter: 0 | ||||||
|  |   isReadable: 0 | ||||||
|  |   streamingMipmaps: 0 | ||||||
|  |   streamingMipmapsPriority: 0 | ||||||
|  |   vTOnly: 0 | ||||||
|  |   ignoreMasterTextureLimit: 0 | ||||||
|  |   grayScaleToAlpha: 0 | ||||||
|  |   generateCubemap: 6 | ||||||
|  |   cubemapConvolution: 0 | ||||||
|  |   seamlessCubemap: 0 | ||||||
|  |   textureFormat: 1 | ||||||
|  |   maxTextureSize: 2048 | ||||||
|  |   textureSettings: | ||||||
|  |     serializedVersion: 2 | ||||||
|  |     filterMode: 1 | ||||||
|  |     aniso: 1 | ||||||
|  |     mipBias: 0 | ||||||
|  |     wrapU: 0 | ||||||
|  |     wrapV: 0 | ||||||
|  |     wrapW: 0 | ||||||
|  |   nPOTScale: 1 | ||||||
|  |   lightmap: 0 | ||||||
|  |   compressionQuality: 50 | ||||||
|  |   spriteMode: 0 | ||||||
|  |   spriteExtrude: 1 | ||||||
|  |   spriteMeshType: 1 | ||||||
|  |   alignment: 0 | ||||||
|  |   spritePivot: {x: 0.5, y: 0.5} | ||||||
|  |   spritePixelsToUnits: 100 | ||||||
|  |   spriteBorder: {x: 0, y: 0, z: 0, w: 0} | ||||||
|  |   spriteGenerateFallbackPhysicsShape: 1 | ||||||
|  |   alphaUsage: 1 | ||||||
|  |   alphaIsTransparency: 0 | ||||||
|  |   spriteTessellationDetail: -1 | ||||||
|  |   textureType: 0 | ||||||
|  |   textureShape: 1 | ||||||
|  |   singleChannelComponent: 0 | ||||||
|  |   flipbookRows: 1 | ||||||
|  |   flipbookColumns: 1 | ||||||
|  |   maxTextureSizeSet: 0 | ||||||
|  |   compressionQualitySet: 0 | ||||||
|  |   textureFormatSet: 0 | ||||||
|  |   ignorePngGamma: 0 | ||||||
|  |   applyGammaDecoding: 0 | ||||||
|  |   cookieLightType: 0 | ||||||
|  |   platformSettings: | ||||||
|  |   - serializedVersion: 3 | ||||||
|  |     buildTarget: DefaultTexturePlatform | ||||||
|  |     maxTextureSize: 2048 | ||||||
|  |     resizeAlgorithm: 0 | ||||||
|  |     textureFormat: -1 | ||||||
|  |     textureCompression: 1 | ||||||
|  |     compressionQuality: 50 | ||||||
|  |     crunchedCompression: 0 | ||||||
|  |     allowsAlphaSplitting: 0 | ||||||
|  |     overridden: 0 | ||||||
|  |     androidETC2FallbackOverride: 0 | ||||||
|  |     forceMaximumCompressionQuality_BC6H_BC7: 0 | ||||||
|  |   - serializedVersion: 3 | ||||||
|  |     buildTarget: Standalone | ||||||
|  |     maxTextureSize: 2048 | ||||||
|  |     resizeAlgorithm: 0 | ||||||
|  |     textureFormat: -1 | ||||||
|  |     textureCompression: 1 | ||||||
|  |     compressionQuality: 50 | ||||||
|  |     crunchedCompression: 0 | ||||||
|  |     allowsAlphaSplitting: 0 | ||||||
|  |     overridden: 0 | ||||||
|  |     androidETC2FallbackOverride: 0 | ||||||
|  |     forceMaximumCompressionQuality_BC6H_BC7: 0 | ||||||
|  |   - serializedVersion: 3 | ||||||
|  |     buildTarget: Android | ||||||
|  |     maxTextureSize: 2048 | ||||||
|  |     resizeAlgorithm: 0 | ||||||
|  |     textureFormat: -1 | ||||||
|  |     textureCompression: 1 | ||||||
|  |     compressionQuality: 50 | ||||||
|  |     crunchedCompression: 0 | ||||||
|  |     allowsAlphaSplitting: 0 | ||||||
|  |     overridden: 0 | ||||||
|  |     androidETC2FallbackOverride: 0 | ||||||
|  |     forceMaximumCompressionQuality_BC6H_BC7: 0 | ||||||
|  |   - serializedVersion: 3 | ||||||
|  |     buildTarget: Server | ||||||
|  |     maxTextureSize: 2048 | ||||||
|  |     resizeAlgorithm: 0 | ||||||
|  |     textureFormat: -1 | ||||||
|  |     textureCompression: 1 | ||||||
|  |     compressionQuality: 50 | ||||||
|  |     crunchedCompression: 0 | ||||||
|  |     allowsAlphaSplitting: 0 | ||||||
|  |     overridden: 0 | ||||||
|  |     androidETC2FallbackOverride: 0 | ||||||
|  |     forceMaximumCompressionQuality_BC6H_BC7: 0 | ||||||
|  |   spriteSheet: | ||||||
|  |     serializedVersion: 2 | ||||||
|  |     sprites: [] | ||||||
|  |     outline: [] | ||||||
|  |     physicsShape: [] | ||||||
|  |     bones: [] | ||||||
|  |     spriteID:  | ||||||
|  |     internalID: 0 | ||||||
|  |     vertices: [] | ||||||
|  |     indices:  | ||||||
|  |     edges: [] | ||||||
|  |     weights: [] | ||||||
|  |     secondaryTextures: [] | ||||||
|  |     nameFileIdTable: {} | ||||||
|  |   spritePackingTag:  | ||||||
|  |   pSDRemoveMatte: 0 | ||||||
|  |   pSDShowRemoveMatteOption: 0 | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,100 @@ | ||||||
|  | # Guru Framework | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | **Version 2.1.0** | ||||||
|  | 
 | ||||||
|  | - 插件整体调整了文件结构,细分了相关模块的路径,添加了对应的 `asmdef` 文件 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | **Version 2.0.3** | ||||||
|  | 
 | ||||||
|  | - [升级须知](#notice) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## 依赖库 | ||||||
|  | 
 | ||||||
|  | ### Firebase | ||||||
|  | - 整体升级为 10.1.1 | ||||||
|  | 
 | ||||||
|  | ### AppLovin Max | ||||||
|  | - 整体升级为 11.11.3 | ||||||
|  | - 详细的广告Adapter版本, [详见这里](https://docs.google.com/spreadsheets/d/161UnDimGerqetIYNiMCfUBmJ7qozht8z1baxnxRdCnI) | ||||||
|  | 
 | ||||||
|  | --- | ||||||
|  | 
 | ||||||
|  | ## 子模块 | ||||||
|  | 
 | ||||||
|  | ### GuruCore | ||||||
|  | GuruSDK 核心逻辑类 | ||||||
|  | 
 | ||||||
|  | ### GuruAds | ||||||
|  | GuruSDK 封装了广告服务相关的接口 | ||||||
|  | - 新增了 Moloco 和 Pubmatic 两个渠道 | ||||||
|  | - 新增了ATTManager 用于管理ATT弹出和相关事件统计 | ||||||
|  | 
 | ||||||
|  | ### GuruAnalytics | ||||||
|  | Guru自打点统计模块 | ||||||
|  | - 更新了用户时长统计修复, 修复 Worker 启动报错的问题 | ||||||
|  | 
 | ||||||
|  | ### GuruConsent | ||||||
|  | 使用 Funding Choices 作为数据的启动广告隐私权限引导模块  | ||||||
|  | 
 | ||||||
|  | ### GuruBuildTool | ||||||
|  | 构建工具合集 | ||||||
|  | - 更新了SKADNetwork 数据 | ||||||
|  | - 更新 Xcode15 构建支持 | ||||||
|  | 
 | ||||||
|  | ### GuruIAP | ||||||
|  | 支付服务相关接口, 底层使用的是 Unity 的 In-App-Purchase 插件 | ||||||
|  | 
 | ||||||
|  | ### GuruEntry | ||||||
|  | 游戏入口模块 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ### GuruL10n | ||||||
|  | Guru的翻译模块, 内部衔接了 I2 Localization 插件, 外部衔接中台自动翻译接口 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ### GuruRating | ||||||
|  | 游戏评分模块 | ||||||
|  | 
 | ||||||
|  | ### Keywords | ||||||
|  | Max Keywords 上报模块 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | --- | ||||||
|  | 
 | ||||||
|  | <span id="notice"></span> | ||||||
|  | ## 升级须知 | ||||||
|  | 
 | ||||||
|  | ### Android  | ||||||
|  | 
 | ||||||
|  | - 需要在 `BuildSettings/Player Settings.../Publishing Settings` 内, 开启使用 `Custom Main Gradle Template` | ||||||
|  | - 可以直接使用中台提供的 `launcherTemplate.gradle` 文件 | ||||||
|  | - 或者在新生成的 `Assets/Plugins/Android/launcherTemplate.gradle` 内添加如下代码: | ||||||
|  |     ```groove | ||||||
|  |     android {     | ||||||
|  |         ... | ||||||
|  |          | ||||||
|  |         lintOptions { | ||||||
|  |             abortOnError false | ||||||
|  |             checkReleaseBuilds false // <---请添加此行代码 | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |          | ||||||
|  |         // 请将模版内的 **PACKAGING_OPTIONS** 替换为如下代码 | ||||||
|  |         packagingOptions { | ||||||
|  |             exclude("META-INF/*.kotlin_module") | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         ...    | ||||||
|  |     }     | ||||||
|  |     ``` | ||||||
|  | 
 | ||||||
|  | - Android 构建的最小 Target Version 为 21 | ||||||
|  | 
 | ||||||
|  | ### iOS | ||||||
|  | - 构建相关的升级已经提交至 BuildTools 内 | ||||||
|  | - 其他问题持续收集中 | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 9d4fe0abfc67449296f3992f2b823b78 | ||||||
|  | timeCreated: 1671010548 | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: f0c01eb685db4af99bbfa645d8307495 | ||||||
|  | timeCreated: 1671499352 | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 205f90aa56f64c759adf90d34e07fadf | ||||||
|  | timeCreated: 1702973536 | ||||||
|  | @ -0,0 +1,150 @@ | ||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using Firebase.RemoteConfig; | ||||||
|  | using Guru; | ||||||
|  | using Guru.LitJson; | ||||||
|  | using UnityEngine; | ||||||
|  | 
 | ||||||
|  | namespace Guru | ||||||
|  | { | ||||||
|  |      | ||||||
|  |      | ||||||
|  |     /// <summary> | ||||||
|  |     /// ABTEST 管理器 | ||||||
|  |     /// </summary> | ||||||
|  |     public class ABTestManager : Singleton<ABTestManager> | ||||||
|  |     { | ||||||
|  | 
 | ||||||
|  |         private FirebaseRemoteConfig _remoteConfig; | ||||||
|  | 
 | ||||||
|  |         private List<ABParamData> _params; | ||||||
|  | 
 | ||||||
|  |          | ||||||
|  |         #region 初始化 | ||||||
|  |          | ||||||
|  |         /// <summary> | ||||||
|  |         ///  初始化 | ||||||
|  |         /// </summary> | ||||||
|  |         public static void Init() | ||||||
|  |         { | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 Instance.Setup(); | ||||||
|  |             } | ||||||
|  |             catch (Exception e) | ||||||
|  |             { | ||||||
|  |                 Debug.LogError(e); | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         /// <summary> | ||||||
|  |         /// 安装服务 | ||||||
|  |         /// </summary> | ||||||
|  |         private void Setup() | ||||||
|  |         { | ||||||
|  |             Debug.Log($"[AB] --- <color=#88ff00>ABTest Init</color>"); | ||||||
|  |             _params = new List<ABParamData>(); | ||||||
|  |              | ||||||
|  |             _remoteConfig = FirebaseRemoteConfig.DefaultInstance; | ||||||
|  |              | ||||||
|  |             string strValue; | ||||||
|  |             foreach (var key in _remoteConfig.Keys) | ||||||
|  |             { | ||||||
|  |                 strValue = _remoteConfig.GetValue(key).StringValue; | ||||||
|  |                 AddParam(strValue); | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             // ------- ABTest ----------- | ||||||
|  |             // Debug.Log($"<color=orange> --- start parse test string --- </color>"); | ||||||
|  |             // var testStr = @"{""enabled"":true,""value"":2,""id"":""B"",""guru_ab_23100715"":""B""}"; | ||||||
|  |             // AddParam(testStr); | ||||||
|  |              | ||||||
|  |             if (_params.Count > 0) | ||||||
|  |             { | ||||||
|  |                 for (int i = 0; i < _params.Count; i++) | ||||||
|  |                 { | ||||||
|  |                     // 上报实验AB属性 | ||||||
|  |                     GuruAnalytics.SetUserProperty(_params[i].id, _params[i].group); | ||||||
|  | #if UNITY_EDITOR | ||||||
|  |                     Debug.Log($"[AB] --- Add AB Param <color=cyan>{_params[i].ToString()}</color>"); | ||||||
|  | #else | ||||||
|  |                     Debug.Log($"[AB] --- Add AB Param {_params[i].ToString()}"); | ||||||
|  | #endif | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |          | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         #endregion | ||||||
|  |          | ||||||
|  |         #region 添加AB参数 | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// 添加AB参数 | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="value"></param> | ||||||
|  |         private void AddParam(string value) | ||||||
|  |         { | ||||||
|  |             if (!string.IsNullOrEmpty(value) && value.Contains("guru_ab_")) | ||||||
|  |             { | ||||||
|  |                 _params.Add(ABParamData.Parse(value)); // 添加参数 | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         #endregion | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     [Serializable] | ||||||
|  |     internal class ABParamData | ||||||
|  |     { | ||||||
|  |         private const int PARAM_NAME_LENGTH = 23; // 从开始"ab_" 计算, 往后20个字符 | ||||||
|  |          | ||||||
|  |         public string id; | ||||||
|  |         public string group; | ||||||
|  |         public string value; | ||||||
|  |          | ||||||
|  |         public static ABParamData Parse(string value) | ||||||
|  |         { | ||||||
|  |             Debug.Log($"--- ABParamData.Parse: {value}"); | ||||||
|  |             var p = new ABParamData(); | ||||||
|  |             p.value = value; | ||||||
|  |              | ||||||
|  |             // 发现Guru AB测试标志位 | ||||||
|  |             var dict = JsonMapper.ToObject<Dictionary<string, object>>(value); | ||||||
|  |             if (null != dict) | ||||||
|  |             { | ||||||
|  |                 foreach (var k in dict.Keys) | ||||||
|  |                 { | ||||||
|  |                     if (k.StartsWith("guru_ab")) | ||||||
|  |                     { | ||||||
|  |                         p.id = GetItemKey(k); | ||||||
|  |                         p.group = dict[k].ToString(); | ||||||
|  |                         // Debug.Log($"[AB] add property {k}: {dict[k]}"); | ||||||
|  |                         break; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             return p; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static string GetItemKey(string raw) | ||||||
|  |         { | ||||||
|  |             var h = raw.Replace("guru_", ""); | ||||||
|  |             var key = h.Substring(0, Mathf.Min(PARAM_NAME_LENGTH, h.Length)); // 最大长度23 | ||||||
|  |             return key;  | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// 输出字符串 | ||||||
|  |         /// </summary> | ||||||
|  |         /// <returns></returns> | ||||||
|  |         public override string ToString() | ||||||
|  |         { | ||||||
|  |             return $"{id} : {group}"; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 9da3c10a9dde46499798d47cdd48fd70 | ||||||
|  | timeCreated: 1696650618 | ||||||
|  | @ -0,0 +1,46 @@ | ||||||
|  | 
 | ||||||
|  | # ABTest 中台接口 | ||||||
|  | 
 | ||||||
|  | - [相关实现技术文档链接](https://docs.google.com/document/d/1AG9PLq-dI0plIati2qgVpuD5QchNO2P8phY1GWr8SeQ/edit?pli=1#heading=h.906ruqltpzqz) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## 云控配置 | ||||||
|  | - 需要根据每个测试来配置对应的属性和ID | ||||||
|  | 
 | ||||||
|  |   ```javascript | ||||||
|  |   // 参数 KEY 构成 | ||||||
|  |   guru_ab_    +    231009   +     01 | ||||||
|  |       ^               ^            ^ | ||||||
|  |   固定前缀           年月日       实验序号  | ||||||
|  |    | ||||||
|  |   // 参数值构成 | ||||||
|  |   "A" 或 "B" | ||||||
|  |   "C" 或 "D" | ||||||
|  |     | ||||||
|  |   ``` | ||||||
|  |    | ||||||
|  | 
 | ||||||
|  | - 属性字段是追加在云控参数体内的 | ||||||
|  |     ```json | ||||||
|  |     // 云控的json 参数数据体 | ||||||
|  |     { | ||||||
|  |       "id": 1, | ||||||
|  |       "value": "test", | ||||||
|  |    | ||||||
|  |       "guru_ab_23100901": "A",   // 第一个实验的分组 | ||||||
|  |       "guru_ab_23100902": "C"    // 第二个实验的分组 | ||||||
|  |        | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     ``` | ||||||
|  | 
 | ||||||
|  | ## 结果验证 | ||||||
|  | 
 | ||||||
|  | - 对于已经切换了自打点的项目, 需要BI组配合项目组, 抽取每组实验用户的数据, 可形成有效报告 | ||||||
|  | - 项目接入后在启动时不会卡顿, 项目不会产生异常和崩溃 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: a1e7ab6ebd2348d99444732ad4f6422b | ||||||
|  | timeCreated: 1697167744 | ||||||
|  | @ -0,0 +1,30 @@ | ||||||
|  | { | ||||||
|  |     "name": "Guru.Runtime", | ||||||
|  |     "rootNamespace": "Guru", | ||||||
|  |     "references": [ | ||||||
|  |         "Adjust", | ||||||
|  |         "MaxSdk", | ||||||
|  |         "MaxSdk.Scripts", | ||||||
|  |         "Amazon", | ||||||
|  |         "OpenWrapSDK", | ||||||
|  |         "UniWebView-CSharp", | ||||||
|  |         "UnityEngine.Purchasing", | ||||||
|  |         "UnityEngine.Purchasing.Apple", | ||||||
|  |         "UnityEngine.Purchasing.Stores", | ||||||
|  |         "UnityEngine.Purchasing.Security", | ||||||
|  |         "UnityEngine.Purchasing.SecurityCore", | ||||||
|  |         "UnityEngine.Purchasing.SecurityStub", | ||||||
|  |         "Google.Play.Review", | ||||||
|  |         "Google.Play.Common", | ||||||
|  |         "Guru.LitJson" | ||||||
|  |     ], | ||||||
|  |     "includePlatforms": [], | ||||||
|  |     "excludePlatforms": [], | ||||||
|  |     "allowUnsafeCode": false, | ||||||
|  |     "overrideReferences": false, | ||||||
|  |     "precompiledReferences": [], | ||||||
|  |     "autoReferenced": true, | ||||||
|  |     "defineConstraints": [], | ||||||
|  |     "versionDefines": [], | ||||||
|  |     "noEngineReferences": false | ||||||
|  | } | ||||||
|  | @ -0,0 +1,7 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: e241d247f550f427da05939864204192 | ||||||
|  | AssemblyDefinitionImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,8 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: a66a5db145b064b588d9f4def3d72b12 | ||||||
|  | folderAsset: yes | ||||||
|  | DefaultImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 29a493ec2a4443b7a707065c40e88459 | ||||||
|  | timeCreated: 1694079394 | ||||||
|  | @ -0,0 +1,132 @@ | ||||||
|  | #if UNITY_IOS | ||||||
|  | 
 | ||||||
|  | namespace Guru | ||||||
|  | { | ||||||
|  |     using UnityEngine; | ||||||
|  |     using System; | ||||||
|  |     using Unity.Advertisement.IosSupport; | ||||||
|  |     public class ATTManager | ||||||
|  |     { | ||||||
|  |         public const string Version = "1.0.0"; | ||||||
|  | 
 | ||||||
|  |         public const string ATT_STATUS_AUTHORIZED = "authorized"; | ||||||
|  |         public const string ATT_STATUS_DENIED = "denied"; | ||||||
|  |         public const string ATT_STATUS_RESTRICTED = "restricted"; | ||||||
|  |         public const string ATT_STATUS_NOT_DETERMINED = "notDetermined"; | ||||||
|  |         public const string ATT_STATUS_NOT_APPLICABLE = "notApplicable"; | ||||||
|  |         public const int ATT_REQUIRED_MIN_OS = 14; | ||||||
|  |          | ||||||
|  |         //----------  引导类型 ------------ | ||||||
|  |         public const string GUIDE_TYPE_ADMOB = "admob"; | ||||||
|  |         public const string GUIDE_TYPE_CUSTOM = "custom"; | ||||||
|  |         public const string GUIDE_TYPE_MAX = "max"; | ||||||
|  |          | ||||||
|  |         /// <summary> | ||||||
|  |         /// 获取状态 | ||||||
|  |         /// </summary> | ||||||
|  |         /// <returns></returns> | ||||||
|  |         public static string GetStatus() | ||||||
|  |         { | ||||||
|  |             if (!IsATTSupported()) return ATT_STATUS_NOT_APPLICABLE; | ||||||
|  |             var status = GetStatusString(ATTrackingStatusBinding.GetAuthorizationTrackingStatus()); | ||||||
|  |             if(!string.IsNullOrEmpty(status)) return status; | ||||||
|  |             return ATT_STATUS_NOT_APPLICABLE; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// 转字符串 | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="status"></param> | ||||||
|  |         /// <returns></returns> | ||||||
|  |         public static string GetStatusString(ATTrackingStatusBinding.AuthorizationTrackingStatus status) | ||||||
|  |         { | ||||||
|  |             switch (status) | ||||||
|  |             { | ||||||
|  |                 case ATTrackingStatusBinding.AuthorizationTrackingStatus.NOT_DETERMINED: | ||||||
|  |                     return ATT_STATUS_NOT_DETERMINED; | ||||||
|  |                 case ATTrackingStatusBinding.AuthorizationTrackingStatus.AUTHORIZED: | ||||||
|  |                     return ATT_STATUS_AUTHORIZED; | ||||||
|  |                 case ATTrackingStatusBinding.AuthorizationTrackingStatus.DENIED: | ||||||
|  |                     return ATT_STATUS_DENIED; | ||||||
|  |                 case ATTrackingStatusBinding.AuthorizationTrackingStatus.RESTRICTED: | ||||||
|  |                     return ATT_STATUS_RESTRICTED; | ||||||
|  |             } | ||||||
|  |             return ""; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// 状态码转字符串 | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="value"></param> | ||||||
|  |         /// <returns></returns> | ||||||
|  |         public static string GetStatusString(int value)  | ||||||
|  |             => GetStatusString((ATTrackingStatusBinding.AuthorizationTrackingStatus)value); | ||||||
|  |          | ||||||
|  |         /// <summary> | ||||||
|  |         /// 是否支持ATT | ||||||
|  |         /// </summary> | ||||||
|  |         /// <returns></returns> | ||||||
|  |         private static bool IsATTSupported() | ||||||
|  |         { | ||||||
|  |             string version = UnityEngine.iOS.Device.systemVersion; | ||||||
|  |              | ||||||
|  |             Debug.Log($"[ATT] --- Get iOS system version: {version}"); | ||||||
|  | 
 | ||||||
|  |             string tmp = version; | ||||||
|  |             if (version.Contains(" ")) | ||||||
|  |             { | ||||||
|  |                 var a1 = version.Split(' '); | ||||||
|  |                 tmp = a1[a1.Length - 1]; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             string num = tmp; | ||||||
|  |             if (tmp.Contains(".")) | ||||||
|  |             { | ||||||
|  |                 num = tmp.Split('.')[0]; | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             if (int.TryParse(num, out var ver)) | ||||||
|  |             { | ||||||
|  |                 if (ver >= ATT_REQUIRED_MIN_OS) return true; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         /// <summary> | ||||||
|  |         /// 请求系统弹窗 | ||||||
|  |         /// </summary> | ||||||
|  |         public static void RequestATTDailog(Action<string> callback = null) | ||||||
|  |         { | ||||||
|  |             if (!IsATTSupported()) | ||||||
|  |             { | ||||||
|  |                 callback?.Invoke(ATT_STATUS_NOT_APPLICABLE); //  不支持 | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             ATTrackingStatusBinding.RequestAuthorizationTracking(status =>{ | ||||||
|  |                 callback?.Invoke(GetStatusString(status)); | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         /// <summary> | ||||||
|  |         /// 启动时检查状态 | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="callback"></param> | ||||||
|  |         public static void CheckStatus(Action<string> callback = null) | ||||||
|  |         { | ||||||
|  |             if (!IsATTSupported()) | ||||||
|  |             { | ||||||
|  |                 callback?.Invoke(ATT_STATUS_NOT_APPLICABLE); //  不支持 | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             callback?.Invoke(GetStatus()); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: c64a8943316547c88c9ace04dd5e3d05 | ||||||
|  | timeCreated: 1694079403 | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 367cb7699f154c8894d8c3ca85c1fe94 | ||||||
|  | timeCreated: 1681280112 | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 9bb6c50e89674152823d45b10ce535a2 | ||||||
|  | timeCreated: 1687238820 | ||||||
|  | @ -0,0 +1 @@ | ||||||
|  | -keep class com.amazon.device.ads.** { *; }  | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 8ce2781a43d0405d8481e54e2a8e5789 | ||||||
|  | timeCreated: 1687238837 | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: f0fcdcfefdeb4214bb75f2cc6a8356f8 | ||||||
|  | timeCreated: 1681280138 | ||||||
										
											Binary file not shown.
										
									
								
							|  | @ -0,0 +1,7 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: f5e96341271f7477280791fe0c9a4015 | ||||||
|  | DefaultImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,52 @@ | ||||||
|  | # GuruAds 扩展 Amazon安装说明 | ||||||
|  | 
 | ||||||
|  | ## Version 1.0.0 | ||||||
|  | 
 | ||||||
|  | ## 更新内容: | ||||||
|  | - 添加 Amazon 广告适配兼容 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## Amazon 使用说明 | ||||||
|  | 
 | ||||||
|  | ### 介绍 | ||||||
|  | - 本扩展基于 GuruCore.Ads.ADService `1.2.0` 开发, | ||||||
|  |   > 低于该版本的Guru框架请先升级至[ Guru 1.8.0 ](https://raw.githubusercontent.com/castbox/unity_gurucore/main/gurucore_1.8.0.unitypackage) | ||||||
|  | - 导入本扩展后, 需要先手动安装 APS 插件 | ||||||
|  |   > unitypackage 可从以下路径解压缩后自行安装: | ||||||
|  |   >    | ||||||
|  |   > Assets/Guru/GuruAds/Editor/Pcakge/unity_aps_1.4.3.zip | ||||||
|  |   >  | ||||||
|  | - 安装完毕后,请打开 GuruSettings 设置对应的 Amazon 广告配置参数 ,详见 `Amazon Setting` | ||||||
|  | 
 | ||||||
|  | ### 代码调用 | ||||||
|  | 
 | ||||||
|  | - 请直接使用 `ADServiceAPS` 来完成所有广告生命周期相关的功能, 它继承了 `IADService` | ||||||
|  | - 实际上 `ADServiceAPS` 继承自 `ADService`。 具备基础功能的同时实现了Amazon的广告请求逻辑。 | ||||||
|  | - 所有的广告申请, 加载和播放结果的回调同之前是一致的。 | ||||||
|  |   ```C# | ||||||
|  |      | ||||||
|  |     // 定义广告实例     | ||||||
|  |     var service = ADService.Instance; | ||||||
|  |      | ||||||
|  |     // 启动广告服务 | ||||||
|  |     bool isDebugMode = true; | ||||||
|  |     service.StartService(() => | ||||||
|  |     { | ||||||
|  |         //TODO 执行广告初始化成功后的逻辑 | ||||||
|  |     }, isDebugMode); | ||||||
|  | 
 | ||||||
|  |     // 显示 Banner | ||||||
|  |     service.ShowBanner(); | ||||||
|  |        | ||||||
|  |     // 显示 IV | ||||||
|  |     service.ShowInterstitialAD("level_end"); | ||||||
|  |        | ||||||
|  |     // 显示 RV | ||||||
|  |     service.ShowRewardedAD("add_free_coin", () => | ||||||
|  |     { | ||||||
|  |         UserData.Coin += 10; | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |   ``` | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 6f5bfcb26e384536ab6e134d2cad1c18 | ||||||
|  | timeCreated: 1680158911 | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 36da1246d638491bb601be91b8f18091 | ||||||
|  | timeCreated: 1681280144 | ||||||
|  | @ -0,0 +1,242 @@ | ||||||
|  | /********************************************** | ||||||
|  |  * Amazon 广告渠道 | ||||||
|  |  **********************************************/ | ||||||
|  | namespace Guru | ||||||
|  | { | ||||||
|  |     using AmazonAds; | ||||||
|  |     using UnityEngine; | ||||||
|  |     using System; | ||||||
|  | 
 | ||||||
|  |     internal struct AdSize | ||||||
|  |     { | ||||||
|  |         public int width; | ||||||
|  |         public int height; | ||||||
|  |     }  | ||||||
|  | 
 | ||||||
|  |     /// <summary> | ||||||
|  |     /// 广告渠道: Amazon | ||||||
|  |     /// </summary> | ||||||
|  |     public class AdChanelAmazon: IAdChannel, IAsyncRequestChannel | ||||||
|  |     { | ||||||
|  |         #region 广告尺寸 | ||||||
|  |          | ||||||
|  |         // Banner 尺寸参数 | ||||||
|  |         private static AdSize BannerSize = new AdSize() { width = 320, height = 50 }; | ||||||
|  |         // Video 尺寸参数 | ||||||
|  |         private static AdSize VideoSize = new AdSize() { width = 320, height = 480 }; | ||||||
|  |          | ||||||
|  |         #endregion | ||||||
|  |          | ||||||
|  |         #region SLOT IDS | ||||||
|  | 
 | ||||||
|  |         // ---------------  获取各种SlotID -------------------- | ||||||
|  |         private string AmazonBannerSlotID => GuruSettings.Instance.AmazonSetting.BannerSlotID; | ||||||
|  |         private string AmazonInterVideoSlotID => GuruSettings.Instance.AmazonSetting.InterSlotID; | ||||||
|  |         private string AmazonRewardSlotID => GuruSettings.Instance.AmazonSetting.RewardSlotID; | ||||||
|  |         private string AmazonAppID => GuruSettings.Instance.AmazonSetting.AppID; | ||||||
|  |          | ||||||
|  |         // ---------------- Max 广告位ID --------------------  | ||||||
|  |         private string MaxBannerSlotID => GuruSettings.Instance.ADSetting.GetBannerID(); | ||||||
|  |         private string MaxIVSlotID => GuruSettings.Instance.ADSetting.GetInterstitialID(); | ||||||
|  |         private string MaxRVSlotID => GuruSettings.Instance.ADSetting.GetRewardedVideoID(); | ||||||
|  |          | ||||||
|  |         #endregion | ||||||
|  | 
 | ||||||
|  |         #region Async 回调 | ||||||
|  | 
 | ||||||
|  |         public Action<bool, bool> OnBannerRequestOver { get; set; } | ||||||
|  |         public Action<bool, bool> OnInterstitialRequestOver { get; set; } | ||||||
|  |         public Action<bool, bool> OnRewardRequestOver { get; set; } | ||||||
|  |          | ||||||
|  |         #endregion | ||||||
|  | 
 | ||||||
|  |         #region 属性 | ||||||
|  |          | ||||||
|  |         public static readonly string ChanelName = "Amazon"; | ||||||
|  |         public string Name => ChanelName; | ||||||
|  |          | ||||||
|  |         public Action<string> OnRequestOver { get; set; } | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// 当前平台是否可用 | ||||||
|  |         /// </summary> | ||||||
|  |         public bool IsEnabled | ||||||
|  |         { | ||||||
|  |             get | ||||||
|  |             { | ||||||
|  | #if UNITY_EDITOR | ||||||
|  |                 return false;  | ||||||
|  | #endif | ||||||
|  |                 // return GuruSettings.Instance.AmazonSetting.Enable; | ||||||
|  |                 return true; // 常驻开启 | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         #endregion | ||||||
|  |          | ||||||
|  |         #region 初始化 | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// 初始化平台 | ||||||
|  |         /// </summary> | ||||||
|  |         public void Initialize() | ||||||
|  |         { | ||||||
|  | #if UNITY_EDITOR | ||||||
|  |             Debug.Log($"<color=orange>=== Amazon will not init on Editor ===</color>"); | ||||||
|  | #endif | ||||||
|  |             if (!IsEnabled) | ||||||
|  |             { | ||||||
|  |                  Debug.Log($"[Ads] --- Amazon is not enabled"); | ||||||
|  |                  return; | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             // 初始化Amazon | ||||||
|  |             Amazon.Initialize (AmazonAppID); | ||||||
|  |             Amazon.SetAdNetworkInfo(new AdNetworkInfo(DTBAdNetwork.MAX)); | ||||||
|  | #if UNITY_EDITOR || DEBUG  | ||||||
|  |             Amazon.EnableTesting (true); // Make sure to take this off when going live. | ||||||
|  | #else | ||||||
|  |             Amazon.EnableLogging (false); | ||||||
|  | #endif | ||||||
|  | 		 | ||||||
|  | #if UNITY_IOS | ||||||
|  |             Amazon.SetAPSPublisherExtendedIdFeatureEnabled(true); | ||||||
|  | #endif | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         #endregion | ||||||
|  |          | ||||||
|  |         #region Banner | ||||||
|  |          | ||||||
|  |         private APSBannerAdRequest bannerAdRequest; | ||||||
|  |         private bool _firstLoadBanner = true; | ||||||
|  |         public void LoadBannerAD() | ||||||
|  |         { | ||||||
|  |             if (!IsEnabled) return; | ||||||
|  |              | ||||||
|  |             Debug.Log($"--- Amazon banner start load ---"); | ||||||
|  |             if (bannerAdRequest != null) bannerAdRequest.DestroyFetchManager(); | ||||||
|  |             bannerAdRequest = new APSBannerAdRequest(BannerSize.width, BannerSize.height, AmazonBannerSlotID); | ||||||
|  |             bannerAdRequest.onSuccess += (adResponse) => | ||||||
|  |             { | ||||||
|  |                 Debug.Log($"--- Amazon Banner Load Success ---"); | ||||||
|  |                 MaxSdk.SetBannerLocalExtraParameter(MaxBannerSlotID,  | ||||||
|  |                     "amazon_ad_response",  | ||||||
|  |                     adResponse.GetResponse()); | ||||||
|  |                 OnBannerRequestOver?.Invoke(true, _firstLoadBanner); | ||||||
|  |                 _firstLoadBanner = false; | ||||||
|  |             }; | ||||||
|  |              | ||||||
|  |             bannerAdRequest.onFailedWithError += (adError) => | ||||||
|  |             { | ||||||
|  |                 Debug.Log($"--- Amazon Banner Load Fail: [{adError.GetCode()}] {adError.GetMessage()}"); | ||||||
|  |                 MaxSdk.SetBannerLocalExtraParameter(MaxBannerSlotID,  | ||||||
|  |                     "amazon_ad_error",  | ||||||
|  |                     adError.GetAdError()); | ||||||
|  |                 OnBannerRequestOver?.Invoke(false, _firstLoadBanner); | ||||||
|  |             }; | ||||||
|  |              | ||||||
|  |             bannerAdRequest.LoadAd(); | ||||||
|  | 
 | ||||||
|  | #if UNITY_EDITOR | ||||||
|  |             OnBannerRequestOver?.Invoke(true, false); | ||||||
|  | #endif | ||||||
|  |              | ||||||
|  |         } | ||||||
|  |         #endregion | ||||||
|  | 
 | ||||||
|  |         #region Intersitial | ||||||
|  | 
 | ||||||
|  |         private bool _isFirstLoadInter = true; | ||||||
|  |         private APSVideoAdRequest interstitialAdRequest; | ||||||
|  |          | ||||||
|  |         public void LoadInterstitialAD() | ||||||
|  |         { | ||||||
|  |             if (!IsEnabled) return; | ||||||
|  |              | ||||||
|  |             // 首次启动注入渠道参数 | ||||||
|  |             if (_isFirstLoadInter) | ||||||
|  |             { | ||||||
|  |                 Debug.Log($"--- Amazon INTER start load ---"); | ||||||
|  |                 interstitialAdRequest = new APSVideoAdRequest(VideoSize.width, VideoSize.height, AmazonInterVideoSlotID); | ||||||
|  |                 interstitialAdRequest.onSuccess += (adResponse) => | ||||||
|  |                 { | ||||||
|  |                     Debug.Log($"--- Amazon INTER Load Success ---"); | ||||||
|  |                     MaxSdk.SetInterstitialLocalExtraParameter(MaxIVSlotID,  | ||||||
|  |                         "amazon_ad_response", | ||||||
|  |                         adResponse.GetResponse()); | ||||||
|  |                     OnInterstitialRequestOver?.Invoke(true, true); | ||||||
|  |                     _isFirstLoadInter = false; | ||||||
|  |                 }; | ||||||
|  |                 interstitialAdRequest.onFailedWithError += (adError) => | ||||||
|  |                 { | ||||||
|  |                     Debug.Log($"--- Amazon INTER Load Fail: [{adError.GetCode()}] {adError.GetMessage()}"); | ||||||
|  |                     MaxSdk.SetInterstitialLocalExtraParameter(MaxIVSlotID,  | ||||||
|  |                         "amazon_ad_error",  | ||||||
|  |                         adError.GetAdError()); | ||||||
|  |                     OnInterstitialRequestOver?.Invoke(false, true); // 不成功则一直请求Amazon广告 | ||||||
|  |                 }; | ||||||
|  |                 interstitialAdRequest.LoadAd(); | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 OnInterstitialRequestOver?.Invoke(true, false); // 走默认的广告加载逻辑 | ||||||
|  |             } | ||||||
|  |              | ||||||
|  | #if UNITY_EDITOR | ||||||
|  |             OnInterstitialRequestOver?.Invoke(true, false); | ||||||
|  |             _isFirstLoadInter = false; | ||||||
|  | #endif | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |          | ||||||
|  | 
 | ||||||
|  |         #endregion | ||||||
|  |          | ||||||
|  |         #region Reward | ||||||
|  |          | ||||||
|  |         private bool _firstLoadRewward = true; | ||||||
|  |         private APSVideoAdRequest rewardedVideoAdRequest; | ||||||
|  |          | ||||||
|  |         public void LoadRewardAD() | ||||||
|  |         { | ||||||
|  |             if (!IsEnabled) return; | ||||||
|  |              | ||||||
|  |             if (_firstLoadRewward) | ||||||
|  |             { | ||||||
|  |                 Debug.Log($"--- Amazon Reward start load ---"); | ||||||
|  |                 rewardedVideoAdRequest = new APSVideoAdRequest(VideoSize.width, VideoSize.height, AmazonRewardSlotID); | ||||||
|  |                 rewardedVideoAdRequest.onSuccess += (adResponse) => | ||||||
|  |                 { | ||||||
|  |                     Debug.Log($"--- Amazon Reward Load Success ---"); | ||||||
|  |                     MaxSdk.SetRewardedAdLocalExtraParameter(MaxRVSlotID,  | ||||||
|  |                         "amazon_ad_response", | ||||||
|  |                         adResponse.GetResponse()); | ||||||
|  |                     OnRewardRequestOver?.Invoke(true, true); | ||||||
|  |                     _firstLoadRewward = false; | ||||||
|  |                 }; | ||||||
|  |                 rewardedVideoAdRequest.onFailedWithError += (adError) => | ||||||
|  |                 { | ||||||
|  |                     Debug.Log($"--- Amazon Reward Load Fail: [{adError.GetCode()}] {adError.GetMessage()}"); | ||||||
|  |                     MaxSdk.SetRewardedAdLocalExtraParameter(MaxRVSlotID,  | ||||||
|  |                         "amazon_ad_error",  | ||||||
|  |                         adError.GetAdError()); | ||||||
|  |                     OnRewardRequestOver?.Invoke(false, true);  // 不成功则一直请求Amazon广告 | ||||||
|  |                 }; | ||||||
|  |                 rewardedVideoAdRequest.LoadAd(); | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 OnRewardRequestOver?.Invoke(true, false); // 走默认的广告加载逻辑 | ||||||
|  |             } | ||||||
|  | #if UNITY_EDITOR | ||||||
|  |             OnRewardRequestOver?.Invoke(true, false); | ||||||
|  | #endif | ||||||
|  |         } | ||||||
|  |          | ||||||
|  | 
 | ||||||
|  |         #endregion | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  |      | ||||||
|  | } | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 9c01d5cecc9140c8a4a9e98a983299a2 | ||||||
|  | timeCreated: 1681280193 | ||||||
|  | @ -0,0 +1,57 @@ | ||||||
|  | namespace Guru | ||||||
|  | { | ||||||
|  |     using System; | ||||||
|  |     using UnityEngine; | ||||||
|  | 
 | ||||||
|  |     public partial class GuruSettings | ||||||
|  |     { | ||||||
|  |         [Header("Amazon 广告配置")] | ||||||
|  |         public AmazonSetting AmazonSetting; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |      | ||||||
|  |     /// <summary> | ||||||
|  |     /// Amazon广告配置 | ||||||
|  |     /// </summary> | ||||||
|  |     [Serializable] | ||||||
|  |     public class AmazonSetting | ||||||
|  |     { | ||||||
|  | 
 | ||||||
|  |         [SerializeField] public bool Enable; | ||||||
|  |         [SerializeField] private AmazonPlatformSetting Android; | ||||||
|  |         [SerializeField] private AmazonPlatformSetting iOS; | ||||||
|  | 		 | ||||||
|  |         /// <summary> | ||||||
|  |         /// 获取AppID | ||||||
|  |         /// </summary> | ||||||
|  |         /// <returns></returns> | ||||||
|  |         public AmazonPlatformSetting GetPlatform() | ||||||
|  |         { | ||||||
|  | #if UNITY_IOS | ||||||
|  | 			return iOS;  | ||||||
|  | #else | ||||||
|  |             return Android; | ||||||
|  | #endif  | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public string AppID => GetPlatform().appID; | ||||||
|  |         public string BannerSlotID => GetPlatform().bannerSlotID; | ||||||
|  |         public string InterSlotID => GetPlatform().interSlotID; | ||||||
|  |         public string RewardSlotID => GetPlatform().rewardSlotID; | ||||||
|  |              | ||||||
|  |     } | ||||||
|  |          | ||||||
|  |          | ||||||
|  |     /// <summary> | ||||||
|  |     /// Amazon平台专属配置 | ||||||
|  |     /// </summary> | ||||||
|  |     [Serializable] | ||||||
|  |     public class AmazonPlatformSetting | ||||||
|  |     { | ||||||
|  |         // public string name;		// 平台名称 | ||||||
|  |         public string appID;		// AppID | ||||||
|  |         public string bannerSlotID;	// Banner ID | ||||||
|  |         public string interSlotID;	// Inter ID | ||||||
|  |         public string rewardSlotID;	// Reward ID | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 074cbc5efb0c40d4856e20904a9b434d | ||||||
|  | timeCreated: 1681281263 | ||||||
|  | @ -0,0 +1,8 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: a44918376993443edbe32c4ae267ad11 | ||||||
|  | folderAsset: yes | ||||||
|  | DefaultImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 23cd1489a0b9410ea9e6e2f410926351 | ||||||
|  | timeCreated: 1689144991 | ||||||
										
											Binary file not shown.
										
									
								
							|  | @ -0,0 +1,7 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 31dd283a9317e403e9eb07eba271e9c7 | ||||||
|  | DefaultImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: f369f6d7d0c6425d88a3b8ec6ec1b4fc | ||||||
|  | timeCreated: 1689145012 | ||||||
|  | @ -0,0 +1,53 @@ | ||||||
|  | using System; | ||||||
|  | using UnityEngine; | ||||||
|  | 
 | ||||||
|  | namespace Guru | ||||||
|  | { | ||||||
|  |     public partial class GuruSettings | ||||||
|  |     { | ||||||
|  |         [Header("[ Moloco ] 测试广告配置")] [Tooltip("此配置只在测试阶段验证广告时使用")]
 | ||||||
|  |         public MolocoSetting MolocoSetting; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// <summary> | ||||||
|  |     /// Moloco 广告配置 | ||||||
|  |     /// </summary> | ||||||
|  |     [Serializable] | ||||||
|  |     public class MolocoSetting | ||||||
|  |     { | ||||||
|  |         [SerializeField] public bool Enable; | ||||||
|  |         [SerializeField] private MolocoPlatformSetting Android; | ||||||
|  |         [SerializeField] private MolocoPlatformSetting iOS; | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// 获取AppID | ||||||
|  |         /// </summary> | ||||||
|  |         /// <returns></returns> | ||||||
|  |         public MolocoPlatformSetting GetPlatform() | ||||||
|  |         { | ||||||
|  | #if UNITY_IOS | ||||||
|  | 			return iOS; | ||||||
|  | #else | ||||||
|  |             return Android; | ||||||
|  | #endif | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public string BannerTestUnitID => GetPlatform().bannerTestUnitID; | ||||||
|  |         public string InterTestUnitID => GetPlatform().interTestUnitID; | ||||||
|  |         public string RewardTestUnitID => GetPlatform().rewardTestUnitID; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// Moloco 平台专属配置 | ||||||
|  |         /// </summary> | ||||||
|  |         [Serializable] | ||||||
|  |         public class MolocoPlatformSetting | ||||||
|  |         { | ||||||
|  |             // public string name;		// 平台名称 | ||||||
|  |             public string bannerTestUnitID; // Banner ID | ||||||
|  |             public string interTestUnitID; // Inter ID | ||||||
|  |             public string rewardTestUnitID; // Reward ID | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 73843b3931f04824be84b4adcefdbb94 | ||||||
|  | timeCreated: 1689145037 | ||||||
|  | @ -0,0 +1,211 @@ | ||||||
|  | using UnityEngine; | ||||||
|  | 
 | ||||||
|  | namespace Guru | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Moloco 测试接口 | ||||||
|  |     /// </summary> | ||||||
|  |     public class MolocoTestAPI | ||||||
|  |     { | ||||||
|  |          | ||||||
|  | 
 | ||||||
|  |         #region 初始化 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         #endregion | ||||||
|  |          | ||||||
|  |         #region Banner | ||||||
|  | 
 | ||||||
|  |         private static string _testBannerId; | ||||||
|  |         private static bool _isLoadingBanner = false; | ||||||
|  |         /// <summary> | ||||||
|  |         /// 请求测试广告Ba | ||||||
|  |         /// </summary> | ||||||
|  |         public static void LoadDebugBanner(string bannerId) | ||||||
|  |         { | ||||||
|  |             if (_isLoadingBanner) return; | ||||||
|  |             _isLoadingBanner = true; | ||||||
|  |             _testBannerId = bannerId; | ||||||
|  |             AddBannerCallBacks(); | ||||||
|  |             MaxSdk.CreateBanner(_testBannerId, MaxSdkBase.BannerPosition.BottomCenter); | ||||||
|  |             ShowToast($"Load Banner: {_testBannerId}"); | ||||||
|  |         } | ||||||
|  |         private static void AddBannerCallBacks() | ||||||
|  |         { | ||||||
|  |             MaxSdkCallbacks.Banner.OnAdLoadedEvent += OnDebugBannerLoadSuccess; | ||||||
|  |             MaxSdkCallbacks.Banner.OnAdLoadFailedEvent += OnDebugBannerLoadFailed; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         private static void RemoveBannerCallBacks() | ||||||
|  |         { | ||||||
|  |             MaxSdkCallbacks.Banner.OnAdLoadedEvent -= OnDebugBannerLoadSuccess; | ||||||
|  |             MaxSdkCallbacks.Banner.OnAdLoadFailedEvent -= OnDebugBannerLoadFailed; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static void OnDebugBannerLoadSuccess(string adUnitId, MaxSdkBase.AdInfo adInfo) | ||||||
|  |         { | ||||||
|  |             if (adUnitId == _testBannerId) | ||||||
|  |             { | ||||||
|  |                 RemoveBannerCallBacks(); | ||||||
|  |                 Debug.Log($"[PM] Load Banner success => Revenue: {adInfo.Revenue}"); | ||||||
|  |                 MaxSdk.ShowBanner(adUnitId); | ||||||
|  |                 _isLoadingBanner = false; | ||||||
|  |                  | ||||||
|  |                 ShowToast($"Banner Loaded: {_testBannerId}"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static void OnDebugBannerLoadFailed(string adUnitId, MaxSdkBase.ErrorInfo errorInfo) | ||||||
|  |         { | ||||||
|  |             if (adUnitId == _testBannerId) | ||||||
|  |             { | ||||||
|  |                 RemoveBannerCallBacks(); | ||||||
|  |                 Debug.Log($"[PM] Load Banner fail => Waterfall:{errorInfo.WaterfallInfo.Name}"); | ||||||
|  |                 _isLoadingBanner = false; | ||||||
|  |                  | ||||||
|  |                 ShowToast($"Banner Load fail: {_testBannerId}"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         #endregion | ||||||
|  |          | ||||||
|  |         #region Interstitial | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         private static bool _isLoadingIV = false; | ||||||
|  |         private static string _testIVId; | ||||||
|  |          | ||||||
|  |         /// <summary> | ||||||
|  |         /// 请求测试广告IV | ||||||
|  |         /// </summary> | ||||||
|  |         public static void LoadDebugIV(string unitId) | ||||||
|  |         { | ||||||
|  |             if (_isLoadingIV) return; | ||||||
|  |             _isLoadingIV = true; | ||||||
|  |             _testIVId = unitId; | ||||||
|  |             AddIvCallBacks(); | ||||||
|  |             MaxSdk.LoadInterstitial(_testIVId); | ||||||
|  |              | ||||||
|  |             ShowToast($"Load Interstitial: {_testIVId}"); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         private static void AddIvCallBacks() | ||||||
|  |         { | ||||||
|  |             MaxSdkCallbacks.Interstitial.OnAdLoadedEvent += OnDebugIVLoadSuccess; | ||||||
|  |             MaxSdkCallbacks.Interstitial.OnAdLoadFailedEvent += OnDebugIVLoadFailed; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         private static void RemoveIvCallBacks() | ||||||
|  |         { | ||||||
|  |             MaxSdkCallbacks.Interstitial.OnAdLoadedEvent -= OnDebugIVLoadSuccess; | ||||||
|  |             MaxSdkCallbacks.Interstitial.OnAdLoadFailedEvent -= OnDebugIVLoadFailed; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static void OnDebugIVLoadSuccess(string adUnitId, MaxSdkBase.AdInfo adInfo) | ||||||
|  |         { | ||||||
|  |             if (adUnitId == _testIVId) | ||||||
|  |             { | ||||||
|  |                 RemoveIvCallBacks(); | ||||||
|  |                 _isLoadingIV = false; | ||||||
|  |                 Debug.Log($"[PM] Load IV success => Revenue: {adInfo.Revenue}"); | ||||||
|  |                 string placement = "pm_test_iv"; | ||||||
|  |                 MaxSdk.ShowInterstitial(adUnitId, placement); | ||||||
|  |                 ShowToast($"Load IV Success: {_testIVId}"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static void OnDebugIVLoadFailed(string adUnitId, MaxSdkBase.ErrorInfo errorInfo) | ||||||
|  |         { | ||||||
|  |             if (adUnitId == _testIVId) | ||||||
|  |             { | ||||||
|  |                 RemoveIvCallBacks(); | ||||||
|  |                 _isLoadingIV = false; | ||||||
|  |                 Debug.Log($"[PM] Load IV fail => Waterfall:{errorInfo.WaterfallInfo.Name}"); | ||||||
|  |                 ShowToast($"Load IV Fail: {_testIVId}"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         #endregion | ||||||
|  | 
 | ||||||
|  |         #region Reward | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         private static string _testRVId; | ||||||
|  |         private static bool _isLoadingRV = false; | ||||||
|  |         /// <summary> | ||||||
|  |         /// 请求测试广告RV | ||||||
|  |         /// </summary> | ||||||
|  |         public static void LoadDebugRV(string unitId) | ||||||
|  |         { | ||||||
|  |             if (_isLoadingRV) return; | ||||||
|  |             _isLoadingRV = true; | ||||||
|  |             _testRVId = unitId; | ||||||
|  |             AddRvCallBacks(); | ||||||
|  |             MaxSdk.LoadRewardedAd(_testRVId); | ||||||
|  |              | ||||||
|  |             ShowToast($"Load RV: {_testRVId}"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static void AddRvCallBacks() | ||||||
|  |         { | ||||||
|  |             MaxSdkCallbacks.Rewarded.OnAdLoadedEvent += OnDebugRVLoadSuccess; | ||||||
|  |             MaxSdkCallbacks.Rewarded.OnAdLoadFailedEvent += OnDebugRVLoadFailed; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         private static void RemoveRvCallBacks() | ||||||
|  |         { | ||||||
|  |             MaxSdkCallbacks.Rewarded.OnAdLoadedEvent -= OnDebugRVLoadSuccess; | ||||||
|  |             MaxSdkCallbacks.Rewarded.OnAdLoadFailedEvent -= OnDebugRVLoadFailed; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         private static void OnDebugRVLoadSuccess(string adUnitId, MaxSdkBase.AdInfo adInfo) | ||||||
|  |         { | ||||||
|  |             if (adUnitId == _testRVId) | ||||||
|  |             { | ||||||
|  |                 RemoveRvCallBacks(); | ||||||
|  |                 _isLoadingRV = false; | ||||||
|  |                 Debug.Log($"[PM] Load RV success => Revenue: {adInfo.Revenue}"); | ||||||
|  |                 string placement = "pm_test_rv"; | ||||||
|  |                 MaxSdk.ShowRewardedAd(adUnitId, placement); | ||||||
|  |                 ShowToast($"Load RV Success: {_testRVId}"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static void OnDebugRVLoadFailed(string adUnitId, MaxSdkBase.ErrorInfo errorInfo) | ||||||
|  |         { | ||||||
|  |             if (adUnitId == _testRVId) | ||||||
|  |             { | ||||||
|  |                 RemoveRvCallBacks(); | ||||||
|  |                 _isLoadingRV = false; | ||||||
|  |                 Debug.Log($"[PM] Load RV fail => Waterfall:{errorInfo.WaterfallInfo.Name}"); | ||||||
|  |                 ShowToast($"Load RV Fail: {_testRVId}"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         #endregion | ||||||
|  |          | ||||||
|  |         #region Debug | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// 显示Toast信息 | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="msg"></param> | ||||||
|  |         public static void ShowToast(string msg) | ||||||
|  |         { | ||||||
|  | #if UNITY_ANDROID    | ||||||
|  |             // U3D2Android.ShowToast(msg); | ||||||
|  | #else | ||||||
|  |             Debug.Log(msg); | ||||||
|  | #endif | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         #endregion | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 92c8ebc4f05c4729b6ef6a6bae5a34b7 | ||||||
|  | timeCreated: 1689147226 | ||||||
|  | @ -0,0 +1,8 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 476080445a424f538e3295b6d0c39c37 | ||||||
|  | folderAsset: yes | ||||||
|  | DefaultImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,8 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 4f27758688594110b42e5d6cc9368f2b | ||||||
|  | folderAsset: yes | ||||||
|  | DefaultImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1 @@ | ||||||
|  | -keep class com.pubmatic.sdk.** { *; } | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 337880599b034517bf8ff594099c5c1d | ||||||
|  | timeCreated: 1687158363 | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: c6fc887e2efc448b85609ea99608acdd | ||||||
|  | timeCreated: 1681289567 | ||||||
										
											Binary file not shown.
										
									
								
							|  | @ -0,0 +1,7 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 3a5b29c0662724638bd2fb91e3a98972 | ||||||
|  | DefaultImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,31 @@ | ||||||
|  | # PubMatic 广告插件 | ||||||
|  | 
 | ||||||
|  | ## Version 1.0.0 | ||||||
|  | 
 | ||||||
|  | 使用方法: | ||||||
|  | - 先解压缩Package内的zip文档 | ||||||
|  | - 导入两个unitypackage文件 | ||||||
|  | - 将 PubMatic 对应的的 AdChannelPubMatic.cs 文件导入项目 | ||||||
|  | - 在 AdService 内注册AdChannelPubMatic. (需要升级 ADService ) | ||||||
|  | - 在 PlayerSettings 内添加 `AD_PUBMATIC` 的宏 | ||||||
|  | - 在 proguard-user.txt 内添加下面的代码: | ||||||
|  |     ```yaml | ||||||
|  |     # PubMatic  | ||||||
|  |     -keep class com.pubmatic.** { *; } | ||||||
|  |     ``` | ||||||
|  | 
 | ||||||
|  | 广告申请和展示时按照正常的 MAX 广告申请接口调用 | ||||||
|  |   | ||||||
|  | 测试时, 需要使用专用的Debug接口直接拉起 PubMatic 测试广告 | ||||||
|  | ```csharp | ||||||
|  | 
 | ||||||
|  |     Debug.Log($"--- 显示 PubMatic Banner ---"); | ||||||
|  |     AdChannelPubMatic.RequestDebugBanner(); | ||||||
|  | 
 | ||||||
|  |     Debug.Log($"--- 显示 PubMatic IV ---"); | ||||||
|  |     AdChannelPubMatic.LoadDebugIV(); | ||||||
|  | 
 | ||||||
|  |     Debug.Log($"--- 显示 PubMatic RV ---"); | ||||||
|  |     AdChannelPubMatic.RequestDebugRV(); | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 40bcecbb98df437bae363dfc4956bed4 | ||||||
|  | timeCreated: 1681451207 | ||||||
|  | @ -0,0 +1,8 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 22a8f46f2a1cc4ebd830e5dd9ec2a898 | ||||||
|  | folderAsset: yes | ||||||
|  | DefaultImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,370 @@ | ||||||
|  | /********************************************** | ||||||
|  |  * Pubmatic 广告渠道 | ||||||
|  |  **********************************************/ | ||||||
|  | namespace Guru | ||||||
|  | { | ||||||
|  |     using OpenWrapSDK; | ||||||
|  |     using System; | ||||||
|  |     using OpenWrapSDK.Mediation.AppLovinMAX; | ||||||
|  |     using UnityEngine; | ||||||
|  |      | ||||||
|  |     public class AdChannelPubMatic: IAdChannel | ||||||
|  |     { | ||||||
|  |         #region 属性定义 | ||||||
|  |          | ||||||
|  |         public static readonly string ChanelName = "PubMatic"; | ||||||
|  |         public string Name => ChanelName; | ||||||
|  |          | ||||||
|  |         public Action<string> OnRequestOver { get; set; } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         // ---------------  获取各种SlotID -------------------- | ||||||
|  |         private static string PMBannerUnitID => GuruSettings.Instance.PubmaticSetting.BannerUnitID; | ||||||
|  |         private static string PMInterUnitID => GuruSettings.Instance.PubmaticSetting.InterUnitID; | ||||||
|  |         private static string PMRewardUnitID => GuruSettings.Instance.PubmaticSetting.RewardUnitID; | ||||||
|  |         private static string PMStoreUrl => GuruSettings.Instance.PubmaticSetting.StoreUrl; | ||||||
|  | 
 | ||||||
|  |         public static readonly int BidRequestTimeout = 5; // 请求超时 (秒) | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// 当前平台是否可用 | ||||||
|  |         /// </summary> | ||||||
|  |         public bool IsEnabled | ||||||
|  |         { | ||||||
|  |             get | ||||||
|  |             { | ||||||
|  | #if UNITY_EDITOR | ||||||
|  |                 return false;  | ||||||
|  | #endif | ||||||
|  |                 // return GuruSettings.Instance.PubmaticSetting.Enable; | ||||||
|  |                 return true; // 常驻开启 | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         #endregion | ||||||
|  |          | ||||||
|  |         #region 初始化 | ||||||
|  | 
 | ||||||
|  |         /* | ||||||
|  |          * You must set the App Store/Google Play Store storeURL of your app | ||||||
|  |          * before it can request an ad using OpenWrap SDK. | ||||||
|  |          * The storeURL is the URL where users can download your app from the App Store/Google Play Store. | ||||||
|  |          */ | ||||||
|  |         public void Initialize() | ||||||
|  |         { | ||||||
|  | #if UNITY_EDITOR | ||||||
|  |                 Debug.Log($"<color=orange>=== PubMatic will not init on Editor ===</color>"); | ||||||
|  | #endif | ||||||
|  |             if (!IsEnabled) | ||||||
|  |             { | ||||||
|  |                 Debug.Log($"[Ads] --- PubMatic is not enabled"); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             var appInfo = new POBApplicationInfo(); | ||||||
|  |             appInfo.StoreURL = new Uri(PMStoreUrl); | ||||||
|  |             POBOpenWrapSDK.SetApplicationInfo(appInfo); | ||||||
|  |         } | ||||||
|  |       | ||||||
|  |         #endregion | ||||||
|  |          | ||||||
|  |         #region 基础参数设置 | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// 设置Banner参数 | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="adUnitId"></param> | ||||||
|  |         /// <param name="key"></param> | ||||||
|  |         /// <param name="value"></param> | ||||||
|  |         private static void SetBannerParams(string adUnitId, string key, object value) | ||||||
|  |         { | ||||||
|  | #if UNITY_IOS | ||||||
|  |             MaxSdk.SetBannerLocalExtraParameter(adUnitId,  | ||||||
|  |                 key,  | ||||||
|  |                 POBMAXUtil.GetIntPtr(adUnitId, value)); | ||||||
|  | #elif UNITY_ANDROID | ||||||
|  |             MaxSdk.SetBannerLocalExtraParameter(adUnitId,  | ||||||
|  |                 key,  | ||||||
|  |                 POBMAXUtil.GetAndroidJavaObject(value)); | ||||||
|  | #endif | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         /// <summary> | ||||||
|  |         /// 设置IV参数 | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="adUnitId"></param> | ||||||
|  |         /// <param name="key"></param> | ||||||
|  |         /// <param name="value"></param> | ||||||
|  |         private static void SetIVParams(string adUnitId, string key, object value) | ||||||
|  |         { | ||||||
|  | #if UNITY_IOS | ||||||
|  |             MaxSdk.SetInterstitialLocalExtraParameter(adUnitId,  | ||||||
|  |                 key,  | ||||||
|  |                 POBMAXUtil.GetIntPtr(adUnitId, value)); | ||||||
|  | #elif UNITY_ANDROID | ||||||
|  |             MaxSdk.SetInterstitialLocalExtraParameter(adUnitId,  | ||||||
|  |                 key,  | ||||||
|  |                 POBMAXUtil.GetAndroidJavaObject(value)); | ||||||
|  | #endif | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         /// <summary> | ||||||
|  |         /// 设置RV参数 | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="adUnitId"></param> | ||||||
|  |         /// <param name="key"></param> | ||||||
|  |         /// <param name="value"></param> | ||||||
|  |         private static void SetRVParams(string adUnitId, string key, object value) | ||||||
|  |         { | ||||||
|  | #if UNITY_IOS | ||||||
|  |             MaxSdk.SetRewardedAdLocalExtraParameter(adUnitId,  | ||||||
|  |                 key,  | ||||||
|  |                 POBMAXUtil.GetIntPtr(adUnitId, value)); | ||||||
|  | #elif UNITY_ANDROID | ||||||
|  |             MaxSdk.SetRewardedAdLocalExtraParameter(adUnitId,  | ||||||
|  |                 key,  | ||||||
|  |                 POBMAXUtil.GetAndroidJavaObject(value)); | ||||||
|  | #endif | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |          | ||||||
|  |         //---------------- 设置广告超时 ------------------------- | ||||||
|  | 
 | ||||||
|  |         private void SetBannerTimeout(string adUnitId, int timeout = 5) | ||||||
|  |         { | ||||||
|  |             SetBannerParams(adUnitId, POBMAXConstants.NetworkTimeoutKey, timeout); | ||||||
|  |         } | ||||||
|  |         private void SetIVTimeout(string adUnitId, int timeout = 5) | ||||||
|  |         { | ||||||
|  |             SetIVParams(adUnitId, POBMAXConstants.NetworkTimeoutKey, timeout); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         private void SetRVTimeout(string adUnitId, int timeout = 5) | ||||||
|  |         { | ||||||
|  |             SetRVParams(adUnitId, POBMAXConstants.NetworkTimeoutKey, timeout); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         //------------------ 打开测试广告 ---------------------------- | ||||||
|  | 
 | ||||||
|  |         private void EnableBannerTestAds(string adUnitId) | ||||||
|  |         { | ||||||
|  |             SetBannerParams(adUnitId, POBMAXConstants.EnableTestModeKey, true); | ||||||
|  |         } | ||||||
|  |         private void EnableIVTestAds(string adUnitId) | ||||||
|  |         { | ||||||
|  |             SetIVParams(adUnitId, POBMAXConstants.EnableTestModeKey, true); | ||||||
|  |         } | ||||||
|  |         private void EnableRVTestAds(string adUnitId) | ||||||
|  |         { | ||||||
|  |             SetRVParams(adUnitId, POBMAXConstants.EnableTestModeKey, true); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         //------------------- 打开调试模式 --------------------------- | ||||||
|  |         private static void EnableBannerDebugMode(string adUnitId) | ||||||
|  |         { | ||||||
|  |             SetBannerParams(adUnitId, POBMAXConstants.EnableDebugModeKey, true); | ||||||
|  |         } | ||||||
|  |         private static void EnableIVDebugMode(string adUnitId) | ||||||
|  |         { | ||||||
|  |             SetIVParams(adUnitId, POBMAXConstants.EnableDebugModeKey, true); | ||||||
|  |         } | ||||||
|  |         private static void EnableRVDebugMode(string adUnitId) | ||||||
|  |         { | ||||||
|  |             SetRVParams(adUnitId, POBMAXConstants.EnableDebugModeKey, true); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         #endregion | ||||||
|  |          | ||||||
|  |         #region Banner | ||||||
|  | 
 | ||||||
|  |         public void LoadBannerAD() | ||||||
|  |         { | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         private static bool _isLoadingBanner = false; | ||||||
|  |         /// <summary> | ||||||
|  |         /// 请求测试广告Ba | ||||||
|  |         /// </summary> | ||||||
|  |         public static void RequestDebugBanner() | ||||||
|  |         { | ||||||
|  |             if (_isLoadingBanner) return; | ||||||
|  |             _isLoadingBanner = true; | ||||||
|  |             EnableBannerDebugMode(PMBannerUnitID); | ||||||
|  |             AddBannerCallBacks(); | ||||||
|  |             MaxSdk.CreateBanner(PMBannerUnitID, MaxSdkBase.BannerPosition.BottomCenter); | ||||||
|  |             ShowToast($"Load Banner: {PMBannerUnitID}"); | ||||||
|  |         } | ||||||
|  |         private static void AddBannerCallBacks() | ||||||
|  |         { | ||||||
|  |             MaxSdkCallbacks.Banner.OnAdLoadedEvent += OnDebugBannerLoadSuccess; | ||||||
|  |             MaxSdkCallbacks.Banner.OnAdLoadFailedEvent += OnDebugBannerLoadFailed; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         private static void RemoveBannerCallBacks() | ||||||
|  |         { | ||||||
|  |             MaxSdkCallbacks.Banner.OnAdLoadedEvent -= OnDebugBannerLoadSuccess; | ||||||
|  |             MaxSdkCallbacks.Banner.OnAdLoadFailedEvent -= OnDebugBannerLoadFailed; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static void OnDebugBannerLoadSuccess(string adUnitId, MaxSdkBase.AdInfo adInfo) | ||||||
|  |         { | ||||||
|  |             if (adUnitId == PMBannerUnitID) | ||||||
|  |             { | ||||||
|  |                 RemoveBannerCallBacks(); | ||||||
|  |                 Debug.Log($"[PM] Load Banner success => Revenue: {adInfo.Revenue}"); | ||||||
|  |                 MaxSdk.ShowBanner(adUnitId); | ||||||
|  |                 _isLoadingBanner = false; | ||||||
|  |                  | ||||||
|  |                 ShowToast($"Banner Loaded: {PMBannerUnitID}"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static void OnDebugBannerLoadFailed(string adUnitId, MaxSdkBase.ErrorInfo errorInfo) | ||||||
|  |         { | ||||||
|  |             if (adUnitId == PMBannerUnitID) | ||||||
|  |             { | ||||||
|  |                 RemoveBannerCallBacks(); | ||||||
|  |                 Debug.Log($"[PM] Load Banner fail => Waterfall:{errorInfo.WaterfallInfo.Name}"); | ||||||
|  |                 _isLoadingBanner = false; | ||||||
|  |                  | ||||||
|  |                 ShowToast($"Banner Load fail: {PMBannerUnitID}"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         #endregion | ||||||
|  | 
 | ||||||
|  |         #region Interstitial | ||||||
|  | 
 | ||||||
|  |         public void LoadInterstitialAD() | ||||||
|  |         { | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |          | ||||||
|  |         private static bool _isLoadingIV = false; | ||||||
|  |         /// <summary> | ||||||
|  |         /// 请求测试广告IV | ||||||
|  |         /// </summary> | ||||||
|  |         public static void LoadDebugIV() | ||||||
|  |         { | ||||||
|  |             if (_isLoadingIV) return; | ||||||
|  |             _isLoadingIV = true; | ||||||
|  |             EnableIVDebugMode(PMInterUnitID); | ||||||
|  |             AddIvCallBacks(); | ||||||
|  |             MaxSdk.LoadInterstitial(PMInterUnitID); | ||||||
|  |              | ||||||
|  |             ShowToast($"Load Interstitial: {PMInterUnitID}"); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         private static void AddIvCallBacks() | ||||||
|  |         { | ||||||
|  |             MaxSdkCallbacks.Interstitial.OnAdLoadedEvent += OnDebugIVLoadSuccess; | ||||||
|  |             MaxSdkCallbacks.Interstitial.OnAdLoadFailedEvent += OnDebugIVLoadFailed; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         private static void RemoveIvCallBacks() | ||||||
|  |         { | ||||||
|  |             MaxSdkCallbacks.Interstitial.OnAdLoadedEvent -= OnDebugIVLoadSuccess; | ||||||
|  |             MaxSdkCallbacks.Interstitial.OnAdLoadFailedEvent -= OnDebugIVLoadFailed; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static void OnDebugIVLoadSuccess(string adUnitId, MaxSdkBase.AdInfo adInfo) | ||||||
|  |         { | ||||||
|  |             if (adUnitId == PMInterUnitID) | ||||||
|  |             { | ||||||
|  |                 RemoveIvCallBacks(); | ||||||
|  |                 _isLoadingIV = false; | ||||||
|  |                 Debug.Log($"[PM] Load IV success => Revenue: {adInfo.Revenue}"); | ||||||
|  |                 string placement = "pm_test_iv"; | ||||||
|  |                 MaxSdk.ShowInterstitial(adUnitId, placement); | ||||||
|  |                 ShowToast($"Load IV Success: {PMInterUnitID}"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static void OnDebugIVLoadFailed(string adUnitId, MaxSdkBase.ErrorInfo errorInfo) | ||||||
|  |         { | ||||||
|  |             if (adUnitId == PMInterUnitID) | ||||||
|  |             { | ||||||
|  |                 RemoveIvCallBacks(); | ||||||
|  |                 _isLoadingIV = false; | ||||||
|  |                 Debug.Log($"[PM] Load IV fail => Waterfall:{errorInfo.WaterfallInfo.Name}"); | ||||||
|  |                 ShowToast($"Load IV Fail: {PMInterUnitID}"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         #endregion | ||||||
|  | 
 | ||||||
|  |         #region Reward | ||||||
|  | 
 | ||||||
|  |         public void LoadRewardAD() | ||||||
|  |         { | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         private static bool _isLoadingRV = false; | ||||||
|  |         /// <summary> | ||||||
|  |         /// 请求测试广告RV | ||||||
|  |         /// </summary> | ||||||
|  |         public static void RequestDebugRV() | ||||||
|  |         { | ||||||
|  |             if (_isLoadingRV) return; | ||||||
|  |             _isLoadingRV = true; | ||||||
|  |             EnableIVDebugMode(PMRewardUnitID); | ||||||
|  |             AddRvCallBacks(); | ||||||
|  |             MaxSdk.LoadRewardedAd(PMRewardUnitID); | ||||||
|  |              | ||||||
|  |             ShowToast($"Load RV: {PMInterUnitID}"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static void AddRvCallBacks() | ||||||
|  |         { | ||||||
|  |             MaxSdkCallbacks.Rewarded.OnAdLoadedEvent += OnDebugRVLoadSuccess; | ||||||
|  |             MaxSdkCallbacks.Rewarded.OnAdLoadFailedEvent += OnDebugRVLoadFailed; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         private static void RemoveRvCallBacks() | ||||||
|  |         { | ||||||
|  |             MaxSdkCallbacks.Rewarded.OnAdLoadedEvent -= OnDebugRVLoadSuccess; | ||||||
|  |             MaxSdkCallbacks.Rewarded.OnAdLoadFailedEvent -= OnDebugRVLoadFailed; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         private static void OnDebugRVLoadSuccess(string adUnitId, MaxSdkBase.AdInfo adInfo) | ||||||
|  |         { | ||||||
|  |             if (adUnitId == PMRewardUnitID) | ||||||
|  |             { | ||||||
|  |                 RemoveRvCallBacks(); | ||||||
|  |                 _isLoadingRV = false; | ||||||
|  |                 Debug.Log($"[PM] Load RV success => Revenue: {adInfo.Revenue}"); | ||||||
|  |                 string placement = "pm_test_rv"; | ||||||
|  |                 MaxSdk.ShowRewardedAd(adUnitId, placement); | ||||||
|  |                 ShowToast($"Load RV Success: {PMRewardUnitID}"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static void OnDebugRVLoadFailed(string adUnitId, MaxSdkBase.ErrorInfo errorInfo) | ||||||
|  |         { | ||||||
|  |             if (adUnitId == PMRewardUnitID) | ||||||
|  |             { | ||||||
|  |                 RemoveRvCallBacks(); | ||||||
|  |                 _isLoadingRV = false; | ||||||
|  |                 Debug.Log($"[PM] Load RV fail => Waterfall:{errorInfo.WaterfallInfo.Name}"); | ||||||
|  |                 ShowToast($"Load RV Fail: {PMRewardUnitID}"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         #endregion | ||||||
|  |          | ||||||
|  |         #region Debug | ||||||
|  |          | ||||||
|  |         private static void ShowToast(string msg) | ||||||
|  |         { | ||||||
|  | #if UNITY_ANDROID    | ||||||
|  |             DeviceUtil.ShowToast(msg); | ||||||
|  | #endif | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         #endregion | ||||||
|  |       | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,11 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: d1e329abcf494bd19dfc930b04c0ba5e | ||||||
|  | MonoImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   serializedVersion: 2 | ||||||
|  |   defaultReferences: [] | ||||||
|  |   executionOrder: 0 | ||||||
|  |   icon: {instanceID: 0} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,67 @@ | ||||||
|  | //================================================ | ||||||
|  | // Company Name: Castbox | ||||||
|  | // Project: Ball Sort Puzzle | ||||||
|  | // Author: HYZ | ||||||
|  | // CreateTime: 2023-04-12 21:06:02 | ||||||
|  | // Version: 1.0.0 | ||||||
|  | // Desc:  | ||||||
|  | //================================================ | ||||||
|  | 
 | ||||||
|  | using UnityEngine.Serialization; | ||||||
|  | 
 | ||||||
|  | namespace Guru | ||||||
|  | { | ||||||
|  |     using System; | ||||||
|  |     using UnityEngine; | ||||||
|  |      | ||||||
|  |     public partial class GuruSettings | ||||||
|  |     { | ||||||
|  |         [Header("Pubmatic 广告配置")] | ||||||
|  |         public PubmaticSetting PubmaticSetting; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     /// <summary> | ||||||
|  |     /// Amazon广告配置 | ||||||
|  |     /// </summary> | ||||||
|  |     [Serializable] | ||||||
|  |     public class PubmaticSetting | ||||||
|  |     { | ||||||
|  |         [SerializeField] public bool Enable; | ||||||
|  |         [SerializeField] private PubmaticPlatformSetting Android; | ||||||
|  |         [SerializeField] private PubmaticPlatformSetting iOS; | ||||||
|  | 	 | ||||||
|  |         /// <summary> | ||||||
|  |         /// 获取AppID | ||||||
|  |         /// </summary> | ||||||
|  |         /// <returns></returns> | ||||||
|  |         public PubmaticPlatformSetting GetPlatform() | ||||||
|  |         { | ||||||
|  | #if UNITY_IOS | ||||||
|  | 			return iOS;  | ||||||
|  | #else | ||||||
|  |             return Android; | ||||||
|  | #endif  | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         public string BannerUnitID => GetPlatform().bannerUnitID; | ||||||
|  |         public string InterUnitID => GetPlatform().interUnitID; | ||||||
|  |         public string RewardUnitID => GetPlatform().rewardUnitID; | ||||||
|  |         public string StoreUrl => GetPlatform().storeUrl; | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     /// <summary> | ||||||
|  |     /// Amazon平台专属配置 | ||||||
|  |     /// </summary> | ||||||
|  |     [Serializable] | ||||||
|  |     public class PubmaticPlatformSetting | ||||||
|  |     { | ||||||
|  |         // public string name;		// 平台名称 | ||||||
|  |         public string storeUrl; // 平台商店地址 | ||||||
|  |         public string bannerUnitID;	// Banner ID | ||||||
|  |         public string interUnitID;	// Inter ID | ||||||
|  |         public string rewardUnitID;	// Reward ID | ||||||
|  |     } | ||||||
|  |      | ||||||
|  | } | ||||||
|  | @ -0,0 +1,11 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: f7d6b41f0f5449be8ac0c4d1829bb330 | ||||||
|  | MonoImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   serializedVersion: 2 | ||||||
|  |   defaultReferences: [] | ||||||
|  |   executionOrder: 0 | ||||||
|  |   icon: {instanceID: 0} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,8 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: cd2ad07dfc12b418db1bcd4b3a316329 | ||||||
|  | folderAsset: yes | ||||||
|  | DefaultImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 729cc47bf56d436ba4c20380ca1e5e59 | ||||||
|  | timeCreated: 1673860499 | ||||||
|  | @ -0,0 +1,35 @@ | ||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <!--  | ||||||
|  | Sample Dependencies.xml: | ||||||
|  |     https://github.com/googlesamples/unity-jar-resolver/blob/master/sample/Assets/ExternalDependencyManager/Editor/SampleDependencies.xml | ||||||
|  | --> | ||||||
|  | <dependencies> | ||||||
|  |     <androidPackages> | ||||||
|  |         <androidPackage spec="androidx.core:core:1.7.0" /> | ||||||
|  | <!--        <androidPackage spec="androidx.appcompat:appcompat:1.5.1" />--> | ||||||
|  |         <androidPackage spec="androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1" /> | ||||||
|  |         <androidPackage spec="androidx.work:work-runtime:2.7.1" /> | ||||||
|  |         <androidPackage spec="androidx.work:work-runtime-ktx:2.7.1" /> | ||||||
|  |         <androidPackage spec="androidx.work:work-rxjava2:2.7.1" /> | ||||||
|  |         <androidPackage spec="androidx.lifecycle:lifecycle-process:2.4.0" /> | ||||||
|  |         <androidPackage spec="com.jakewharton.timber:timber:4.7.1" /> | ||||||
|  |         <androidPackage spec="com.google.code.gson:gson:2.8.5" /> | ||||||
|  |         <androidPackage spec="androidx.room:room-runtime:2.4.3" /> | ||||||
|  |         <androidPackage spec="androidx.room:room-rxjava2:2.4.3" /> | ||||||
|  |         <androidPackage spec="com.squareup.retrofit2:retrofit:2.7.1" /> | ||||||
|  |         <androidPackage spec="com.squareup.retrofit2:converter-gson:2.7.1" /> | ||||||
|  |         <androidPackage spec="com.squareup.retrofit2:adapter-rxjava2:2.7.1" /> | ||||||
|  |         <androidPackage spec="com.squareup.okhttp3:okhttp:4.9.3" /> | ||||||
|  |          | ||||||
|  | <!--        <androidPackage spec="com.mapzen:on-the-road:0.8.1" />--> | ||||||
|  | <!--        <androidPackage spec="com.squareup.retrofit2:retrofit:2.7.1" />--> | ||||||
|  |     </androidPackages> | ||||||
|  |     <iosPods> | ||||||
|  |         <iosPod name="GuruAnalyticsLib" version="0.2.16" bitcodeEnabled="false"> | ||||||
|  |             <sources> | ||||||
|  |                 <source>git@github.com:castbox/GuruSpecs.git</source> | ||||||
|  |             </sources> | ||||||
|  |         </iosPod> | ||||||
|  |         <iosPod name="JJException" bitcodeEnabled="false" /> | ||||||
|  |     </iosPods> | ||||||
|  | </dependencies> | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: b9cafb1d82bb45598fdd566d9b8f6a80 | ||||||
|  | timeCreated: 1674110869 | ||||||
|  | @ -0,0 +1,2 @@ | ||||||
|  | -keep class com.guru.** { *; } | ||||||
|  | -keep class guru.core.** { *; } | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue