Skip to content

Commit 075d0be

Browse files
authored
Merge pull request #174 from ReClassNET/FixCodeGenerator
Add support for string types in C# code generator
2 parents fd4d4b3 + fdc0748 commit 075d0be

File tree

1 file changed

+64
-13
lines changed

1 file changed

+64
-13
lines changed

ReClass.NET/CodeGenerator/CSharpCodeGenerator.cs

Lines changed: 64 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)