diff --git a/.gitignore b/.gitignore index c42b0a6..242b8d2 100644 --- a/.gitignore +++ b/.gitignore @@ -207,3 +207,4 @@ scripts/dump src/StarBreaker.Grpc/protos scripts/__pycache__ crack +src/StarBreaker.DataCore.Generated/Generated diff --git a/src/StarBreaker.DataCore.Generated/DataCoreHelper.cs b/src/StarBreaker.DataCore.Generated/DataCoreHelper.cs new file mode 100644 index 0000000..3e2e4f4 --- /dev/null +++ b/src/StarBreaker.DataCore.Generated/DataCoreHelper.cs @@ -0,0 +1,239 @@ +using StarBreaker.Common; +using StarBreaker.DataCoreGenerated; + +namespace StarBreaker.DataCore; + +public static class DataCoreHelper +{ + public static T? ReadFromReference(DataCoreDatabase db, DataCoreReference reference) where T : class, IDataCoreReadable + { + if (reference.RecordId == CigGuid.Empty || reference.InstanceIndex == -1) + return null; + + if (db.MainRecords.TryGetValue(reference.RecordId, out var mr)) + return null; + + var record = db.GetRecord(reference.RecordId); + return ReadFromInstance(db, record.StructIndex, record.InstanceIndex); + } + + public static T? ReadFromPointer(DataCoreDatabase db, DataCorePointer pointer) where T : class, IDataCoreReadable + { + return ReadFromInstance(db, pointer.StructIndex, pointer.InstanceIndex); + } + + public static T? ReadFromInstance(DataCoreDatabase db, int structIndex, int instanceIndex) where T : class, IDataCoreReadable + { + if (structIndex == -1 || instanceIndex == -1) + return null; + + var reader = db.GetReader(structIndex, instanceIndex); + return T.Read(db, db.StructDefinitions[structIndex], ref reader); + } + + public static T EnumParse(string value, T unknown) where T : struct, Enum + { + if (value == "") + return unknown; + + if (!Enum.TryParse(value, out var eVal)) + { + var type = typeof(T); + Console.WriteLine($"Error parsing Enum of type {type.Name} with value {value}. Setting to unknown."); + return unknown; + } + return eVal; + } + + public static T[] ReadReferenceArray(DataCoreDatabase db, ref SpanReader reader) where T : class, IDataCoreReadable + { + var count = reader.ReadInt32(); + var firstIndex = reader.ReadInt32(); + + var array = new T[count]; + + for (var i = firstIndex; i < firstIndex + count; i++) + { + array[i - firstIndex] = ReadFromReference(db, db.ReferenceValues[i]); + } + + return array; + } + + public static T[] ReadWeakPointerArray(DataCoreDatabase db, ref SpanReader reader) + //where T : IDataCoreReadable + { + var count = reader.ReadInt32(); + var firstIndex = reader.ReadInt32(); + + var array = new T[count]; + + for (var i = firstIndex; i < firstIndex + count; i++) + { + //TODO: causes recursive loop + //array[i - firstIndex] = ReadFromPointer(db, db.WeakValues[i]); + array[i - firstIndex] = default; + } + + return array; + } + + public static Lazy[] ReadWeakPointerArrayLazy(DataCoreDatabase db, ref SpanReader reader) where T : class, IDataCoreReadable + { + var count = reader.ReadInt32(); + var firstIndex = reader.ReadInt32(); + + var array = new Lazy[count]; + + for (var i = firstIndex; i < firstIndex + count; i++) + { + var ptr = db.WeakValues[i]; + array[i - firstIndex] = new Lazy(() => ReadFromPointer(db, ptr)); + } + + return array; + } + + public static T[] ReadStrongPointerArray(DataCoreDatabase db, ref SpanReader reader) where T : class, IDataCoreReadable + { + var count = reader.ReadInt32(); + var firstIndex = reader.ReadInt32(); + + var array = new T[count]; + + for (var i = firstIndex; i < firstIndex + count; i++) + { + var strongValue = db.StrongValues[i]; + var read = TypeMap.ReadFromRecord(db, strongValue.StructIndex, strongValue.InstanceIndex); + if (read == null) + array[i - firstIndex] = null!; + else if (read is T readable) + array[i - firstIndex] = readable; + else + throw new Exception($"ReadFromPointer failed to cast {read.GetType()} to {typeof(T)}"); + } + + return array; + } + + public static T[] ReadClassArray(DataCoreDatabase db, ref SpanReader reader, int structIndex) where T : class, IDataCoreReadable + { + var count = reader.ReadInt32(); + var firstIndex = reader.ReadInt32(); + + var array = new T[count]; + + for (var i = firstIndex; i < firstIndex + count; i++) + { + array[i - firstIndex] = ReadFromInstance(db, structIndex, i); + } + + return array; + } + + public static T[] ReadEnumArray(DataCoreDatabase db, ref SpanReader reader) where T : struct, Enum + { + var count = reader.ReadInt32(); + var firstIndex = reader.ReadInt32(); + + var array = new T[count]; + + for (var i = firstIndex; i < firstIndex + count; i++) + { + array[i - firstIndex] = EnumParse(db.EnumValues[i].ToString(db), default); + } + + return array; + } + + public static DataCoreStringId[] ReadStringArray(DataCoreDatabase db, ref SpanReader reader) + { + var count = reader.ReadInt32(); + var firstIndex = reader.ReadInt32(); + return db.StringIdValues.AsSpan(firstIndex, count).ToArray(); + } + + public static DataCoreStringId[] ReadLocaleArray(DataCoreDatabase db, ref SpanReader reader) + { + var count = reader.ReadInt32(); + var firstIndex = reader.ReadInt32(); + return db.LocaleValues.AsSpan(firstIndex, count).ToArray(); + } + + public static sbyte[] ReadSByteArray(DataCoreDatabase db, ref SpanReader reader) + { + var count = reader.ReadInt32(); + var firstIndex = reader.ReadInt32(); + return db.Int8Values.AsSpan(firstIndex, count).ToArray(); + } + + public static short[] ReadInt16Array(DataCoreDatabase db, ref SpanReader reader) + { + var count = reader.ReadInt32(); + var firstIndex = reader.ReadInt32(); + return db.Int16Values.AsSpan(firstIndex, count).ToArray(); + } + + public static int[] ReadInt32Array(DataCoreDatabase db, ref SpanReader reader) + { + var count = reader.ReadInt32(); + var firstIndex = reader.ReadInt32(); + return db.Int32Values.AsSpan(firstIndex, count).ToArray(); + } + + public static long[] ReadInt64Array(DataCoreDatabase db, ref SpanReader reader) + { + var count = reader.ReadInt32(); + var firstIndex = reader.ReadInt32(); + return db.Int64Values.AsSpan(firstIndex, count).ToArray(); + } + + public static byte[] ReadByteArray(DataCoreDatabase db, ref SpanReader reader) + { + var count = reader.ReadInt32(); + var firstIndex = reader.ReadInt32(); + return db.UInt8Values.AsSpan(firstIndex, count).ToArray(); + } + + public static ushort[] ReadUInt16Array(DataCoreDatabase db, ref SpanReader reader) + { + var count = reader.ReadInt32(); + var firstIndex = reader.ReadInt32(); + return db.UInt16Values.AsSpan(firstIndex, count).ToArray(); + } + + public static uint[] ReadUInt32Array(DataCoreDatabase db, ref SpanReader reader) + { + var count = reader.ReadInt32(); + var firstIndex = reader.ReadInt32(); + return db.UInt32Values.AsSpan(firstIndex, count).ToArray(); + } + + public static ulong[] ReadUInt64Array(DataCoreDatabase db, ref SpanReader reader) + { + var count = reader.ReadInt32(); + var firstIndex = reader.ReadInt32(); + return db.UInt64Values.AsSpan(firstIndex, count).ToArray(); + } + + public static bool[] ReadBoolArray(DataCoreDatabase db, ref SpanReader reader) + { + var count = reader.ReadInt32(); + var firstIndex = reader.ReadInt32(); + return db.BooleanValues.AsSpan(firstIndex, count).ToArray(); + } + + public static float[] ReadSingleArray(DataCoreDatabase db, ref SpanReader reader) + { + var count = reader.ReadInt32(); + var firstIndex = reader.ReadInt32(); + return db.SingleValues.AsSpan(firstIndex, count).ToArray(); + } + + public static double[] ReadDoubleArray(DataCoreDatabase db, ref SpanReader reader) + { + var count = reader.ReadInt32(); + var firstIndex = reader.ReadInt32(); + return db.DoubleValues.AsSpan(firstIndex, count).ToArray(); + } +} \ No newline at end of file diff --git a/src/StarBreaker.DataCore.Generated/IDataCoreReadable.cs b/src/StarBreaker.DataCore.Generated/IDataCoreReadable.cs new file mode 100644 index 0000000..f776b3a --- /dev/null +++ b/src/StarBreaker.DataCore.Generated/IDataCoreReadable.cs @@ -0,0 +1,10 @@ +using StarBreaker.Common; + +namespace StarBreaker.DataCore; + +public interface IDataCoreReadable; + +public interface IDataCoreReadable : IDataCoreReadable where T : class, IDataCoreReadable +{ + static abstract T Read(DataCoreDatabase db, DataCoreStructDefinition structDefinition, ref SpanReader reader); +} \ No newline at end of file diff --git a/src/StarBreaker.DataCore.Generated/StarBreaker.DataCore.Generated.csproj b/src/StarBreaker.DataCore.Generated/StarBreaker.DataCore.Generated.csproj new file mode 100644 index 0000000..43ce9dd --- /dev/null +++ b/src/StarBreaker.DataCore.Generated/StarBreaker.DataCore.Generated.csproj @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/StarBreaker.DataCore/DataCoreCodeGenerator.cs b/src/StarBreaker.DataCore/DataCoreCodeGenerator.cs new file mode 100644 index 0000000..57e61e0 --- /dev/null +++ b/src/StarBreaker.DataCore/DataCoreCodeGenerator.cs @@ -0,0 +1,390 @@ +using System.Text; + +namespace StarBreaker.DataCore; + +public class DataCoreCodeGenerator +{ + private readonly DataCoreDatabase Database; + + public DataCoreCodeGenerator(DataCoreDatabase database) + { + Database = database; + } + + //Note: This is not a standard source generator because those don't really deal well with the way + // we are getting the type information. We're reading a pretty big 200mb blob with random data in there, + // usually standard source generators use the code itself or sometimes small and simple text files. + public void Generate(string path) + { + Directory.CreateDirectory(path); + + GenerateTypes(path); + GenerateEnums(path); + GenerateTypeMap(path); + } + + private void GenerateTypeMap(string path) + { + Directory.CreateDirectory(Path.Combine(path)); + + var typeMapSb = new StringBuilder(); + //map the struct indexes to the generated types + typeMapSb.AppendLine("using System;"); + typeMapSb.AppendLine("using System.Collections.Generic;"); + typeMapSb.AppendLine("using StarBreaker.DataCore;"); + typeMapSb.AppendLine(); + typeMapSb.AppendLine("namespace StarBreaker.DataCoreGenerated;"); + typeMapSb.AppendLine(); + typeMapSb.AppendLine("public static class TypeMap"); + typeMapSb.AppendLine("{"); + typeMapSb.AppendLine(" public static IDataCoreReadable? ReadFromRecord(DataCoreDatabase db, int structIndex, int instanceIndex)"); + typeMapSb.AppendLine(" {"); + typeMapSb.AppendLine(" if (structIndex == -1 || instanceIndex == -1)"); + typeMapSb.AppendLine(" return null;"); + typeMapSb.AppendLine(); + typeMapSb.AppendLine(" return structIndex switch"); + typeMapSb.AppendLine(" {"); + for (var i = 0; i < Database.StructDefinitions.Length; i++) + { + var structDefinition = Database.StructDefinitions[i]; + typeMapSb.AppendLine($" {i} => DataCoreHelper.ReadFromInstance<{structDefinition.GetName(Database)}>(db, structIndex, instanceIndex),"); + } + + typeMapSb.AppendLine(" _ => throw new NotImplementedException()"); + typeMapSb.AppendLine(" };"); + + typeMapSb.AppendLine(" }"); + typeMapSb.AppendLine("}"); + + File.WriteAllText(Path.Combine(path, "TypeMap.cs"), typeMapSb.ToString()); + } + + private void GenerateEnums(string path) + { + Directory.CreateDirectory(Path.Combine(path, "Enums")); + foreach (var enumDefinition in Database.EnumDefinitions) + { + var fileName = enumDefinition.GetName(Database) + ".cs"; + var sb = new StringBuilder(); + + sb.AppendLine("using System;"); + sb.AppendLine("using System.Collections.Generic;"); + sb.AppendLine("using System.Text.Json.Serialization;"); + sb.AppendLine("using StarBreaker.DataCore;"); + + sb.AppendLine(); + sb.AppendLine("namespace StarBreaker.DataCoreGenerated;"); + sb.AppendLine(); + + sb.AppendLine($"public enum {enumDefinition.GetName(Database)} : int"); + sb.AppendLine("{"); + + if ("SinglePlayerOrMultiplayer" == enumDefinition.GetName(Database)) + { + Console.WriteLine(); + } + + sb.AppendLine($" __Unknown = -1,"); + + for (var i = 0; i < enumDefinition.ValueCount; i++) + { + var id = Database.EnumOptions[enumDefinition.FirstValueIndex + i]; + var value = Database.GetString2(id); + sb.AppendLine($" {value},"); + } + + sb.AppendLine("}"); + + var final = sb.ToString(); + + File.WriteAllText(Path.Combine(path, "Enums", fileName), final); + } + } + + private void GenerateTypes(string path) + { + Directory.CreateDirectory(Path.Combine(path, "Types")); + for (var structIndex = 0; structIndex < Database.StructDefinitions.Length; structIndex++) + { + var structDefinition = Database.StructDefinitions[structIndex]; + //write each struct definition to a file + var fileName = structDefinition.GetName(Database) + ".cs"; + var sb = new StringBuilder(); + + sb.AppendLine("using System;"); + sb.AppendLine("using System.Collections.Generic;"); + sb.AppendLine("using System.Text.Json.Serialization;"); + sb.AppendLine("using StarBreaker.DataCore;"); + sb.AppendLine("using StarBreaker.Common;"); + sb.AppendLine(); + sb.AppendLine("namespace StarBreaker.DataCoreGenerated;"); + sb.AppendLine(); + + if (structDefinition.ParentTypeIndex != -1) + { + var parent = Database.StructDefinitions[structDefinition.ParentTypeIndex]; + sb.AppendLine($"public record {structDefinition.GetName(Database)} : {parent.GetName(Database)}, IDataCoreReadable<{structDefinition.GetName(Database)}>"); + } + else + { + //sb.AppendLine($"public class {structDefinition.GetName(Database)}"); + sb.AppendLine($"public record {structDefinition.GetName(Database)} : IDataCoreReadable<{structDefinition.GetName(Database)}>"); + } + + sb.AppendLine("{"); + var properties = Database.PropertyDefinitions.AsSpan(structDefinition.FirstAttributeIndex, structDefinition.AttributeCount); + + foreach (var property in properties) + { + var propertyType = GetPropertyType(property); + var name = property.GetName(Database); + + sb.AppendLine($" public {propertyType} @{name} {{ get; init; }}"); + } + + sb.AppendLine(); + + WriteSpecialConstructor(sb, structDefinition, structIndex); + + sb.AppendLine("}"); + + var final = sb.ToString(); + + File.WriteAllText(Path.Combine(path, "Types", fileName), final); + } + } + + private void WriteBasicConstructor(StringBuilder sb, DataCoreStructDefinition structDefinition, int structIndex) + { + // The constructor should take as arguments the properties in the order we expect. + // Which is base type -> derived type -> derived type -> our type strictly. + // then we handle passing the properties to the following constructor. + var allprops = Database.GetProperties(structIndex).AsSpan(); + sb.AppendLine($" public {structDefinition.GetName(Database)}("); + for (var i = 0; i < allprops.Length; i++) + { + var property = allprops[i]; + var propertyType = GetPropertyType(property); + var name = property.GetName(Database); + + sb.Append($" {propertyType} _{name}"); + if (i != allprops.Length - 1) + sb.AppendLine(","); + else sb.AppendLine(); + } + + sb.AppendLine(" )"); + + if (structDefinition.ParentTypeIndex != -1) + { + sb.AppendLine(" : base("); + //we take all properties from all parent types. We will consume our own in our constructor, and pass down the rest. + var propsForConstructor = allprops[..^structDefinition.AttributeCount]; + for (var i = 0; i < propsForConstructor.Length; i++) + { + var property = propsForConstructor[i]; + var name = property.GetName(Database); + sb.Append($" _{name}"); + if (i != propsForConstructor.Length - 1) + sb.AppendLine(","); + else sb.AppendLine(); + } + + sb.AppendLine(" )"); + } + + sb.AppendLine(" {"); + var thisProps = Database.PropertyDefinitions.AsSpan(structDefinition.FirstAttributeIndex, structDefinition.AttributeCount); + foreach (var property in thisProps) + { + var name = property.GetName(Database); + sb.AppendLine($" @{name} = _{name};"); + } + + sb.AppendLine(" }"); + } + + private string GetSimplePropertyType(DataCorePropertyDefinition property) => property.DataType switch + { + DataType.Boolean => "bool", + DataType.Byte => "byte", + DataType.SByte => "sbyte", + DataType.Int16 => "short", + DataType.UInt16 => "ushort", + DataType.Int32 => "int", + DataType.UInt32 => "uint", + DataType.Int64 => "long", + DataType.UInt64 => "ulong", + DataType.Single => "float", + DataType.Double => "double", + DataType.Guid => "CigGuid", + DataType.Locale => "DataCoreStringId", + DataType.String => "DataCoreStringId", + + DataType.EnumChoice => Database.EnumDefinitions[property.StructIndex].GetName(Database), + DataType.Reference => Database.StructDefinitions[property.StructIndex].GetName(Database), + DataType.WeakPointer => "DataCorePointer", + DataType.StrongPointer => Database.StructDefinitions[property.StructIndex].GetName(Database), + DataType.Class => Database.StructDefinitions[property.StructIndex].GetName(Database), + + //todo + // DataType.EnumChoice => Database.EnumDefinitions[property.StructIndex].GetName(Database), + // DataType.Class => Database.StructDefinitions[property.StructIndex].GetName(Database), + // DataType.Reference => Database.StructDefinitions[property.StructIndex].GetName(Database), + // DataType.WeakPointer => Database.StructDefinitions[property.StructIndex].GetName(Database), + // DataType.StrongPointer => Database.StructDefinitions[property.StructIndex].GetName(Database), + _ => throw new ArgumentOutOfRangeException() + }; + + + private string GetPropertyType(DataCorePropertyDefinition property) + { + var baseProperty = GetSimplePropertyType(property); + + return property.ConversionType switch + { + ConversionType.Attribute => baseProperty, + _ => $"{baseProperty}[]" + }; + } + + //TODO: generate a constructor that accepts something useful like a SpanReader and a database. + // it should then, based on its properties, generate either: + // if attribute, reader.Read(). For struct types, we *have* to pass down the spanreader or it gets out of sync. + // For references or pointers, we can just read that and ski the actual data for a POC impl. + // if array, we probably pass it down to a generic method that handles reading the array i and count, and reads the elements as needed. + // for a poc, realistically we read those two ints and skip. things should work fine from there on even half complete. + + private void WriteSpecialConstructor(StringBuilder sb, DataCoreStructDefinition structDefinition, int structIndex) + { + if (structDefinition.ParentTypeIndex != -1) + sb.AppendLine($" public new static {structDefinition.GetName(Database)} Read(DataCoreDatabase db, DataCoreStructDefinition structDefinition, ref SpanReader reader)"); + else + sb.AppendLine($" public static {structDefinition.GetName(Database)} Read(DataCoreDatabase db, DataCoreStructDefinition structDefinition, ref SpanReader reader)"); + + var allprops = Database.GetProperties(structIndex).AsSpan(); + + //for now we ignore parent types + sb.AppendLine(" {"); + sb.AppendLine($" return new {structDefinition.GetName(Database)}"); + sb.AppendLine(" {"); + + foreach (var property in allprops) + { + if (property.ConversionType == ConversionType.Attribute) + WriteSingleRead(sb, property); + else + WriteArrayRead(sb, property); + } + + sb.AppendLine(" };"); + + sb.AppendLine(" }"); + } + + private void WriteSingleRead(StringBuilder sb, DataCorePropertyDefinition property) + { + var propertyType = GetSimplePropertyType(property); + var name = property.GetName(Database); + + switch (property.DataType) + { + case DataType.Class: + sb.AppendLine($" @{name} = {propertyType}.Read(db, structDefinition, ref reader),"); + break; + case DataType.EnumChoice: + { + var enumName = Database.EnumDefinitions[property.StructIndex].GetName(Database); + sb.AppendLine($" @{name} = DataCoreHelper.EnumParse<{enumName}>(reader.Read().ToString(db), {enumName}.__Unknown),"); + break; + } + case DataType.Reference: + sb.AppendLine($" @{name} = DataCoreHelper.ReadFromReference<{propertyType}>(db, reader.Read()),"); + break; + case DataType.StrongPointer: + sb.AppendLine($" @{name} = DataCoreHelper.ReadFromPointer<{propertyType}>(db, reader.Read()),"); + break; + case DataType.WeakPointer: + //do as default. we probably should handle this, it's actually feasible now :D + sb.AppendLine($" @{name} = reader.Read<{propertyType}>(),"); + break; + default: + //this one should be fine for everything else. + sb.AppendLine($" @{name} = reader.Read<{propertyType}>(),"); + break; + } + } + + private void WriteArrayRead(StringBuilder sb, DataCorePropertyDefinition property) + { + var propertyType = GetSimplePropertyType(property); + var name = property.GetName(Database); + + switch (property.DataType) + { + case DataType.Reference: + sb.AppendLine($" @{name} = DataCoreHelper.ReadReferenceArray<{propertyType}>(db, ref reader),"); + break; + case DataType.StrongPointer: + sb.AppendLine($" @{name} = DataCoreHelper.ReadStrongPointerArray<{propertyType}>(db, ref reader),"); + break; + case DataType.WeakPointer: + sb.AppendLine($" @{name} = DataCoreHelper.ReadWeakPointerArray<{propertyType}>(db, ref reader),"); + break; + case DataType.Class: + sb.AppendLine($" @{name} = DataCoreHelper.ReadClassArray<{propertyType}>(db, ref reader, {property.StructIndex}),"); + break; + case DataType.Boolean: + sb.AppendLine($" @{name} = DataCoreHelper.ReadBoolArray(db, ref reader),"); + break; + case DataType.Byte: + sb.AppendLine($" @{name} = DataCoreHelper.ReadByteArray(db, ref reader),"); + break; + case DataType.SByte: + sb.AppendLine($" @{name} = DataCoreHelper.ReadSByteArray(db, ref reader),"); + break; + case DataType.Int16: + sb.AppendLine($" @{name} = DataCoreHelper.ReadInt16Array(db, ref reader),"); + break; + case DataType.UInt16: + sb.AppendLine($" @{name} = DataCoreHelper.ReadUInt16Array(db, ref reader),"); + break; + case DataType.Int32: + sb.AppendLine($" @{name} = DataCoreHelper.ReadInt32Array(db, ref reader),"); + break; + case DataType.UInt32: + sb.AppendLine($" @{name} = DataCoreHelper.ReadUInt32Array(db, ref reader),"); + break; + case DataType.Int64: + sb.AppendLine($" @{name} = DataCoreHelper.ReadInt64Array(db, ref reader),"); + break; + case DataType.UInt64: + sb.AppendLine($" @{name} = DataCoreHelper.ReadUInt64Array(db, ref reader),"); + break; + case DataType.Single: + sb.AppendLine($" @{name} = DataCoreHelper.ReadSingleArray(db, ref reader),"); + break; + case DataType.Double: + sb.AppendLine($" @{name} = DataCoreHelper.ReadDoubleArray(db, ref reader),"); + break; + case DataType.Guid: + sb.AppendLine($" @{name} = DataCoreHelper.ReadGuidArray(db, ref reader),"); + break; + case DataType.Locale: + sb.AppendLine($" @{name} = DataCoreHelper.ReadLocaleArray(db, ref reader),"); + break; + case DataType.String: + sb.AppendLine($" @{name} = DataCoreHelper.ReadStringArray(db, ref reader),"); + break; + case DataType.EnumChoice: + { + var enumName = Database.EnumDefinitions[property.StructIndex].GetName(Database); + sb.AppendLine($" @{name} = DataCoreHelper.ReadEnumArray<{enumName}>(db, ref reader),"); + break; + } + default: + sb.AppendLine($" @{name} = DataCoreHelper.ReadDummyArray<{propertyType}>(db, ref reader),"); + break; + } + } +} \ No newline at end of file diff --git a/src/StarBreaker.Sandbox/DataCoreSandbox.cs b/src/StarBreaker.Sandbox/DataCoreSandbox.cs index 273cb8a..07eb41e 100644 --- a/src/StarBreaker.Sandbox/DataCoreSandbox.cs +++ b/src/StarBreaker.Sandbox/DataCoreSandbox.cs @@ -1,5 +1,7 @@ -using StarBreaker.Common; +using System.Runtime.CompilerServices; +using StarBreaker.Common; using StarBreaker.DataCore; +using StarBreaker.DataCoreGenerated; namespace StarBreaker.Sandbox; @@ -7,9 +9,23 @@ public static class DataCoreSandbox { public static void Run() { + //GenerateTypes(); + //ExtractGenerated(); + //ExtractUnp4k(); //ExtractProblematic(); - ExtractAll(); + ExtractAll(); + } + + private static void GenerateTypes() + { + var timer = new TimeLogger(); + var dcr = new DataCoreCodeGenerator(new DataCoreDatabase(new MemoryStream(File.ReadAllBytes(@"D:\StarCitizen\P4k\Data\Game2.dcb")))); + timer.LogReset("Loaded DataForge"); + + dcr.Generate(@"C:\Development\StarCitizen\StarBreaker\src\StarBreaker.DataCore.Generated\Generated"); + + timer.LogReset("Generate"); } private static void ExtractUnp4k() @@ -27,11 +43,11 @@ private static void ExtractAll() var dcb = new DataForge(new MemoryStream(File.ReadAllBytes(@"D:\StarCitizen\P4k\Data\Game2.dcb"))); timer.LogReset("Loaded DataForge"); Directory.CreateDirectory(@"D:\StarCitizen\DataCore\Sandbox"); - #if DEBUG +#if DEBUG dcb.ExtractAll(@"D:\StarCitizen\DataCore\Sandbox"); - #else +#else dcb.ExtractAllParallel(@"D:\StarCitizen\DataCore\Sandbox"); - #endif +#endif timer.LogReset("Extracted all records."); } @@ -41,6 +57,14 @@ private static void ExtractProblematic() var dcb = new DataForge(new MemoryStream(File.ReadAllBytes(@"D:\StarCitizen\P4k\Data\Game2.dcb"))); timer.LogReset("Loaded DataForge"); + + var yy = dcb.DataCore.Database.MainRecords + .AsParallel() + .Select(x => dcb.GetFromRecord(x)).ToArray(); + + timer.LogReset("Extracted all records."); + + return; Directory.CreateDirectory(@"D:\StarCitizen\DataCore\Sandbox"); var megaMap = dcb.GetRecordsByFileName("*megamap.pu*").Values.Single(); @@ -53,8 +77,51 @@ private static void ExtractProblematic() //dcb.GetFromRecord(another).Save(@"D:\StarCitizen\DataCore\Sandbox\another.xml"); // dcb.GetFromRecord(zeroggraph).Save(@"D:\StarCitizen\DataCore\Sandbox\zeroggraph.xml"); // dcb.GetFromRecord(broker).Save(@"D:\StarCitizen\DataCore\Sandbox\broker.xml"); - dcb.GetFromRecord(unittest).Save(@"D:\StarCitizen\DataCore\Sandbox\unittesta.xml"); - // dcb.GetFromRecord(tagDatabase).Save(@"D:\StarCitizen\DataCore\Sandbox\tagdatabase.xml"); + //dcb.GetFromRecord(unittest).Save(@"D:\StarCitizen\DataCore\Sandbox\unittesta.xml"); + var db = dcb.GetFromRecord(tagDatabase); + + Console.WriteLine(db); + //.Save(@"D:\StarCitizen\DataCore\Sandbox\tagdatabase.xml"); // dcb.GetFromRecord(megaMap).Save(@"D:\StarCitizen\DataCore\Sandbox\megamap.xml"); } + + private static void ExtractGenerated() + { + var timer = new TimeLogger(); + + var dcb = new DataForge(new MemoryStream(File.ReadAllBytes(@"D:\StarCitizen\P4k\Data\Game2.dcb"))); + timer.LogReset("Loaded DataForge"); + + + var megaMap = dcb.GetRecordsByFileName("*megamap.pu*").Values.Single(); + var tagDatabase = dcb.GetRecordsByFileName("*TagDatabase*").Values.Single(); + var broker = dcb.GetRecordsByFileName("*missionbroker.pu*").Values.Single(); + var unittest = dcb.GetRecordsByFileName("*unittesta*").Values.Single(); + var zeroggraph = dcb.GetRecordsByFileName("*playerzerogtraversalgraph*").Values.Single(); + var another = dcb.DataCore.Database.GetRecord(new CigGuid("04cd25f7-e0c6-4564-95ae-ecfc998e285f")); + + var yy = dcb.DataCore.Database.MainRecords + .Select(x => + { + var y = dcb.DataCore.Database.GetRecord(x); + return TypeMap.ReadFromRecord(dcb.DataCore.Database, y.StructIndex, y.InstanceIndex); + }).ToList(); + timer.LogReset("Extracted all records."); + + var gladius = yy.OfType().ToList(); + + //stupid idiots at CIG decided to have enumDefinition options be string id 2, + // but the enum values in the data map to id 1. wonderful. we can't do a (fast) dumb cast properly unless we want it to be jank. + // if we have to enum.Parse I'll be sad :( + + //actual + var b = Unsafe.BitCast(2687); + var d = Unsafe.BitCast(4858636); + + //expected + var e = Unsafe.BitCast(7321); + var r = Unsafe.BitCast(7085); + + Console.WriteLine(); + } } \ No newline at end of file diff --git a/src/StarBreaker.Sandbox/StarBreaker.Sandbox.csproj b/src/StarBreaker.Sandbox/StarBreaker.Sandbox.csproj index 855d9c6..2978de4 100644 --- a/src/StarBreaker.Sandbox/StarBreaker.Sandbox.csproj +++ b/src/StarBreaker.Sandbox/StarBreaker.Sandbox.csproj @@ -9,6 +9,7 @@ + diff --git a/src/StarBreaker.sln b/src/StarBreaker.sln index 68caacb..7109613 100644 --- a/src/StarBreaker.sln +++ b/src/StarBreaker.sln @@ -39,6 +39,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StarBreaker.GrpcClient", "StarBreaker.GrpcClient\StarBreaker.GrpcClient.csproj", "{5B8D1493-7025-4EF3-B56C-70905A5787DF}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StarBreaker.DataCore.Generated", "StarBreaker.DataCore.Generated\StarBreaker.DataCore.Generated.csproj", "{DDBA37FE-1A2A-4901-AC1C-54428A377E90}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -97,5 +99,9 @@ Global {19E97822-5C5B-1DE1-EE3E-3DF8D603413B}.Release|Any CPU.Build.0 = Release|Any CPU {5B8D1493-7025-4EF3-B56C-70905A5787DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5B8D1493-7025-4EF3-B56C-70905A5787DF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DDBA37FE-1A2A-4901-AC1C-54428A377E90}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DDBA37FE-1A2A-4901-AC1C-54428A377E90}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DDBA37FE-1A2A-4901-AC1C-54428A377E90}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DDBA37FE-1A2A-4901-AC1C-54428A377E90}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal