public string GenerateCode(BinaryFileSchema schema) { StringBuilder b = new StringBuilder(); List<CodeClass> codeClasses = new List<CodeClass>(); FormatName = schema.FormatBlock.Name; b.AppendLine("using System;"); b.AppendLine("using System.Collections.Generic;"); b.AppendLine("using System.IO;"); b.AppendLine("using System.Text;"); b.AppendLine("namespace " + schema.FormatBlock.Name); b.AppendLine("{"); CodeClass mainClass = new CodeClass(schema.FormatBlock.Name + "Parser"); codeClasses.Add(mainClass); rootParser = mainClass; CodeMethod mainMethod = new CodeMethod("void Main(string[] args)"); mainMethod.CodeLines.Add("try{"); mainMethod.CodeLines.Add("\t" + schema.FormatBlock.Name + " ps = " + schema.FormatBlock.Name + "Parser.ReadFile(args[0]);"); mainMethod.CodeLines.Add("\tConsole.WriteLine(ps.ToString());"); mainMethod.CodeLines.Add("}"); mainMethod.CodeLines.Add("catch(Exception e){"); mainMethod.CodeLines.Add("\tConsole.WriteLine(e.Message);"); mainMethod.CodeLines.Add("\tConsole.WriteLine(e.StackTrace);"); mainMethod.CodeLines.Add("}"); mainClass.CodeMethods.Add(mainMethod); CodeMethod readFile = new CodeMethod( schema.FormatBlock.Name + " ReadFile(string filename)"); mainClass.CodeMethods.Add(readFile); readFile.CodeLines.Add("FileStream fstream = new FileStream(filename, FileMode.Open);"); readFile.CodeLines.Add("BinaryReader reader = new BinaryReader(fstream);"); string endianness = ((schema.ByteOrder.ByteOrder == BfsByteOrderEnum.BigEndian) ? BfsByteOrderEnum.BigEndian : BfsByteOrderEnum.LittleEndian).ToString(); readFile.CodeLines.Add("BfsBinaryReader bfsReader = new BfsBinaryReader(reader, BfsBinaryReader.Endianness." + endianness + ");"); readFile.CodeLines.Add("return " + schema.FormatBlock.Name + ".Read(bfsReader);"); foreach (IBfsDataBlock dataBlock in schema.DatablockList) { CodeClass newClass = MakeClass(dataBlock); if(newClass != null) codeClasses.Add(newClass); } //Output the classes foreach (CodeClass cc in codeClasses) b.Append(cc.ToString()); b.AppendLine("}"); return b.ToString(); }
public void MakeBitfield(CodeClass codeClass, BfsBitfield dataField) { codeClass.ClassType = CodeClass.ClassTypeEnum.Struct; //Adds the implicit 'value' variable for storing the read primitive type. codeClass.CodeFields.Add(CSHelper.TypeMap(dataField.PrimitiveType).ToLower() + " value;"); string objectType = dataField.Name; string varName = dataField.Name.ToLower(); StringBuilder b = new StringBuilder("bool "); for ( int i = 0; i< dataField.BitFieldFields.Count; i++) { if (i != 0) b.Append(", "); BfsBitfieldField field = dataField.BitFieldFields[i]; if (field.Name == null) b.Append("bit" + field.BitNumber); else b.Append(field.Name); } b.Append(";"); codeClass.CodeFields.Add(b.ToString()); CodeMethod codeMethod = new CodeMethod(objectType + " Read(BfsBinaryReader file)"); codeClass.CodeMethods.Add(codeMethod); codeMethod.CodeLines.Add(objectType + " " + varName + " = new " + objectType + "();"); codeMethod.CodeLines.Add(varName + ".value = file.Read" + dataField.PrimitiveType.PrimitiveType.ToString() + "();"); foreach( BfsBitfieldField field in dataField.BitFieldFields ) { int size = Environments.GetSizeOfPrimitiveType(dataField.PrimitiveType); string mask = MakeMask(field.BitNumber, size); string bitName = ((field.Name != null) ? field.Name : ("bit" + field.BitNumber)); if (field.Actions.Count > 0) { IfBlock block = new IfBlock("(" + varName + ".value & " + mask + ") == 1"); block.CodeLines.Add(varName + "." + bitName + " = true;"); MakeActionList(dataField, block.CodeLines, field.Actions); codeMethod.AddSplitLines(block.ToString()); } else codeMethod.CodeLines.Add(varName + "." + bitName + " = " + "(" + varName + ".value & " + mask + ") == 1;"); } codeMethod.CodeLines.Add("return " + varName + ";"); }
public void MakeStruct(CodeClass codeClass, BfsStruct dataStruct) { foreach (BfsStructField structField in dataStruct.StructFieldList) { string type = ""; string name = structField.Name; if (structField.FieldType is BfsNamedType) { BfsNamedType namedType = structField.FieldType as BfsNamedType; type = namedType.DataBlock.Name; } else if (structField.FieldType is BfsPrimitiveType) { BfsPrimitiveType primitiveType = structField.FieldType as BfsPrimitiveType; type = CSHelper.TypeMap(primitiveType).ToLower(); } else if (structField.FieldType is BfsFunctionType) { BfsFunctionType functionType = structField.FieldType as BfsFunctionType; if (functionType.FunctionName == "ascii") type = "string"; else BfsCompiler.ReportError(functionType.SourceRange, "Only ASCII strings are supported so far..!"); } if (structField.FieldType.ArrayExtension == null) codeClass.CodeFields.Add(type + " " + name + ";"); else { if (structField.FieldType.ArrayExtension is BfsKnownArray) codeClass.CodeFields.Add(type + " [] " + name + ";"); else if (structField.FieldType.ArrayExtension is BfsUnknownArray) codeClass.CodeFields.Add("List<" + type + "> " + name + ";"); else BfsCompiler.ReportError(structField.FieldType.ArrayExtension.SourceRange, "Unknown array extended type. Known or unknown array type expected!"); } } CodeMethod read = new CodeMethod(dataStruct.Name + " Read(BfsBinaryReader file)"); codeClass.CodeMethods.Add(read); MakeReadStruct(dataStruct, read); //ToString() method CodeMethod toString = new CodeMethod("override string ToString()"); codeClass.CodeMethods.Add(toString); toString.CodeLines.Add("StringBuilder sb = new StringBuilder();"); toString.CodeLines.Add("sb.AppendLine(\"== Struct: "+ dataStruct.Name +" ==\");"); foreach (BfsStructField field in dataStruct.StructFieldList) toString.CodeLines.Add("sb.AppendLine(\"" + field.Name + " : \" + " + field.ToString() + ".ToString());"); toString.CodeLines.Add("return sb.ToString();"); }
public void MakeReadStruct(BfsStruct dataStruct, CodeMethod codeMethod) { //Wrap the binary reader class into a compression stream if nessecary if (dataStruct.CompressionMethod != null) { if(dataStruct.CompressionMethod == "GZip" || dataStruct.CompressionMethod == "Deflate") { codeMethod.CodeLines.Add( dataStruct.CompressionMethod+"Stream bfs_decompress = new "+ dataStruct.CompressionMethod +"Stream(file.BaseStream, CompressionMode.Decompress);"); codeMethod.CodeLines.Add("file = new BfsBinaryReader(new BinaryReader(bfs_decompress), file.FileEndianness);"); } else BfsCompiler.ReportError("Does not know the compression mode '" + dataStruct.CompressionMethod + "'"); } //The class to return codeMethod.CodeLines.Add( dataStruct.Name + " " + dataStruct.Name.ToLower() + " = new " + dataStruct.Name + "();"); foreach (IBfsNamedField namedField in dataStruct.ParseOrder) { if (namedField is BfsStructField) { BfsStructField field = namedField as BfsStructField; if (field.Conditional != null) codeMethod.CodeLines.Add("if (" + CSHelper.MakeExpression(field.Conditional, dataStruct) + ")"); if (field.FieldType.ArrayExtension == null) codeMethod.CodeLines.Add(((field.Conditional != null) ? "\t" : "") + dataStruct.Name.ToLower() + "." + field.Name + " = " + ReadType(field.FieldType)+ ";"); else if (field.FieldType.ArrayExtension is BfsKnownArray) { BfsKnownArray knownArray = field.FieldType.ArrayExtension as BfsKnownArray; codeMethod.CodeLines.Add(dataStruct.Name.ToLower() + "." + field.Name + " = new " + CSHelper.TypeMap(field.FieldType).ToLower() + "[" + CSHelper.MakeExpression(knownArray.Expression, dataStruct) + "];"); codeMethod.CodeLines.Add("for ( int i=0; i<" + CSHelper.MakeExpression(knownArray.Expression, dataStruct) + "; i++)"); codeMethod.CodeLines.Add("\t" + dataStruct.Name.ToLower() + "." + field.Name + "[i] = " + ReadType(field.FieldType) + ";"); } else if (field.FieldType.ArrayExtension is BfsUnknownArray) { BfsUnknownArray unknownArray = field.FieldType.ArrayExtension as BfsUnknownArray; //Make temporary copy of the list to convert all ascii strings into hex strings. List<IBfsStopCase> stopcases = new List<IBfsStopCase>(unknownArray.StopCases); bool stopAtEOF = false; foreach (IBfsStopCase sc in stopcases) if (sc is BfsStopCaseEndOfFile) stopAtEOF = true; string type = CSHelper.TypeMap(field.FieldType).ToLower(); codeMethod.CodeLines.Add(dataStruct.Name.ToLower() + "." + field.Name + " = new List<" + type + ">();"); codeMethod.CodeLines.Add("StopCaseTester " + field.Name + "_tester = new StopCaseTester(file, "+stopAtEOF.ToString().ToLower()+", new StopCase[] {"); bool hasInclude = false, hasSkipped = false; bool onlyIncluded = true, onlySkipped = true; for (int i = 0; i < stopcases.Count; i++) { byte[] stopcase = new byte [1]; if (stopcases[i] is BfsStopCaseHex) stopcase = ByteArrayConverter.ConvertHexString((stopcases[i] as BfsStopCaseHex).HexString); else if(stopcases[i] is BfsStopCaseString) stopcase = ByteArrayConverter.ConvertString((stopcases[i] as BfsStopCaseString).StopString); StringBuilder builder = new StringBuilder("\tnew StopCase(new byte [] { "); for (int x = 0; x < stopcase.Length; x++) { builder.Append("0x" + stopcase[x].ToString("X")); if (x < stopcase.Length - 1) builder.Append(", "); } builder.Append(" }, "); BfsInclusionEnum inclusion = stopcases[i].Inclusion; bool included = (inclusion == BfsInclusionEnum.Included); bool skipped = (inclusion == BfsInclusionEnum.Skipped); builder.Append((included ? "true" : "false") + ", "); //If things are consumed, they are automatically considered consumed so this is not needed. builder.Append(((skipped && !included) ? "true" : "false") + ")"); if (stopcases[i].Inclusion == BfsInclusionEnum.Included) hasInclude = true; else onlyIncluded = false; if (stopcases[i].Inclusion == BfsInclusionEnum.Skipped) hasSkipped = true; else onlySkipped = false; if (i < stopcases.Count - 1) builder.Append(","); codeMethod.CodeLines.Add(builder.ToString()); } codeMethod.CodeLines.Add("});"); codeMethod.CodeLines.Add("while("+field.Name + "_tester.CanContinue())"); codeMethod.CodeLines.Add("\t" + dataStruct.Name.ToLower() + "." + field.Name + ".Add(" + ReadType(field.FieldType) + ");"); string indent = string.Empty; if (hasInclude) { //The 'if'-test on the inclusion stopcase is only needed if not all cases are included if (!onlyIncluded) { codeMethod.CodeLines.Add("if(" + field.Name + "_tester.StoppedAtCase().isIncluded)"); indent = "\t"; } codeMethod.CodeLines.Add(indent + field.Name + ".Add(" + ReadType(field.FieldType) + ");"); } indent = string.Empty; if (hasSkipped) { if (!onlySkipped) { codeMethod.CodeLines.Add("if(" + field.Name + "_tester.StoppedAtCase().isSkipped)"); indent = "\t"; } codeMethod.CodeLines.Add(indent + "file.SkipBytes(" + field.Name + "_tester.StoppedAtCase().stopcase.Length);"); } } } //Local fields else if (namedField is BfsLocalField) { BfsLocalField field = namedField as BfsLocalField; if (field.AssignmentExpression != null) codeMethod.CodeLines.Add( dataStruct.Name.ToLower() + "." + field.Name + " = " + CSHelper.MakeExpression(field.AssignmentExpression, dataStruct) + ";"); } } codeMethod.CodeLines.Add("return " + dataStruct.Name.ToLower() + ";"); }
public void MakeEnum(CodeClass codeClass, BfsEnum dataEnum) { codeClass.ClassType = CodeClass.ClassTypeEnum.Struct; //Only add nessecary internal enum aliases if there are any. if (dataEnum.EnumAliases.Count > 0) { StringBuilder b = new StringBuilder(); b.Append("enum " + dataEnum.Name + "Enum {"); List<string> foundEnumAliases = new List<string>(); for (int i = 0; i < dataEnum.EnumFields.Count; i++) { BfsEnumField enumField = dataEnum.EnumFields[i]; if (enumField.Alias != null && !foundEnumAliases.Contains(enumField.Alias)) { if (i != 0) b.Append(", "); b.Append(enumField.Alias); foundEnumAliases.Add(enumField.Alias); } } b.Append("}"); codeClass.CodeFields.Add(b.ToString()); codeClass.CodeFields.Add(dataEnum.Name + "Enum " + dataEnum.Name.ToLower() + ";"); } //Adds the implicit 'value' variable for storing the read primitive type. codeClass.CodeFields.Add(CSHelper.TypeMap(dataEnum.PrimitiveType).ToLower() + " value;"); CodeMethod codeMethod = new CodeMethod(dataEnum.Name + " Read(BfsBinaryReader file)"); codeClass.CodeMethods.Add(codeMethod); string objectType = dataEnum.Name; string varName = dataEnum.Name.ToLower(); codeMethod.CodeLines.Add(objectType + " " + varName + " = new " + objectType + "();"); codeMethod.CodeLines.Add(varName + ".value = file.Read" + dataEnum.PrimitiveType.PrimitiveType.ToString() + "();"); //Sorts the enum fields into categories to make a pretty switch-case/if-then-else structure. List<BfsEnumField> singleValues = new List<BfsEnumField>(); List<BfsEnumField> rangeValues = new List<BfsEnumField>(); BfsEnumField elseValue = null; foreach (BfsEnumField enumField in dataEnum.EnumFields) if (enumField.EnumMatch is BfsEnumValue) singleValues.Add(enumField); else if (enumField.EnumMatch is BfsEnumRange) rangeValues.Add(enumField); else if (enumField.EnumMatch is BfsEnumElse) elseValue = enumField; string tab = string.Empty; //If there are 2 or above single enum matches a switch/case statement is made with the values as cases. //In case the value isn't any of the values it checks the ranges one by one in an if/else/elseif/elseif/... kind of way. if (singleValues.Count > 1) { codeMethod.CodeLines.Add("switch(" + varName + ".value)"); codeMethod.CodeLines.Add("{"); tab = "\t\t"; foreach (BfsEnumField enumField in singleValues) { BfsEnumValue enumVal = enumField.EnumMatch as BfsEnumValue; codeMethod.CodeLines.Add("\tcase " + enumVal.Value + ":"); if (enumField.Alias != null) codeMethod.CodeLines.Add("\t\t" + varName + "." + varName + " = " + objectType + "Enum." + enumField.Alias + ";"); List<string> actionList = new List<string>(); bool addBreak = MakeActionList(dataEnum, actionList, enumField.Actions); foreach (string str in actionList) codeMethod.CodeLines.Add("\t\t" + str); if (addBreak) codeMethod.CodeLines.Add("\t\tbreak;"); } codeMethod.CodeLines.Add("\tdefault:"); } string defaultTab = "\t"; if (singleValues.Count <= 1) defaultTab = string.Empty; //If there only are one single value case, then include it in the if-then-else sequence IfThenElseSequence ifthenelse = new IfThenElseSequence(defaultTab, varName + ": '" + "\" + " + varName + ".value + \""); if (elseValue != null) { ifthenelse.ElseBlock = new IfBlock(); MakeActionList(dataEnum, ifthenelse.ElseBlock.CodeLines, elseValue.Actions); } int countIfElse = 0; if (singleValues.Count == 1) { BfsEnumField enumField = singleValues[0]; BfsEnumValue enumVal = enumField.EnumMatch as BfsEnumValue; IfBlock ifblock = new IfBlock(varName + ".value == " + enumVal.Value); ifthenelse.IfBlocks.Add(ifblock); if (enumField.Alias != null) ifblock.CodeLines.Add(varName + "." + varName + " = " + objectType + "Enum." + enumField.Alias + ";"); MakeActionList(dataEnum, ifblock.CodeLines, enumField.Actions); countIfElse++; } //Enum ranges foreach (BfsEnumField enumField in rangeValues) { BfsEnumRange enumVal = enumField.EnumMatch as BfsEnumRange; string firstEquality = ((enumVal.StartInclusion == BfsInclusionEnum.Included) ? ">= " : "> ") + enumVal.StartValue; string secondEquality = ((enumVal.EndInclusion == BfsInclusionEnum.Included) ? "<= " : "< ") + enumVal.EndValue; IfBlock ifblock = new IfBlock(varName + ".value " + firstEquality + " && " + varName + ".value " + secondEquality); ifthenelse.IfBlocks.Add(ifblock); if (enumField.Alias != null) ifblock.CodeLines.Add(varName + "." + varName + " = " + objectType + "Enum." + enumField.Alias + ";"); MakeActionList(dataEnum, ifblock.CodeLines, enumField.Actions); } codeMethod.AddSplitLines(ifthenelse.ToString()); //End off the switch if (singleValues.Count > 1) { //Only add the final 'break' if the default case doesn't throw any exception //to prevent the code not reachable error if (ifthenelse.IfBlocks.Count != 0 || !ifthenelse.ElseBlock.ToString().Contains("throw new ")) codeMethod.CodeLines.Add("\t\tbreak;"); codeMethod.CodeLines.Add("}"); } codeMethod.CodeLines.Add("return " + varName + ";"); //ToString() method CodeMethod toString = new CodeMethod("override string ToString()"); codeClass.CodeMethods.Add(toString); if(dataEnum.EnumAliases.Count > 0) toString.CodeLines.Add("return " + dataEnum.Name.ToLower() + ".ToString();"); else toString.CodeLines.Add("return value.ToString();"); }