'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' XPS Driver SDK : smartcard_singlewire + Omnikey chip example
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Imports System.IO
Imports System.Text
Imports dxp01sdk
Imports SmartcardCommonNamespace


Public Class CommandLineOptions
    Public printerName As String
    Public cardType As String
    Public print As Boolean
    Public jobCompletion As Boolean
    Public parkBack As Boolean
End Class

Class smartcard_singlewire_omnikey
    Public Shared ReadCardIdCommandBytes As Byte() = {&HFF, &HCA, &H0, &H0, &H0}
    Private Shared MifareLoadKeyCommandBytes As Byte() = {&HFF, &H82, &H0, &H1, &H6, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF}
    Private Shared MifareAuthenticateCommandBytes As Byte() = {&HFF, &H86, &H0, &H0, &H5, &H1, &H0, &H1, &H60, &H1}
    Private Shared MifareReadBlockCommandBytes As Byte() = {&HFF, &HB0, &H0, &H2, &H10}
    Private Shared MifareWriteBlockCommandBytes As Byte() = {&HFF, &HD6, &H0, &H2, &H10}
    Private Shared IClassReadCardSNCommandBytes As Byte() = {&HFF, &HB0, &H0, &H0, &H8}
    Private Shared IclassLoadKeyCommandBytes As Byte() = {&HFF, &H82, &H20, &H28, &H8, &H0, &H11, &H22, &H33, &H44, &H55, &H66, &H77}
    Private Shared IclassAuthenticateCommandBytes As Byte() = {&HFF, &H86, &H0, &H0, &H5, &H1, &H0, &H3, &H0, &H28}
    Private Shared IclassReadBlockCommandBytes As Byte() = {&HFF, &HB0, &H0, &H12, &H8}
    Private Shared IclassWriteBlockCommandBytes As Byte() = {&HFF, &HD6, &H0, &H12, &H8}
    Private Shared HidProxReadPacsCommandBytes As Byte() = {&HFF, &H70, &H7, &H6B, &H7, &HA0, &H5, &HA1, &H3, &H80, &H1, &H4, &H0}

    Private Shared Sub Usage()
        Dim thisExeName As String = Path.GetFileName(Environment.GetCommandLineArgs()(0))
        Console.Write(thisExeName)
        Console.WriteLine(" demonstrates interactive mode parking in the")
        Console.WriteLine("smart card reader, performing single-wire smart card functions, and moving the")
        Console.WriteLine("card from the reader. It has options to print and poll for job completion.")
        Console.WriteLine("The omnikey reader supports multiple cards types, allowing you to select")
        Console.WriteLine("the type of card used. For each card type, the omnikey reader performs a")
        Console.WriteLine("series of functions, described below.")
        Console.WriteLine()
        Console.WriteLine(thisExeName & " -n <printername> -t <cardType> [-p [-c]")
        Console.WriteLine()
        Console.WriteLine("options:")
        Console.WriteLine("  -n <printername>. Required. Try -n ""XPS Card Printer"".")
        Console.WriteLine("  -t <cardtype>. Required. Specify one of the following:")
        Console.WriteLine("     -t Mifare. Reads the card ID and status, and reads and writes data to")
        Console.WriteLine("        block 2.")
        Console.WriteLine("     -t iClass. Reads the card ID and status, and reads and writes data to")
        Console.WriteLine("        block 18.")
        Console.WriteLine("     -t HIDProx. Reads the card ID (UID) and facility code (FAC).")
        Console.WriteLine("  -p Print simple black text on one or both sides of the card depending on the")
        Console.WriteLine("     printer used.")
        Console.WriteLine("  -c Poll for job completion.")
        Console.WriteLine()
        Console.WriteLine(thisExeName & " -n ""XPS Card Printer"" -t mifare")
        Console.WriteLine("  Parks a card in the smart card reader, connects to the Mifare chip,")
        Console.WriteLine("  performs the Mifare card functions, and moves the card from the reader.")
        Console.WriteLine()
        Console.WriteLine(thisExeName & " -n ""XPS Card Printer"" -t iClass -p -c")
        Console.WriteLine("  Parks a card in the smart card reader, connects to the iClass chip,")
        Console.WriteLine("  performs the iClass card functions, moves the card from the reader,")
        Console.WriteLine("  prints text and graphics on the front of the card, and polls for job")
        Console.WriteLine("  completion and displays the status.")
        Console.WriteLine()
        Environment.[Exit](-1)
    End Sub

    Private Shared Sub InterpretResponseCode(ByVal bytes As Byte())
        Dim nSize As Integer = bytes.Length

        If nSize < 2 Then
            Console.WriteLine("expecting a two (Or more) Byte response code")
        Else

            If bytes(nSize - 2) = &H90 AndAlso bytes(nSize - 1) = &H0 Then
                Console.WriteLine("response code = SUCCESS")
            ElseIf bytes(nSize - 2) = &H62 AndAlso bytes(nSize - 1) = &H82 Then
                Console.WriteLine("response code = End Of file reached")
            ElseIf bytes(nSize - 2) = &H67 AndAlso bytes(nSize - 1) = &H0 Then
                Console.WriteLine("response code = Wrong Length")
            ElseIf bytes(nSize - 2) = &H65 AndAlso bytes(nSize - 1) = &H81 Then
                Console.WriteLine("response code = Memory Failure")
            ElseIf bytes(nSize - 2) = &H6A AndAlso bytes(nSize - 1) = &H81 Then
                Console.WriteLine("response code = Function Not supported")
            ElseIf bytes(nSize - 2) = &H6B AndAlso bytes(nSize - 1) = &H0 Then
                Console.WriteLine("response code = Wrong parameter")
            ElseIf bytes(nSize - 2) = &H6F AndAlso bytes(nSize - 1) = &H0 Then
                Console.WriteLine("response code = Operation Failed")
            Else
                Console.WriteLine("response code = Unknown Response Code")
            End If
        End If
    End Sub


    Private Shared Function GetCommandlineOptions(args As String()) As CommandLineOptions

        If args.Length = 0 Then
            Usage()
        End If

        Dim commandLineOptions As CommandLineOptions = New CommandLineOptions()
        Dim arguments As CommandLine.Utility.Arguments = New CommandLine.Utility.Arguments(args)

        If String.IsNullOrEmpty(arguments("n")) Then
            Usage()
            Environment.Exit(-1)
        ElseIf String.IsNullOrEmpty(arguments("t")) Then
            Usage()
            Environment.Exit(-1)
        Else
            ' we might have a -n with no printer name:
            Dim boolVal As Boolean = False
            If Boolean.TryParse(arguments("n"), boolVal) Then
                Usage()
                Environment.Exit(-1)
            End If
            commandLineOptions.printerName = arguments("n")

            ' we might have a -t with no card type:
            If Boolean.TryParse(arguments("t"), boolVal) Then
                Usage()
                Environment.Exit(-1)
            End If
            commandLineOptions.cardType = arguments("t")

        End If

        commandLineOptions.print = Not String.IsNullOrEmpty(arguments("p"))
        commandLineOptions.parkBack = Not String.IsNullOrEmpty(arguments("b"))
        commandLineOptions.jobCompletion = Not String.IsNullOrEmpty(arguments("c"))

        Return commandLineOptions
    End Function

    Private Shared Sub ReadCardID(ByVal scard As SCard)
        Dim exceptionText As StringBuilder = New StringBuilder()
        SmartcardCommon.DisplayByteVectorAsHex("ReadCardId() - bytes sent", ReadCardIdCommandBytes)
        Dim receivedBytes As Byte() = New Byte(-1) {}
        Dim scardResult As Long = scard.SCardTransmit(ReadCardIdCommandBytes, receivedBytes)
        Console.WriteLine("{0}", SmartcardCommon.SCardResultMessage(scardResult, "SCardTransmit()"))

        If scard_error.SCARD_S_SUCCESS <> CType(scardResult, scard_error) Then
            exceptionText.Append(SmartcardCommon.SCardResultMessage(scardResult, "SCardTransmit()"))
            Throw New Exception(exceptionText.ToString())
        End If

        If 0 = receivedBytes.Length Then
            Throw New Exception("no response from read CardID command")
        End If

        SmartcardCommon.DisplayByteVectorAsHex("read CardID response", receivedBytes)

        If receivedBytes.Length >= 2 Then
            InterpretResponseCode(receivedBytes)
        End If
    End Sub


    Private Shared Sub IClassReadCardSN(ByVal scard As SCard)
        Console.WriteLine()
        Dim exceptionText As StringBuilder = New StringBuilder()
        SmartcardCommon.DisplayByteVectorAsHex("IClassReadCardSN() - bytes sent", IClassReadCardSNCommandBytes)
        Dim receivedBytes As Byte() = New Byte(-1) {}
        Dim scardResult As Long = scard.SCardTransmit(IClassReadCardSNCommandBytes, receivedBytes)
        Console.WriteLine("{0}", SmartcardCommon.SCardResultMessage(scardResult, "SCardTransmit()"))

        If scard_error.SCARD_S_SUCCESS <> CType(scardResult, scard_error) Then
            exceptionText.Append(SmartcardCommon.SCardResultMessage(scardResult, "SCardTransmit()"))
            Throw New Exception(exceptionText.ToString())
        End If

        If 0 = receivedBytes.Length Then
            Throw New Exception("no response from ReadCardSN command")
        End If

        SmartcardCommon.DisplayByteVectorAsHex("Read Card Serial Number response", receivedBytes)

        If receivedBytes.Length >= 2 Then
            InterpretResponseCode(receivedBytes)
        End If
    End Sub




    ''' <summary>
    ''' load mifare keys
    ''' </summary>
    Private Shared Sub MifareLoadKey(ByVal scard As SCard)
        Dim exceptionText As StringBuilder = New StringBuilder()
        SmartcardCommon.DisplayByteVectorAsHex("load a 6-Byte Mifare key - bytes sent", MifareLoadKeyCommandBytes)
        Dim receivedBytes As Byte() = New Byte(-1) {}
        Dim scardResult As Long = scard.SCardTransmit(MifareLoadKeyCommandBytes, receivedBytes)
        Console.WriteLine("{0}", SmartcardCommon.SCardResultMessage(scardResult, "SCardTransmit()"))

        If scard_error.SCARD_S_SUCCESS <> CType(scardResult, scard_error) Then
            exceptionText.Append(SmartcardCommon.SCardResultMessage(scardResult, "SCardTransmit()"))
            Throw New Exception(exceptionText.ToString())
        End If

        If 0 = receivedBytes.Length Then
            Throw New Exception("no response from read block command")
        End If

        SmartcardCommon.DisplayByteVectorAsHex("load a 6-Byte Mifare key response", receivedBytes)

        If receivedBytes.Length >= 2 Then
            InterpretResponseCode(receivedBytes)
        End If
    End Sub


    Private Shared Sub MifareAuthenticate(ByVal scard As SCard)
        Dim exceptionText As StringBuilder = New StringBuilder()
        Console.WriteLine()
        SmartcardCommon.DisplayByteVectorAsHex(" Authenticate block 1 - bytes sent", MifareAuthenticateCommandBytes)
        Dim receivedBytes As Byte() = New Byte(-1) {}
        Dim scardResult As Long = scard.SCardTransmit(MifareAuthenticateCommandBytes, receivedBytes)
        Console.WriteLine("{0}", SmartcardCommon.SCardResultMessage(scardResult, "SCardTransmit()"))

        If scard_error.SCARD_S_SUCCESS <> CType(scardResult, scard_error) Then
            exceptionText.Append(SmartcardCommon.SCardResultMessage(scardResult, "SCardTransmit()"))
            Throw New Exception(exceptionText.ToString())
        End If

        If 0 = receivedBytes.Length Then
            Throw New Exception("no response from read block command")
        End If

        SmartcardCommon.DisplayByteVectorAsHex(" Authenticate block 1 response", receivedBytes)

        If receivedBytes.Length >= 2 Then
            InterpretResponseCode(receivedBytes)
        End If
    End Sub


    Private Shared Sub MifareReadBlock(ByVal scard As SCard)
        Dim exceptionText As StringBuilder = New StringBuilder()
        Console.WriteLine()
        SmartcardCommon.DisplayByteVectorAsHex("Read block 2 - bytes sent", MifareReadBlockCommandBytes)
        Dim receivedBytes As Byte() = New Byte(-1) {}
        Dim scardResult As Long = scard.SCardTransmit(MifareReadBlockCommandBytes, receivedBytes)
        Console.WriteLine("{0}", SmartcardCommon.SCardResultMessage(scardResult, "SCardTransmit()"))

        If scard_error.SCARD_S_SUCCESS <> CType(scardResult, scard_error) Then
            exceptionText.Append(SmartcardCommon.SCardResultMessage(scardResult, "SCardTransmit()"))
            Throw New Exception(exceptionText.ToString())
        End If

        If 0 = receivedBytes.Length Then
            Throw New Exception("no response from read block command")
        End If

        SmartcardCommon.DisplayByteVectorAsHex("read block2 response", receivedBytes)

        If receivedBytes.Length >= 2 Then
            InterpretResponseCode(receivedBytes)
        End If
    End Sub


    Private Shared Sub MifareWriteBlock(ByVal scard As SCard)
        Dim exceptionText As StringBuilder = New StringBuilder()
        Dim testDataString As String = System.DateTime.Now.ToString("yyyy.mm.dd.hh.mm")
        Dim testDataBytes As Byte() = Encoding.ASCII.GetBytes(testDataString)
        Dim commandBytes As Byte() = New Byte(MifareWriteBlockCommandBytes.Length + testDataBytes.Length - 1) {}
        System.Buffer.BlockCopy(MifareWriteBlockCommandBytes, 0, commandBytes, 0, MifareWriteBlockCommandBytes.Length)
        System.Buffer.BlockCopy(testDataBytes, 0, commandBytes, MifareWriteBlockCommandBytes.Length, testDataBytes.Length)
        Console.WriteLine()
        Console.WriteLine("writing '{0}' to chip", testDataString)
        SmartcardCommon.DisplayByteVectorAsHex("write block 2 - bytes sent", commandBytes)
        Dim receivedBytes As Byte() = New Byte(-1) {}
        Dim scardResult As Long = scard.SCardTransmit(commandBytes, receivedBytes)
        Console.WriteLine("{0}", SmartcardCommon.SCardResultMessage(scardResult, "SCardTransmit()"))

        If scard_error.SCARD_S_SUCCESS <> CType(scardResult, scard_error) Then
            exceptionText.Append(SmartcardCommon.SCardResultMessage(scardResult, "SCardTransmit()"))
            Throw New Exception(exceptionText.ToString())
        End If

        If 0 = receivedBytes.Length Then
            Throw New Exception("no response from write block command")
        End If

        SmartcardCommon.DisplayByteVectorAsHex("write block response", receivedBytes)

        If receivedBytes.Length >= 2 Then
            InterpretResponseCode(receivedBytes)
        End If
    End Sub


    Private Shared Sub MifareReadWriteData(ByVal scard As SCard)
        Console.WriteLine()
        MifareLoadKey(scard)
        MifareAuthenticate(scard)
        MifareReadBlock(scard)
        MifareWriteBlock(scard)
        MifareReadBlock(scard)
    End Sub


    Private Shared Sub IclassLoadKey(ByVal scard As SCard)
        Dim exceptionText As StringBuilder = New StringBuilder()
        SmartcardCommon.DisplayByteVectorAsHex("load iClass key - bytes sent", IclassLoadKeyCommandBytes)
        Dim receivedBytes As Byte() = New Byte(-1) {}
        Dim scardResult As Long = scard.SCardTransmit(MifareReadBlockCommandBytes, receivedBytes)
        Console.WriteLine("{0}", SmartcardCommon.SCardResultMessage(scardResult, "SCardTransmit()"))

        If scard_error.SCARD_S_SUCCESS <> CType(scardResult, scard_error) Then
            exceptionText.Append(SmartcardCommon.SCardResultMessage(scardResult, "SCardTransmit()"))
            Throw New Exception(exceptionText.ToString())
        End If

        If 0 = receivedBytes.Length Then
            Throw New Exception("no response IclassLoadKey")
        End If

        SmartcardCommon.DisplayByteVectorAsHex("load a iClass load key response", receivedBytes)

        If receivedBytes.Length >= 2 Then
            InterpretResponseCode(receivedBytes)
        End If
    End Sub


    Private Shared Sub IclassAuthenticate(ByVal scard As SCard)
        Dim exceptionText As StringBuilder = New StringBuilder()
        Console.WriteLine()
        SmartcardCommon.DisplayByteVectorAsHex(" Authenticate block 1 - bytes sent", IclassAuthenticateCommandBytes)
        Dim receivedBytes As Byte() = New Byte(-1) {}
        Dim scardResult As Long = scard.SCardTransmit(IclassAuthenticateCommandBytes, receivedBytes)
        Console.WriteLine("{0}", SmartcardCommon.SCardResultMessage(scardResult, "SCardTransmit()"))

        If scard_error.SCARD_S_SUCCESS <> CType(scardResult, scard_error) Then
            exceptionText.Append(SmartcardCommon.SCardResultMessage(scardResult, "SCardTransmit()"))
            Throw New Exception(exceptionText.ToString())
        End If

        If 0 = receivedBytes.Length Then
            Throw New Exception("no response from read block command")
        End If

        SmartcardCommon.DisplayByteVectorAsHex(" Authenticate block 1 response", receivedBytes)

        If receivedBytes.Length >= 2 Then
            InterpretResponseCode(receivedBytes)
        End If
    End Sub


    Private Shared Sub IclassReadBlock(ByVal scard As SCard)
        Dim exceptionText As StringBuilder = New StringBuilder()
        Console.WriteLine()
        SmartcardCommon.DisplayByteVectorAsHex("Read block 0x12 - bytes sent", IclassReadBlockCommandBytes)
        Dim receivedBytes As Byte() = New Byte(-1) {}
        Dim scardResult As Long = scard.SCardTransmit(IclassReadBlockCommandBytes, receivedBytes)
        Console.WriteLine("{0}", SmartcardCommon.SCardResultMessage(scardResult, "SCardTransmit()"))

        If scard_error.SCARD_S_SUCCESS <> CType(scardResult, scard_error) Then
            exceptionText.Append(SmartcardCommon.SCardResultMessage(scardResult, "SCardTransmit()"))
            Throw New Exception(exceptionText.ToString())
        End If

        If 0 = receivedBytes.Length Then
            Throw New Exception("no response from read block command")
        End If

        SmartcardCommon.DisplayByteVectorAsHex("read block 0x12 response", receivedBytes)

        If receivedBytes.Length >= 2 Then
            InterpretResponseCode(receivedBytes)
        End If
    End Sub


    Private Shared Sub IclassWriteBlock(ByVal scard As SCard)
        Dim exceptionText As StringBuilder = New StringBuilder()
        Dim testDataString As String = System.DateTime.Now.ToString("hh.mm.ss")
        Dim testDataBytes As Byte() = Encoding.ASCII.GetBytes(testDataString)
        Dim commandBytes As Byte() = New Byte(IclassWriteBlockCommandBytes.Length + testDataBytes.Length - 1) {}
        System.Buffer.BlockCopy(IclassWriteBlockCommandBytes, 0, commandBytes, 0, IclassWriteBlockCommandBytes.Length)
        System.Buffer.BlockCopy(testDataBytes, 0, commandBytes, IclassWriteBlockCommandBytes.Length, testDataBytes.Length)
        Console.WriteLine()
        Console.WriteLine("writing '{0}' to chip", testDataString)
        SmartcardCommon.DisplayByteVectorAsHex("write block 0x12 - bytes sent", commandBytes)
        Dim receivedBytes As Byte() = New Byte(-1) {}
        Dim scardResult As Long = scard.SCardTransmit(commandBytes, receivedBytes)
        Console.WriteLine("{0}", SmartcardCommon.SCardResultMessage(scardResult, "SCardTransmit()"))

        If scard_error.SCARD_S_SUCCESS <> CType(scardResult, scard_error) Then
            exceptionText.Append(SmartcardCommon.SCardResultMessage(scardResult, "SCardTransmit()"))
            Throw New Exception(exceptionText.ToString())
        End If

        If 0 = receivedBytes.Length Then
            Throw New Exception("no response from write block command")
        End If

        SmartcardCommon.DisplayByteVectorAsHex("write block response", receivedBytes)

        If receivedBytes.Length >= 2 Then
            InterpretResponseCode(receivedBytes)
        End If
    End Sub


    Private Shared Sub IclassReadWriteData(ByVal scard As SCard)
        Console.WriteLine()
        IclassLoadKey(scard)
        IclassAuthenticate(scard)
        IclassReadBlock(scard)
        IclassWriteBlock(scard)
        IclassReadBlock(scard)
    End Sub


    Private Shared Function HidProxReadPacs(ByVal scard As SCard) As Byte()
        Console.WriteLine()
        Dim exceptionText As StringBuilder = New StringBuilder()
        Dim receivedBytes As Byte() = New Byte(-1) {}
        SmartcardCommon.DisplayByteVectorAsHex("read PACS info - bytes sent", HidProxReadPacsCommandBytes)
        Dim scardResult As Long = scard.SCardTransmit(HidProxReadPacsCommandBytes, receivedBytes)

        If scard_error.SCARD_S_SUCCESS <> CType(scardResult, scard_error) Then
            exceptionText.Append(SmartcardCommon.SCardResultMessage(scardResult, "SCardTransmit()"))
            Throw New Exception(exceptionText.ToString())
        End If

        If 0 = receivedBytes.Length Then
            Throw New Exception("no response from read PACS info")
        End If

        SmartcardCommon.DisplayByteVectorAsHex("read PACS info response", receivedBytes)

        If receivedBytes.Length >= 2 Then
            InterpretResponseCode(receivedBytes)
        End If

        Return receivedBytes
    End Function


    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    ''' HidProxParseH10301
    '''
    ''' H10301 PACS data Is 26 bits (in 4 bytes of data)
    '''    - 1 bit Parity, 8 bit Facility code, 16 bit CardID, 1 bit Parity, 7 "0's" for padding
    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''    
    Private Shared Sub HidProxParseH10301(ByVal nPacs As UInteger)
        Console.WriteLine()

        ''' H10301 formatting = Parity bit + 8 bit Facility code + 16 bit CardID + parity bit + 7 "0's"
        Dim cardID As ULong = nPacs And CULng(&H7FFF80)
        Console.WriteLine("cardID = 0x{0:X}", cardID)
        cardID = cardID >> 7
        Console.WriteLine("cardID = 0x{0:X}", cardID)

        Dim facilityCode As ULong = nPacs And CULng(&H7F800000)
        Console.WriteLine("facilityCode = 0x{0:X}", facilityCode)
        facilityCode = facilityCode >> 23

        Console.WriteLine("facilityCode = 0x{0:X}", facilityCode)
        Console.WriteLine("H10301 facility code = {0}, cardID = {1}", facilityCode, cardID)
    End Sub

    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    ''' HidProxParseH10302AndH10304
    ''' 
    ''' H10302 PACS data Is 37 bits (in 5 bytes of data)
    '''    - 1 bit Parity, NO Facility code, 35 bit CardID, 1 bit Parity, 3 "0's" for padding
    ''' H10304 PACS data Is 37 bits (in 5 bytes of data)
    '''    - 1 bit Parity, 16 bit Facility code, 19 bit CardID, 1 bit Parity, 3 "0's" for padding
    ''' FYI - if there are 12 PACS bytes, this code does Not know if H10302 Or H10304 format was used.
    '''
    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

    Private Shared Sub HidProxParseH10302AndH10304(ByVal nPacs As ULong)
        Console.WriteLine()

        ''' H10302 formatting = Parity bit + NO Facility code + 35 bit CardID + parity bit + 3 "0's"
        Dim cardID As ULong = nPacs And CULng(&H7FFFFFFFF0)
        cardID = cardID >> 4
        Console.WriteLine("H10302 facility code = No Facility Code, cardID = {0}", cardID)

        ''' H10304 formatting = "Parity bit + 16 bit Facility code + 19 bit CardID + parity bit + 3 '0's"
        cardID = nPacs And CULng(&H7FFFF0)
        cardID = cardID >> 4

        Dim facilityCode As ULong = nPacs And CULng(&H7FFF800000)
        facilityCode = facilityCode >> 23

        Console.WriteLine("H10304 facility code = {0}, cardID = {1}", facilityCode, cardID)
    End Sub


    Private Shared Sub HidProxParsePacs(ByVal inputBytes As Byte())
        Console.WriteLine()
        Dim exceptionText As StringBuilder = New StringBuilder()
        Dim receivedBytes As Byte() = New Byte(-1) {}
        Dim vectorLength As Integer = inputBytes.Length
        Dim successBytes As Byte() = {&H90, &H0}

        If vectorLength <> 11 AndAlso vectorLength <> 12 Then
            exceptionText.Append("HidProxParsePacs() Invalid number of PACS bytes")
            Throw New Exception(exceptionText.ToString())
        End If

        If inputBytes(vectorLength - 2) <> successBytes(0) AndAlso inputBytes(vectorLength - 1) <> successBytes(1) Then
            exceptionText.Append("HidProxParsePacs() invalid PACS information")
            Throw New Exception(exceptionText.ToString())
        End If

        Dim nLongPacs As UInteger = 0

        If vectorLength = 11 Then
            '''assuming H10301 format
            '''need bytes 5 - 8.
            Console.WriteLine()

            For i As Integer = 5 To 8
                '''   shift nPancs to the left 8 bits
                nLongPacs = nLongPacs << 8

                nLongPacs = nLongPacs Or CUInt(inputBytes(i))
                Console.Write("0x{0:X} ", nLongPacs)
            Next

            Console.WriteLine()
            Console.WriteLine("nLongPacs(bytes) = 0x{0:X}", nLongPacs)
            Console.WriteLine("nLongPacs(unsignedLong) = {0}", nLongPacs)

            HidProxParseH10301(nLongPacs)
        Else
            ''' Test code - change bytes 5 - 9 to the following
            ''' 1011 0011 1000 1111 0000 1111 1000 0011 1111 0000
            ''' This will yield the following results
            ''' H10302 - No facility code, cardid = 35 bits = 13840218175 (decimal)
            ''' H10304 - Facility code = 16 bits, CardID = 19 bits
            '''  011 0011 1000 1111 0 - Facility code = 26398 (decimal)
            '''                      000 1111 1000 0011 1111 - CardID = 63551 (decimal)
            '''inputBytes(5) = &HB3        ''' 1011 0011 = 0xB3
            '''inputBytes(6) = &H8F        ''' 1000 1111 = 0x8F
            '''inputBytes(7) = &HF         ''' 0000 1111 = 0x0F
            '''inputBytes(8) = &H83        ''' 1000 0011 = 0x83
            '''inputBytes(9) = &HF0        ''' 1111 0000 = 0xF0

            ''' assuming H10302 Or H10304 formatting
            ''' need bytes 5 - 9.

            Dim nLongLongPacs As ULong = 0

            For i As Integer = 5 To 9
                '''   shift nLongPacs to the left 8 bits
                nLongLongPacs = nLongLongPacs << 8

                nLongLongPacs = nLongLongPacs Or CUInt(inputBytes(i))
                Console.Write("0x{0:X} ", nLongLongPacs)
            Next

            Console.WriteLine()
            Console.WriteLine("PACS (bytes) =  = 0x{0:X}", nLongLongPacs)
            Console.WriteLine("PACS (unsigned integer)) = {0}", nLongLongPacs)
            HidProxParseH10302AndH10304(nLongLongPacs)
        End If
    End Sub



    Private Shared Sub SmartCardControl(ByVal bidiSpl As BidiSplWrap)

        Dim exceptionText As StringBuilder = New StringBuilder()
        Dim scard As SCard = New SCard(bidiSpl)

        Console.WriteLine()

        ''' create a byte std:: vector for the upcoming SCardControl() method.
        ''' particular bytes should function with this type of contactless chip
        '''         HID Prox
        '''         HID iClass
        '''         Mifare 1K Classic
        '''
        '''   The control codes below are used to turn on / off  the field of the omnikey reader.
        '''   The final byte Is 0x00 for protocol contact(rf off) And 0x01 for contactless(rf on).
        '''   It has been tested using contact connection with
        '''   FW rev R4.1.2-3
        '''
        ''' Byte controlCode[] = { 0xff, 0x9a, 0x04, 0x00 };  // contact
        '''
        ''' Make contact connection using SCARD_PROTOCOL_T0

        ''' this control code has been tested on contactless using SCARD_PROTOCOL_CL
        ''' FW rev R4.1.2-3
        Dim controlCode As Byte() = {&HFF, &H9A, &H4, &H1}  ''' contactless

        SmartcardCommon.DisplayByteVectorAsHex("SCardControl bytes sent", controlCode)
        Dim receivedBytes As Byte() = New Byte(-1) {}
        Dim scardResult As Long = scard.SCardControl(controlCode, receivedBytes)
        Console.WriteLine(SmartcardCommon.SCardResultMessage(scardResult, "SCardControl()"))

        If scard_error.SCARD_S_SUCCESS <> CType(scardResult, scard_error) Then
            Dim msg As String = SmartcardCommon.SCardResultMessage(scardResult, "SCardControl()")
            Throw New Exception("SCardControl() fail: " & msg)
        End If

        If 0 = receivedBytes.Length Then
            Console.WriteLine("no response from SCardControl command")
        Else
            SmartcardCommon.DisplayByteVectorAsHex("SCardControl() response", receivedBytes)
        End If
    End Sub



    Private Shared Sub PersonalizeSmartcard(ByVal bidiSpl As BidiSplWrap, ByVal cardType As String)
        Dim scard As SCard = New SCard(bidiSpl)
        Dim protocol As UInteger = CUInt(scard_protocol.SCARD_PROTOCOL_DEFAULT)
        Dim scardResult As Long = scard.SCardConnect(SCard.ChipConnection.contactless, protocol)
        Console.WriteLine("SCardConnect() result: {0}", scardResult)

        If scard_error.SCARD_S_SUCCESS <> CType(scardResult, scard_error) Then
            Dim msg As String = SmartcardCommon.SCardResultMessage(scardResult, "")
            Throw New Exception("SCardConnect() fail: " & msg)
        End If

        SmartcardCommon.DisplayProtocols(protocol)
        Console.WriteLine()

        SmartCardControl(bidiSpl)

        SmartcardCommon.GetSCardStatus(scard)

        Console.WriteLine()
        ReadCardID(scard)

        Console.WriteLine()
        SmartcardCommon.ReadVendorInfo(scard)

        Dim strMifare As String = "Mifare"
        Dim strIClass As String = "iClass"
        Dim strHidProx As String = "HidProx"

        If String.Equals(cardType, strMifare, StringComparison.CurrentCultureIgnoreCase) Then
            MifareReadWriteData(scard)
        ElseIf String.Equals(cardType, strIClass, StringComparison.CurrentCultureIgnoreCase) Then
            IClassReadCardSN(scard)
            IclassReadWriteData(scard)
        ElseIf String.Equals(cardType, strHidProx, StringComparison.CurrentCultureIgnoreCase) Then
            Dim pacsBytes As Byte() = HidProxReadPacs(scard)
            HidProxParsePacs(pacsBytes)
        Else
            Console.WriteLine()
            Dim msg As String = String.Format("'{0}' is not a valid card type.", cardType)
            Throw New Exception(msg)
        End If

        Console.WriteLine()
        scardResult = scard.SCardDisConnect(CInt(scard_disposition.SCARD_LEAVE_CARD))
        Console.WriteLine("SCardDisConnect() result: {0}", scardResult)

        If scard_error.SCARD_S_SUCCESS <> CType(scardResult, scard_error) Then
            Dim msg As String = SmartcardCommon.SCardResultMessage(scardResult, "")
            Throw New Exception("SCardDisConnect() fail: " & msg)
        End If
    End Sub




    Public Shared Sub Main(args As String())

        Dim commandLineOptions As CommandLineOptions = GetCommandlineOptions(args)

        Dim bidiSpl As BidiSplWrap = Nothing
        Dim printerJobID As Integer = 0

        Try
            bidiSpl = New BidiSplWrap()
            bidiSpl.BindDevice(commandLineOptions.printerName)

            ' optional:
            Dim driverVersionXml As String = bidiSpl.GetPrinterData(strings.SDK_VERSION)
            Console.WriteLine(Environment.NewLine & "driver version: " & Util.ParseDriverVersionXML(driverVersionXml) & Environment.NewLine)

            Dim printerOptionsXml As String = bidiSpl.GetPrinterData(strings.PRINTER_OPTIONS2)
            Dim printerOptionsValues As PrinterOptionsValues = Util.ParsePrinterOptionsXML(printerOptionsXml)
            If "Ready" <> printerOptionsValues._printerStatus AndAlso "Busy" <> printerOptionsValues._printerStatus Then
                Throw New Exception((commandLineOptions.printerName & " is not ready. status: ") + printerOptionsValues._printerStatus)
            End If

            If "Single wire" <> printerOptionsValues._optionSmartcard Then
                Dim msg As String = String.Format("{0} needs 'Single wire' for smartcard option. '{1}' was returned.",
                    commandLineOptions.printerName, printerOptionsValues._optionSmartcard)
                Throw New Exception(msg)
            End If

            Dim hopperID As String = String.Empty
            printerJobID = Util.StartJob(bidiSpl, hopperID)

            SmartcardCommon.ParkCard(bidiSpl, commandLineOptions.parkBack)

            PersonalizeSmartcard(bidiSpl, commandLineOptions.cardType)

            SmartcardCommon.ResumeCard(bidiSpl, printerJobID, 0)

            If commandLineOptions.print Then
                Util.PrintTextAndGraphics(bidiSpl, commandLineOptions.printerName)
                Util.WaitForWindowsJobID(bidiSpl, commandLineOptions.printerName)
            End If

            bidiSpl.SetPrinterData(strings.ENDJOB)

            If commandLineOptions.jobCompletion Then
                Util.PollForJobCompletion(bidiSpl, printerJobID)
            End If
        Catch e As BidiException
            Console.WriteLine(e.Message)
            Util.CancelJob(bidiSpl, e.PrinterJobID, e.ErrorCode)
        Catch e As Exception
            Console.WriteLine(e.Message)
            If 0 <> printerJobID Then
                Util.CancelJob(bidiSpl, printerJobID, 0)
            End If
        Finally
            bidiSpl.UnbindDevice()
        End Try
    End Sub
End Class