using System; using System.IO; using System.Text; using System.Collections.Generic; using GeViSetEditor.Core.Models; namespace GeViSetEditor.Core.Parsers { /// /// Writes ComprehensiveConfigFile back to binary .set format /// Enables edit/add/delete operations on configuration /// public class BinaryConfigWriter { /// /// Rebuild complete .set file from configuration nodes /// public byte[] Write(ComprehensiveConfigFile config) { using var ms = new MemoryStream(); // Write header WriteHeader(ms, config); // Write all nodes sequentially foreach (var node in config.RootNodes) { WriteNode(ms, node); } return ms.ToArray(); } private void WriteHeader(MemoryStream ms, ComprehensiveConfigFile config) { // Write null prefix if it was present if (config.HeaderNullPrefix) { ms.WriteByte(0x00); } // Write header string if (!string.IsNullOrEmpty(config.Header)) { byte[] headerBytes = Encoding.UTF8.GetBytes(config.Header); ms.WriteByte((byte)headerBytes.Length); ms.Write(headerBytes, 0, headerBytes.Length); } } private void WriteNode(MemoryStream ms, ConfigNode node) { switch (node.NodeType) { case "boolean": WriteBoolean(ms, node); break; case "integer": WriteInteger(ms, node); break; case "string": WriteString(ms, node); break; case "property": WriteProperty(ms, node); break; case "marker": WriteMarker(ms, node); break; default: throw new InvalidOperationException($"Unknown node type: {node.NodeType}"); } } private void WriteBoolean(MemoryStream ms, ConfigNode node) { ms.WriteByte(0x01); // Boolean marker bool value = Convert.ToBoolean(node.Value); ms.WriteByte(value ? (byte)1 : (byte)0); } private void WriteInteger(MemoryStream ms, ConfigNode node) { ms.WriteByte(0x04); // Integer marker int value = Convert.ToInt32(node.Value); byte[] bytes = BitConverter.GetBytes(value); ms.Write(bytes, 0, bytes.Length); } private void WriteString(MemoryStream ms, ConfigNode node) { ms.WriteByte(0x07); // String marker string value = node.Value?.ToString() ?? ""; byte[] bytes = Encoding.UTF8.GetBytes(value); if (bytes.Length > 255) throw new InvalidOperationException($"String too long: {bytes.Length} bytes (max 255)"); ms.WriteByte((byte)bytes.Length); ms.Write(bytes, 0, bytes.Length); } private void WriteProperty(MemoryStream ms, ConfigNode node) { // Write property name (07 ) ms.WriteByte(0x07); byte[] nameBytes = Encoding.UTF8.GetBytes(node.Name ?? ""); if (nameBytes.Length > 255) throw new InvalidOperationException($"Property name too long: {nameBytes.Length} bytes (max 255)"); ms.WriteByte((byte)nameBytes.Length); ms.Write(nameBytes, 0, nameBytes.Length); // Write property value based on ValueType WriteTypedValue(ms, node.Value, node.ValueType); } private void WriteMarker(MemoryStream ms, ConfigNode node) { // Write marker (05 ) ms.WriteByte(0x05); byte[] nameBytes = Encoding.UTF8.GetBytes(node.Name ?? ""); if (nameBytes.Length > 255) throw new InvalidOperationException($"Marker name too long: {nameBytes.Length} bytes (max 255)"); ms.WriteByte((byte)nameBytes.Length); ms.Write(nameBytes, 0, nameBytes.Length); // If it's a Rules marker with actions, write them if (node.Name == "Rules" && node.Value is List actions) { WriteRulesActions(ms, actions); } } private void WriteRulesActions(MemoryStream ms, List actions) { // Write action strings in the special format: 07 01 40 foreach (var action in actions) { ms.WriteByte(0x07); ms.WriteByte(0x01); ms.WriteByte(0x40); byte[] actionBytes = Encoding.UTF8.GetBytes(action); ushort length = (ushort)actionBytes.Length; byte[] lenBytes = BitConverter.GetBytes(length); ms.Write(lenBytes, 0, 2); // Write length as 2-byte LE ms.Write(actionBytes, 0, actionBytes.Length); } } private void WriteTypedValue(MemoryStream ms, object? value, string? valueType) { if (value == null) { // Write null as empty string ms.WriteByte(0x07); ms.WriteByte(0x00); return; } switch (valueType) { case "boolean": ms.WriteByte(0x01); bool boolVal = Convert.ToBoolean(value); ms.WriteByte(boolVal ? (byte)1 : (byte)0); break; case "integer": ms.WriteByte(0x04); int intVal = Convert.ToInt32(value); byte[] intBytes = BitConverter.GetBytes(intVal); ms.Write(intBytes, 0, intBytes.Length); break; case "string": ms.WriteByte(0x07); string strVal = value.ToString() ?? ""; byte[] strBytes = Encoding.UTF8.GetBytes(strVal); if (strBytes.Length > 255) throw new InvalidOperationException($"String value too long: {strBytes.Length} bytes (max 255)"); ms.WriteByte((byte)strBytes.Length); ms.Write(strBytes, 0, strBytes.Length); break; default: // Default to string representation WriteTypedValue(ms, value.ToString(), "string"); break; } } } }