const std = @import("std"); const Allocator = std.mem.Allocator; const io = @import("io"); var fmtBuffer: [256]u8 = undefined; /// Convert C type to Zig type /// Simple table-based conversion for SDL3 types pub fn convertType(c_type: []const u8, allocator: Allocator) ![]const u8 { const trimmed = std.mem.trim(u8, c_type, " \t"); // Handle opaque struct pointers: "struct X *" -> "*anyopaque" if (std.mem.startsWith(u8, trimmed, "struct ") and std.mem.endsWith(u8, trimmed, " *")) { return try allocator.dupe(u8, "*anyopaque"); } // Handle function pointers: For now, just return as placeholder until we implement full conversion if (std.mem.indexOf(u8, trimmed, "(SDLCALL *") != null or std.mem.indexOf(u8, trimmed, "(*") != null) { // TODO: Implement full function pointer conversion // For now, return a placeholder type return try std.fmt.allocPrint(allocator, "?*const anyopaque", .{}); } // Handle array types: "Uint8[2]" -> "[2]u8" if (std.mem.indexOf(u8, trimmed, "[")) |bracket_pos| { const base_type = std.mem.trim(u8, trimmed[0..bracket_pos], " \t"); var array_part = trimmed[bracket_pos..]; // "[2]" // Recursively convert the base type const zig_base = try convertType(base_type, allocator); defer allocator.free(zig_base); const inner = array_part[1 .. array_part.len - 1]; if (inner.len > 0) { var alias_to_c: bool = false; _ = std.fmt.parseInt(u32, inner, 0) catch { // not a real error alias_to_c = true; }; if (alias_to_c) { var b = std.io.fixedBufferStream(&fmtBuffer); _ = try b.write("["); _ = try b.write("c."); _ = try b.write(inner); _ = try b.write("]"); array_part = b.getWritten(); std.debug.print("arrya_part = {s}\n", .{array_part}); } } // Return Zig array notation: [size]Type return try std.fmt.allocPrint(allocator, "{s}{s}", .{ array_part, zig_base }); } // Primitives if (std.mem.eql(u8, trimmed, "void")) return try allocator.dupe(u8, "void"); if (std.mem.eql(u8, trimmed, "bool")) return try allocator.dupe(u8, "bool"); if (std.mem.eql(u8, trimmed, "SDL_bool")) return try allocator.dupe(u8, "bool"); if (std.mem.eql(u8, trimmed, "float")) return try allocator.dupe(u8, "f32"); if (std.mem.eql(u8, trimmed, "double")) return try allocator.dupe(u8, "f64"); if (std.mem.eql(u8, trimmed, "char")) return try allocator.dupe(u8, "u8"); if (std.mem.eql(u8, trimmed, "int")) return try allocator.dupe(u8, "c_int"); if (std.mem.eql(u8, trimmed, "va_list")) return try allocator.dupe(u8, "std.builtin.VaList"); // SDL integer types if (std.mem.eql(u8, trimmed, "Uint8")) return try allocator.dupe(u8, "u8"); if (std.mem.eql(u8, trimmed, "Uint16")) return try allocator.dupe(u8, "u16"); if (std.mem.eql(u8, trimmed, "Uint32")) return try allocator.dupe(u8, "u32"); if (std.mem.eql(u8, trimmed, "Uint64")) return try allocator.dupe(u8, "u64"); if (std.mem.eql(u8, trimmed, "Sint8")) return try allocator.dupe(u8, "i8"); if (std.mem.eql(u8, trimmed, "Sint16")) return try allocator.dupe(u8, "i16"); if (std.mem.eql(u8, trimmed, "Sint32")) return try allocator.dupe(u8, "i32"); if (std.mem.eql(u8, trimmed, "Sint64")) return try allocator.dupe(u8, "i64"); if (std.mem.eql(u8, trimmed, "size_t")) return try allocator.dupe(u8, "usize"); if (std.mem.eql(u8, trimmed, "intptr_t")) return try allocator.dupe(u8, "isize"); // Common pointer types if (std.mem.eql(u8, trimmed, "const char *")) return try allocator.dupe(u8, "[*c]const u8"); if (std.mem.eql(u8, trimmed, "const char **")) return try allocator.dupe(u8, "[*c][*c]const u8"); if (std.mem.eql(u8, trimmed, "const char * const *")) return try allocator.dupe(u8, "[*c]const [*c]const u8"); if (std.mem.eql(u8, trimmed, "char *")) return try allocator.dupe(u8, "[*c]u8"); if (std.mem.eql(u8, trimmed, "char **")) return try allocator.dupe(u8, "[*c][*c]u8"); if (std.mem.eql(u8, trimmed, "char**")) return try allocator.dupe(u8, "[*c][*c]u8"); if (std.mem.eql(u8, trimmed, "void *")) return try allocator.dupe(u8, "?*anyopaque"); if (std.mem.eql(u8, trimmed, "const void *")) return try allocator.dupe(u8, "?*const anyopaque"); if (std.mem.eql(u8, trimmed, "void **")) return try allocator.dupe(u8, "[*c]?*anyopaque"); if (std.mem.eql(u8, trimmed, "const Uint8 *")) return try allocator.dupe(u8, "[*c]const u8"); if (std.mem.eql(u8, trimmed, "Uint8 *")) return try allocator.dupe(u8, "[*c]u8"); if (std.mem.eql(u8, trimmed, "Uint8 **")) return try allocator.dupe(u8, "[*c][*c]u8"); if (std.mem.eql(u8, trimmed, "const int *")) return try allocator.dupe(u8, "[*c]const c_int"); // Handle SDL types with pointers // Check for double pointers like "SDL_Type **" or "SDL_Type *const *" or "SDL_Type * const *" if (std.mem.startsWith(u8, trimmed, "SDL_")) { // Match "SDL_Type *const *" (no space before const) if (std.mem.indexOf(u8, trimmed, " *const *")) |pos| { const base_type = trimmed[4..pos]; // Remove SDL_ prefix and get type return std.fmt.allocPrint(allocator, "[*c]*const {s}", .{base_type}); } // Match "SDL_Type * const *" (space before const) if (std.mem.indexOf(u8, trimmed, " * const *")) |pos| { const base_type = trimmed[4..pos]; // Remove SDL_ prefix and get type return std.fmt.allocPrint(allocator, "[*c]*const {s}", .{base_type}); } if (std.mem.indexOf(u8, trimmed, " **")) |pos| { const base_type = trimmed[4..pos]; // Remove SDL_ prefix and get type return std.fmt.allocPrint(allocator, "[*c][*c]{s}", .{base_type}); } } // Handle primitive pointer types if (std.mem.eql(u8, trimmed, "int *")) return try allocator.dupe(u8, "*c_int"); if (std.mem.eql(u8, trimmed, "bool *")) return try allocator.dupe(u8, "*bool"); if (std.mem.eql(u8, trimmed, "size_t *")) return try allocator.dupe(u8, "*usize"); if (std.mem.eql(u8, trimmed, "float *")) return try allocator.dupe(u8, "*f32"); if (std.mem.eql(u8, trimmed, "const float *")) return try allocator.dupe(u8, "*const f32"); if (std.mem.eql(u8, trimmed, "double *")) return try allocator.dupe(u8, "*f64"); if (std.mem.eql(u8, trimmed, "Uint8 *")) return try allocator.dupe(u8, "*u8"); if (std.mem.eql(u8, trimmed, "Uint16 *")) return try allocator.dupe(u8, "*u16"); if (std.mem.eql(u8, trimmed, "Uint32 *")) return try allocator.dupe(u8, "*u32"); if (std.mem.eql(u8, trimmed, "Uint64 *")) return try allocator.dupe(u8, "*u64"); if (std.mem.eql(u8, trimmed, "Sint8 *")) return try allocator.dupe(u8, "*i8"); if (std.mem.eql(u8, trimmed, "Sint16 *")) return try allocator.dupe(u8, "*i16"); if (std.mem.eql(u8, trimmed, "Sint32 *")) return try allocator.dupe(u8, "*i32"); if (std.mem.eql(u8, trimmed, "const bool *")) return try allocator.dupe(u8, "*const bool"); if (std.mem.startsWith(u8, trimmed, "const ")) { const rest = trimmed[6..]; if (std.mem.endsWith(u8, rest, " *") or std.mem.endsWith(u8, rest, "*")) { const base_type = if (std.mem.endsWith(u8, rest, " *")) rest[0 .. rest.len - 2] else rest[0 .. rest.len - 1]; if (std.mem.startsWith(u8, base_type, "SDL_")) { // const SDL_Foo * -> *const Foo const zig_type = base_type[4..]; // Remove SDL_ return std.fmt.allocPrint(allocator, "*const {s}", .{zig_type}); } } } if (std.mem.endsWith(u8, trimmed, " *") or std.mem.endsWith(u8, trimmed, "*")) { const base_type = if (std.mem.endsWith(u8, trimmed, " *")) trimmed[0 .. trimmed.len - 2] else trimmed[0 .. trimmed.len - 1]; if (std.mem.startsWith(u8, base_type, "SDL_")) { // SDL_Foo * or SDL_Foo* -> ?*Foo (nullable for opaque types from C) const zig_type = base_type[4..]; // Remove SDL_ return std.fmt.allocPrint(allocator, "?*{s}", .{zig_type}); } } // Handle SDL types without pointers if (std.mem.startsWith(u8, trimmed, "SDL_")) { // SDL_Foo -> Foo return try allocator.dupe(u8, trimmed[4..]); } // Generic pointer handling for any remaining pointer types // Handle "const struct Foo *" -> "*const Foo" if (std.mem.startsWith(u8, trimmed, "const struct ")) { if (std.mem.endsWith(u8, trimmed, " *")) { const struct_name = trimmed[13 .. trimmed.len - 2]; // Remove "const struct " and " *" return std.fmt.allocPrint(allocator, "*const {s}", .{struct_name}); } } // Handle "Foo *" for any remaining types (fallback to C pointer) if (std.mem.endsWith(u8, trimmed, " *")) { const base_type = trimmed[0 .. trimmed.len - 2]; return std.fmt.allocPrint(allocator, "[*c]{s}", .{base_type}); } if (std.mem.endsWith(u8, trimmed, "*")) { const base_type = trimmed[0 .. trimmed.len - 1]; return std.fmt.allocPrint(allocator, "[*c]{s}", .{base_type}); } // Fallback: return as-is return try allocator.dupe(u8, trimmed); } /// Determine the appropriate cast for a given type when calling C functions pub fn getCastType(zig_type: []const u8) CastType { // Bool needs @bitCast if (std.mem.eql(u8, zig_type, "bool")) { return .bit_cast; } // Opaque pointers need @ptrCast (both ?*Type and *Type) // Skip [*c] (C pointers) and *anyopaque if (std.mem.startsWith(u8, zig_type, "?*") or std.mem.startsWith(u8, zig_type, "*")) { // Exclude anyopaque and C pointers if (std.mem.indexOf(u8, zig_type, "anyopaque") != null or std.mem.startsWith(u8, zig_type, "[*c]") or std.mem.startsWith(u8, zig_type, "?[*c]")) { return .none; } return .ptr_cast; } // Enums need @intFromEnum // We'll detect these by naming convention or explicit marking // For now, assume types ending in certain patterns are enums if (std.mem.indexOf(u8, zig_type, "Type") != null or std.mem.indexOf(u8, zig_type, "Mode") != null or std.mem.indexOf(u8, zig_type, "Op") != null) { return .int_from_enum; } // Flags (packed structs) need @bitCast if (std.mem.endsWith(u8, zig_type, "Flags") or std.mem.endsWith(u8, zig_type, "Format")) { return .bit_cast; } return .none; } /// Convert C function pointer type to Zig function pointer syntax /// Example: Sint64 (SDLCALL *size)(void *userdata) -> *const fn (?*anyopaque) callconv(.C) i64 fn convertFunctionPointerType(c_type: []const u8, allocator: Allocator) ![]const u8 { // Pattern: ReturnType (SDLCALL *name)(params) or ReturnType (*name)(params) // Find the return type (everything before the opening paren) const open_paren = std.mem.indexOf(u8, c_type, "(") orelse return try allocator.dupe(u8, c_type); const return_type_str = std.mem.trim(u8, c_type[0..open_paren], " \t"); // Find the parameters (between the last ( and the last )) const last_open_paren = std.mem.lastIndexOf(u8, c_type, "(") orelse return try allocator.dupe(u8, c_type); const last_close_paren = std.mem.lastIndexOf(u8, c_type, ")") orelse return try allocator.dupe(u8, c_type); if (last_close_paren <= last_open_paren) return try allocator.dupe(u8, c_type); const params_str = std.mem.trim(u8, c_type[last_open_paren + 1 .. last_close_paren], " \t"); // Convert return type (but don't recursively convert function pointers) const zig_return = if (std.mem.indexOf(u8, return_type_str, "(") != null) try allocator.dupe(u8, return_type_str) else try convertType(return_type_str, allocator); defer allocator.free(zig_return); // Convert parameters var params_list = std.ArrayList([]const u8).init(allocator); defer { for (params_list.items) |param| { allocator.free(param); } params_list.deinit(); } // Parse comma-separated parameters var param_iter = std.mem.splitScalar(u8, params_str, ','); while (param_iter.next()) |param| { const trimmed_param = std.mem.trim(u8, param, " \t"); if (trimmed_param.len == 0) continue; // Extract just the type (remove parameter name if present) // Pattern: "void *userdata" -> "void *" // Pattern: "Sint64 offset" -> "Sint64" var param_type: []const u8 = trimmed_param; // Find the last space that separates type from name if (std.mem.lastIndexOf(u8, trimmed_param, " ")) |space_pos| { // Check if what comes after is an identifier (not a *) const after_space = trimmed_param[space_pos + 1 ..]; if (after_space.len > 0 and after_space[0] != '*') { param_type = std.mem.trimRight(u8, trimmed_param[0..space_pos], " \t"); } } // Don't recursively convert function pointers in params const zig_param = if (std.mem.indexOf(u8, param_type, "(") != null) try allocator.dupe(u8, param_type) else try convertType(param_type, allocator); try params_list.append(zig_param); } // Build Zig function pointer type var result = std.ArrayList(u8).init(allocator); defer result.deinit(); try result.appendSlice("?*const fn ("); for (params_list.items, 0..) |param, i| { if (i > 0) try result.appendSlice(", "); try result.appendSlice(param); } try result.appendSlice(") callconv(.C) "); try result.appendSlice(zig_return); return try result.toOwnedSlice(); } pub const CastType = enum { none, ptr_cast, bit_cast, int_from_enum, enum_from_int, };