diff --git a/Runtime/GuruCore/Runtime/Analytics/Analytics.Const.cs b/Runtime/GuruCore/Runtime/Analytics/Analytics.Const.cs
index f1d9dcf..32b8ad0 100644
--- a/Runtime/GuruCore/Runtime/Analytics/Analytics.Const.cs
+++ b/Runtime/GuruCore/Runtime/Analytics/Analytics.Const.cs
@@ -114,6 +114,7 @@ namespace Guru
 		// 经济相关
 		public static readonly string ParameterBalance = "balance"; // 用于余额
 		public static readonly string ParameterSku = "sku"; // sku
+		public static readonly string ParameterScene = "scene"; // sku
 		public static readonly string ParameterVirtualCurrencyName = "virtual_currency_name"; // 虚拟货币名称
 	}
 }
\ No newline at end of file
diff --git a/Runtime/GuruCore/Runtime/Analytics/Analytics.Economic.cs b/Runtime/GuruCore/Runtime/Analytics/Analytics.Economic.cs
index a650af8..80e1e71 100644
--- a/Runtime/GuruCore/Runtime/Analytics/Analytics.Economic.cs
+++ b/Runtime/GuruCore/Runtime/Analytics/Analytics.Economic.cs
@@ -72,65 +72,59 @@ namespace Guru
 
 
         /// 
-        /// 获取虚拟货币
+        /// 获取道具/货币
         /// 
-        /// 
-        /// 
-        /// 
+        /// 
         /// 
-        /// 
-        /// 
-        /// 
-        /// 
-        public static void EarnVirtualCurrency(string currencyName, int value, int balance, 
-            string method = "", 
-            string levelName = "",
-            bool isIap = false,
-            string sku = "",
-            string scene = "")
+        /// 
+        /// 
+        /// 
+        public static void EarnVirtualCurrency(string virtualCurrencyName, string method, int balance, int value,  
+            string methodDetails = "")
         {
-            if (isIap) method = "iap_buy";  
             var data = new Dictionary()
             {
-                { ParameterVirtualCurrencyName, currencyName },
+                { ParameterVirtualCurrencyName, virtualCurrencyName },
+                { ParameterItemCategory, method },
+                { ParameterItemName, methodDetails },
                 { ParameterValue, value },
                 { ParameterBalance, balance },
-                { ParameterLevelName, levelName },
-                { ParameterItemCategory, method },
             };
-            
-            if (!string.IsNullOrEmpty(scene)) data[ParameterItemName] = scene; // 获取的虚拟货币或者道具的场景
-            if (!string.IsNullOrEmpty(sku)) data[ParameterSku] = sku; // 商品的 sku
-            
             LogEvent(EventEarnVirtualCurrency, data, new EventSetting() { EnableFirebaseAnalytics = true });
             
             // FB 上报收入点
-            FB.LogAppEvent(EventEarnVirtualCurrency, value, data);
+            // FB.LogAppEvent(EventEarnVirtualCurrency, value, data);
         }
         
-        
-        public static void SpendVirtualCurrency(string currencyName, int value, int balance, 
-            string method = "", 
-            string levelName = "",
-            string scene = "")
+        /// 
+        /// 消耗道具/货币
+        /// 
+        /// 
+        /// 
+        /// 
+        /// 
+        /// 
+        /// 
+        public static void SpendVirtualCurrency(string contentId, string contentType, int price,
+            string virtualCurrencyName, int balance, string scene = "")
         {
             var data = new Dictionary()
             {
-                { ParameterVirtualCurrencyName, currencyName },
-                { ParameterValue, value },
+                { ParameterVirtualCurrencyName, virtualCurrencyName },
+                { ParameterValue, price },
                 { ParameterBalance, balance },
-                { ParameterLevelName, levelName },
-                { ParameterItemCategory, method },
+                { ParameterItemName, contentId },
+                { ParameterItemCategory, contentType },
             };
-            
-            if (!string.IsNullOrEmpty(scene)) data[ParameterItemName] = scene; // 获取的虚拟货币或者道具的场景
+            if (!string.IsNullOrEmpty(scene)) data[ParameterScene] = scene; // 获取的虚拟货币或者道具的场景
             
             LogEvent(EventSpendVirtualCurrency, data, new EventSetting() { EnableFirebaseAnalytics = true });
             
             // FB 上报消费点
-            FB.LogAppEvent(EventSpendVirtualCurrency, value, data);
+            // FB.LogAppEvent(EventSpendVirtualCurrency, value, data);
+            
             // FB 上报消耗事件买量点
-            FBSpentCredits(value, scene, method);  // 点位信息有变化
+            FBSpentCredits(contentId, contentType, price);  // 点位信息有变化
         }
 
 
@@ -140,7 +134,7 @@ namespace Guru
         /// 
         /// 
         /// 
-        private static void FBSpentCredits(int amount, string contentId, string contentType)
+        private static void FBSpentCredits(string contentId, string contentType, float amount)
         {
             FB.LogAppEvent(AppEventName.SpentCredits, amount, 
                 new Dictionary()
diff --git a/Runtime/GuruInventory.meta b/Runtime/GuruInventory.meta
new file mode 100644
index 0000000..ac8a012
--- /dev/null
+++ b/Runtime/GuruInventory.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 93bb1ade31b44cc9bb3dbad3051cbe78
+timeCreated: 1706922645
\ No newline at end of file
diff --git a/Runtime/GuruInventory/README.md b/Runtime/GuruInventory/README.md
new file mode 100644
index 0000000..e69de29
diff --git a/Runtime/GuruInventory/README.md.meta b/Runtime/GuruInventory/README.md.meta
new file mode 100644
index 0000000..2475cd2
--- /dev/null
+++ b/Runtime/GuruInventory/README.md.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 1f4820a46a3a4b02ac85a48a4e25014c
+timeCreated: 1706922699
\ No newline at end of file
diff --git a/Runtime/GuruInventory/Runtime.meta b/Runtime/GuruInventory/Runtime.meta
new file mode 100644
index 0000000..303bb17
--- /dev/null
+++ b/Runtime/GuruInventory/Runtime.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: e921121b06404396b164c6538e68c6aa
+timeCreated: 1706922657
\ No newline at end of file
diff --git a/Runtime/GuruInventory/Runtime/InventoryData.cs b/Runtime/GuruInventory/Runtime/InventoryData.cs
new file mode 100644
index 0000000..98ae1f9
--- /dev/null
+++ b/Runtime/GuruInventory/Runtime/InventoryData.cs
@@ -0,0 +1,131 @@
+
+namespace Guru
+{
+    using System;
+    using System.Collections.Generic;
+    using Newtonsoft.Json;
+
+    [Serializable]
+    public class InventoryData: IJsonData
+    {
+        [JsonProperty("v")] 
+        public List valid;
+        [JsonProperty("e")] 
+        public List expired;
+
+        public static InventoryData FromJson(string json)
+        {
+            if (JsonDataHelper.Parse(json, out var d))
+            {
+                return d;
+            }
+            return new InventoryData();
+        }
+
+        public InventoryData()
+        {
+            valid = new List();
+            expired = new List();
+        }
+
+        public InventoryData(List valid, List expired)
+        {
+            this.valid = valid;
+            this.expired = expired;
+        }
+        
+        public static InventoryData Create(LimitedBalance balance = null)
+        {
+            var t = new InventoryData();
+            if (balance != null)
+            {
+                if (balance.expireAt > InventoryManager.CurrentTimeInMillis)
+                {
+                    t.valid.Add(balance);
+                }
+            }
+            return t;
+        }
+
+        
+        public InventoryData AttachLimitedBalance(LimitedBalance balance)
+        {
+            if (balance.expireAt > InventoryManager.CurrentTimeInMillis)
+            {
+                valid.Add(balance);
+                return new InventoryData()
+                {
+                    valid =  this.valid,
+                    expired = this.expired,
+                };
+            }
+            return this;
+        }
+
+        /// 
+        /// 回收过期的道具
+        /// 
+        /// 
+        public RecycleResult RecycleExpiredBalance()
+        {
+            long now = InventoryManager.CurrentTimeInMillis;
+            List newValid = new List();
+            List newExpired = new List();
+            int expiredBalance = 0;
+            foreach(var item in valid) {
+                if (now >= item.expireAt) {
+                    expiredBalance += item.amount;
+                    newExpired.Add(item);
+                } else {
+                    newValid.Add(item);
+                }
+            }
+            return new RecycleResult(expiredBalance, new InventoryData(newValid, newExpired));
+        }
+
+
+    }
+    
+    [Serializable]
+    public class LimitedBalance
+    {
+        [JsonProperty("a")]
+        public int amount = 0;
+        [JsonProperty("e")]
+        public long expireAt = -1;
+    }
+    
+    public class RecycleResult {
+        public int expiredBalance;
+        public InventoryData InventoryData;
+        
+        public RecycleResult(int expiredBalance, InventoryData inventoryData) {
+            this.expiredBalance = expiredBalance;
+            this.InventoryData = inventoryData;
+        }
+    }
+
+    public class ConsumeResult {
+        public bool consumed;
+        public InventoryItem item;
+        
+        public ConsumeResult(InventoryItem item, bool consumed)
+        {
+            this.item = item;
+            this.consumed = consumed;
+        
+        }
+        
+        public static ConsumeResult Success(InventoryItem item)
+        {
+            return new ConsumeResult(item, true);
+        }
+
+        public static ConsumeResult Error(InventoryItem item)
+        {
+            return new ConsumeResult(item, false);
+        }
+
+    }
+    
+}
\ No newline at end of file
diff --git a/Runtime/GuruInventory/Runtime/InventoryData.cs.meta b/Runtime/GuruInventory/Runtime/InventoryData.cs.meta
new file mode 100644
index 0000000..c7d9488
--- /dev/null
+++ b/Runtime/GuruInventory/Runtime/InventoryData.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 54c27103f7594aa9bc337f5e076fb550
+timeCreated: 1706959298
\ No newline at end of file
diff --git a/Runtime/GuruInventory/Runtime/InventoryManager.Const.cs b/Runtime/GuruInventory/Runtime/InventoryManager.Const.cs
new file mode 100644
index 0000000..76d98ae
--- /dev/null
+++ b/Runtime/GuruInventory/Runtime/InventoryManager.Const.cs
@@ -0,0 +1,55 @@
+namespace Guru
+{
+    /// 
+    /// 交易方式
+    /// 
+    public enum TransactionMethod
+    {
+        unknown  = 0,
+        iap, // IAP购买
+        igc, // In-game currency 购买(coin/gems..)
+        reward, // 奖励获得
+        bonus, // 优惠
+        prop, // 道具
+        free,
+    }
+
+    /// 
+    /// 道具列别
+    /// 
+    public class InventoryCategory
+    {
+        public const string Prop = "prop";
+    }
+
+
+    public partial class InventoryManager
+    {
+     
+        /// 
+        /// 获取交易方式的字段值
+        /// 
+        /// 
+        /// 
+        private string ConvertTransactionMethodName(TransactionMethod method) {
+            switch (method) {
+                case TransactionMethod.iap:
+                    return "iap_buy";
+                case TransactionMethod.igc:
+                    return "igc";
+                case TransactionMethod.reward:
+                    return "reward";
+                case TransactionMethod.bonus:
+                    return "bonus";
+                case TransactionMethod.prop:
+                    return "prop";
+                case TransactionMethod.free:
+                    return "prop";
+                default:
+                    return "unknown";
+            }
+        }
+        
+        
+    }
+}
\ No newline at end of file
diff --git a/Runtime/GuruInventory/Runtime/InventoryManager.Const.cs.meta b/Runtime/GuruInventory/Runtime/InventoryManager.Const.cs.meta
new file mode 100644
index 0000000..6a97470
--- /dev/null
+++ b/Runtime/GuruInventory/Runtime/InventoryManager.Const.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 0b4e29d06a7f4ddb860e08d3aa81610e
+timeCreated: 1706945881
\ No newline at end of file
diff --git a/Runtime/GuruInventory/Runtime/InventoryManager.cs b/Runtime/GuruInventory/Runtime/InventoryManager.cs
new file mode 100644
index 0000000..dbe1fb0
--- /dev/null
+++ b/Runtime/GuruInventory/Runtime/InventoryManager.cs
@@ -0,0 +1,578 @@
+
+namespace Guru
+{
+    using SQLite4Unity3d;
+    using UnityEngine;
+    using System.Collections.Generic;
+    using Newtonsoft.Json;
+    using System;
+
+    public interface IInventoryDelegate
+    {
+        string GetInventoryCategory(string id);
+    }
+
+    /// 
+    /// 道具管理器
+    /// 
+    public partial class InventoryManager
+    {
+        
+        private static InventoryManager _instance;
+        public  static InventoryManager Instance
+        {
+            get
+            {
+                if(_instance == null) _instance = new InventoryManager();
+                return _instance;
+            }
+        }
+        private IInventoryDelegate _delegate;
+        private InventoryTable _table;
+
+        public bool IsReady { get; private set; } = false;
+        
+        public static long CurrentTimeInMillis => TimeUtil.GetCurrentTimeStamp();
+
+
+        /// 
+        /// 初始化
+        /// 
+        /// 
+        public static void Install(DBService service)
+        {
+            if (service != null)
+            {
+                Instance._table = InventoryTable.LoadOrCreate(service);
+                Instance.IsReady = true;
+            }
+        }
+
+
+
+
+        /// 
+        /// 获取道具分类
+        /// 
+        /// 
+        /// 
+        public string GetInventoryCategory(string id)
+        {
+            return _delegate?.GetInventoryCategory(id) ?? InventoryCategory.Prop;
+        }
+
+        private InventoryItem GetData(string sku) => _table.GetItem(sku);
+
+
+
+        /// 
+        /// 获取道具(组)
+        /// 通过[method]中的的特定[specific]方式 获得了指定的 [items]
+        /// * method: iap -> specific: sku
+        /// * method: igc -> specific: coin/gems...
+        /// * method: reward -> specific: ads/lottery/daily/...
+        /// * method: bonus -> specific: ads/other/...
+        /// * method: prop -> specific: hint/hammer/swap/magic/..
+        ///
+        /// method 最终会在 earnVirtualCurrency 中成为 item_category
+        /// specific 最终会在 earnVirtualCurrency 中成为 item_name
+        /// 
+        /// 
+        /// 
+        /// 
+        public void Acquire(List items, TransactionMethod method, string specific = "")
+        {
+            if (!IsReady) return;
+            
+            List acquired = new List(items.Count);
+            string category;
+            StockItem item;
+            InventoryItem invItem;
+            
+            for (int i = 0; i < items.Count; i++)
+            {
+                item = items[i];
+                category = GetInventoryCategory(item.sku);
+                invItem = GetData(item.sku)?.Acquire(method, item.amount) 
+                          ?? InventoryItem.Create(item.sku, category, item.attr,  balance:item.amount, method: method);
+                acquired.Add(invItem);
+                
+                Analytics.EarnVirtualCurrency(
+                    item.sku,
+                    method:ConvertTransactionMethodName(method),
+                    methodDetails:specific,
+                    balance: invItem.balance,
+                    value: item.amount);
+            }
+
+            _table.UpdateInventoryItems(acquired); // 更新数据库信息
+        }
+
+
+        public bool Consume(List items, string contentId, string scene, string category = "")
+        {
+            if (!IsReady) return false;
+
+            List consumed = new List(items.Count);
+            bool isConsumed = true;
+
+            StockItem item;
+            InventoryItem invItem;
+
+            for (int i = 0; i < items.Count; i++)
+            {
+                item = items[i];
+                var result = GetData(item.sku)?.Consume(scene, item.amount) ?? null;
+                if (result == null)
+                {
+                    Debug.LogError($"consume item not found: {item.sku}");
+                    return false;
+                }
+
+                consumed.Add(result.item);
+                // 这里如果没有消耗掉,将跳出
+                if (!result.consumed)
+                {
+                    Debug.Log($"consume failed: {item.sku}:{item.amount}");
+                    isConsumed = false;
+                    break;
+                }
+            }
+
+            if (consumed.Count == 0)
+            {
+                return false;
+            }
+            else
+            {
+                if (isConsumed && consumed.Count == items.Count)
+                {
+                    for (int i = 0; i < items.Count; ++i)
+                    {
+                        Analytics.SpendVirtualCurrency(
+                            contentId, category, items[i].amount,
+                            virtualCurrencyName: consumed[i].sku, balance: consumed[i].balance);
+                    }
+                }
+            }
+
+
+            _table.UpdateInventoryItems(consumed); // 更新数据库信息
+            return true;
+        }
+
+
+        public bool Consume(List items, Manifest redeem)
+        {
+            return Consume(items, redeem.scene, redeem.contentId, redeem.category);
+        }
+
+
+
+        /// 
+        /// 判断是否可以支付
+        /// 
+        /// 
+        /// 
+        /// 
+        public bool CanAfford(String id, int amount) {
+            var item = GetData(id);
+            return item != null && item.balance > amount;
+        }
+        
+        
+    }
+
+
+    // 
+    /// 库存道具
+    /// 
+    public class StockItem
+    {
+        [PrimaryKey] 
+        public string sku { get; set; }
+        public int amount { get; set; }
+        public int attr { get; set; }
+        public long expired { get; set; }
+        
+
+        public static StockItem Consumable(string sku, int amount)
+        {
+            return Create(sku, amount, DetailsAttr.Consumable);
+        }
+        
+        public static StockItem Permanent(string sku, int amount)
+        {
+            return Create (sku, amount, DetailsAttr.Permanent);
+        }
+        
+        
+        public static StockItem Create(string sku, int amount, int att, long expiredStamp = 0)
+        {
+            return new StockItem
+            {
+                sku = sku,
+                amount = amount,
+                attr = att,
+                expired = expiredStamp
+            };
+        }
+    }
+
+
+    [Serializable]
+    public class InventoryItem: IJsonData
+    {
+        [JsonProperty(InventoryTable.dbSku)]
+        public string sku { get; set; }
+        [JsonProperty(InventoryTable.dbBalance)]
+        public int balance { get; set; }
+        [JsonProperty(InventoryTable.dbCategory)]
+        public string category { get; set; }
+        [JsonProperty(InventoryTable.dbAttr)]
+        public int attr { get; set; }
+        [JsonProperty(InventoryTable.dbTimeSensitive)]
+        public InventoryData Inventory { get; set; }
+        [JsonProperty(InventoryTable.dbUpdateAt)]
+        public long updateAt{ get; set; }
+        [JsonProperty(InventoryTable.dbCreateAt)]
+        public long createAt{ get; set; }
+        [JsonProperty(InventoryTable.dbDetails)]
+        public InventoryDetails details { get; set; }
+        
+        public static Action ExceptionHandler;
+
+
+        
+        
+
+        public static InventoryItem FromJson(string json)
+        {
+            if (JsonDataHelper.Parse(json, out var d))
+            {
+                return d;
+            }
+            return null;
+        }
+
+        public InventoryItem(string sku, int balance, string category, int attr, InventoryDetails details = null, InventoryData inventory = null, long createAt = -1, long updateAt = -1)
+        {
+            this.sku = sku;
+            this.category = category;
+            this.balance = balance;
+            this.attr = attr;
+            this.details = details;
+            this.Inventory = inventory;
+            this.createAt = createAt;
+            this.updateAt = updateAt;
+        }
+
+
+        public static InventoryItem Create(string sku,  string category, int attr, 
+            int balance = 0, InventoryDetails details = null, 
+            InventoryData inventory = null, long createAt = -1, long updateAt = -1, 
+            int expireAt = -1, TransactionMethod method = TransactionMethod.unknown)
+        {
+            long stamp = InventoryManager.CurrentTimeInMillis;
+            return new InventoryItem(
+                sku,
+                balance,
+                category,
+                attr,
+                details ?? InventoryDetails.Create(balance),
+                inventory?? InventoryData.Create(new LimitedBalance() { amount = balance}),
+                stamp,
+                stamp
+            );
+        }
+        
+        /// 
+        /// 获取道具
+        /// 
+        /// 
+        /// 
+        /// 
+        public InventoryItem Acquire(TransactionMethod method, int amount)
+        {
+            var now = InventoryManager.CurrentTimeInMillis;
+            var recycled = Inventory.RecycleExpiredBalance();
+            var target = balance + amount;
+            var newBalance = Math.Clamp(target - recycled.expiredBalance, 0, target);
+            return new InventoryItem(this.sku, newBalance,this.category, this.attr, 
+                details.Acquire(method, amount),
+                recycled.InventoryData,
+                createAt, now);
+        }
+
+        /// 
+        /// 消耗道具
+        /// 
+        /// 
+        /// 
+        /// 
+        public ConsumeResult Consume(string scene, int amount)
+        {
+            var now = InventoryManager.CurrentTimeInMillis;
+            var recycled = Inventory.RecycleExpiredBalance();
+            var target = Math.Clamp(balance - recycled.expiredBalance, 0, balance);
+            if (target > amount && attr == DetailsAttr.Consumable)
+            {
+                target -= amount;
+                return ConsumeResult.Success(new InventoryItem(sku, target, category, attr,
+                    details.Consume(scene, amount), recycled.InventoryData, createAt, now));
+            }
+            return ConsumeResult.Error(new InventoryItem(
+                sku, target, category, attr, details, recycled.InventoryData, createAt, now));
+        }
+
+    }
+
+    [Serializable]
+    public class InventoryDetails: IJsonData
+    {
+        [JsonProperty("a")]
+        public Dictionary acquired { get; set; } 
+        
+        [JsonProperty("c")]
+        public Dictionary consumed { get; set; }
+        
+        [JsonProperty("d")]
+        public Dictionary data { get; set; }
+
+
+        public InventoryDetails()
+        {
+            acquired = new Dictionary(10);
+            consumed = new Dictionary(10);
+            data = new Dictionary(10);
+        }
+
+        public static InventoryDetails Create(int amount)
+        {
+            var d = new InventoryDetails();
+            if (amount > 0)
+            {
+                d.acquired[TransactionMethod.unknown] = amount;
+            }
+            return d;
+        }
+
+        public InventoryDetails Acquire(TransactionMethod method, int amount)
+        {
+            if (acquired.TryGetValue(method, out var a))
+            {
+                acquired[method] = a + amount;
+            }
+            else
+            {
+                acquired[method] = amount;
+            }
+            
+            return this;
+        }
+        
+        public InventoryDetails Consume(string scene, int amount)
+        {
+            if (consumed.TryGetValue(scene, out var a))
+            {
+                consumed[scene] = a + amount;
+            }
+            else
+            {
+                consumed[scene] = amount;
+            }
+            return this;
+        }
+
+        /// 
+        /// 从 JSON 中解析
+        /// 
+        /// 
+        /// 
+        public InventoryDetails FromJson(string json)
+        {
+            if (JsonDataHelper.Parse(json, out var d))
+            {
+                return d;
+            }
+            return new InventoryDetails();
+        }
+
+        public void SetValue(String key, object value) {
+            data[key] = value;
+        }
+
+        public int GetInt(string key, int defaultValue = 0)
+        {
+            if(data.TryGetValue(key, out var value))
+            {
+                return (int)value;
+            }
+            return defaultValue;      
+        }
+        
+        public double GetDouble(string key, double defaultValue = 0)
+        {
+            if(data.TryGetValue(key, out var value))
+            {
+                return (double)value;
+            }
+            return defaultValue;      
+        }
+        
+        public string GetString(string key, string defaultValue = "")
+        {
+            if(data.TryGetValue(key, out var value))
+            {
+                return (string)value;
+            }
+            return defaultValue;      
+        }
+        
+        public bool GetBool(string key, bool defaultValue = false)
+        {
+            if(data.TryGetValue(key, out var value))
+            {
+                return (bool)value;
+            }
+            return defaultValue;      
+        }
+
+    }
+    
+    /// 
+    /// 内置物品表维护器
+    /// 
+    internal class InventoryTable
+    {
+        
+        internal const string tbName = "inventory"; // Product Transaction Table
+        internal const string dbSku = "sku";
+        internal const string dbBalance = "balance";
+        internal const string dbCategory = "cat";
+        internal const string dbAttr = "attr";
+        internal const string dbDetails = "details";
+        internal const string dbTimeSensitive = "tsv";
+        internal const string dbUpdateAt = "update_at";
+        internal const string dbCreateAt = "create_at";
+        
+        
+        public static InventoryTable LoadOrCreate(DBService db)
+        {
+            var table = new InventoryTable();
+            table.Setup(db);
+            return table;
+        }
+
+        private List _dataList;
+        
+        private DBService _db;
+        private void Setup(DBService db)
+        {
+            _db = db;
+            Refresh();
+            
+            // 执行命令
+            // _db.Execute($"CREATE INDEX inventory_item_idx ON {tbName} ({dbSku});", ts =>
+            // {
+            //     _db.Execute($"CREATE INDEX inventory_item_category_idx ON {tbName} ({dbCategory});");
+            // });
+
+        }
+
+        private void Refresh()
+        {
+            _dataList = _db.GetTableList();
+        }
+
+        private bool HasItem(string id)
+        {
+            return _dataList.Exists(c => c.id == id);
+        }
+        
+        /// 
+        /// 更新道具
+        /// 
+        /// 
+        public void UpdateInventoryItems(List items)
+        {
+            List updates = new List(items.Count);
+            List insets = new List(items.Count);
+            InventoryItem item;
+            inventory inv;
+            for (int i = 0; i < items.Count; i++)
+            {
+                item = items[i];
+                inv = inventory.Create(item);
+                if (!HasItem(item.sku))
+                {
+                    insets.Add(inv);
+                }
+                else
+                {
+                    updates.Add(inv);
+                }
+            }
+            _db.InsertAll(insets);
+            _db.UpdateAll(updates);
+            
+            Refresh();
+        }
+
+        public InventoryItem GetItem(string sku)
+        {
+            if (HasItem(sku))
+            {
+                var inv = _dataList.Find(c => c.sku == sku);
+                if (inv != null) return inv.ToItem();
+            }
+            return null;
+        }
+
+    }
+
+    /// 
+    /// 表头及数据结构
+    /// 
+    internal class inventory
+    {
+        [Indexed(InventoryTable.dbSku, 1)]
+        public string sku { get; set; }
+        public int balance { get; set; }
+        [Indexed(InventoryTable.dbCategory, 2)]
+        public string cat{ get; set; }
+        public int attr { get; set; }
+        public string tsv { get; set; }
+        public long update_at{ get; set; }
+        public long create_at{ get; set; }
+
+        [PrimaryKey]
+        public string id => sku;
+
+        public static inventory Create(InventoryItem item)
+        {
+            return new inventory()
+            {
+                sku = item.sku,
+                balance = item.balance,
+                cat = item.category,
+                attr = item.attr,
+                tsv = item.Inventory.ToJson(),
+                update_at = item.updateAt,
+                create_at = item.createAt
+            };
+        }
+
+        public InventoryItem ToItem()
+        {
+            return InventoryItem.Create(
+                sku, cat, attr, balance,
+                null,
+                InventoryData.FromJson(tsv),
+                this.update_at,
+                this.create_at);
+        }
+
+    }
+    
+}
\ No newline at end of file
diff --git a/Runtime/GuruInventory/Runtime/InventoryManager.cs.meta b/Runtime/GuruInventory/Runtime/InventoryManager.cs.meta
new file mode 100644
index 0000000..f47425d
--- /dev/null
+++ b/Runtime/GuruInventory/Runtime/InventoryManager.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 21e2bced768541b5befa3b4397783424
+timeCreated: 1706944831
\ No newline at end of file
diff --git a/Runtime/GuruInventory/Runtime/Manifest.cs b/Runtime/GuruInventory/Runtime/Manifest.cs
new file mode 100644
index 0000000..cf165b7
--- /dev/null
+++ b/Runtime/GuruInventory/Runtime/Manifest.cs
@@ -0,0 +1,47 @@
+using System.Collections.Generic;
+using Unity.Plastic.Newtonsoft.Json;
+
+namespace Guru
+{
+    
+    public class DetailsAttr
+    {
+        /// 
+        /// 永久物品
+        /// 
+        public const int Permanent = 1;
+        /// 
+        /// 可消耗
+        /// 
+        public const int Consumable = 2;
+    }
+    
+    public class ExtraReservedField {
+        public const string scene = "__scene";
+        public const string offerId = "__offer_id";
+        public const string basePlanId = "__base_plan_id";
+        public const string sales = "__sales";
+        public const string rate = "__rate";
+        public const string contentId = "__content_id";
+    }
+    
+    /// 
+    /// 商品清单
+    /// 
+    public class Manifest: IJsonData
+    {
+        
+        [JsonProperty("category")]
+        public string category;
+
+        [JsonProperty("extra")]
+        public Dictionary extra;
+
+        public string scene => extra.TryGetValue(ExtraReservedField.scene, out var v)? v : "";
+        
+        public string basePlanId => extra.TryGetValue(ExtraReservedField.basePlanId, out var v)? v : "";
+        
+        public string offerId => extra.TryGetValue(ExtraReservedField.offerId, out var v)? v : "";
+        public string contentId => extra.TryGetValue(ExtraReservedField.contentId, out var v)? v : "";
+    }
+}
\ No newline at end of file
diff --git a/Runtime/GuruInventory/Runtime/Manifest.cs.meta b/Runtime/GuruInventory/Runtime/Manifest.cs.meta
new file mode 100644
index 0000000..e81ad91
--- /dev/null
+++ b/Runtime/GuruInventory/Runtime/Manifest.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: a1e83edde24c4a46bd8ce00aaf09f273
+timeCreated: 1706945145
\ No newline at end of file
diff --git a/Runtime/GuruLibs.meta b/Runtime/GuruLibs.meta
new file mode 100644
index 0000000..90bbe64
--- /dev/null
+++ b/Runtime/GuruLibs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: c176d24ea24445f9ab67763aa8ef3f7c
+timeCreated: 1706925964
\ No newline at end of file
diff --git a/Runtime/GuruLibs/Datebase.meta b/Runtime/GuruLibs/Datebase.meta
new file mode 100644
index 0000000..835b6f7
--- /dev/null
+++ b/Runtime/GuruLibs/Datebase.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 42b7e7382b1d45d39f1542ed00f4e890
+timeCreated: 1706923167
\ No newline at end of file
diff --git a/Runtime/GuruLibs/Datebase/DBService.cs b/Runtime/GuruLibs/Datebase/DBService.cs
new file mode 100644
index 0000000..c3d9ace
--- /dev/null
+++ b/Runtime/GuruLibs/Datebase/DBService.cs
@@ -0,0 +1,190 @@
+
+
+namespace Guru
+{
+    using SQLite4Unity3d;
+    using UnityEngine;
+    using System.Collections;
+    using System;
+    using System.IO;
+    using System.Collections.Generic;
+    using System.Linq;
+    using System.Linq.Expressions;
+    using UnityEngine.Networking;
+    
+    public class DBService
+    {
+	    internal const string DefaultDBName = "data";
+	    internal const string DefaultDBExtension = ".db";
+	    internal static string DebugDBPath = "mocks_dir/db";
+	    private SQLiteConnection _connection;
+        private string _dbName;
+        private string _dbPath;
+		private bool _isDebug = false;
+		
+		/// 
+		/// 获取数据库的路径
+		/// 
+		public string DatabasePath => _dbPath;
+
+        /// 
+        /// 启动服务
+        /// 
+        /// 
+        /// 
+        public DBService(string name, bool isDebug = false)
+        {
+	        _isDebug = isDebug;
+	        if (string.IsNullOrEmpty(name)) name = DefaultDBName;
+	        _dbName = name;
+	        _dbPath = Path.GetFullPath($"{Application.persistentDataPath}/{_dbName}{DefaultDBExtension}");
+
+	        if (isDebug)
+	        {
+		        _dbPath = Path.GetFullPath($"{Application.persistentDataPath}/{_dbName}_debug{DefaultDBExtension}");
+	        }
+        }
+
+
+        public static DBService Open(string name, bool isDebug = false)
+        {
+	        var ds = new DBService(name, isDebug);
+	        ds.Connect();
+	        return ds;
+        }
+
+        /// 
+        /// 关闭连接
+        /// 
+        public void Close() => _connection?.Close();
+        
+        /// 
+		/// 建立连接, 若不存在则创建数据库
+		/// 
+        public void Connect(string dbPath = "")
+        {
+	        if(!string.IsNullOrEmpty(dbPath)) _dbPath = dbPath;
+	        _connection = new SQLiteConnection(_dbPath, SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create);
+        }
+
+        public bool Exists => File.Exists(_dbPath);
+
+		/// 
+		/// 部署默认的 Database
+		/// 将内置在 StreamingAssets 中的数据库文件部署到 PersistentDataPath 中
+		/// 
+		/// 
+        public void DeployEmbeddedDB(string embeddedPath, Action onComplete)
+        {
+	        string from = Path.Combine(Application.streamingAssetsPath, embeddedPath);
+
+	        var uwr = new UnityWebRequest(from);
+	        uwr.SendWebRequest().completed += ao =>
+	        {
+		        var success = false;
+		        try
+		        {
+			        if ( uwr.result == UnityWebRequest.Result.Success)
+			        {
+				        File.WriteAllBytes(_dbPath, uwr.downloadHandler.data);
+				        success = true;
+			        }
+		        }
+		        catch (Exception e)
+		        {
+			        Debug.LogError(e);
+		        }
+		        onComplete?.Invoke(success);
+	        };
+        }
+
+
+
+
+		/// 
+		/// 获取表格
+		/// 
+		/// 
+		/// 
+        public IEnumerable CreatOrLoadTable() where T: new()
+        {
+	       _connection.CreateTable();
+	       return  _connection.Table();
+        }
+		
+        public List GetTableList()  where T: new()
+        {
+	        var tb = CreatOrLoadTable();
+	        return tb?.ToList() ?? new List();
+        }
+		
+		
+		
+        public int Insert(T value)
+        {
+	        return _connection.Insert(value);
+        }
+
+        public int InsertAll(IEnumerable data)
+        {
+	        return _connection.InsertAll(data);
+        }
+
+        public int Remove(object primaryKey)
+        {
+	        return _connection.Delete(primaryKey);
+        }
+
+
+        public int Remove(object primaryKey)
+        {
+	        return _connection.Delete(primaryKey);
+        }
+
+        public int UpdateAll(IEnumerable data)
+        {
+	        return _connection.UpdateAll(data);
+        }
+
+        public T Get(object primaryKey) where T : new()
+        {
+	        return _connection.Get(primaryKey);
+        }
+
+
+        public T Find(Expression> predicate) where T : new()
+        {
+	        return _connection.Find(predicate);
+        }
+
+        private bool _onCmdExecution = false;
+        /// 
+        /// 执行 SQL 语句
+        /// 
+        /// 
+        /// 
+        /// 
+        /// 
+        public int Execute(string query, Action onExecutionComplete = null,  params object[] args)
+        {
+	        if (_onCmdExecution) return -1;
+	        
+	        SQLiteConnection.TimeExecutionHandler del = null;
+	        del = (t1, t2) =>
+	        {
+		        _onCmdExecution = false;
+		        _connection.TimeExecutionEvent -= del;
+		        onExecutionComplete?.Invoke(t2.TotalSeconds);
+	        };
+	        _connection.TimeExecutionEvent += del;
+	        _onCmdExecution = true;
+	        return _connection.Execute(query, args);
+        }
+
+
+    }
+    
+    
+    
+    
+}
\ No newline at end of file
diff --git a/Runtime/GuruLibs/Datebase/DBService.cs.meta b/Runtime/GuruLibs/Datebase/DBService.cs.meta
new file mode 100644
index 0000000..e39d715
--- /dev/null
+++ b/Runtime/GuruLibs/Datebase/DBService.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 230a328bcc91476ea24d47a0ecbdf441
+timeCreated: 1706926988
\ No newline at end of file
diff --git a/Runtime/GuruLibs/Datebase/GuruDB.cs b/Runtime/GuruLibs/Datebase/GuruDB.cs
new file mode 100644
index 0000000..e13ef5f
--- /dev/null
+++ b/Runtime/GuruLibs/Datebase/GuruDB.cs
@@ -0,0 +1,54 @@
+
+
+using SQLite4Unity3d;
+
+namespace Guru
+{
+    using System.Collections.Generic;
+    
+    
+    
+    public class GuruDB
+    {
+
+
+        private static GuruDB _instance;
+        public static GuruDB Instance
+        {
+            get
+            {
+                if(_instance == null) _instance = new GuruDB();
+                return _instance;
+            }
+        }
+
+        public int dbVersion = 1;
+        public string dbName = "guru";
+        private DBService _dbService;
+        internal DBService Service => _dbService;
+        public bool IsDebug { get; private set; } = false;
+        public bool IsReady { get; private set; }
+
+        public GuruDB()
+        {
+#if UNITY_EDITOR
+            IsDebug = true;
+#endif
+            _dbService = DBService.Open(dbName, IsDebug);
+
+            IsReady = _dbService != null;
+        }
+    }
+
+
+    public interface IDBItem
+    {
+        [PrimaryKey]
+        string id { get; set; }
+    }
+
+
+
+
+
+}
\ No newline at end of file
diff --git a/Runtime/GuruLibs/Datebase/GuruDB.cs.meta b/Runtime/GuruLibs/Datebase/GuruDB.cs.meta
new file mode 100644
index 0000000..f6082ce
--- /dev/null
+++ b/Runtime/GuruLibs/Datebase/GuruDB.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 1c3e8542bfad4731b0c54abf3afe7cf3
+timeCreated: 1706923244
\ No newline at end of file
diff --git a/Runtime/GuruLibs/Datebase/UnityTest.meta b/Runtime/GuruLibs/Datebase/UnityTest.meta
new file mode 100644
index 0000000..2cf7259
--- /dev/null
+++ b/Runtime/GuruLibs/Datebase/UnityTest.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: efa1c022e13449558b8d4d620fae4e69
+timeCreated: 1706927602
\ No newline at end of file
diff --git a/Runtime/GuruLibs/Json.meta b/Runtime/GuruLibs/Json.meta
new file mode 100644
index 0000000..cb696a3
--- /dev/null
+++ b/Runtime/GuruLibs/Json.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 84ef5b243482457ab96d4c376fec23d6
+timeCreated: 1706949578
\ No newline at end of file
diff --git a/Runtime/GuruLibs/Json/IJsonData.cs b/Runtime/GuruLibs/Json/IJsonData.cs
new file mode 100644
index 0000000..16839a2
--- /dev/null
+++ b/Runtime/GuruLibs/Json/IJsonData.cs
@@ -0,0 +1,7 @@
+namespace Guru
+{
+    public interface IJsonData
+    {
+        
+    }
+}
\ No newline at end of file
diff --git a/Runtime/GuruLibs/Json/IJsonData.cs.meta b/Runtime/GuruLibs/Json/IJsonData.cs.meta
new file mode 100644
index 0000000..870b1f2
--- /dev/null
+++ b/Runtime/GuruLibs/Json/IJsonData.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 68451c547db14e748cb50b5e773c6f00
+timeCreated: 1706949587
\ No newline at end of file
diff --git a/Runtime/GuruLibs/Json/JsonDataHelper.cs b/Runtime/GuruLibs/Json/JsonDataHelper.cs
new file mode 100644
index 0000000..e991ed2
--- /dev/null
+++ b/Runtime/GuruLibs/Json/JsonDataHelper.cs
@@ -0,0 +1,62 @@
+
+using System;
+using UnityEngine;
+
+namespace Guru
+{
+    using Newtonsoft.Json;
+
+    
+    
+    public static class JsonDataHelper
+    {
+        public static Action ExceptionHandler;
+
+        
+        public static string ToJsonString(object obj)
+        {
+            try
+            {
+                return JsonConvert.SerializeObject(obj);
+            }
+            catch (Exception e)
+            {
+                ExceptionHandler?.Invoke(e);
+                Debug.LogError(e);
+            }
+            return "";
+        }
+
+
+        public static bool Parse(string json, out T result)
+        {
+            bool success = false;
+            result = default(T);
+            
+            if(string.IsNullOrEmpty(json)) return false;
+            
+            try
+            {
+                result =  JsonConvert.DeserializeObject(json);
+                success = true;
+            }
+            catch (Exception e)
+            {
+                ExceptionHandler?.Invoke(e);
+                Debug.LogError(e);
+            }
+
+            return success;
+        }
+
+
+
+        public static string ToJson(this IJsonData  obj)
+        {
+            return ToJsonString((object)obj);
+        }
+
+        
+
+    }
+}
\ No newline at end of file
diff --git a/Runtime/GuruLibs/Json/JsonDataHelper.cs.meta b/Runtime/GuruLibs/Json/JsonDataHelper.cs.meta
new file mode 100644
index 0000000..e74c972
--- /dev/null
+++ b/Runtime/GuruLibs/Json/JsonDataHelper.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: ce50b36309b34a41b8bb844efbba1a17
+timeCreated: 1706949612
\ No newline at end of file
diff --git a/Runtime/GuruLibs/Property.meta b/Runtime/GuruLibs/Property.meta
new file mode 100644
index 0000000..0b45618
--- /dev/null
+++ b/Runtime/GuruLibs/Property.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 043ca2bcb28a45fba7edf46e5819a53b
+timeCreated: 1706923871
\ No newline at end of file
diff --git a/Runtime/GuruLibs/Property/DB.meta b/Runtime/GuruLibs/Property/DB.meta
new file mode 100644
index 0000000..2c6b1a5
--- /dev/null
+++ b/Runtime/GuruLibs/Property/DB.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: e4425fb6790b4f16b10a6ac9545f087c
+timeCreated: 1706925944
\ No newline at end of file
diff --git a/Runtime/GuruLibs/Property/DB/PropertyDatabase.cs b/Runtime/GuruLibs/Property/DB/PropertyDatabase.cs
new file mode 100644
index 0000000..ef8bf91
--- /dev/null
+++ b/Runtime/GuruLibs/Property/DB/PropertyDatabase.cs
@@ -0,0 +1,48 @@
+using System;
+
+namespace Guru
+{
+    
+    public partial class PropertyDatabase
+    {
+        
+        
+        
+        
+        
+        
+        
+    }
+
+
+
+
+    [Serializable]
+    public class PropertyEntity
+    {
+        const string TableName = "properties";
+        // internal const string dbKey = "key";
+        // internal const string dbValue = "value";
+        // internal const string dbGroup = "gp";
+        // internal const string dbUsage = "usage";
+        // internal const string dbTag = "tag";
+        // internal const string dbUpdateAt = "upt";
+
+        public string key;
+        public string value;
+        public string gp = PropertyKey.DefaultGroup;
+        public int usage = PropertyKey.UsageGeneral;
+        public string tag;
+        public string upt;
+        public int updateAt = 0;
+        
+        
+       
+        
+        
+
+
+    }
+
+
+}
\ No newline at end of file
diff --git a/Runtime/GuruLibs/Property/DB/PropertyDatabase.cs.meta b/Runtime/GuruLibs/Property/DB/PropertyDatabase.cs.meta
new file mode 100644
index 0000000..516d3c0
--- /dev/null
+++ b/Runtime/GuruLibs/Property/DB/PropertyDatabase.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 357ec7a04d0a454086a09bd2f2fcfe79
+timeCreated: 1706926072
\ No newline at end of file
diff --git a/Runtime/GuruLibs/Property/PropertyKey.cs b/Runtime/GuruLibs/Property/PropertyKey.cs
new file mode 100644
index 0000000..decafb4
--- /dev/null
+++ b/Runtime/GuruLibs/Property/PropertyKey.cs
@@ -0,0 +1,59 @@
+namespace Guru
+{
+    public struct PropertyKey
+    {
+        public const int UsageGeneral = 0;
+        public const int UsageSettings = 1;
+        public const string DefaultGroup = "guru";
+
+        public string name { get; private set; }
+        public string key { get; private set; }
+        public string group { get; private set; }
+        public string tag { get; private set; }
+        public int usage { get; private set; }
+        
+        public static PropertyKey General(string name, string group = DefaultGroup, string tag = "")
+        {
+            return new PropertyKey
+            {
+                name = name,
+                key = $"{group}@{name}",
+                group = group,
+                tag = tag,
+                usage = UsageGeneral
+            };
+        }
+        
+        public static PropertyKey Setting(string name, string group = DefaultGroup, string tag = "")
+        {
+            return new PropertyKey
+            {
+                name = name,
+                key = $"{group}@{name}",
+                group = group,
+                tag = tag,
+                usage = UsageSettings
+            };
+        }
+        
+        /// 
+        /// 判断两个属性 Key 是否相等
+        /// 
+        /// 
+        /// 
+        public  bool Equals(PropertyKey other)
+        {
+            return name == other.name && 
+                   key == other.key && 
+                   group == other.group && 
+                   tag == other.tag &&
+                   usage == other.usage;
+        }
+
+        public override string ToString()
+        {
+            return $"#{tag}#[{key}]({usage})";
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/Runtime/GuruLibs/Property/PropertyKey.cs.meta b/Runtime/GuruLibs/Property/PropertyKey.cs.meta
new file mode 100644
index 0000000..868180e
--- /dev/null
+++ b/Runtime/GuruLibs/Property/PropertyKey.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 5b1517229c974c46ac85c3c8f8b3b330
+timeCreated: 1706924244
\ No newline at end of file
diff --git a/Runtime/GuruLibs/Property/Storage.meta b/Runtime/GuruLibs/Property/Storage.meta
new file mode 100644
index 0000000..90746da
--- /dev/null
+++ b/Runtime/GuruLibs/Property/Storage.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: d50e6dd9a1324036af9e950f5ec489ed
+timeCreated: 1706925986
\ No newline at end of file
diff --git a/Runtime/GuruLibs/Property/Storage/IPropertyStorage.cs b/Runtime/GuruLibs/Property/Storage/IPropertyStorage.cs
new file mode 100644
index 0000000..c1be5e5
--- /dev/null
+++ b/Runtime/GuruLibs/Property/Storage/IPropertyStorage.cs
@@ -0,0 +1,19 @@
+namespace Guru
+{
+    public interface IPropertyStorage
+    {
+        void SetDouble(PropertyKey key, double value);
+        void SetInt(PropertyKey key, int value);
+        void SetBool(PropertyKey key, bool value);
+        void SetString(PropertyKey key, string value);
+        double? GetDouble(PropertyKey key, double? defaultValue);
+        int? GetInt(PropertyKey key, int? defaultValue);
+        bool? GetBool(PropertyKey key, bool? defaultValue);
+        string GetString(PropertyKey key, string defaultValue);
+        
+        void remove(PropertyKey key);
+        void removeAllWithTag(string tag);
+        
+        
+    }
+}
\ No newline at end of file
diff --git a/Runtime/GuruLibs/Property/Storage/IPropertyStorage.cs.meta b/Runtime/GuruLibs/Property/Storage/IPropertyStorage.cs.meta
new file mode 100644
index 0000000..5fc3f2f
--- /dev/null
+++ b/Runtime/GuruLibs/Property/Storage/IPropertyStorage.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 761fa4520c9746b5b14e27078be64003
+timeCreated: 1706923906
\ No newline at end of file
diff --git a/Runtime/GuruLibs/SQLite4Unity3d.meta b/Runtime/GuruLibs/SQLite4Unity3d.meta
new file mode 100644
index 0000000..906a3bf
--- /dev/null
+++ b/Runtime/GuruLibs/SQLite4Unity3d.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 36d6b6e33eeef40509c8647e2d7c555d
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Runtime/GuruLibs/SQLite4Unity3d/Plugins.meta b/Runtime/GuruLibs/SQLite4Unity3d/Plugins.meta
new file mode 100644
index 0000000..00d3d9f
--- /dev/null
+++ b/Runtime/GuruLibs/SQLite4Unity3d/Plugins.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 4475ec4c51944461ba8479536dfcda61
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Runtime/GuruLibs/SQLite4Unity3d/Plugins/Android.meta b/Runtime/GuruLibs/SQLite4Unity3d/Plugins/Android.meta
new file mode 100644
index 0000000..2b78603
--- /dev/null
+++ b/Runtime/GuruLibs/SQLite4Unity3d/Plugins/Android.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: f288c6262551743be968c8c546dd065e
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Runtime/GuruLibs/SQLite4Unity3d/Plugins/Android/libs.meta b/Runtime/GuruLibs/SQLite4Unity3d/Plugins/Android/libs.meta
new file mode 100644
index 0000000..216ab9a
--- /dev/null
+++ b/Runtime/GuruLibs/SQLite4Unity3d/Plugins/Android/libs.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: c1aacb5a55fc94e4da727839f3ea4d24
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Runtime/GuruLibs/SQLite4Unity3d/Plugins/Android/libs/arm64-v8a.meta b/Runtime/GuruLibs/SQLite4Unity3d/Plugins/Android/libs/arm64-v8a.meta
new file mode 100644
index 0000000..e6bae48
--- /dev/null
+++ b/Runtime/GuruLibs/SQLite4Unity3d/Plugins/Android/libs/arm64-v8a.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 8a0518887aa4d45798babfb2249cdd6d
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Runtime/GuruLibs/SQLite4Unity3d/Plugins/Android/libs/arm64-v8a/libsqlite3.so b/Runtime/GuruLibs/SQLite4Unity3d/Plugins/Android/libs/arm64-v8a/libsqlite3.so
new file mode 100644
index 0000000..1e2ce2d
Binary files /dev/null and b/Runtime/GuruLibs/SQLite4Unity3d/Plugins/Android/libs/arm64-v8a/libsqlite3.so differ
diff --git a/Runtime/GuruLibs/SQLite4Unity3d/Plugins/Android/libs/arm64-v8a/libsqlite3.so.meta b/Runtime/GuruLibs/SQLite4Unity3d/Plugins/Android/libs/arm64-v8a/libsqlite3.so.meta
new file mode 100644
index 0000000..99ffb87
--- /dev/null
+++ b/Runtime/GuruLibs/SQLite4Unity3d/Plugins/Android/libs/arm64-v8a/libsqlite3.so.meta
@@ -0,0 +1,27 @@
+fileFormatVersion: 2
+guid: 035fff0fb0daa454f882f263a44d8a71
+PluginImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  iconMap: {}
+  executionOrder: {}
+  defineConstraints: []
+  isPreloaded: 0
+  isOverridable: 0
+  isExplicitlyReferenced: 0
+  validateReferences: 1
+  platformData:
+  - first:
+      Any: 
+    second:
+      enabled: 1
+      settings: {}
+  - first:
+      Editor: Editor
+    second:
+      enabled: 0
+      settings:
+        DefaultValueInitialized: true
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Runtime/GuruLibs/SQLite4Unity3d/Plugins/Android/libs/armeabi-v7a.meta b/Runtime/GuruLibs/SQLite4Unity3d/Plugins/Android/libs/armeabi-v7a.meta
new file mode 100644
index 0000000..2e5a3c1
--- /dev/null
+++ b/Runtime/GuruLibs/SQLite4Unity3d/Plugins/Android/libs/armeabi-v7a.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: a8bf69720c6da40ff9d7ada1a6e52198
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Runtime/GuruLibs/SQLite4Unity3d/Plugins/Android/libs/armeabi-v7a/libsqlite3.so b/Runtime/GuruLibs/SQLite4Unity3d/Plugins/Android/libs/armeabi-v7a/libsqlite3.so
new file mode 100644
index 0000000..2c6e2bf
Binary files /dev/null and b/Runtime/GuruLibs/SQLite4Unity3d/Plugins/Android/libs/armeabi-v7a/libsqlite3.so differ
diff --git a/Runtime/GuruLibs/SQLite4Unity3d/Plugins/Android/libs/armeabi-v7a/libsqlite3.so.meta b/Runtime/GuruLibs/SQLite4Unity3d/Plugins/Android/libs/armeabi-v7a/libsqlite3.so.meta
new file mode 100644
index 0000000..2a73927
--- /dev/null
+++ b/Runtime/GuruLibs/SQLite4Unity3d/Plugins/Android/libs/armeabi-v7a/libsqlite3.so.meta
@@ -0,0 +1,27 @@
+fileFormatVersion: 2
+guid: fbfa6b2ad32f74cb9ba69b1613f0ac8b
+PluginImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  iconMap: {}
+  executionOrder: {}
+  defineConstraints: []
+  isPreloaded: 0
+  isOverridable: 0
+  isExplicitlyReferenced: 0
+  validateReferences: 1
+  platformData:
+  - first:
+      Any: 
+    second:
+      enabled: 1
+      settings: {}
+  - first:
+      Editor: Editor
+    second:
+      enabled: 0
+      settings:
+        DefaultValueInitialized: true
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Runtime/GuruLibs/SQLite4Unity3d/Plugins/Android/libs/x86.meta b/Runtime/GuruLibs/SQLite4Unity3d/Plugins/Android/libs/x86.meta
new file mode 100644
index 0000000..b367fb2
--- /dev/null
+++ b/Runtime/GuruLibs/SQLite4Unity3d/Plugins/Android/libs/x86.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: c0bc4319db8fc4e87b92d8849e10effa
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Runtime/GuruLibs/SQLite4Unity3d/Plugins/Android/libs/x86/libsqlite3.so b/Runtime/GuruLibs/SQLite4Unity3d/Plugins/Android/libs/x86/libsqlite3.so
new file mode 100644
index 0000000..9d08844
Binary files /dev/null and b/Runtime/GuruLibs/SQLite4Unity3d/Plugins/Android/libs/x86/libsqlite3.so differ
diff --git a/Runtime/GuruLibs/SQLite4Unity3d/Plugins/Android/libs/x86/libsqlite3.so.meta b/Runtime/GuruLibs/SQLite4Unity3d/Plugins/Android/libs/x86/libsqlite3.so.meta
new file mode 100644
index 0000000..07b59b5
--- /dev/null
+++ b/Runtime/GuruLibs/SQLite4Unity3d/Plugins/Android/libs/x86/libsqlite3.so.meta
@@ -0,0 +1,27 @@
+fileFormatVersion: 2
+guid: 533c36573354e4e82a0871a3ae353c64
+PluginImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  iconMap: {}
+  executionOrder: {}
+  defineConstraints: []
+  isPreloaded: 0
+  isOverridable: 0
+  isExplicitlyReferenced: 0
+  validateReferences: 1
+  platformData:
+  - first:
+      Any: 
+    second:
+      enabled: 1
+      settings: {}
+  - first:
+      Editor: Editor
+    second:
+      enabled: 0
+      settings:
+        DefaultValueInitialized: true
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Runtime/GuruLibs/SQLite4Unity3d/Plugins/x64.meta b/Runtime/GuruLibs/SQLite4Unity3d/Plugins/x64.meta
new file mode 100644
index 0000000..7cc330b
--- /dev/null
+++ b/Runtime/GuruLibs/SQLite4Unity3d/Plugins/x64.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 87d29778372204305920a77087f262a4
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Runtime/GuruLibs/SQLite4Unity3d/Plugins/x64/sqlite3.dll b/Runtime/GuruLibs/SQLite4Unity3d/Plugins/x64/sqlite3.dll
new file mode 100644
index 0000000..6f07d5e
Binary files /dev/null and b/Runtime/GuruLibs/SQLite4Unity3d/Plugins/x64/sqlite3.dll differ
diff --git a/Runtime/GuruLibs/SQLite4Unity3d/Plugins/x64/sqlite3.dll.meta b/Runtime/GuruLibs/SQLite4Unity3d/Plugins/x64/sqlite3.dll.meta
new file mode 100644
index 0000000..ce0b494
--- /dev/null
+++ b/Runtime/GuruLibs/SQLite4Unity3d/Plugins/x64/sqlite3.dll.meta
@@ -0,0 +1,52 @@
+fileFormatVersion: 2
+guid: 25884d71c32604d7987793cbcbe801ac
+PluginImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  iconMap: {}
+  executionOrder: {}
+  defineConstraints: []
+  isPreloaded: 0
+  isOverridable: 0
+  isExplicitlyReferenced: 0
+  validateReferences: 1
+  platformData:
+  - first:
+      Any: 
+    second:
+      enabled: 1
+      settings: {}
+  - first:
+      Editor: Editor
+    second:
+      enabled: 0
+      settings:
+        CPU: x86_64
+        DefaultValueInitialized: true
+  - first:
+      Standalone: Linux64
+    second:
+      enabled: 1
+      settings:
+        CPU: x86_64
+  - first:
+      Standalone: OSXUniversal
+    second:
+      enabled: 1
+      settings:
+        CPU: x86_64
+  - first:
+      Standalone: Win
+    second:
+      enabled: 0
+      settings:
+        CPU: None
+  - first:
+      Standalone: Win64
+    second:
+      enabled: 1
+      settings:
+        CPU: x86_64
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Runtime/GuruLibs/SQLite4Unity3d/Plugins/x86.meta b/Runtime/GuruLibs/SQLite4Unity3d/Plugins/x86.meta
new file mode 100644
index 0000000..ceb30a7
--- /dev/null
+++ b/Runtime/GuruLibs/SQLite4Unity3d/Plugins/x86.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: f7a5439d4fb9e4828937865037857668
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Runtime/GuruLibs/SQLite4Unity3d/Plugins/x86/sqlite3.dll b/Runtime/GuruLibs/SQLite4Unity3d/Plugins/x86/sqlite3.dll
new file mode 100644
index 0000000..d48a7ef
Binary files /dev/null and b/Runtime/GuruLibs/SQLite4Unity3d/Plugins/x86/sqlite3.dll differ
diff --git a/Runtime/GuruLibs/SQLite4Unity3d/Plugins/x86/sqlite3.dll.meta b/Runtime/GuruLibs/SQLite4Unity3d/Plugins/x86/sqlite3.dll.meta
new file mode 100644
index 0000000..e7cfc0e
--- /dev/null
+++ b/Runtime/GuruLibs/SQLite4Unity3d/Plugins/x86/sqlite3.dll.meta
@@ -0,0 +1,52 @@
+fileFormatVersion: 2
+guid: 8f6a8ec41de2b4f569e23b20011eec8c
+PluginImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  iconMap: {}
+  executionOrder: {}
+  defineConstraints: []
+  isPreloaded: 0
+  isOverridable: 0
+  isExplicitlyReferenced: 0
+  validateReferences: 1
+  platformData:
+  - first:
+      Any: 
+    second:
+      enabled: 1
+      settings: {}
+  - first:
+      Editor: Editor
+    second:
+      enabled: 0
+      settings:
+        CPU: x86
+        DefaultValueInitialized: true
+  - first:
+      Standalone: Linux64
+    second:
+      enabled: 0
+      settings:
+        CPU: None
+  - first:
+      Standalone: OSXUniversal
+    second:
+      enabled: 0
+      settings:
+        CPU: x86
+  - first:
+      Standalone: Win
+    second:
+      enabled: 1
+      settings:
+        CPU: x86
+  - first:
+      Standalone: Win64
+    second:
+      enabled: 0
+      settings:
+        CPU: None
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Runtime/GuruLibs/SQLite4Unity3d/SQLite.cs b/Runtime/GuruLibs/SQLite4Unity3d/SQLite.cs
new file mode 100644
index 0000000..18ce6c3
--- /dev/null
+++ b/Runtime/GuruLibs/SQLite4Unity3d/SQLite.cs
@@ -0,0 +1,3377 @@
+//
+// Copyright (c) 2009-2012 Krueger Systems, Inc.
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+#if WINDOWS_PHONE && !USE_WP8_NATIVE_SQLITE
+#define USE_CSHARP_SQLITE
+#endif
+
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Threading;
+
+#if USE_CSHARP_SQLITE
+using Sqlite3 = Community.CsharpSqlite.Sqlite3;
+using Sqlite3DatabaseHandle = Community.CsharpSqlite.Sqlite3.sqlite3;
+using Sqlite3Statement = Community.CsharpSqlite.Sqlite3.Vdbe;
+#elif USE_WP8_NATIVE_SQLITE
+using Sqlite3 = Sqlite.Sqlite3;
+using Sqlite3DatabaseHandle = Sqlite.Database;
+using Sqlite3Statement = Sqlite.Statement;
+#else
+using Sqlite3DatabaseHandle = System.IntPtr;
+using Sqlite3Statement = System.IntPtr;
+#endif
+
+namespace SQLite4Unity3d
+{
+	public class SQLiteException : Exception
+	{
+		public SQLite3.Result Result { get; private set; }
+
+		protected SQLiteException (SQLite3.Result r,string message) : base(message)
+		{
+			Result = r;
+		}
+
+		public static SQLiteException New (SQLite3.Result r, string message)
+		{
+			return new SQLiteException (r, message);
+		}
+	}
+
+	public class NotNullConstraintViolationException : SQLiteException
+	{
+		public IEnumerable Columns { get; protected set; }
+
+		protected NotNullConstraintViolationException (SQLite3.Result r, string message)
+			: this (r, message, null, null)
+		{
+
+		}
+
+		protected NotNullConstraintViolationException (SQLite3.Result r, string message, TableMapping mapping, object obj)
+			: base (r, message)
+		{
+			if (mapping != null && obj != null) {
+				this.Columns = from c in mapping.Columns
+							   where c.IsNullable == false && c.GetValue(obj) == null
+							   select c;
+			}
+		}
+
+		public static new NotNullConstraintViolationException New (SQLite3.Result r, string message)
+		{
+			return new NotNullConstraintViolationException (r, message);
+		}
+
+		public static NotNullConstraintViolationException New (SQLite3.Result r, string message, TableMapping mapping, object obj)
+		{
+			return new NotNullConstraintViolationException (r, message, mapping, obj);
+		}
+
+		public static NotNullConstraintViolationException New (SQLiteException exception, TableMapping mapping, object obj)
+		{
+			return new NotNullConstraintViolationException (exception.Result, exception.Message, mapping, obj);
+		}
+	}
+
+	[Flags]
+	public enum SQLiteOpenFlags {
+		ReadOnly = 1, ReadWrite = 2, Create = 4,
+		NoMutex = 0x8000, FullMutex = 0x10000,
+		SharedCache = 0x20000, PrivateCache = 0x40000,
+		ProtectionComplete = 0x00100000,
+		ProtectionCompleteUnlessOpen = 0x00200000,
+		ProtectionCompleteUntilFirstUserAuthentication = 0x00300000,
+		ProtectionNone = 0x00400000
+	}
+
+	[Flags]
+	public enum CreateFlags
+	{
+		None = 0,
+		ImplicitPK = 1,    // create a primary key for field called 'Id' (Orm.ImplicitPkName)
+		ImplicitIndex = 2, // create an index for fields ending in 'Id' (Orm.ImplicitIndexSuffix)
+		AllImplicit = 3,   // do both above
+
+		AutoIncPK = 4      // force PK field to be auto inc
+	}
+
+	/// 
+	/// Represents an open connection to a SQLite database.
+	/// 
+	public partial class SQLiteConnection : IDisposable
+	{
+		private bool _open;
+		private TimeSpan _busyTimeout;
+		private Dictionary _mappings = null;
+		private Dictionary _tables = null;
+		private System.Diagnostics.Stopwatch _sw;
+		private TimeSpan _elapsed = default(TimeSpan);
+
+		private int _transactionDepth = 0;
+		private Random _rand = new Random ();
+
+		public Sqlite3DatabaseHandle Handle { get; private set; }
+		internal static readonly Sqlite3DatabaseHandle NullHandle = default(Sqlite3DatabaseHandle);
+
+		public string DatabasePath { get; private set; }
+
+		// Dictionary of synchronization objects.
+		//
+		// To prevent database disruption, a database file must be accessed *synchronously*.
+		// For the purpose we create synchronous objects for each database file and store in the
+		// static dictionary to share it among all connections.
+		// The key of the dictionary is database file path and its value is an object to be used
+		// by lock() statement.
+		//
+		// Use case:
+		// - database file lock is done implicitly and automatically.
+		// - To prepend deadlock, application may lock a database file explicity by either way:
+		//   - RunInTransaction(Action) locks the database during the transaction (for insert/update)
+		//   - RunInDatabaseLock(Action) similarly locks the database but no transaction (for query)
+		private static Dictionary syncObjects = new Dictionary();
+
+		#region debug tracing
+
+		public bool Trace { get; set; }
+        public bool TimeExecution { get; set; }
+
+		public delegate void TraceHandler (string message);
+		public event TraceHandler TraceEvent;
+
+		internal void InvokeTrace (string message)
+		{
+			if (TraceEvent != null) {
+				TraceEvent(message);
+			}
+		}
+
+		public delegate void TimeExecutionHandler (TimeSpan executionTime, TimeSpan totalExecutionTime);
+		public event TimeExecutionHandler TimeExecutionEvent;
+
+		internal void InvokeTimeExecution(TimeSpan executionTime, TimeSpan totalExecutionTime)
+		{
+			if (TimeExecutionEvent != null) {
+				TimeExecutionEvent(executionTime, totalExecutionTime);
+			}
+		}
+
+		#endregion
+
+		public bool StoreDateTimeAsTicks { get; private set; }
+
+		/// 
+		/// Constructs a new SQLiteConnection and opens a SQLite database specified by databasePath.
+		/// 
+		/// 
+		/// Specifies the path to the database file.
+		/// 
+		/// 
+		/// Specifies whether to store DateTime properties as ticks (true) or strings (false). You
+		/// absolutely do want to store them as Ticks in all new projects. The default of false is
+		/// only here for backwards compatibility. There is a *significant* speed advantage, with no
+		/// down sides, when setting storeDateTimeAsTicks = true.
+		/// 
+		public SQLiteConnection (string databasePath, bool storeDateTimeAsTicks = false)
+			: this (databasePath, SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create, storeDateTimeAsTicks)
+		{
+		}
+
+		/// 
+		/// Constructs a new SQLiteConnection and opens a SQLite database specified by databasePath.
+		/// 
+		/// 
+		/// Specifies the path to the database file.
+		/// 
+		/// 
+		/// Specifies whether to store DateTime properties as ticks (true) or strings (false). You
+		/// absolutely do want to store them as Ticks in all new projects. The default of false is
+		/// only here for backwards compatibility. There is a *significant* speed advantage, with no
+		/// down sides, when setting storeDateTimeAsTicks = true.
+		/// 
+		public SQLiteConnection (string databasePath, SQLiteOpenFlags openFlags, bool storeDateTimeAsTicks = false)
+		{
+			if (string.IsNullOrEmpty (databasePath))
+				throw new ArgumentException ("Must be specified", "databasePath");
+
+			DatabasePath = databasePath;
+			mayCreateSyncObject(databasePath);
+
+#if NETFX_CORE
+			SQLite3.SetDirectory(/*temp directory type*/2, Windows.Storage.ApplicationData.Current.TemporaryFolder.Path);
+#endif
+
+			Sqlite3DatabaseHandle handle;
+
+#if SILVERLIGHT || USE_CSHARP_SQLITE
+			var r = SQLite3.Open (databasePath, out handle, (int)openFlags, IntPtr.Zero);
+#else
+			// open using the byte[]
+			// in the case where the path may include Unicode
+			// force open to using UTF-8 using sqlite3_open_v2
+			var databasePathAsBytes = GetNullTerminatedUtf8 (DatabasePath);
+			var r = SQLite3.Open (databasePathAsBytes, out handle, (int) openFlags, IntPtr.Zero);
+#endif
+
+			Handle = handle;
+			if (r != SQLite3.Result.OK) {
+				throw SQLiteException.New (r, String.Format ("Could not open database file: {0} ({1})", DatabasePath, r));
+			}
+			_open = true;
+
+			StoreDateTimeAsTicks = storeDateTimeAsTicks;
+			
+			BusyTimeout = TimeSpan.FromSeconds (0.1);
+		}
+		
+		static SQLiteConnection ()
+		{
+			if (_preserveDuringLinkMagic) {
+				var ti = new ColumnInfo ();
+				ti.Name = "magic";
+			}
+		}
+
+		void mayCreateSyncObject(string databasePath)
+		{
+			if (!syncObjects.ContainsKey(databasePath)) {
+				syncObjects[databasePath] = new object();
+			}
+		}
+
+		/// 
+		/// Gets the synchronous object, to be lock the database file for updating.
+		/// 
+		/// The sync object.
+		public object SyncObject { get { return syncObjects[DatabasePath];} }
+
+		public void EnableLoadExtension(int onoff)
+		{
+			SQLite3.Result r = SQLite3.EnableLoadExtension(Handle, onoff);
+			if (r != SQLite3.Result.OK) {
+				string msg = SQLite3.GetErrmsg (Handle);
+				throw SQLiteException.New (r, msg);
+			}
+		}
+
+		static byte[] GetNullTerminatedUtf8 (string s)
+		{
+			var utf8Length = System.Text.Encoding.UTF8.GetByteCount (s);
+			var bytes = new byte [utf8Length + 1];
+			utf8Length = System.Text.Encoding.UTF8.GetBytes(s, 0, s.Length, bytes, 0);
+			return bytes;
+		}
+		
+		/// 
+		/// Used to list some code that we want the MonoTouch linker
+		/// to see, but that we never want to actually execute.
+		/// 
+		#pragma warning disable 649
+		static bool _preserveDuringLinkMagic;
+		#pragma warning restore 649
+
+		/// 
+		/// Sets a busy handler to sleep the specified amount of time when a table is locked.
+		/// The handler will sleep multiple times until a total time of  has accumulated.
+		/// 
+		public TimeSpan BusyTimeout {
+			get { return _busyTimeout; }
+			set {
+				_busyTimeout = value;
+				if (Handle != NullHandle) {
+					SQLite3.BusyTimeout (Handle, (int)_busyTimeout.TotalMilliseconds);
+				}
+			}
+		}
+
+		/// 
+		/// Returns the mappings from types to tables that the connection
+		/// currently understands.
+		/// 
+		public IEnumerable TableMappings {
+			get {
+				return _tables != null ? _tables.Values : Enumerable.Empty ();
+			}
+		}
+
+		/// 
+		/// Retrieves the mapping that is automatically generated for the given type.
+		/// 
+		/// 
+		/// The type whose mapping to the database is returned.
+		///          
+		/// 
+		/// Optional flags allowing implicit PK and indexes based on naming conventions
+		///      
+		/// 
+		/// The mapping represents the schema of the columns of the database and contains 
+		/// methods to set and get properties of objects.
+		/// 
+		public TableMapping GetMapping(Type type, CreateFlags createFlags = CreateFlags.None)
+		{
+			if (_mappings == null) {
+				_mappings = new Dictionary ();
+			}
+			TableMapping map;
+			if (!_mappings.TryGetValue (type.FullName, out map)) {
+				map = new TableMapping (type, createFlags);
+				_mappings [type.FullName] = map;
+			}
+			return map;
+		}
+		
+		/// 
+		/// Retrieves the mapping that is automatically generated for the given type.
+		/// 
+		/// 
+		/// The mapping represents the schema of the columns of the database and contains 
+		/// methods to set and get properties of objects.
+		/// 
+		public TableMapping GetMapping ()
+		{
+			return GetMapping (typeof (T));
+		}
+
+		private struct IndexedColumn
+		{
+			public int Order;
+			public string ColumnName;
+		}
+
+		private struct IndexInfo
+		{
+			public string IndexName;
+			public string TableName;
+			public bool Unique;
+			public List Columns;
+		}
+
+		/// 
+		/// Executes a "drop table" on the database.  This is non-recoverable.
+		/// 
+		public int DropTable()
+		{
+			var map = GetMapping (typeof (T));
+
+			var query = string.Format("drop table if exists \"{0}\"", map.TableName);
+
+			return Execute (query);
+		}
+		
+		/// 
+		/// Executes a "create table if not exists" on the database. It also
+		/// creates any specified indexes on the columns of the table. It uses
+		/// a schema automatically generated from the specified type. You can
+		/// later access this schema by calling GetMapping.
+		/// 
+		/// 
+		/// The number of entries added to the database schema.
+		/// 
+		public int CreateTable(CreateFlags createFlags = CreateFlags.None)
+		{
+			return CreateTable(typeof (T), createFlags);
+		}
+
+		/// 
+		/// Executes a "create table if not exists" on the database. It also
+		/// creates any specified indexes on the columns of the table. It uses
+		/// a schema automatically generated from the specified type. You can
+		/// later access this schema by calling GetMapping.
+		/// 
+		/// Type to reflect to a database table.
+		/// Optional flags allowing implicit PK and indexes based on naming conventions.  
+		/// 
+		/// The number of entries added to the database schema.
+		/// 
+		public int CreateTable(Type ty, CreateFlags createFlags = CreateFlags.None)
+		{
+			if (_tables == null) {
+				_tables = new Dictionary ();
+			}
+			TableMapping map;
+			if (!_tables.TryGetValue (ty.FullName, out map)) {
+				map = GetMapping (ty, createFlags);
+				_tables.Add (ty.FullName, map);
+			}
+			var query = "create table if not exists \"" + map.TableName + "\"(\n";
+			
+			var decls = map.Columns.Select (p => Orm.SqlDecl (p, StoreDateTimeAsTicks));
+			var decl = string.Join (",\n", decls.ToArray ());
+			query += decl;
+			query += ")";
+			
+			var count = Execute (query);
+			
+			if (count == 0) { //Possible bug: This always seems to return 0?
+				// Table already exists, migrate it
+				MigrateTable (map);
+			}
+
+			var indexes = new Dictionary ();
+			foreach (var c in map.Columns) {
+				foreach (var i in c.Indices) {
+					var iname = i.Name ?? map.TableName + "_" + c.Name;
+					IndexInfo iinfo;
+					if (!indexes.TryGetValue (iname, out iinfo)) {
+						iinfo = new IndexInfo {
+							IndexName = iname,
+							TableName = map.TableName,
+							Unique = i.Unique,
+							Columns = new List ()
+						};
+						indexes.Add (iname, iinfo);
+					}
+
+					if (i.Unique != iinfo.Unique)
+						throw new Exception ("All the columns in an index must have the same value for their Unique property");
+
+					iinfo.Columns.Add (new IndexedColumn {
+						Order = i.Order,
+						ColumnName = c.Name
+					});
+				}
+			}
+
+			foreach (var indexName in indexes.Keys) {
+				var index = indexes[indexName];
+				string[] columnNames = new string[index.Columns.Count];
+				if (index.Columns.Count == 1) {
+					columnNames[0] = index.Columns[0].ColumnName;
+				} else {
+					index.Columns.Sort((lhs, rhs) => {
+						return lhs.Order - rhs.Order;
+					});
+					for (int i = 0, end = index.Columns.Count; i < end; ++i) {
+						columnNames[i] = index.Columns[i].ColumnName;
+					}
+				}
+				count += CreateIndex(indexName, index.TableName, columnNames, index.Unique);
+			}
+			
+			return count;
+		}
+
+		/// 
+		/// Creates an index for the specified table and columns.
+		/// 
+		/// Name of the index to create
+		/// Name of the database table
+		/// An array of column names to index
+		/// Whether the index should be unique
+		public int CreateIndex(string indexName, string tableName, string[] columnNames, bool unique = false)
+		{
+			const string sqlFormat = "create {2} index if not exists \"{3}\" on \"{0}\"(\"{1}\")";
+			var sql = String.Format(sqlFormat, tableName, string.Join ("\", \"", columnNames), unique ? "unique" : "", indexName);
+			return Execute(sql);
+		}
+
+		/// 
+		/// Creates an index for the specified table and column.
+		/// 
+		/// Name of the index to create
+		/// Name of the database table
+		/// Name of the column to index
+		/// Whether the index should be unique
+		public int CreateIndex(string indexName, string tableName, string columnName, bool unique = false)
+		{
+			return CreateIndex(indexName, tableName, new string[] { columnName }, unique);
+		}
+		
+		/// 
+		/// Creates an index for the specified table and column.
+		/// 
+		/// Name of the database table
+		/// Name of the column to index
+		/// Whether the index should be unique
+		public int CreateIndex(string tableName, string columnName, bool unique = false)
+		{
+			return CreateIndex(tableName + "_" + columnName, tableName, columnName, unique);
+		}
+
+		/// 
+		/// Creates an index for the specified table and columns.
+		/// 
+		/// Name of the database table
+		/// An array of column names to index
+		/// Whether the index should be unique
+		public int CreateIndex(string tableName, string[] columnNames, bool unique = false)
+		{
+			return CreateIndex(tableName + "_" + string.Join ("_", columnNames), tableName, columnNames, unique);
+		}
+
+		/// 
+		/// Creates an index for the specified object property.
+		/// e.g. CreateIndex(c => c.Name);
+		/// 
+		/// Type to reflect to a database table.
+		/// Property to index
+		/// Whether the index should be unique
+		public void CreateIndex(Expression> property, bool unique = false)
+		{
+			MemberExpression mx;
+			if (property.Body.NodeType == ExpressionType.Convert)
+			{
+				mx = ((UnaryExpression)property.Body).Operand as MemberExpression;
+			}
+			else
+			{
+				mx= (property.Body as MemberExpression);
+			}
+			var propertyInfo = mx.Member as PropertyInfo;
+			if (propertyInfo == null)
+			{
+				throw new ArgumentException("The lambda expression 'property' should point to a valid Property");
+			}
+
+			var propName = propertyInfo.Name;
+
+			var map = GetMapping();
+			var colName = map.FindColumnWithPropertyName(propName).Name;
+
+			CreateIndex(map.TableName, colName, unique);
+		}
+
+		public class ColumnInfo
+		{
+//			public int cid { get; set; }
+
+			[Column ("name")]
+			public string Name { get; set; }
+
+//			[Column ("type")]
+//			public string ColumnType { get; set; }
+
+			public int notnull { get; set; }
+
+//			public string dflt_value { get; set; }
+
+//			public int pk { get; set; }
+
+			public override string ToString ()
+			{
+				return Name;
+			}
+		}
+
+		public List GetTableInfo (string tableName)
+		{
+			var query = "pragma table_info(\"" + tableName + "\")";			
+			return Query (query);
+		}
+
+		void MigrateTable (TableMapping map)
+		{
+			var existingCols = GetTableInfo (map.TableName);
+			
+			var toBeAdded = new List ();
+			
+			foreach (var p in map.Columns) {
+				var found = false;
+				foreach (var c in existingCols) {
+					found = (string.Compare (p.Name, c.Name, StringComparison.OrdinalIgnoreCase) == 0);
+					if (found)
+						break;
+				}
+				if (!found) {
+					toBeAdded.Add (p);
+				}
+			}
+			
+			foreach (var p in toBeAdded) {
+				var addCol = "alter table \"" + map.TableName + "\" add column " + Orm.SqlDecl (p, StoreDateTimeAsTicks);
+				Execute (addCol);
+			}
+		}
+
+		/// 
+		/// Creates a new SQLiteCommand. Can be overridden to provide a sub-class.
+		/// 
+		/// 
+		protected virtual SQLiteCommand NewCommand ()
+		{
+			return new SQLiteCommand (this);
+		}
+
+		/// 
+		/// Creates a new SQLiteCommand given the command text with arguments. Place a '?'
+		/// in the command text for each of the arguments.
+		/// 
+		/// 
+		/// The fully escaped SQL.
+		/// 
+		/// 
+		/// Arguments to substitute for the occurences of '?' in the command text.
+		/// 
+		/// 
+		/// A 
+		/// 
+		public SQLiteCommand CreateCommand (string cmdText, params object[] ps)
+		{
+			if (!_open)
+				throw SQLiteException.New (SQLite3.Result.Error, "Cannot create commands from unopened database");
+
+			var cmd = NewCommand ();
+			cmd.CommandText = cmdText;
+			foreach (var o in ps) {
+				cmd.Bind (o);
+			}
+			return cmd;
+		}
+
+		/// 
+		/// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
+		/// in the command text for each of the arguments and then executes that command.
+		/// Use this method instead of Query when you don't expect rows back. Such cases include
+		/// INSERTs, UPDATEs, and DELETEs.
+		/// You can set the Trace or TimeExecution properties of the connection
+		/// to profile execution.
+		/// 
+		/// 
+		/// The fully escaped SQL.
+		/// 
+		/// 
+		/// Arguments to substitute for the occurences of '?' in the query.
+		/// 
+		/// 
+		/// The number of rows modified in the database as a result of this execution.
+		/// 
+		public int Execute (string query, params object[] args)
+		{
+			var cmd = CreateCommand (query, args);
+			
+			if (TimeExecution) {
+				if (_sw == null) {
+					_sw = new Stopwatch ();
+				}
+				_sw.Reset ();
+				_sw.Start ();
+			}
+
+			var r = cmd.ExecuteNonQuery ();
+			
+			if (TimeExecution) {
+				_sw.Stop ();
+				_elapsed += _sw.Elapsed;
+				this.InvokeTimeExecution (_sw.Elapsed, _elapsed);
+			}
+			
+			return r;
+		}
+
+		public T ExecuteScalar (string query, params object[] args)
+		{
+			var cmd = CreateCommand (query, args);
+			
+			if (TimeExecution) {
+				if (_sw == null) {
+					_sw = new Stopwatch ();
+				}
+				_sw.Reset ();
+				_sw.Start ();
+			}
+			
+			var r = cmd.ExecuteScalar ();
+			
+			if (TimeExecution) {
+				_sw.Stop ();
+				_elapsed += _sw.Elapsed;
+				this.InvokeTimeExecution (_sw.Elapsed, _elapsed);
+			}
+			
+			return r;
+		}
+
+		/// 
+		/// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
+		/// in the command text for each of the arguments and then executes that command.
+		/// It returns each row of the result using the mapping automatically generated for
+		/// the given type.
+		/// 
+		/// 
+		/// The fully escaped SQL.
+		/// 
+		/// 
+		/// Arguments to substitute for the occurences of '?' in the query.
+		/// 
+		/// 
+		/// An enumerable with one result for each row returned by the query.
+		/// 
+		public List Query (string query, params object[] args) where T : new()
+		{
+			var cmd = CreateCommand (query, args);
+			return cmd.ExecuteQuery ();
+		}
+
+		/// 
+		/// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
+		/// in the command text for each of the arguments and then executes that command.
+		/// It returns each row of the result using the mapping automatically generated for
+		/// the given type.
+		/// 
+		/// 
+		/// The fully escaped SQL.
+		/// 
+		/// 
+		/// Arguments to substitute for the occurences of '?' in the query.
+		/// 
+		/// 
+		/// An enumerable with one result for each row returned by the query.
+		/// The enumerator will call sqlite3_step on each call to MoveNext, so the database
+		/// connection must remain open for the lifetime of the enumerator.
+		/// 
+		public IEnumerable DeferredQuery(string query, params object[] args) where T : new()
+		{
+			var cmd = CreateCommand(query, args);
+			return cmd.ExecuteDeferredQuery();
+		}
+
+		/// 
+		/// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
+		/// in the command text for each of the arguments and then executes that command.
+		/// It returns each row of the result using the specified mapping. This function is
+		/// only used by libraries in order to query the database via introspection. It is
+		/// normally not used.
+		/// 
+		/// 
+		/// A  to use to convert the resulting rows
+		/// into objects.
+		/// 
+		/// 
+		/// The fully escaped SQL.
+		/// 
+		/// 
+		/// Arguments to substitute for the occurences of '?' in the query.
+		/// 
+		/// 
+		/// An enumerable with one result for each row returned by the query.
+		/// 
+		public List