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 CppCodeGenerator : ICodeGenerator { private readonly Dictionary typeToTypedefMap = new Dictionary { [typeof(DoubleNode)] = Program.Settings.TypeDouble, [typeof(FloatNode)] = Program.Settings.TypeFloat, [typeof(FunctionPtrNode)] = Program.Settings.TypeFunctionPtr, [typeof(Int8Node)] = Program.Settings.TypeInt8, [typeof(Int16Node)] = Program.Settings.TypeInt16, [typeof(Int32Node)] = Program.Settings.TypeInt32, [typeof(Int64Node)] = Program.Settings.TypeInt64, [typeof(Matrix3x3Node)] = Program.Settings.TypeMatrix3x3, [typeof(Matrix3x4Node)] = Program.Settings.TypeMatrix3x4, [typeof(Matrix4x4Node)] = Program.Settings.TypeMatrix4x4, [typeof(UInt8Node)] = Program.Settings.TypeUInt8, [typeof(UInt16Node)] = Program.Settings.TypeUInt16, [typeof(UInt32Node)] = Program.Settings.TypeUInt32, [typeof(UInt64Node)] = Program.Settings.TypeUInt64, [typeof(UTF8TextNode)] = Program.Settings.TypeUTF8Text, [typeof(UTF8TextPtrNode)] = Program.Settings.TypeUTF8TextPtr, [typeof(UTF16TextNode)] = Program.Settings.TypeUTF16Text, [typeof(UTF16TextPtrNode)] = Program.Settings.TypeUTF16TextPtr, [typeof(UTF32TextNode)] = Program.Settings.TypeUTF32Text, [typeof(UTF32TextPtrNode)] = Program.Settings.TypeUTF32PtrText, [typeof(Vector2Node)] = Program.Settings.TypeVector2, [typeof(Vector3Node)] = Program.Settings.TypeVector3, [typeof(Vector4Node)] = Program.Settings.TypeVector4 }; public Language Language => Language.Cpp; public string GenerateCode(IEnumerable classes, ILogger logger) { var sb = new StringBuilder(); sb.AppendLine($"// Created with {Constants.ApplicationName} by {Constants.Author}"); sb.AppendLine(); sb.AppendLine( string.Join( "\n\n", OrderByInheritance(classes).Select(c => { var csb = new StringBuilder(); csb.Append($"class {c.Name}"); bool skipFirstMember = false; var inheritedFromNode = c.Nodes.FirstOrDefault() as ClassInstanceNode; if (inheritedFromNode != null) { skipFirstMember = true; csb.Append(" : public "); csb.Append(inheritedFromNode.InnerNode.Name); } if (!string.IsNullOrEmpty(c.Comment)) { csb.Append($" // {c.Comment}"); } csb.AppendLine(); csb.AppendLine("{"); csb.AppendLine("public:"); csb.AppendLine( string.Join( "\n", YieldMemberDefinitions(c.Nodes.Skip(skipFirstMember ? 1 : 0), logger) .Select(m => MemberDefinitionToString(m)) .Select(s => "\t" + s) ) ); var vtables = c.Nodes.OfType(); if (vtables.Any()) { csb.AppendLine(); csb.AppendLine( string.Join( "\n", vtables.SelectMany(vt => vt.Nodes).OfType().Select(m => $"\tvirtual void {m.MethodName}();") ) ); } csb.Append($"}}; //Size: 0x{c.MemorySize:X04}"); return csb.ToString(); }) ) ); return sb.ToString(); } private IEnumerable OrderByInheritance(IEnumerable classes) { Contract.Requires(classes != null); Contract.Requires(Contract.ForAll(classes, c => c != null)); Contract.Ensures(Contract.Result>() != null); var alreadySeen = new HashSet(); return classes.SelectMany(c => YieldReversedHierarchy(c, alreadySeen)).Distinct(); } private IEnumerable YieldReversedHierarchy(ClassNode node, HashSet alreadySeen) { Contract.Requires(node != null); Contract.Requires(alreadySeen != null); Contract.Requires(Contract.ForAll(alreadySeen, c => c != null)); Contract.Ensures(Contract.Result>() != null); if (!alreadySeen.Add(node)) { yield break; } foreach (var referenceNode in node.Nodes.OfType()) { foreach (var referencedNode in YieldReversedHierarchy(referenceNode.InnerNode as ClassNode, alreadySeen)) { yield return referencedNode; } } yield return node; } 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)); int fill = 0; int fillStart = 0; foreach (var member in members.Where(m => !(m is VTableNode))) { if (member is BaseHexNode) { if (fill == 0) { fillStart = member.Offset.ToInt32(); } fill += member.MemorySize; continue; } if (fill != 0) { yield return new MemberDefinition(Program.Settings.TypePadding, fill, $"pad_{fillStart:X04}", fillStart, string.Empty); fill = 0; } string type; if (typeToTypedefMap.TryGetValue(member.GetType(), out type)) { int count = 0; if (member is BaseTextNode) { count = ((BaseTextNode)member).Length; } yield return new MemberDefinition(member, type, count); } else if (member is BitFieldNode) { switch (((BitFieldNode)member).Bits) { case 8: type = Program.Settings.TypeUInt8; break; case 16: type = Program.Settings.TypeUInt16; break; case 32: type = Program.Settings.TypeUInt32; break; case 64: type = Program.Settings.TypeUInt64; break; } yield return new MemberDefinition(member, type); } else if (member is ClassInstanceArrayNode) { var instanceArray = (ClassInstanceArrayNode)member; yield return new MemberDefinition(member, instanceArray.InnerNode.Name, instanceArray.Count); } else if (member is ClassInstanceNode) { yield return new MemberDefinition(member, ((ClassInstanceNode)member).InnerNode.Name); } else if (member is ClassPtrArrayNode) { var ptrArray = (ClassPtrArrayNode)member; yield return new MemberDefinition(member, $"class {ptrArray.InnerNode.Name}*", ptrArray.Count); } else if (member is ClassPtrNode) { yield return new MemberDefinition(member, $"class {((ClassPtrNode)member).InnerNode.Name}*"); } else { var generator = CustomCodeGenerator.GetGenerator(member, Language); if (generator != null) { yield return generator.GetMemberDefinition(member, Language, logger); } else { logger.Log(LogLevel.Error, $"Skipping node with unhandled type: {member.GetType()}"); } } } if (fill != 0) { yield return new MemberDefinition(Program.Settings.TypePadding, fill, $"pad_{fillStart:X04}", fillStart, string.Empty); } } private string MemberDefinitionToString(MemberDefinition member) { Contract.Requires(member != null); if (member.IsArray) { return $"{member.Type} {member.Name}[{member.ArrayCount}]; //0x{member.Offset:X04} {member.Comment}".Trim(); } else { return $"{member.Type} {member.Name}; //0x{member.Offset:X04} {member.Comment}".Trim(); } } } }