/* * Copyright (C) 2007-2024 C# Port * Original Copyright (C) 2007-2018 Siemens AG * * Exact EXI Header implementation - byte-compatible with OpenV2G * Matches EXIHeaderDecoder.c and EXIHeaderEncoder.c exactly */ using System; namespace V2GDecoderNet.EXI { /// /// EXI Error codes - exact match to C implementation /// public static class EXIErrorCodesExact { public const int EXI_OK = 0; public const int EXI_ERROR_UNEXPECTED_END_OF_STREAM = -1; public const int EXI_UNSUPPORTED_HEADER_COOKIE = -2; public const int EXI_UNSUPPORTED_HEADER_OPTIONS = -3; public const int EXI_ERROR_UNKNOWN_EVENT = -4; public const int EXI_ERROR_OUT_OF_BYTE_BUFFER = -5; public const int EXI_ERROR_OUT_OF_BOUNDS = -6; public const int EXI_ERROR_STRINGVALUES_NOT_SUPPORTED = -7; public const int EXI_ERROR_NOT_IMPLEMENTED_YET = -8; } /// /// EXI Header decoder - exact implementation of EXIHeaderDecoder.c /// public static class EXIHeaderDecoderExact { /// /// Decode EXI header - exact implementation of decodeEXIHeader() /// public static int DecodeHeader(BitInputStreamExact stream, EXIHeaderExact header) { if (stream == null) throw new ArgumentNullException(nameof(stream)); if (header == null) throw new ArgumentNullException(nameof(header)); // Read the header byte int headerByte = stream.ReadBits(8); if (headerByte < 0) return EXIErrorCodesExact.EXI_ERROR_UNEXPECTED_END_OF_STREAM; byte header_b = (byte)headerByte; // Check for EXI Cookie - not supported in this implementation if (header_b == 0x24) // '$' character { return EXIErrorCodesExact.EXI_UNSUPPORTED_HEADER_COOKIE; } // Check presence bit for EXI Options (bit 5, value 0x20) if ((header_b & 0x20) != 0) { return EXIErrorCodesExact.EXI_UNSUPPORTED_HEADER_OPTIONS; } // Parse simple header format (distinguishing bits = "1") // Bit pattern: 1 | Version[4] | Presence[1] | Format[2] // Extract format version (bits 6-3, mask 0x1E, shift right 1) header.FormatVersion = (byte)((header_b & 0x1E) >> 1); // Extract format field (bits 1-0, mask 0x03) byte format = (byte)(header_b & 0x03); // Set preservation options based on format field switch (format) { case 0: // Format 00: No preservation header.PreserveComments = false; header.PreservePIs = false; header.PreserveDTD = false; header.PreservePrefixes = false; break; case 1: // Format 01: Preserve comments and PIs header.PreserveComments = true; header.PreservePIs = true; header.PreserveDTD = false; header.PreservePrefixes = false; break; case 2: // Format 10: Preserve DTD and prefixes header.PreserveComments = false; header.PreservePIs = false; header.PreserveDTD = true; header.PreservePrefixes = true; break; case 3: // Format 11: Preserve all header.PreserveComments = true; header.PreservePIs = true; header.PreserveDTD = true; header.PreservePrefixes = true; break; } // Header always has no cookie in this implementation header.HasCookie = false; return EXIErrorCodesExact.EXI_OK; } } /// /// EXI Header encoder - exact implementation of EXIHeaderEncoder.c /// public static class EXIHeaderEncoderExact { /// /// Encode EXI header - exact implementation of encodeEXIHeader() /// Always writes simple header format (0x80 = 128) /// public static int EncodeHeader(BitOutputStreamExact stream, EXIHeaderExact header) { if (stream == null) throw new ArgumentNullException(nameof(stream)); if (header == null) throw new ArgumentNullException(nameof(header)); try { // Simple header format: always write 128 (0x80) // Bit pattern: 1 0000 0 00 = 10000000 = 0x80 = 128 // - Distinguishing bit: 1 // - Version: 0000 (format version 0) // - Presence bit: 0 (no options) // - Format: 00 (no preservation) stream.WriteBits(8, EXIConstantsExact.EXI_HEADER_SIMPLE); return EXIErrorCodesExact.EXI_OK; } catch { return EXIErrorCodesExact.EXI_ERROR_OUT_OF_BYTE_BUFFER; } } } /// /// EXI Exception for exact error handling /// public class EXIExceptionExact : Exception { public int ErrorCode { get; } public EXIExceptionExact(int errorCode, string message) : base(message) { ErrorCode = errorCode; } public EXIExceptionExact(int errorCode, string message, Exception innerException) : base(message, innerException) { ErrorCode = errorCode; } public static string GetErrorMessage(int errorCode) { return errorCode switch { EXIErrorCodesExact.EXI_OK => "No error", EXIErrorCodesExact.EXI_ERROR_UNEXPECTED_END_OF_STREAM => "Unexpected end of stream", EXIErrorCodesExact.EXI_UNSUPPORTED_HEADER_COOKIE => "EXI header cookie not supported", EXIErrorCodesExact.EXI_UNSUPPORTED_HEADER_OPTIONS => "EXI header options not supported", EXIErrorCodesExact.EXI_ERROR_UNKNOWN_EVENT => "Unknown EXI event", EXIErrorCodesExact.EXI_ERROR_OUT_OF_BYTE_BUFFER => "Output buffer overflow", EXIErrorCodesExact.EXI_ERROR_OUT_OF_BOUNDS => "Index out of bounds", EXIErrorCodesExact.EXI_ERROR_STRINGVALUES_NOT_SUPPORTED => "String values not supported", EXIErrorCodesExact.EXI_ERROR_NOT_IMPLEMENTED_YET => "Feature not implemented", _ => $"Unknown error code: {errorCode}" }; } } }