2023-04-03 11:04:31 +08:00

655 lines
16 KiB
C#

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<ProtoField> fields = new List<ProtoField>();
}
public class ProtoStruct
{
public string typeName = "";
public List<ProtoField> fields = new List<ProtoField>();
}
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<ProtoEnum> protoEnums = new List<ProtoEnum>();
static List<ProtoModule> protoModules = new List<ProtoModule>();
static List<ProtoMessage> protoMessages = new List<ProtoMessage>();
static List<ProtoStruct> protoStructs = new List<ProtoStruct>();
static List<int> moduleIds = new List<int>();
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("<I><B>Auto Generated By: </B></I>");
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("<I><B>文档分为四部分:</B></I>\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("<br>");
sw.WriteLine("<br>");
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("<br>");
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 "<a name = \"" + name + "\" />";
}
static void DrawTopBtn(StreamWriter sw)
{
sw.WriteLine("<p align=\"right\"><a href=\"#top\"><I><U>回到顶部</U></I></a></p>");
}
}
}