@@ -37,7 +37,7 @@ public class CSharpCodeGenerator : ICodeGenerator
3737 [ typeof ( Vector2Node ) ] = "Vector2" ,
3838 [ typeof ( Vector3Node ) ] = "Vector3" ,
3939 [ typeof ( Vector4Node ) ] = "Vector4"
40- } ;
40+ } ;
4141
4242 public Language Language => Language . CSharp ;
4343
@@ -77,21 +77,42 @@ public string GenerateCode(IReadOnlyList<ClassNode> classes, IReadOnlyList<EnumD
7777 . Where ( c => c . Nodes . None ( n => n is FunctionNode ) ) // Skip class which contains FunctionNodes because these are not data classes.
7878 . Distinct ( ) ;
7979
80+ var unicodeStringClassLengthsToGenerate = new HashSet < int > ( ) ;
81+
8082 using ( var en = classesToWrite . GetEnumerator ( ) )
8183 {
8284 if ( en . MoveNext ( ) )
8385 {
86+ void FindUnicodeStringClasses ( IEnumerable < BaseNode > nodes )
87+ {
88+ unicodeStringClassLengthsToGenerate . UnionWith ( nodes . OfType < Utf16TextNode > ( ) . Select ( n => n . Length ) ) ;
89+ }
90+
91+ FindUnicodeStringClasses ( en . Current ! . Nodes ) ;
92+
8493 WriteClass ( iw , en . Current , logger ) ;
8594
8695 while ( en . MoveNext ( ) )
8796 {
8897 iw . WriteLine ( ) ;
8998
99+ FindUnicodeStringClasses ( en . Current ! . Nodes ) ;
100+
90101 WriteClass ( iw , en . Current , logger ) ;
91102 }
92103 }
93104 }
94105
106+ if ( unicodeStringClassLengthsToGenerate . Any ( ) )
107+ {
108+ foreach ( var length in unicodeStringClassLengthsToGenerate )
109+ {
110+ iw . WriteLine ( ) ;
111+
112+ WriteUnicodeStringClass ( iw , length ) ;
113+ }
114+ }
115+
95116 return sw . ToString ( ) ;
96117 }
97118
@@ -152,7 +173,7 @@ private static void WriteClass(IndentedTextWriter writer, ClassNode @class, ILog
152173 Contract . Requires ( @class != null ) ;
153174 Contract . Requires ( logger != null ) ;
154175
155- writer . WriteLine ( "[StructLayout(LayoutKind.Explicit)]" ) ;
176+ writer . WriteLine ( "[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi )]" ) ;
156177 writer . Write ( "public struct " ) ;
157178 writer . Write ( @class . Name ) ;
158179
@@ -171,13 +192,19 @@ private static void WriteClass(IndentedTextWriter writer, ClassNode @class, ILog
171192 . WhereNot ( n => n is FunctionNode || n is BaseHexNode ) ;
172193 foreach ( var node in nodes )
173194 {
174- var type = GetTypeDefinition ( node ) ;
195+ var ( type , attribute ) = GetTypeDefinition ( node ) ;
175196 if ( type != null )
176197 {
177- writer . Write ( $ "[FieldOffset(0x{ node . Offset : X} )] public readonly { type } { node . Name } ;") ;
198+ if ( attribute != null )
199+ {
200+ writer . WriteLine ( attribute ) ;
201+ }
202+
203+ writer . WriteLine ( $ "[FieldOffset(0x{ node . Offset : X} )]") ;
204+ writer . Write ( $ "public readonly { type } { node . Name } ;") ;
178205 if ( ! string . IsNullOrEmpty ( node . Comment ) )
179206 {
180- writer . Write ( " " ) ;
207+ writer . Write ( " // " ) ;
181208 writer . Write ( node . Comment ) ;
182209 }
183210 writer . WriteLine ( ) ;
@@ -193,11 +220,11 @@ private static void WriteClass(IndentedTextWriter writer, ClassNode @class, ILog
193220 }
194221
195222 /// <summary>
196- /// Gets the type definition for the given node. If the node is not a simple node <c>null</c> is returned.
223+ /// Gets the type definition for the given node. If the node is not expressible <c>null</c> as typename is returned.
197224 /// </summary>
198225 /// <param name="node">The target node.</param>
199- /// <returns>The type definition for the node or null if no simple type is available .</returns>
200- private static string GetTypeDefinition ( BaseNode node )
226+ /// <returns>The type definition for the node or null as typename if the node is not expressible .</returns>
227+ private static ( string typeName , string attribute ) GetTypeDefinition ( BaseNode node )
201228 {
202229 Contract . Requires ( node != null ) ;
203230
@@ -210,15 +237,39 @@ private static string GetTypeDefinition(BaseNode node)
210237
211238 if ( nodeTypeToTypeDefinationMap . TryGetValue ( node . GetType ( ) , out var type ) )
212239 {
213- return type ;
240+ return ( type , null ) ;
214241 }
215242
216- if ( node is EnumNode enumNode )
243+ return node switch
217244 {
218- return enumNode . Enum . Name ;
219- }
245+ EnumNode enumNode => ( enumNode . Enum . Name , null ) ,
246+ Utf8TextNode utf8TextNode => ( "string" , $ "[MarshalAs(UnmanagedType.ByValTStr, SizeConst = { utf8TextNode . Length } )]") ,
247+ Utf16TextNode utf16TextNode => ( GetUnicodeStringClassName ( utf16TextNode . Length ) , "[MarshalAs(UnmanagedType.Struct)]" ) ,
248+ _ => ( null , null )
249+ } ;
250+ }
251+
252+ private static string GetUnicodeStringClassName ( int length ) => $ "__UnicodeString{ length } ";
253+
254+ /// <summary>
255+ /// Writes a helper class for unicode strings with the specific length.
256+ /// </summary>
257+ /// <param name="writer">The writer to output to.</param>
258+ /// <param name="length">The string length for this class.</param>
259+ private static void WriteUnicodeStringClass ( IndentedTextWriter writer , int length )
260+ {
261+ var className = GetUnicodeStringClassName ( length ) ;
220262
221- return null ;
263+ writer . WriteLine ( "[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]" ) ;
264+ writer . WriteLine ( $ "public struct { className } ") ;
265+ writer . WriteLine ( "{" ) ;
266+ writer . Indent ++ ;
267+ writer . WriteLine ( $ "[MarshalAs(UnmanagedType.ByValTStr, SizeConst = { length } )]") ;
268+ writer . WriteLine ( "public string Value;" ) ;
269+ writer . WriteLine ( ) ;
270+ writer . WriteLine ( $ "public static implicit operator string({ className } value) => value.Value;") ;
271+ writer . Indent -- ;
272+ writer . WriteLine ( "}" ) ;
222273 }
223274 }
224275}
0 commit comments