using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using ReClassNET.Logger;
using ReClassNET.Nodes;
namespace ReClassNET.CodeGenerator
{
class CSharpCodeGenerator : ICodeGenerator
{
private readonly Dictionary typeToTypedefMap = new Dictionary
{
[typeof(BoolNode)] = "bool",
[typeof(DoubleNode)] = "double",
[typeof(FloatNode)] = "float",
[typeof(Int8Node)] = "sbyte",
[typeof(Int16Node)] = "short",
[typeof(Int32Node)] = "int",
[typeof(Int64Node)] = "long",
[typeof(UInt8Node)] = "byte",
[typeof(UInt16Node)] = "ushort",
[typeof(UInt32Node)] = "uint",
[typeof(UInt64Node)] = "ulong",
[typeof(FunctionPtrNode)] = "IntPtr",
[typeof(UTF8TextPtrNode)] = "IntPtr",
[typeof(UTF16TextPtrNode)] = "IntPtr",
[typeof(UTF32TextPtrNode)] = "IntPtr",
[typeof(ClassPtrNode)] = "IntPtr",
[typeof(VTableNode)] = "IntPtr"
};
public Language Language => Language.CSharp;
public string GenerateCode(IEnumerable classes, ILogger logger)
{
var sb = new StringBuilder();
sb.AppendLine($"// Created with {Constants.ApplicationName} by {Constants.Author}");
sb.AppendLine();
sb.AppendLine("// Warning: The code doesn't contain arrays and instances!");
sb.AppendLine();
sb.AppendLine("using System.Runtime.InteropServices;");
sb.AppendLine();
sb.Append(
string.Join(
"\n\n",
classes.Select(c =>
{
var csb = new StringBuilder();
csb.AppendLine("[StructLayout(LayoutKind.Explicit)]");
csb.Append($"struct {c.Name}");
if (!string.IsNullOrEmpty(c.Comment))
{
csb.Append($" // {c.Comment}");
}
csb.AppendLine();
csb.AppendLine("{");
csb.AppendLine(
string.Join(
"\n\n",
YieldMemberDefinitions(c.Nodes, logger)
.Select(m => $"\t{GetFieldDecorator(m)}\n\t{GetFieldDefinition(m)}")
)
);
csb.Append("}");
return csb.ToString();
})
)
);
return sb.ToString();
}
private IEnumerable YieldMemberDefinitions(IEnumerable members, ILogger logger)
{
Contract.Requires(members != null);
Contract.Requires(Contract.ForAll(members, m => m != null));
Contract.Ensures(Contract.Result>() != null);
Contract.Ensures(Contract.ForAll(Contract.Result>(), d => d != null));
foreach (var member in members.Where(n => !(n is BaseHexNode)))
{
if (member is BitFieldNode)
{
string type;
switch (((BitFieldNode)member).Bits)
{
default:
case 8:
type = typeToTypedefMap[typeof(UInt8Node)];
break;
case 16:
type = typeToTypedefMap[typeof(UInt16Node)];
break;
case 32:
type = typeToTypedefMap[typeof(UInt32Node)];
break;
case 64:
type = typeToTypedefMap[typeof(UInt64Node)];
break;
}
yield return new MemberDefinition(member, type);
}
else
{
string type;
if (typeToTypedefMap.TryGetValue(member.GetType(), out type))
{
yield return new MemberDefinition(member, type);
}
else
{
var generator = CustomCodeGenerator.GetGenerator(member, Language);
if (generator != null)
{
yield return generator.GetMemberDefinition(member, Language, logger);
continue;
}
logger.Log(LogLevel.Error, $"Skipping node with unhandled type: {member.GetType()}");
}
}
}
}
private string GetFieldDecorator(MemberDefinition member)
{
Contract.Requires(member != null);
return $"[FieldOffset({member.Offset})]";
}
private string GetFieldDefinition(MemberDefinition member)
{
Contract.Requires(member != null);
return $"public {member.Type} {member.Name}; //0x{member.Offset:X04} {member.Comment}".Trim();
}
}
}