public void Save(string path) { // First we build the type metadata var metadataList = ParameterList .OrderByDescending(e => e.Depth) // Base Tree Meta Data is stored by depth within the tree .Where(e => e.NodeType == NodeType.CustomType || e.NodeType == NodeType.CustomTypeArray) // We only want custom types .GroupBy(e => e.CustomTypeName).Select(e => e.FirstOrDefault()) // DistinctBy - We only want one of each definition .ToList(); metadataList.Add(ParameterRoot); using (var fs = new FileStream(path, FileMode.Create)) using (var writer = new BinaryStream(fs, ByteConverter.Little)) { writer.WriteString("SDEF", StringCoding.Raw); writer.Position += 4; // Runtime File Ptr writer.WriteInt32(Version); if (Version >= 1) { writer.Position += 1; } writer.WriteInt32(metadataList.Count); for (int i = 0; i < metadataList.Count; i++) { SDEFBase type = metadataList[i]; writer.WriteInt32(type.CustomTypeName.Length + 1); writer.WriteString(type.CustomTypeName, StringCoding.ZeroTerminated); if (type.NodeType == NodeType.CustomTypeArray) { var firstElement = (type as SDEFParamArray).Values.First(); writer.WriteInt32(firstElement.ChildParameters.Count); WriteParameterMetadata(writer, metadataList, firstElement); } else { writer.WriteInt32(type.ChildParameters.Count); WriteParameterMetadata(writer, metadataList, type); } } // Write the first data to read writer.WriteInt16((short)metadataList.FindIndex(e => e.CustomTypeName == ParameterRoot.CustomTypeName)); writer.WriteBoolean(ParameterRoot.NodeType == NodeType.CustomTypeArray || ParameterRoot.NodeType == NodeType.CustomType, BooleanCoding.Word); // Now the data itself if needed TraverseAndWriteData(writer, ParameterRoot); } }
private void TraverseAndWriteData(BinaryStream writer, SDEFBase param) { foreach (var entry in param.ChildParameters) { if (entry.NodeType == NodeType.CustomType) { TraverseAndWriteData(writer, entry); } else if (entry.NodeType == NodeType.RawValue) { (entry as SDEFParam).RawValue.WriteToStream(writer); } else { var arr = entry as SDEFParamArray; if (entry.NodeType == NodeType.CustomTypeArray) { if (Version == 0) { writer.WriteInt32(arr.Values.Count); } for (int i = 0; i < arr.Values.Count; i++) { TraverseAndWriteData(writer, arr.Values[i]); } } else if (entry.NodeType == NodeType.RawValueArray) { if (Version == 0) { writer.WriteInt32(arr.RawValuesArray.Length); } foreach (var val in arr.RawValuesArray) { val.WriteToStream(writer); } } } } }
public void WriteParameterMetadata(BinaryStream writer, List <SDEFBase> metadataList, SDEFBase sdefBase) { for (int j = 0; j < sdefBase.ChildParameters.Count; j++) { SDEFBase entry = sdefBase.ChildParameters[j]; writer.WriteInt32(entry.Name.Length + 1); writer.WriteString(entry.Name, StringCoding.ZeroTerminated); if (entry is SDEFParamArray) { writer.WriteInt16((short)ValueType.Array); } else { SDEFParam param = entry as SDEFParam; if (entry.NodeType == NodeType.RawValue) { writer.WriteInt16((short)param.RawValue.Type); } else if (entry.NodeType == NodeType.CustomType) { int typeIndex = metadataList.FindIndex(metaType => metaType.CustomTypeName == entry.CustomTypeName); writer.WriteInt16((short)typeIndex); } } writer.WriteBoolean(entry.NodeType == NodeType.CustomType, BooleanCoding.Word); if (entry is SDEFParamArray array) { if (entry.NodeType == NodeType.RawValueArray) { writer.WriteInt16((short)array.RawValuesArray[0].Type); } else { int typeIndex = metadataList.FindIndex(metaType => metaType.CustomTypeName == entry.CustomTypeName); writer.WriteInt16((short)typeIndex); } writer.WriteBoolean(entry.NodeType == NodeType.CustomTypeArray, BooleanCoding.Word); if (entry.NodeType == NodeType.CustomTypeArray) { if (Version == 0) { writer.WriteInt32(0); } else { writer.WriteInt32((short)array.Values.Count); } } else { if (Version == 0) { writer.WriteInt32(0); } else { writer.WriteInt32((short)array.RawValuesArray.Length); } } } } }
public static void Traverse(BinaryStream reader, int version, PseudoReflectionObject sdef, SDEFBase parentNode, SDEFMetaData sdefMetadata, SDEFMetaDataCategory nodeCategory, ref int depth) { depth++; foreach (var entry in nodeCategory.Entries) { SDEFBase current; if (!entry.HasCustomType && (ValueType)entry.TypeOrIndex == ValueType.Array) { current = new SDEFParamArray(); // Param is a param array } else { current = new SDEFParam(); // Param is regular parameter, but if its a custom type it may have children parameters } current.Name = entry.Name; parentNode.ChildParameters.Add(current); sdef.ParameterList.Add(current); if (entry.HasCustomType) { current.CustomTypeName = sdefMetadata.Categories[entry.TypeOrIndex].Name; current.NodeType = NodeType.CustomType; // Traverse children parameter for this basic type Traverse(reader, version, sdef, current, sdefMetadata, sdefMetadata.Categories[entry.TypeOrIndex], ref depth); } else if ((ValueType)entry.TypeOrIndex == ValueType.Array) { if (entry.ArrayHasCustomType) { current.NodeType = NodeType.CustomTypeArray; current.CustomTypeName = sdefMetadata.Categories[entry.ArrayCategoryIndex].Name; if (version == 0) { entry.ArrayLength = reader.ReadUInt32(); } for (int i = 0; i < entry.ArrayLength; i++) { // Create the element for the array to add later var arrayElement = new SDEFParam(); arrayElement.CustomTypeName = current.CustomTypeName; arrayElement.NodeType = NodeType.CustomType; arrayElement.Name = $"[{i}]"; Traverse(reader, version, sdef, arrayElement, sdefMetadata, sdefMetadata.Categories[entry.ArrayCategoryIndex], ref depth); // Don't forget to add our array element to the global parameter list sdef.ParameterList.Add(arrayElement); (current as SDEFParamArray).Values.Add(arrayElement); } } else { current.CustomTypeName = nodeCategory.Name; current.NodeType = NodeType.RawValueArray; if (version == 0) { entry.ArrayLength = reader.ReadUInt32(); } (current as SDEFParamArray).RawValuesArray = new SDEFVariant[entry.ArrayLength]; for (int i = 0; i < entry.ArrayLength; i++) { var val = ReadData(reader, entry, (ValueType)entry.ArrayCategoryIndex); (current as SDEFParamArray).RawValuesArray[i] = val; } } } else { current.NodeType = NodeType.RawValue; var variant = ReadData(reader, entry, (ValueType)entry.TypeOrIndex); (current as SDEFParam).RawValue = variant; } current.Depth = depth; } depth--; }