pkNX/pkNX.Structures.FlatBuffers.Reflection/ReflectionDumpSchema.cs
2024-03-04 20:19:09 -06:00

226 lines
7.8 KiB
C#

using System;
using System.IO;
using System.Linq;
using FlatSharp;
namespace pkNX.Structures.FlatBuffers.Reflection;
public static class SchemaDump
{
/// <summary>
/// Exports fbs, cs
/// </summary>
/// <param name="data"></param>
/// <param name="filePath"></param>
/// <param name="dir"></param>
/// <param name="settings"></param>
public static Schema HandleReflection(Memory<byte> data, ReadOnlySpan<char> filePath, string dir, SchemaDumpSettings settings)
{
var schema = Schema.Serializer.Parse(data, FlatBufferDeserializationOption.GreedyMutable);
DumpSchema(schema, filePath, dir, settings);
return schema;
}
public static void DumpSchema(Schema schema, ReadOnlySpan<char> filePath, string dir, SchemaDumpSettings settings)
{
var rootName = schema.RootTable?.Name ?? Path.GetFileNameWithoutExtension(filePath);
var fileCS = Path.Combine(dir, $"{rootName}.cs");
using var fsCS = File.Create(fileCS);
using var cs = new StreamWriter(fsCS);
var fileFBS = Path.Combine(dir, $"{rootName}.fbs");
using var fsFBS = File.Create(fileFBS);
using var fbs = new StreamWriter(fsFBS);
DumpSchema(schema, fbs, cs, settings);
}
public static void DumpSchema(Memory<byte> data, TextWriter fbs, TextWriter cs, SchemaDumpSettings settings)
{
var schema = Schema.Serializer.Parse(data, FlatBufferDeserializationOption.GreedyMutable);
DumpSchema(schema, fbs, cs, settings);
}
public static void DumpSchema(Schema schema, TextWriter fbs, TextWriter cs, SchemaDumpSettings settings)
{
WriteHeaderFBS(schema, fbs, settings.FileNamespace);
WriteHeaderCS(schema, cs, settings.FileNamespace);
DumpObjects(schema, fbs, cs, settings.StripNamespace);
DumpEnums(schema, fbs, settings.StripNamespace);
if (schema.RootTable?.Name is { } x)
fbs.WriteLine($"root_type {x};");
}
private static void WriteHeaderFBS(Schema schema, TextWriter fbs, ReadOnlySpan<char> fileNameSpace)
{
fbs.WriteLine($"namespace {fileNameSpace};");
fbs.WriteLine();
fbs.WriteLine("attribute \"fs_vector\";");
fbs.WriteLine("attribute \"fs_serializer\";");
fbs.WriteLine("attribute \"fs_valueStruct\";");
fbs.WriteLine("attribute \"fs_nonVirtual\";");
fbs.WriteLine("attribute \"fs_unsafeStructVector\";");
fbs.WriteLine();
fbs.WriteLine("// Generated by FlatSharp");
fbs.WriteLine($"// FileIdentifier: {schema.FileIdent}");
fbs.WriteLine($"// FileExtension: {schema.FileExt}");
fbs.WriteLine($"// Object Count: {schema.Objects.Count}");
fbs.WriteLine($"// Enum Count: {schema.Enums.Count}");
}
private static void WriteHeaderCS(Schema schema, TextWriter cs, ReadOnlySpan<char> fileNameSpace)
{
cs.WriteLine("using System.Collections.Generic;");
cs.WriteLine("using System.ComponentModel;");
cs.WriteLine("using FlatSharp.Attributes;");
cs.WriteLine("// ReSharper disable UnusedMember.Global");
cs.WriteLine("// ReSharper disable ClassNeverInstantiated.Global");
cs.WriteLine("// ReSharper disable UnusedType.Global");
cs.WriteLine("#nullable disable");
cs.WriteLine();
cs.WriteLine($"namespace {fileNameSpace};");
cs.WriteLine();
cs.WriteLine("// Generated by FlatSharp");
cs.WriteLine($"// FileIdentifier: {schema.FileIdent}");
cs.WriteLine($"// FileExtension: {schema.FileExt}");
cs.WriteLine($"// Object Count: {schema.Objects.Count}");
cs.WriteLine($"// Enum Count: {schema.Enums.Count}");
cs.WriteLine($"// Root Type: {schema.RootTable?.Name ?? "None Specified"}");
}
private static ReadOnlySpan<char> GetName(ReadOnlySpan<char> defName, bool stripNamespace)
{
if (!stripNamespace)
return defName;
var idx = defName.LastIndexOf('.');
return idx < 0 ? defName : defName[(idx + 1)..];
}
private static void DumpObjects(Schema schema, TextWriter fbs, TextWriter cs, bool stripNamespace)
{
var list = schema.Objects;
for (var i = list.Count - 1; i >= 0; i--)
{
var obj = list[i];
cs.WriteLine();
cs.WriteLine("[TypeConverter(typeof(ExpandableObjectConverter))]");
cs.WriteLine($"public partial {(obj.IsStruct ? "struct" : "class")} {GetName(obj.Name, stripNamespace)};");
DumpObjectFBS(schema, fbs, stripNamespace, obj);
}
}
private static void DumpObjectFBS(Schema schema, TextWriter fbs, bool stripNamespace, Object def)
{
fbs.WriteLine();
fbs.Write(def.IsStruct ? "struct" : "table");
fbs.Write(" ");
fbs.Write(GetName(def.Name, stripNamespace));
if (schema.RootTable?.Name == def.Name)
fbs.Write(" (fs_serializer)");
fbs.WriteLine(" {"); // end of class header
// Write fields to fbs
foreach (var field in def.Fields.OrderBy(z => z.Id))
{
fbs.Write(" ");
fbs.Write(field.Name);
fbs.Write(":");
fbs.Write(field.Type.ToFBS(schema, field.Type.Index));
WriteAttributes(fbs, field);
fbs.WriteLine(";"); // end of line
}
fbs.WriteLine("}");
}
private static void WriteAttributes(TextWriter fbs, Field field)
{
bool any = false;
void Write(string value)
{
fbs.Write(!any ? " (" : ", ");
fbs.Write(value);
any = true;
}
if (field.Key)
Write("key");
if (field.Required)
Write("required");
if (field.Optional)
Write("optional");
if (field.Deprecated)
Write("deprecated");
if (any)
fbs.Write(")");
}
private static void DumpEnums(Schema schema, TextWriter fbs, bool stripNamespace)
{
var list = schema.Enums;
for (var i = 0; i < list.Count; i++)
{
var def = list[i];
fbs.WriteLine();
var typeName = def.UnderlyingType.ToFBS(schema, i, true);
var implType = $" : {typeName}";
var defName = GetName(def.Name, stripNamespace);
fbs.WriteLine($"enum {defName}{implType}");
fbs.WriteLine("{");
{
foreach (var value in def.Values)
fbs.WriteLine($" {value.Name} = {value.Value},");
}
fbs.WriteLine("}");
}
}
private static string ToFBS(this Type type, Schema schema, int index, bool force = false)
{
var size = type.FixedLength;
var sizeStr = size > 0 ? $":{size}" : "";
if (index != -1)
{
var idx = type.Index;
if (type.BaseType is BaseType.Array)
return $"[{GetName(schema, idx, type.Element)}{sizeStr}]";
if (type.BaseType == BaseType.Vector)
return $"[{GetName(schema, idx, type.Element)}{sizeStr}]";
if (type.BaseType == BaseType.Obj)
return $"{schema.Objects[idx].Name}";
if (!force)
return $"{schema.Enums[idx].Name}";
}
var baseType = type.BaseType;
if (baseType is BaseType.Array)
return $"[{GetName(type.Element)}{sizeStr}]";
if (baseType is BaseType.Vector)
return $"[{GetName(type.Element)}{sizeStr}]";
var x = GetName(baseType);
if (type.Element != BaseType.None)
return $"[{x}]";
return x;
}
public static string GetName(BaseType type) => type.ToString().ToLower();
private static string GetName(Schema schema, int idx, BaseType type)
{
if (type is BaseType.Obj)
return schema.Objects[idx].Name;
return schema.Enums[idx].Name;
}
}