Add -out option for file output and fix stdin detection logic
- Add -out/-o option to save decode/encode output to files - Encoded files are saved as binary, console still shows hex strings - Decoded XML files are saved as text - Fix stdin detection logic to prioritize command line arguments - Add missing System.Collections.Generic import for List<string> usage - File extension auto-processing now supports -out option - Clean up data folder with renamed files (data0-3.dump/xml) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
276
Program.cs
276
Program.cs
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
@@ -8,9 +9,43 @@ namespace V2GProtocol
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Console.WriteLine("=== V2G Transfer Protocol Decoder/Encoder ===");
|
||||
Console.WriteLine("ISO 15118-2 / DIN SPEC 70121 / SAP Protocol");
|
||||
Console.WriteLine();
|
||||
// Only show header for help or analysis mode (not for encode/decode operations)
|
||||
bool showHeader = false;
|
||||
|
||||
if (args.Length == 0)
|
||||
{
|
||||
showHeader = true;
|
||||
}
|
||||
else if (args.Length > 0)
|
||||
{
|
||||
string firstArg = args[0].ToLower();
|
||||
|
||||
// Check if it's help command
|
||||
if (firstArg == "--help" || firstArg == "-h")
|
||||
{
|
||||
showHeader = true;
|
||||
}
|
||||
// Check if it's a file for analysis mode (only .dump/.txt without options)
|
||||
else if (File.Exists(args[0]) && args.Length == 1)
|
||||
{
|
||||
string extension = Path.GetExtension(args[0]).ToLower();
|
||||
|
||||
// Only show header for analysis mode (.dump/.txt)
|
||||
// NOT for auto-conversion (.hex, .xml)
|
||||
if (extension == ".dump" || extension == ".txt")
|
||||
{
|
||||
showHeader = true;
|
||||
}
|
||||
// .hex and .xml are auto-conversion, no header
|
||||
}
|
||||
}
|
||||
|
||||
if (showHeader)
|
||||
{
|
||||
Console.WriteLine("=== V2G Transfer Protocol Decoder/Encoder ===");
|
||||
Console.WriteLine("ISO 15118-2 / DIN SPEC 70121 / SAP Protocol");
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
@@ -45,20 +80,58 @@ namespace V2GProtocol
|
||||
// 두 번째 인자가 옵션인 경우 (filename --decode, filename --encode)
|
||||
else if (args.Length >= 2 && (args[1].ToLower() == "--decode" || args[1].ToLower() == "-d"))
|
||||
{
|
||||
// filename --decode 형태
|
||||
string[] newArgs = { args[1], args[0] }; // 순서를 바꿔서 --decode filename 형태로 만듦
|
||||
HandleDecodeCommand(newArgs);
|
||||
// filename --decode 형태 - 나머지 인자들도 전달
|
||||
var newArgs = new List<string> { args[1], args[0] };
|
||||
for (int i = 2; i < args.Length; i++)
|
||||
newArgs.Add(args[i]);
|
||||
HandleDecodeCommand(newArgs.ToArray());
|
||||
}
|
||||
else if (args.Length >= 2 && (args[1].ToLower() == "--encode" || args[1].ToLower() == "-e"))
|
||||
{
|
||||
// filename --encode 형태
|
||||
string[] newArgs = { args[1], args[0] }; // 순서를 바꿔서 --encode filename 형태로 만듦
|
||||
HandleEncodeCommand(newArgs);
|
||||
// filename --encode 형태 - 나머지 인자들도 전달
|
||||
var newArgs = new List<string> { args[1], args[0] };
|
||||
for (int i = 2; i < args.Length; i++)
|
||||
newArgs.Add(args[i]);
|
||||
HandleEncodeCommand(newArgs.ToArray());
|
||||
}
|
||||
// 파일명만 전달된 경우 (분석 모드)
|
||||
else if (File.Exists(args[0]))
|
||||
{
|
||||
AnalyzeFile(args[0]);
|
||||
// Check file extension to determine default action
|
||||
string extension = Path.GetExtension(args[0]).ToLower();
|
||||
|
||||
if (extension == ".dump" || extension == ".txt")
|
||||
{
|
||||
// Hex dump files - use full analysis mode
|
||||
AnalyzeFile(args[0]);
|
||||
}
|
||||
else if (extension == ".hex")
|
||||
{
|
||||
// .hex files - automatically decode to XML
|
||||
var autoArgs = new List<string> { "--decode", args[0] };
|
||||
// Add remaining args (like -out)
|
||||
for (int i = 1; i < args.Length; i++)
|
||||
autoArgs.Add(args[i]);
|
||||
HandleDecodeCommand(autoArgs.ToArray());
|
||||
}
|
||||
else if (extension == ".xml")
|
||||
{
|
||||
// .xml files - automatically encode to EXI
|
||||
var autoArgs = new List<string> { "--encode", args[0] };
|
||||
// Add remaining args (like -out)
|
||||
for (int i = 1; i < args.Length; i++)
|
||||
autoArgs.Add(args[i]);
|
||||
HandleEncodeCommand(autoArgs.ToArray());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unknown extension - require explicit option
|
||||
Console.WriteLine($"Error: Unknown file extension '{extension}'. Please specify operation:");
|
||||
Console.WriteLine($" {Path.GetFileName(args[0])} --decode # To decode as EXI to XML");
|
||||
Console.WriteLine($" {Path.GetFileName(args[0])} --encode # To encode as XML to EXI");
|
||||
Console.WriteLine($" OR use: --decode {args[0]} / --encode {args[0]}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -75,71 +148,87 @@ namespace V2GProtocol
|
||||
static void ShowUsage()
|
||||
{
|
||||
Console.WriteLine("Usage:");
|
||||
Console.WriteLine(" V2GDecoder.exe [file.txt] # Analyze hex dump file");
|
||||
Console.WriteLine(" V2GDecoder.exe [file.dump/.txt] # Analyze hex dump file");
|
||||
Console.WriteLine(" V2GDecoder.exe [file.hex] # Auto-decode hex to XML");
|
||||
Console.WriteLine(" V2GDecoder.exe [file.xml] # Auto-encode XML to EXI");
|
||||
Console.WriteLine(" V2GDecoder.exe --decode <exi_hex_or_file> # Decode EXI to XML");
|
||||
Console.WriteLine(" V2GDecoder.exe --encode <xml_file_or_string> # Encode XML to EXI");
|
||||
Console.WriteLine(" V2GDecoder.exe <file> --decode # Decode file content to XML");
|
||||
Console.WriteLine(" V2GDecoder.exe <file> --encode # Encode file content to EXI");
|
||||
Console.WriteLine(" V2GDecoder.exe <file> --decode [-out file] # Force decode operation");
|
||||
Console.WriteLine(" V2GDecoder.exe <file> --encode [-out file] # Force encode operation");
|
||||
Console.WriteLine(" V2GDecoder.exe --help # Show this help");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("File Extension Auto-Processing:");
|
||||
Console.WriteLine(" .dump, .txt → Full analysis mode (network + V2G message)");
|
||||
Console.WriteLine(" .hex → Auto-decode to XML");
|
||||
Console.WriteLine(" .xml → Auto-encode to EXI hex");
|
||||
Console.WriteLine(" Other → Requires --decode or --encode option");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Pipe Usage:");
|
||||
Console.WriteLine(" type file.xml | V2GDecoder.exe --encode # Pipe XML file to encode");
|
||||
Console.WriteLine(" echo \"hex_string\" | V2GDecoder.exe --decode # Pipe hex string to decode");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Examples:");
|
||||
Console.WriteLine(" V2GDecoder.exe data/dump2.txt # Full analysis");
|
||||
Console.WriteLine(" V2GDecoder.exe data/dump2.dump # Full analysis of hex dump");
|
||||
Console.WriteLine(" V2GDecoder.exe data/test.hex # Auto-decode hex to XML");
|
||||
Console.WriteLine(" V2GDecoder.exe data/encode0.xml # Auto-encode XML to EXI");
|
||||
Console.WriteLine(" V2GDecoder.exe -d \"8098021050908C0C0C0E0C5211003200\" # Decode hex string");
|
||||
Console.WriteLine(" V2GDecoder.exe -d data/dump2.txt # Extract & decode from file");
|
||||
Console.WriteLine(" V2GDecoder.exe -e \"<V2G_Message>...</V2G_Message>\" # Encode XML string");
|
||||
Console.WriteLine(" V2GDecoder.exe -e message.xml # Encode XML file");
|
||||
Console.WriteLine(" V2GDecoder.exe data/encode0.xml --encode # Encode XML file to EXI");
|
||||
Console.WriteLine(" V2GDecoder.exe data/dump2.txt --decode # Decode file to XML only");
|
||||
Console.WriteLine(" type data\\encode0.xml | V2GDecoder.exe --encode # Pipe file content to encode");
|
||||
Console.WriteLine(" echo \"8098021050908C0C0C0E0C5211003200\" | V2GDecoder.exe -d # Pipe hex to decode");
|
||||
Console.WriteLine(" V2GDecoder.exe data/test.dat --decode # Decode unknown file type");
|
||||
Console.WriteLine(" V2GDecoder.exe data/data0.xml -out data0.hex # Encode to binary file");
|
||||
Console.WriteLine(" V2GDecoder.exe --decode data.hex -out out.xml # Decode to XML file");
|
||||
Console.WriteLine(" type data\\encode0.xml | V2GDecoder.exe -e # Pipe XML to encode");
|
||||
}
|
||||
|
||||
static string? GetOutputFilename(string[] args)
|
||||
{
|
||||
for (int i = 0; i < args.Length - 1; i++)
|
||||
{
|
||||
if (args[i].ToLower() == "-out" || args[i].ToLower() == "-o")
|
||||
{
|
||||
return args[i + 1];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static void HandleDecodeCommand(string[] args)
|
||||
{
|
||||
string input;
|
||||
byte[] exiBytes;
|
||||
string? outputFile = GetOutputFilename(args);
|
||||
|
||||
// Check if input is coming from stdin (pipe)
|
||||
if (args.Length < 2)
|
||||
// Check if we have sufficient arguments for file/string input
|
||||
if (args.Length >= 2)
|
||||
{
|
||||
if (!Console.IsInputRedirected)
|
||||
{
|
||||
Console.WriteLine("Error: EXI hex string or file required for decode operation");
|
||||
Console.WriteLine("Usage: V2GDecoder.exe --decode <exi_hex_string_or_file>");
|
||||
Console.WriteLine(" or: echo \"hex_string\" | V2GDecoder.exe --decode");
|
||||
return;
|
||||
}
|
||||
|
||||
// Read from stdin
|
||||
Console.WriteLine("Reading EXI data from stdin (pipe input)...");
|
||||
// We have command line arguments, use them
|
||||
input = args[1];
|
||||
}
|
||||
else if (Console.IsInputRedirected)
|
||||
{
|
||||
// No sufficient arguments, check if input is coming from stdin (pipe)
|
||||
input = Console.In.ReadToEnd().Trim();
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
Console.WriteLine("Error: No input data received from stdin");
|
||||
return;
|
||||
}
|
||||
outputFile = null; // Force console output for stdin
|
||||
}
|
||||
else
|
||||
{
|
||||
input = args[1];
|
||||
Console.WriteLine("Error: EXI hex string or file required for decode operation");
|
||||
Console.WriteLine("Usage: V2GDecoder.exe --decode <exi_hex_string_or_file> [-out output_file]");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if input is a file
|
||||
if (File.Exists(input))
|
||||
{
|
||||
Console.WriteLine($"Reading EXI data from file: {input}");
|
||||
|
||||
// Handle different file extensions
|
||||
string extension = Path.GetExtension(input).ToLower();
|
||||
|
||||
if (extension == ".dump")
|
||||
{
|
||||
// Hex dump file format with arrows (→)
|
||||
Console.WriteLine("Processing hex dump format file...");
|
||||
byte[] rawData = V2GDecoder.ParseHexFile(input);
|
||||
|
||||
// Find V2G message in the hex dump
|
||||
@@ -147,11 +236,9 @@ namespace V2GProtocol
|
||||
if (v2gMessage != null)
|
||||
{
|
||||
exiBytes = v2gMessage;
|
||||
Console.WriteLine($"Extracted V2G EXI message from hex dump ({exiBytes.Length} bytes).");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Warning: No V2G message header (01 FE) found in hex dump. Attempting to decode raw data.");
|
||||
// Try to decode the raw data directly (might be pure EXI without V2G header)
|
||||
exiBytes = rawData;
|
||||
}
|
||||
@@ -159,11 +246,9 @@ namespace V2GProtocol
|
||||
else if (extension == ".hex")
|
||||
{
|
||||
// Plain hex file (pure hex string)
|
||||
Console.WriteLine("Processing hex string file...");
|
||||
string fileContent = File.ReadAllText(input).Trim();
|
||||
fileContent = fileContent.Replace(" ", "").Replace("-", "").Replace("0x", "").Replace("\n", "").Replace("\r", "");
|
||||
exiBytes = Convert.FromHexString(fileContent);
|
||||
Console.WriteLine($"Parsed hex string from file ({exiBytes.Length} bytes).");
|
||||
}
|
||||
else if (extension == ".txt")
|
||||
{
|
||||
@@ -172,13 +257,11 @@ namespace V2GProtocol
|
||||
if (fileContent.Contains("→"))
|
||||
{
|
||||
// Hex dump format
|
||||
Console.WriteLine("Processing legacy hex dump format file...");
|
||||
byte[] rawData = V2GDecoder.ParseHexFile(input);
|
||||
var v2gMessage = ExtractV2GMessageFromHexDump(rawData);
|
||||
if (v2gMessage != null)
|
||||
{
|
||||
exiBytes = v2gMessage;
|
||||
Console.WriteLine($"Extracted V2G EXI message from hex dump ({exiBytes.Length} bytes).");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -190,13 +273,11 @@ namespace V2GProtocol
|
||||
// Plain hex
|
||||
fileContent = fileContent.Replace(" ", "").Replace("-", "").Replace("0x", "").Replace("\n", "").Replace("\r", "");
|
||||
exiBytes = Convert.FromHexString(fileContent);
|
||||
Console.WriteLine($"Parsed hex string from file ({exiBytes.Length} bytes).");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unknown extension, try as plain hex
|
||||
Console.WriteLine($"Unknown file extension '{extension}', attempting to parse as hex string...");
|
||||
string fileContent = File.ReadAllText(input).Trim();
|
||||
fileContent = fileContent.Replace(" ", "").Replace("-", "").Replace("0x", "").Replace("\n", "").Replace("\r", "");
|
||||
exiBytes = Convert.FromHexString(fileContent);
|
||||
@@ -214,18 +295,24 @@ namespace V2GProtocol
|
||||
}
|
||||
|
||||
exiBytes = Convert.FromHexString(exiHexString);
|
||||
Console.WriteLine("Processing direct hex string input.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Console.WriteLine($"Decoding EXI data ({exiBytes.Length} bytes):");
|
||||
Console.WriteLine($"Input: {Convert.ToHexString(exiBytes)}");
|
||||
Console.WriteLine();
|
||||
|
||||
// Decode EXI to XML
|
||||
// Decode EXI to XML - output only the XML
|
||||
var decodedXml = V2GDecoder.DecodeEXIToXML(exiBytes);
|
||||
Console.WriteLine(decodedXml);
|
||||
|
||||
if (outputFile != null)
|
||||
{
|
||||
// Save to file
|
||||
File.WriteAllText(outputFile, decodedXml);
|
||||
Console.WriteLine($"Decoded XML saved to: {outputFile}");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Output to console
|
||||
Console.WriteLine(decodedXml);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -259,72 +346,63 @@ namespace V2GProtocol
|
||||
{
|
||||
string xmlInput;
|
||||
string xmlContent;
|
||||
string? outputFile = GetOutputFilename(args);
|
||||
|
||||
// Check if input is coming from stdin (pipe)
|
||||
if (args.Length < 2)
|
||||
// Check if we have sufficient arguments for file/string input
|
||||
if (args.Length >= 2)
|
||||
{
|
||||
if (!Console.IsInputRedirected)
|
||||
{
|
||||
Console.WriteLine("Error: XML string or file required for encode operation");
|
||||
Console.WriteLine("Usage: V2GDecoder.exe --encode <xml_file_or_string>");
|
||||
Console.WriteLine(" or: type file.xml | V2GDecoder.exe --encode");
|
||||
return;
|
||||
}
|
||||
// We have command line arguments, use them
|
||||
xmlInput = args[1];
|
||||
|
||||
// Read from stdin
|
||||
Console.WriteLine("Reading XML data from stdin (pipe input)...");
|
||||
// Check if input is a file
|
||||
if (File.Exists(xmlInput))
|
||||
{
|
||||
xmlContent = File.ReadAllText(xmlInput);
|
||||
}
|
||||
else
|
||||
{
|
||||
xmlContent = xmlInput;
|
||||
}
|
||||
}
|
||||
else if (Console.IsInputRedirected)
|
||||
{
|
||||
// No sufficient arguments, check if input is coming from stdin (pipe)
|
||||
xmlContent = Console.In.ReadToEnd().Trim();
|
||||
if (string.IsNullOrEmpty(xmlContent))
|
||||
{
|
||||
Console.WriteLine("Error: No input data received from stdin");
|
||||
return;
|
||||
}
|
||||
outputFile = null; // Force console output for stdin
|
||||
}
|
||||
else
|
||||
{
|
||||
xmlInput = args[1];
|
||||
|
||||
// Check if input is a file
|
||||
if (File.Exists(xmlInput))
|
||||
{
|
||||
string extension = Path.GetExtension(xmlInput).ToLower();
|
||||
|
||||
if (extension == ".xml")
|
||||
{
|
||||
Console.WriteLine($"Reading XML from file: {xmlInput}");
|
||||
xmlContent = File.ReadAllText(xmlInput);
|
||||
}
|
||||
else
|
||||
{
|
||||
// For non-XML extensions, still try to read as text file
|
||||
Console.WriteLine($"Reading content from file: {xmlInput} (extension: {extension})");
|
||||
xmlContent = File.ReadAllText(xmlInput);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Processing XML string:");
|
||||
xmlContent = xmlInput;
|
||||
}
|
||||
Console.WriteLine("Error: XML string or file required for encode operation");
|
||||
Console.WriteLine("Usage: V2GDecoder.exe --encode <xml_file_or_string> [-out output_file]");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Console.WriteLine("Input XML:");
|
||||
Console.WriteLine(xmlContent);
|
||||
Console.WriteLine();
|
||||
|
||||
// Encode XML to EXI
|
||||
var exiBytes = V2GDecoder.EncodeXMLToEXI(xmlContent);
|
||||
var exiHexString = Convert.ToHexString(exiBytes);
|
||||
|
||||
Console.WriteLine($"Encoded EXI data ({exiBytes.Length} bytes):");
|
||||
Console.WriteLine(exiHexString);
|
||||
|
||||
// Also show formatted hex
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Formatted hex:");
|
||||
Console.WriteLine(V2GDecoder.BytesToHex(exiBytes));
|
||||
if (outputFile != null)
|
||||
{
|
||||
// Save as binary file
|
||||
File.WriteAllBytes(outputFile, exiBytes);
|
||||
Console.WriteLine($"Encoded binary saved to: {outputFile} ({exiBytes.Length} bytes)");
|
||||
|
||||
// Also show hex string on console for reference
|
||||
var exiHexString = Convert.ToHexString(exiBytes);
|
||||
Console.WriteLine($"Hex: {exiHexString}");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Output hex string to console
|
||||
var exiHexString = Convert.ToHexString(exiBytes);
|
||||
Console.WriteLine(exiHexString);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user