Skip to content

Commit fb47496

Browse files
committed
tweak: simplify code, reduce json generation
1 parent 19be729 commit fb47496

31 files changed

Lines changed: 2009 additions & 784 deletions

README.md

Lines changed: 148 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ NativeCodeGen is a code generation tool for RDR3 (Red Dead Redemption 3) native
1212
- Parses C-style enum definitions with hexadecimal values
1313
- Parses C-style struct definitions with field attributes
1414
- Generates TypeScript wrapper classes with proper type mappings
15+
- Generates Lua wrapper classes with proper type annotations
1516
- Generates JSON database for web-based documentation
1617
- Generates Protocol Buffer binary files for efficient data transfer
1718
- Validates native definitions with error accumulation and reporting
@@ -84,9 +85,10 @@ dotnet run --project src/NativeCodeGen.Cli -- generate -i <input> -o <output> [o
8485

8586
| Option | Description | Default |
8687
|--------|-------------|---------|
87-
| `-f, --format` | Output format: `typescript`, `json`, `proto` | `typescript` |
88+
| `-f, --format` | Output format: `typescript`, `lua`, `json`, `proto` | `typescript` |
8889
| `-n, --namespaces` | Filter specific namespaces (comma-separated) | All namespaces |
89-
| `--raw` | Generate raw native declarations without wrapper classes (TypeScript only) | `false` |
90+
| `--raw` | Generate raw native declarations without wrapper classes (TypeScript/Lua) | `false` |
91+
| `--single-file` | Combine all natives into a single file (requires `--raw`) | `false` |
9092
| `--strict` | Treat warnings as errors | `false` |
9193

9294
#### Examples
@@ -97,12 +99,36 @@ Generate TypeScript wrapper classes:
9799
nativegen generate -i /path/to/rdr3-natives -o ./output -f typescript
98100
```
99101

100-
Generate raw TypeScript declarations (no classes):
102+
Generate raw TypeScript declarations (no classes, one file per namespace):
101103

102104
```bash
103105
nativegen generate -i /path/to/rdr3-natives -o ./output -f typescript --raw
104106
```
105107

108+
Generate raw TypeScript declarations (single file with all natives):
109+
110+
```bash
111+
nativegen generate -i /path/to/rdr3-natives -o ./output -f typescript --raw --single-file
112+
```
113+
114+
Generate Lua wrapper classes:
115+
116+
```bash
117+
nativegen generate -i /path/to/rdr3-natives -o ./output -f lua
118+
```
119+
120+
Generate raw Lua declarations (no classes, one file per namespace):
121+
122+
```bash
123+
nativegen generate -i /path/to/rdr3-natives -o ./output -f lua --raw
124+
```
125+
126+
Generate raw Lua declarations (single file with all natives):
127+
128+
```bash
129+
nativegen generate -i /path/to/rdr3-natives -o ./output -f lua --raw --single-file
130+
```
131+
106132
Generate JSON database:
107133

108134
```bash
@@ -141,6 +167,64 @@ dotnet run --project src/NativeCodeGen.Cli -- validate -i <input> [options]
141167
nativegen validate -i /path/to/rdr3-natives --strict
142168
```
143169

170+
## Generation Modes
171+
172+
The `--raw` and `--single-file` flags control how native functions are generated:
173+
174+
### Default Mode (Classes)
175+
176+
Without flags, generates OOP-style wrapper classes where natives are organized as methods on entity classes:
177+
178+
```typescript
179+
// classes/Entity.ts
180+
export class Entity implements IHandle {
181+
constructor(public handle: number) {}
182+
183+
getCoords(alive: boolean): Vector3 {
184+
return Vector3.fromArray(inv<number[]>('0x...', this.handle, alive, rav()));
185+
}
186+
}
187+
188+
// Usage
189+
const ped = new Ped(pedHandle);
190+
const coords = ped.getCoords(true);
191+
```
192+
193+
### Raw Mode (`--raw`)
194+
195+
Generates standalone exported functions with handles as plain numbers. One file per namespace:
196+
197+
```typescript
198+
// natives/ENTITY.ts
199+
export function GetEntityCoords(entity: number, alive: boolean): Vector3 {
200+
return Vector3.fromArray(inv('0x...', entity, alive, rav()));
201+
}
202+
203+
// Usage
204+
const coords = GetEntityCoords(pedHandle, true);
205+
```
206+
207+
### Single File Mode (`--raw --single-file`)
208+
209+
Generates all natives in a single file as global functions. Useful for smaller bundles:
210+
211+
```typescript
212+
// natives.ts
213+
globalThis.GetEntityCoords = function(entity: number, alive: boolean): Vector3 {
214+
return Vector3.fromArray(inv('0x...', entity, alive, rav()));
215+
};
216+
217+
// Usage (functions are on globalThis)
218+
const coords = GetEntityCoords(pedHandle, true);
219+
```
220+
221+
### Lua Modes
222+
223+
Lua follows the same pattern:
224+
225+
- **Raw mode**: Module tables per namespace (`Water.GetWaterHeight(...)`)
226+
- **Single file mode**: Global functions (`GetWaterHeight(...)`)
227+
144228
## Output Formats
145229

146230
### TypeScript (default)
@@ -167,7 +251,7 @@ output/
167251
└── *.ts # Static utility classes
168252
```
169253

170-
With `--raw` flag, generates standalone functions without class wrappers:
254+
With `--raw` flag, generates standalone functions without class wrappers (one file per namespace):
171255

172256
```
173257
output/
@@ -176,7 +260,63 @@ output/
176260
├── enums/
177261
├── structs/
178262
└── natives/
179-
└── *.ts # Raw native function exports
263+
└── *.ts # Raw native function exports per namespace
264+
```
265+
266+
With `--raw --single-file` flags, generates all natives in a single file:
267+
268+
```
269+
output/
270+
├── index.ts
271+
├── types/
272+
├── enums/
273+
├── structs/
274+
└── natives.ts # All native functions in one file
275+
```
276+
277+
### Lua
278+
279+
Generates typed wrapper classes with LuaLS annotations:
280+
281+
```
282+
output/
283+
├── init.lua # Re-exports all modules
284+
├── types/
285+
│ ├── IHandle.lua # Handle interface
286+
│ ├── Vector3.lua # Vector3 class
287+
│ └── BufferedClass.lua # Base class for structs
288+
├── enums/
289+
│ └── *.lua # Enum definitions
290+
├── structs/
291+
│ └── *.lua # Struct classes (BufferedClass)
292+
├── classes/
293+
│ ├── Entity.lua # Base entity class
294+
│ ├── Ped.lua # Ped class (extends Entity)
295+
│ ├── Vehicle.lua # Vehicle class (extends Entity)
296+
│ └── *.lua # Other handle classes
297+
└── namespaces/
298+
└── *.lua # Static utility classes
299+
```
300+
301+
With `--raw` flag, generates standalone functions (one file per namespace):
302+
303+
```
304+
output/
305+
├── fxmanifest.lua
306+
├── enums/
307+
├── structs/
308+
└── natives/
309+
└── *.lua # Raw native function exports per namespace (module tables)
310+
```
311+
312+
With `--raw --single-file` flags, generates all natives in a single file:
313+
314+
```
315+
output/
316+
├── fxmanifest.lua
317+
├── enums/
318+
├── structs/
319+
└── natives.lua # All native functions in one file (global functions)
180320
```
181321

182322
### JSON
@@ -300,5 +440,8 @@ NativeCodeGen/
300440
├── NativeCodeGen.TypeScript/ # TypeScript generation
301441
│ ├── Generation/ # Code generators
302442
│ └── Utilities/ # Name conversion, etc.
443+
├── NativeCodeGen.Lua/ # Lua generation
444+
│ ├── Generation/ # Code generators
445+
│ └── Utilities/ # Name conversion, etc.
303446
└── NativeCodeGen.Cli/ # Command-line interface
304447
```

src/NativeCodeGen.Cli/Program.cs

Lines changed: 25 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,12 @@ static async Task<int> Main(string[] args)
4040

4141
var rawOption = new Option<bool>(
4242
aliases: new[] { "--raw" },
43-
description: "Generate raw native declarations without wrapper classes (typescript only)",
43+
description: "Generate raw native declarations without wrapper classes",
44+
getDefaultValue: () => false);
45+
46+
var singleFileOption = new Option<bool>(
47+
aliases: new[] { "--single-file" },
48+
description: "Generate all natives in a single file (requires --raw)",
4449
getDefaultValue: () => false);
4550

4651
var strictOption = new Option<bool>(
@@ -53,12 +58,13 @@ static async Task<int> Main(string[] args)
5358
generateCommand.AddOption(formatOption);
5459
generateCommand.AddOption(namespacesOption);
5560
generateCommand.AddOption(rawOption);
61+
generateCommand.AddOption(singleFileOption);
5662
generateCommand.AddOption(strictOption);
5763

58-
generateCommand.SetHandler(async (input, output, format, namespaces, raw, strict) =>
64+
generateCommand.SetHandler(async (input, output, format, namespaces, raw, singleFile, strict) =>
5965
{
60-
await Generate(input, output, format, namespaces, raw, strict);
61-
}, inputOption, outputOption, formatOption, namespacesOption, rawOption, strictOption);
66+
await Generate(input, output, format, namespaces, raw, singleFile, strict);
67+
}, inputOption, outputOption, formatOption, namespacesOption, rawOption, singleFileOption, strictOption);
6268

6369
// Validate command
6470
var validateCommand = new Command("validate", "Validate MDX files without generating output");
@@ -87,12 +93,19 @@ static async Task<int> Main(string[] args)
8793
return await rootCommand.InvokeAsync(args);
8894
}
8995

90-
static async Task Generate(string input, string output, string format, string[]? namespaces, bool raw, bool strict)
96+
static async Task Generate(string input, string output, string format, string[]? namespaces, bool raw, bool singleFile, bool strict)
9197
{
9298
Console.WriteLine($"Generating {format} output...");
9399
Console.WriteLine($"Input: {input}");
94100
Console.WriteLine($"Output: {output}");
95101

102+
if (singleFile && !raw)
103+
{
104+
Console.Error.WriteLine("Error: --single-file requires --raw");
105+
Environment.ExitCode = 1;
106+
return;
107+
}
108+
96109
var (db, errors, warnings) = await ParseAllFiles(input);
97110

98111
// Report issues
@@ -126,6 +139,7 @@ static async Task Generate(string input, string output, string format, string[]?
126139
var options = new ExportOptions
127140
{
128141
Raw = raw,
142+
SingleFile = singleFile,
129143
Strict = strict,
130144
Namespaces = namespaces?.Length > 0
131145
? new HashSet<string>(namespaces.SelectMany(n => n.Split(',')), StringComparer.OrdinalIgnoreCase)
@@ -225,21 +239,14 @@ static async Task Validate(string input, bool strict)
225239

226240
if (result.Value != null)
227241
{
228-
allNatives.Add(result.Value);
229-
230-
foreach (var enumName in result.Value.UsedEnums)
242+
// Resolve enum types for parameters and return type
243+
foreach (var param in result.Value.Parameters)
231244
{
232-
if (!enumRegistry.Contains(enumName))
233-
{
234-
allWarnings.Add(new ParseWarning
235-
{
236-
FilePath = file,
237-
Line = 1,
238-
Column = 1,
239-
Message = $"Referenced enum '{enumName}' not found in registry"
240-
});
241-
}
245+
param.Type.ResolveEnumType(enumRegistry.GetBaseType);
242246
}
247+
result.Value.ReturnType.ResolveEnumType(enumRegistry.GetBaseType);
248+
249+
allNatives.Add(result.Value);
243250
}
244251

245252
Interlocked.Increment(ref processedCount);
@@ -261,18 +268,6 @@ static async Task Validate(string input, bool strict)
261268
.GroupBy(n => n.Namespace, StringComparer.OrdinalIgnoreCase)
262269
.ToDictionary(g => g.Key, g => g.ToList(), StringComparer.OrdinalIgnoreCase);
263270

264-
// Track enum usage (single-threaded, after parallel processing)
265-
foreach (var native in allNatives)
266-
{
267-
foreach (var enumName in native.UsedEnums)
268-
{
269-
if (enumRegistry.Contains(enumName))
270-
{
271-
enumRegistry.TrackUsage(enumName, native.Hash);
272-
}
273-
}
274-
}
275-
276271
// Track struct usage - check parameter types for struct references
277272
var structDict = structRegistry.GetAllStructs();
278273
foreach (var native in allNatives)
@@ -298,7 +293,6 @@ static async Task Validate(string input, bool strict)
298293
.OrderBy(n => n.Name)
299294
.ToList();
300295

301-
// Update enums with usage tracking
302296
db.Enums = enumRegistry.GetAllEnums();
303297

304298
return (db, allErrors.ToList(), allWarnings.ToList());

src/NativeCodeGen.Core/Export/BaseExporter.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,12 @@ public void Export(NativeDatabase db, string outputPath, ExportOptions options)
2424
};
2525
}
2626

27-
Generator.Generate(db, outputPath, !options.Raw);
27+
var generatorOptions = new GeneratorOptions
28+
{
29+
UseClasses = !options.Raw,
30+
SingleFile = options.SingleFile
31+
};
32+
Generator.Generate(db, outputPath, generatorOptions);
2833

2934
// Output any warnings from code generation
3035
foreach (var warning in Generator.Warnings)

0 commit comments

Comments
 (0)