using System.Linq; using System.Text; using System.Collections.Generic; using System; using System.IO; using BF; namespace BFEditor { public class ProtoEnum { public string enumName = ""; public int enumNum = 0; public int moduleId = 0; } public class ProtoModule { public string moduleDesc = ""; public int moduleNum = 0; } public class ProtoMessage { public string commentStr = ""; public string messageName = ""; public List fields = new List(); } public class ProtoStruct { public string typeName = ""; public List fields = new List(); } public class ProtoField { public string commentStr = ""; public string fieldName = ""; public string typeName = ""; public bool isRepeated = false; public bool isMap = false; public string keyTypeName = ""; public string valueTypeName = ""; } public class Proto2MarkDown { const string COMMENT_CONST = "comment = "; const string MESSAGE_NAME_CONST = "message_name = "; const string MARKDOWN_FILE_NAME = "/bf_proto.md"; static string[] TYPE_KEYWORDS = new string[] { "uint32", "int32", "uint64", "int64", "string", "bool" }; static string MarkdownPath { get { return LocalData.GetProtoPath(); } } static string MsgIdsProtoPath { get { return LocalData.GetProtoPath() + "/bf_msgids.proto"; } } static string CommonProtoPath { get { return LocalData.GetProtoPath() + "/bf_comm.proto"; } } static string LogicProtoPath { get { return LocalData.GetProtoPath() + "/bf_logic.proto"; } } static string BattleProtoPath { get { return LocalData.GetProtoPath() + "/bf_battle.proto"; } } static string protoMessageCommentStr = COMMENT_CONST; static string protoStructTypeNameStr = ""; static StringBuilder sb = new StringBuilder(); static bool readingProtoEnum = false; static bool readingProtoMessage = false; static bool readingProtoStruct = false; static List protoEnums = new List(); static List protoModules = new List(); static List protoMessages = new List(); static List protoStructs = new List(); static List moduleIds = new List(); public static bool Pb2Markdown() { protoEnums.Clear(); protoModules.Clear(); protoMessages.Clear(); protoStructs.Clear(); moduleIds.Clear(); if (string.IsNullOrEmpty(MsgIdsProtoPath) || string.IsNullOrEmpty(CommonProtoPath) || string.IsNullOrEmpty(LogicProtoPath) || string.IsNullOrEmpty(MarkdownPath) || string.IsNullOrEmpty(BattleProtoPath)) { return false; } ParsingEnum(); ParsingLogic(); ParsingCommon(); ParsingBattle(); SortEnum(); Export2Markdown(); return true; } static void ParsingEnum() { var reader = new StreamReader(new FileStream(MsgIdsProtoPath, FileMode.Open)); var logicLineStr = ""; while ((logicLineStr = reader.ReadLine()) != null) { if (logicLineStr == "") { continue; } if (logicLineStr == "enum MsgId") { readingProtoEnum = true; continue; } if (readingProtoEnum) { if (logicLineStr == "{") { continue; } if (logicLineStr == "}") { readingProtoEnum = false; continue; } ReadProtoEnum(logicLineStr); } } reader.Close(); reader.Dispose(); } static void ParsingLogic() { var reader = new StreamReader(new FileStream(LogicProtoPath, FileMode.Open)); var logicLineStr = ""; while ((logicLineStr = reader.ReadLine()) != null) { if (logicLineStr == "") { continue; } if (logicLineStr.Replace(" ", "").Replace("\t", "").StartsWith("//") && logicLineStr.Contains("========")) { ReadProtoModule(logicLineStr); continue; } if (logicLineStr.Replace(" ", "").Replace("\t", "").StartsWith("//")) { protoMessageCommentStr = protoMessageCommentStr + logicLineStr.Replace("//", " "); continue; } if (logicLineStr.Replace(" ", "").Replace("\t", "").StartsWith("message")) { readingProtoMessage = true; sb.AppendLine(protoMessageCommentStr); protoMessageCommentStr = COMMENT_CONST; string messageName = MESSAGE_NAME_CONST + logicLineStr.Replace(" ", "").Replace("\t", "").Replace("message", "").Replace("{", ""); sb.AppendLine(messageName); continue; } if (readingProtoMessage) { if (logicLineStr.Replace(" ", "").Replace("\t", "") == "}") { ReadProtoMessage(sb); readingProtoMessage = false; } else { sb.AppendLine(logicLineStr); } continue; } } reader.Close(); reader.Dispose(); } static void ReadProtoEnum(string enumStr) { enumStr = enumStr.Replace(" ", "").Replace("\t", ""); enumStr = enumStr.Replace(";", ""); var index = enumStr.IndexOf("//"); if (index > 0) { enumStr = enumStr.Remove(index, enumStr.Length - index); } var equalIndex = enumStr.IndexOf("="); var name = enumStr.Substring(0, equalIndex); var num = Int32.Parse(enumStr.Substring(equalIndex + 1, enumStr.Length - equalIndex - 1)); var pe = new ProtoEnum(); pe.enumName = name; pe.enumNum = num; var moduleKey = (num - (num % 1000)) / 1000; pe.moduleId = moduleKey; protoEnums.Add(pe); } static void ReadProtoModule(string moduleStr) { moduleStr = moduleStr.Replace(" ", "").Replace("\t", ""); moduleStr = moduleStr.Replace("=", ""); moduleStr = moduleStr.Replace("//", ""); var pm = new ProtoModule(); pm.moduleDesc = moduleStr; var num = Int32.Parse(System.Text.RegularExpressions.Regex.Replace(moduleStr, @"[^0-9]+", "")); pm.moduleNum = num; moduleIds.Add(num); protoModules.Add(pm); } static void ReadProtoMessage(StringBuilder sb) { var message = sb.ToString(); var lines = message.Split(new char[] { '\n' }); var pm = new ProtoMessage(); foreach (var line in lines) { if (line == "") { continue; } if (line.Contains(COMMENT_CONST)) { pm.commentStr = line.Replace(COMMENT_CONST, ""); continue; } if (line.Contains(MESSAGE_NAME_CONST)) { pm.messageName = line.Replace(MESSAGE_NAME_CONST, ""); continue; } var pf = GetProtoField(line); pm.fields.Add(pf); } protoMessages.Add(pm); sb.Clear(); } static void SortEnum() { protoEnums.Sort((x, y) => { return x.enumNum - y.enumNum; }); } static void ParsingCommon() { var reader = new StreamReader(new FileStream(CommonProtoPath, FileMode.Open)); var commonLineStr = ""; while ((commonLineStr = reader.ReadLine()) != null) { if (commonLineStr == "") { continue; } if (commonLineStr.Replace(" ", "").Replace("\t", "").StartsWith("message")) { protoStructTypeNameStr = commonLineStr.Replace(" ", "").Replace("\t", "").Replace("message", "").Replace("{", ""); readingProtoStruct = true; continue; } if (readingProtoStruct) { if (commonLineStr.Replace(" ", "").Replace("\t", "") == "}") { ReadProtoStruct(sb, protoStructTypeNameStr); protoStructTypeNameStr = ""; readingProtoStruct = false; } else { sb.AppendLine(commonLineStr); } continue; } } reader.Close(); reader.Dispose(); } static void ParsingBattle() { var reader = new StreamReader(new FileStream(BattleProtoPath, FileMode.Open)); var lineStr = ""; while ((lineStr = reader.ReadLine()) != null) { if (lineStr == "") { continue; } if (lineStr.Replace(" ", "").Replace("\t", "").StartsWith("message")) { protoStructTypeNameStr = lineStr.Replace(" ", "").Replace("\t", "").Replace("message", "").Replace("{", ""); readingProtoStruct = true; continue; } if (readingProtoStruct) { if (lineStr.Replace(" ", "").Replace("\t", "") == "}") { ReadProtoStruct(sb, protoStructTypeNameStr); protoStructTypeNameStr = ""; readingProtoStruct = false; } else { sb.AppendLine(lineStr); } continue; } } reader.Close(); reader.Dispose(); } static void ReadProtoStruct(StringBuilder sb, string structName) { var ps = new ProtoStruct(); ps.typeName = structName; var message = sb.ToString(); var lines = message.Split(new char[] { '\n' }); foreach (var line in lines) { if (line == "") { continue; } ProtoField pf = GetProtoField(line); ps.fields.Add(pf); } sb.Clear(); protoStructs.Add(ps); } static ProtoField GetProtoField(string fieldLine) { var pf = new ProtoField(); // 解析filed if (fieldLine.Replace(" ", "").Replace("\t", "").StartsWith("repeated")) { pf.isRepeated = true; } if (fieldLine.Contains("//")) { var index1 = fieldLine.IndexOf("//"); var comment = fieldLine.Substring(index1, fieldLine.Length - index1); pf.commentStr = comment; } var index2 = fieldLine.IndexOf("="); var fieldStr = fieldLine.Remove(index2).Replace("repeated", ""); if (fieldStr.Replace(" ", "").Replace("\t", "").StartsWith("map<")) // map结构 { pf.isMap = true; var index3 = fieldStr.IndexOf(">") + 1; var fieldName = fieldStr.Substring(index3, fieldStr.Length - index3).Replace(" ", "").Replace("\t", ""); pf.fieldName = fieldName; var keyValueStr = fieldStr.Replace("map<", "").Replace(">", "").Replace(fieldName, "").Replace(" ", "").Replace("\t", ""); var index4 = keyValueStr.IndexOf(","); var keyName = keyValueStr.Substring(0, index4); pf.keyTypeName = keyName; var vauleName = keyValueStr.Substring(index4 + 1, keyValueStr.Length - index4 - 1); pf.valueTypeName = vauleName.Replace(" ", "").Replace("\t", ""); } else { while (fieldStr.Contains(" ")) // 这个处理不好 { fieldStr = fieldStr.Replace(" ", " "); } var typeFiledNameStr = fieldStr.Split(new char[] { ' ' }); foreach (var item in typeFiledNameStr) { if (item == "") { continue; } if (pf.typeName == "") { pf.typeName = item.Replace(" ", "").Replace("\t", ""); continue; } else { pf.fieldName = item; break; } } } return pf; } static void Export2Markdown() { var sw = new StreamWriter(MarkdownPath + MARKDOWN_FILE_NAME, false); WriteTitle(sw); WriteProtoModule(sw); WriteProtoEnum(sw); WriteProtoMessage(sw); WriteProtoStructs(sw); sw.Close(); sw.Dispose(); } static void WriteTitle(StreamWriter sw) { sw.WriteLine("# *BF协议文档*"); sw.WriteLine(" "); sw.WriteLine("Auto Generated By: "); sw.WriteLine(" "); sw.WriteLine("bf_logic.proto (协议描述文件)\n"); sw.WriteLine("bf_comm.proto、bf_battle.proto (结构体描述文件)\n"); sw.WriteLine("bf_msgids.proto(协议枚举描述文件)\n"); sw.WriteLine(" "); sw.WriteLine("文档分为四部分:\n"); sw.WriteLine("[功能模块](#一)、[协议枚举](#二)、[消息内容](#三)、[自定义结构体](#四)"); sw.WriteLine(" "); sw.WriteLine("req: 前端请求, rsp: 服务器应答"); } static void WriteProtoModule(StreamWriter sw) { sw.WriteLine(" "); sw.WriteLine("## " + GetHtmlLink("top") + GetHtmlLink("一") + "第一部分 模块"); foreach (var item in protoModules) { sw.WriteLine(" - [" + "模块id: " + item.moduleNum + " // " + item.moduleDesc + "]" + "(#" + item.moduleNum + ")"); } } static void WriteProtoEnum(StreamWriter sw) { sw.WriteLine(" "); sw.WriteLine("## " + GetHtmlLink("二") + "第二部分 ProtoEnum"); var moduleId = protoEnums[0].moduleId; sw.WriteLine("### " + GetHtmlLink(moduleId.ToString()) + "模块" + moduleId + " // " + GetModuleDesc(moduleId)); foreach (var item in protoEnums) { if (item.moduleId != moduleId) { moduleId = item.moduleId; sw.WriteLine("### " + GetHtmlLink(moduleId.ToString()) + "模块" + moduleId + " // " + GetModuleDesc(moduleId)); } sw.WriteLine("> - [" + item.enumName + " = " + item.enumNum + "]" + "(#" + item.enumName + ")"); } } static string GetModuleDesc(int key) { foreach (var item in protoModules) { if (item.moduleNum == key) { return item.moduleDesc; } } return ""; } static void WriteProtoMessage(StreamWriter sw) { sw.WriteLine(" "); sw.WriteLine("## " + GetHtmlLink("三") + "第三部分 ProtoMessage"); foreach (var item in protoEnums) { var rspPM = GetRspMessage(item.enumName); var reqPM = GetReqMessage(item.enumName); if (rspPM == null && reqPM == null) { continue; } string comment = ""; if (reqPM != null && reqPM.commentStr != "") { comment = reqPM.commentStr; } else { comment = rspPM.commentStr; } sw.WriteLine("## " + "__" + GetHtmlLink(item.enumName) + "消息 " + item.enumName + " //" + comment + "__"); WriteProtoMessage(sw, reqPM, "req : "); sw.WriteLine(" "); WriteProtoMessage(sw, rspPM, "rsp : "); sw.WriteLine(" "); DrawTopBtn(sw); sw.WriteLine("
"); sw.WriteLine("
"); sw.WriteLine(" "); } } static ProtoMessage GetRspMessage(string enumName) { const string RSP = "rsp"; foreach (var item in protoMessages) { if (item.messageName == RSP + enumName) { return item; } } return null; } static ProtoMessage GetReqMessage(string enumName) { const string REQ = "req"; foreach (var item in protoMessages) { if (item.messageName == REQ + enumName) { return item; } } return null; } static void WriteProtoMessage(StreamWriter sw, ProtoMessage pm, string title) { if (pm == null) { return; } sw.WriteLine("### " + title); sw.WriteLine(" "); WriteTableTitle(sw); foreach (var item in pm.fields) { WriteField(sw, item); } } static void WriteTableTitle(StreamWriter sw) { sw.WriteLine("| repeated | 类型 | 字段名 | 注释 |"); sw.WriteLine("| :------------ | :------------ | :------------ | :------------ |"); } static void WriteField(StreamWriter sw, ProtoField pf) { var str = "| "; if (pf.isRepeated) { str = str + "repeated |"; } else { str = str + " |"; } if (pf.isMap) { str = str + "map< " + GetTypeMarkdownStr(pf.keyTypeName) + " , " + GetTypeMarkdownStr(pf.valueTypeName) + "> |"; } else { str = str + GetTypeMarkdownStr(pf.typeName) + " |"; } str = str + pf.fieldName + " |"; str = str + pf.commentStr + " |"; sw.WriteLine(str); } static string GetTypeMarkdownStr(string typeName) { if (TYPE_KEYWORDS.Contains(typeName)) { return typeName; } else { return "[" + typeName + "]" + "(#TYPE_" + typeName + ")"; } } static void WriteProtoStructs(StreamWriter sw) { sw.WriteLine(" "); sw.WriteLine("## " + GetHtmlLink("四") + "第四部分 结构体"); foreach (var item in protoStructs) { WriteStruct(sw, item); sw.WriteLine(" "); DrawTopBtn(sw); sw.WriteLine("
"); sw.WriteLine(" "); } } static void WriteStruct(StreamWriter sw, ProtoStruct ps) { sw.WriteLine("### " + "__" + GetHtmlLink("TYPE_" + ps.typeName) + "结构体 " + ps.typeName + "__"); WriteTableTitle(sw); foreach (var item in ps.fields) { WriteField(sw, item); } } static string GetHtmlLink(string name) { return ""; } static void DrawTopBtn(StreamWriter sw) { sw.WriteLine("

回到顶部

"); } } }