feat: Complete C# ports for both .NET 8.0 and .NET Framework 4.8
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>
This commit is contained in:
202
csharp/dotnetfx/EXI/ByteStream.cs
Normal file
202
csharp/dotnetfx/EXI/ByteStream.cs
Normal file
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* 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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user