// Outputs a DEFINE_IL2CPP_ARG_TYPE call for all root or non-generic types defined by this file private void DefineIl2CppArgTypes(CppStreamWriter writer, CppTypeContext context) { var type = context.LocalType; // DEFINE_IL2CPP_ARG_TYPE var(ns, il2cppName) = type.This.GetIl2CppName(); // For Name and Namespace here, we DO want all the `, /, etc if (!type.This.IsGeneric) { IncludeIl2CppTypeCheckIfNotAlready(writer); string fullName = context.GetCppName(context.LocalType.This, true, true, CppTypeContext.NeedAs.Definition, CppTypeContext.ForceAsType.Literal) ?? throw new UnresolvedTypeException(context.LocalType.This, context.LocalType.This); if (context.LocalType.Info.Refness == Refness.ReferenceType) { fullName += "*"; } writer.WriteLine($"DEFINE_IL2CPP_ARG_TYPE({fullName}, \"{ns}\", \"{il2cppName}\");"); } else if (type.This.DeclaringType is null || !type.This.DeclaringType.IsGeneric) { IncludeIl2CppTypeCheckIfNotAlready(writer); string templateName = context.GetCppName(context.LocalType.This, true, false, CppTypeContext.NeedAs.Declaration, CppTypeContext.ForceAsType.Literal) ?? throw new UnresolvedTypeException(context.LocalType.This, context.LocalType.This); var structStr = context.LocalType.Info.Refness == Refness.ReferenceType ? "CLASS" : "STRUCT"; writer.WriteLine($"DEFINE_IL2CPP_ARG_TYPE_GENERIC_{structStr}({templateName}, \"{ns}\", \"{il2cppName}\");"); } foreach (var nested in context.NestedContexts.Where(n => n.InPlace)) { DefineIl2CppArgTypes(writer, nested); } }
internal void Serialize(CppTypeContext context) { var data = context.LocalType; var headerLocation = Path.Combine(_config.OutputDirectory, _config.OutputHeaderDirectory, context.HeaderFileName); Directory.CreateDirectory(Path.GetDirectoryName(headerLocation)); using var ms = new MemoryStream(); using var rawWriter = new StreamWriter(ms); using var writer = new CppStreamWriter(rawWriter, " "); // Write header writer.WriteComment($"Autogenerated from {nameof(CppHeaderCreator)}"); writer.WriteComment("Created by Sc2ad"); writer.WriteComment("========================================================================="); writer.WriteLine("#pragma once"); // TODO: determine when/if we need this // For sizes that are valid, we ALSO want to write with pack of 1 // Invalid sizes are ignored. // Write SerializerContext and actual type try { _serializer.Serialize(writer, context, true); } catch (UnresolvedTypeException e) { if (_config.UnresolvedTypeExceptionHandling?.TypeHandling == UnresolvedTypeExceptionHandling.DisplayInFile) { writer.WriteComment("Unresolved type exception!"); writer.WriteLine("/*"); writer.WriteLine(e); writer.WriteLine("*/"); } else if (_config.UnresolvedTypeExceptionHandling?.TypeHandling == UnresolvedTypeExceptionHandling.SkipIssue) { return; } else if (_config.UnresolvedTypeExceptionHandling?.TypeHandling == UnresolvedTypeExceptionHandling.Elevate) { throw new InvalidOperationException($"Cannot elevate {e} to a parent type- there is no parent type!"); } } // End the namespace writer.CloseDefinition(); hasIl2CppTypeCheckInclude = context.NeedIl2CppUtilsFunctionsInHeader; if (data.This.Namespace == "System" && data.This.Name == "ValueType") { IncludeIl2CppTypeCheckIfNotAlready(writer); writer.WriteLine("template<class T>"); writer.WriteLine("struct is_value_type<T, typename std::enable_if_t<std::is_base_of_v<System::ValueType, T>>> : std::true_type{};"); } DefineIl2CppArgTypes(writer, context); writer.WriteLine("#include \"extern/beatsaber-hook/shared/utils/il2cpp-utils-methods.hpp\""); _serializer.WritePostSerializeMethods(writer, context, true); writer.Flush(); writer.WriteIfDifferent(headerLocation, context); }
internal void WriteIfDifferent(string filePath, CppTypeContext context) { if (!Written.Add(Path.GetFullPath(filePath))) { throw new InvalidOperationException($"Was about to overwrite existing file: {filePath} with context: {context.LocalType.This}"); } if (WriteIfDifferent(filePath, rawWriter.BaseStream)) { NumChangedFiles++; } }
internal void Serialize(CppTypeContext context) { var sourceLocation = _config.OneSourceFile ? Path.Combine(_config.OutputDirectory, _config.OutputSourceDirectory, context.CppFileName) : overallSourceLocation; CppStreamWriter?writer = overallStreamWriter; if (writer is null) { Directory.CreateDirectory(Path.GetDirectoryName(sourceLocation)); using var ms = new MemoryStream(); using var rawWriter = new StreamWriter(ms); writer = new CppStreamWriter(rawWriter, " "); } // Write header writer.WriteComment($"Autogenerated from {nameof(CppSourceCreator)}"); writer.WriteComment($"Created by Sc2ad"); writer.WriteComment("========================================================================="); try { // Write SerializerContext and actual type _serializer.Serialize(writer, context, false); } catch (UnresolvedTypeException e) { if (_config.UnresolvedTypeExceptionHandling?.TypeHandling == UnresolvedTypeExceptionHandling.DisplayInFile) { writer.WriteLine("// Unresolved type exception!"); writer.WriteLine("/*"); writer.WriteLine(e); writer.WriteLine("*/"); } else if (_config.UnresolvedTypeExceptionHandling?.TypeHandling == UnresolvedTypeExceptionHandling.SkipIssue) { return; } else if (_config.UnresolvedTypeExceptionHandling?.TypeHandling == UnresolvedTypeExceptionHandling.Elevate) { throw new InvalidOperationException($"Cannot elevate {e} to a parent type- there is no parent type!"); } } writer.Flush(); if (overallStreamWriter is null) { writer.WriteIfDifferent(sourceLocation, context); writer.Close(); writer.Dispose(); } }
// Resolve the field into context here public override void PreSerialize(CppTypeContext context, IField field) { if (context is null) { throw new ArgumentNullException(nameof(context)); } if (field is null) { throw new ArgumentNullException(nameof(field)); } // In this situation, if the type is a pointer, we can simply forward declare. // Otherwise, we need to include the corresponding file. This must be resolved via context // If the resolved type name is null, we won't serialize this field // First, resolve the field type to see if it exists // If it does, because it is a field, we can FD it if it is a pointer // If it is not a pointer, then we need to include it // If it is a nested class, we need to deal with some stuff (maybe) var resolvedName = context.GetCppName(field.Type, true); if (!string.IsNullOrEmpty(resolvedName)) { Resolved(field); } // In order to ensure we get an UnresolvedTypeException when we serialize ResolvedTypeNames.Add(field, resolvedName); string SafeFieldName() { var name = field.Name; if (name.EndsWith("k__BackingField")) { name = name.Split(angleBrackets, StringSplitOptions.RemoveEmptyEntries)[0]; } name = string.Join("$", name.Split(angleBrackets)).Trim('_'); if (char.IsDigit(name[0])) { name = "_" + name; } return(_config.SafeName(name)); } SafeFieldNames.Add(field, SafeFieldName()); }
internal static void CreateConversionOperator(CppDataSerializer ser, ITypeData type, CppTypeContext self) { if (self._soloFieldConversionOperator != null) { return; } FieldConversionOperator?parentFieldConversionOperator = null; if (type.Parent != null) { var resolved = type.Parent.Resolve(self.Types); if (resolved is null) { throw new UnresolvedTypeException(type.This, type.Parent); } var parentContext = CppDataSerializer.TypeToContext[resolved]; CreateConversionOperator(ser, resolved, parentContext); parentFieldConversionOperator = parentContext._soloFieldConversionOperator; } self._soloFieldConversionOperator = new FieldConversionOperator(type, parentFieldConversionOperator); }
public abstract void PreSerialize(CppTypeContext context, T obj);