Skip to content

Commit 7f7106d

Browse files
committed
Rewrote C# code generation with the IndentedTextWriter class.
1 parent 87bf38d commit 7f7106d

File tree

1 file changed

+154
-52
lines changed

1 file changed

+154
-52
lines changed

ReClass.NET/CodeGenerator/CSharpCodeGenerator.cs

Lines changed: 154 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
using System;
2+
using System.CodeDom.Compiler;
23
using System.Collections.Generic;
34
using System.Diagnostics.Contracts;
5+
using System.IO;
46
using System.Linq;
5-
using System.Text;
67
using ReClassNET.Extensions;
78
using ReClassNET.Logger;
89
using 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

Comments
 (0)