|  | @@ -1,6 +1,8 @@
 | 
	
		
			
				|  |  |  using System;
 | 
	
		
			
				|  |  |  using System.Collections.Generic;
 | 
	
		
			
				|  |  | +using System.IO;
 | 
	
		
			
				|  |  |  using System.Linq;
 | 
	
		
			
				|  |  | +using System.Text;
 | 
	
		
			
				|  |  |  using ArcToolkitCLI.Commands.Options;
 | 
	
		
			
				|  |  |  using ArcToolkitCLI.Util;
 | 
	
		
			
				|  |  |  using CommandLine;
 | 
	
	
		
			
				|  | @@ -10,6 +12,11 @@ namespace ArcToolkitCLI.Commands.Converters
 | 
	
		
			
				|  |  |      [Verb("nei", HelpText = "Convert NEI files into CSV and back")]
 | 
	
		
			
				|  |  |      class NeiConverter : IConverterCommand, IInputOptions, IOutputOptions
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  | +        private static readonly byte[] NEI_KEY = { 0xAA, 0xC9, 0xD2, 0x35, 0x22, 0x87, 0x20, 0xF2, 0x40, 0xC5, 0x61, 0x7C, 0x01, 0xDF, 0x66, 0x54 };
 | 
	
		
			
				|  |  | +        private static readonly byte[] NEI_MAGIC = { 0x77, 0x73, 0x76, 0xFF };
 | 
	
		
			
				|  |  | +        private static readonly Encoding ShiftJisEncoding = Encoding.GetEncoding(932);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          public int Run()
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              var files = Glob.EnumerateFiles(Input);
 | 
	
	
		
			
				|  | @@ -22,13 +29,16 @@ namespace ArcToolkitCLI.Commands.Converters
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              foreach (var file in files)
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  | +                int result = 0;
 | 
	
		
			
				|  |  |                  switch (file.Extension.ToLowerInvariant())
 | 
	
		
			
				|  |  |                  {
 | 
	
		
			
				|  |  | -                    case "nei":
 | 
	
		
			
				|  |  | -                        ToCSV(file.FullName);
 | 
	
		
			
				|  |  | +                    case ".nei":
 | 
	
		
			
				|  |  | +                        if ((result = ToCSV(file.FullName)) != 0)
 | 
	
		
			
				|  |  | +                            return result;
 | 
	
		
			
				|  |  |                          break;
 | 
	
		
			
				|  |  | -                    case "csv":
 | 
	
		
			
				|  |  | -                        ToNei(file.FullName);
 | 
	
		
			
				|  |  | +                    case ".csv":
 | 
	
		
			
				|  |  | +                        if ((result = ToNei(file.FullName)) != 0)
 | 
	
		
			
				|  |  | +                            return result;
 | 
	
		
			
				|  |  |                          break;
 | 
	
		
			
				|  |  |                      default:
 | 
	
		
			
				|  |  |                          Console.WriteLine($"File {file.FullName} is neither .nei nor .csv file. Skipping...");
 | 
	
	
		
			
				|  | @@ -39,17 +49,69 @@ namespace ArcToolkitCLI.Commands.Converters
 | 
	
		
			
				|  |  |              return 0;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        void ToNei(string filePath)
 | 
	
		
			
				|  |  | +        int ToNei(string filePath)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | +            return 0;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        void ToCSV(string filePath)
 | 
	
		
			
				|  |  | +        int ToCSV(string filePath)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | +            string nameNoExt = Path.GetFileNameWithoutExtension(filePath);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            byte[] neiData = Encryption.DecryptBytes(File.ReadAllBytes(filePath), NEI_KEY);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            using (var ms = new MemoryStream(neiData))
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                using (var br = new BinaryReader(ms))
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    if (!br.ReadBytes(4).SequenceEqual(NEI_MAGIC))
 | 
	
		
			
				|  |  | +                    {
 | 
	
		
			
				|  |  | +                        Console.WriteLine($"File {filePath} is not a valid NEI file");
 | 
	
		
			
				|  |  | +                        return 1;
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                    var cols = br.ReadUInt32();
 | 
	
		
			
				|  |  | +                    var rows = br.ReadUInt32();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                    var strLengths = new int[cols * rows * 4];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +                    for (int cell = 0; cell < cols * rows; cell++)
 | 
	
		
			
				|  |  | +                    {
 | 
	
		
			
				|  |  | +                        br.ReadInt32(); // Total length of all strings
 | 
	
		
			
				|  |  | +                        strLengths[cell] = br.ReadInt32();
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                    var values = new string[cols * rows];
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                    for (int cell = 0; cell < cols * rows; cell++)
 | 
	
		
			
				|  |  | +                    {
 | 
	
		
			
				|  |  | +                        var len = strLengths[cell];
 | 
	
		
			
				|  |  | +                        values[cell] = ShiftJisEncoding.GetString(br.ReadBytes(len), 0, Math.Max(len - 1, 0));
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                    using (var sw = new StreamWriter(File.Create(Path.Combine(Output, $"{nameNoExt}.csv"))))
 | 
	
		
			
				|  |  | +                    {
 | 
	
		
			
				|  |  | +                        for (int row = 0; row < rows; row++)
 | 
	
		
			
				|  |  | +                        {
 | 
	
		
			
				|  |  | +                            for (int col = 0; col < cols; col++)
 | 
	
		
			
				|  |  | +                            {
 | 
	
		
			
				|  |  | +                                sw.Write(values[row * cols + col]);
 | 
	
		
			
				|  |  | +                                sw.Write(ValueSeparator);
 | 
	
		
			
				|  |  | +                            }
 | 
	
		
			
				|  |  | +                            sw.WriteLine();
 | 
	
		
			
				|  |  | +                        }
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            return 0;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          public IEnumerable<string> Input { get; set; }
 | 
	
		
			
				|  |  |          public string Output { get; set; }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        [Option('s', "separator", Default = ";", HelpText = "Value separator of the CSV file")]
 | 
	
		
			
				|  |  | +        public string ValueSeparator { get; set; }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  }
 |