-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathgenerate.py
202 lines (174 loc) · 8.14 KB
/
generate.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
import json
from jinja2 import Environment, FileSystemLoader
# Image * is an array only once, SetWindowIcons.
# void * is usually a buffer, could convert? sometimes it is a pointer to a pixel in a buffer etc
LEGACY_NAMES=False
def c_type_to_java_type(field_type):
match field_type:
case "bool":
return "boolean"
case "unsigned char":
return "byte"
case "char":
return "byte"
case "float":
return "float"
case "float *":
return "java.nio.FloatBuffer"
case "float[2]": # TODO test if arrays are working, byte order might be wrong
return "float[]"
case "float[4]":
return "float[]"
case "double":
return "double"
case "unsigned int":
return "int"
case "int":
return "int"
case "int *":
return "java.nio.IntBuffer"
case "long":
return "long"
case "void":
return "void"
case "const char *":
return "String"
# case "Camera3D *": # currently always a pointer to one, never an array
# return "Camera3D"
case "unsigned char *":
return "java.nio.ByteBuffer"
case "const unsigned char *":
return "java.nio.ByteBuffer"
case "char *": # C char is not same as Java char, so we just use bytes
return "java.nio.ByteBuffer" # We could convert to String in SOME cases, but honestly noone should be using Raylib for string manipulation
case "...":
raise Exception("dont support varargs")
case "#endif":
raise Exception("bug in raylib_parser")
case _:
if field_type in struct_names:
return field_type
elif field_type in struct_names_pointers:
return field_type[:-2]
else:
print(f"WARNING: unknown field_type {field_type}.")
return "MemorySegment"
def converter_to_c_type(field_type, field_name): # could we do this on javatype?
if field_type in aliases:
field_type = aliases[field_type]
if field_type in struct_names:
return field_name+".memorySegment"
elif field_type in struct_names_pointers:
return field_name+".memorySegment"
else:
match field_type:
case "const char *":
return "localArena.allocateFrom(" + field_name + ")"
case "float *":
return "MemorySegment.ofBuffer(" + field_name + ")"
case "int *": # LoadFontEx will accept a null pointer so we need to provide one
# TODO: check if any other functions require this
# TODO: limit check to only functions that require,
# TODO: provide an overloaded method that takes no parameter rather a null
return field_name + " == null ? MemorySegment.NULL : MemorySegment.ofBuffer(" + field_name + ")"
case "char *":
return "MemorySegment.ofBuffer(" + field_name + ")"
case "unsigned char *":
return "MemorySegment.ofBuffer(" + field_name + ")"
case "const unsigned char *":
return "MemorySegment.ofBuffer(" + field_name + ")"
case "float[2]":
return "Arena.ofAuto().allocateFrom(ValueLayout.JAVA_FLOAT, " + field_name + ")" # FIXME localArena would better
case "float[4]":
return "Arena.ofAuto().allocateFrom(ValueLayout.JAVA_FLOAT, " + field_name + ")" # FIXME localArena would better
case _:
return field_name
def converter_from_memorysegment(java_type):
match java_type:
case "String":
return ".getString(0)"
case "java.nio.FloatBuffer":
return ".reinterpret(Integer.MAX_VALUE/2).asByteBuffer().order(ByteOrder.nativeOrder()).asFloatBuffer()"
case "java.nio.IntBuffer":
return ".reinterpret(Integer.MAX_VALUE/2).asByteBuffer().order(ByteOrder.nativeOrder()).asIntBuffer()"
case "java.nio.ByteBuffer":
return ".reinterpret(Integer.MAX_VALUE/2).asByteBuffer().order(ByteOrder.nativeOrder())"
case "float[]":
return ".toArray(ValueLayout.JAVA_FLOAT)"
case _:
return ""
class Field:
def __init__(self, name, type, description=""):
self.name = name
if type in aliases:
type = aliases[type]
self.type = type
self.description = description
self.getter = "get" + name[0].upper() + name[1:]
self.setter = "set" + name[0].upper() + name[1:]
self.java_type = c_type_to_java_type(type)
self.converter_to_c_type = converter_to_c_type(type, name)
self.value_to_c_type = converter_to_c_type(type, "value")
self.needs_local_allocator = type == "const char *"
self.is_a_struct = type in struct_names
self.is_a_struct_pointer = type in struct_names_pointers
self.converter_from_memorysegment = converter_from_memorysegment(self.java_type)
class Function:
def __init__(self, name, return_type, description, params=[]):
self.name = name
self.java_name = name[0].lower() + name[1:]
if LEGACY_NAMES:
self.java_name = name
if return_type in aliases:
return_type = aliases[return_type]
self.return_type = return_type
self.java_return_type = c_type_to_java_type(return_type)
self.return_type_is_a_struct = self.java_return_type in struct_names
self.params = params
self.description = description
self.needs_allocator = f"public static MemorySegment {name}(SegmentAllocator allocator" in raylib_h
self.needs_local_allocator = any(x.needs_local_allocator for x in params)
self.converter_from_memorysegment = converter_from_memorysegment(self.java_return_type)
with open("src/main/java/com/raylib/jextract/raylib_h.java", 'r') as file:
raylib_h = file.read()
with open("src/main/java/com/raylib/jextract/raylib_h_1.java", 'r') as file:
raylib_h += file.read()
raylib_data = json.load(open('raylib_api.json'))
raymath_data = json.load(open('raymath_api.json'))
rlgl_data = json.load(open('rlgl_api.json'))
# bug in raylib_parser incorrectly parses rlVertexBuffer, so skip it
rlgl_data['structs'] = [obj for obj in rlgl_data['structs'] if obj.get('name') != 'rlVertexBuffer']
struct_names = []
aliases = {}
for struct in raylib_data['structs'] + rlgl_data['structs']:
struct_names.append(struct['name'])
for alias in raylib_data['aliases']:
aliases[alias['name']] = alias['type']
aliases[alias['name']+" *"] = alias['type']+" *"
struct_names_pointers = [x + " *" for x in struct_names]
environment = Environment(loader=FileSystemLoader("templates/"))
template = environment.get_template("struct.java")
for struct in raylib_data['structs']+ rlgl_data['structs']:
struct_name = struct['name']
struct_description = struct['description']
fields = []
for field in struct['fields']:
fields.append(Field(field['name'], field['type'], field['description']))
content = template.render(struct_name=struct_name, fields=fields, struct_description=struct_description)
with open("src/main/java/com/raylib/" + struct_name + ".java", "w") as f:
f.write(content)
functions = []
for function in raylib_data['functions'] + raymath_data['functions'] + rlgl_data['functions']:
params = []
try:
if function['name'] not in raylib_h:
raise RuntimeError("function not found in raylib_h")
if 'params' in function:
for param in function['params']:
params.append(Field(param['name'], param['type'], ""))
functions.append(Function(function['name'], function['returnType'], function['description'], params))
except Exception as e:
print(f"WARNING: skipping function {function['name']} because {e}")
enums = raylib_data['enums'] + rlgl_data['enums']
with open("src/main/java/com/raylib/Raylib.java", "w") as f:
f.write(environment.get_template("Raylib.java").render(functions=functions, struct_names=struct_names, enums=enums))