 90dc39fbe8
			
		
	
	90dc39fbe8
	
	
	
		
			
			🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			333 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			333 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| /*
 | |
|  * Copyright (C) 2024 C# Port
 | |
|  * 
 | |
|  * V2GDecoderNet - C# port of OpenV2G EXI codec
 | |
|  * 
 | |
|  * This program is free software: you can redistribute it and/or modify
 | |
|  * it under the terms of the GNU Lesser General Public License as published
 | |
|  * by the Free Software Foundation, either version 3 of the License, or
 | |
|  * (at your option) any later version.
 | |
|  */
 | |
| 
 | |
| using V2GDecoderNet.EXI;
 | |
| using V2GDecoderNet.V2G;
 | |
| 
 | |
| namespace V2GDecoderNet
 | |
| {
 | |
|     class Program
 | |
|     {
 | |
|         static void MainOriginal(string[] args)
 | |
|         {
 | |
|             Console.WriteLine("=== V2GDecoderNet - C# EXI Codec ===");
 | |
|             Console.WriteLine("OpenV2G C# Port v1.0.0");
 | |
|             Console.WriteLine();
 | |
| 
 | |
|             if (args.Length < 1)
 | |
|             {
 | |
|                 ShowUsage();
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             try
 | |
|             {
 | |
|                 string command = args[0].ToLower();
 | |
|                 
 | |
|                 switch (command)
 | |
|                 {
 | |
|                     case "decode":
 | |
|                         if (args.Length < 2)
 | |
|                         {
 | |
|                             Console.WriteLine("Error: Input file required for decode command");
 | |
|                             ShowUsage();
 | |
|                             return;
 | |
|                         }
 | |
|                         DecodeFile(args[1], args.Length > 2 ? args[2] : null);
 | |
|                         break;
 | |
|                         
 | |
|                     case "encode":
 | |
|                         if (args.Length < 2)
 | |
|                         {
 | |
|                             Console.WriteLine("Error: Input file required for encode command");
 | |
|                             ShowUsage();
 | |
|                             return;
 | |
|                         }
 | |
|                         EncodeFile(args[1], args.Length > 2 ? args[2] : null);
 | |
|                         break;
 | |
|                         
 | |
|                     case "test":
 | |
|                         RunRoundtripTest(args.Length > 1 ? args[1] : "../../test1.exi");
 | |
|                         break;
 | |
|                         
 | |
|                     case "analyze":
 | |
|                         if (args.Length < 2)
 | |
|                         {
 | |
|                             Console.WriteLine("Error: Input file required for analyze command");
 | |
|                             ShowUsage();
 | |
|                             return;
 | |
|                         }
 | |
|                         AnalyzeFile(args[1]);
 | |
|                         break;
 | |
|                         
 | |
|                     default:
 | |
|                         Console.WriteLine($"Error: Unknown command '{command}'");
 | |
|                         ShowUsage();
 | |
|                         break;
 | |
|                 }
 | |
|             }
 | |
|             catch (Exception ex)
 | |
|             {
 | |
|                 Console.WriteLine($"Error: {ex.Message}");
 | |
|                 if (ex is EXIException exiEx)
 | |
|                 {
 | |
|                     Console.WriteLine($"EXI Error Code: {exiEx.ErrorCode}");
 | |
|                 }
 | |
| #if DEBUG
 | |
|                 Console.WriteLine($"Stack Trace: {ex.StackTrace}");
 | |
| #endif
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         static void ShowUsage()
 | |
|         {
 | |
|             Console.WriteLine("Usage:");
 | |
|             Console.WriteLine("  V2GDecoderNet decode <input.exi> [output.xml]   - Decode EXI to XML");
 | |
|             Console.WriteLine("  V2GDecoderNet encode <input.xml> [output.exi]   - Encode XML to EXI");  
 | |
|             Console.WriteLine("  V2GDecoderNet test [input.exi]                  - Run roundtrip test");
 | |
|             Console.WriteLine("  V2GDecoderNet analyze <input.exi>               - Analyze EXI structure");
 | |
|             Console.WriteLine();
 | |
|             Console.WriteLine("Examples:");
 | |
|             Console.WriteLine("  V2GDecoderNet decode test1.exi test1.xml");
 | |
|             Console.WriteLine("  V2GDecoderNet encode test1.xml test1_new.exi");
 | |
|             Console.WriteLine("  V2GDecoderNet test test1.exi");
 | |
|         }
 | |
| 
 | |
|         static void DecodeFile(string inputFile, string? outputFile = null)
 | |
|         {
 | |
|             Console.WriteLine($"Decoding: {inputFile}");
 | |
|             
 | |
|             if (!File.Exists(inputFile))
 | |
|             {
 | |
|                 throw new FileNotFoundException($"Input file not found: {inputFile}");
 | |
|             }
 | |
| 
 | |
|             // Read EXI data
 | |
|             var result = ByteStream.ReadBytesFromFile(inputFile, out byte[] exiData, out int bytesRead);
 | |
|             if (result != 0)
 | |
|             {
 | |
|                 throw new EXIException(result, $"Failed to read input file: {inputFile}");
 | |
|             }
 | |
| 
 | |
|             Console.WriteLine($"Read {bytesRead} bytes from {inputFile}");
 | |
| 
 | |
|             // Extract EXI body from V2GTP data if present
 | |
|             byte[] exiBody = V2GProtocol.ExtractEXIBody(exiData);
 | |
|             
 | |
|             if (exiBody.Length != exiData.Length)
 | |
|             {
 | |
|                 Console.WriteLine($"Extracted EXI body: {exiBody.Length} bytes (V2GTP header removed)");
 | |
|             }
 | |
| 
 | |
|             // Analyze packet structure
 | |
|             var analysis = V2GProtocol.AnalyzeDataStructure(exiData);
 | |
|             Console.WriteLine($"Packet structure: {analysis}");
 | |
| 
 | |
|             // Decode EXI to XML - use simplified decoder for now
 | |
|             var simpleDecoder = new SimpleV2GDecoder();
 | |
|             string xmlOutput = simpleDecoder.DecodeToSimpleXml(exiBody);
 | |
| 
 | |
|             // Determine output file name
 | |
|             outputFile ??= Path.ChangeExtension(inputFile, ".xml");
 | |
| 
 | |
|             // Write XML output
 | |
|             File.WriteAllText(outputFile, xmlOutput);
 | |
|             Console.WriteLine($"XML written to: {outputFile}");
 | |
|             Console.WriteLine($"XML size: {xmlOutput.Length} characters");
 | |
|         }
 | |
| 
 | |
|         static void EncodeFile(string inputFile, string? outputFile = null)
 | |
|         {
 | |
|             Console.WriteLine($"Encoding: {inputFile}");
 | |
|             
 | |
|             if (!File.Exists(inputFile))
 | |
|             {
 | |
|                 throw new FileNotFoundException($"Input file not found: {inputFile}");
 | |
|             }
 | |
| 
 | |
|             // Read XML data
 | |
|             string xmlContent = File.ReadAllText(inputFile);
 | |
|             Console.WriteLine($"Read {xmlContent.Length} characters from {inputFile}");
 | |
| 
 | |
|             // Encode XML to EXI - use simplified encoder for now
 | |
|             var simpleEncoder = new SimpleV2GEncoder();
 | |
|             byte[] exiData = simpleEncoder.EncodeToSimpleEXI(xmlContent);
 | |
| 
 | |
|             // Determine output file name
 | |
|             outputFile ??= Path.ChangeExtension(inputFile, ".exi");
 | |
| 
 | |
|             // Write EXI output
 | |
|             int writeResult = ByteStream.WriteBytesToFile(exiData, outputFile);
 | |
|             if (writeResult != 0)
 | |
|             {
 | |
|                 throw new EXIException(writeResult, $"Failed to write output file: {outputFile}");
 | |
|             }
 | |
| 
 | |
|             Console.WriteLine($"EXI written to: {outputFile}");
 | |
|             Console.WriteLine($"EXI size: {exiData.Length} bytes");
 | |
|         }
 | |
| 
 | |
|         static void AnalyzeFile(string inputFile)
 | |
|         {
 | |
|             Console.WriteLine($"Analyzing: {inputFile}");
 | |
|             
 | |
|             if (!File.Exists(inputFile))
 | |
|             {
 | |
|                 throw new FileNotFoundException($"Input file not found: {inputFile}");
 | |
|             }
 | |
| 
 | |
|             // Read file data
 | |
|             var result = ByteStream.ReadBytesFromFile(inputFile, out byte[] data, out int bytesRead);
 | |
|             if (result != 0)
 | |
|             {
 | |
|                 throw new EXIException(result, $"Failed to read input file: {inputFile}");
 | |
|             }
 | |
| 
 | |
|             Console.WriteLine($"File size: {bytesRead} bytes");
 | |
| 
 | |
|             // Analyze packet structure
 | |
|             var analysis = V2GProtocol.AnalyzeDataStructure(data);
 | |
|             Console.WriteLine();
 | |
|             Console.WriteLine("=== Data Structure Analysis ===");
 | |
|             Console.WriteLine(analysis);
 | |
|             Console.WriteLine();
 | |
| 
 | |
|             // Show hex dump of first 64 bytes
 | |
|             int dumpSize = Math.Min(64, data.Length);
 | |
|             Console.WriteLine($"Hex dump (first {dumpSize} bytes):");
 | |
|             string hexDump = ByteStream.ByteArrayToHexString(data.Take(dumpSize).ToArray());
 | |
|             
 | |
|             for (int i = 0; i < hexDump.Length; i += 32)
 | |
|             {
 | |
|                 int length = Math.Min(32, hexDump.Length - i);
 | |
|                 string line = hexDump.Substring(i, length);
 | |
|                 
 | |
|                 // Format as pairs
 | |
|                 var pairs = new List<string>();
 | |
|                 for (int j = 0; j < line.Length; j += 2)
 | |
|                 {
 | |
|                     pairs.Add(line.Substring(j, Math.Min(2, line.Length - j)));
 | |
|                 }
 | |
|                 
 | |
|                 Console.WriteLine($"{i/2:X4}: {string.Join(" ", pairs)}");
 | |
|             }
 | |
| 
 | |
|             // If it has EXI content, try to decode header
 | |
|             byte[] exiBody = V2GProtocol.ExtractEXIBody(data);
 | |
|             if (exiBody.Length > 0)
 | |
|             {
 | |
|                 Console.WriteLine();
 | |
|                 Console.WriteLine("=== EXI Header Analysis ===");
 | |
|                 
 | |
|                 try
 | |
|                 {
 | |
|                     var decoder = new EXIDecoder();
 | |
|                     var inputStream = new BitInputStream(exiBody);
 | |
|                     var header = decoder.DecodeHeader(inputStream);
 | |
|                     
 | |
|                     Console.WriteLine($"Has Cookie: {header.HasCookie}");
 | |
|                     Console.WriteLine($"Format Version: {header.FormatVersion}");
 | |
|                     Console.WriteLine($"Preserve Comments: {header.PreserveComments}");
 | |
|                     Console.WriteLine($"Preserve PIs: {header.PreservePIs}");
 | |
|                     Console.WriteLine($"Preserve DTD: {header.PreserveDTD}");
 | |
|                     Console.WriteLine($"Preserve Prefixes: {header.PreservePrefixes}");
 | |
|                 }
 | |
|                 catch (Exception ex)
 | |
|                 {
 | |
|                     Console.WriteLine($"Header analysis failed: {ex.Message}");
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         static void RunRoundtripTest(string inputFile)
 | |
|         {
 | |
|             Console.WriteLine($"Running roundtrip test on: {inputFile}");
 | |
|             
 | |
|             if (!File.Exists(inputFile))
 | |
|             {
 | |
|                 throw new FileNotFoundException($"Input file not found: {inputFile}");
 | |
|             }
 | |
| 
 | |
|             // Step 1: Read original EXI file
 | |
|             var result = ByteStream.ReadBytesFromFile(inputFile, out byte[] originalExi, out int originalSize);
 | |
|             if (result != 0)
 | |
|             {
 | |
|                 throw new EXIException(result, $"Failed to read input file: {inputFile}");
 | |
|             }
 | |
| 
 | |
|             Console.WriteLine($"Original EXI size: {originalSize} bytes");
 | |
| 
 | |
|             // Step 2: Decode EXI to XML - use simplified decoder for now
 | |
|             byte[] exiBody = V2GProtocol.ExtractEXIBody(originalExi);
 | |
|             var simpleDecoder = new SimpleV2GDecoder();
 | |
|             string xmlContent = simpleDecoder.DecodeToSimpleXml(exiBody);
 | |
| 
 | |
|             string xmlFile = Path.ChangeExtension(inputFile, ".xml");
 | |
|             File.WriteAllText(xmlFile, xmlContent);
 | |
|             Console.WriteLine($"Decoded to XML: {xmlFile} ({xmlContent.Length} characters)");
 | |
| 
 | |
|             // Step 3: Encode XML back to EXI - use simplified encoder for now
 | |
|             var simpleEncoder = new SimpleV2GEncoder();
 | |
|             byte[] newExi = simpleEncoder.EncodeToSimpleEXI(xmlContent);
 | |
| 
 | |
|             string newExiFile = Path.ChangeExtension(inputFile, "_new.exi");
 | |
|             int writeResult = ByteStream.WriteBytesToFile(newExi, newExiFile);
 | |
|             if (writeResult != 0)
 | |
|             {
 | |
|                 throw new EXIException(writeResult, $"Failed to write output file: {newExiFile}");
 | |
|             }
 | |
| 
 | |
|             Console.WriteLine($"Encoded to EXI: {newExiFile} ({newExi.Length} bytes)");
 | |
| 
 | |
|             // Step 4: Compare original vs new EXI
 | |
|             bool identical = exiBody.SequenceEqual(newExi);
 | |
|             
 | |
|             Console.WriteLine();
 | |
|             Console.WriteLine("=== Roundtrip Test Results ===");
 | |
|             Console.WriteLine($"Original EXI body: {exiBody.Length} bytes");
 | |
|             Console.WriteLine($"New EXI:           {newExi.Length} bytes");
 | |
|             Console.WriteLine($"Files identical:   {(identical ? "YES ✓" : "NO ✗")}");
 | |
| 
 | |
|             if (!identical)
 | |
|             {
 | |
|                 Console.WriteLine();
 | |
|                 Console.WriteLine("Differences found:");
 | |
|                 int maxCompare = Math.Min(exiBody.Length, newExi.Length);
 | |
|                 int differences = 0;
 | |
|                 
 | |
|                 for (int i = 0; i < maxCompare; i++)
 | |
|                 {
 | |
|                     if (exiBody[i] != newExi[i])
 | |
|                     {
 | |
|                         differences++;
 | |
|                         if (differences <= 10) // Show first 10 differences
 | |
|                         {
 | |
|                             Console.WriteLine($"  Offset {i:X4}: {exiBody[i]:X2} -> {newExi[i]:X2}");
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|                 
 | |
|                 if (differences > 10)
 | |
|                 {
 | |
|                     Console.WriteLine($"  ... and {differences - 10} more differences");
 | |
|                 }
 | |
|                 
 | |
|                 if (exiBody.Length != newExi.Length)
 | |
|                 {
 | |
|                     Console.WriteLine($"  Size difference: {newExi.Length - exiBody.Length} bytes");
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             Console.WriteLine();
 | |
|             Console.WriteLine(identical ? "✓ Roundtrip test PASSED" : "✗ Roundtrip test FAILED");
 | |
|         }
 | |
|     }
 | |
| } |