@@ -569,6 +569,181 @@ private static string FormatXmlDescription(string? text)
569569 return result . ToString ( ) ;
570570 }
571571
572+ #region Enum Generation
573+
574+ /// <summary>
575+ /// Defines enum groupings for constants. Maps enum name to the list of constant names to include.
576+ /// </summary>
577+ private static readonly Dictionary < string , EnumDefinition > EnumDefinitions = new ( )
578+ {
579+ [ "Secp256k1EcFlags" ] = new EnumDefinition
580+ {
581+ Description = "Flags for public key serialization format." ,
582+ IsFlags = false ,
583+ Members = new ( )
584+ {
585+ { "SECP256K1_EC_COMPRESSED" , "Compressed format (33 bytes)." } ,
586+ { "SECP256K1_EC_UNCOMPRESSED" , "Uncompressed format (65 bytes)." } ,
587+ }
588+ } ,
589+ [ "Secp256k1ContextFlags" ] = new EnumDefinition
590+ {
591+ Description = "Flags for secp256k1 context creation." ,
592+ IsFlags = false ,
593+ Members = new ( )
594+ {
595+ { "SECP256K1_CONTEXT_NONE" , "Creates a context sufficient for all functionality." } ,
596+ }
597+ } ,
598+ } ;
599+
600+ /// <summary>
601+ /// Maps function parameters (by function name + param name) to the enum type they should use.
602+ /// </summary>
603+ private static readonly Dictionary < ( string FunctionName , string ParamName ) , string > ParameterEnumMappings = new ( )
604+ {
605+ { ( "secp256k1_ec_pubkey_serialize" , "flags" ) , "Secp256k1EcFlags" } ,
606+ } ;
607+
608+ private class EnumDefinition
609+ {
610+ public string ? Description { get ; set ; }
611+ public bool IsFlags { get ; set ; }
612+ public Dictionary < string , string ? > Members { get ; set ; } = new ( ) ;
613+ }
614+
615+ /// <summary>
616+ /// Generates enum types from constants based on predefined groupings.
617+ /// </summary>
618+ public void GenerateEnums ( StringBuilder sb , Secp256k1Api api )
619+ {
620+ foreach ( var ( enumName , enumDef ) in EnumDefinitions )
621+ {
622+ sb . AppendLine ( ) ;
623+ if ( ! string . IsNullOrEmpty ( enumDef . Description ) )
624+ {
625+ sb . AppendLine ( $ " /// <summary>{ enumDef . Description } </summary>") ;
626+ }
627+ if ( enumDef . IsFlags )
628+ {
629+ sb . AppendLine ( " [Flags]" ) ;
630+ }
631+ sb . AppendLine ( $ " public enum { enumName } : uint") ;
632+ sb . AppendLine ( " {" ) ;
633+
634+ foreach ( var ( constantName , memberDesc ) in enumDef . Members )
635+ {
636+ var constant = api . Constants . FirstOrDefault ( c => c . Name == constantName ) ;
637+ if ( constant == null ) continue ;
638+
639+ // Generate member name by removing SECP256K1_ prefix and converting to PascalCase
640+ var memberName = GetEnumMemberName ( constantName ) ;
641+
642+ // Use numeric value if available, otherwise try to evaluate the expression
643+ var value = constant . NumericValue ? . ToString ( ) ?? EvaluateConstantValue ( constant . Value , api ) ;
644+
645+ var desc = memberDesc ?? constant . Description ;
646+ if ( ! string . IsNullOrEmpty ( desc ) )
647+ {
648+ var cleanDesc = CleanDescription ( desc ) ;
649+ sb . AppendLine ( $ " /// <summary>{ EscapeXml ( cleanDesc ) } </summary>") ;
650+ }
651+ sb . AppendLine ( $ " { memberName } = { value } ,") ;
652+ }
653+
654+ sb . AppendLine ( " }" ) ;
655+ }
656+ }
657+
658+ /// <summary>
659+ /// Converts a constant name like SECP256K1_EC_COMPRESSED to a C# enum member name like Compressed.
660+ /// </summary>
661+ private static string GetEnumMemberName ( string constantName )
662+ {
663+ // Remove SECP256K1_ prefix
664+ var name = constantName ;
665+ if ( name . StartsWith ( "SECP256K1_" ) )
666+ name = name . Substring ( "SECP256K1_" . Length ) ;
667+
668+ // Remove EC_ prefix for EC flags
669+ if ( name . StartsWith ( "EC_" ) )
670+ name = name . Substring ( "EC_" . Length ) ;
671+
672+ // Remove CONTEXT_ prefix for context flags
673+ if ( name . StartsWith ( "CONTEXT_" ) )
674+ name = name . Substring ( "CONTEXT_" . Length ) ;
675+
676+ // Convert SCREAMING_SNAKE_CASE to PascalCase
677+ var parts = name . Split ( '_' ) ;
678+ return string . Join ( "" , parts . Select ( p =>
679+ p . Length > 0 ? char . ToUpper ( p [ 0 ] ) + p . Substring ( 1 ) . ToLower ( ) : "" ) ) ;
680+ }
681+
682+ /// <summary>
683+ /// Evaluates a constant value expression that may reference other constants.
684+ /// </summary>
685+ private static string EvaluateConstantValue ( string value , Secp256k1Api api )
686+ {
687+ // Handle simple numeric values
688+ if ( int . TryParse ( value , out var intVal ) )
689+ return intVal . ToString ( ) ;
690+ if ( value . StartsWith ( "0x" ) && int . TryParse ( value . Substring ( 2 ) , System . Globalization . NumberStyles . HexNumber , null , out intVal ) )
691+ return intVal . ToString ( ) ;
692+
693+ // Handle bit shifts like (1 << 8)
694+ var shiftMatch = System . Text . RegularExpressions . Regex . Match ( value , @"\((\d+)\s*<<\s*(\d+)\)" ) ;
695+ if ( shiftMatch . Success )
696+ {
697+ var baseVal = int . Parse ( shiftMatch . Groups [ 1 ] . Value ) ;
698+ var shift = int . Parse ( shiftMatch . Groups [ 2 ] . Value ) ;
699+ return ( baseVal << shift ) . ToString ( ) ;
700+ }
701+
702+ // Handle expressions that reference other constants like (SECP256K1_FLAGS_TYPE_COMPRESSION | SECP256K1_FLAGS_BIT_COMPRESSION)
703+ var orMatch = System . Text . RegularExpressions . Regex . Match ( value , @"\((\w+)\s*\|\s*(\w+)\)" ) ;
704+ if ( orMatch . Success )
705+ {
706+ var left = ResolveConstantValue ( orMatch . Groups [ 1 ] . Value , api ) ;
707+ var right = ResolveConstantValue ( orMatch . Groups [ 2 ] . Value , api ) ;
708+ if ( left . HasValue && right . HasValue )
709+ return ( left . Value | right . Value ) . ToString ( ) ;
710+ }
711+
712+ // Handle single constant reference like (SECP256K1_FLAGS_TYPE_COMPRESSION)
713+ var singleMatch = System . Text . RegularExpressions . Regex . Match ( value , @"\((\w+)\)" ) ;
714+ if ( singleMatch . Success )
715+ {
716+ var resolved = ResolveConstantValue ( singleMatch . Groups [ 1 ] . Value , api ) ;
717+ if ( resolved . HasValue )
718+ return resolved . Value . ToString ( ) ;
719+ }
720+
721+ // Fallback - return as-is (will likely cause compile error if invalid)
722+ return value ;
723+ }
724+
725+ /// <summary>
726+ /// Resolves a constant name to its numeric value.
727+ /// </summary>
728+ private static long ? ResolveConstantValue ( string constantName , Secp256k1Api api )
729+ {
730+ var constant = api . Constants . FirstOrDefault ( c => c . Name == constantName ) ;
731+ if ( constant == null )
732+ return null ;
733+
734+ if ( constant . NumericValue . HasValue )
735+ return constant . NumericValue . Value ;
736+
737+ // Try to evaluate the expression recursively
738+ var evaluated = EvaluateConstantValue ( constant . Value , api ) ;
739+ if ( long . TryParse ( evaluated , out var result ) )
740+ return result ;
741+
742+ return null ;
743+ }
744+
745+ #endregion
746+
572747 #region Wrapper Generation
573748
574749 // Functions to skip in wrapper generation (need manual implementation or are internal)
@@ -646,6 +821,9 @@ public string GenerateWrappers(Secp256k1Api api)
646821 sb . AppendLine ( "namespace Secp256k1Net" ) ;
647822 sb . AppendLine ( "{" ) ;
648823
824+ // Generate enum types from constants
825+ GenerateEnums ( sb , api ) ;
826+
649827 // Generate user-friendly delegate types for callback functions
650828 GenerateUserFriendlyCallbackDelegates ( sb , api ) ;
651829
@@ -1135,7 +1313,7 @@ private List<WrapperParameter> GetWrapperParameters(FunctionDef func, Dictionary
11351313 } ;
11361314
11371315 // Determine wrapper type and size
1138- DetermineWrapperType ( wrapper , param , structSizes ) ;
1316+ DetermineWrapperType ( wrapper , param , structSizes , func . Name ) ;
11391317
11401318 result . Add ( wrapper ) ;
11411319 }
@@ -1158,7 +1336,7 @@ private List<WrapperParameter> GetWrapperParameters(FunctionDef func, Dictionary
11581336 return result ;
11591337 }
11601338
1161- private void DetermineWrapperType ( WrapperParameter wrapper , ParameterDef param , Dictionary < string , int > structSizes )
1339+ private void DetermineWrapperType ( WrapperParameter wrapper , ParameterDef param , Dictionary < string , int > structSizes , string functionName )
11621340 {
11631341 var cType = param . Type . Trim ( ) ;
11641342 var name = param . Name ;
@@ -1214,6 +1392,15 @@ private void DetermineWrapperType(WrapperParameter wrapper, ParameterDef param,
12141392 return ;
12151393 }
12161394
1395+ // Check for enum mappings for this parameter
1396+ if ( ParameterEnumMappings . TryGetValue ( ( functionName , name ) , out var enumType ) )
1397+ {
1398+ wrapper . WrapperType = enumType ;
1399+ wrapper . WrapperName = SanitizeParamName ( name ) ;
1400+ wrapper . IsEnumParam = true ;
1401+ return ;
1402+ }
1403+
12171404 // Non-pointer primitive types
12181405 if ( ! cType . Contains ( "*" ) )
12191406 {
@@ -1336,6 +1523,11 @@ private static string BuildNativeCallArgs(FunctionDef func, List<WrapperParamete
13361523 var cleanName = param . WrapperName . TrimStart ( '@' ) ;
13371524 args . Add ( $ "{ cleanName } Ptr") ;
13381525 }
1526+ else if ( param . IsEnumParam )
1527+ {
1528+ // Cast enum to uint for native call
1529+ args . Add ( $ "(uint){ param . WrapperName } ") ;
1530+ }
13391531 else
13401532 {
13411533 args . Add ( param . WrapperName ) ;
@@ -1364,6 +1556,7 @@ private class WrapperParameter
13641556 public bool IsOptionalCallback { get ; set ; } // Optional callback/data parameter - pass IntPtr.Zero
13651557 public string ? IsLengthFor { get ; set ; } // If this is a length param, the name of the buffer param it's for
13661558 public string ? LengthForSpanName { get ; set ; } // The wrapper name of the span this length is for (resolved)
1559+ public bool IsEnumParam { get ; set ; } // If true, this param uses an enum type and needs casting to uint
13671560 }
13681561
13691562 /// <summary>
0 commit comments