This commit adds comprehensive C# ports of the OpenV2G EXI codec to support both modern .NET and legacy .NET Framework environments. ## .NET 8.0 Version (csharp/dotnet/) - Full-featured port with complete EXI codec implementation - Modern C# features (nullable types, switch expressions, using declarations) - Comprehensive roundtrip testing functionality - Successfully processes all test files (test1.exi - test5.exi) - Supports decode/encode/analyze/test commands ## .NET Framework 4.8 Version (csharp/dotnetfx/) - Simplified but functional port for legacy environments - C# 7.3 compatible codebase - Core V2GTP protocol parsing and analysis - Roundtrip demonstration functionality - Successfully processes all test files ## Validation Results Both versions successfully tested with all available test files: - test1.exi (131 bytes) → XML → EXI roundtrip ✓ - test2.exi (51 bytes) → XML → EXI roundtrip ✓ - test3.exi (43 bytes) → XML → EXI roundtrip ✓ - test4.exi (43 bytes) → XML → EXI roundtrip ✓ - test5.exi (43 bytes) → XML → EXI roundtrip ✓ ## Technical Implementation - Proper V2GTP header parsing and EXI body extraction - XML generation with valid structure for testing - Binary EXI encoding for roundtrip validation - Cross-platform compatibility maintained - Build systems: dotnet CLI (.NET 8.0) and MSBuild (.NET FX 4.8) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
202 lines
6.8 KiB
C#
202 lines
6.8 KiB
C#
/*
|
|
* Copyright (C) 2007-2024 C# Port
|
|
* Original Copyright (C) 2007-2018 Siemens AG
|
|
*
|
|
* 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 System;
|
|
using System.IO;
|
|
using System.Linq;
|
|
|
|
namespace V2GDecoderNetFx.EXI
|
|
{
|
|
/// <summary>
|
|
/// Byte Stream utilities for file operations
|
|
/// </summary>
|
|
public static class ByteStream
|
|
{
|
|
/// <summary>
|
|
/// Write bytes to file
|
|
/// </summary>
|
|
/// <param name="data">byte array</param>
|
|
/// <param name="filename">File name</param>
|
|
/// <returns>Error-Code != 0 on failure</returns>
|
|
public static int WriteBytesToFile(byte[] data, string filename)
|
|
{
|
|
try
|
|
{
|
|
if (data == null)
|
|
return EXIErrorCodes.EXI_ERROR_OUT_OF_BYTE_BUFFER;
|
|
|
|
if (string.IsNullOrEmpty(filename))
|
|
return EXIErrorCodes.EXI_ERROR_OUTPUT_FILE;
|
|
|
|
File.WriteAllBytes(filename, data);
|
|
return 0; // Success
|
|
}
|
|
catch (UnauthorizedAccessException)
|
|
{
|
|
return EXIErrorCodes.EXI_ERROR_OUTPUT_FILE;
|
|
}
|
|
catch (DirectoryNotFoundException)
|
|
{
|
|
return EXIErrorCodes.EXI_ERROR_OUTPUT_FILE;
|
|
}
|
|
catch (IOException)
|
|
{
|
|
return EXIErrorCodes.EXI_ERROR_OUTPUT_FILE;
|
|
}
|
|
catch
|
|
{
|
|
return EXIErrorCodes.EXI_ERROR_OUTPUT_FILE;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Read bytes from file
|
|
/// </summary>
|
|
/// <param name="filename">File name</param>
|
|
/// <param name="data">Output byte array</param>
|
|
/// <param name="bytesRead">Number of bytes actually read</param>
|
|
/// <returns>Error-Code != 0 on failure</returns>
|
|
public static int ReadBytesFromFile(string filename, out byte[] data, out int bytesRead)
|
|
{
|
|
data = new byte[0];
|
|
bytesRead = 0;
|
|
|
|
try
|
|
{
|
|
if (string.IsNullOrEmpty(filename))
|
|
return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
|
|
|
|
if (!File.Exists(filename))
|
|
return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
|
|
|
|
data = File.ReadAllBytes(filename);
|
|
bytesRead = data.Length;
|
|
return 0; // Success
|
|
}
|
|
catch (UnauthorizedAccessException)
|
|
{
|
|
return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
|
|
}
|
|
catch (DirectoryNotFoundException)
|
|
{
|
|
return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
|
|
}
|
|
catch (FileNotFoundException)
|
|
{
|
|
return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
|
|
}
|
|
catch (IOException)
|
|
{
|
|
return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
|
|
}
|
|
catch
|
|
{
|
|
return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Read bytes from file with buffer size limit
|
|
/// </summary>
|
|
/// <param name="filename">File name</param>
|
|
/// <param name="maxSize">Maximum buffer size</param>
|
|
/// <param name="data">Output byte array</param>
|
|
/// <param name="bytesRead">Number of bytes actually read</param>
|
|
/// <returns>Error-Code != 0 on failure</returns>
|
|
public static int ReadBytesFromFile(string filename, int maxSize, out byte[] data, out int bytesRead)
|
|
{
|
|
data = new byte[0];
|
|
bytesRead = 0;
|
|
|
|
try
|
|
{
|
|
if (string.IsNullOrEmpty(filename))
|
|
return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
|
|
|
|
if (!File.Exists(filename))
|
|
return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
|
|
|
|
using (var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read))
|
|
{
|
|
var fileSize = (int)fileStream.Length;
|
|
|
|
if (fileSize > maxSize)
|
|
return EXIErrorCodes.EXI_ERROR_OUT_OF_BYTE_BUFFER;
|
|
|
|
data = new byte[fileSize];
|
|
bytesRead = fileStream.Read(data, 0, fileSize);
|
|
|
|
return 0; // Success
|
|
}
|
|
}
|
|
catch (UnauthorizedAccessException)
|
|
{
|
|
return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
|
|
}
|
|
catch (DirectoryNotFoundException)
|
|
{
|
|
return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
|
|
}
|
|
catch (FileNotFoundException)
|
|
{
|
|
return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
|
|
}
|
|
catch (IOException)
|
|
{
|
|
return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
|
|
}
|
|
catch
|
|
{
|
|
return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Convert hex string to byte array
|
|
/// </summary>
|
|
/// <param name="hex">Hex string</param>
|
|
/// <returns>Byte array</returns>
|
|
public static byte[] HexStringToByteArray(string hex)
|
|
{
|
|
if (string.IsNullOrEmpty(hex))
|
|
return new byte[0];
|
|
|
|
// Remove any whitespace or separators
|
|
hex = hex.Replace(" ", "").Replace("-", "").Replace(":", "");
|
|
|
|
if (hex.Length % 2 != 0)
|
|
throw new ArgumentException("Hex string must have even number of characters");
|
|
|
|
var result = new byte[hex.Length / 2];
|
|
for (int i = 0; i < result.Length; i++)
|
|
{
|
|
if (!byte.TryParse(hex.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber, null, out result[i]))
|
|
throw new ArgumentException($"Invalid hex characters at position {i * 2}");
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Convert byte array to hex string
|
|
/// </summary>
|
|
/// <param name="data">Byte array</param>
|
|
/// <param name="uppercase">Use uppercase hex digits</param>
|
|
/// <returns>Hex string</returns>
|
|
public static string ByteArrayToHexString(byte[] data, bool uppercase = true)
|
|
{
|
|
if (data == null || data.Length == 0)
|
|
return string.Empty;
|
|
|
|
var format = uppercase ? "X2" : "x2";
|
|
return string.Concat(data.Select(b => b.ToString(format)));
|
|
}
|
|
}
|
|
} |