using System; using System.Collections.Generic; using System.Linq; using System.Text; using GeViSetEditor.Core.Models; namespace GeViSetEditor.Core.Parsers { /// /// Comprehensive parser that extracts ALL configuration data /// Based on binary analysis showing 15K booleans, 9K integers, 6K strings /// public class ComprehensiveConfigParser { private byte[] _data; private int _position; private int _depth; public ComprehensiveConfigFile Parse(byte[] data) { _data = data; _position = 0; _depth = 0; var config = new ComprehensiveConfigFile { OriginalData = data, FileSize = data.Length }; Console.WriteLine($"\n=== Comprehensive Configuration Parser ==="); Console.WriteLine($"File size: {data.Length:N0} bytes\n"); // Parse header ParseHeader(config); Console.WriteLine($"Starting systematic parse from offset {_position}...\n"); // Parse all data structures systematically int itemsParsed = 0; while (_position < _data.Length - 10) { var node = TryParseNextNode(); if (node != null) { config.RootNodes.Add(node); itemsParsed++; if (itemsParsed % 1000 == 0) Console.WriteLine($"Parsed {itemsParsed} nodes, position: {_position}/{_data.Length}"); } else { // Skip unknown byte _position++; } } Console.WriteLine($"\n=== Parse Complete ==="); Console.WriteLine($"Total nodes parsed: {itemsParsed}"); Console.WriteLine($"Position: {_position}/{_data.Length}"); // Calculate statistics CalculateStatistics(config); return config; } private void ParseHeader(ComprehensiveConfigFile config) { // Skip null byte if (_position < _data.Length && _data[_position] == 0x00) { config.HeaderNullPrefix = true; _position++; } // Read header if (_position < _data.Length) { byte headerLen = _data[_position++]; if (_position + headerLen <= _data.Length) { config.Header = Encoding.UTF8.GetString(_data, _position, headerLen); _position += headerLen; Console.WriteLine($"Header: '{config.Header}'"); Console.WriteLine($"Header ends at: {_position} (0x{_position:X})\n"); } } } private ConfigNode TryParseNextNode() { if (_position >= _data.Length) return null; int startOffset = _position; byte marker = _data[_position]; ConfigNode node = null; switch (marker) { case 0x01: // Boolean node = ParseBoolean(startOffset); break; case 0x04: // Integer node = ParseInteger(startOffset); break; case 0x05: // Special marker (Rules, etc.) node = ParseSpecialMarker(startOffset); break; case 0x07: // String or Object node = ParseStringOrObject(startOffset); break; default: // Unknown marker - might be raw data return null; } return node; } private ConfigNode ParseBoolean(int startOffset) { if (_position + 2 > _data.Length) return null; _position++; // Skip 0x01 bool value = _data[_position++] != 0; return new ConfigNode { StartOffset = startOffset, EndOffset = _position, NodeType = "boolean", Value = value }; } private ConfigNode ParseInteger(int startOffset) { if (_position + 5 > _data.Length) return null; _position++; // Skip 0x04 int value = BitConverter.ToInt32(_data, _position); _position += 4; return new ConfigNode { StartOffset = startOffset, EndOffset = _position, NodeType = "integer", Value = value }; } private ConfigNode ParseSpecialMarker(int startOffset) { if (_position + 2 > _data.Length) return null; _position++; // Skip 0x05 byte nameLen = _data[_position++]; if (nameLen > 100 || _position + nameLen > _data.Length) { _position = startOffset; return null; } string markerName = Encoding.UTF8.GetString(_data, _position, nameLen); _position += nameLen; var node = new ConfigNode { StartOffset = startOffset, EndOffset = _position, NodeType = "marker", Name = markerName }; // If it's "Rules", parse action mappings if (markerName == "Rules") { ParseRulesSection(node); } return node; } private ConfigNode ParseStringOrObject(int startOffset) { if (_position + 2 > _data.Length) return null; _position++; // Skip 0x07 byte length = _data[_position++]; if (length == 0 || length > 200 || _position + length > _data.Length) { _position = startOffset; return null; } string stringValue = Encoding.UTF8.GetString(_data, _position, length); _position += length; // Check if this string is followed by typed values (object property) if (_position < _data.Length) { byte nextMarker = _data[_position]; if (nextMarker == 0x01 || nextMarker == 0x04 || nextMarker == 0x07) { // This is a property name followed by value var node = new ConfigNode { StartOffset = startOffset, NodeType = "property", Name = stringValue }; // Parse the value var valueNode = TryParseNextNode(); if (valueNode != null) { node.Value = valueNode.Value; node.ValueType = valueNode.NodeType; node.EndOffset = _position; return node; } } } // Just a standalone string return new ConfigNode { StartOffset = startOffset, EndOffset = _position, NodeType = "string", Value = stringValue }; } private void ParseRulesSection(ConfigNode rulesNode) { // Skip metadata bytes while (_position < _data.Length && _data[_position] <= 0x04) { _position++; } // Parse action strings (07 01 40 pattern) var actions = new List(); int attempts = 0; while (attempts < 200 && _position + 10 < _data.Length) { if (_data[_position] == 0x07 && _data[_position + 1] == 0x01 && _data[_position + 2] == 0x40) { ushort actionLen = BitConverter.ToUInt16(_data, _position + 3); if (actionLen > 0 && actionLen < 500 && _position + 5 + actionLen <= _data.Length) { string action = Encoding.UTF8.GetString(_data, _position + 5, actionLen); actions.Add(action); _position += 5 + actionLen; attempts = 0; // Reset } else { _position++; attempts++; } } else { _position++; attempts++; } } rulesNode.Value = actions; rulesNode.EndOffset = _position; } private void CalculateStatistics(ComprehensiveConfigFile config) { config.Statistics = new ComprehensiveConfigStatistics { TotalNodes = config.RootNodes.Count, BooleanCount = config.RootNodes.Count(n => n.NodeType == "boolean"), IntegerCount = config.RootNodes.Count(n => n.NodeType == "integer"), StringCount = config.RootNodes.Count(n => n.NodeType == "string"), PropertyCount = config.RootNodes.Count(n => n.NodeType == "property"), MarkerCount = config.RootNodes.Count(n => n.NodeType == "marker"), RulesCount = config.RootNodes.Count(n => n.NodeType == "marker" && n.Name == "Rules") }; Console.WriteLine($"\n=== Statistics ==="); Console.WriteLine($"Total nodes: {config.Statistics.TotalNodes:N0}"); Console.WriteLine($"Properties: {config.Statistics.PropertyCount:N0}"); Console.WriteLine($"Booleans: {config.Statistics.BooleanCount:N0}"); Console.WriteLine($"Integers: {config.Statistics.IntegerCount:N0}"); Console.WriteLine($"Strings: {config.Statistics.StringCount:N0}"); Console.WriteLine($"Markers: {config.Statistics.MarkerCount:N0}"); Console.WriteLine($"Rules sections: {config.Statistics.RulesCount:N0}"); // Count property names var propertyNames = config.RootNodes .Where(n => n.NodeType == "property") .GroupBy(n => n.Name) .OrderByDescending(g => g.Count()) .Take(20); if (propertyNames.Any()) { Console.WriteLine($"\n=== Top 20 Property Names ==="); foreach (var group in propertyNames) { Console.WriteLine($" {group.Key}: {group.Count()} occurrences"); } } } } }