Skip to content

Add support for string types in C# code generator #174

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Oct 11, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 64 additions & 13 deletions ReClass.NET/CodeGenerator/CSharpCodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public class CSharpCodeGenerator : ICodeGenerator
[typeof(Vector2Node)] = "Vector2",
[typeof(Vector3Node)] = "Vector3",
[typeof(Vector4Node)] = "Vector4"
};
};

public Language Language => Language.CSharp;

Expand Down Expand Up @@ -77,21 +77,42 @@ public string GenerateCode(IReadOnlyList<ClassNode> classes, IReadOnlyList<EnumD
.Where(c => c.Nodes.None(n => n is FunctionNode)) // Skip class which contains FunctionNodes because these are not data classes.
.Distinct();

var unicodeStringClassLengthsToGenerate = new HashSet<int>();

using (var en = classesToWrite.GetEnumerator())
{
if (en.MoveNext())
{
void FindUnicodeStringClasses(IEnumerable<BaseNode> nodes)
{
unicodeStringClassLengthsToGenerate.UnionWith(nodes.OfType<Utf16TextNode>().Select(n => n.Length));
}

FindUnicodeStringClasses(en.Current!.Nodes);

WriteClass(iw, en.Current, logger);

while (en.MoveNext())
{
iw.WriteLine();

FindUnicodeStringClasses(en.Current!.Nodes);

WriteClass(iw, en.Current, logger);
}
}
}

if (unicodeStringClassLengthsToGenerate.Any())
{
foreach (var length in unicodeStringClassLengthsToGenerate)
{
iw.WriteLine();

WriteUnicodeStringClass(iw, length);
}
}

return sw.ToString();
}

Expand Down Expand Up @@ -152,7 +173,7 @@ private static void WriteClass(IndentedTextWriter writer, ClassNode @class, ILog
Contract.Requires(@class != null);
Contract.Requires(logger != null);

writer.WriteLine("[StructLayout(LayoutKind.Explicit)]");
writer.WriteLine("[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]");
writer.Write("public struct ");
writer.Write(@class.Name);

Expand All @@ -171,13 +192,19 @@ private static void WriteClass(IndentedTextWriter writer, ClassNode @class, ILog
.WhereNot(n => n is FunctionNode || n is BaseHexNode);
foreach (var node in nodes)
{
var type = GetTypeDefinition(node);
var (type, attribute) = GetTypeDefinition(node);
if (type != null)
{
writer.Write($"[FieldOffset(0x{node.Offset:X})] public readonly {type} {node.Name};");
if (attribute != null)
{
writer.WriteLine(attribute);
}

writer.WriteLine($"[FieldOffset(0x{node.Offset:X})]");
writer.Write($"public readonly {type} {node.Name};");
if (!string.IsNullOrEmpty(node.Comment))
{
writer.Write(" ");
writer.Write(" //");
writer.Write(node.Comment);
}
writer.WriteLine();
Expand All @@ -193,11 +220,11 @@ private static void WriteClass(IndentedTextWriter writer, ClassNode @class, ILog
}

/// <summary>
/// Gets the type definition for the given node. If the node is not a simple node <c>null</c> is returned.
/// Gets the type definition for the given node. If the node is not expressible <c>null</c> as typename is returned.
/// </summary>
/// <param name="node">The target node.</param>
/// <returns>The type definition for the node or null if no simple type is available.</returns>
private static string GetTypeDefinition(BaseNode node)
/// <returns>The type definition for the node or null as typename if the node is not expressible.</returns>
private static (string typeName, string attribute) GetTypeDefinition(BaseNode node)
{
Contract.Requires(node != null);

Expand All @@ -210,15 +237,39 @@ private static string GetTypeDefinition(BaseNode node)

if (nodeTypeToTypeDefinationMap.TryGetValue(node.GetType(), out var type))
{
return type;
return (type, null);
}

if (node is EnumNode enumNode)
return node switch
{
return enumNode.Enum.Name;
}
EnumNode enumNode => (enumNode.Enum.Name, null),
Utf8TextNode utf8TextNode => ("string", $"[MarshalAs(UnmanagedType.ByValTStr, SizeConst = {utf8TextNode.Length})]"),
Utf16TextNode utf16TextNode => (GetUnicodeStringClassName(utf16TextNode.Length), "[MarshalAs(UnmanagedType.Struct)]"),
_ => (null, null)
};
}

private static string GetUnicodeStringClassName(int length) => $"__UnicodeString{length}";

/// <summary>
/// Writes a helper class for unicode strings with the specific length.
/// </summary>
/// <param name="writer">The writer to output to.</param>
/// <param name="length">The string length for this class.</param>
private static void WriteUnicodeStringClass(IndentedTextWriter writer, int length)
{
var className = GetUnicodeStringClassName(length);

return null;
writer.WriteLine("[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]");
writer.WriteLine($"public struct {className}");
writer.WriteLine("{");
writer.Indent++;
writer.WriteLine($"[MarshalAs(UnmanagedType.ByValTStr, SizeConst = {length})]");
writer.WriteLine("public string Value;");
writer.WriteLine();
writer.WriteLine($"public static implicit operator string({className} value) => value.Value;");
writer.Indent--;
writer.WriteLine("}");
}
}
}