// scprobe: Simple contactless card analyzer, V1.7, Windows
#pragma warning (disable:4996)

#include "mf0xAB.h"

void verbose_err(DWORD err, LPTSTR szErr)
{
    if (err == LERR_SUCCESS) lstrcpy(szErr, TEXT("Success!"));
    else if (err == LERR_FAIL) lstrcpy(szErr, TEXT("Command execution error!"));
    else if (err == LERR_HARDWARE) lstrcpy(szErr, TEXT("Hardware error!"));
    else if (err == LERR_TIMEOUT_TX) lstrcpy(szErr, TEXT("Communication error: TX timeout!"));
    else if (err == LERR_TIMEOUT_RX) lstrcpy(szErr, TEXT("Communication error: RX timeout!"));
    else if (err == LERR_NOTSYNC) lstrcpy(szErr, TEXT("Communication error: synchronization lost!"));
    else if (err == LERR_NOREADER) lstrcpy(szErr, TEXT("Can't find reader!"));
    else if (err == LERR_NOCARD) lstrcpy(szErr, TEXT("Can't find card!"));
    else if (err == LERR_INVALIDCARD) lstrcpy(szErr, TEXT("Invalid card type!"));
    else if (err == LERR_INVALIDKEY) lstrcpy(szErr, TEXT("Invalid auth key or card type!"));
    else if (err == LERR_IOERR) lstrcpy(szErr, TEXT("I/O error!"));
    else if (err == LERR_DATAREAD) lstrcpy(szErr, TEXT("Can't read data from card!"));
    else if (err == LERR_DATAWRITE) lstrcpy(szErr, TEXT("Can't write data to card!"));
    else if (err == LERR_BUSY) lstrcpy(szErr, TEXT("Device busy error!"));
    else lstrcpy(szErr, TEXT("Unknown error!"));
}

void scp_verbscd(BYTE scd, LPTSTR sztp)
{
    if (scd == SCD_UL) lstrcpy(sztp, TEXT("UL"));
    else if (scd == SCD_CL1K) lstrcpy(sztp, TEXT("CL1K"));
    else if (scd == SCD_CL4K) lstrcpy(sztp, TEXT("CL4K"));
    else if (scd == SCD_CL1K1) lstrcpy(sztp, TEXT("CL1K1"));
    else if (scd == SCD_CL4K1) lstrcpy(sztp, TEXT("CL4K1"));
    else if (scd == SCD_PL2S1) lstrcpy(sztp, TEXT("PL2S1"));
    else if (scd == SCD_PL4S1) lstrcpy(sztp, TEXT("PL4S1"));
    else if (scd == SCD_PL2S2) lstrcpy(sztp, TEXT("PL2S2"));
    else if (scd == SCD_PL4S2) lstrcpy(sztp, TEXT("PL4S2"));
    else if (scd == SCD_PL2S3) lstrcpy(sztp, TEXT("PL2S3"));
    else if (scd == SCD_PL4S3) lstrcpy(sztp, TEXT("PL4S3"));
    else if (scd == SCD_MINI) lstrcpy(sztp, TEXT("MINI"));
    else if (scd == SCD_DF1) lstrcpy(sztp, TEXT("DF1"));
    else if (scd == SCD_DF2) lstrcpy(sztp, TEXT("DF2"));
    else if (scd == SCD_SMMX) lstrcpy(sztp, TEXT("SMMX"));
    else if (scd == SCD_SM1K) lstrcpy(sztp, TEXT("SM1K"));
    else if (scd == SCD_SM2K) lstrcpy(sztp, TEXT("SM2K"));
    else if (scd == SCD_SM4K) lstrcpy(sztp, TEXT("SM4K"));
    else lstrcpy(sztp, TEXT("???"));
}

int __cdecl _tmain(int argc, TCHAR *argv[], TCHAR *envp[])
{
    CCR         ccr;
    DWORD       err = LERR_SUCCESS;
    TCHAR       szLocal[256];
    DWORD       scp_mode = 1, scp_parm1 = 0, scp_parm2 = 0;;

    BYTE        r_model = 0, r_hw = 0, phdl = 0;
    DWORD       r_sn = 0;

    BYTE        sc_type = SCD_UNKNOWN;
    BYTE        sc_uidlen = 0, sc_uid[SZ_UID];
    WORD        sc_atqa = 0;
    BYTE        sc_sak = 0;
    BYTE        sc_atslen, sc_ats[SZ_ATS];

    BYTE        txbuf[128], rxbuf[128], bitlen, bytelen;
    DWORD       cnt, ecnt, idx, rpt;

    _tprintf(TEXT("SC Probe, contactless card analyzer, V%s. USB only.\n--\n"), RDRAPI_VER);
    memset(&ccr, 0, sizeof(ccr));

    if (argc > 1)
    {
        if (!lstrcmpi(argv[1], TEXT("-WUPA"))) scp_mode = 0;
        else if ((!lstrcmpi(argv[1], TEXT("-COMM"))) && (argc == 3) && (_istxdigit(argv[2][0])))
        {
            scp_mode = 1;
            _stscanf(argv[2], TEXT("%X"), &scp_parm1);
            if (scp_parm1 > 255)
            {
                _tprintf(TEXT("Invalid -COMM parameter!\n"));
                goto main_exit;
            }
        }
        else if ((!lstrcmpi(argv[1], TEXT("-C2PL"))) && (argc == 4) && (_istxdigit(argv[2][0])) && (_istxdigit(argv[3][0])))
        {
            scp_mode = 2;
            _stscanf(argv[2], TEXT("%X"), &scp_parm1);
            if (scp_parm1 > 255)
            {
                _tprintf(TEXT("Invalid -C2PL parameter 1!\n"));
                goto main_exit;
            }
            _stscanf(argv[3], TEXT("%X"), &scp_parm2);
            if (scp_parm2 > 255)
            {
                _tprintf(TEXT("Invalid -C2PL parameter 2!\n"));
                goto main_exit;
            }
        }
        else
        {
            _tprintf(TEXT("Usage: scprobe.exe -WUPA\n       scprobe.exe -COMM <hex_cmd>\n       scprobe.exe -C2PL <hex_cmd> <hex_parm>\n"));
            goto main_exit;
        }
    }

    // open USB device; return LERR_SUCCESS, LERR_HARDWARE
    if ((err = link_hidopen(0, NULL, &ccr)) != LERR_SUCCESS)
    {
        err = LERR_NOREADER;
        goto main_exit;
    }
    // DIR-mode initialization
    if ((err = mfInit(&ccr, &r_model, &r_hw, &r_sn)) != LERR_SUCCESS) goto main_exit;
    _tprintf(TEXT("RD-0%uAB, SN:%08X, HW:%u.%u\n--\n"), r_model, r_sn, r_hw >> 4, r_hw & 15);

    // card type detection
    _tprintf(TEXT("Search for the type of card... "));
    sc_uidlen = MFDM_RWC;
    if ((err = mfType(&ccr, 7, &sc_type, &sc_uidlen, sc_uid, &sc_atqa, &sc_sak, &sc_atslen, sc_ats)) != LERR_SUCCESS) goto main_exit;
    _tprintf(TEXT("Ok.\n\n"));

    scp_verbscd(sc_type, szLocal);
    if (sc_uidlen & MFDM_RWC) lstrcat(szLocal, TEXT("-RW"));

    lstrcat(szLocal, TEXT(", UID="));
    for (cnt = 0; (cnt < ((DWORD)sc_uidlen&15)) && (cnt < 10); cnt++)
        wsprintf(&szLocal[lstrlen(szLocal)], TEXT("%02X"), sc_uid[cnt]);
    wsprintf(&szLocal[lstrlen(szLocal)], TEXT(", ATQA=%04X, SAK=%02X."), sc_atqa, sc_sak);
    _tprintf(TEXT("%s\n"), szLocal);

    lstrcpy(szLocal, TEXT("ATS="));
    if (sc_atslen == 0) lstrcat(szLocal, TEXT("none"));
    else if (sc_atslen == 1) wsprintf(&szLocal[lstrlen(szLocal)], TEXT("none (ack=%02X)"), sc_ats[0]);
    else
    {
        for (cnt = 0; ((cnt < sc_atslen) && (cnt < SZ_ATS)); cnt++)
            wsprintf(&szLocal[lstrlen(szLocal)], TEXT("%02X"), sc_ats[cnt]);
    }
    lstrcat(szLocal, TEXT("."));
    _tprintf(TEXT("%s\n--\n"), szLocal);

    // *** read phd level and extended error flags after select ***
    if ((err = mfSelect(&ccr, 7, PICC_REQALL, NULL, NULL, NULL, NULL)) != LERR_SUCCESS) goto main_exit;
    if ((err = link_packet(&ccr, CMG_ISO, 0, NULL, 0, rxbuf, 2)) != LERR_SUCCESS) goto main_exit;
    phdl = rxbuf[1] & 0x1F;
    _tprintf(TEXT("PHD level: %02X.\n--\n"), phdl);

    if (scp_mode == 0)
    {
        // *** search for WUPA command ***
        _tprintf(TEXT("WUPA probe loop: from 0 to 127 (7 bits).\n"));
        for (ecnt = 0, cnt = 0; cnt != 128; cnt++)
        {
            _tprintf(TEXT("\r Probe %02X... "), cnt);

            for (rpt = 3; rpt != 0; rpt--)
            {
                // break carrier
                if ((err = mfBrkCarrier(&ccr, 30)) != LERR_SUCCESS) goto main_exit;
                // set phd level
                if ((err = link_packet(&ccr, CMS_ISO, phdl, NULL, 0, NULL, 0)) != LERR_SUCCESS) goto main_exit;
                // set WUPA command
                txbuf[0] = (BYTE)cnt;
                if (((err = mfBitIO(&ccr, txbuf, 7, rxbuf, &bitlen, NULL)) != LERR_SUCCESS) && (err != LERR_FAIL)) goto main_exit;
                if ((err == LERR_SUCCESS) && (bitlen))
                {
                    ecnt++;
                    bytelen = (bitlen % 8) ? bitlen / 8 + 1 : bitlen / 8;
                    lstrcpy(szLocal, TEXT("ATQA="));
                    for (idx = 0; idx < bytelen; idx++)
                    {
                        if (idx) wsprintf(&szLocal[lstrlen(szLocal)], TEXT("."));
                        wsprintf(&szLocal[lstrlen(szLocal)], TEXT("%02X"), rxbuf[idx]);
                    }
                    wsprintf(&szLocal[lstrlen(szLocal)], TEXT(" - %u bits."), bitlen);
                    _tprintf(TEXT("%s\n"), szLocal);
                    if ((err = mfBeep(&ccr, 0x02)) != LERR_SUCCESS) goto main_exit;
                    break;
                }
            }
        }
        _tprintf(TEXT("\rWUPA probe loop completed.  %u acks found.\n"), ecnt);
    }
    else if (scp_mode == 1)
    {
        // *** search for command ***
        _tprintf(TEXT("Command probe loop: from 0 to 255 (8 bits). Parameter: %02X.\n"), scp_parm1);
        for (ecnt = 0, cnt = 0; cnt != 256; cnt++)
        {
            _tprintf(TEXT("\r Probe %02X... "), cnt);
            for (rpt = 3; rpt != 0; rpt--)
            {
                // break carrier
                if ((err = mfBrkCarrier(&ccr, 30)) != LERR_SUCCESS) goto main_exit;
                // set phd level
                if ((err = link_packet(&ccr, CMS_ISO, phdl | 0x20, NULL, 0, NULL, 0)) != LERR_SUCCESS) goto main_exit;
                // select card
                if ((err = mfSelect(&ccr, 7, PICC_REQALL, NULL, NULL, NULL, NULL)) != LERR_SUCCESS) goto main_exit;
                // set command
                txbuf[0] = (BYTE)cnt; txbuf[1] = (BYTE)scp_parm1;
                rxbuf[0] = 0;
                if (((err = mfLong(&ccr, txbuf, 2, rxbuf, &bitlen)) != LERR_SUCCESS) && (err != LERR_FAIL)) goto main_exit;
                if ((err == LERR_SUCCESS) && ((bitlen > 4) || (rxbuf[0] == 0x0A) ))
                {
                    ecnt++;
                    bytelen = (bitlen % 8) ? bitlen / 8 + 1 : bitlen / 8;
                    lstrcpy(szLocal, TEXT("ACK="));
                    for (idx = 0; idx < bytelen; idx++)
                    {
                        if (idx) wsprintf(&szLocal[lstrlen(szLocal)], TEXT("."));
                        wsprintf(&szLocal[lstrlen(szLocal)], TEXT("%02X"), rxbuf[idx]);
                    }
                    wsprintf(&szLocal[lstrlen(szLocal)], TEXT("|%u bits"), bitlen);
                    _tprintf(TEXT("%s\n"), szLocal);
                    if ((err = mfBeep(&ccr, 0x02)) != LERR_SUCCESS) goto main_exit;
                    break;
                }
            }
        }
        _tprintf(TEXT("\rCommand probe loop completed.  %u acks found.\n"), ecnt);
    }
    else if (scp_mode == 2)
    {
        // *** search for length ***
        _tprintf(TEXT("Length probe: from 0 to 16. Command: %02X.\n"), scp_parm1);
        for (cnt = 0; cnt != 16; cnt++)
        {
            for (rpt = 3; rpt != 0; rpt--)
            {
                _tprintf(TEXT("\r Probe %02X... "), cnt);
                // break carrier
                if ((err = mfBrkCarrier(&ccr, 30)) != LERR_SUCCESS) goto main_exit;
                // set phd level
                if ((err = link_packet(&ccr, CMS_ISO, phdl | 0x20, NULL, 0, NULL, 0)) != LERR_SUCCESS) goto main_exit;
                // select card
                if ((err = mfSelect(&ccr, 7, PICC_REQALL, NULL, NULL, NULL, NULL)) != LERR_SUCCESS) goto main_exit;
                // set command
                txbuf[0] = (BYTE)scp_parm1; txbuf[1] = (BYTE)scp_parm2;
                rxbuf[0] = 0;
                if (((err = mfLong(&ccr, txbuf, 2, rxbuf, &bitlen)) != LERR_SUCCESS) && (err != LERR_FAIL)) goto main_exit;
                if ((err == LERR_SUCCESS) && (bitlen == 4) && (rxbuf[0] == 0x0A))
                {
                    _tprintf(TEXT("ACK correct!\n"));
                }
                else continue;

                // * Success! Sending string now... *
                for (ecnt = 0; ecnt != 32; ecnt++) txbuf[ecnt] = (BYTE)ecnt;
                rxbuf[0] = 0;
                _tprintf(TEXT("Sending data ["));
                for (ecnt = 0; ecnt <= cnt; ecnt++)
                {
                    if (ecnt) _tprintf(TEXT(".%02X"), txbuf[ecnt]);
                    else _tprintf(TEXT("%02X"), txbuf[ecnt]);
                }
                _tprintf(TEXT("]... "));
                if (((err = mfLong(&ccr, txbuf, (BYTE)cnt + 1, rxbuf, &bitlen)) != LERR_SUCCESS) && (err != LERR_FAIL)) goto main_exit;
                if ((err == LERR_SUCCESS) && (bitlen >=4 ))
                {
                    bytelen = (bitlen % 8) ? bitlen / 8 + 1 : bitlen / 8;
                    lstrcpy(szLocal, TEXT("ACK="));
                    for (idx = 0; idx < bytelen; idx++)
                    {
                        if (idx) wsprintf(&szLocal[lstrlen(szLocal)], TEXT("."));
                        wsprintf(&szLocal[lstrlen(szLocal)], TEXT("%02X"), rxbuf[idx]);
                    }
                    wsprintf(&szLocal[lstrlen(szLocal)], TEXT("|%u bits"), bitlen);
                    _tprintf(TEXT("%s\n"), szLocal);
                    if ((err = mfBeep(&ccr, 0x02)) != LERR_SUCCESS) goto main_exit;
                    break;
                }
                else
                {
                    _tprintf(TEXT("\n"));
                }

            }
        }
        _tprintf(TEXT("\nLength probe loop completed.\n"));
    }

    err = LERR_SUCCESS;
main_exit:
    if (err)
    {
        verbose_err(err, szLocal);
        _tprintf(TEXT("%s\n"), szLocal);
    }

    if ((link_isopen(&ccr)) && (err != LERR_HARDWARE) && (err != LERR_NOREADER))
    {
        if (err) mfBeep(&ccr, 0x03);    // if fail
        else mfBeep(&ccr, 0x02);        // if success
    }
    link_close(&ccr);

    return 0;
}
