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>
198 lines
6.7 KiB
C#
198 lines
6.7 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.IO;
|
|
|
|
namespace V2GDecoderNet.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 = Array.Empty<byte>();
|
|
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 = Array.Empty<byte>();
|
|
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 Array.Empty<byte>();
|
|
|
|
// 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)));
|
|
}
|
|
}
|
|
} |