Skip to content

Commit 0936307

Browse files
committed
tweak: add more tests to make sure our proto build stays up to date
Use flags to simplify code gen
1 parent 1fcaaa6 commit 0936307

20 files changed

Lines changed: 722 additions & 154 deletions

.github/workflows/release.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ jobs:
1717
- os: ubuntu-latest
1818
rid: linux-x64
1919
artifact: nativegen-linux-x64
20+
- os: ubuntu-latest
21+
rid: linux-musl-x64
22+
artifact: nativegen-linux-musl-x64
2023
- os: windows-latest
2124
rid: win-x64
2225
artifact: nativegen-win-x64.exe

src/NativeCodeGen.Core/Export/DatabaseConverter.cs

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ public static ExportDatabase Convert(NativeDatabase db, ExportOptions options)
5353
export.SharedExamples.Add(ConvertSharedExample(example));
5454
}
5555

56+
// Add type definitions
57+
foreach (var (name, typeInfo) in TypeRegistry.GetTypeDefinitions())
58+
{
59+
export.Types.Add(new ExportTypeEntry { Name = name, Type = typeInfo });
60+
}
61+
5662
return export;
5763
}
5864

@@ -75,18 +81,12 @@ private static ExportNative ConvertNative(NativeDefinition native)
7581

7682
private static ExportParameter ConvertParameter(NativeParameter param)
7783
{
78-
var flags = ParamFlags.None;
79-
if (param.IsOutput) flags |= ParamFlags.Output;
80-
if (param.Attributes.IsThis) flags |= ParamFlags.This;
81-
if (param.Attributes.IsNotNull) flags |= ParamFlags.NotNull;
82-
if (param.Attributes.IsIn) flags |= ParamFlags.In;
83-
8484
return new ExportParameter
8585
{
8686
Name = param.Name,
8787
Type = param.Type.ToString(),
8888
Description = param.Description,
89-
Flags = flags,
89+
Flags = param.Flags,
9090
DefaultValue = param.DefaultValue
9191
};
9292
}
@@ -124,24 +124,12 @@ private static ExportStruct ConvertStruct(StructDefinition structDef)
124124

125125
private static ExportStructField ConvertStructField(StructField f)
126126
{
127-
var flags = FieldFlags.None;
128-
if (f.IsPadding) flags |= FieldFlags.Padding;
129-
else
130-
{
131-
// @in = setter only (input to native)
132-
// @out = getter only (output from native)
133-
// Both true = full access (default, no flag needed)
134-
// Both false = padding (handled above)
135-
if (f.IsInput && !f.IsOutput) flags |= FieldFlags.In;
136-
if (f.IsOutput && !f.IsInput) flags |= FieldFlags.Out;
137-
}
138-
139127
return new ExportStructField
140128
{
141129
Name = f.Name,
142130
Type = f.Type.ToString(),
143131
Comment = f.Comment,
144-
Flags = flags,
132+
Flags = f.Flags,
145133
ArraySize = f.ArraySize,
146134
NestedStructName = f.IsNestedStruct ? f.NestedStructName : null,
147135
Alignment = f.Alignment

src/NativeCodeGen.Core/Export/ExportModels.cs

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Text.Json.Serialization;
2+
using NativeCodeGen.Core.Models;
23
using ProtoBuf;
34

45
namespace NativeCodeGen.Core.Export;
@@ -7,33 +8,9 @@ namespace NativeCodeGen.Core.Export;
78
/// Export models for both JSON and Protobuf serialization.
89
/// Use [JsonIgnore] for fields that should be skipped in JSON (e.g., redundant keys).
910
/// Prefixed with "Export" to distinguish from parsing models.
11+
/// Flag enums (ParamFlags, FieldFlags) are defined in Models namespace and shared.
1012
/// </summary>
1113

12-
/// <summary>
13-
/// Bitflags for parameter attributes. Omitted from JSON when 0.
14-
/// </summary>
15-
[Flags]
16-
public enum ParamFlags
17-
{
18-
None = 0,
19-
Output = 1, // Pointer output parameter (value returned via pointer)
20-
This = 2, // @this - use as instance method receiver
21-
NotNull = 4, // @notnull - string cannot be null
22-
In = 8 // @in - input+output pointer (uses initialized value)
23-
}
24-
25-
/// <summary>
26-
/// Bitflags for struct field attributes. Omitted from JSON when 0.
27-
/// </summary>
28-
[Flags]
29-
public enum FieldFlags
30-
{
31-
None = 0,
32-
In = 1, // @in - setter only (input to native)
33-
Out = 2, // @out - getter only (output from native)
34-
Padding = 4 // @padding - no accessors, reserves space
35-
}
36-
3714
[ProtoContract]
3815
public partial class ExportDatabase
3916
{
@@ -48,6 +25,9 @@ public partial class ExportDatabase
4825

4926
[ProtoMember(4)]
5027
public List<ExportSharedExample> SharedExamples { get; set; } = new();
28+
29+
[ProtoMember(5)]
30+
public List<ExportTypeEntry> Types { get; set; } = new();
5131
}
5232

5333
[ProtoContract]

src/NativeCodeGen.Core/Export/JsonContext.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Text.Json.Serialization;
2+
using NativeCodeGen.Core.Models;
23

34
namespace NativeCodeGen.Core.Export;
45

@@ -30,6 +31,11 @@ namespace NativeCodeGen.Core.Export;
3031
[JsonSerializable(typeof(Dictionary<string, ExportEnum>))]
3132
[JsonSerializable(typeof(Dictionary<string, ExportStruct>))]
3233
[JsonSerializable(typeof(Dictionary<string, ExportSharedExample>))]
34+
[JsonSerializable(typeof(Dictionary<string, ExportTypeInfo>))]
35+
[JsonSerializable(typeof(ExportTypeInfo))]
36+
[JsonSerializable(typeof(ExportTypeCategory))]
37+
[JsonSerializable(typeof(ExportTypeEntry))]
38+
[JsonSerializable(typeof(List<ExportTypeEntry>))]
3339
public partial class ExportJsonContext : JsonSerializerContext
3440
{
3541
}

src/NativeCodeGen.Core/Export/JsonExporter.cs

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System.Text.Json;
22
using System.Text.Json.Serialization;
33
using NativeCodeGen.Core.Parsing;
4+
using ProtoBuf;
45

56
namespace NativeCodeGen.Core.Export;
67

@@ -13,6 +14,7 @@ public void Export(NativeDatabase db, string outputPath, ExportOptions options)
1314
// For JSON, we want dictionaries keyed by name for enums, structs, and examples
1415
var output = new JsonOutput
1516
{
17+
Types = TypeRegistry.GetTypeDefinitions(),
1618
Natives = exportDb.Namespaces.SelectMany(ns => ns.Natives).ToList(),
1719
Enums = exportDb.Enums.ToDictionary(e => e.Name, e => e),
1820
Structs = exportDb.Structs.ToDictionary(s => s.Name, s => s),
@@ -31,8 +33,127 @@ public void Export(NativeDatabase db, string outputPath, ExportOptions options)
3133
/// </summary>
3234
public class JsonOutput
3335
{
36+
public Dictionary<string, ExportTypeInfo> Types { get; set; } = new();
3437
public List<ExportNative> Natives { get; set; } = new();
3538
public Dictionary<string, ExportEnum> Enums { get; set; } = new();
3639
public Dictionary<string, ExportStruct> Structs { get; set; } = new();
3740
public Dictionary<string, ExportSharedExample> SharedExamples { get; set; } = new();
3841
}
42+
43+
/// <summary>
44+
/// Type information for JSON/Protobuf export.
45+
/// </summary>
46+
[ProtoContract]
47+
public class ExportTypeInfo
48+
{
49+
[ProtoMember(1)]
50+
[JsonConverter(typeof(JsonStringEnumConverter<ExportTypeCategory>))]
51+
public ExportTypeCategory Category { get; set; }
52+
53+
[ProtoMember(2)]
54+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
55+
public string? NativeType { get; set; }
56+
57+
[ProtoMember(3)]
58+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
59+
public string? Description { get; set; }
60+
}
61+
62+
/// <summary>
63+
/// Type categories for the type registry.
64+
/// </summary>
65+
[ProtoContract]
66+
public enum ExportTypeCategory
67+
{
68+
Primitive = 0,
69+
Handle = 1,
70+
Hash = 2,
71+
Vector3 = 3,
72+
String = 4,
73+
Void = 5,
74+
Any = 6
75+
}
76+
77+
/// <summary>
78+
/// Type entry for protobuf serialization (since protobuf doesn't support dictionaries directly).
79+
/// </summary>
80+
[ProtoContract]
81+
public class ExportTypeEntry
82+
{
83+
[ProtoMember(1)]
84+
public string Name { get; set; } = string.Empty;
85+
86+
[ProtoMember(2)]
87+
public ExportTypeInfo Type { get; set; } = new();
88+
}
89+
90+
/// <summary>
91+
/// Registry of all supported types and their mappings.
92+
/// </summary>
93+
public static class TypeRegistry
94+
{
95+
public static Dictionary<string, ExportTypeInfo> GetTypeDefinitions()
96+
{
97+
var types = new Dictionary<string, ExportTypeInfo>();
98+
99+
// Primitives - integers
100+
types["int"] = new ExportTypeInfo { Category = ExportTypeCategory.Primitive, NativeType = "int", Description = "32-bit signed integer" };
101+
types["uint"] = new ExportTypeInfo { Category = ExportTypeCategory.Primitive, NativeType = "int", Description = "32-bit unsigned integer" };
102+
types["i8"] = new ExportTypeInfo { Category = ExportTypeCategory.Primitive, NativeType = "int", Description = "8-bit signed integer" };
103+
types["i16"] = new ExportTypeInfo { Category = ExportTypeCategory.Primitive, NativeType = "int", Description = "16-bit signed integer" };
104+
types["i32"] = new ExportTypeInfo { Category = ExportTypeCategory.Primitive, NativeType = "int", Description = "32-bit signed integer" };
105+
types["i64"] = new ExportTypeInfo { Category = ExportTypeCategory.Primitive, NativeType = "int", Description = "64-bit signed integer" };
106+
types["u8"] = new ExportTypeInfo { Category = ExportTypeCategory.Primitive, NativeType = "int", Description = "8-bit unsigned integer" };
107+
types["u16"] = new ExportTypeInfo { Category = ExportTypeCategory.Primitive, NativeType = "int", Description = "16-bit unsigned integer" };
108+
types["u32"] = new ExportTypeInfo { Category = ExportTypeCategory.Primitive, NativeType = "int", Description = "32-bit unsigned integer" };
109+
types["u64"] = new ExportTypeInfo { Category = ExportTypeCategory.Primitive, NativeType = "int", Description = "64-bit unsigned integer" };
110+
111+
// Primitives - floats
112+
types["float"] = new ExportTypeInfo { Category = ExportTypeCategory.Primitive, NativeType = "float", Description = "32-bit floating point" };
113+
types["double"] = new ExportTypeInfo { Category = ExportTypeCategory.Primitive, NativeType = "float", Description = "64-bit floating point" };
114+
types["f32"] = new ExportTypeInfo { Category = ExportTypeCategory.Primitive, NativeType = "float", Description = "32-bit floating point" };
115+
types["f64"] = new ExportTypeInfo { Category = ExportTypeCategory.Primitive, NativeType = "float", Description = "64-bit floating point" };
116+
117+
// Primitives - boolean
118+
types["bool"] = new ExportTypeInfo { Category = ExportTypeCategory.Primitive, NativeType = "int", Description = "Boolean (0 or 1)" };
119+
types["BOOL"] = new ExportTypeInfo { Category = ExportTypeCategory.Primitive, NativeType = "int", Description = "Boolean (0 or 1)" };
120+
121+
// Special types
122+
types["void"] = new ExportTypeInfo { Category = ExportTypeCategory.Void, Description = "No return value" };
123+
types["Any"] = new ExportTypeInfo { Category = ExportTypeCategory.Any, NativeType = "int", Description = "Any type (context-dependent)" };
124+
types["Hash"] = new ExportTypeInfo { Category = ExportTypeCategory.Hash, NativeType = "int", Description = "32-bit hash value (joaat)" };
125+
types["Vector3"] = new ExportTypeInfo { Category = ExportTypeCategory.Vector3, Description = "3D vector (x, y, z floats)" };
126+
types["string"] = new ExportTypeInfo { Category = ExportTypeCategory.String, Description = "Null-terminated string pointer" };
127+
types["char*"] = new ExportTypeInfo { Category = ExportTypeCategory.String, Description = "Null-terminated string pointer" };
128+
129+
// Handle types - all map to int at native level
130+
var handles = new[]
131+
{
132+
("Entity", "Base type for all world entities"),
133+
("Ped", "Pedestrian/character handle (extends Entity)"),
134+
("Vehicle", "Vehicle handle (extends Entity)"),
135+
("Object", "Object/prop handle (extends Entity)"),
136+
("Pickup", "Pickup handle"),
137+
("Player", "Player handle"),
138+
("Cam", "Camera handle"),
139+
("Blip", "Map blip handle"),
140+
("Interior", "Interior handle"),
141+
("FireId", "Fire instance handle"),
142+
("AnimScene", "Animation scene handle"),
143+
("ItemSet", "Item set handle"),
144+
("PersChar", "Persistent character handle"),
145+
("PopZone", "Population zone handle"),
146+
("PropSet", "Prop set handle"),
147+
("Volume", "Volume handle"),
148+
("ScrHandle", "Generic script handle"),
149+
("PedGroup", "Ped group handle")
150+
};
151+
152+
foreach (var (name, desc) in handles)
153+
{
154+
types[name] = new ExportTypeInfo { Category = ExportTypeCategory.Handle, NativeType = "int", Description = desc };
155+
}
156+
157+
return types;
158+
}
159+
}

src/NativeCodeGen.Core/Export/ProtobufExporter.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Diagnostics.CodeAnalysis;
2+
using NativeCodeGen.Core.Models;
23
using NativeCodeGen.Core.Parsing;
34
using ProtoBuf;
45

@@ -20,6 +21,11 @@ public class ProtobufExporter : IExporter
2021
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(ExportStructField))]
2122
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(ExportNativeReference))]
2223
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(ExportSharedExample))]
24+
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(ExportTypeInfo))]
25+
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(ExportTypeEntry))]
26+
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(ExportTypeCategory))]
27+
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(ParamFlags))]
28+
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(FieldFlags))]
2329
public void Export(NativeDatabase db, string outputPath, ExportOptions options)
2430
{
2531
var exportDb = DatabaseConverter.Convert(db, options);

src/NativeCodeGen.Core/Generation/ArgumentBuilder.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@ public static string GetArgumentExpression(NativeParameter param, ITypeMapper ty
2424
var useFloat = config.UseFloatWrapper;
2525
var useHash = config.UseHashWrapper;
2626

27-
// Output-only pointer (int*, float*, Vector3* without @in)
28-
if (param.IsOutput)
27+
// Pure output pointer (int*, float*, Vector3* without @in)
28+
if (param.IsPureOutput)
2929
{
3030
return typeMapper.GetPointerPlaceholder(param.Type);
3131
}
3232

3333
// Input+output pointer (int*, float*, Vector3*, Entity* with @in)
34-
if (param.Type.IsPointer && param.Attributes.IsIn)
34+
if (param.IsInOut)
3535
{
3636
var format = typeMapper.GetInitializedPointerFormat(param.Type);
3737
// Handle types need to pass .handle (unless raw mode)

src/NativeCodeGen.Core/Generation/NativeClassifier.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ public ClassifiedNatives Classify(NativeDatabase db)
9292

9393
var firstParam = native.Parameters[0];
9494

95-
if (firstParam.Attributes.IsThis && firstParam.Type.Category == TypeCategory.Handle)
95+
if (firstParam.IsThis && firstParam.Type.Category == TypeCategory.Handle)
9696
{
9797
var typeName = firstParam.Type.Name;
9898
return typeName == "Object" ? "Prop" : typeName;

src/NativeCodeGen.Core/Generation/RawNativeBuilder.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,8 @@ public void EmitModuleFooter(string moduleName)
120120
public void EmitFunction(NativeDefinition native, BindingStyle binding, string? moduleName = null, string? nameOverride = null)
121121
{
122122
var name = nameOverride ?? GetFunctionName(native.Name);
123-
var inputParams = native.Parameters.Where(p => !p.IsOutput).ToList();
124-
var outputParams = native.Parameters.Where(p => p.IsOutput).ToList();
123+
var inputParams = native.Parameters.Where(p => !p.IsPureOutput).ToList();
124+
var outputParams = native.Parameters.Where(p => p.IsPureOutput).ToList();
125125

126126
// Emit doc comment
127127
EmitDoc(native, inputParams, outputParams);
@@ -295,7 +295,7 @@ private void EmitInvokeNative(NativeDefinition native, List<NativeParameter> out
295295
args.Add(native.Hash);
296296

297297
// Input params - use ArgumentBuilder shared logic
298-
foreach (var p in native.Parameters.Where(p => !p.IsOutput))
298+
foreach (var p in native.Parameters.Where(p => !p.IsPureOutput))
299299
{
300300
args.Add(GetArgumentExpression(p));
301301
}

0 commit comments

Comments
 (0)