mirror of
				https://github.com/raylib-cs/raylib-cs
				synced 2025-10-29 05:59:51 -04:00 
			
		
		
		
	- Reviewing generator contribution. Work in progress.
This commit is contained in:
		| @@ -1,13 +1,21 @@ | ||||
| using System; | ||||
| /* Raylib-cs | ||||
|  * Program.cs - Generator for creating pinvoke bindings from raylib headers. | ||||
|  * Copyright 2019 | ||||
|  * | ||||
|  * Release under zLib License. | ||||
|  * See LICENSE for details. | ||||
|  */ | ||||
|  | ||||
| using System; | ||||
| using System.IO; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Globalization; | ||||
| using System.Diagnostics; | ||||
| using Microsoft.CodeAnalysis.CSharp.Syntax; | ||||
| using Microsoft.CodeAnalysis.CSharp; | ||||
| using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; | ||||
| using Microsoft.CodeAnalysis; | ||||
| using System.Diagnostics; | ||||
| using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; | ||||
|  | ||||
| namespace Generator | ||||
| { | ||||
| @@ -16,6 +24,7 @@ namespace Generator | ||||
|         public string Name; | ||||
|         public Type Type; | ||||
|         public int Offset; | ||||
|  | ||||
|         public TypeMember(string name, Type type, int offset) | ||||
|         { | ||||
|             Name = name; | ||||
| @@ -28,6 +37,7 @@ namespace Generator | ||||
|     { | ||||
|         public string Name; | ||||
|         public List<KeyValuePair<string, int>> Enums; | ||||
|  | ||||
|         public UserDefinedEnumData(string name) | ||||
|         { | ||||
|             Name = name; | ||||
| @@ -47,7 +57,6 @@ namespace Generator | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public struct Function | ||||
|     { | ||||
|         public string Name; | ||||
| @@ -60,10 +69,8 @@ namespace Generator | ||||
|             Name = Isreturnpointer ? name.Replace("*", "") : name; | ||||
|             ReturnType = new Type(returnType, Isreturnpointer); | ||||
|             Params = new List<FunctionParam>(parameters); | ||||
|  | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public override string ToString() | ||||
|         { | ||||
|             var str = $"{ReturnType} {Name}("; | ||||
| @@ -88,7 +95,6 @@ namespace Generator | ||||
|             IsPointer = isPointer; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public override string ToString() | ||||
|         { | ||||
|             return Name + (IsPointer == true ? "*" : ""); | ||||
| @@ -108,6 +114,13 @@ namespace Generator | ||||
|  | ||||
|         public FunctionParam(string FullParam) | ||||
|         { | ||||
|             if (FullParam == "") | ||||
|             { | ||||
|                 Name = ""; | ||||
|                 Type = new Type(); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             FullParam = FullParam.Trim(); | ||||
|             if (FullParam.Split(' ').Length == 2) | ||||
|             { | ||||
| @@ -129,46 +142,116 @@ namespace Generator | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     struct SyntaxData | ||||
|     { | ||||
|         public string tag; | ||||
|         public List<string> functions; | ||||
|         public HashSet<string> types; | ||||
|         public HashSet<string> enums; | ||||
|         public List<Function> funcs; | ||||
|         public List<string> comments; | ||||
|         public Dictionary<string, UserDefinedTypeData> tps; | ||||
|         public Dictionary<string, UserDefinedEnumData> eps; | ||||
|  | ||||
|         public SyntaxData(string _tag) | ||||
|         { | ||||
|             tag = _tag; | ||||
|             functions = new List<string>(); | ||||
|             types = new HashSet<string>(); | ||||
|             enums = new HashSet<string>(); | ||||
|             funcs = new List<Function>(); | ||||
|             comments = new List<string>(); | ||||
|             tps = new Dictionary<string, UserDefinedTypeData>(); | ||||
|             eps = new Dictionary<string, UserDefinedEnumData>(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     class Program | ||||
|     { | ||||
|         static void Main(string[] args) | ||||
|         { | ||||
|             var process = Process.Start(new ProcessStartInfo("Dia2Dump.exe", " -t raylib.pdb") { RedirectStandardOutput = true, UseShellExecute = false }); | ||||
|              | ||||
|             string typedata = ""; | ||||
|             var ischkout = process.StandardOutput; | ||||
|              | ||||
|             while (process.HasExited == false) | ||||
|             { | ||||
|                 typedata += ischkout.ReadToEnd(); | ||||
|             } | ||||
|             File.WriteAllText("types.txt", typedata); | ||||
|             var typesfile = File.ReadAllLines("types.txt"); | ||||
|  | ||||
|             var TypeMap = new Dictionary<string, string>(); | ||||
|             typesfile = typesfile.Where(x => x != "").ToArray(); | ||||
|  | ||||
|             var sources = new KeyValuePair<string, string>[] { new KeyValuePair<string, string>( "raylib.h","RLAPI") , new KeyValuePair<string, string>("physac.h", "PHYSACDEF"), | ||||
|                 new KeyValuePair<string, string>( "easings.h","EASEDEF"),new KeyValuePair<string, string>( "raygui.h","RAYGUIDEF")}; | ||||
|             var sources = new KeyValuePair<string, string>[] { | ||||
|                 new KeyValuePair<string, string>( "raylib.h","RLAPI"), | ||||
|                 // new KeyValuePair<string, string>("physac.h", "PHYSACDEF"), | ||||
|                 // new KeyValuePair<string, string>( "easings.h","EASEDEF"), | ||||
|                 // new KeyValuePair<string, string>( "raygui.h","RAYGUIDEF") | ||||
|             }; | ||||
|  | ||||
|             foreach (var sourcefilenameandexporttag in sources) | ||||
|             { | ||||
|                 var functions = new List<string>(); | ||||
|                 var types = new HashSet<string>(); | ||||
|                 var enums = new HashSet<string>(); | ||||
|                 var Funcs = new List<Function>(); | ||||
|                 var tps = new Dictionary<string, UserDefinedTypeData>(); | ||||
|                 var eps = new Dictionary<string, UserDefinedEnumData>(); | ||||
|  | ||||
|                 var sourcefilename = sourcefilenameandexporttag.Key; | ||||
|                 var FileName = new CultureInfo("en-us", false).TextInfo.ToTitleCase(sourcefilename.Replace(".h", "")); | ||||
|                 var ExportTag = sourcefilenameandexporttag.Value; | ||||
|                 var sourcefile = File.ReadAllLines(sourcefilename); | ||||
|              | ||||
|                 var syntax = GetSyntax(sourcefile, ExportTag, TypeMap, typesfile); | ||||
|                 GenerateBinding(syntax, FileName); | ||||
|             } | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // Takes the source from a raylib module and stores the syntax data | ||||
|         static SyntaxData GetSyntax(string[] sourcefile, string tag, Dictionary<string, string> TypeMap, string[] typesfile) | ||||
|         { | ||||
|             var syntax = new SyntaxData(tag); | ||||
|  | ||||
|             // local references | ||||
|             var ExportTag = syntax.tag; | ||||
|             var comments = syntax.comments; | ||||
|             var functions = syntax.functions; | ||||
|             var enums = syntax.enums; | ||||
|             var Funcs = syntax.funcs; | ||||
|             var types = syntax.types; | ||||
|             var tps = syntax.tps; | ||||
|             var eps = syntax.eps; | ||||
|  | ||||
|             for (int i = 0; i < sourcefile.Length; i++) | ||||
|             { | ||||
|                 var source = sourcefile[i].Split("//".ToCharArray())[0].Trim(); | ||||
|  | ||||
|                 // Comments test | ||||
|                 if (source.StartsWith("*") || source.StartsWith("//")) | ||||
|                 { | ||||
|                     comments.Add(source); | ||||
|                 } | ||||
|  | ||||
|                 if (source.Contains(ExportTag)) | ||||
|                 { | ||||
|                     if (!source.Contains("#define")) | ||||
|                     { | ||||
|                         source = source.TrimStart(ExportTag.ToCharArray()).Trim(); | ||||
|                             if (!source.Contains("{")) functions.Add(source); | ||||
|                         if (!source.Contains("{"))  | ||||
|                         { | ||||
|                             // Some functions go across multiple lines | ||||
|                             if (source[source.Length - 1] == ',') | ||||
|                             { | ||||
|                                 i++; | ||||
|                                 var nextLine = sourcefile[i].Split("//".ToCharArray())[0].Trim(); | ||||
|                                 source += " " + nextLine; | ||||
|                             } | ||||
|  | ||||
|                             functions.Add(source); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
| @@ -191,17 +274,20 @@ namespace Generator | ||||
|                             } | ||||
|                             types.Add(src.Trim('}', ';').Trim()); | ||||
|                         } | ||||
|  | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // Break down function string | ||||
|             for (int i = 0; i < functions.Count; i++) | ||||
|             { | ||||
|                 var func = functions[i]; | ||||
|                 var returntype = func.Split('(')[0].Split(' ').Length == 3 ? func.Split('(')[0].Split(' ')[0] + " " + func.Split('(')[0].Split(' ')[1] : func.Split('(')[0].Split(' ')[0]; | ||||
|  | ||||
|                 types.Add(returntype); | ||||
|                 func = func.Substring(returntype.Length).Trim(); | ||||
|  | ||||
|                 //@TODO extra comma in param list | ||||
|                 var funcname = func.Split('(')[0].Trim(); | ||||
|                 var Func = new Function(funcname, returntype); | ||||
|                 if (func.Contains(',')) | ||||
| @@ -217,7 +303,6 @@ namespace Generator | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|  | ||||
|                             var TypeAndVar = Param.Trim().Split(' '); | ||||
|                             if (TypeAndVar.Length == 2) | ||||
|                             { | ||||
| @@ -233,7 +318,6 @@ namespace Generator | ||||
|                             } | ||||
|  | ||||
|                             Func.Params.Add(new FunctionParam(Param)); | ||||
|  | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
| @@ -243,6 +327,7 @@ namespace Generator | ||||
|                     var TypeAndVar = Param.Trim().Split(' '); | ||||
|                     var type = TypeAndVar[0]; | ||||
|                     var Var = TypeAndVar[1]; | ||||
|  | ||||
|                     types.Add(type); | ||||
|                     Func.Params.Add(new FunctionParam(Param)); | ||||
|                 } | ||||
| @@ -290,7 +375,6 @@ namespace Generator | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|  | ||||
|             for (int i = 0; i < Funcs.Count; i++) | ||||
|             { | ||||
|                 var Func = Funcs[i]; | ||||
| @@ -298,6 +382,7 @@ namespace Generator | ||||
|                 { | ||||
|                     Func.ReturnType.Name = TypeMap[Func.ReturnType.Name]; | ||||
|                 } | ||||
|  | ||||
|                 for (int t = 0; t < Func.Params.Count; t++) | ||||
|                 { | ||||
|                     if (TypeMap.ContainsKey(Func.Params[t].Type.Name)) | ||||
| @@ -308,6 +393,7 @@ namespace Generator | ||||
|                 } | ||||
|                 Funcs[i] = Func; | ||||
|             } | ||||
|  | ||||
|             for (int i = 0; i < typesfile.Length; i++) | ||||
|             { | ||||
|                 var typestr = typesfile[i]; | ||||
| @@ -319,6 +405,7 @@ namespace Generator | ||||
|                         var usertype = new UserDefinedTypeData(type); | ||||
|                         int t = 1; | ||||
|                         typestr = typesfile[i + t]; | ||||
|  | ||||
|                         while (!(typestr.Contains("UserDefinedType: ") && typestr["UserDefinedType: ".Length - 1] == ' ')) | ||||
|                         { | ||||
|                             if (typestr.Contains("Member")) | ||||
| @@ -327,17 +414,21 @@ namespace Generator | ||||
|                                 var MemberNameandType = tpsrc.Substring(tpsrc.IndexOf("Type:") + "Type:".Length).Trim().Split(','); | ||||
|                                 var MemberName = MemberNameandType[1].Trim(); | ||||
|                                 var MemberType = MemberNameandType[0].Trim(); | ||||
|  | ||||
|                                 if (MemberType.Contains("<unnamed-enum-false>")) | ||||
|                                 { | ||||
|                                     MemberType = "bool"; | ||||
|                                 } | ||||
|  | ||||
|                                 var isptr = MemberType[MemberType.Length - 1] == '*'; | ||||
|                                 var offset = int.Parse(tpsrc.Split(',')[0].Split(':')[1].Trim().Substring("this+0x".Length), System.Globalization.NumberStyles.HexNumber); | ||||
|  | ||||
|                                 if (isptr) | ||||
|                                 { | ||||
|                                     MemberType = MemberType.Remove(MemberType.Length - 1); | ||||
|                                 } | ||||
|                                 usertype.Members.Add(new TypeMember(MemberName, new Type(MemberType, isptr), offset)); | ||||
|  | ||||
|                                 if (typesfile[i + t + 1].Contains(MemberType.Trim("struct".ToCharArray())) && | ||||
|                                     typesfile[i + t + 1].Contains("UserDefinedType:  ")) | ||||
|                                 { | ||||
| @@ -350,6 +441,7 @@ namespace Generator | ||||
|                         tps.Add(usertype.Name, usertype); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 if (typestr.Contains("Enum           : ")) | ||||
|                 { | ||||
|                     var Enum = typestr.Substring("Enum           : ".Length).Split(',')[0].Trim(); | ||||
| @@ -358,6 +450,7 @@ namespace Generator | ||||
|                         var UserDefineEnum = new UserDefinedEnumData(Enum); | ||||
|                         int t = 1; | ||||
|                         typestr = typesfile[i + t]; | ||||
|  | ||||
|                         while (typestr.Contains("Constant")) | ||||
|                         { | ||||
|                             var intstr = typestr.Split(",".ToCharArray(), 2)[0].Split(':')[1].Split(' ')[4].Substring("0x".Length).Trim(); | ||||
| @@ -373,24 +466,38 @@ namespace Generator | ||||
|             } | ||||
|             types.IntersectWith(tps.Select(x => x.Key)); | ||||
|  | ||||
|             return syntax; | ||||
|         } | ||||
|  | ||||
|         // Takes in SyntaxData and creates a .cs binding file | ||||
|         static void GenerateBinding(SyntaxData syntax, string fileName) | ||||
|         { | ||||
|             // local references | ||||
|             var ExportTag = syntax.tag; | ||||
|             var comments = syntax.comments; | ||||
|             var functions = syntax.functions; | ||||
|             var enums = syntax.enums; | ||||
|             var Funcs = syntax.funcs; | ||||
|             var types = syntax.types; | ||||
|             var tps = syntax.tps; | ||||
|             var eps = syntax.eps; | ||||
|  | ||||
|             // ---------------------------------------------------------------------------- | ||||
|             // Begin generating bindings here using SyntaxTree | ||||
|             // ---------------------------------------------------------------------------- | ||||
|             var codetree = CompilationUnit().AddUsings(UsingDirective(ParseName("System"))) | ||||
|                 .AddUsings(UsingDirective(ParseName("System.IO"))) | ||||
|                 .AddUsings(UsingDirective(ParseName("System.Collections.Generic"))) | ||||
|                 .AddUsings(UsingDirective(ParseName("System.Security"))) | ||||
|                     .AddUsings(UsingDirective(ParseName("System.Runtime.InteropServices"))) | ||||
|                     ; | ||||
|                 .AddUsings(UsingDirective(ParseName("System.Runtime.InteropServices"))); | ||||
|  | ||||
|             var Raylibnamespace = NamespaceDeclaration(ParseName("Raylib")).NormalizeWhitespace(); | ||||
|  | ||||
|             foreach (var Enumtype in eps.Values) | ||||
|             { | ||||
|                     var Enum = EnumDeclaration(Enumtype.Name) | ||||
|             .WithModifiers( | ||||
|                 TokenList( | ||||
|                     Token(SyntaxKind.PublicKeyword))); | ||||
|                 var Enum = EnumDeclaration(Enumtype.Name).WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword))); | ||||
|                 foreach (var Member in Enumtype.Enums) | ||||
|                 { | ||||
|  | ||||
|                     var enummember = EnumMemberDeclaration( | ||||
|                             Identifier(Member.Key)) | ||||
|                         .WithEqualsValue( | ||||
| @@ -402,6 +509,7 @@ namespace Generator | ||||
|                 } | ||||
|                 Raylibnamespace = Raylibnamespace.AddMembers(Enum); | ||||
|             } | ||||
|  | ||||
|             foreach (var Type in tps.Values) | ||||
|             { | ||||
|                 var Struct = StructDeclaration(Type.Name).WithAttributeLists( | ||||
| @@ -431,8 +539,11 @@ namespace Generator | ||||
|                     .WithModifiers( | ||||
|                         TokenList( | ||||
|                             Token(SyntaxKind.PublicKeyword))); | ||||
|  | ||||
|  | ||||
|                 bool IsUnsafe = false; | ||||
|                 var FixedStructTypes = new List<string>(); | ||||
|  | ||||
|                 foreach (var Member in Type.Members) | ||||
|                 { | ||||
|                     var IsStruct = false; | ||||
| @@ -458,7 +569,6 @@ namespace Generator | ||||
|                         TypeName = TypeName.Replace("unsigned char", "byte"); | ||||
|                     } | ||||
|  | ||||
|  | ||||
|                     var IsFixed = false; | ||||
|                     var VariableDec = VariableDeclarator(Member.Name); | ||||
|                     if (TypeName.Contains("[")) | ||||
| @@ -554,17 +664,19 @@ namespace Generator | ||||
|                             IsUnsafe = true; | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     var variable = VariableDeclaration(ParseTypeName(TypeName)).AddVariables(VariableDec); | ||||
|                     var field = FieldDeclaration(variable).AddModifiers(Token(SyntaxKind.PublicKeyword)); | ||||
|                     if (IsFixed) field = field.AddModifiers(Token(SyntaxKind.FixedKeyword)); | ||||
|                     Struct = Struct.AddMembers(field); | ||||
|                 } | ||||
|  | ||||
|                 if (IsUnsafe) Struct = Struct.AddModifiers(Token(SyntaxKind.UnsafeKeyword)); | ||||
|                 Raylibnamespace = Raylibnamespace.AddMembers(Struct); | ||||
|             } | ||||
|  | ||||
|             { | ||||
|                     var RaylibClass = ClassDeclaration(FileName) | ||||
|                 var RaylibClass = ClassDeclaration(fileName) | ||||
|         .WithAttributeLists( | ||||
|             SingletonList<AttributeListSyntax>( | ||||
|                 AttributeList( | ||||
| @@ -668,12 +780,10 @@ namespace Generator | ||||
|             }          | ||||
|             codetree = codetree.AddMembers(Raylibnamespace); | ||||
|             Console.WriteLine(codetree.NormalizeWhitespace().ToFullString()); | ||||
|                 File.WriteAllText($"{FileName}.cs", codetree.NormalizeWhitespace().ToFullString()); | ||||
|             File.WriteAllText($"{fileName}.cs", codetree.NormalizeWhitespace().ToFullString()); | ||||
|     | ||||
|             } | ||||
|             Console.WriteLine("Finished generating bindings for file "); | ||||
|             Console.ReadLine(); | ||||
|         } | ||||
|  | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user