using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace GeViSetEditor.Core.Parsers { /// /// Advanced binary analyzer to understand .set file structure completely /// public class AdvancedBinaryAnalyzer { private byte[] _data; private int _position; /// /// Analyze binary structure and report findings /// public BinaryStructureReport Analyze(byte[] data) { _data = data; _position = 0; var report = new BinaryStructureReport { FileSize = data.Length }; Console.WriteLine("\n=== Advanced Binary Structure Analysis ===\n"); // Read header AnalyzeHeader(report); // Find all markers FindAllMarkers(report); // Analyze data types distribution AnalyzeDataTypes(report); // Find repeated patterns FindPatterns(report); // Identify section structure AnalyzeSections(report); return report; } private void AnalyzeHeader(BinaryStructureReport report) { Console.WriteLine("=== Header Analysis ==="); // Skip null byte if present if (_position < _data.Length && _data[_position] == 0x00) { report.HeaderHasNullPrefix = true; _position++; Console.WriteLine($"Null byte at position 0"); } // Read header length if (_position < _data.Length) { byte headerLen = _data[_position++]; if (_position + headerLen <= _data.Length) { report.Header = Encoding.UTF8.GetString(_data, _position, headerLen); _position += headerLen; Console.WriteLine($"Header: '{report.Header}' (length: {headerLen})"); Console.WriteLine($"Header ends at offset: {_position} (0x{_position:X})\n"); report.HeaderEndOffset = _position; } } } private void FindAllMarkers(BinaryStructureReport report) { Console.WriteLine("=== Finding Known Markers ==="); // Known markers var markers = new Dictionary { { "Rules", new byte[] { 0x05, 0x52, 0x75, 0x6C, 0x65, 0x73 } }, { "Description", new byte[] { 0x07, 0x0B, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6F, 0x6E } }, { "Name", new byte[] { 0x07, 0x04, 0x4E, 0x61, 0x6D, 0x65 } }, { "Enabled", new byte[] { 0x07, 0x07, 0x45, 0x6E, 0x61, 0x62, 0x6C, 0x65, 0x64 } } }; foreach (var marker in markers) { var offsets = FindPattern(_data, marker.Value); report.Markers[marker.Key] = offsets; Console.WriteLine($"{marker.Key}: {offsets.Count} occurrences"); } Console.WriteLine(); } private void AnalyzeDataTypes(BinaryStructureReport report) { Console.WriteLine("=== Data Type Distribution ==="); int boolCount = 0, intCount = 0, stringCount = 0, actionCount = 0; for (int i = 0; i < _data.Length; i++) { if (_data[i] == 0x01) boolCount++; else if (_data[i] == 0x04) intCount++; else if (_data[i] == 0x07) stringCount++; // Action string pattern if (i + 2 < _data.Length && _data[i] == 0x07 && _data[i+1] == 0x01 && _data[i+2] == 0x40) { actionCount++; } } Console.WriteLine($"0x01 (Boolean marker): {boolCount}"); Console.WriteLine($"0x04 (Integer marker): {intCount}"); Console.WriteLine($"0x07 (String marker): {stringCount}"); Console.WriteLine($"Action pattern (07 01 40): {actionCount}\n"); report.DataTypeCounts["boolean"] = boolCount; report.DataTypeCounts["integer"] = intCount; report.DataTypeCounts["string"] = stringCount; report.DataTypeCounts["action"] = actionCount; } private void FindPatterns(BinaryStructureReport report) { Console.WriteLine("=== Repeated Byte Patterns ==="); // Find sequences that repeat var sequences = new Dictionary(); // Check 4-byte patterns for (int i = 0; i < _data.Length - 4; i++) { var seq = $"{_data[i]:X2} {_data[i+1]:X2} {_data[i+2]:X2} {_data[i+3]:X2}"; if (sequences.ContainsKey(seq)) sequences[seq]++; else sequences[seq] = 1; } var topPatterns = sequences .Where(kv => kv.Value > 50) // More than 50 occurrences .OrderByDescending(kv => kv.Value) .Take(10); foreach (var pattern in topPatterns) { Console.WriteLine($"{pattern.Key}: {pattern.Value} times"); } Console.WriteLine(); } private void AnalyzeSections(BinaryStructureReport report) { Console.WriteLine("=== Section Structure Analysis ==="); // Start after header _position = report.HeaderEndOffset; int sectionCount = 0; while (_position < _data.Length - 100) { var section = TryParseSection(_position); if (section != null) { report.Sections.Add(section); sectionCount++; _position = section.EndOffset; } else { _position++; } } Console.WriteLine($"Identified {sectionCount} potential sections\n"); // Show first few sections Console.WriteLine("=== First 10 Sections ==="); foreach (var section in report.Sections.Take(10)) { Console.WriteLine($"Offset {section.StartOffset}-{section.EndOffset}: {section.Name}"); Console.WriteLine($" Items: {section.Items.Count}, Size: {section.Size} bytes"); } Console.WriteLine(); } private SectionInfo TryParseSection(int offset) { // Try to identify section start // Common pattern: Pascal string followed by items if (offset + 10 > _data.Length) return null; // Look for string marker if (_data[offset] != 0x07) return null; byte nameLen = _data[offset + 1]; if (nameLen < 3 || nameLen > 50 || offset + 2 + nameLen > _data.Length) return null; string name = Encoding.UTF8.GetString(_data, offset + 2, nameLen); // Validate name if (!name.All(c => char.IsLetterOrDigit(c) || c == '_' || c == ' ')) return null; var section = new SectionInfo { StartOffset = offset, Name = name, NameLength = nameLen }; int pos = offset + 2 + nameLen; // Parse items in section until we hit another section or Rules int itemCount = 0; while (pos < _data.Length - 10 && itemCount < 100) { var item = TryParseItem(pos); if (item != null) { section.Items.Add(item); pos = item.EndOffset; itemCount++; } else { // Check if we hit next section or Rules if (IsLikelyNextSection(pos)) break; pos++; } } section.EndOffset = pos; section.Size = pos - offset; return section.Items.Count > 0 ? section : null; } private ItemInfo TryParseItem(int offset) { if (offset + 5 > _data.Length) return null; // Item pattern: 07 if (_data[offset] != 0x07) return null; byte keyLen = _data[offset + 1]; if (keyLen < 2 || keyLen > 40 || offset + 2 + keyLen + 2 > _data.Length) return null; string key = Encoding.UTF8.GetString(_data, offset + 2, keyLen); // Validate key if (!key.All(c => char.IsLetterOrDigit(c) || c == '_' || c == '-')) return null; int valueOffset = offset + 2 + keyLen; byte typeMarker = _data[valueOffset]; var item = new ItemInfo { StartOffset = offset, Key = key, TypeMarker = typeMarker }; // Parse value based on type switch (typeMarker) { case 0x01: // Boolean if (valueOffset + 2 <= _data.Length) { item.Value = _data[valueOffset + 1] != 0; item.ValueType = "boolean"; item.EndOffset = valueOffset + 2; return item; } break; case 0x04: // Integer if (valueOffset + 5 <= _data.Length) { item.Value = BitConverter.ToInt32(_data, valueOffset + 1); item.ValueType = "integer"; item.EndOffset = valueOffset + 5; return item; } break; case 0x07: // String if (valueOffset + 2 <= _data.Length) { byte strLen = _data[valueOffset + 1]; if (strLen > 0 && valueOffset + 2 + strLen <= _data.Length) { item.Value = Encoding.UTF8.GetString(_data, valueOffset + 2, strLen); item.ValueType = "string"; item.EndOffset = valueOffset + 2 + strLen; return item; } } break; } return null; } private bool IsLikelyNextSection(int pos) { if (pos + 10 > _data.Length) return false; // Check for Rules marker if (_data[pos] == 0x05 && pos + 6 <= _data.Length) { string marker = Encoding.UTF8.GetString(_data, pos + 1, 5); if (marker == "Rules") return true; } // Check for section name pattern if (_data[pos] == 0x07) { byte len = _data[pos + 1]; if (len >= 5 && len <= 30 && pos + 2 + len <= _data.Length) { string name = Encoding.UTF8.GetString(_data, pos + 2, len); return name.All(c => char.IsLetterOrDigit(c) || c == '_'); } } return false; } private List FindPattern(byte[] data, byte[] pattern) { var offsets = new List(); for (int i = 0; i <= data.Length - pattern.Length; i++) { bool match = true; for (int j = 0; j < pattern.Length; j++) { if (data[i + j] != pattern[j]) { match = false; break; } } if (match) offsets.Add(i); } return offsets; } } public class BinaryStructureReport { public int FileSize { get; set; } public bool HeaderHasNullPrefix { get; set; } public string Header { get; set; } = ""; public int HeaderEndOffset { get; set; } public Dictionary> Markers { get; set; } = new(); public Dictionary DataTypeCounts { get; set; } = new(); public List Sections { get; set; } = new(); } public class SectionInfo { public int StartOffset { get; set; } public int EndOffset { get; set; } public int Size { get; set; } public string Name { get; set; } = ""; public int NameLength { get; set; } public List Items { get; set; } = new(); } public class ItemInfo { public int StartOffset { get; set; } public int EndOffset { get; set; } public string Key { get; set; } = ""; public byte TypeMarker { get; set; } public string ValueType { get; set; } = ""; public object Value { get; set; } } }