11using System ;
2+ using System . CodeDom . Compiler ;
23using System . Collections . Generic ;
34using System . Diagnostics . Contracts ;
5+ using System . IO ;
46using System . Linq ;
5- using System . Text ;
67using ReClassNET . Extensions ;
78using ReClassNET . Logger ;
89using ReClassNET . Nodes ;
@@ -12,7 +13,7 @@ namespace ReClassNET.CodeGenerator
1213{
1314 public class CSharpCodeGenerator : ICodeGenerator
1415 {
15- private static readonly Dictionary < Type , string > typeToTypedefMap = new Dictionary < Type , string >
16+ private static readonly Dictionary < Type , string > nodeTypeToTypeDefinationMap = new Dictionary < Type , string >
1617 {
1718 [ typeof ( DoubleNode ) ] = "double" ,
1819 [ typeof ( FloatNode ) ] = "float" ,
@@ -38,76 +39,172 @@ public class CSharpCodeGenerator : ICodeGenerator
3839
3940 public string GenerateCode ( IReadOnlyList < ClassNode > classes , IReadOnlyList < EnumDescription > enums , ILogger logger )
4041 {
41- var sb = new StringBuilder ( ) ;
42- sb . AppendLine ( $ "// Created with { Constants . ApplicationName } by { Constants . Author } " ) ;
43- sb . AppendLine ( ) ;
44- sb . AppendLine ( "// Warning: The code generator doesn't support all node types!" ) ;
45- sb . AppendLine ( ) ;
46- sb . AppendLine ( "using System.Runtime.InteropServices;" ) ;
47- sb . AppendLine ( ) ;
48-
49- sb . Append (
50- string . Join (
51- Environment . NewLine + Environment . NewLine ,
52- classes . Select ( c =>
42+ using ( var sw = new StringWriter ( ) )
43+ {
44+ using ( var iw = new IndentedTextWriter ( sw , " \t " ) )
45+ {
46+ iw . WriteLine ( $ "// Created with { Constants . ApplicationName } { Constants . ApplicationVersion } by { Constants . Author } " ) ;
47+ iw . WriteLine ( ) ;
48+ iw . WriteLine ( "// Warning: The C# code generator doesn't support all node types!" ) ;
49+ iw . WriteLine ( ) ;
50+ iw . WriteLine ( "using System.Runtime.InteropServices;" ) ;
51+ iw . WriteLine ( ) ;
52+
53+ using ( var en = enums . GetEnumerator ( ) )
5354 {
54- var csb = new StringBuilder ( ) ;
55+ if ( en . MoveNext ( ) )
56+ {
57+ WriteEnum ( iw , en . Current ) ;
58+
59+ while ( en . MoveNext ( ) )
60+ {
61+ iw . WriteLine ( ) ;
5562
56- csb . AppendLine ( "[StructLayout(LayoutKind.Explicit)]" ) ;
57- csb . Append ( $ "struct { c . Name } ") ;
58- if ( ! string . IsNullOrEmpty ( c . Comment ) )
63+ WriteEnum ( iw , en . Current ) ;
64+ }
65+
66+ iw . WriteLine ( ) ;
67+ }
68+ }
69+
70+ var classesToWrite = classes
71+ . Where ( c => c . Nodes . None ( n => n is FunctionNode ) ) // Skip class which contains FunctionNodes because these are not data classes.
72+ . Distinct ( ) ;
73+
74+ using ( var en = classesToWrite . GetEnumerator ( ) )
75+ {
76+ if ( en . MoveNext ( ) )
5977 {
60- csb . Append ( $ " // { c . Comment } ") ;
78+ WriteClass ( iw , en . Current , logger ) ;
79+
80+ while ( en . MoveNext ( ) )
81+ {
82+ iw . WriteLine ( ) ;
83+
84+ WriteClass ( iw , en . Current , logger ) ;
85+ }
6186 }
62- csb . AppendLine ( ) ;
63-
64- csb . AppendLine ( "{" ) ;
65-
66- csb . AppendLine (
67- string . Join (
68- Environment . NewLine + Environment . NewLine ,
69- GetTypeDeclerationsForNodes ( c . Nodes , logger )
70- . Select ( t => $ "\t { t . Item1 } { Environment . NewLine } \t { t . Item2 } ")
71- )
72- ) ;
73- csb . Append ( "}" ) ;
74- return csb . ToString ( ) ;
75- } )
76- )
77- ) ;
78-
79- return sb . ToString ( ) ;
87+ }
88+ }
89+
90+ return sw . ToString ( ) ;
91+ }
8092 }
8193
82- private static IEnumerable < Tuple < string , string > > GetTypeDeclerationsForNodes ( IEnumerable < BaseNode > members , ILogger logger )
94+ /// <summary>
95+ /// Outputs the C# code for the given enum to the <see cref="TextWriter"/> instance.
96+ /// </summary>
97+ /// <param name="writer">The writer to output to.</param>
98+ /// <param name="enum">The enum to output.</param>
99+ private static void WriteEnum ( IndentedTextWriter writer , EnumDescription @enum )
83100 {
84- Contract . Requires ( members != null ) ;
85- Contract . Requires ( Contract . ForAll ( members , m => m != null ) ) ;
101+ Contract . Requires ( writer != null ) ;
102+ Contract . Requires ( @enum != null ) ;
103+
104+ writer . Write ( $ "enum { @enum . Name } : ") ;
105+ switch ( @enum . Size )
106+ {
107+ case EnumDescription . UnderlyingTypeSize . OneByte :
108+ writer . WriteLine ( nodeTypeToTypeDefinationMap [ typeof ( Int8Node ) ] ) ;
109+ break ;
110+ case EnumDescription . UnderlyingTypeSize . TwoBytes :
111+ writer . WriteLine ( nodeTypeToTypeDefinationMap [ typeof ( Int16Node ) ] ) ;
112+ break ;
113+ case EnumDescription . UnderlyingTypeSize . FourBytes :
114+ writer . WriteLine ( nodeTypeToTypeDefinationMap [ typeof ( Int32Node ) ] ) ;
115+ break ;
116+ case EnumDescription . UnderlyingTypeSize . EightBytes :
117+ writer . WriteLine ( nodeTypeToTypeDefinationMap [ typeof ( Int64Node ) ] ) ;
118+ break ;
119+ }
120+ writer . WriteLine ( "{" ) ;
121+ writer . Indent ++ ;
122+ for ( var j = 0 ; j < @enum . Values . Count ; ++ j )
123+ {
124+ var kv = @enum . Values [ j ] ;
125+
126+ writer . Write ( kv . Key ) ;
127+ writer . Write ( " = " ) ;
128+ writer . Write ( kv . Value ) ;
129+ if ( j < @enum . Values . Count - 1 )
130+ {
131+ writer . Write ( "," ) ;
132+ }
133+ writer . WriteLine ( ) ;
134+ }
135+ writer . Indent -- ;
136+ writer . WriteLine ( "};" ) ;
137+ }
138+
139+ /// <summary>
140+ /// Outputs the C# code for the given class to the <see cref="TextWriter"/> instance.
141+ /// </summary>
142+ /// <param name="writer">The writer to output to.</param>
143+ /// <param name="class">The class to output.</param>
144+ /// <param name="logger">The logger.</param>
145+ private static void WriteClass ( IndentedTextWriter writer , ClassNode @class , ILogger logger )
146+ {
147+ Contract . Requires ( writer != null ) ;
148+ Contract . Requires ( @class != null ) ;
86149 Contract . Requires ( logger != null ) ;
87- Contract . Ensures ( Contract . Result < IEnumerable < Tuple < string , string > > > ( ) != null ) ;
88- Contract . Ensures ( Contract . ForAll ( Contract . Result < IEnumerable < Tuple < string , string > > > ( ) , t => t != null ) ) ;
89150
90- foreach ( var member in members . WhereNot ( n => n is BaseHexNode ) )
151+ writer . WriteLine ( "[StructLayout(LayoutKind.Explicit)]" ) ;
152+ writer . Write ( "class " ) ;
153+ writer . Write ( @class . Name ) ;
154+
155+ if ( ! string . IsNullOrEmpty ( @class . Comment ) )
91156 {
92- var type = GetTypeForNode ( member , logger ) ;
157+ writer . Write ( " // " ) ;
158+ writer . Write ( @class . Comment ) ;
159+ }
160+
161+ writer . WriteLine ( ) ;
162+
163+ writer . WriteLine ( "{" ) ;
164+ writer . Indent ++ ;
165+
166+ var nodes = @class . Nodes
167+ . WhereNot ( n => n is FunctionNode || n is BaseHexNode ) ;
168+ foreach ( var node in nodes )
169+ {
170+ var type = GetTypeDefinition ( node ) ;
93171 if ( type != null )
94172 {
95- yield return Tuple . Create (
96- $ "[FieldOffset({ member . Offset . ToInt32 ( ) } )]",
97- $ "public { type } { member . Name } ; //0x{ member . Offset . ToInt32 ( ) : X04} { member . Comment } ". Trim ( )
98- ) ;
173+ writer . Write ( "[FieldOffset(" ) ;
174+ writer . Write ( node . Offset . ToInt32 ( ) ) ;
175+ writer . WriteLine ( ")]" ) ;
176+
177+ writer . Write ( "public " ) ;
178+ writer . Write ( type ) ;
179+ writer . Write ( " " ) ;
180+ writer . Write ( node . Name ) ;
181+ writer . Write ( "; //0x" ) ;
182+ writer . Write ( $ "{ node . Offset . ToInt32 ( ) : X04} ") ;
183+ if ( ! string . IsNullOrEmpty ( node . Comment ) )
184+ {
185+ writer . Write ( " " ) ;
186+ writer . Write ( node . Comment ) ;
187+ }
188+ writer . WriteLine ( ) ;
99189 }
100190 else
101191 {
102- logger . Log ( LogLevel . Warning , $ "Skipping node with unhandled type: { member . GetType ( ) } ") ;
192+ logger . Log ( LogLevel . Warning , $ "Skipping node with unhandled type: { node . GetType ( ) } ") ;
103193 }
104194 }
195+
196+ writer . Indent -- ;
197+ writer . WriteLine ( "}" ) ;
105198 }
106199
107- private static string GetTypeForNode ( BaseNode node , ILogger logger )
200+ /// <summary>
201+ /// Gets the type definition for the given node. If the node is not a simple node <c>null</c> is returned.
202+ /// </summary>
203+ /// <param name="node">The target node.</param>
204+ /// <returns>The type definition for the node or null if no simple type is available.</returns>
205+ private static string GetTypeDefinition ( BaseNode node )
108206 {
109207 Contract . Requires ( node != null ) ;
110- Contract . Requires ( logger != null ) ;
111208
112209 if ( node is BitFieldNode bitFieldNode )
113210 {
@@ -116,11 +213,16 @@ private static string GetTypeForNode(BaseNode node, ILogger logger)
116213 node = underlayingNode ;
117214 }
118215
119- if ( typeToTypedefMap . TryGetValue ( node . GetType ( ) , out var type ) )
216+ if ( nodeTypeToTypeDefinationMap . TryGetValue ( node . GetType ( ) , out var type ) )
120217 {
121218 return type ;
122219 }
123220
221+ if ( node is EnumNode enumNode )
222+ {
223+ return enumNode . Enum . Name ;
224+ }
225+
124226 return null ;
125227 }
126228 }
0 commit comments