├── .gitignore ├── Editor.meta ├── Editor ├── ConfigConstants.cs ├── ConfigConstants.cs.meta ├── ConfigMenu.cs ├── ConfigMenu.cs.meta ├── ConfigTools.cs ├── ConfigTools.cs.meta ├── ConfigWindow.cs ├── ConfigWindow.cs.meta ├── Generator.meta ├── Generator │ ├── SheetGenerator.cs │ ├── SheetGenerator.cs.meta │ ├── StructGenerator.cs │ └── StructGenerator.cs.meta ├── Library.meta ├── Library │ ├── EPPlus.meta │ └── EPPlus │ │ ├── EPPlus.XML │ │ ├── EPPlus.XML.meta │ │ ├── EPPlus.dll │ │ ├── EPPlus.dll.meta │ │ ├── readme.txt │ │ └── readme.txt.meta ├── Parser.meta ├── Parser │ ├── JSONParser.cs │ ├── JSONParser.cs.meta │ ├── SheetParser.cs │ ├── SheetParser.cs.meta │ ├── XmlParser.cs │ └── XmlParser.cs.meta ├── Serializer.meta ├── Serializer │ ├── DeserializerGenerator.cs │ ├── DeserializerGenerator.cs.meta │ ├── SerializableSetGenerator.cs │ ├── SerializableSetGenerator.cs.meta │ ├── Serializer.cs │ └── Serializer.cs.meta ├── Source.meta └── Source │ ├── SheetSource.cs │ ├── SheetSource.cs.meta │ ├── Source.cs │ ├── Source.cs.meta │ ├── StructSource.cs │ └── StructSource.cs.meta ├── README.en.md ├── README.en.md.meta ├── README.md └── README.md.meta /.gitignore: -------------------------------------------------------------------------------- 1 | #Unity缓存文件夹 2 | ./Library/ 3 | Temp/ 4 | 5 | #发布文件夹 6 | Bin-Windows/ 7 | Bin-Android/ 8 | 9 | #VS文件 10 | *.csproj 11 | *.sln 12 | *.suo 13 | *.user 14 | *.swp 15 | 16 | #项目文件 17 | Assets/CustomPlugins/ 18 | Assets/CustomPlugins.meta 19 | -------------------------------------------------------------------------------- /Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a2ee8938bd0a25c4aacf32ed0a298532 3 | folderAsset: yes 4 | timeCreated: 1499696488 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Editor/ConfigConstants.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Rick 3 | * Create: 7/22/2017 4:32:01 PM 4 | * Email: rickjiangshu@gmail.com 5 | * Follow: https://github.com/RickJiangShu 6 | */ 7 | namespace ConfigManagerEditor 8 | { 9 | using System; 10 | using System.Globalization; 11 | using System.Collections.Generic; 12 | public class ConfigConstants 13 | { 14 | public const NumberStyles NUMBER_STYPLES = 15 | NumberStyles.AllowCurrencySymbol | 16 | NumberStyles.AllowExponent | 17 | NumberStyles.AllowDecimalPoint | 18 | NumberStyles.AllowParentheses | 19 | NumberStyles.AllowTrailingSign | 20 | NumberStyles.AllowLeadingSign | 21 | NumberStyles.AllowTrailingWhite | 22 | NumberStyles.AllowLeadingWhite; 23 | 24 | public static Type OBJECT_ARRAY_TYPE = typeof(object[]); 25 | public static Type OBJECT_DICTIONARY_TYPE = typeof(Dictionary); 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /Editor/ConfigConstants.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0a05f439b2178d24a92ad0c04a3a5a48 3 | timeCreated: 1500712431 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Editor/ConfigMenu.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Rick 3 | * Create: 7/10/2017 9:35:30 PM 4 | * Email: rickjiangshu@gmail.com 5 | * Follow: https://github.com/RickJiangShu 6 | */ 7 | namespace ConfigManagerEditor 8 | { 9 | using UnityEngine; 10 | using UnityEditor; 11 | using System.IO; 12 | 13 | /// 14 | /// ConfigMenu 15 | /// 16 | public class ConfigMenu : ScriptableObject 17 | { 18 | [MenuItem("Assets/Config Manager/Set to Source Path")] 19 | static void Set2SourcePath() 20 | { 21 | ConfigWindow.Get().cache.sourceFolder = GetSelectedPathOrFallback(); 22 | ConfigWindow.Get().SaveCache(); 23 | } 24 | 25 | [MenuItem("Assets/Config Manager/Set to Config Output")] 26 | static void Set2ConfigOutput() 27 | { 28 | ConfigWindow.Get().cache.configOutputFolder = GetSelectedPathOrFallback(); 29 | ConfigWindow.Get().SaveCache(); 30 | } 31 | 32 | [MenuItem("Assets/Config Manager/Set to Asset Output")] 33 | static void Set2AssetOutput() 34 | { 35 | ConfigWindow.Get().cache.assetOutputFolder = GetSelectedPathOrFallback(); 36 | ConfigWindow.Get().SaveCache(); 37 | } 38 | 39 | 40 | /// 41 | /// Fork:https://gist.github.com/allanolivei/9260107 42 | /// 43 | /// 44 | public static string GetSelectedPathOrFallback() 45 | { 46 | string path = "Assets"; 47 | 48 | foreach (UnityEngine.Object obj in Selection.GetFiltered(typeof(UnityEngine.Object), SelectionMode.Assets)) 49 | { 50 | path = AssetDatabase.GetAssetPath(obj); 51 | if (!string.IsNullOrEmpty(path) && File.Exists(path)) 52 | { 53 | path = Path.GetDirectoryName(path); 54 | break; 55 | } 56 | } 57 | return path; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Editor/ConfigMenu.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 750b92f20a9b7a74c80bb37c413afdbb 3 | timeCreated: 1499693738 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Editor/ConfigTools.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Rick 3 | * Create: 2017/7/10 11:57:52 4 | * Email: rickjiangshu@gmail.com 5 | * Follow: https://github.com/RickJiangShu 6 | */ 7 | namespace ConfigManagerEditor 8 | { 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Globalization; 12 | using System.IO; 13 | using System.Text; 14 | using System.Text.RegularExpressions; 15 | 16 | /// 17 | /// 配置工具集 18 | /// 19 | public class ConfigTools 20 | { 21 | public static bool IsArrayType(string sourceType) 22 | { 23 | int idx = sourceType.LastIndexOf('['); 24 | return idx != -1; 25 | } 26 | 27 | /// 28 | /// 判断是否是数组类型 29 | /// 30 | /// 31 | public static bool IsArrayType(string sourceType,out string sourceBaseType) 32 | { 33 | int idx = sourceType.LastIndexOf('['); 34 | if (idx != -1) 35 | sourceBaseType = SourceBaseType2CSharpBaseType(sourceType.Substring(0, idx)); 36 | else 37 | sourceBaseType = sourceType; 38 | return idx != -1; 39 | } 40 | 41 | #region 将SourceType转换为CSharp 42 | /// 43 | /// 将类型字符串转换为C#基础类型 44 | /// 45 | /// 46 | /// 47 | public static string SourceType2CSharpType(int column ,string[,] matrix) 48 | { 49 | string sourceType = matrix[1, column]; 50 | 51 | if (string.IsNullOrEmpty(sourceType) || sourceType == "*") 52 | { 53 | return Smart2CSharpType(column, matrix); 54 | } 55 | else if (sourceType == "*[]") 56 | { 57 | return Smart2CSharpArrayType(column, matrix); 58 | } 59 | else 60 | { 61 | string sourceBaseType; 62 | if (IsArrayType(sourceType, out sourceBaseType)) 63 | { 64 | return sourceBaseType + "[]"; 65 | } 66 | return SourceBaseType2CSharpBaseType(sourceType); 67 | } 68 | } 69 | 70 | /// 71 | /// 将SourceBaseType转换为C#基础类型 72 | /// 73 | /// 74 | /// 75 | private static string SourceBaseType2CSharpBaseType(string sourceBaseType) 76 | { 77 | string baseType; 78 | switch (sourceBaseType) 79 | { 80 | case "bool": 81 | baseType = "bool"; 82 | break; 83 | case "byte": 84 | case "uint8": 85 | baseType = "byte"; 86 | break; 87 | case "ushort": 88 | case "uint16": 89 | baseType = "ushort"; 90 | break; 91 | case "uint": 92 | case "uint32": 93 | baseType = "uint"; 94 | break; 95 | case "ulong": 96 | case "uint64": 97 | baseType = "ulong"; 98 | break; 99 | case "sbyte": 100 | case "int8": 101 | baseType = "sbyte"; 102 | break; 103 | case "short": 104 | case "int16": 105 | baseType = "short"; 106 | break; 107 | case "int": 108 | case "int32": 109 | baseType = "int"; 110 | break; 111 | case "long": 112 | case "int64": 113 | baseType = "long"; 114 | break; 115 | case "float": 116 | baseType = "float"; 117 | break; 118 | case "double": 119 | baseType = "double"; 120 | break; 121 | case "string": 122 | baseType = "string"; 123 | break; 124 | default: 125 | baseType = sourceBaseType; 126 | break; 127 | } 128 | return baseType; 129 | } 130 | #endregion 131 | 132 | #region 智能判断CSharp类型 133 | private static string Smart2CSharpType(int column, string[,] matrix) 134 | { 135 | int row = matrix.GetLength(0); 136 | string valid = FindValidValue(column, matrix); 137 | 138 | if (IsNumber(valid)) 139 | { 140 | string[] numbersOfColumn = new string[row - 3];//这行所有的Number 141 | for(int y = 3,i = 0;y numbersOfColumn = new List();//不仅要遍历行,还要遍历数组中的值 165 | for (int y = 3, i = 0; y < row; y++, i++) 166 | { 167 | string rowData = matrix[y, column]; 168 | if (string.IsNullOrEmpty(rowData)) 169 | continue; 170 | 171 | numbersOfColumn.AddRange(rowData.Split(',')); 172 | } 173 | 174 | Type minimalType = FindMinimalNumberType(numbersOfColumn); 175 | return Type2SourceBaseType(minimalType) + "[]"; 176 | } 177 | else if (IsBool(validArray[0])) 178 | { 179 | return Type2SourceBaseType(typeof(bool)) + "[]"; 180 | } 181 | else 182 | return "string[]"; 183 | } 184 | 185 | private static string FindValidValue(int column, string[,] matrix) 186 | { 187 | string valid = ""; 188 | int row = matrix.GetLength(0);//行总数 189 | for (int y = 3; y < row; y++) 190 | { 191 | valid = matrix[y, column]; 192 | if (!string.IsNullOrEmpty(valid)) 193 | break; 194 | } 195 | return valid; 196 | } 197 | #endregion 198 | 199 | #region 将SourceType <-> SystemType 200 | private static Type SourceBaseType2Type(string sourceBaseType) 201 | { 202 | string csharpBaseType = SourceBaseType2CSharpBaseType(sourceBaseType); 203 | switch (csharpBaseType) 204 | { 205 | case "bool": 206 | return typeof(bool); 207 | case "byte": 208 | return typeof(byte); 209 | case "ushort": 210 | return typeof(ushort); 211 | case "uint": 212 | return typeof(uint); 213 | case "sbyte": 214 | return typeof(sbyte); 215 | case "short": 216 | return typeof(short); 217 | case "int": 218 | return typeof(int); 219 | case "long": 220 | return typeof(long); 221 | case "ulong": 222 | return typeof(ulong); 223 | case "float": 224 | return typeof(float); 225 | case "double": 226 | return typeof(double); 227 | case "string": 228 | return typeof(string); 229 | default: 230 | return null; 231 | } 232 | } 233 | private static string Type2SourceBaseType(Type type) 234 | { 235 | if (type == typeof(bool)) 236 | return "bool"; 237 | else if (type == typeof(byte)) 238 | return "byte"; 239 | else if (type == typeof(ushort)) 240 | return "ushort"; 241 | else if (type == typeof(uint)) 242 | return "uint"; 243 | else if (type == typeof(sbyte)) 244 | return "sbyte"; 245 | else if (type == typeof(short)) 246 | return "short"; 247 | else if (type == typeof(int)) 248 | return "int"; 249 | else if (type == typeof(long)) 250 | return "long"; 251 | else if (type == typeof(ulong)) 252 | return "ulong"; 253 | else if (type == typeof(float)) 254 | return "float"; 255 | else if (type == typeof(double)) 256 | return "double"; 257 | else if (type == typeof(string)) 258 | return "string"; 259 | 260 | return ""; 261 | } 262 | #endregion 263 | 264 | #region 将SourceValue转换为Object 265 | /// 266 | /// 解析源值 267 | /// 268 | /// 269 | /// 270 | /// 271 | public static object SourceValue2Object(string sourceType, string sourceValue) 272 | { 273 | string sourceBaseType; 274 | if (IsArrayType(sourceType,out sourceBaseType)) 275 | { 276 | return SourceValue2ArrayObject(sourceType, sourceValue, sourceBaseType); 277 | } 278 | else 279 | { 280 | //返回独值 281 | return SourceValue2BaseObject(sourceType, sourceValue); 282 | } 283 | } 284 | 285 | private static object SourceValue2BaseObject(string sourceBaseType,string sourceValue) 286 | { 287 | string csharpType = SourceBaseType2CSharpBaseType(sourceBaseType); 288 | switch (csharpType) 289 | { 290 | case "bool": 291 | return sourceValue != "0" && sourceValue != "false" && sourceValue != "False" && sourceValue != "FALSE"; 292 | case "byte": 293 | return byte.Parse(sourceValue); 294 | case "ushort": 295 | return ushort.Parse(sourceValue); 296 | case "uint": 297 | return uint.Parse(sourceValue); 298 | case "sbyte": 299 | return sbyte.Parse(sourceValue); 300 | case "short": 301 | return short.Parse(sourceValue); 302 | case "int": 303 | return int.Parse(sourceValue); 304 | case "long": 305 | return long.Parse(sourceValue); 306 | case "ulong": 307 | return ulong.Parse(sourceValue); 308 | case "float": 309 | return float.Parse(sourceValue); 310 | case "double": 311 | return double.Parse(sourceValue); 312 | case "string": 313 | return sourceValue; 314 | default: 315 | return sourceValue; 316 | } 317 | } 318 | 319 | /// 320 | /// 将配置的数组转换为C#数组 321 | /// 322 | /// 323 | /// 324 | /// 325 | /// 326 | public static Array SourceValue2ArrayObject(string sourceType,string sourceValue,string sourceBaseType) 327 | { 328 | //解析数组 329 | if (string.IsNullOrEmpty(sourceValue) || sourceValue == "0") return null; 330 | 331 | string[] values = sourceValue.Split(','); 332 | Type type = SourceBaseType2Type(sourceBaseType); 333 | Array array = Array.CreateInstance(type,values.Length); 334 | for(int i = 0,l = array.Length;i 344 | /// 写入文件 345 | /// 346 | /// 347 | /// 348 | public static void WriteFile(string path,string content) 349 | { 350 | //文件写入 351 | FileStream fileStream; 352 | if (!File.Exists(path)) 353 | { 354 | fileStream = File.Create(path); 355 | } 356 | else 357 | { 358 | fileStream = File.Open(path, FileMode.Truncate); 359 | } 360 | 361 | StreamWriter writer = new StreamWriter(fileStream, System.Text.Encoding.UTF8); 362 | writer.Write(content); 363 | writer.Close(); 364 | } 365 | 366 | 367 | /// 368 | /// 读取文件内容 369 | /// Fork: 370 | /// https://stackoverflow.com/questions/1389155/easiest-way-to-read-text-file-which-is-locked-by-another-application 371 | /// https://stackoverflow.com/questions/221925/creating-a-byte-array-from-a-stream 372 | /// 373 | /// 374 | /// 375 | public static string ReadFile(FileStream fileStream,out byte[] bytes) 376 | { 377 | BinaryReader br = new BinaryReader(fileStream); 378 | bytes = br.ReadBytes((int)fileStream.Length); 379 | StreamReader renderer = new StreamReader(fileStream); 380 | string content = renderer.ReadToEnd(); 381 | fileStream.Close(); 382 | return content; 383 | } 384 | 385 | private static Encoding gb2312 = Encoding.GetEncoding("gb2312"); 386 | // Function to detect the encoding for UTF-7, UTF-8/16/32 (bom, no bom, little 387 | // & big endian), and local default codepage, and potentially other codepages. 388 | // 'taster' = number of bytes to check of the file (to save processing). Higher 389 | // value is slower, but more reliable (especially UTF-8 with special characters 390 | // later on may appear to be ASCII initially). If taster = 0, then taster 391 | // becomes the length of the file (for maximum reliability). 'text' is simply 392 | // the string with the discovered encoding applied to the file. 393 | public static Encoding DetectTextEncoding(byte[] b, out String text, int taster = 1000) 394 | { 395 | //////////////// First check the low hanging fruit by checking if a 396 | //////////////// BOM/signature exists (sourced from http://www.unicode.org/faq/utf_bom.html#bom4) 397 | if (b.Length >= 4 && b[0] == 0x00 && b[1] == 0x00 && b[2] == 0xFE && b[3] == 0xFF) { text = Encoding.GetEncoding("utf-32BE").GetString(b, 4, b.Length - 4); return Encoding.GetEncoding("utf-32BE"); } // UTF-32, big-endian 398 | else if (b.Length >= 4 && b[0] == 0xFF && b[1] == 0xFE && b[2] == 0x00 && b[3] == 0x00) { text = Encoding.UTF32.GetString(b, 4, b.Length - 4); return Encoding.UTF32; } // UTF-32, little-endian 399 | else if (b.Length >= 2 && b[0] == 0xFE && b[1] == 0xFF) { text = Encoding.BigEndianUnicode.GetString(b, 2, b.Length - 2); return Encoding.BigEndianUnicode; } // UTF-16, big-endian 400 | else if (b.Length >= 2 && b[0] == 0xFF && b[1] == 0xFE) { text = Encoding.Unicode.GetString(b, 2, b.Length - 2); return Encoding.Unicode; } // UTF-16, little-endian 401 | else if (b.Length >= 3 && b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF) { text = Encoding.UTF8.GetString(b, 3, b.Length - 3); return Encoding.UTF8; } // UTF-8 402 | else if (b.Length >= 3 && b[0] == 0x2b && b[1] == 0x2f && b[2] == 0x76) { text = Encoding.UTF7.GetString(b, 3, b.Length - 3); return Encoding.UTF7; } // UTF-7 403 | 404 | 405 | //////////// If the code reaches here, no BOM/signature was found, so now 406 | //////////// we need to 'taste' the file to see if can manually discover 407 | //////////// the encoding. A high taster value is desired for UTF-8 408 | if (taster == 0 || taster > b.Length) taster = b.Length; // Taster size can't be bigger than the filesize obviously. 409 | 410 | 411 | // Some text files are encoded in UTF8, but have no BOM/signature. Hence 412 | // the below manually checks for a UTF8 pattern. This code is based off 413 | // the top answer at: https://stackoverflow.com/questions/6555015/check-for-invalid-utf8 414 | // For our purposes, an unnecessarily strict (and terser/slower) 415 | // implementation is shown at: https://stackoverflow.com/questions/1031645/how-to-detect-utf-8-in-plain-c 416 | // For the below, false positives should be exceedingly rare (and would 417 | // be either slightly malformed UTF-8 (which would suit our purposes 418 | // anyway) or 8-bit extended ASCII/UTF-16/32 at a vanishingly long shot). 419 | int i = 0; 420 | bool utf8 = false; 421 | while (i < taster - 4) 422 | { 423 | if (b[i] <= 0x7F) { i += 1; continue; } // If all characters are below 0x80, then it is valid UTF8, but UTF8 is not 'required' (and therefore the text is more desirable to be treated as the default codepage of the computer). Hence, there's no "utf8 = true;" code unlike the next three checks. 424 | if (b[i] >= 0xC2 && b[i] <= 0xDF && b[i + 1] >= 0x80 && b[i + 1] < 0xC0) { i += 2; utf8 = true; continue; } 425 | if (b[i] >= 0xE0 && b[i] <= 0xF0 && b[i + 1] >= 0x80 && b[i + 1] < 0xC0 && b[i + 2] >= 0x80 && b[i + 2] < 0xC0) { i += 3; utf8 = true; continue; } 426 | if (b[i] >= 0xF0 && b[i] <= 0xF4 && b[i + 1] >= 0x80 && b[i + 1] < 0xC0 && b[i + 2] >= 0x80 && b[i + 2] < 0xC0 && b[i + 3] >= 0x80 && b[i + 3] < 0xC0) { i += 4; utf8 = true; continue; } 427 | utf8 = false; break; 428 | } 429 | if (utf8 == true) 430 | { 431 | text = Encoding.UTF8.GetString(b); 432 | return Encoding.UTF8; 433 | } 434 | 435 | 436 | // The next check is a heuristic attempt to detect UTF-16 without a BOM. 437 | // We simply look for zeroes in odd or even byte places, and if a certain 438 | // threshold is reached, the code is 'probably' UF-16. 439 | double threshold = 0.1; // proportion of chars step 2 which must be zeroed to be diagnosed as utf-16. 0.1 = 10% 440 | int count = 0; 441 | for (int n = 0; n < taster; n += 2) if (b[n] == 0) count++; 442 | if (((double)count) / taster > threshold) { text = Encoding.BigEndianUnicode.GetString(b); return Encoding.BigEndianUnicode; } 443 | count = 0; 444 | for (int n = 1; n < taster; n += 2) if (b[n] == 0) count++; 445 | if (((double)count) / taster > threshold) { text = Encoding.Unicode.GetString(b); return Encoding.Unicode; } // (little-endian) 446 | 447 | 448 | // Finally, a long shot - let's see if we can find "charset=xyz" or 449 | // "encoding=xyz" to identify the encoding: 450 | for (int n = 0; n < taster - 9; n++) 451 | { 452 | if ( 453 | ((b[n + 0] == 'c' || b[n + 0] == 'C') && (b[n + 1] == 'h' || b[n + 1] == 'H') && (b[n + 2] == 'a' || b[n + 2] == 'A') && (b[n + 3] == 'r' || b[n + 3] == 'R') && (b[n + 4] == 's' || b[n + 4] == 'S') && (b[n + 5] == 'e' || b[n + 5] == 'E') && (b[n + 6] == 't' || b[n + 6] == 'T') && (b[n + 7] == '=')) || 454 | ((b[n + 0] == 'e' || b[n + 0] == 'E') && (b[n + 1] == 'n' || b[n + 1] == 'N') && (b[n + 2] == 'c' || b[n + 2] == 'C') && (b[n + 3] == 'o' || b[n + 3] == 'O') && (b[n + 4] == 'd' || b[n + 4] == 'D') && (b[n + 5] == 'i' || b[n + 5] == 'I') && (b[n + 6] == 'n' || b[n + 6] == 'N') && (b[n + 7] == 'g' || b[n + 7] == 'G') && (b[n + 8] == '=')) 455 | ) 456 | { 457 | if (b[n + 0] == 'c' || b[n + 0] == 'C') n += 8; else n += 9; 458 | if (b[n] == '"' || b[n] == '\'') n++; 459 | int oldn = n; 460 | while (n < taster && (b[n] == '_' || b[n] == '-' || (b[n] >= '0' && b[n] <= '9') || (b[n] >= 'a' && b[n] <= 'z') || (b[n] >= 'A' && b[n] <= 'Z'))) 461 | { n++; } 462 | byte[] nb = new byte[n - oldn]; 463 | Array.Copy(b, oldn, nb, 0, n - oldn); 464 | try 465 | { 466 | string internalEnc = Encoding.ASCII.GetString(nb); 467 | text = Encoding.GetEncoding(internalEnc).GetString(b); 468 | return Encoding.GetEncoding(internalEnc); 469 | } 470 | catch { break; } // If C# doesn't recognize the name of the encoding, break. 471 | } 472 | } 473 | 474 | // If all else fails, the encoding is probably (though certainly not 475 | // definitely) the user's local codepage! One might present to the user a 476 | // list of alternative encodings as shown here: https://stackoverflow.com/questions/8509339/what-is-the-most-common-encoding-of-each-language 477 | // A full list can be found using Encoding.GetEncodings(); 478 | text = gb2312.GetString(b); 479 | return gb2312; 480 | } 481 | 482 | #region 转换类型方法 483 | public static object ConvertBaseObject(object obj) 484 | { 485 | return ConvertBaseObject(obj.ToString()); 486 | } 487 | 488 | /// 489 | /// 将字符串转换成 Number,Bool Or string 490 | /// 491 | /// 492 | /// 493 | public static object ConvertBaseObject(string content) 494 | { 495 | if (IsNumber(content)) 496 | return ConvertNumber(content); 497 | 498 | if (IsBool(content)) 499 | return ConvertBool(content); 500 | 501 | return content; 502 | } 503 | 504 | public static object ConvertNumber(string content) 505 | { 506 | //byte 507 | byte bNum; 508 | if (byte.TryParse(content, out bNum)) 509 | return bNum; 510 | 511 | //sbyte 512 | sbyte sbNum; 513 | if (sbyte.TryParse(content, out sbNum)) 514 | return sbNum; 515 | 516 | //ushort 517 | ushort usNum; 518 | if (ushort.TryParse(content, out usNum)) 519 | return usNum; 520 | 521 | //short 522 | short sNum; 523 | if (short.TryParse(content, out sNum)) 524 | return sNum; 525 | 526 | //uint 527 | uint uiNum; 528 | if (uint.TryParse(content, out uiNum)) 529 | return uiNum; 530 | 531 | //int 532 | int iNum; 533 | if (int.TryParse(content, out iNum)) 534 | return iNum; 535 | 536 | //ulong 537 | ulong ulNum; 538 | if (ulong.TryParse(content, out ulNum)) 539 | return ulNum; 540 | 541 | //long 542 | long lNum; 543 | if (long.TryParse(content, out lNum)) 544 | return lNum; 545 | 546 | //float 547 | float fNum; 548 | if (float.TryParse(content, ConfigConstants.NUMBER_STYPLES, CultureInfo.CurrentCulture, out fNum)) 549 | return fNum; 550 | 551 | //double 552 | double dNum; 553 | if (double.TryParse(content, ConfigConstants.NUMBER_STYPLES, CultureInfo.CurrentCulture, out dNum)) 554 | return dNum; 555 | 556 | return content; 557 | } 558 | /// 559 | /// 转换Bool 560 | /// 561 | /// 562 | public static bool ConvertBool(string content) 563 | { 564 | return content == "true" || content == "True" || content == "TRUE"; 565 | } 566 | #endregion 567 | 568 | #region 判断类型方法 569 | /// 570 | /// 判断是否是数字 571 | /// 572 | /// 573 | /// 574 | public static bool IsNumber(string content) 575 | { 576 | double retNum; 577 | bool isNum = Double.TryParse(content, ConfigConstants.NUMBER_STYPLES, CultureInfo.CurrentCulture, out retNum); 578 | return isNum; 579 | } 580 | 581 | public static bool IsNumber(Type t) 582 | { 583 | switch (Type.GetTypeCode(t)) 584 | { 585 | case TypeCode.Byte: 586 | case TypeCode.SByte: 587 | case TypeCode.UInt16: 588 | case TypeCode.UInt32: 589 | case TypeCode.UInt64: 590 | case TypeCode.Int16: 591 | case TypeCode.Int32: 592 | case TypeCode.Int64: 593 | case TypeCode.Decimal: 594 | case TypeCode.Double: 595 | case TypeCode.Single: 596 | return true; 597 | default: 598 | return false; 599 | } 600 | } 601 | 602 | /// 603 | /// 判断字符串是否是Bool 604 | /// 605 | /// 606 | /// 607 | public static bool IsBool(string content) 608 | { 609 | return content == "true" || content == "false" || content == "True" || content == "False" || content == "TRUE" || content == "FALSE"; 610 | } 611 | 612 | /// 613 | /// ObjectDictionary是否相同 614 | /// 615 | /// 616 | public static bool IsSameObjectDictionary(Dictionary a, Dictionary b) 617 | { 618 | int al = a.Count; 619 | int bl = b.Count; 620 | if (al != bl) 621 | return false; 622 | 623 | foreach (string akey in a.Keys) 624 | { 625 | if (!b.ContainsKey(akey)) 626 | return false; 627 | 628 | if (!IsSameObject(a[akey], b[akey])) 629 | return false; 630 | } 631 | return true; 632 | } 633 | 634 | /// 635 | /// 判断ObjectArray是否相等 636 | /// 637 | /// 638 | /// 639 | /// 640 | public static bool IsSameObjectArray(object[] a, object[] b) 641 | { 642 | return a[0].GetType() == b[0].GetType(); 643 | /* 644 | int al = a.Length; 645 | int bl = b.Length; 646 | if (al != bl) 647 | return false; 648 | 649 | for (int i = 0; i < al; i++) 650 | { 651 | if (!IsSameObject(a[i], b[i])) 652 | return false; 653 | } 654 | 655 | return true; 656 | */ 657 | } 658 | 659 | public static bool IsSameObject(object a, object b) 660 | { 661 | Type atype = a.GetType(); 662 | Type btype = b.GetType(); 663 | if (atype != btype) 664 | return false; 665 | 666 | if (atype == ConfigConstants.OBJECT_DICTIONARY_TYPE) 667 | { 668 | return IsSameObjectDictionary((Dictionary)a, (Dictionary)b); 669 | } 670 | else if (atype == ConfigConstants.OBJECT_ARRAY_TYPE) 671 | { 672 | return IsSameObjectArray((object[])a, (object[])b); 673 | } 674 | return true; 675 | } 676 | #endregion 677 | 678 | /// 679 | /// 在数组中找到最小的数字类型 680 | /// 681 | /// 682 | public static Type FindMinimalNumberType(IEnumerable array) 683 | { 684 | if (FindParseSuccess(array, byte.TryParse)) 685 | return typeof(byte); 686 | 687 | if (FindParseSuccess(array, sbyte.TryParse)) 688 | return typeof(sbyte); 689 | 690 | if (FindParseSuccess(array, ushort.TryParse)) 691 | return typeof(ushort); 692 | 693 | if (FindParseSuccess(array, short.TryParse)) 694 | return typeof(short); 695 | 696 | if (FindParseSuccess(array, uint.TryParse)) 697 | return typeof(uint); 698 | 699 | if (FindParseSuccess(array, int.TryParse)) 700 | return typeof(int); 701 | 702 | if (FindParseSuccess(array, float.TryParse)) 703 | return typeof(float); 704 | 705 | return typeof(double); 706 | } 707 | 708 | public delegate bool TryParseHandler(string value, out T result); 709 | public static bool FindParseSuccess(IEnumerable array, TryParseHandler tryParse) 710 | { 711 | foreach (object item in array) 712 | { 713 | T v; 714 | if (!tryParse(item.ToString(), out v)) 715 | { 716 | return false; 717 | } 718 | } 719 | return true; 720 | } 721 | } 722 | } 723 | -------------------------------------------------------------------------------- /Editor/ConfigTools.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8bbcfe335df8b2f4e8fbe6700f5cc1af 3 | timeCreated: 1499670932 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Editor/ConfigWindow.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Rick 3 | * Create: 2017/7/6 18:01:59 4 | * Email: rickjiangshu@gmail.com 5 | * Follow: https://github.com/RickJiangShu 6 | */ 7 | namespace ConfigManagerEditor 8 | { 9 | using UnityEngine; 10 | using UnityEditor; 11 | using System; 12 | using System.IO; 13 | using System.Collections.Generic; 14 | using System.Text; 15 | using System.Text.RegularExpressions; 16 | using OfficeOpenXml; 17 | 18 | /// 19 | /// ConfigManager窗口 20 | /// 21 | public class ConfigWindow : EditorWindow 22 | { 23 | private const string CACHE_DISK_NAME = "ConfigManagerCache.json"; 24 | private const string ASSET_NAME = "SerializableSet.asset"; 25 | 26 | private static bool justRecompiled; 27 | 28 | public static event Action serializeCompleted;//序列化完成 29 | 30 | static ConfigWindow() 31 | { 32 | justRecompiled = true; 33 | } 34 | 35 | [MenuItem("Window/Config Manager")] 36 | public static ConfigWindow Get() 37 | { 38 | return EditorWindow.GetWindow("Config"); 39 | } 40 | 41 | /// 42 | /// 缓存数据 43 | /// 44 | public Cache cache; 45 | 46 | /// 47 | /// 缓存磁盘路径 48 | /// 49 | private string cacheDiskPath; 50 | 51 | void Awake() 52 | { 53 | LoadCache(); 54 | } 55 | 56 | 57 | public void OnGUI() 58 | { 59 | //Base Settings 60 | GUILayout.Label("Base Settings", EditorStyles.boldLabel); 61 | 62 | cache.sourceFolder = EditorGUILayout.TextField("Source Folder", cache.sourceFolder); 63 | cache.configOutputFolder = EditorGUILayout.TextField("Config Output", cache.configOutputFolder); 64 | cache.assetOutputFolder = EditorGUILayout.TextField("Asset Output", cache.assetOutputFolder); 65 | 66 | //Source Type 67 | EditorGUILayout.Space(); 68 | GUILayout.Label("Original Type", EditorStyles.boldLabel); 69 | 70 | cache.txtEnabled = EditorGUILayout.Toggle("*.txt", cache.txtEnabled); 71 | cache.csvEnabled = EditorGUILayout.Toggle("*.csv", cache.csvEnabled); 72 | // cache.jsonEnabled = EditorGUILayout.Toggle("*.json", cache.jsonEnabled); 73 | // cache.xmlEnabled = EditorGUILayout.Toggle("*.xml", cache.xmlEnabled); 74 | cache.xlsxEnabled = EditorGUILayout.Toggle("*.xlsx", cache.xlsxEnabled); 75 | 76 | //Operation 77 | EditorGUILayout.Space(); 78 | GUILayout.Label("Operation", EditorStyles.boldLabel); 79 | 80 | 81 | if (GUILayout.Button("Serialize")) 82 | { 83 | Serialize(); 84 | } 85 | 86 | if (GUILayout.Button("Clear Output")) 87 | { 88 | if (EditorUtility.DisplayDialog("Clear Output", 89 | "Are you sure you want to clear " + cache.configOutputFolder + " and " + cache.assetOutputFolder + "/" + ASSET_NAME, 90 | "Yes", "No")) 91 | { 92 | ClearOutput(); 93 | } 94 | } 95 | 96 | if (GUILayout.Button("Output")) 97 | { 98 | Output(); 99 | } 100 | 101 | 102 | //缓存设置 103 | if (GUI.changed) 104 | { 105 | SaveCache(); 106 | } 107 | } 108 | 109 | /// 110 | /// 清空输出目录 111 | /// 112 | private void ClearOutput() 113 | { 114 | //Clear Config 115 | if (Directory.Exists(cache.configOutputFolder)) 116 | { 117 | Directory.Delete(cache.configOutputFolder, true); 118 | File.Delete(cache.configOutputFolder + ".meta"); 119 | 120 | } 121 | 122 | ////Clear Asset 123 | string assetPath = cache.assetOutputFolder + "/" + ASSET_NAME; 124 | if(File.Exists(assetPath)) 125 | { 126 | File.Delete(assetPath); 127 | File.Delete(assetPath + ".meta"); 128 | } 129 | 130 | AssetDatabase.Refresh(); 131 | } 132 | 133 | /// 134 | /// 输出文件 135 | /// 136 | private void Output() 137 | { 138 | //检出输出目录 139 | if (!Directory.Exists(cache.configOutputFolder)) 140 | { 141 | Directory.CreateDirectory(cache.configOutputFolder); 142 | } 143 | if (!Directory.Exists(cache.serializerOutputFolder)) 144 | { 145 | Directory.CreateDirectory(cache.serializerOutputFolder); 146 | } 147 | 148 | //获取源 149 | List sheets;//表格 150 | List structs;//结构 151 | 152 | GetSources(out sheets, out structs); 153 | 154 | if (sheets.Count == 0 && structs.Count == 0) 155 | { 156 | Debug.LogError(cache.sourceFolder + "没有找到任何文件!"); 157 | return; 158 | } 159 | 160 | SheetGenerator.Generate(sheets, cache.configOutputFolder);//生产Configs 161 | StructGenerator.Generate(structs, cache.configOutputFolder);//生成Jsons 162 | 163 | //生产SerializableSet 164 | SerializableSetGenerator.Generate(sheets, structs, cache.serializerOutputFolder); 165 | 166 | //生产Deserializer 167 | DeserializerGenerator.Generate(sheets, structs, cache.serializerOutputFolder); 168 | 169 | //刷新 170 | AssetDatabase.Refresh(); 171 | 172 | //等待序列化 173 | if (EditorApplication.isCompiling) 174 | { 175 | waitingForSerialize = true; 176 | Debug.Log("输出完成,正在等待Unity编译后序列化数据..."); 177 | } 178 | else 179 | { 180 | Serialize(); 181 | } 182 | } 183 | 184 | 185 | /// 186 | /// 是否正在等待序列化(新生成脚本需要编译) 187 | /// 188 | private bool waitingForSerialize = false; 189 | 190 | void Update() 191 | { 192 | if (justRecompiled && waitingForSerialize) 193 | { 194 | waitingForSerialize = false; 195 | Serialize(); 196 | } 197 | justRecompiled = false; 198 | } 199 | 200 | 201 | /// 202 | /// 获取磁盘中的 203 | /// 204 | /// 205 | private bool TryGetDiskCache(out string content,out string path) 206 | { 207 | DirectoryInfo assetFolder = new DirectoryInfo(Application.dataPath); 208 | FileInfo[] files = assetFolder.GetFiles(CACHE_DISK_NAME, SearchOption.AllDirectories); 209 | if (files.Length > 0) 210 | { 211 | StreamReader stream = files[0].OpenText(); 212 | content = stream.ReadToEnd(); 213 | path = files[0].FullName; 214 | stream.Close(); 215 | return true; 216 | } 217 | content = ""; 218 | path = ""; 219 | return false; 220 | } 221 | 222 | /// 223 | /// 加载缓存或初始化 224 | /// 225 | private void LoadCache() 226 | { 227 | string content; 228 | string path; 229 | if (TryGetDiskCache(out content,out path)) 230 | { 231 | cache = JsonUtility.FromJson(content); 232 | cacheDiskPath = path; 233 | } 234 | else 235 | { 236 | cache = new Cache(); 237 | cacheDiskPath = "Assets/" + CACHE_DISK_NAME; 238 | } 239 | } 240 | 241 | /// 242 | /// 保存缓存 243 | /// 244 | public void SaveCache() 245 | { 246 | string json = JsonUtility.ToJson(cache, true); 247 | ConfigTools.WriteFile(cacheDiskPath, json); 248 | } 249 | 250 | 251 | /// 252 | /// 获取所有源 253 | /// 254 | /// 255 | private void GetSources(out List sheets,out List structs) 256 | { 257 | //获取所有配置文件 258 | DirectoryInfo directory = new DirectoryInfo(cache.sourceFolder); 259 | FileInfo[] files = directory.GetFiles("*.*", SearchOption.AllDirectories); 260 | 261 | //源 262 | sheets = new List(); 263 | structs = new List(); 264 | 265 | for (int i = 0, l = files.Length; i < l; i++) 266 | { 267 | FileInfo file = files[i]; 268 | if (IsTemporaryFile(file.Name))//临时文件 269 | continue; 270 | 271 | OriginalType type; 272 | if(!TypeEnabled(file.Extension,out type)) 273 | continue; 274 | 275 | //可以同时读流 276 | FileStream fileStream = file.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite); 277 | 278 | ExcelWorksheet excelData = null; 279 | string content = ""; 280 | 281 | //读取Excel 282 | if (type == OriginalType.Xlsx) 283 | { 284 | ExcelPackage package = new ExcelPackage(fileStream); 285 | excelData = package.Workbook.Worksheets[1]; 286 | 287 | fileStream.Close(); 288 | } 289 | //其他 290 | else 291 | { 292 | //读Byte 293 | byte[] bytes; 294 | BinaryReader br = new BinaryReader(fileStream); 295 | bytes = br.ReadBytes((int)fileStream.Length); 296 | StreamReader renderer = new StreamReader(fileStream); 297 | content = renderer.ReadToEnd(); 298 | 299 | ConfigTools.DetectTextEncoding(bytes, out content);//转换不同的编码格式 300 | 301 | if (string.IsNullOrEmpty(content)) 302 | { 303 | Debug.LogWarning(file.Name + "内容为空!"); 304 | continue; 305 | } 306 | } 307 | 308 | switch(type) 309 | { 310 | case OriginalType.Txt: 311 | case OriginalType.Csv: 312 | try 313 | { 314 | SheetSource source = SheetParser.Parse(content, file.Name); 315 | sheets.Add(source); 316 | } 317 | catch(Exception e) 318 | { 319 | UnityEngine.Debug.LogError(file.Name + "解析失败!请检查格式是否正确,如果格式正确请联系作者:https://github.com/RickJiangShu/ConfigManager/issues" + "\n" + e); 320 | } 321 | break; 322 | case OriginalType.Json: 323 | try 324 | { 325 | StructSource st = JsonParser.Parse(content, file.Name); 326 | structs.Add(st); 327 | } 328 | catch (Exception e) 329 | { 330 | UnityEngine.Debug.LogError(file.Name + "解析失败!请检查格式是否正确,如果格式正确请联系作者:https://github.com/RickJiangShu/ConfigManager/issues" + "\n" + e); 331 | } 332 | break; 333 | case OriginalType.Xml: 334 | try 335 | { 336 | StructSource st = XmlParser.Parse(content, file.Name); 337 | structs.Add(st); 338 | } 339 | catch (Exception e) 340 | { 341 | UnityEngine.Debug.LogError(file.Name + "解析失败!请检查格式是否正确,如果格式正确请联系作者:https://github.com/RickJiangShu/ConfigManager/issues" + "\n" + e); 342 | } 343 | break; 344 | case OriginalType.Xlsx: 345 | try 346 | { 347 | SheetSource st = SheetParser.Parse(excelData, file.Name); 348 | sheets.Add(st); 349 | } 350 | catch (Exception e) 351 | { 352 | UnityEngine.Debug.LogError(file.Name + "解析失败!请检查格式是否正确,如果格式正确请联系作者:https://github.com/RickJiangShu/ConfigManager/issues" + "\n" + e); 353 | } 354 | break; 355 | } 356 | } 357 | } 358 | 359 | /// 360 | /// 序列化 361 | /// 362 | private void Serialize() 363 | { 364 | //如果输出文件夹不存在,先创建 365 | if (!Directory.Exists(cache.assetOutputFolder)) 366 | { 367 | Directory.CreateDirectory(cache.assetOutputFolder); 368 | } 369 | 370 | //无法缓存只能重新获取 371 | List sheets; 372 | List structs; 373 | GetSources(out sheets, out structs); 374 | 375 | //通过反射序列化 376 | UnityEngine.Object set = (UnityEngine.Object)Serializer.Serialize(sheets, structs); 377 | string o = cache.assetOutputFolder + "/" + ASSET_NAME; 378 | 379 | if (File.Exists(o)) 380 | { 381 | UnityEngine.Object old = AssetDatabase.LoadMainAssetAtPath(o); 382 | if(old != null) 383 | EditorUtility.CopySerialized(set, old); 384 | else 385 | AssetDatabase.CreateAsset(set, o); 386 | } 387 | else 388 | { 389 | AssetDatabase.CreateAsset(set, o); 390 | } 391 | 392 | Debug.Log("序列化完成!"); 393 | 394 | if (serializeCompleted != null) 395 | serializeCompleted(); 396 | 397 | } 398 | 399 | /// 400 | /// 检测类型是否启用 401 | /// 402 | /// 403 | /// 404 | private bool TypeEnabled(string extension,out OriginalType type) 405 | { 406 | switch(extension) 407 | { 408 | case ".txt": 409 | type = OriginalType.Txt; 410 | return cache.txtEnabled; 411 | case ".csv": 412 | type = OriginalType.Csv; 413 | return cache.csvEnabled; 414 | /* 415 | case ".json": 416 | type = OriginalType.Json; 417 | return cache.jsonEnabled; 418 | case ".xml": 419 | type = OriginalType.Xml; 420 | return cache.xmlEnabled; 421 | */ 422 | case ".xlsx": 423 | type = OriginalType.Xlsx; 424 | return cache.xlsxEnabled; 425 | } 426 | type = OriginalType.Txt; 427 | return false; 428 | } 429 | 430 | /// 431 | /// like ~$Equip 432 | /// 433 | /// 434 | private bool IsTemporaryFile(string fileName) 435 | { 436 | return Regex.Match(fileName, @"^\~\$").Success; 437 | } 438 | } 439 | 440 | [System.Serializable] 441 | public class Cache 442 | { 443 | public string sourceFolder = "Assets"; 444 | public string configOutputFolder = "Assets/Output"; 445 | public string assetOutputFolder = "Assets/Resources"; 446 | 447 | public bool txtEnabled = true; 448 | public bool csvEnabled = true; 449 | // public bool jsonEnabled = true; 450 | // public bool xmlEnabled = true; 451 | // public bool xlsEnabled = true; 452 | public bool xlsxEnabled = true; 453 | 454 | public string serializerOutputFolder { get { return configOutputFolder + "/Serializer"; } } 455 | } 456 | } 457 | 458 | -------------------------------------------------------------------------------- /Editor/ConfigWindow.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8e0c1bb5faaab1b40a20d9377881ce3b 3 | timeCreated: 1499408366 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Editor/Generator.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b7d722d7821942948831389a2e1e0707 3 | folderAsset: yes 4 | timeCreated: 1499670931 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Editor/Generator/SheetGenerator.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Rick 3 | * Create: 2017/7/7 15:45:53 4 | * Email: rickjiangshu@gmail.com 5 | * Follow: https://github.com/RickJiangShu 6 | */ 7 | namespace ConfigManagerEditor 8 | { 9 | using System; 10 | using System.Collections; 11 | using System.Collections.Generic; 12 | using System.IO; 13 | 14 | /// 15 | /// Config解析器,负责将txt解析成*.cs 16 | /// 17 | public class SheetGenerator 18 | { 19 | private static string templete = 20 | @"using System.Collections.Generic; 21 | 22 | [System.Serializable] 23 | public class /*ClassName*/ 24 | { 25 | /*DeclareProperties*/ 26 | private static Dictionary dictionary = new Dictionary(); 27 | 28 | /// 29 | /// 通过/*IDField*/获取/*ClassName*/的实例 30 | /// 31 | /// 索引 32 | /// /*ClassName*/的实例 33 | public static /*ClassName*/ Get(/*IDType*/ /*IDField*/) 34 | { 35 | return dictionary[/*IDField*/]; 36 | } 37 | 38 | /// 39 | /// 获取字典 40 | /// 41 | /// 字典 42 | public static Dictionary GetDictionary() 43 | { 44 | return dictionary; 45 | } 46 | } 47 | "; 48 | private static string templete2 = 49 | @" /// 50 | /// {0} 51 | /// 52 | public {1} {2}; 53 | 54 | "; 55 | 56 | /// 57 | /// 生成Config 58 | /// 59 | public static void Generate(List sheets, string outputFolder) 60 | { 61 | foreach (SheetSource src in sheets) 62 | { 63 | string content = templete; 64 | string outputPath = outputFolder + "/" + src.className + ".cs"; 65 | 66 | string idType = src.matrix[1, 0]; 67 | string idField = src.matrix[2, 0]; 68 | 69 | //属性声明 70 | string declareProperties = ""; 71 | for (int x = 0; x < src.column; x++) 72 | { 73 | string comment = src.matrix[0, x]; 74 | string csType = src.matrix[1, x]; 75 | string field = src.matrix[2, x]; 76 | string declare = string.Format(templete2, comment, csType, field); 77 | declareProperties += declare; 78 | } 79 | 80 | //替换 81 | content = content.Replace("/*ClassName*/", src.className); 82 | content = content.Replace("/*DeclareProperties*/", declareProperties); 83 | content = content.Replace("/*IDType*/", idType); 84 | content = content.Replace("/*IDField*/", idField); 85 | 86 | //写入 87 | ConfigTools.WriteFile(outputPath, content); 88 | } 89 | } 90 | 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /Editor/Generator/SheetGenerator.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4e964d6f1bff0f64298e33dedc03e469 3 | timeCreated: 1499415101 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Editor/Generator/StructGenerator.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Rick 3 | * Create: 2017/7/18 17:08:58 4 | * Email: rickjiangshu@gmail.com 5 | * Follow: https://github.com/RickJiangShu 6 | */ 7 | namespace ConfigManagerEditor 8 | { 9 | using System; 10 | using System.Collections; 11 | using System.Collections.Generic; 12 | using UnityEngine; 13 | 14 | /// 15 | /// 结构化配置生成器 16 | /// 17 | public class StructGenerator 18 | { 19 | private const string templateRoot = 20 | @"[System.Serializable] 21 | public class /*ClassName*/ 22 | { 23 | /*Declarations*/ 24 | public static /*ClassName*/ instance; 25 | public static /*ClassName*/ Get() 26 | { 27 | return instance; 28 | } 29 | }"; 30 | 31 | private static string tempDeclaration = 32 | @" public {0} {1}; 33 | "; 34 | 35 | 36 | private const string templateSubObject = 37 | @"[System.Serializable] 38 | public class /*ClassName*/ 39 | { 40 | /*Declarations*/ 41 | } 42 | "; 43 | 44 | //decalration 45 | 46 | private static List subObjects; 47 | private static uint subID; 48 | 49 | public static void Generate(List structs, string outputFolder) 50 | { 51 | subObjects = new List(); 52 | subID = 0; 53 | 54 | //创建各个Json 55 | foreach (StructSource src in structs) 56 | { 57 | string content = templateRoot; 58 | string outputPath = outputFolder + "/" + src.className + ".cs"; 59 | 60 | //Decalre 61 | List declarations = DeclareObject(src.obj); 62 | 63 | 64 | string declarationStr = CombineDeclarations(declarations); 65 | 66 | //替换 67 | content = content.Replace("/*ClassName*/", src.className); 68 | content = content.Replace("/*Declarations*/", declarationStr); 69 | 70 | //写入 71 | ConfigTools.WriteFile(outputPath, content); 72 | } 73 | 74 | //创建JsonObject子类 75 | string allSubClasses = ""; 76 | 77 | foreach (StructSubObject subObj in subObjects) 78 | { 79 | string declarations = CombineDeclarations(subObj.declarations); 80 | string subClass = templateSubObject; 81 | subClass = subClass.Replace("/*ClassName*/", subObj.name); 82 | subClass = subClass.Replace("/*Declarations*/", declarations); 83 | 84 | allSubClasses += subClass; 85 | } 86 | 87 | //写入 88 | ConfigTools.WriteFile(outputFolder + "/" + "StructObjects.cs",allSubClasses); 89 | 90 | subObjects = null; 91 | } 92 | 93 | /// 94 | /// 声明对象 95 | /// 96 | private static List DeclareObject(Dictionary obj) 97 | { 98 | List declarations = new List(); 99 | 100 | //创建声明 101 | foreach (string field in obj.Keys) 102 | { 103 | object value = obj[field]; 104 | string typeStr = GetDeclarationType(value); 105 | 106 | Declaration declaration = new Declaration(); 107 | declaration.type = typeStr; 108 | declaration.field = field; 109 | declarations.Add(declaration); 110 | } 111 | 112 | return declarations; 113 | } 114 | 115 | /// 116 | /// 检测数组中是否含有子对象 117 | /// 118 | /// 119 | private static string DeclareArray(object[] array) 120 | { 121 | //判断是否需要创建Array类 122 | bool arrayClass = false; 123 | Type lt = null; 124 | object li = null; 125 | foreach (object item in array) 126 | { 127 | Type t = item.GetType(); 128 | if (t.IsArray) 129 | { 130 | arrayClass = true; 131 | break; 132 | } 133 | if (lt != null) 134 | { 135 | //类型不同 136 | if (lt != t) 137 | { 138 | //数字类型不同不算 139 | if (!ConfigTools.IsNumber(lt) || !ConfigTools.IsNumber(t)) 140 | { 141 | arrayClass = true; 142 | break; 143 | } 144 | } 145 | //类型相同 && 都是Dictionary 146 | else if(t == ConfigConstants.OBJECT_DICTIONARY_TYPE) 147 | { 148 | if (!ConfigTools.IsSameObjectDictionary((Dictionary)li, (Dictionary)item)) 149 | { 150 | arrayClass = true; 151 | break; 152 | } 153 | } 154 | } 155 | lt = t; 156 | li = item; 157 | } 158 | 159 | //普通数组 160 | if (!arrayClass) 161 | { 162 | Type firstType = array[0].GetType(); 163 | if (firstType == typeof(Dictionary)) 164 | { 165 | return GetSubObject((Dictionary)array[0]) + "[]"; 166 | } 167 | if (ConfigTools.IsNumber(firstType))//数字 168 | { 169 | return ConfigTools.FindMinimalNumberType(array).ToString() + "[]"; 170 | } 171 | else 172 | { 173 | //string bool 174 | return firstType.ToString() + "[]"; 175 | } 176 | } 177 | else//创建数组对象 178 | { 179 | List declarations = new List(); 180 | int i = 0; 181 | foreach (object item in array) 182 | { 183 | Declaration declaration = new Declaration(); 184 | declaration.field = "arg" + i++; 185 | declaration.type = GetDeclarationType(item); 186 | declarations.Add(declaration); 187 | } 188 | return GetSubObject(declarations); 189 | } 190 | 191 | /* 192 | //获取所有项的类型名称 193 | Type type = array[0].GetType(); 194 | //对象 195 | if (type == typeof(Dictionary)) 196 | { 197 | return GetSubObject((Dictionary)array[0]) + "[]"; 198 | } 199 | //数组 200 | else if (type.IsArray) 201 | { 202 | Debug.LogError("不支持嵌套数组!"); 203 | return "string"; 204 | } 205 | else 206 | { 207 | return type.ToString() + "[]"; 208 | } 209 | */ 210 | } 211 | 212 | 213 | /// 214 | /// 组合声明成字符串 215 | /// 216 | /// 217 | /// 218 | private static string CombineDeclarations(List declarations) 219 | { 220 | string declarationsStr = ""; 221 | foreach (Declaration declaration in declarations) 222 | { 223 | declarationsStr += string.Format(tempDeclaration, declaration.type, declaration.field); 224 | } 225 | return declarationsStr; 226 | } 227 | 228 | /// 229 | /// 获取Object、Array 和 基础类型的声明字符 230 | /// 231 | /// 232 | /// 233 | private static string GetDeclarationType(object value) 234 | { 235 | Type type = value.GetType(); 236 | string typeStr = ""; 237 | //对象 238 | if (type == typeof(Dictionary)) 239 | { 240 | string subObjName = GetSubObject((Dictionary)value); 241 | typeStr = subObjName; 242 | } 243 | //数组 244 | else if (type.IsArray) 245 | { 246 | //检测是否需要添加子类 247 | object[] array = (object[])value; 248 | typeStr = DeclareArray(array); 249 | } 250 | else 251 | { 252 | //字符串、Number、Bool 253 | typeStr = type.ToString(); 254 | } 255 | return typeStr; 256 | } 257 | 258 | /// 259 | /// 获取/新建SubObject 260 | /// 261 | /// 262 | private static string GetSubObject(Dictionary obj) 263 | { 264 | List declarations = DeclareObject(obj);//声明列表 265 | return GetSubObject(declarations); 266 | } 267 | 268 | private static string GetSubObject(List declarations) 269 | { 270 | //和已有的子对象进行比对 271 | foreach (StructSubObject subObj in subObjects) 272 | { 273 | if (subObj.Equals(declarations)) 274 | return subObj.name; 275 | } 276 | 277 | //新建子对象 278 | StructSubObject subObject = new StructSubObject(); 279 | subObject.id = ++subID; 280 | subObject.name = "StructObject" + subObject.id; 281 | subObject.declarations = declarations; 282 | subObjects.Add(subObject); 283 | 284 | return subObject.name; 285 | } 286 | 287 | 288 | /// 289 | /// JsonObject 子对象 290 | /// 291 | private class StructSubObject 292 | { 293 | public uint id; 294 | public string name; 295 | public List declarations;//声明 296 | 297 | /// 298 | /// 对比 299 | /// 300 | /// 301 | /// 302 | public bool Equals(List ds) 303 | { 304 | int l = declarations.Count; 305 | int k = ds.Count; 306 | if (l != k) 307 | return false; 308 | 309 | for (int i = 0; i < l; i++) 310 | { 311 | bool isHave = false; 312 | for (int j = 0; j < k; j++) 313 | { 314 | if (declarations[i].type == ds[j].type && declarations[i].field == ds[j].field) 315 | { 316 | isHave = true; 317 | break; 318 | } 319 | } 320 | if (!isHave) 321 | return false; 322 | } 323 | 324 | return true; 325 | } 326 | } 327 | 328 | /// 329 | /// 声明 330 | /// 331 | private class Declaration 332 | { 333 | public string type; 334 | public string field; 335 | } 336 | } 337 | 338 | } 339 | 340 | -------------------------------------------------------------------------------- /Editor/Generator/StructGenerator.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 01ce24980e3a8ce4aa760071bc5bdb5b 3 | timeCreated: 1500369036 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Editor/Library.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c08e3183b6ea5f348b8dc7a7bf96c0a8 3 | folderAsset: yes 4 | timeCreated: 1500876515 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Editor/Library/EPPlus.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 27d86394cd1f3a443bbc02387786f259 3 | folderAsset: yes 4 | timeCreated: 1501236620 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Editor/Library/EPPlus/EPPlus.XML.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f5eea5f583c1d4e4e97c247539458bb1 3 | timeCreated: 1448966026 4 | licenseType: Pro 5 | TextScriptImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Library/EPPlus/EPPlus.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RickJiangShu/ConfigManager/e107b8bedb235fe6b3b3d03cf82d96c7fee2ec9a/Editor/Library/EPPlus/EPPlus.dll -------------------------------------------------------------------------------- /Editor/Library/EPPlus/EPPlus.dll.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 779effc783b37445b91de0d6b70078e3 3 | timeCreated: 1448966026 4 | licenseType: Pro 5 | PluginImporter: 6 | serializedVersion: 1 7 | iconMap: {} 8 | executionOrder: {} 9 | isPreloaded: 0 10 | isOverridable: 0 11 | platformData: 12 | Editor: 13 | enabled: 1 14 | settings: 15 | DefaultValueInitialized: true 16 | WindowsStoreApps: 17 | enabled: 0 18 | settings: 19 | CPU: AnyCPU 20 | data: 21 | enabled: 0 22 | settings: {} 23 | userData: 24 | assetBundleName: 25 | assetBundleVariant: 26 | -------------------------------------------------------------------------------- /Editor/Library/EPPlus/readme.txt: -------------------------------------------------------------------------------- 1 | EPPlus 4.0.4 2 | 3 | Visit epplus.codeplex.com for the latest information 4 | 5 | EPPlus-Create Advanced Excel spreadsheet. 6 | 7 | New features 8 | 9 | Replaced Packaging API with DotNetZip 10 | * This will remove any problems with Isolated Storage and enable multi threading 11 | 12 | 13 | New Cell store 14 | * Less memory consumtion 15 | * Insert columns (not on the range level) 16 | * Faster row inserts, 17 | 18 | Formula Parser 19 | * Calculates all formulas in a workbook, a worksheet or in a specified range 20 | * 100+ functions implemented 21 | * Access via Calculate methods on Workbook, Worksheet and Range objects. 22 | * Add custom/missing Excel functions via Workbook.FormulaParserManager. 23 | * Samples added to the EPPlusSamples project. 24 | 25 | The formula parser does not support Array Formulas 26 | * Intersect operator (Space) 27 | * References to external workbooks 28 | * And probably a whole lot of other stuff as well :) 29 | 30 | Perfomance 31 | *Of course the performance of the formula parser is nowhere near Excels.Our focus has been functionality. 32 | 33 | Agile Encryption (Office 2012-) 34 | * Support for newer type of encryption. 35 | 36 | Minor new features 37 | * Chart worksheets 38 | * New Chart Types Bubblecharts 39 | * Radar Charts 40 | * Area Charts 41 | * And lots of bugfixes... 42 | 43 | Beta 2 Changes 44 | * Fixed bug when using RepeatColumns & RepeatRows at the same time. 45 | * VBA project will be left untouched if its not accessed. 46 | * Fixed problem with strings on save. 47 | * Added locks to the cellstore for access by mulitple threads. 48 | * Implemented Indirect function 49 | * Used DisplayNameAttribute to generate column headers from LoadFromCollection 50 | * Rewrote ExcelRangeBase.Copy function. 51 | * Added caching to Save ZipStream for Cells and shared strings to speed up the Save method. 52 | * Added Missing InsertColumn and DeleteColumn 53 | * Added pull request to support Date1904 54 | * Added pull request ExcelWorksheet.LoadFromDataReader 55 | 56 | Release Candidare changes 57 | * Fixed some problems with Range.Copy Function 58 | * InsertColumn and Delete column didn't work in some cases 59 | * Chart.DisplayBlankAs had the wrong default type in Excel 2010+ 60 | * Datavalidation list overflow cauesed corruption of the package 61 | * Fixed a few Calculation when refering ranges (for example If) function and some 62 | * Added ChartAxis.DisplayUnit 63 | * Fixed a bug related to shared formulas 64 | * Named styles faild in some cases. 65 | * Style.Indent got an invalid value in some cases. 66 | * Fixed a problem with AutofitColumns method. 67 | * Performance fix. 68 | * An a whole lot of other small fixes. 69 | 70 | 4.0.1 Fixes 71 | * VBA unreadable content 72 | * Fixed a few issues with InsertRow and DeleteRow 73 | * Fixed bug in Average and AverageA 74 | * Handling of Div/0 in functions 75 | * Fixed VBA CodeModule error when copying a worksheet. 76 | * Value decoding when reading str element for cell value. 77 | * Better exception when accessing a worksheet out of range in the Excelworksheets indexer. 78 | * Added Small and Large function to formula parser. Performance fix when encountering an unknown function. 79 | * Fixed handling strings in formulas 80 | * Calculate hanges if formula start with a parenthes. 81 | * Worksheet.Dimension returned an invalid range in some cases. 82 | * Rowheight was wrong in some cases. 83 | * ExcelSeries.Header had an incorrect validation check. 84 | 85 | 4.0.2 Fixes 86 | * Fixes a whole bunch of bugs related to the cell store (Worksheet.InsertColumn, Worksheet.InsertRow, Worksheet.DeleteColumn, Worksheet.DeleteRow, Range.Copy, Range.Clear) 87 | * Added functions Acos, Acosh, Asinh, Atanh, Atan, CountBlank, CountIfs, Mina, Offset, Median, Hyperlink, Rept 88 | * Fix for reading Excel comment content from the t-element. 89 | * Fix to make Range.LoadFromCollection work better with inheritence 90 | * And alot of other smal fixes 91 | 92 | 4.0.3 Fixes 93 | * Added compilation directive for MONO (Thanks Danny) 94 | * Added functions IfError, Char, Error.Type, Degrees, Fixed, IsNonText, IfNa and SumIfs 95 | * And fixed a lot of issues. See http://epplus.codeplex.com/SourceControl/list/changesets for more details 96 | 97 | 4.0.4 Fixes 98 | * Added functions Daverage, Dvar Dvarp, DMax, DMin DSum, DGet, DCount and DCountA 99 | * Exposed the formula parser logging functionality via FormulaParserManager. 100 | * And fixed a lot of issues. See http://epplus.codeplex.com/SourceControl/list/changesets for more details 101 | -------------------------------------------------------------------------------- /Editor/Library/EPPlus/readme.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 28b2754fb50034ca0aaaa7609262d7af 3 | timeCreated: 1448966026 4 | licenseType: Pro 5 | TextScriptImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Parser.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | <<<<<<< Updated upstream 3 | guid: c90bab94acbd5a047b8cd7f397c28493 4 | folderAsset: yes 5 | timeCreated: 1500364185 6 | ======= 7 | guid: a83253fcf9c00e04ebb1a86d234e4d64 8 | folderAsset: yes 9 | timeCreated: 1500348049 10 | >>>>>>> Stashed changes 11 | licenseType: Pro 12 | DefaultImporter: 13 | userData: 14 | assetBundleName: 15 | assetBundleVariant: 16 | -------------------------------------------------------------------------------- /Editor/Parser/JSONParser.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Rick 3 | * Create: 2017/7/18 11:14:54 4 | * Email: rickjiangshu@gmail.com 5 | * Follow: https://github.com/RickJiangShu 6 | */ 7 | namespace ConfigManagerEditor 8 | { 9 | using System.Collections; 10 | using System.Collections.Generic; 11 | using System.Text.RegularExpressions; 12 | 13 | /// 14 | /// Json解析器 15 | /// 16 | public class JsonParser 17 | { 18 | #region 正则表达式 19 | //基础类型 20 | private const string regexString = "\"([^\"]*)\"";//"[^"]*" 21 | private const string regexNumber = @"(-?(?=[1-9]|0(?!\d))\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)"; 22 | private const string regexBool = "(true|false|null)"; 23 | #endregion 24 | 25 | public static StructSource Parse(string content,string fileName) 26 | { 27 | StructSource source = new StructSource(); 28 | 29 | source.original = content; 30 | source.originalName = fileName.Substring(0, fileName.LastIndexOf('.')); ;//文件名 31 | source.className = source.originalName + "Json";//类名 32 | 33 | source.obj = ParseObject(content); 34 | return source; 35 | } 36 | 37 | 38 | /// 39 | /// 解析对象 40 | /// 41 | /// 42 | /// 43 | private static Dictionary ParseObject(string contentOfObject) 44 | { 45 | Dictionary obj = new Dictionary(); 46 | List items = Split(contentOfObject); 47 | 48 | for (int i = 0, l = items.Count; i < l; i++) 49 | { 50 | string item = items[i]; 51 | string name; 52 | string contentOfValue; 53 | SplitObject(item, out name, out contentOfValue);//切割对象 54 | 55 | int type = IsObjectOrArray(contentOfValue); 56 | object value = null; 57 | 58 | switch (type) 59 | { 60 | case 0: 61 | value = ParseBaseValue(contentOfValue); 62 | break; 63 | case 1: 64 | value = ParseObject(contentOfValue); 65 | break; 66 | case 2: 67 | value = ParseArray(contentOfValue); 68 | break; 69 | } 70 | obj.Add(name, value); 71 | } 72 | return obj; 73 | } 74 | 75 | /// 76 | /// 解析数组 77 | /// 78 | /// 79 | /// 80 | private static object[] ParseArray(string contentOfArray) 81 | { 82 | List objects = new List(); 83 | List items = Split(contentOfArray); 84 | 85 | for (int i = 0, l = items.Count; i < l; i++) 86 | { 87 | string item = items[i]; 88 | int type = IsObjectOrArray(item); 89 | switch(type) 90 | { 91 | case 0: 92 | objects.Add(ParseBaseValue(item)); 93 | break; 94 | case 1: 95 | objects.Add(ParseObject(item)); 96 | break; 97 | case 2: 98 | objects.Add(ParseArray(item)); 99 | break; 100 | } 101 | } 102 | return objects.ToArray(); 103 | } 104 | 105 | private static object ParseBaseValue(string contentOfValue) 106 | { 107 | Match result; 108 | result = Regex.Match(contentOfValue, regexString); 109 | if (result.Success) 110 | { 111 | return result.Groups[1].Value; 112 | } 113 | 114 | result = Regex.Match(contentOfValue, regexNumber); 115 | if (result.Success) 116 | { 117 | return ConfigTools.ConvertNumber(result.Groups[1].Value); 118 | } 119 | 120 | result = Regex.Match(contentOfValue, regexBool); 121 | if (result.Success) 122 | { 123 | return ConfigTools.ConvertBool(result.Groups[1].Value); 124 | } 125 | 126 | return null; 127 | } 128 | 129 | 130 | /// 131 | /// 切割对象 132 | /// 133 | /// 134 | /// 135 | private static void SplitObject(string item, out string name, out string contentOfValue) 136 | { 137 | int nameStart = -1; 138 | int nameEnd = -1; 139 | int colon = -1; 140 | 141 | for (int i = 0, l = item.Length; i < l; i++) 142 | { 143 | if (item[i] == '"') 144 | { 145 | if (nameStart == -1) 146 | nameStart = i + 1; 147 | else 148 | nameEnd = i; 149 | } 150 | 151 | if (item[i] == ':') 152 | { 153 | colon = i; 154 | break; 155 | } 156 | } 157 | name = item.Substring(nameStart, nameEnd - nameStart); 158 | contentOfValue = item.Substring(colon + 1);//值为冒号后面都为值 159 | } 160 | 161 | /// 162 | /// 以','切割数组或数组 163 | /// 164 | /// 格式 {p1,p2,p3} or [p1,p2,p3] 165 | /// 166 | private static List Split(string objectOrArray) 167 | { 168 | List items = new List(); 169 | 170 | int last = -1; 171 | int level = -1; 172 | bool inString = false; 173 | for (int i = 0, l = objectOrArray.Length; i < l; i++) 174 | { 175 | char c = objectOrArray[i]; 176 | 177 | if (c == '"') 178 | { 179 | inString = !inString; 180 | } 181 | else 182 | { 183 | if (inString) 184 | continue; 185 | 186 | if (c == '{' || c == '[') 187 | { 188 | if (++level == 0) 189 | { 190 | last = i + 1; 191 | } 192 | } 193 | else if (c == '}' || c == ']') 194 | { 195 | if (level-- == 0) 196 | { 197 | string item = objectOrArray.Substring(last, i - last); 198 | items.Add(item); 199 | } 200 | } 201 | else if (c == ',' && level == 0) 202 | { 203 | string item = objectOrArray.Substring(last, i - last); 204 | items.Add(item); 205 | last = i + 1; 206 | } 207 | } 208 | } 209 | 210 | return items; 211 | } 212 | 213 | /// 214 | /// 判断是否是对象或者数组 215 | /// 216 | /// 217 | private static int IsObjectOrArray(string contentOfValue) 218 | { 219 | for (int i = 0, l = contentOfValue.Length; i < l; i++) 220 | { 221 | if (contentOfValue[i] == '{') 222 | return 1; 223 | else if (contentOfValue[i] == '[') 224 | return 2; 225 | } 226 | return 0; 227 | } 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /Editor/Parser/JSONParser.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | <<<<<<< Updated upstream 3 | guid: 0ce7ed4a68520114b8c3746e44dc61e5 4 | timeCreated: 1500364185 5 | ======= 6 | guid: 68320383b59bac44a854c96c8d42b704 7 | timeCreated: 1500348049 8 | >>>>>>> Stashed changes 9 | licenseType: Pro 10 | MonoImporter: 11 | serializedVersion: 2 12 | defaultReferences: [] 13 | executionOrder: 0 14 | icon: {instanceID: 0} 15 | userData: 16 | assetBundleName: 17 | assetBundleVariant: 18 | -------------------------------------------------------------------------------- /Editor/Parser/SheetParser.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Rick 3 | * Create: 2017/7/18 16:40:02 4 | * Email: rickjiangshu@gmail.com 5 | * Follow: https://github.com/RickJiangShu 6 | */ 7 | namespace ConfigManagerEditor 8 | { 9 | using OfficeOpenXml; 10 | using System.Text.RegularExpressions; 11 | /// 12 | /// SheetParser 13 | /// 14 | public class SheetParser 15 | { 16 | 17 | /// 18 | /// Excel 19 | /// 20 | /// 21 | /// 22 | public static SheetSource Parse(ExcelWorksheet sheet, string fileName) 23 | { 24 | int row = sheet.Dimension.Rows; 25 | int column = sheet.Dimension.Columns; 26 | 27 | string[,] matrix = new string[row, column]; 28 | for (int i = 0; i < row; i++) 29 | { 30 | for (int j = 0; j < column; j++) 31 | { 32 | string value = sheet.GetValue(i + 1, j + 1); 33 | matrix[i, j] = value; 34 | } 35 | } 36 | ConvertOriginalType(matrix); 37 | string originalName = fileName.Substring(0, fileName.LastIndexOf('.')); 38 | string className = originalName + "Sheet"; 39 | return CreateSource(sheet, originalName, className, matrix); 40 | } 41 | 42 | /// 43 | /// 切割字符串(txt/csv) 44 | /// 45 | /// 46 | /// 47 | /// 48 | public static SheetSource Parse(string content,string fileName) 49 | { 50 | string lf; 51 | string sv; 52 | bool isCsv; 53 | 54 | //判断换行符 55 | if (content.IndexOf("\r\n") != -1) 56 | lf = "\r\n"; 57 | else 58 | lf = "\n"; 59 | 60 | //判断分割符 61 | if (content.IndexOf("\t") != -1) 62 | { 63 | sv = "\t"; 64 | isCsv = false; 65 | } 66 | else 67 | { 68 | sv = ",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)";//Fork:https://stackoverflow.com/questions/6542996/how-to-split-csv-whose-columns-may-contain 69 | isCsv = true; 70 | } 71 | 72 | string originalName = fileName.Substring(0, fileName.LastIndexOf('.')); 73 | string className = originalName + "Config"; 74 | string[,] matrix = Content2Matrix(content, sv, lf); 75 | 76 | if (isCsv) 77 | ClearCsvQuotation(matrix); 78 | 79 | ConvertOriginalType(matrix); 80 | return CreateSource(content, originalName, className, matrix); 81 | } 82 | 83 | private static SheetSource CreateSource(object original,string originalName,string className,string[,] matrix) 84 | { 85 | SheetSource source = new SheetSource(); 86 | source.original = original; 87 | source.originalName = originalName;//文件名 88 | source.className = className;//类名 89 | source.matrix = matrix; 90 | return source; 91 | } 92 | 93 | /// 94 | /// 从配置转换成矩阵数组(0行1列) 95 | /// 96 | /// 配置文件 97 | /// 分隔符 Separated Values 98 | /// 换行符 Line Feed 99 | /// 100 | private static string[,] Content2Matrix(string config, string sv, string lf) 101 | { 102 | config = config.Trim();//清空末尾的空白 103 | 104 | //分割 105 | string[] lines = Regex.Split(config, lf); 106 | string[] firstLine = Regex.Split(lines[0], sv, RegexOptions.Compiled); 107 | 108 | int row = lines.Length; 109 | int col = firstLine.Length; 110 | string[,] matrix = new string[row, col]; 111 | //为第一行赋值 112 | for (int i = 0, l = firstLine.Length; i < l; i++) 113 | { 114 | matrix[0, i] = firstLine[i]; 115 | } 116 | //为其他行赋值 117 | for (int i = 1, l = lines.Length; i < l; i++) 118 | { 119 | string[] line = Regex.Split(lines[i], sv); 120 | for (int j = 0, k = line.Length; j < k; j++) 121 | { 122 | matrix[i, j] = line[j]; 123 | } 124 | } 125 | return matrix; 126 | } 127 | 128 | /// 129 | /// 转换原始配置配型 -> CShartType 130 | /// 131 | private static void ConvertOriginalType(string[,] matrix) 132 | { 133 | int column = matrix.GetLength(1); 134 | for (int x = 0; x < column; x++) 135 | { 136 | matrix[1, x] = ConfigTools.SourceType2CSharpType(x, matrix); 137 | } 138 | } 139 | 140 | /// 141 | /// 去除CSV引号 142 | /// 143 | /// 144 | private static void ClearCsvQuotation(string[,] matrix) 145 | { 146 | int row = matrix.GetLength(0); 147 | int column = matrix.GetLength(1); 148 | for (int y = 0; y < row; y++) 149 | { 150 | for (int x = 0; x < column; x++) 151 | { 152 | string v = matrix[y, x]; 153 | if (string.IsNullOrEmpty(v) || v.Length < 2) 154 | continue; 155 | 156 | if (v[0] == '"') 157 | { 158 | v = v.Remove(0, 1); 159 | v = v.Remove(v.Length - 1, 1); 160 | matrix[y, x] = v; 161 | } 162 | } 163 | } 164 | } 165 | } 166 | } 167 | 168 | -------------------------------------------------------------------------------- /Editor/Parser/SheetParser.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ef4dc6a8924a9d44c983f93d005fdda8 3 | timeCreated: 1500368450 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Editor/Parser/XmlParser.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Rick 3 | * Create: 2017/7/18 13:40:02 4 | * Email: rickjiangshu@gmail.com 5 | * Follow: https://github.com/RickJiangShu 6 | */ 7 | namespace ConfigManagerEditor 8 | { 9 | using System.Collections; 10 | using System.Xml; 11 | using System.Collections.Generic; 12 | 13 | public class XmlParser 14 | { 15 | public static StructSource Parse(string content,string fileName) 16 | { 17 | StructSource source = new StructSource(); 18 | 19 | source.original = content; 20 | source.originalName = fileName.Substring(0, fileName.LastIndexOf('.')); ;//文件名 21 | source.className = source.originalName + "Xml";//类名 22 | 23 | XmlDocument document = new XmlDocument(); 24 | document.LoadXml(content); 25 | source.obj = (Dictionary)ParseNode(document); 26 | return source; 27 | } 28 | 29 | private static object ParseNode(XmlNode node) 30 | { 31 | //判断是否是对象或数组 32 | if (IsObject(node)) 33 | { 34 | Dictionary result = new Dictionary(); 35 | //添加属性字段 36 | if (node.Attributes != null) 37 | { 38 | foreach (XmlAttribute attribute in node.Attributes) 39 | { 40 | result.Add(attribute.Name, ConfigTools.ConvertBaseObject(attribute.Value)); 41 | } 42 | } 43 | 44 | List addedNodeNames = new List(); 45 | //添加子节点(如果多个同名子节点则是数组) 46 | foreach (XmlNode childNode in node.ChildNodes) 47 | { 48 | if (childNode.NodeType != XmlNodeType.Element) 49 | continue; 50 | 51 | string childName = childNode.Name; 52 | if (addedNodeNames.Contains(childName)) 53 | continue; 54 | 55 | XmlNodeList sameNameNodes = node.SelectNodes(childName);//相同名字的 56 | 57 | //数组 58 | if (sameNameNodes.Count > 1) 59 | { 60 | object[] objects = new object[sameNameNodes.Count]; 61 | for (int i = 0, l = sameNameNodes.Count; i < l; i++) 62 | { 63 | XmlNode sameNameChild = sameNameNodes[i]; 64 | objects[i] = ParseNode(sameNameChild); 65 | } 66 | result.Add(childName, objects); 67 | } 68 | //对象 69 | else 70 | { 71 | result.Add(childName, ParseNode(childNode)); 72 | } 73 | 74 | addedNodeNames.Add(childName); 75 | } 76 | 77 | return result; 78 | } 79 | return ConfigTools.ConvertBaseObject(node.InnerText.Trim()); 80 | } 81 | 82 | /// 83 | /// 判断是否是对象 84 | /// 85 | /// 86 | /// 87 | private static bool IsObject(XmlNode node) 88 | { 89 | if (node.Attributes != null && node.Attributes.Count > 0) 90 | return true; 91 | 92 | int elementCount = 0; 93 | foreach (XmlNode child in node.ChildNodes) 94 | { 95 | if (child.NodeType == XmlNodeType.Element) 96 | elementCount++; 97 | } 98 | return elementCount >= 1; 99 | } 100 | } 101 | 102 | 103 | } 104 | -------------------------------------------------------------------------------- /Editor/Parser/XmlParser.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 052178480d1ce774689554677c7ad7dd 3 | timeCreated: 1500632134 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Editor/Serializer.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 19f1d3b5e4bca4548878061f85c2bfff 3 | folderAsset: yes 4 | timeCreated: 1499670931 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Editor/Serializer/DeserializerGenerator.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Rick 3 | * Create: 2017/7/11 10:05:46 4 | * Email: rickjiangshu@gmail.com 5 | * Follow: https://github.com/RickJiangShu 6 | */ 7 | using UnityEngine; 8 | using UnityEditor; 9 | using System.Collections.Generic; 10 | 11 | namespace ConfigManagerEditor 12 | { 13 | /// 14 | /// 用于生成解析器 15 | /// 16 | public class DeserializerGenerator : ScriptableObject 17 | { 18 | 19 | private const string template = 20 | @"public class Deserializer 21 | { 22 | public static void Deserialize(SerializableSet set) 23 | { 24 | /*SetDictionaries*/ 25 | /*SetJsons*/ 26 | } 27 | } 28 | "; 29 | private const string template2 = 30 | @" 31 | for (int i = 0, l = set./*SourceName*/s.Length; i < l; i++) 32 | { 33 | /*ConfigName*/.GetDictionary().Add(set./*SourceName*/s[i]./*IDField*/, set./*SourceName*/s[i]); 34 | } 35 | "; 36 | private const string template3 = 37 | @" /*ClassName*/.instance = set./*SourceName*/; 38 | "; 39 | 40 | public static void Generate(List sheets,List jsons,string outputFolder) 41 | { 42 | string outputPath = outputFolder + "/Deserializer.cs"; 43 | string content = template; 44 | 45 | //sheets 46 | string setDictionaries = ""; 47 | foreach (SheetSource sheet in sheets) 48 | { 49 | string idField = sheet.matrix[2, 0]; 50 | string setScript = template2; 51 | 52 | setScript = setScript.Replace("/*ConfigName*/", sheet.className); 53 | setScript = setScript.Replace("/*SourceName*/", sheet.originalName); 54 | setScript = setScript.Replace("/*IDField*/", idField); 55 | 56 | setDictionaries += setScript; 57 | } 58 | 59 | //jsons 60 | string setJsons = ""; 61 | foreach (StructSource json in jsons) 62 | { 63 | string setScript = template3; 64 | setScript = setScript.Replace("/*ClassName*/", json.className); 65 | setScript = setScript.Replace("/*SourceName*/", json.originalName); 66 | 67 | setJsons += setScript; 68 | } 69 | 70 | 71 | content = content.Replace("/*SetDictionaries*/", setDictionaries); 72 | content = content.Replace("/*SetJsons*/", setJsons); 73 | 74 | ConfigTools.WriteFile(outputPath, content); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Editor/Serializer/DeserializerGenerator.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 360a368c9346a0c44b6c9c634c996964 3 | timeCreated: 1499751248 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Editor/Serializer/SerializableSetGenerator.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Rick 3 | * Create: 2017/7/10 15:04:49 4 | * Email: rickjiangshu@gmail.com 5 | * Follow: https://github.com/RickJiangShu 6 | */ 7 | namespace ConfigManagerEditor 8 | { 9 | using System; 10 | using System.Collections.Generic; 11 | /// 12 | /// 序列化集合 生产 13 | /// 14 | public class SerializableSetGenerator 15 | { 16 | private const string template = 17 | @"[System.Serializable] 18 | public class SerializableSet : UnityEngine.ScriptableObject 19 | { 20 | /*ConfigDeclarations*/ 21 | /*JsonDeclarations*/ 22 | } 23 | "; 24 | private const string template2 = 25 | @" public /*ConfigName*/[] /*SourceName*/s; 26 | "; 27 | private const string template3 = 28 | @" public /*JsonName*/ /*SourceName*/; 29 | "; 30 | 31 | public static void Generate(List sheets,List jsons, string outputFolder) 32 | { 33 | string outputPath = outputFolder + "/SerializableSet.cs"; 34 | string content = template; 35 | 36 | //Sheet声明 37 | string configDeclarations = ""; 38 | foreach (SheetSource sheet in sheets) 39 | { 40 | string declaration = template2; 41 | declaration = declaration.Replace("/*ConfigName*/", sheet.className); 42 | declaration = declaration.Replace("/*SourceName*/", sheet.originalName); 43 | configDeclarations += declaration; 44 | } 45 | content = content.Replace("/*ConfigDeclarations*/", configDeclarations); 46 | 47 | //Json声明 48 | string jsonDeclarations = ""; 49 | foreach (StructSource json in jsons) 50 | { 51 | string declaration = template3; 52 | declaration = declaration.Replace("/*JsonName*/", json.className); 53 | declaration = declaration.Replace("/*SourceName*/", json.originalName); 54 | jsonDeclarations += declaration; 55 | } 56 | content = content.Replace("/*JsonDeclarations*/", jsonDeclarations); 57 | 58 | ConfigTools.WriteFile(outputPath, content); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Editor/Serializer/SerializableSetGenerator.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5abd3047c68cb2f4387027625225f928 3 | timeCreated: 1499670931 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Editor/Serializer/Serializer.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Rick 3 | * Create: 2017/7/10 10:09:42 4 | * Email: rickjiangshu@gmail.com 5 | * Follow: https://github.com/RickJiangShu 6 | */ 7 | namespace ConfigManagerEditor 8 | { 9 | using System; 10 | using System.Reflection; 11 | using System.Collections; 12 | using System.Collections.Generic; 13 | /// 14 | /// 序列化器,通过反射的方式序列化出SerializableList 15 | /// 16 | public class Serializer 17 | { 18 | public static object Serialize(List sheets,List structs) 19 | { 20 | Type t = FindType("SerializableSet"); 21 | if (t == null) 22 | { 23 | UnityEngine.Debug.LogError("找不到SerializableSet类!"); 24 | return null; 25 | } 26 | 27 | object set = UnityEngine.ScriptableObject.CreateInstance(t); 28 | 29 | //Config 30 | foreach (SheetSource source in sheets) 31 | { 32 | string fieldName = source.originalName + "s"; 33 | Array configs = Source2Configs(source); 34 | FieldInfo fieldInfo = t.GetField(fieldName); 35 | fieldInfo.SetValue(set,configs); 36 | } 37 | 38 | //Json 39 | foreach (StructSource source in structs) 40 | { 41 | string fieldName = source.originalName; 42 | 43 | //使用 FromJson 获取json对象 44 | Type jsonType = FindType(source.className); 45 | object jsonObj = SerializeObject(jsonType, source.obj); 46 | 47 | FieldInfo fieldInfo = t.GetField(fieldName); 48 | fieldInfo.SetValue(set, jsonObj); 49 | } 50 | return set; 51 | } 52 | 53 | /// 54 | /// 从源数据反射为对应的配置数组 55 | /// 56 | /// 57 | private static Array Source2Configs(SheetSource source) 58 | { 59 | Type configType = FindType(source.className); 60 | 61 | int count = source.row - 3; 62 | Array configs = Array.CreateInstance(configType, count); 63 | for(int y = 3,i = 0;i 90 | /// 序列化结构对象 91 | /// 92 | /// 93 | /// 94 | private static object SerializeObject(Type outputType,Dictionary inputObject) 95 | { 96 | object obj = Activator.CreateInstance(outputType); 97 | 98 | foreach (string field in inputObject.Keys) 99 | { 100 | FieldInfo fieldInfo = outputType.GetField(field); 101 | Type fieldType = fieldInfo.FieldType;//生成类的类型 102 | 103 | object value = inputObject[field]; 104 | Type type = value.GetType();//解析的类型 105 | 106 | //对象 107 | if (type == typeof(Dictionary)) 108 | { 109 | object subOjbect = SerializeObject(fieldType, (Dictionary)value); 110 | fieldInfo.SetValue(obj, subOjbect); 111 | } 112 | //数组 113 | else if (type.IsArray) 114 | { 115 | object array = SerializeArray(fieldType, (object[])value); 116 | fieldInfo.SetValue(obj, array); 117 | } 118 | //String\Number\Bool 119 | else 120 | { 121 | fieldInfo.SetValue(obj, value); 122 | } 123 | } 124 | return obj; 125 | } 126 | 127 | /// 128 | /// 129 | /// 130 | /// FieldType 131 | /// object[] 132 | /// 133 | private static object SerializeArray(Type outputType,object[] inputArray) 134 | { 135 | //普通数组 136 | if (outputType.IsArray) 137 | { 138 | Type elementType = outputType.GetElementType(); 139 | Array outputArray = Array.CreateInstance(elementType, inputArray.Length); 140 | 141 | if (inputArray[0].GetType() == typeof(Dictionary)) 142 | { 143 | for (int i = 0; i < inputArray.Length; i++) 144 | { 145 | outputArray.SetValue(SerializeObject(elementType, (Dictionary)inputArray[i]), i); 146 | } 147 | } 148 | else 149 | { 150 | for (int i = 0; i < inputArray.Length; i++) 151 | { 152 | outputArray.SetValue(inputArray[i], i); 153 | } 154 | } 155 | return outputArray; 156 | } 157 | //自定义数组 158 | else 159 | { 160 | object arrayObject = Activator.CreateInstance(outputType); 161 | for (int i = 0, l = inputArray.Length; i < l; i++) 162 | { 163 | string field = "arg" + i; 164 | FieldInfo fieldInfo = outputType.GetField(field); 165 | 166 | //对象 167 | if (inputArray[i].GetType() == typeof(Dictionary)) 168 | { 169 | object value = SerializeObject(fieldInfo.FieldType, (Dictionary)inputArray[i]); 170 | fieldInfo.SetValue(arrayObject, value); 171 | } 172 | //数组 173 | else if (inputArray[i].GetType().IsArray) 174 | { 175 | fieldInfo.SetValue(arrayObject, SerializeArray(fieldInfo.FieldType, (object[])inputArray[i])); 176 | } 177 | else 178 | { 179 | fieldInfo.SetValue(arrayObject, inputArray[i]); 180 | } 181 | } 182 | return arrayObject; 183 | } 184 | } 185 | 186 | /// 187 | /// 188 | /// 189 | /// 190 | /// 191 | private static Type FindType(string qualifiedTypeName) 192 | { 193 | Type t = Type.GetType(qualifiedTypeName); 194 | 195 | if (t != null) 196 | { 197 | return t; 198 | } 199 | else 200 | { 201 | foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies()) 202 | { 203 | t = asm.GetType(qualifiedTypeName); 204 | if (t != null) 205 | return t; 206 | } 207 | return null; 208 | } 209 | } 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /Editor/Serializer/Serializer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e3cbdfe87ac97f64e90ad04aaffa878f 3 | timeCreated: 1499673074 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Editor/Source.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b69f6d818e4a0f7498f1788e9556fe3b 3 | folderAsset: yes 4 | timeCreated: 1500368450 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Editor/Source/SheetSource.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Rick 3 | * Create: 2017/7/18 16:38:31 4 | * Email: rickjiangshu@gmail.com 5 | * Follow: https://github.com/RickJiangShu 6 | */ 7 | namespace ConfigManagerEditor 8 | { 9 | /// 10 | /// 表格(.txt .csv .xls .xlsx) 11 | /// 12 | public class SheetSource : Source 13 | { 14 | /// 15 | /// 解析出来的矩阵 16 | /// 17 | public string[,] matrix; 18 | 19 | /// 20 | /// 行 21 | /// 22 | public int row 23 | { 24 | get { return matrix.GetLength(0); } 25 | } 26 | 27 | /// 28 | /// 列 29 | /// 30 | public int column 31 | { 32 | get { return matrix.GetLength(1); } 33 | } 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /Editor/Source/SheetSource.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3986e8453d4841742a069993783950da 3 | timeCreated: 1500368450 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Editor/Source/Source.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Rick 3 | * Create: 2017/7/10 13:49:46 4 | * Email: rickjiangshu@gmail.com 5 | * Follow: https://github.com/RickJiangShu 6 | */ 7 | namespace ConfigManagerEditor 8 | { 9 | /// 10 | /// 源数据 11 | /// 12 | public class Source 13 | { 14 | /// 15 | /// 原始内容(string/DataTable) 16 | /// 17 | public object original; 18 | 19 | /// 20 | /// 源文件的文件名 21 | /// 22 | public string originalName; 23 | 24 | /// 25 | /// 类名 26 | /// 27 | public string className; 28 | } 29 | 30 | /// 31 | /// 原始文件类型 32 | /// 33 | public enum OriginalType 34 | { 35 | Txt, 36 | Csv, 37 | Json, 38 | Xml, 39 | // Xls, 插件不支持 40 | Xlsx, 41 | // Sheet,//表格型数据 txt csv 42 | // Struct,//结构型数据 json xml 43 | } 44 | 45 | /// 46 | /// 源类型 47 | /// 48 | public enum SourceType 49 | { 50 | Sheet,//表格 51 | Struct,//结构 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Editor/Source/Source.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fb7947a941ae2f34d9cff62a28a8dcf1 3 | timeCreated: 1499670932 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Editor/Source/StructSource.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Rick 3 | * Create: 2017/7/18 17:02:57 4 | * Email: rickjiangshu@gmail.com 5 | * Follow: https://github.com/RickJiangShu 6 | */ 7 | namespace ConfigManagerEditor 8 | { 9 | using System.Collections.Generic; 10 | /// 11 | /// 结构型数据源 12 | /// 13 | public class StructSource : Source 14 | { 15 | public Dictionary obj; 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /Editor/Source/StructSource.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 189336c908d988048a02a3ad018c1ba7 3 | timeCreated: 1500369036 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /README.en.md: -------------------------------------------------------------------------------- 1 | # Config Manager 2 | Fast & Easy Config Manage Tool
3 | 4 | *Read this in other languages: [简体中文](README.md) [English](README.en.md)*
5 | ![logo](https://github.com/RickJiangShu/ConfigManager-Example/blob/master/Poster/Icon/Icon516x389.png "logo") 6 | 7 | # Features 8 | 1. **Fast Parse**: Parse in Editor 9 | 2. **Formats**: txt,csv,json,xml,xls,xlsx... 10 | 3. **No size**: all scripts in editor 11 | 4. **Intelligent**: Auto select type 12 | 5. **Reduce errors**: "."operation 13 | 14 | # Quick Start 15 | ### Edit Configs: 16 | ![](https://raw.githubusercontent.com/RickJiangShu/ConfigManager-Example/master/Poster/p3.jpg "")
17 | [Sheet](https://github.com/RickJiangShu/ConfigManager-Example/blob/master/Doc/Sheet.md "Sheet")
18 | [Json](https://github.com/RickJiangShu/ConfigManager-Example/blob/master/Doc/Json.md "Json")
19 | [Xml](https://github.com/RickJiangShu/ConfigManager-Example/blob/master/Doc/Xml.md "Xml")
20 | 21 | ### In Editor: 22 | 1. Press menu "Window/Config Manager"; 23 | 2. Set paths; 24 | 3. Press "output" 25 | 26 | ![](https://raw.githubusercontent.com/RickJiangShu/ConfigManager-Example/master/Poster/p4.jpg "") 27 |
28 | ### In Runtime: 29 | 1. Call Deserialize(); 30 | 2. Call Get() use them. 31 | ``` 32 | SerializableSet set = Resources.Load("SerializableSet"); 33 | Deserializer.Deserialize(set); 34 | 35 | /* 与加载解耦,不依赖加载方式 36 | AssetBundle bundle = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/config.ab"); 37 | set = bundle.LoadAsset("SerializableSet"); 38 | Deserializer.Deserialize(set); 39 | */ 40 | 41 | MonsterSheet monsterSheet = MonsterSheet.Get(210102) 42 | print(monsterSheet.name); 43 | ``` 44 | 45 | ![](https://raw.githubusercontent.com/RickJiangShu/ConfigManager-Example/master/Poster/p7.jpg "") 46 | 47 | ### Example: 48 | https://github.com/RickJiangShu/ConfigManager-Example 49 | 50 | -------------------------------------------------------------------------------- /README.en.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 59c8844f545a49f4d85430e754e7fade 3 | timeCreated: 1501040555 4 | licenseType: Pro 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Config Manager 2 | ConfigManager帮您一键生成配置对应的解析类,并将其序列化。
3 | 你只需轻点鼠标即可读取配置~
4 | 5 | *其他语言版本: [简体中文](README.md) [English](README.en.md)*
6 | ![logo](https://github.com/RickJiangShu/ConfigManager-Example/blob/master/Poster/Icon/Icon516x389.png "logo") 7 | 8 | # 本工具优势 9 | 1. **光速解析**:解析完全放在编辑模式下完成,运行时只需加载序列化文件即可。 10 | 2. **支持任意格式**:txt、csv、json、xml、xls、xlsx等等。 11 | 3. **无体积**:无运行时脚本,完全不占发行包体积。 12 | 4. **智能判断类型**:不需要配置人员懂类型概念,自动选取最优类型。 13 | 5. **防止出错**:"."操作符索引数据,编译器自动检查。 14 | 15 | # 快速使用 16 | ### 编辑配置: 17 | ![](https://raw.githubusercontent.com/RickJiangShu/ConfigManager-Example/master/Poster/p3.jpg "")
18 | [表格配置说明](https://github.com/RickJiangShu/ConfigManager-Example/blob/master/Doc/Sheet.md "表格配置说明")
19 | [Json配置说明](https://github.com/RickJiangShu/ConfigManager-Example/blob/master/Doc/Json.md "Json配置说明")
20 | [Xml配置说明](https://github.com/RickJiangShu/ConfigManager-Example/blob/master/Doc/Xml.md "Xml配置说明")
21 | 22 | ### 编辑器: 23 | 1. 点击菜单栏"Window/Config Manager"; 24 | 2. 设置对应的输入/输出路径; 25 | 3. 点击Output。 26 | 27 | ![](https://raw.githubusercontent.com/RickJiangShu/ConfigManager-Example/master/Poster/p4.jpg "") 28 |
29 | ### 运行时: 30 | 1. 调用反序列化接口; 31 | 2. 使用配置文件。 32 | ``` 33 | SerializableSet set = Resources.Load("SerializableSet"); 34 | Deserializer.Deserialize(set); 35 | 36 | /* 与加载解耦,不依赖加载方式 37 | AssetBundle bundle = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/config.ab"); 38 | set = bundle.LoadAsset("SerializableSet"); 39 | Deserializer.Deserialize(set); 40 | */ 41 | 42 | MonsterSheet monsterSheet = MonsterSheet.Get(210102) 43 | print(monsterSheet.name); 44 | ``` 45 | 46 | ![](https://raw.githubusercontent.com/RickJiangShu/ConfigManager-Example/master/Poster/p7.jpg "") 47 | 48 | ### 范例: 49 | https://github.com/RickJiangShu/ConfigManager-Example 50 | 51 | # 贡献者名单 52 | 如果你有任何Bug、问题和意见请在Issues或[蛮牛](http://www.manew.com/thread-105598-1-1.html "一键读取Txt、Excel等表格配置【源码+原理】")里提出来,有时间一定立马回复,意见一经采纳就被列入“贡献者名单”。 53 | 1. LiGo 提供希望支持csv的建议 54 | 2. k1104480005 提供希望支持Get所有数据的方法 55 | 3. fuliufuliu 希望直接解析xls和xlsx文件 56 | 4. zhengyiunity MAC电脑上解析时,注释部分中文乱码。 57 | 5. takaaptech 让我意识到不要覆盖AssetBundleName和提供打包回调接口 58 | 6. nijjkk 反馈在Mac上Excel输入中文会带拼音 59 | 7. Hazukiaoi 反馈Json解析问题 60 | 8. kaifeng0301 提供“按需加载”的需求(尚未支持) 61 | 9. 漂流小子 对于安全性的提醒 62 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5e7c264a2ce41c74098d752d41b8e840 3 | timeCreated: 1499696488 4 | licenseType: Pro 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | --------------------------------------------------------------------------------