using System; using System.IO; using System.Collections; using System.Diagnostics; using Cpp2Beef; using LibClang; namespace SDL3.Setup; class SDL3Generator : Cpp2BeefGenerator, this(Span args) { protected override Span Args => args; protected override Flags Flags => .None; private BumpAllocator alloc = new .() ~ delete _; append Dictionary writers = .(16); protected override StreamWriter GetWriterForHeader(StringView header) { String filename = Path.GetFileNameWithoutExtension(header, ..scope .(64)); if (!filename.StartsWith("SDL_") || filename == "SDL_begin_code") return null; if (writers.TryAdd(filename, let keyPtr, let valuePtr)) { *keyPtr = new:alloc .(filename); *valuePtr = new:alloc .()..Create(scope $"../src/{filename}.bf")..Write(""" // This file was generated by Cpp2Beef using System; using System.Interop; using static SDL3.SDL; namespace SDL3; """); } return *valuePtr; } protected override Block GetCursorBlock(CXCursor cursor) { if (Path.GetFileName(GetFilePath!(fileInfo.file), ..scope .(128)).StartsWith("SDL_test")) return .CustomBlock("SDLTest"); return .CustomBlock("SDL"); } protected override void HandleCursor(CXCursor cursor) { switch (cursor.kind) { case .MacroDefinition: if (Clang.Cursor_IsMacroFunctionLike(cursor) != 0 && GetFilePath!(fileInfo.file).EndsWith("SDL_version.h")) { BeginCursor(cursor); let tokens = ScopeTokenize!(cursor, unit); WriteCustomAttributes(cursor); str.Append("public static "); if (GetCursorSpelling!(cursor) == "SDL_VERSION_ATLEAST") str.Append("bool "); else str.Append("c_int "); GetNameInBindings(cursor, str); bool inArgs = true; for (let token in tokens[1...]) { if (inArgs) { let spelling = GetTokenSpelling!(token); if (spelling == ")") { inArgs = false; str.Append(") => "); continue; } if (Clang.GetTokenKind(token) == .Identifier) str.Append("c_int "); str.Append(spelling); if (spelling == ",") str.Append(' '); } else WriteToken(token); } str.Append(';'); return; } let spelling = GetCursorSpelling!(cursor); if (spelling.StartsWith("SDL_PRI") || spelling.StartsWith("VK_DEFINE_")) return; switch (spelling) { case "FONT_LINE_HEIGHT": BeginCursor(cursor); str.Append("public static c_int FONT_LINE_HEIGHT => (FONT_CHARACTER_SIZE + 2);"); return; case "PFN_xrGetInstanceProcAddr": BeginCursor(cursor); str.Append("public typealias PFN_xrGetInstanceProcAddr = SDL.FunctionPointer;"); return; case "main", "SDL_SIZE_MAX", "SDL_FUNCTION", "SDL_FILE", "SDL_ASSERT_FILE", "SDL_LINE", "SDL_SCOPED_CAPABILITY", "SDL_NO_THREAD_SAFETY_ANALYSIS": return; } case .TypedefDecl: String prefix = scope .(64); var spelling = GetCursorSpelling!(cursor); if (spelling == "SDL_Keycode") prefix.Set("SDLK_"); else { if (!spelling.StartsWith("SDL_")) fallthrough; spelling.RemoveFromStart(4); /*if (GetTypeSpelling!(Clang.GetTypedefDeclUnderlyingType(cursor)) != "Uint32") fallthrough;*/ if (spelling == "TrayEntryFlags") spelling = "Trayentry"; else if (spelling == "MouseButtonFlags") spelling = "Button"; else if (spelling.EndsWith("Flags")) spelling.RemoveFromEnd(5); else if (spelling.EndsWith("Flag")) spelling.RemoveFromEnd(4); /*else if (spelling.EndsWith("ID")) spelling.RemoveFromEnd(2);*/ else if (spelling == "BlendMode") spelling = "Blendmode"; else if (spelling == "GLContextResetNotification") spelling = "GlContextReset"; else if (spelling == "GLProfile") spelling = "GlContextProfile"; else if (spelling == "Keycode") spelling = "K"; else if (spelling != "GPUShaderFormat") fallthrough; if (spelling.StartsWith("GL")) { spelling = scope:: String(spelling); spelling[1] = 'l'; } prefix.Append("SDL"); if (spelling.StartsWith("GPU")) { prefix.Append("_GPU_"); for (let c in spelling[3...]) prefix.Append(c.ToUpper); prefix.Append('_'); } else { for (let c in spelling) { if (c.IsUpper) prefix.Append('_'); prefix.Append(c.ToUpper); } prefix.Append('_'); } } BeginCursor(cursor); if (spelling == "Button") str.Append("[AllowDuplicates] "); AccessSpecifier(cursor); str.Append("enum "); GetNameInBindings(cursor, str); str.Append(" : "); Type(Clang.GetTypedefDeclUnderlyingType(cursor)); void FixCurly() { if (fileInfo.queuedTokens.HasFlag(.LSquirly)) str.Append("\n{"); if (fileInfo.queuedTokens.HasFlag(.RSquirly)) str.Append("\n}"); else str.Append("\n\t"); fileInfo.queuedTokens = .None; } bool isSDL_GLContextFlag = spelling == "GlContext"; { BeginBody!(cursor); for (let macro in this.[Friend]unitMacros) { let macroCursor = macro.value; if (Clang.Cursor_IsMacroFunctionLike(macroCursor) != 0) continue; if (Clang.GetFileLocation(Clang.GetCursorLocation(macroCursor), ..?, ?, ?, ?) != fileInfo.file) continue; var macroSpelling = GetCursorSpelling!(macroCursor); if (!macroSpelling.StartsWith(prefix)) continue; if (isSDL_GLContextFlag) { if (!macroSpelling.EndsWith("_FLAG")) continue; macroSpelling.RemoveFromEnd(5); } macroSpelling.RemoveFromStart(prefix.Length); BeginCursor(macroCursor); FixCurly(); if (macroSpelling[0].IsDigit) str.Append('_'); UpperSnakeCase2PascalCase(macroSpelling, str); let tokens = ScopeTokenize!(macroCursor, unit); AllWhiteSpaceBetween(Clang.GetRangeEnd(Clang.GetTokenExtent(unit, tokens[0])), Clang.GetRangeStart(Clang.GetTokenExtent(unit, tokens[1]))); str.Append("= "); WriteTokens(tokens[1...], Clang.GetNullLocation(), .Punctuation); str.Append(','); @macro.Remove(); WriteComments(Clang.GetCursorLocation(macroCursor)); } } FixCurly(); return; default: } base.HandleCursor(cursor); } protected override void GetNameInBindings(CXCursor cursor, String outString) { bool RemovePrefix(ref StringView view) { if (view.StartsWith("SDLTest_")) view.RemoveFromStart(8); else if (view.StartsWith("SDL_")) view.RemoveFromStart(4); else return false; return true; } var spelling = GetCursorSpelling!(cursor); if (!RemovePrefix(ref spelling)) { if (cursor.kind == .MacroDefinition) switch (spelling) { case "NULL": outString.Append("null"); return; case "TRUE": outString.Append("true"); return; case "FALSE": outString.Append("false"); return; } base.GetNameInBindings(cursor, outString); return; } switch (cursor.kind) { case .EnumConstantDecl: var parentSpelling = GetCursorSpelling!(Clang.GetCursorSemanticParent(cursor)); if (parentSpelling == "SDL_GPUTextureFormat") { spelling.RemoveFromStart("GPU_TEXTUREFORMAT_".Length); outString.Append(spelling); break; } StringView name = UpperSnakeCase2PascalCase(spelling, ..scope .(spelling.Length)); switch (parentSpelling) { case "SDL_AudioFormat": parentSpelling = "Audio"; case "SDL_AssertState": parentSpelling = "Assertion"; case "SDL_GamepadBindingType": parentSpelling = "GamepadBindtype"; case "SDL_RendererLogicalPresentation": parentSpelling = "LogicalPresentation"; default: RemovePrefix(ref parentSpelling); } for (let c in parentSpelling) { if (name[0].ToLower == c.ToLower) name.RemoveFromStart(1); else break; } if (name[0].IsDigit) outString.Append('_'); outString.Append(name); default: outString.Append(spelling); } } protected override void WriteToken(CXToken token) { var spelling = GetTokenSpelling!(token); switch (spelling) { case "size_t": str.Append("c_size"); return; case "SDL_BUTTON_LEFT": str.Append("Left"); return; case "SDL_BUTTON_MIDDLE": str.Append("Middle"); return; case "SDL_BUTTON_RIGHT": str.Append("Right"); return; case "SDL_BUTTON_X1": str.Append("X1"); return; case "SDL_BUTTON_X2": str.Append("X2"); return; } if (!spelling.StartsWith("SDL_") || !spelling.EndsWith("ID")) { base.WriteToken(token); return; } spelling.RemoveFromStart(4); str.Append("SDL."); str.Append(spelling); } protected override bool IsOutParam(CXCursor arg, CXCursor method) { let comment = Clang.Cursor_GetParsedComment(method); let spelling = GetCursorSpelling!(arg); for (let i < Clang.Comment_GetNumChildren(comment)) { let param = Clang.Comment_GetChild(comment, i); if (Clang.Comment_GetKind(param) != .ParamCommand || ScopeCXString!(Clang.ParamCommandComment_GetParamName(param)) != spelling) continue; let paragraph = Clang.BlockCommandComment_GetParagraph(param); if (Clang.Comment_GetNumChildren(paragraph) == 0) continue; let paragraphChild = Clang.Comment_GetChild(paragraph, 0); if (Clang.Comment_GetKind(paragraphChild) != .Text) continue; StringView paragraphText = ScopeCXString!(Clang.TextComment_GetText(paragraphChild))..TrimStart(); if (paragraphText.StartsWith("a pointer filled in") || paragraphText.StartsWith("a pointer to receive")) return true; else break; } return base.IsOutParam(arg, method); } } class Program { [CLink] static extern int32 system(char8*); public static int Main(String[] args) { //Runtime.Assert(system("git -C .. submodule update") == 0, "Failed to fetch SDL3, check your internet connection"); { StreamWriter all = scope .()..Create("SDL3_all.h"); for (let file in Directory.EnumerateFiles("../SDL/include/SDL3")) { let filename = file.GetFileName(..scope .(64)); if (filename == "SDL_oldnames.h" || filename == "SDL_egl.h" || filename == "SDL_intrin.h" || filename == "SDL_endian.h" || filename == "SDL_platform_defines.h" || filename == "SDL_begin_code.h" || filename == "SDL_close_code.h" || filename.StartsWith("SDL_opengl")) continue; all.Write("#include \n"); } } SDL3Generator generator = scope .(char8*[?]("--language=c", "-I../SDL/include", "-DSDL_oldnames_h_", "-DSDL_platform_defines_h_", "-DSDL_endian_h_", "-DCrcUint32=Uint32", "-DCrcUint8=Uint8", "-DSDL_SLOW_MEMCPY", "-DSDL_SLOW_MEMMOVE", "-DSDL_SLOW_MEMSET", "-DSDL_DISABLE_ALLOCA", "-DSDL_DISABLE_ANALYZE_MACROS")); generator.Generate("SDL3_all.h", null); return 0; } }