diff --git a/ExcelTool/ExcelHelper.cs b/ExcelTool/ExcelHelper.cs index 8879b0d..6896c9a 100644 --- a/ExcelTool/ExcelHelper.cs +++ b/ExcelTool/ExcelHelper.cs @@ -147,7 +147,7 @@ namespace ExcelTool return result; } - static string GetCellValue(ICell cell) + internal static string GetCellValue(ICell cell) { if (cell == null) return ""; diff --git a/ExcelTool/Program.cs b/ExcelTool/Program.cs index d71e752..2c29c24 100644 --- a/ExcelTool/Program.cs +++ b/ExcelTool/Program.cs @@ -12,35 +12,16 @@ namespace ExcelTool { static async Task Main(string[] args) { - Option inputOption = new( - name: "--input", - description: "Excel 文件所在目录", - getDefaultValue: () => new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory) + Option tablesOption = new( + name: "--tables", + description: "__tables__.xlsx 的路径", + getDefaultValue: () => new FileInfo(AppDomain.CurrentDomain.BaseDirectory) ); - - Option outputCodeDirOption = new( - name: "--outputCodeDir", - description: "CS 模板代码输出目录" - ); - - Option outputDataDirOption = new( - name: "--outputDataDir", - description: "二进制数据输出目录" - ); - - Option namespaceOption = new( - name: "--namespace", - description: "生成代码的命名空间", - getDefaultValue: () => "" - ); RootCommand rootCommand = new(); - rootCommand.AddOption(inputOption); - rootCommand.AddOption(outputCodeDirOption); - rootCommand.AddOption(outputDataDirOption); - rootCommand.AddOption(namespaceOption); + rootCommand.AddOption(tablesOption); - rootCommand.SetHandler(ExcelProcess.Run, inputOption, outputCodeDirOption, outputDataDirOption, namespaceOption); + rootCommand.SetHandler(ExcelProcess.Run, tablesOption); // TODO 单元测试 return await rootCommand.InvokeAsync(args); @@ -49,18 +30,21 @@ namespace ExcelTool static class ExcelProcess { - public static void Run(DirectoryInfo inputDir, DirectoryInfo? outputCodeDir, DirectoryInfo? outputDataDir, - string nameSpace) + public static void Run(FileInfo tablesFile) { - string path = inputDir.FullName; - string codeDir = outputCodeDir?.FullName ?? outputDataDir?.FullName ?? path; - string dataDir = outputDataDir?.FullName ?? outputCodeDir?.FullName ?? path; - - DirectoryInfo dirInfo = new(path); - FileInfo[] excels = dirInfo.GetFiles("*.xlsx", SearchOption.AllDirectories); - if (excels.Length <= 0) + if(!tablesFile.Exists) { - "当前exe目录或者目标目录没有excels文件,请重新设置目录".WriteErrorLine(); + $"未在 {tablesFile} 处找到文件!".WriteErrorLine(); + return; + } + + + string path = tablesFile.FullName; + List tableEntries = TablesConfig.Parse(path); + + if (tableEntries.Count <= 0) + { + "__tables__.xlsx内没有数据".WriteErrorLine(); } else { @@ -69,55 +53,76 @@ namespace ExcelTool "== 说明:将exe放在xlsx目录中或者exe或者传入根目录 ==".WriteSuccessLine(); "==========================================================".WriteSuccessLine(); - excels = dirInfo.GetFiles("*.xlsx", SearchOption.AllDirectories); + // excels = dirInfo.GetFiles("*.xlsx", SearchOption.AllDirectories); //读取 - foreach (FileInfo file in excels) + foreach (TableEntry tableEntry in tableEntries) { - if (file.Name.StartsWith("~$")) continue; + if (!Path.Exists(tableEntry.InputFile)) + { + $"{tableEntry.InputFile} 不存在!".WriteWarningLine(); + continue; + } - List sheets = ExcelHelper.ParseAllSheets(file.FullName, out List? enumSheets); + if (!Path.Exists(tableEntry.OutputCodeDir)) + { + Directory.CreateDirectory(tableEntry.OutputCodeDir); + } + + if (!Path.Exists(tableEntry.OutputDataDir)) + { + Directory.CreateDirectory(tableEntry.OutputDataDir); + } + + string fileName = Path.GetFileName(tableEntry.InputFile); + + List sheets = ExcelHelper.ParseAllSheets(tableEntry.InputFile, out List? enumSheets); //生成CS文件 - bool res = GenModels.GenCSharpModel(sheets, codeDir, nameSpace); + bool res = GenModels.GenCSharpModel(sheets, tableEntry.OutputCodeDir, tableEntry.Namespace); if (res) { - $"{file.Name}CS模板生成成功".WriteSuccessLine(); + $"{fileName}CS模板生成成功".WriteSuccessLine(); } else { - $"{file.Name}CS模板生成失败".WriteErrorLine(); + $"{fileName}CS模板生成失败".WriteErrorLine(); } // 生成CS枚举文件 if (enumSheets.Count > 0) { - bool enumRes = GenEnums.GenCSharpEnum(enumSheets, codeDir, nameSpace); + bool enumRes = GenEnums.GenCSharpEnum(enumSheets, tableEntry.OutputCodeDir, tableEntry.Namespace); if (enumRes) - $"{file.Name} 枚举代码生成成功".WriteSuccessLine(); + $"{fileName} 枚举代码生成成功".WriteSuccessLine(); else - $"{file.Name} 枚举代码生成失败".WriteErrorLine(); + $"{fileName} 枚举代码生成失败".WriteErrorLine(); - bool enumIdsRes = GenEnums.GenEnumIds(enumSheets, codeDir, nameSpace); + bool enumIdsRes = GenEnums.GenEnumIds(enumSheets, tableEntry.OutputCodeDir, tableEntry.Namespace); if (enumIdsRes) - $"{file.Name} EnumIds 生成成功".WriteSuccessLine(); + $"{fileName} EnumIds 生成成功".WriteSuccessLine(); else - $"{file.Name} EnumIds 生成失败".WriteErrorLine(); + $"{fileName} EnumIds 生成失败".WriteErrorLine(); } //生成二进制文件,如果list或者vector数据为空则写入0,要根据类型来读取csv的字段数据强转成对应的数据类型然后写入 - res = TableExcelExportBytes.ExportToFile(sheets, dataDir); + res = TableExcelExportBytes.ExportToFile(sheets, tableEntry.OutputDataDir); if (res) { - $"{file.Name}二进制数据生成成功".WriteSuccessLine(); + $"{fileName}二进制数据生成成功".WriteSuccessLine(); } else { - $"{file.Name}二进制数据生成失败".WriteErrorLine(); + $"{fileName}二进制数据生成失败".WriteErrorLine(); } } } } + + public static void Test() + { + + } } } diff --git a/ExcelTool/TablesConfig.cs b/ExcelTool/TablesConfig.cs new file mode 100644 index 0000000..33df8b7 --- /dev/null +++ b/ExcelTool/TablesConfig.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.IO; +using NPOI.SS.UserModel; +using NPOI.XSSF.UserModel; + +namespace ExcelTool; + +public static class TablesConfig +{ + internal static List Parse(string tablesXlsxPath) + { + List tableEntries = []; + + string tablesDir = Path.GetDirectoryName(Path.GetFullPath(tablesXlsxPath))!; + + try + { + using FileStream fs = File.OpenRead(tablesXlsxPath); + XSSFWorkbook workbook = new(fs); + + ISheet sheet = workbook.GetSheetAt(0); + + for (int i = 4; i <= sheet.LastRowNum; i++) + { + IRow row = sheet.GetRow(i); + if (row == null || string.IsNullOrEmpty(ExcelHelper.GetCellValue(row.GetCell(0)))) continue; + + TableEntry tableEntry = new() + { + InputFile = ResolveDir(ExcelHelper.GetCellValue(row.GetCell(0))), + Namespace = ExcelHelper.GetCellValue(row.GetCell(1)), + OutputCodeDir = ResolveDir(ExcelHelper.GetCellValue(row.GetCell(2))), + OutputDataDir = ResolveDir(ExcelHelper.GetCellValue(row.GetCell(3))), + + }; + + tableEntries.Add(tableEntry); + } + + } + catch (Exception exception) + { + exception.ToString().WriteErrorLine(); + } + + return tableEntries; + + string ResolveDir(string raw) + { + if (raw != null) + { + return Path.IsPathRooted(raw) + ? raw + : Path.GetFullPath(Path.Combine(tablesDir, raw)); + } + return null; + } + } +} + +public class TableEntry +{ + public string InputFile { get; init; } // e.g. "AudioConfigs.xlsx" + public string Namespace { get; init; } // e.g. "OCES.Audio" + public string OutputCodeDir { get; init; } + public string OutputDataDir { get; init; } +} diff --git a/README.md b/README.md index 053f438..3afc286 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,6 @@ -# ExcelTool(Unity打表工具) - -通用Excel打表工具 - -#### 前言 - -目前项目中用的csv转json工具,会带来严重的gc性能问题,例如启动加载慢,影响帧率等问题,转成的json数据,我们需要手动写对应的解析模板代码,所以针对以上问题,本通用打表工具应运而生,用二进制数据替代json数据会比较好的解决性能问题,而且能够自动生成对应的模板代码,节省了写重复代码的时间,而且二进制数据比json读取速度更快,文件大小更小。 +# ExcelTool +本项目基于 https://github.com/dingxiaowei/ExcelTool 开发。用于 [AudioSystem](https://github.com/htw128/UnityAudioExtend), [HapticSystem](https://github.com/htw128/UnityAudioExtend#Haptic) 导出数据使用。 #### 功能 @@ -22,16 +17,9 @@ * 支持自定义字段 vector,例如[1,2,3] * 支持自定义字段 list,其实是vector数组,例如[[1,2,3],[2,3,4],[4,5,6]] -#### 效果 - -![](img/1.gif) - -![](img/2.jpg) -相比较json,二进制文件大小只有其1/4 - ##### 自动生成的模板代码 -``` +```c# /* * auto generated by tools(注意:千万不要手动修改本文件) * avatarguideTest @@ -197,7 +185,7 @@ public partial class avatarguideTestConfig : IBinarySerializable ##### 解析二进制文件 -``` +```c# IBinarySerializable newavList = new avatarguideTestList(); var readOK = FileManager.ReadBinaryDataFromFile(Path.Combine(path, "avatarguideTest.dat"), ref newavList); if (readOK) @@ -216,7 +204,7 @@ else 将二进制文件(后缀必须是.bytes)放在Resources目录,然后通过Resources.Load接口加载 -``` +```c# var bytes = Resources.Load("avatarguideTest"); if (bytes == null) { @@ -242,7 +230,7 @@ else #### 泛型读表 -``` +```c# T ReadTable(string tableName) where T : IBinarySerializable, new() { var bytes = LoadTextAsset(tableName); @@ -265,7 +253,7 @@ return default(T); ##### 查询数据 -``` +```c# var avatarguideTest = (avatarguideTestConfig)newavList; var obj = avatarguideTest.QueryById(2); if (obj != null && obj.Count() > 0) @@ -274,7 +262,7 @@ if (obj != null && obj.Count() > 0) #### 自动化拷贝批处理 -``` +```batch @echo off echo "开始拷贝jsons" @@ -292,21 +280,17 @@ pause 上面的config类采用的是partical class 就是为了方便扩展自定义的方法,如果想要添加自定义方法也定义一个partical class文件作为扩展即可 -#### 待扩展的功能 - -目前还不是最理想的状态,还有很多可以扩展完善的功能,如下: +#### TODO * [x] 支持枚举类型 -* [ ] 支持生成各种类型的文件,例如json、xml、proto、lua等 -* [ ] 支持生成各种语法的模板代码 +* [ ] 支持不同表不同namespace * [ ] 支持Excel数据配置规范性检测,例如手误配置不符合规范导致加载异常,例如大小写逗号(肉眼容易忽略),或者空格等等 -* [ ] 支持Excel字段客户端服务器可选项 + * [ ] ID不能重复 + * [ ] MusicContainer不能自引用 + * [ ] 同时播放的音乐BPM必须一致 +* [ ] 生成CueSheet避免magic number * [ ] 支持更多自定义数据类型扩展 -#### 代码 - -https://github.com/dingxiaowei/ExcelTool 喜欢麻烦点个star吧 - #### Unity客户端使用范例 采用的美术效果机型适配商业化解决方案Demo