1
0

Fixed naming conventions

This commit is contained in:
2024-01-10 09:33:51 +01:00
parent 68dd0839eb
commit 9cd1344266
11 changed files with 218 additions and 241 deletions

View File

@@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using AMWD.Common.Packing.Tar.Interfaces;
using AMWD.Common.Packing.Tar.Utils;
@@ -16,10 +14,10 @@ namespace AMWD.Common.Packing.Tar
/// </remarks>
public class TarReader
{
private readonly byte[] dataBuffer = new byte[512];
private readonly UsTarHeader header;
private readonly Stream inStream;
private long remainingBytesInFile;
private readonly byte[] _dataBuffer = new byte[512];
private readonly UsTarHeader _header;
private readonly Stream _inStream;
private long _remainingBytesInFile;
/// <summary>
/// Constructs TarReader object to read data from `tarredData` stream
@@ -27,14 +25,11 @@ namespace AMWD.Common.Packing.Tar
/// <param name="tarredData">A stream to read tar archive from</param>
public TarReader(Stream tarredData)
{
inStream = tarredData;
header = new UsTarHeader();
_inStream = tarredData;
_header = new UsTarHeader();
}
public ITarHeader FileInfo
{
get { return header; }
}
public ITarHeader FileInfo => _header;
/// <summary>
/// Read all files from an archive to a directory. It creates some child directories to
@@ -49,7 +44,7 @@ namespace AMWD.Common.Packing.Tar
/// to your business logic.
public void ReadToEnd(string destDirectory)
{
while (MoveNext(false))
while (MoveNext(skipData: false))
{
string fileNameFromArchive = FileInfo.FileName;
string totalPath = destDirectory + Path.DirectorySeparatorChar + fileNameFromArchive;
@@ -59,14 +54,14 @@ namespace AMWD.Common.Packing.Tar
Directory.CreateDirectory(totalPath);
continue;
}
// If record is a file
string fileName = Path.GetFileName(totalPath);
string directory = totalPath.Remove(totalPath.Length - fileName.Length);
Directory.CreateDirectory(directory);
using (FileStream file = File.Create(totalPath))
{
Read(file);
}
using FileStream file = File.Create(totalPath);
Read(file);
}
}
@@ -74,55 +69,57 @@ namespace AMWD.Common.Packing.Tar
/// Read data from a current file to a Stream.
/// </summary>
/// <param name="dataDestanation">A stream to read data to</param>
///
/// <seealso cref="MoveNext"/>
public void Read(Stream dataDestanation)
{
Debug.WriteLine("tar stream position Read in: " + inStream.Position);
Debug.WriteLine("tar stream position Read in: " + _inStream.Position);
int readBytes;
byte[] read;
while ((readBytes = Read(out read)) != -1)
while ((readBytes = Read(out byte[] read)) != -1)
{
Debug.WriteLine("tar stream position Read while(...) : " + inStream.Position);
Debug.WriteLine("tar stream position Read while(...) : " + _inStream.Position);
dataDestanation.Write(read, 0, readBytes);
}
Debug.WriteLine("tar stream position Read out: " + inStream.Position);
Debug.WriteLine("tar stream position Read out: " + _inStream.Position);
}
protected int Read(out byte[] buffer)
{
if (remainingBytesInFile == 0)
if (_remainingBytesInFile == 0)
{
buffer = null;
return -1;
}
int align512 = -1;
long toRead = remainingBytesInFile - 512;
long toRead = _remainingBytesInFile - 512;
if (toRead > 0)
{
toRead = 512;
}
else
{
align512 = 512 - (int)remainingBytesInFile;
toRead = remainingBytesInFile;
align512 = 512 - (int)_remainingBytesInFile;
toRead = _remainingBytesInFile;
}
int bytesRead = _inStream.Read(_dataBuffer, 0, (int)toRead);
_remainingBytesInFile -= bytesRead;
int bytesRead = inStream.Read(dataBuffer, 0, (int)toRead);
remainingBytesInFile -= bytesRead;
if (inStream.CanSeek && align512 > 0)
if (_inStream.CanSeek && align512 > 0)
{
inStream.Seek(align512, SeekOrigin.Current);
_inStream.Seek(align512, SeekOrigin.Current);
}
else
while (align512 > 0)
{
inStream.ReadByte();
_inStream.ReadByte();
--align512;
}
buffer = dataBuffer;
buffer = _dataBuffer;
return bytesRead;
}
@@ -135,7 +132,8 @@ namespace AMWD.Common.Packing.Tar
{
foreach (byte b in buffer)
{
if (b != 0) return false;
if (b != 0)
return false;
}
return true;
}
@@ -154,56 +152,52 @@ namespace AMWD.Common.Packing.Tar
/// <seealso cref="Read(Stream)"/>
public bool MoveNext(bool skipData)
{
Debug.WriteLine("tar stream position MoveNext in: " + inStream.Position);
if (remainingBytesInFile > 0)
Debug.WriteLine("tar stream position MoveNext in: " + _inStream.Position);
if (_remainingBytesInFile > 0)
{
if (!skipData)
{
throw new TarException(
"You are trying to change file while not all the data from the previous one was read. If you do want to skip files use skipData parameter set to true.");
}
// Skip to the end of file.
if (inStream.CanSeek)
if (_inStream.CanSeek)
{
long remainer = (remainingBytesInFile % 512);
inStream.Seek(remainingBytesInFile + (512 - (remainer == 0 ? 512 : remainer)), SeekOrigin.Current);
long remainer = _remainingBytesInFile % 512;
_inStream.Seek(_remainingBytesInFile + (512 - (remainer == 0 ? 512 : remainer)), SeekOrigin.Current);
}
else
{
byte[] buffer;
while (Read(out buffer) != -1)
{
}
while (Read(out _) != -1) ;
}
}
byte[] bytes = header.GetBytes();
byte[] bytes = _header.GetBytes();
int headerRead = inStream.Read(bytes, 0, header.HeaderSize);
int headerRead = _inStream.Read(bytes, 0, _header.HeaderSize);
if (headerRead < 512)
{
throw new TarException("Can not read header");
}
if (IsEmpty(bytes))
{
headerRead = inStream.Read(bytes, 0, header.HeaderSize);
headerRead = _inStream.Read(bytes, 0, _header.HeaderSize);
if (headerRead == 512 && IsEmpty(bytes))
{
Debug.WriteLine("tar stream position MoveNext out(false): " + inStream.Position);
Debug.WriteLine("tar stream position MoveNext out(false): " + _inStream.Position);
return false;
}
throw new TarException("Broken archive");
}
if (header.UpdateHeaderFromBytes())
if (_header.UpdateHeaderFromBytes())
{
throw new TarException("Checksum check failed");
}
remainingBytesInFile = header.SizeInBytes;
_remainingBytesInFile = _header.SizeInBytes;
Debug.WriteLine("tar stream position MoveNext out(true): " + inStream.Position);
Debug.WriteLine("tar stream position MoveNext out(true): " + _inStream.Position);
return true;
}
}

View File

@@ -8,9 +8,9 @@ namespace AMWD.Common.Packing.Tar
// https://github.com/ygoe/DotnetMakeDeb/blob/v1.1.0/DotnetMakeDeb/Tar/TarWriter.cs
public class TarWriter : LegacyTarWriter
{
public TarWriter(Stream outStream) : base(outStream)
{
}
public TarWriter(Stream outStream)
: base(outStream)
{ }
protected override void WriteHeader(string name, DateTime lastModificationTime, long count, int userId, int groupId, int mode, EntryType entryType)
{

View File

@@ -5,39 +5,39 @@ namespace AMWD.Common.Packing.Tar.Utils
{
internal class DataWriter : IArchiveDataWriter
{
private readonly long size;
private long remainingBytes;
private readonly Stream stream;
private readonly long _size;
private long _remainingBytes;
private readonly Stream _stream;
public DataWriter(Stream data, long dataSizeInBytes)
{
size = dataSizeInBytes;
remainingBytes = size;
stream = data;
_size = dataSizeInBytes;
_remainingBytes = _size;
_stream = data;
}
public bool CanWrite { get; private set; } = true;
public int Write(byte[] buffer, int count)
{
if (remainingBytes == 0)
if (_remainingBytes == 0)
{
CanWrite = false;
return -1;
}
int bytesToWrite;
if (remainingBytes - count < 0)
if (_remainingBytes - count < 0)
{
bytesToWrite = (int)remainingBytes;
bytesToWrite = (int)_remainingBytes;
}
else
{
bytesToWrite = count;
}
stream.Write(buffer, 0, bytesToWrite);
remainingBytes -= bytesToWrite;
_stream.Write(buffer, 0, bytesToWrite);
_remainingBytes -= bytesToWrite;
return bytesToWrite;
}

View File

@@ -14,10 +14,13 @@ namespace AMWD.Common.Packing.Tar.Utils
/// </remarks>
public class LegacyTarWriter : IDisposable
{
private readonly Stream outStream;
private readonly Stream _outStream;
private bool _isClosed;
/// <summary>
/// The buffer for writing.
/// </summary>
protected byte[] buffer = new byte[1024];
private bool isClosed;
public bool ReadOnZero = true;
/// <summary>
/// Writes tar (see GNU tar) archive to a stream
@@ -25,16 +28,25 @@ namespace AMWD.Common.Packing.Tar.Utils
/// <param name="outStream">stream to write archive to</param>
public LegacyTarWriter(Stream outStream)
{
this.outStream = outStream;
_outStream = outStream;
}
/// <summary>
/// Gets or sets a value indicating whether to read on zero.
/// </summary>
public bool ReadOnZero { get; set; } = true;
/// <summary>
/// Gets the output stream.
/// </summary>
protected virtual Stream OutStream
{
get { return outStream; }
get { return _outStream; }
}
#region IDisposable Members
/// <inheritdoc/>
public void Dispose()
{
Close();
@@ -85,13 +97,13 @@ namespace AMWD.Common.Packing.Tar.Utils
WriteDirectoryEntry(directory, 0, 0, 0755);
string[] files = Directory.GetFiles(directory);
foreach (var fileName in files)
foreach (string fileName in files)
{
Write(fileName);
}
string[] directories = Directory.GetDirectories(directory);
foreach (var dirName in directories)
foreach (string dirName in directories)
{
WriteDirectoryEntry(dirName, 0, 0, 0755);
if (doRecursive)
@@ -134,10 +146,9 @@ namespace AMWD.Common.Packing.Tar.Utils
AlignTo512(dataSizeInBytes, false);
}
public virtual void Write(Stream data, long dataSizeInBytes, string name, int userId, int groupId, int mode,
DateTime lastModificationTime)
public virtual void Write(Stream data, long dataSizeInBytes, string name, int userId, int groupId, int mode, DateTime lastModificationTime)
{
if (isClosed)
if (_isClosed)
throw new TarException("Can not write to the closed writer");
// handle long file names (> 99 characters)
@@ -172,7 +183,7 @@ namespace AMWD.Common.Packing.Tar.Utils
private void WriteLongName(string name, int userId, int groupId, int mode, DateTime lastModificationTime)
{
// must include a trailing \0
var nameLength = Encoding.UTF8.GetByteCount(name);
int nameLength = Encoding.UTF8.GetByteCount(name);
byte[] entryName = new byte[nameLength + 1];
Encoding.UTF8.GetBytes(name, 0, name.Length, entryName, 0);
@@ -238,7 +249,7 @@ namespace AMWD.Common.Packing.Tar.Utils
public void AlignTo512(long size, bool acceptZero)
{
size = size % 512;
size %= 512;
if (size == 0 && !acceptZero) return;
while (size < 512)
{
@@ -249,10 +260,13 @@ namespace AMWD.Common.Packing.Tar.Utils
public virtual void Close()
{
if (isClosed) return;
if (_isClosed)
return;
AlignTo512(0, true);
AlignTo512(0, true);
isClosed = true;
_isClosed = true;
}
}
}

View File

@@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace AMWD.Common.Packing.Tar.Utils
{

View File

@@ -1,4 +1,5 @@
using System;
using System.Linq;
using System.Net;
using System.Text;
using AMWD.Common.Packing.Tar.Interfaces;
@@ -7,8 +8,11 @@ namespace AMWD.Common.Packing.Tar.Utils
{
internal class TarHeader : ITarHeader
{
private readonly byte[] buffer = new byte[512];
private long headerChecksum;
private static readonly byte[] _spaces = Encoding.ASCII.GetBytes(" ");
private readonly byte[] _buffer = new byte[512];
private string _fileName;
private long _headerChecksum;
public TarHeader()
{
@@ -18,63 +22,51 @@ namespace AMWD.Common.Packing.Tar.Utils
GroupId = 61; // 101 dec
}
private string fileName;
protected readonly DateTime TheEpoch = new DateTime(1970, 1, 1, 0, 0, 0);
public EntryType EntryType { get; set; }
private static byte[] spaces = Encoding.ASCII.GetBytes(" ");
public virtual string FileName
{
get
{
return fileName.Replace("\0", string.Empty);
}
get => _fileName.Replace("\0", string.Empty);
set
{
if (value.Length > 100)
{
throw new TarException("A file name can not be more than 100 chars long");
}
fileName = value;
_fileName = value;
}
}
public int Mode { get; set; }
public string ModeString
{
get { return Convert.ToString(Mode, 8).PadLeft(7, '0'); }
}
=> Convert.ToString(Mode, 8).PadLeft(7, '0');
public int UserId { get; set; }
public virtual string UserName
{
get { return UserId.ToString(); }
set { UserId = Int32.Parse(value); }
get => UserId.ToString();
set => UserId = int.Parse(value);
}
public string UserIdString
{
get { return Convert.ToString(UserId, 8).PadLeft(7, '0'); }
}
=> Convert.ToString(UserId, 8).PadLeft(7, '0');
public int GroupId { get; set; }
public virtual string GroupName
{
get { return GroupId.ToString(); }
set { GroupId = Int32.Parse(value); }
get => GroupId.ToString();
set => GroupId = int.Parse(value);
}
public string GroupIdString
{
get { return Convert.ToString(GroupId, 8).PadLeft(7, '0'); }
}
=> Convert.ToString(GroupId, 8).PadLeft(7, '0');
public long SizeInBytes { get; set; }
public string SizeString
{
get { return Convert.ToString(SizeInBytes, 8).PadLeft(11, '0'); }
}
=> Convert.ToString(SizeInBytes, 8).PadLeft(11, '0');
public DateTime LastModification { get; set; }
@@ -82,71 +74,63 @@ namespace AMWD.Common.Packing.Tar.Utils
{
get
{
return Convert.ToString((long)(LastModification - TheEpoch).TotalSeconds, 8).PadLeft(11, '0');
long unixTime = ((DateTimeOffset)DateTime.SpecifyKind(LastModification, DateTimeKind.Utc)).ToUnixTimeSeconds();
return Convert.ToString(unixTime, 8).PadLeft(11, '0');
}
}
public string HeaderChecksumString
{
get { return Convert.ToString(headerChecksum, 8).PadLeft(6, '0'); }
}
=> Convert.ToString(_headerChecksum, 8).PadLeft(6, '0');
public virtual int HeaderSize
{
get { return 512; }
}
public virtual int HeaderSize => 512;
public byte[] GetBytes()
{
return buffer;
}
public byte[] GetBytes() => _buffer.ToArray();
public virtual bool UpdateHeaderFromBytes()
{
FileName = Encoding.ASCII.GetString(buffer, 0, 100);
// thanks to Shasha Alperocivh. Trimming nulls.
Mode = Convert.ToInt32(Encoding.ASCII.GetString(buffer, 100, 7).Trim(), 8);
UserId = Convert.ToInt32(Encoding.ASCII.GetString(buffer, 108, 7).Trim(), 8);
GroupId = Convert.ToInt32(Encoding.ASCII.GetString(buffer, 116, 7).Trim(), 8);
FileName = Encoding.ASCII.GetString(_buffer, 0, 100);
EntryType = (EntryType)buffer[156];
// Thanks to Shasha Alperocivh. Trimming nulls.
Mode = Convert.ToInt32(Encoding.ASCII.GetString(_buffer, 100, 7).Trim(), 8);
UserId = Convert.ToInt32(Encoding.ASCII.GetString(_buffer, 108, 7).Trim(), 8);
GroupId = Convert.ToInt32(Encoding.ASCII.GetString(_buffer, 116, 7).Trim(), 8);
if ((buffer[124] & 0x80) == 0x80) // if size in binary
EntryType = (EntryType)_buffer[156];
if ((_buffer[124] & 0x80) == 0x80) // if size in binary
{
long sizeBigEndian = BitConverter.ToInt64(buffer, 0x80);
long sizeBigEndian = BitConverter.ToInt64(_buffer, 0x80);
SizeInBytes = IPAddress.NetworkToHostOrder(sizeBigEndian);
}
else
{
SizeInBytes = Convert.ToInt64(Encoding.ASCII.GetString(buffer, 124, 11), 8);
SizeInBytes = Convert.ToInt64(Encoding.ASCII.GetString(_buffer, 124, 11), 8);
}
long unixTimeStamp = Convert.ToInt64(Encoding.ASCII.GetString(buffer, 136, 11), 8);
LastModification = TheEpoch.AddSeconds(unixTimeStamp);
long unixTimeStamp = Convert.ToInt64(Encoding.ASCII.GetString(_buffer, 136, 11), 8);
LastModification = DateTimeOffset.FromUnixTimeSeconds(unixTimeStamp).DateTime;
var storedChecksum = Convert.ToInt32(Encoding.ASCII.GetString(buffer, 148, 6));
RecalculateChecksum(buffer);
if (storedChecksum == headerChecksum)
{
int storedChecksum = Convert.ToInt32(Encoding.ASCII.GetString(_buffer, 148, 6));
RecalculateChecksum(_buffer);
if (storedChecksum == _headerChecksum)
return true;
}
RecalculateAltChecksum(buffer);
return storedChecksum == headerChecksum;
RecalculateAltChecksum(_buffer);
return storedChecksum == _headerChecksum;
}
private void RecalculateAltChecksum(byte[] buf)
{
spaces.CopyTo(buf, 148);
headerChecksum = 0;
_spaces.CopyTo(buf, 148);
_headerChecksum = 0;
foreach (byte b in buf)
{
if ((b & 0x80) == 0x80)
{
headerChecksum -= b ^ 0x80;
_headerChecksum -= b ^ 0x80;
}
else
{
headerChecksum += b;
_headerChecksum += b;
}
}
}
@@ -154,41 +138,42 @@ namespace AMWD.Common.Packing.Tar.Utils
public virtual byte[] GetHeaderValue()
{
// Clean old values
Array.Clear(buffer, 0, buffer.Length);
Array.Clear(_buffer, 0, _buffer.Length);
if (string.IsNullOrEmpty(FileName)) throw new TarException("FileName can not be empty.");
if (FileName.Length >= 100) throw new TarException("FileName is too long. It must be less than 100 bytes.");
if (string.IsNullOrEmpty(FileName))
throw new TarException("FileName can not be empty.");
if (FileName.Length >= 100)
throw new TarException("FileName is too long. It must be less than 100 bytes.");
// Fill header
Encoding.ASCII.GetBytes(FileName.PadRight(100, '\0')).CopyTo(buffer, 0);
Encoding.ASCII.GetBytes(ModeString).CopyTo(buffer, 100);
Encoding.ASCII.GetBytes(UserIdString).CopyTo(buffer, 108);
Encoding.ASCII.GetBytes(GroupIdString).CopyTo(buffer, 116);
Encoding.ASCII.GetBytes(SizeString).CopyTo(buffer, 124);
Encoding.ASCII.GetBytes(LastModificationString).CopyTo(buffer, 136);
Encoding.ASCII.GetBytes(FileName.PadRight(100, '\0')).CopyTo(_buffer, 0);
Encoding.ASCII.GetBytes(ModeString).CopyTo(_buffer, 100);
Encoding.ASCII.GetBytes(UserIdString).CopyTo(_buffer, 108);
Encoding.ASCII.GetBytes(GroupIdString).CopyTo(_buffer, 116);
Encoding.ASCII.GetBytes(SizeString).CopyTo(_buffer, 124);
Encoding.ASCII.GetBytes(LastModificationString).CopyTo(_buffer, 136);
// buffer[156] = 20;
buffer[156] = ((byte)EntryType);
_buffer[156] = ((byte)EntryType);
RecalculateChecksum(buffer);
RecalculateChecksum(_buffer);
// Write checksum
Encoding.ASCII.GetBytes(HeaderChecksumString).CopyTo(buffer, 148);
Encoding.ASCII.GetBytes(HeaderChecksumString).CopyTo(_buffer, 148);
return buffer;
return _buffer;
}
protected virtual void RecalculateChecksum(byte[] buf)
{
// Set default value for checksum. That is 8 spaces.
spaces.CopyTo(buf, 148);
_spaces.CopyTo(buf, 148);
// Calculate checksum
headerChecksum = 0;
_headerChecksum = 0;
foreach (byte b in buf)
{
headerChecksum += b;
}
_headerChecksum += b;
}
}
}

View File

@@ -9,59 +9,48 @@ namespace AMWD.Common.Packing.Tar.Utils
/// </summary>
internal class UsTarHeader : TarHeader
{
private const string magic = "ustar";
private const string version = " ";
private string groupName;
private const string Magic = "ustar";
private const string Version = " ";
private string namePrefix = string.Empty;
private string userName;
private string _groupName;
private string _namePrefix = string.Empty;
private string _userName;
public override string UserName
{
get
{
return userName.Replace("\0", string.Empty);
}
get => _userName.Replace("\0", string.Empty);
set
{
if (value.Length > 32)
{
throw new TarException("user name can not be longer than 32 chars");
}
userName = value;
_userName = value;
}
}
public override string GroupName
{
get
{
return groupName.Replace("\0", string.Empty);
}
get => _groupName.Replace("\0", string.Empty);
set
{
if (value.Length > 32)
{
throw new TarException("group name can not be longer than 32 chars");
}
groupName = value;
_groupName = value;
}
}
public override string FileName
{
get
{
return namePrefix.Replace("\0", string.Empty) + base.FileName.Replace("\0", string.Empty);
}
get => _namePrefix.Replace("\0", string.Empty) + base.FileName.Replace("\0", string.Empty);
set
{
if (value.Length > 100)
{
if (value.Length > 255)
{
throw new TarException("UsTar fileName can not be longer thatn 255 chars");
}
int position = value.Length - 100;
// Find first path separator in the remaining 100 chars of the file name
@@ -75,7 +64,8 @@ namespace AMWD.Common.Packing.Tar.Utils
}
if (position == value.Length)
position = value.Length - 100;
namePrefix = value.Substring(0, position);
_namePrefix = value.Substring(0, position);
base.FileName = value.Substring(position, value.Length - position);
}
else
@@ -88,26 +78,26 @@ namespace AMWD.Common.Packing.Tar.Utils
public override bool UpdateHeaderFromBytes()
{
byte[] bytes = GetBytes();
UserName = Encoding.ASCII.GetString(bytes, 0x109, 32);
GroupName = Encoding.ASCII.GetString(bytes, 0x129, 32);
namePrefix = Encoding.ASCII.GetString(bytes, 347, 157);
_namePrefix = Encoding.ASCII.GetString(bytes, 347, 157);
return base.UpdateHeaderFromBytes();
}
internal static bool IsPathSeparator(char ch)
{
return (ch == '\\' || ch == '/' || ch == '|'); // All the path separators I ever met.
}
=> ch == '\\' || ch == '/' || ch == '|'; // All the path separators I ever met.
public override byte[] GetHeaderValue()
{
byte[] header = base.GetHeaderValue();
Encoding.ASCII.GetBytes(magic).CopyTo(header, 0x101); // Mark header as ustar
Encoding.ASCII.GetBytes(version).CopyTo(header, 0x106);
Encoding.ASCII.GetBytes(Magic).CopyTo(header, 0x101); // Mark header as ustar
Encoding.ASCII.GetBytes(Version).CopyTo(header, 0x106);
Encoding.ASCII.GetBytes(UserName).CopyTo(header, 0x109);
Encoding.ASCII.GetBytes(GroupName).CopyTo(header, 0x129);
Encoding.ASCII.GetBytes(namePrefix).CopyTo(header, 347);
Encoding.ASCII.GetBytes(_namePrefix).CopyTo(header, 347);
if (SizeInBytes >= 0x1FFFFFFFF)
{
@@ -117,6 +107,7 @@ namespace AMWD.Common.Packing.Tar.Utils
RecalculateChecksum(header);
Encoding.ASCII.GetBytes(HeaderChecksumString).CopyTo(header, 148);
return header;
}
@@ -128,7 +119,7 @@ namespace AMWD.Common.Packing.Tar.Utils
private static byte[] AlignTo12(byte[] bytes)
{
var retVal = new byte[12];
byte[] retVal = new byte[12];
bytes.CopyTo(retVal, 12 - bytes.Length);
return retVal;
}