DataTableProcessor.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. //------------------------------------------------------------
  2. // Game Framework
  3. // Copyright © 2013-2021 Jiang Yin. All rights reserved.
  4. // Homepage: https://gameframework.cn/
  5. // Feedback: mailto:ellan@gameframework.cn
  6. //------------------------------------------------------------
  7. using GameFramework;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.IO;
  11. using System.Linq;
  12. using System.Text;
  13. using UnityEngine;
  14. namespace MetaClient.Editor.DataTableTools
  15. {
  16. public sealed partial class DataTableProcessor
  17. {
  18. private const string CommentLineSeparator = "#";
  19. private static readonly char[] DataSplitSeparators = new char[] { '\t' };
  20. private static readonly char[] DataTrimSeparators = new char[] { '\"' };
  21. private readonly string[] m_NameRow;
  22. private readonly string[] m_TypeRow;
  23. private readonly string[] m_DefaultValueRow;
  24. private readonly string[] m_CommentRow;
  25. private readonly int m_ContentStartRow;
  26. private readonly int m_IdColumn;
  27. private readonly DataProcessor[] m_DataProcessor;
  28. private readonly string[][] m_RawValues;
  29. private readonly string[] m_Strings;
  30. private string m_CodeTemplate;
  31. private DataTableCodeGenerator m_CodeGenerator;
  32. public DataTableProcessor(string dataTableFileName, Encoding encoding, int nameRow, int typeRow, int? defaultValueRow, int? commentRow, int contentStartRow, int idColumn)
  33. {
  34. if (string.IsNullOrEmpty(dataTableFileName))
  35. {
  36. throw new GameFrameworkException("Data table file name is invalid.");
  37. }
  38. if (!dataTableFileName.EndsWith(".txt", StringComparison.Ordinal))
  39. {
  40. throw new GameFrameworkException(Utility.Text.Format("Data table file '{0}' is not a txt.", dataTableFileName));
  41. }
  42. if (!File.Exists(dataTableFileName))
  43. {
  44. throw new GameFrameworkException(Utility.Text.Format("Data table file '{0}' is not exist.", dataTableFileName));
  45. }
  46. string[] lines = File.ReadAllLines(dataTableFileName, encoding);
  47. int rawRowCount = lines.Length;
  48. int rawColumnCount = 0;
  49. List<string[]> rawValues = new List<string[]>();
  50. for (int i = 0; i < lines.Length; i++)
  51. {
  52. string[] rawValue = lines[i].Split(DataSplitSeparators);
  53. for (int j = 0; j < rawValue.Length; j++)
  54. {
  55. rawValue[j] = rawValue[j].Trim(DataTrimSeparators);
  56. }
  57. if (i == 0)
  58. {
  59. rawColumnCount = rawValue.Length;
  60. }
  61. else if (rawValue.Length != rawColumnCount)
  62. {
  63. throw new GameFrameworkException(Utility.Text.Format("Data table file '{0}', raw Column is '{2}', but line '{1}' column is '{3}'.", dataTableFileName, i, rawColumnCount, rawValue.Length));
  64. }
  65. rawValues.Add(rawValue);
  66. }
  67. m_RawValues = rawValues.ToArray();
  68. if (nameRow < 0)
  69. {
  70. throw new GameFrameworkException(Utility.Text.Format("Name row '{0}' is invalid.", nameRow));
  71. }
  72. if (typeRow < 0)
  73. {
  74. throw new GameFrameworkException(Utility.Text.Format("Type row '{0}' is invalid.", typeRow));
  75. }
  76. if (contentStartRow < 0)
  77. {
  78. throw new GameFrameworkException(Utility.Text.Format("Content start row '{0}' is invalid.", contentStartRow));
  79. }
  80. if (idColumn < 0)
  81. {
  82. throw new GameFrameworkException(Utility.Text.Format("Id column '{0}' is invalid.", idColumn));
  83. }
  84. if (nameRow >= rawRowCount)
  85. {
  86. throw new GameFrameworkException(Utility.Text.Format("Name row '{0}' >= raw row count '{1}' is not allow.", nameRow, rawRowCount));
  87. }
  88. if (typeRow >= rawRowCount)
  89. {
  90. throw new GameFrameworkException(Utility.Text.Format("Type row '{0}' >= raw row count '{1}' is not allow.", typeRow, rawRowCount));
  91. }
  92. if (defaultValueRow.HasValue && defaultValueRow.Value >= rawRowCount)
  93. {
  94. throw new GameFrameworkException(Utility.Text.Format("Default value row '{0}' >= raw row count '{1}' is not allow.", defaultValueRow.Value, rawRowCount));
  95. }
  96. if (commentRow.HasValue && commentRow.Value >= rawRowCount)
  97. {
  98. throw new GameFrameworkException(Utility.Text.Format("Comment row '{0}' >= raw row count '{1}' is not allow.", commentRow.Value, rawRowCount));
  99. }
  100. if (contentStartRow > rawRowCount)
  101. {
  102. throw new GameFrameworkException(Utility.Text.Format("Content start row '{0}' > raw row count '{1}' is not allow.", contentStartRow, rawRowCount));
  103. }
  104. if (idColumn >= rawColumnCount)
  105. {
  106. throw new GameFrameworkException(Utility.Text.Format("Id column '{0}' >= raw column count '{1}' is not allow.", idColumn, rawColumnCount));
  107. }
  108. m_NameRow = m_RawValues[nameRow];
  109. m_TypeRow = m_RawValues[typeRow];
  110. m_DefaultValueRow = defaultValueRow.HasValue ? m_RawValues[defaultValueRow.Value] : null;
  111. m_CommentRow = commentRow.HasValue ? m_RawValues[commentRow.Value] : null;
  112. m_ContentStartRow = contentStartRow;
  113. m_IdColumn = idColumn;
  114. m_DataProcessor = new DataProcessor[rawColumnCount];
  115. for (int i = 0; i < rawColumnCount; i++)
  116. {
  117. if (i == IdColumn)
  118. {
  119. m_DataProcessor[i] = DataProcessorUtility.GetDataProcessor("id");
  120. }
  121. else
  122. {
  123. m_DataProcessor[i] = DataProcessorUtility.GetDataProcessor(m_TypeRow[i]);
  124. }
  125. }
  126. Dictionary<string, int> strings = new Dictionary<string, int>(StringComparer.Ordinal);
  127. for (int i = contentStartRow; i < rawRowCount; i++)
  128. {
  129. if (IsCommentRow(i))
  130. {
  131. continue;
  132. }
  133. for (int j = 0; j < rawColumnCount; j++)
  134. {
  135. if (m_DataProcessor[j].LanguageKeyword != "string")
  136. {
  137. continue;
  138. }
  139. string str = m_RawValues[i][j];
  140. if (strings.ContainsKey(str))
  141. {
  142. strings[str]++;
  143. }
  144. else
  145. {
  146. strings[str] = 1;
  147. }
  148. }
  149. }
  150. m_Strings = strings.OrderBy(value => value.Key).OrderByDescending(value => value.Value).Select(value => value.Key).ToArray();
  151. m_CodeTemplate = null;
  152. m_CodeGenerator = null;
  153. }
  154. public int RawRowCount
  155. {
  156. get
  157. {
  158. return m_RawValues.Length;
  159. }
  160. }
  161. public int RawColumnCount
  162. {
  163. get
  164. {
  165. return m_RawValues.Length > 0 ? m_RawValues[0].Length : 0;
  166. }
  167. }
  168. public int StringCount
  169. {
  170. get
  171. {
  172. return m_Strings.Length;
  173. }
  174. }
  175. public int ContentStartRow
  176. {
  177. get
  178. {
  179. return m_ContentStartRow;
  180. }
  181. }
  182. public int IdColumn
  183. {
  184. get
  185. {
  186. return m_IdColumn;
  187. }
  188. }
  189. public bool IsIdColumn(int rawColumn)
  190. {
  191. if (rawColumn < 0 || rawColumn >= RawColumnCount)
  192. {
  193. throw new GameFrameworkException(Utility.Text.Format("Raw column '{0}' is out of range.", rawColumn));
  194. }
  195. return m_DataProcessor[rawColumn].IsId;
  196. }
  197. public bool IsCommentRow(int rawRow)
  198. {
  199. if (rawRow < 0 || rawRow >= RawRowCount)
  200. {
  201. throw new GameFrameworkException(Utility.Text.Format("Raw row '{0}' is out of range.", rawRow));
  202. }
  203. return GetValue(rawRow, 0).StartsWith(CommentLineSeparator, StringComparison.Ordinal);
  204. }
  205. public bool IsCommentColumn(int rawColumn)
  206. {
  207. if (rawColumn < 0 || rawColumn >= RawColumnCount)
  208. {
  209. throw new GameFrameworkException(Utility.Text.Format("Raw column '{0}' is out of range.", rawColumn));
  210. }
  211. return string.IsNullOrEmpty(GetName(rawColumn)) || m_DataProcessor[rawColumn].IsComment;
  212. }
  213. public string GetName(int rawColumn)
  214. {
  215. if (rawColumn < 0 || rawColumn >= RawColumnCount)
  216. {
  217. throw new GameFrameworkException(Utility.Text.Format("Raw column '{0}' is out of range.", rawColumn));
  218. }
  219. if (IsIdColumn(rawColumn))
  220. {
  221. return "Id";
  222. }
  223. return m_NameRow[rawColumn];
  224. }
  225. public bool IsSystem(int rawColumn)
  226. {
  227. if (rawColumn < 0 || rawColumn >= RawColumnCount)
  228. {
  229. throw new GameFrameworkException(Utility.Text.Format("Raw column '{0}' is out of range.", rawColumn));
  230. }
  231. return m_DataProcessor[rawColumn].IsSystem;
  232. }
  233. public System.Type GetType(int rawColumn)
  234. {
  235. if (rawColumn < 0 || rawColumn >= RawColumnCount)
  236. {
  237. throw new GameFrameworkException(Utility.Text.Format("Raw column '{0}' is out of range.", rawColumn));
  238. }
  239. return m_DataProcessor[rawColumn].Type;
  240. }
  241. public string GetLanguageKeyword(int rawColumn)
  242. {
  243. if (rawColumn < 0 || rawColumn >= RawColumnCount)
  244. {
  245. throw new GameFrameworkException(Utility.Text.Format("Raw column '{0}' is out of range.", rawColumn));
  246. }
  247. return m_DataProcessor[rawColumn].LanguageKeyword;
  248. }
  249. public string GetDefaultValue(int rawColumn)
  250. {
  251. if (rawColumn < 0 || rawColumn >= RawColumnCount)
  252. {
  253. throw new GameFrameworkException(Utility.Text.Format("Raw column '{0}' is out of range.", rawColumn));
  254. }
  255. return m_DefaultValueRow != null ? m_DefaultValueRow[rawColumn] : null;
  256. }
  257. public string GetComment(int rawColumn)
  258. {
  259. if (rawColumn < 0 || rawColumn >= RawColumnCount)
  260. {
  261. throw new GameFrameworkException(Utility.Text.Format("Raw column '{0}' is out of range.", rawColumn));
  262. }
  263. return m_CommentRow != null ? m_CommentRow[rawColumn] : null;
  264. }
  265. public string GetValue(int rawRow, int rawColumn)
  266. {
  267. if (rawRow < 0 || rawRow >= RawRowCount)
  268. {
  269. throw new GameFrameworkException(Utility.Text.Format("Raw row '{0}' is out of range.", rawRow));
  270. }
  271. if (rawColumn < 0 || rawColumn >= RawColumnCount)
  272. {
  273. throw new GameFrameworkException(Utility.Text.Format("Raw column '{0}' is out of range.", rawColumn));
  274. }
  275. return m_RawValues[rawRow][rawColumn];
  276. }
  277. public string GetString(int index)
  278. {
  279. if (index < 0 || index >= StringCount)
  280. {
  281. throw new GameFrameworkException(Utility.Text.Format("String index '{0}' is out of range.", index));
  282. }
  283. return m_Strings[index];
  284. }
  285. public int GetStringIndex(string str)
  286. {
  287. for (int i = 0; i < StringCount; i++)
  288. {
  289. if (m_Strings[i] == str)
  290. {
  291. return i;
  292. }
  293. }
  294. return -1;
  295. }
  296. public bool GenerateDataFile(string outputFileName)
  297. {
  298. if (string.IsNullOrEmpty(outputFileName))
  299. {
  300. throw new GameFrameworkException("Output file name is invalid.");
  301. }
  302. try
  303. {
  304. using (FileStream fileStream = new FileStream(outputFileName, FileMode.Create, FileAccess.Write))
  305. {
  306. using (BinaryWriter binaryWriter = new BinaryWriter(fileStream, Encoding.UTF8))
  307. {
  308. for (int rawRow = ContentStartRow; rawRow < RawRowCount; rawRow++)
  309. {
  310. if (IsCommentRow(rawRow))
  311. {
  312. continue;
  313. }
  314. byte[] bytes = GetRowBytes(outputFileName, rawRow);
  315. binaryWriter.Write7BitEncodedInt32(bytes.Length);
  316. binaryWriter.Write(bytes);
  317. }
  318. }
  319. }
  320. Debug.Log(Utility.Text.Format("Parse data table '{0}' success.", outputFileName));
  321. return true;
  322. }
  323. catch (Exception exception)
  324. {
  325. Debug.LogError(Utility.Text.Format("Parse data table '{0}' failure, exception is '{1}'.", outputFileName, exception));
  326. return false;
  327. }
  328. }
  329. public bool SetCodeTemplate(string codeTemplateFileName, Encoding encoding)
  330. {
  331. try
  332. {
  333. m_CodeTemplate = File.ReadAllText(codeTemplateFileName, encoding);
  334. Debug.Log(Utility.Text.Format("Set code template '{0}' success.", codeTemplateFileName));
  335. return true;
  336. }
  337. catch (Exception exception)
  338. {
  339. Debug.LogError(Utility.Text.Format("Set code template '{0}' failure, exception is '{1}'.", codeTemplateFileName, exception));
  340. return false;
  341. }
  342. }
  343. public void SetCodeGenerator(DataTableCodeGenerator codeGenerator)
  344. {
  345. m_CodeGenerator = codeGenerator;
  346. }
  347. public bool GenerateCodeFile(string outputFileName, Encoding encoding, object userData = null)
  348. {
  349. if (string.IsNullOrEmpty(m_CodeTemplate))
  350. {
  351. throw new GameFrameworkException("You must set code template first.");
  352. }
  353. if (string.IsNullOrEmpty(outputFileName))
  354. {
  355. throw new GameFrameworkException("Output file name is invalid.");
  356. }
  357. try
  358. {
  359. StringBuilder stringBuilder = new StringBuilder(m_CodeTemplate);
  360. if (m_CodeGenerator != null)
  361. {
  362. m_CodeGenerator(this, stringBuilder, userData);
  363. }
  364. using (FileStream fileStream = new FileStream(outputFileName, FileMode.Create, FileAccess.Write))
  365. {
  366. using (StreamWriter stream = new StreamWriter(fileStream, encoding))
  367. {
  368. stream.Write(stringBuilder.ToString());
  369. }
  370. }
  371. Debug.Log(Utility.Text.Format("Generate code file '{0}' success.", outputFileName));
  372. return true;
  373. }
  374. catch (Exception exception)
  375. {
  376. Debug.LogError(Utility.Text.Format("Generate code file '{0}' failure, exception is '{1}'.", outputFileName, exception));
  377. return false;
  378. }
  379. }
  380. private byte[] GetRowBytes(string outputFileName, int rawRow)
  381. {
  382. using (MemoryStream memoryStream = new MemoryStream())
  383. {
  384. using (BinaryWriter binaryWriter = new BinaryWriter(memoryStream, Encoding.UTF8))
  385. {
  386. for (int rawColumn = 0; rawColumn < RawColumnCount; rawColumn++)
  387. {
  388. if (IsCommentColumn(rawColumn))
  389. {
  390. continue;
  391. }
  392. try
  393. {
  394. m_DataProcessor[rawColumn].WriteToStream(this, binaryWriter, GetValue(rawRow, rawColumn));
  395. }
  396. catch
  397. {
  398. if (m_DataProcessor[rawColumn].IsId || string.IsNullOrEmpty(GetDefaultValue(rawColumn)))
  399. {
  400. Debug.LogError(Utility.Text.Format("Parse raw value failure. OutputFileName='{0}' RawRow='{1}' RowColumn='{2}' Name='{3}' Type='{4}' RawValue='{5}'", outputFileName, rawRow, rawColumn, GetName(rawColumn), GetLanguageKeyword(rawColumn), GetValue(rawRow, rawColumn)));
  401. return null;
  402. }
  403. else
  404. {
  405. Debug.LogWarning(Utility.Text.Format("Parse raw value failure, will try default value. OutputFileName='{0}' RawRow='{1}' RowColumn='{2}' Name='{3}' Type='{4}' RawValue='{5}'", outputFileName, rawRow, rawColumn, GetName(rawColumn), GetLanguageKeyword(rawColumn), GetValue(rawRow, rawColumn)));
  406. try
  407. {
  408. m_DataProcessor[rawColumn].WriteToStream(this, binaryWriter, GetDefaultValue(rawColumn));
  409. }
  410. catch
  411. {
  412. Debug.LogError(Utility.Text.Format("Parse default value failure. OutputFileName='{0}' RawRow='{1}' RowColumn='{2}' Name='{3}' Type='{4}' RawValue='{5}'", outputFileName, rawRow, rawColumn, GetName(rawColumn), GetLanguageKeyword(rawColumn), GetComment(rawColumn)));
  413. return null;
  414. }
  415. }
  416. }
  417. }
  418. return memoryStream.ToArray();
  419. }
  420. }
  421. }
  422. }
  423. }