Major architectural refactoring to achieve 1:1 structural compatibility: 🏗️ **VC2022 Structure Replication** - Iso1EXIDocument: 1:1 replica of VC2022 iso1EXIDocument struct - DinEXIDocument: 1:1 replica of VC2022 dinEXIDocument struct - Iso2EXIDocument: 1:1 replica of VC2022 iso2EXIDocument struct - All _isUsed flags and Initialize() methods exactly matching VC2022 🔄 **VC2022 Function Porting** - ParseXmlToIso1(): Exact port of VC2022 parse_xml_to_iso1() - EncodeIso1ExiDocument(): Exact port of VC2022 encode_iso1ExiDocument() - Choice 76 (V2G_Message) encoding with identical logic - BulkChargingComplete ignore behavior preserved ⚡ **Call Sequence Alignment** - Old: EncodeV2GMessage() → direct EXI encoding - New: EncodeV2GMessage() → Iso1EXIDocument → EncodeIso1ExiDocument() - Exact VC2022 call chain: init → parse → encode → finish 🔍 **1:1 Debug Comparison Ready** - C# exiDoc.V2G_Message_isUsed ↔ VC2022 exiDoc->V2G_Message_isUsed - Identical structure enables line-by-line debugging comparison - Ready for precise 1-byte difference investigation (41 vs 42 bytes) 📁 **Project Reorganization** - Moved from csharp/ to Port/ for cleaner structure - Port/dotnet/ and Port/vc2022/ for parallel development - Complete build system and documentation updates 🎯 **Achievement**: 97.6% binary compatibility (41/42 bytes) Next: 1:1 debug session to identify exact byte difference location 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			174 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			174 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| /*
 | |
|  * 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
 | |
| {
 | |
|     /// <summary>
 | |
|     /// EXI Error codes - exact match to C implementation
 | |
|     /// </summary>
 | |
|     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;
 | |
|     }
 | |
| 
 | |
|     /// <summary>
 | |
|     /// EXI Header decoder - exact implementation of EXIHeaderDecoder.c
 | |
|     /// </summary>
 | |
|     public static class EXIHeaderDecoderExact
 | |
|     {
 | |
|         /// <summary>
 | |
|         /// Decode EXI header - exact implementation of decodeEXIHeader()
 | |
|         /// </summary>
 | |
|         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;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /// <summary>
 | |
|     /// EXI Header encoder - exact implementation of EXIHeaderEncoder.c
 | |
|     /// </summary>
 | |
|     public static class EXIHeaderEncoderExact
 | |
|     {
 | |
|         /// <summary>
 | |
|         /// Encode EXI header - exact implementation of encodeEXIHeader()
 | |
|         /// Always writes simple header format (0x80 = 128)
 | |
|         /// </summary>
 | |
|         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;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /// <summary>
 | |
|     /// EXI Exception for exact error handling
 | |
|     /// </summary>
 | |
|     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}"
 | |
|             };
 | |
|         }
 | |
|     }
 | |
| } |