add bindings

This commit is contained in:
2026-06-09 17:36:38 +02:00
commit 8425f05116
19 changed files with 5694 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
FileVersion = 1
[Project]
Name = "Box2D.Setup"
StartupObject = "Box2D.Setup.Program"
[Dependencies]
corlib = "*"
"Cpp2Beef.git" = {Git = "https://git.unicon-gmbh.de/Rune/Cpp2Beef.git"}
+8
View File
@@ -0,0 +1,8 @@
FileVersion = 1
[Workspace]
StartupProject = "Box2D.Setup"
[Projects]
"Box2D.Setup" = {Path = "."}
"Cpp2Beef.git" = {Git = "https://git.unicon-gmbh.de/Rune/Cpp2Beef.git"}
+11
View File
@@ -0,0 +1,11 @@
FileVersion = 1
[Locks."Clang-C.git".Git]
URL = "https://git.unicon-gmbh.de/BeefBindings/Clang-C.git"
Tag = ""
Hash = "86db0167f15d08a63a91a19e46f983a5511bac1a"
[Locks."Cpp2Beef.git".Git]
URL = "https://git.unicon-gmbh.de/Rune/Cpp2Beef.git"
Tag = ""
Hash = "91ddcd4f79cf6df28adff5abfc59cbdc489c7deb"
+7
View File
@@ -0,0 +1,7 @@
#include <box2d/base.h>
#include <box2d/box2d.h>
#include <box2d/collision.h>
#include <box2d/constants.h>
#include <box2d/id.h>
#include <box2d/math_functions.h>
#include <box2d/types.h>
+223
View File
@@ -0,0 +1,223 @@
using System;
using System.IO;
using System.Collections;
using System.Diagnostics;
using Cpp2Beef;
using LibClang;
namespace Box2D.Setup;
class Box2DGenerator : Cpp2BeefGenerator, this(Span<char8*> args)
{
protected override Span<char8*> Args => args;
protected override Flags Flags => .None;
Dictionary<String, StreamWriter> writers = new .() ~ DeleteDictionaryAndKeysAndValues!(_);
protected override StreamWriter GetWriterForHeader(StringView header)
{
if (!header.Contains("include/box2d") && !header.Contains(@"include\box2d"))
return null;
let filename = Path.GetFileNameWithoutExtension(header, ..scope .(64));
if (writers.TryAdd(filename, let keyPtr, let valuePtr))
{
*keyPtr = new .(filename);
String outFilename = scope .(64);
outFilename.Append("../src/");
UpperSnakeCase2PascalCase(filename, outFilename);
outFilename.Append(".bf");
*valuePtr = new .()..Create(outFilename)..Write("""
// This file was generated by Cpp2Beef
using System;
using System.Interop;
namespace Box2D;
""");
}
return *valuePtr;
}
protected override Block GetCursorBlock(CXCursor cursor)
{
if (cursor.kind == .FunctionDecl && GetCursorSpelling!(cursor).StartsWith("b2"))
return .CustomBlock("Box2D");
return base.GetCursorBlock(cursor);
}
protected override void HandleCursor(CXCursor cursor)
{
switch (cursor.kind)
{
case .StructDecl when GetFilePath!(fileInfo.file).EndsWith("math_functions.h"):
base.HandleCursor(cursor);
str.Append("\n\tpublic this(");
bool comma = false;
Clang.VisitChildren(cursor, (cursor, parent, client_data) =>
{
if (cursor.kind != .FieldDecl) return .Continue;
(Self self, bool* comma) = *(.)client_data;
if (*comma) self.str.Append(", ");
*comma = true;
self.WriteTypeAndName(cursor, Clang.GetCursorType(cursor));
return .Continue;
}, &(this, &comma));
str.Append(") { ");
Clang.VisitChildren(cursor, (cursor, parent, client_data) =>
{
if (cursor.kind != .FieldDecl) return .Continue;
Self self = *(.)client_data;
self.str.Append("this.", GetCursorSpelling!(cursor), " = ", GetCursorSpelling!(cursor), "; ");
return .Continue;
}, &(this, default(void)));
str.Append('}');
case .MacroDefinition:
let spelling = GetCursorSpelling!(cursor);
if (spelling == "B2_BREAKPOINT" || spelling == "B2_API" || spelling == "B2_INLINE" || spelling == "B2_ID_INLINE" || spelling == "B2_ZERO_INIT" || spelling == "B2_NULL_ID")
return;
fallthrough;
default:
if (_ == .VarDecl && GetCursorSpelling!(cursor) == "b2_emptySimplexCache")
return;
base.HandleCursor(cursor);
}
}
List<String> functionNonPtrs = new .(8) ~ DeleteContainerAndItems!(_);
protected override void TypeAlias(CXCursor cursor)
{
if (cursor.kind == .TypedefDecl)
{
CXType type = Clang.GetTypedefDeclUnderlyingType(cursor);
if (type.kind == .FunctionProto || type.kind == .FunctionNoProto)
{
functionNonPtrs.Add(new .(GetCursorSpelling!(cursor)));
WriteCustomAttributes(cursor);
AccessSpecifier(cursor);
str.Append("function ");
WriteTypeAndName(cursor, Clang.GetResultType(type));
WriteFunctionProtoParams(type, ScopeTokenize!(cursor, unit));
str.Append(';');
return;
}
}
base.TypeAlias(cursor);
}
protected override void Type(CXType type, TypeParamMode paramMode = .None)
{
if (type.kind == .Pointer)
{
let pointee = Clang.GetPointeeType(type);
if (paramMode == .OutParam) str.Append("out ");
Type(pointee);
if (functionNonPtrs.ContainsAlt(GetTypeSpelling!(pointee)))
return;
if (paramMode != .OutParam && pointee.kind != .FunctionProto && pointee.kind != .FunctionNoProto)
str.Append('*');
return;
}
base.Type(type, paramMode);
}
protected override void GetNameInBindings(CXCursor cursor, String outString)
{
gnib: switch (cursor.kind)
{
case .FunctionDecl:
StringView spelling = GetCursorSpelling!(cursor);
if (spelling.StartsWith("b2"))
spelling.RemoveFromStart(2);
outString.Append(spelling);
case .EnumConstantDecl:
StringView pre, post;
switch (GetCursorSpelling!(Clang.GetCursorSemanticParent(cursor)))
{
case "b2RecQueryType": pre = "b2_recQuery"; post = "";
case "b2TOIState": pre = "b2_toiState"; post = "";
case "b2TreeNodeFlags": pre = "b2_"; post = "Node";
case "b2BodyType": pre = "b2_"; post = "";
case "b2ShapeType": pre = "b2_"; post = "Shape";
case "b2JointType": pre = "b2_"; post = "Joint";
case "b2HexColor": pre = "b2_color"; post = "";
default: Runtime.FatalError("Unhandled enum");
}
StringView spelling = GetCursorSpelling!(cursor);
if (spelling.StartsWith(pre)) spelling.RemoveFromStart(pre.Length);
if (spelling.EndsWith (post)) spelling.RemoveFromEnd(post.Length);
str.Append(spelling[0].ToUpper);
str.Append(spelling[1...]);
case .MacroDefinition:
let spelling = GetCursorSpelling!(cursor);
switch (spelling)
{
case "UINT64_MAX": outString.Append("uint64.MaxValue");
case "B2_ZERO_INIT", "B2_NULL_ID": outString.Append("default");
default: fallthrough gnib;
}
default:
base.GetNameInBindings(cursor, outString);
}
}
protected override void WriteToken(CXToken token)
{
if (currentCursor.kind == .VarDecl &&
fileInfo.prevEnd.int_data > Clang.GetCursorLocation(currentCursor).int_data &&
Clang.GetTokenKind(token) == .Punctuation)
{
switch (GetTokenSpelling!(token))
{
case "{": str.Append(".("); return;
case "}": str.Append( ")"); return;
}
}
if (Clang.GetTokenKind(token) == .Identifier)
{
let spelling = GetTokenSpelling!(token);
bool invoke = spelling.StartsWith("b2");
if (invoke || spelling == "B2_LINEAR_SLOP")
str.Replace("const let", "static var");
if (invoke)
{
str.Append("Box2D.", spelling[2...]);
return;
}
}
base.WriteToken(token);
}
// There are functions you can override, but these are the most common
}
class Program
{
[CLink] static extern int32 system(char8*);
public static int Main(String[] args)
{
if (system("git -C .. submodule update") != 0)
{
Console.WriteLine("Failed to fetch Box2D, check your internet connection");
return 1;
}
{
StreamWriter all = scope .()..Create("b2_all.h");
for (let file in Directory.EnumerateFiles("../box2d/include/box2d"))
{
let filename = file.GetFileName(..scope .(64));
all.Write("#include <box2d/");
all.Write(filename);
all.Write(">\n");
}
}
scope Box2DGenerator(char8*[?]("--language=c", "-I../box2d/include")).Generate("b2_all.h");
return 0;
}
}