1
1
using System ;
2
+ using System . CodeDom . Compiler ;
2
3
using System . Collections . Generic ;
3
4
using System . Diagnostics . Contracts ;
5
+ using System . IO ;
4
6
using System . Linq ;
5
- using System . Text ;
6
7
using ReClassNET . Extensions ;
7
8
using ReClassNET . Logger ;
8
9
using ReClassNET . Nodes ;
@@ -12,7 +13,7 @@ namespace ReClassNET.CodeGenerator
12
13
{
13
14
public class CSharpCodeGenerator : ICodeGenerator
14
15
{
15
- private static readonly Dictionary < Type , string > typeToTypedefMap = new Dictionary < Type , string >
16
+ private static readonly Dictionary < Type , string > nodeTypeToTypeDefinationMap = new Dictionary < Type , string >
16
17
{
17
18
[ typeof ( DoubleNode ) ] = "double" ,
18
19
[ typeof ( FloatNode ) ] = "float" ,
@@ -38,76 +39,172 @@ public class CSharpCodeGenerator : ICodeGenerator
38
39
39
40
public string GenerateCode ( IReadOnlyList < ClassNode > classes , IReadOnlyList < EnumDescription > enums , ILogger logger )
40
41
{
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 ( ) )
53
54
{
54
- var csb = new StringBuilder ( ) ;
55
+ if ( en . MoveNext ( ) )
56
+ {
57
+ WriteEnum ( iw , en . Current ) ;
58
+
59
+ while ( en . MoveNext ( ) )
60
+ {
61
+ iw . WriteLine ( ) ;
55
62
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 ( ) )
59
77
{
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
+ }
61
86
}
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
+ }
80
92
}
81
93
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 )
83
100
{
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 ) ;
86
149
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 ) ) ;
89
150
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 ) )
91
156
{
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 ) ;
93
171
if ( type != null )
94
172
{
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 ( ) ;
99
189
}
100
190
else
101
191
{
102
- logger . Log ( LogLevel . Warning , $ "Skipping node with unhandled type: { member . GetType ( ) } ") ;
192
+ logger . Log ( LogLevel . Warning , $ "Skipping node with unhandled type: { node . GetType ( ) } ") ;
103
193
}
104
194
}
195
+
196
+ writer . Indent -- ;
197
+ writer . WriteLine ( "}" ) ;
105
198
}
106
199
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 )
108
206
{
109
207
Contract . Requires ( node != null ) ;
110
- Contract . Requires ( logger != null ) ;
111
208
112
209
if ( node is BitFieldNode bitFieldNode )
113
210
{
@@ -116,11 +213,16 @@ private static string GetTypeForNode(BaseNode node, ILogger logger)
116
213
node = underlayingNode ;
117
214
}
118
215
119
- if ( typeToTypedefMap . TryGetValue ( node . GetType ( ) , out var type ) )
216
+ if ( nodeTypeToTypeDefinationMap . TryGetValue ( node . GetType ( ) , out var type ) )
120
217
{
121
218
return type ;
122
219
}
123
220
221
+ if ( node is EnumNode enumNode )
222
+ {
223
+ return enumNode . Enum . Name ;
224
+ }
225
+
124
226
return null ;
125
227
}
126
228
}
0 commit comments