#pragma warning (disable:4996)

#include <windows.h>
#include <commctrl.h>

#include "c2rw.h"
#include "mf0xAB.h"
#include "c2rw_gui.h"
#include "c2rw_gvar.h"
#include "resource.h"

CCR                     ccr = { 0 };
DWORD                   n_caption = NULL;   // #nres for process dialog caption
LPTHREAD_START_ROUTINE  p_thrfun = NULL;    // pointer to thread fn

LRESULT CALLBACK proc_dlgp(HWND hDlg, DWORD msg, WPARAM wParam, LPARAM lParam);

// always added if b.6 == 1, b.7 - key A|B, b5..b0 - real sector
BYTE defKeys[][7] = {
        { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // dummy key
        { 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, // default key A (first key used by program if no user defined key)
        { 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, // default key B (first key used by program if no user defined key)
        { 0x40, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5 }, // default key A
        { 0xC0, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5 }, // default key B
        { 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Blank key, A
        { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Blank key, B
        { 0x40, 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7 }, // A key, NFCForum content key
        { 0xC0, 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7 }, // B key, NFCForum content key
        { 0x40, 0x4D, 0x3A, 0x99, 0xC3, 0x51, 0xDD }, // A key, current key - original mfoc
        { 0xC0, 0x4D, 0x3A, 0x99, 0xC3, 0x51, 0xDD }, // B key, current key - original mfoc
        { 0x40, 0x1A, 0x98, 0x2C, 0x7E, 0x45, 0x9A },
        { 0xC0, 0x1A, 0x98, 0x2C, 0x7E, 0x45, 0x9A },
        { 0x40, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF },
        { 0xC0, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF },
        { 0x40, 0x71, 0x4C, 0x5C, 0x88, 0x6E, 0x97 },
        { 0xC0, 0x71, 0x4C, 0x5C, 0x88, 0x6E, 0x97 },
        { 0x40, 0x58, 0x7E, 0xE5, 0xF9, 0x35, 0x0F },
        { 0xC0, 0x58, 0x7E, 0xE5, 0xF9, 0x35, 0x0F },
        { 0x40, 0xA0, 0x47, 0x8C, 0xC3, 0x90, 0x91 },
        { 0xC0, 0xA0, 0x47, 0x8C, 0xC3, 0x90, 0x91 },
        { 0x40, 0x53, 0x3C, 0xB6, 0xC7, 0x23, 0xF6 },
        { 0xC0, 0x53, 0x3C, 0xB6, 0xC7, 0x23, 0xF6 },
        { 0x40, 0x8F, 0xD0, 0xA4, 0xF2, 0x56, 0xE9 },
        { 0xC0, 0x8F, 0xD0, 0xA4, 0xF2, 0x56, 0xE9 } };


// thread launcher
BOOL launch_thread(LPTHREAD_START_ROUTINE p_tfn, DWORD nres)
{
    BOOL    bRet = FALSE;

    gui_disen(FALSE);
    n_caption = nres;   // set caption number
    p_thrfun = p_tfn;   // set thread fn
    // call dialog box
    bRet = (DialogBox(hInst, MAKEINTRESOURCE(IDD_PROC), hMain, (DLGPROC)proc_dlgp)) ? TRUE : FALSE;
    gui_disen(TRUE);

    return bRet;
}

void thr_setpbar(HWND hDlg, DWORD val, DWORD max)
{
    SendMessage(GetDlgItem(hDlg, IDC_PBAR), PBM_SETPOS, (WPARAM)val * 10000 / max, 0L);
    SendMessage(GetDlgItem(hDlg, IDC_PBAR), PBM_SETBARCOLOR, 0, (LPARAM)RGB(0, 255, 0));
}

void thr_rstpbar(HWND hDlg)
{
    SendMessage(GetDlgItem(hDlg, IDC_PBAR), PBM_SETRANGE, 0L, MAKELPARAM(0, 10000));
    SendMessage(GetDlgItem(hDlg, IDC_PBAR), PBM_SETSTEP, 0, 0);
    SendMessage(GetDlgItem(hDlg, IDC_PBAR), PBM_SETPOS, 0, 0L);
    thr_setpbar(hDlg, 0, 100);
}


void thr_logclr(HWND hEdit)
{
    SendMessage(hEdit, EM_LIMITTEXT, -1, 0);
    SetWindowText(hEdit, TEXT(""));
}

void thr_logadd(HWND hEdit, LPTSTR szMsg)
{
    DWORD       loglen;
    HWND        old_wnd;

    loglen = GetWindowTextLength(hEdit);
    old_wnd = GetFocus();
    SetFocus(hEdit);
    SendMessage(hEdit, EM_SETSEL, (WPARAM)loglen, (LPARAM)loglen);
    SendMessage(hEdit, EM_REPLACESEL, 0, (LPARAM)((LPSTR)szMsg));
    SetFocus(old_wnd);
}

void thr_res2log(HWND hEdit, DWORD nres, BOOL ret)
{
    TCHAR               szLocal[256];
    LoadString(hInst, nres, szLocal, sizeof(szLocal) / sizeof(TCHAR));
    if (ret) lstrcat(szLocal, TEXT("\r\n"));
    thr_logadd(hEdit, szLocal);
}

void thr_verboseerr(DWORD err, TCHAR *szMsg)
{
    TCHAR               szLocal[128];

    if (err <= LERR_USER)
    {
        LoadString(hInst, STR_SUCCESS + err, szLocal, sizeof(szLocal) / sizeof(TCHAR));
        lstrcpy(szMsg, szLocal);
    }
}

void thr_verbosetype(BYTE scd, TCHAR *sztp)
{
    if (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("???"));
    }
}

DWORD thr_probekey(HWND hDlg, BYTE blk, BYTE *authkey)
{
    DWORD       err = LERR_SUCCESS;
    TCHAR       szSample[128];
    TCHAR       szLocal[128];
    DWORD       cnt;

    LoadString(hInst, STR_DKSRC02, szSample, sizeof(szSample) / sizeof(TCHAR)); // "Probe key"
    wsprintf(szLocal, szSample, (authkey[0]) ? 'B' : 'A');
    thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), szLocal);                          // add message to log
    for (szLocal[0] = 0, cnt = 0; cnt < 6; cnt++)                               // build key string
        wsprintf(&szLocal[lstrlen(szLocal)], TEXT("%02X"), authkey[cnt + 1] & 0xFF);
    thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), szLocal);                          // add key to log
    thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DKSRC02T, FALSE);             // terminator "..."
    // set authorization key to RAM
    if ((err = mfSetAK(&ccr, 0, &authkey[1])) != LERR_SUCCESS) goto thr_exit;
    // data block authorization only then card halted
    if (((err = mfAuth(&ccr, 4, (authkey[0]) ? PICC_AUTHENT1B : PICC_AUTHENT1A, 0, blk,
        NULL, NULL, NULL, NULL)) != LERR_SUCCESS) && (err != LERR_INVALIDKEY)) goto thr_exit;
    thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), (err == LERR_SUCCESS) ? STR_DKSRC02G : STR_DKSRC02N, FALSE);
thr_exit:
    return err;
}

void build_rsauth(BYTE rs, BYTE(*auth)[7], PDWORD pauthlen)
{
    DWORD       idx, cnt;
    // idx == 2: skip key A & B from dump
    // cnt == 1: skip dummy key
    for (idx = 2, cnt = 1; (cnt < sizeof(defKeys) / sizeof(defKeys[0])) && (idx < *pauthlen); cnt++)
    {
        if ((defKeys[cnt][0] & 0x40) || ((defKeys[cnt][0] & 0x3F) == rs))
        {
            memcpy(auth[idx], defKeys[cnt], 7);
            auth[idx++][0] &= 0x80;
        }
    }
    *pauthlen = idx;
}

DWORD thr_rwtest(HWND hDlg, PBYTE prw, PBYTE pconti)
{
    DWORD       err = LERR_SUCCESS;
    BYTE        rwf;

    thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_RTSTRW01, FALSE);         // message "Test for RW-card..."
    // *** test for RW-card ***
    if (((err = mfSelect(&ccr, 4, PICC_REQRW, NULL, NULL, NULL, NULL)) != LERR_SUCCESS) &&
        (err != LERR_NOCARD)) goto thr_exit;
    // test for normal card if non-RW
    if ((rwf = (err == LERR_SUCCESS) ? 1 : 0) == 0)
    {
        if ((err = mfSelect(&ccr, 4, PICC_REQALL, NULL, NULL, NULL, NULL)) != LERR_SUCCESS) goto thr_exit;
    }
    // *** carrier break ***
    if ((err = mfBrkCarrier(&ccr, 50)) != LERR_SUCCESS) goto thr_exit;
    thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), (rwf) ? STR_RTSTRW01G : STR_RTSTRW01N, FALSE);
    err = LERR_SUCCESS;
    if (prw) *prw = rwf;                        // set RW-flag
    if (pconti) *pconti = 1;                    // set "continue" flag to TRUE
    // * error box if non-RW *
    if ((!rwf) && (c2rw_set.flg & MASK_NRWMSG))
    {
        if (dlg4err(STR_CWARN, STR_SNRWWARN, STR_QNRWWARN, MB_YESNO | MB_ICONWARNING) != IDYES)
        {
            if (pconti) *pconti = 0;            // reset "continue" flag to FALSE
        }
    }
thr_exit:
    return err;
}

LRESULT CALLBACK proc_dlgp(HWND hDlg, DWORD msg, WPARAM wParam, LPARAM lParam)
{
    TCHAR               szLocal[128];
    static  DWORD       dRet;
    // dialog fn
    switch (msg)
    {
        case WM_CLOSE:
        case WM_QUIT:
            if (hThr)
            {
                // set "break" status if thread not closed
                if (hThr) gui_res2stbar(STR_SBREAK);
                TerminateThread(hThr, 0);   // terminate active thread
                hThr = NULL;                // clear handle
                // close handle
                if (link_isopen(&ccr)) link_close(&ccr);
            }
            EndDialog(hDlg, dRet);          // set exit code
            break;

        case WM_INITDIALOG:
            dRet = 0;
            // window centering
            CenterWindow(hDlg, hMain);
            // set dialog caption
            LoadString(hInst, n_caption, szLocal, sizeof(szLocal) / sizeof(TCHAR));
            SetWindowText(hDlg, szLocal);
            thr_logclr(GetDlgItem(hDlg, IDE_LOGBOX));
            thr_rstpbar(hDlg);
            // thread launch
            if (!hThr) TerminateThread(hThr, 0);                    // terminate active thread
            hThr = CreateThread(NULL, 0, p_thrfun, hDlg, 0, NULL);  // create new thread
            if (!hThr) SendMessage(hDlg, WM_CLOSE, 0, 0);           // close dlg if error
            else SetThreadPriority(hThr, THREAD_PRIORITY_NORMAL);   // set priority
            return TRUE;

        case WM_COMMAND:
            if (LOWORD(wParam) == IDCANCEL)
            {
                SendMessage(hDlg, WM_CLOSE, 0, 0);
                break;
            }
            else if (LOWORD(wParam) == IDOK)
            {
                dRet = 1;
                SendMessage(hDlg, WM_CLOSE, 0, 0);
                break;
            }
            break;

        default:
            goto defp;
    }
    return TRUE;

defp:
    return FALSE;
}

DWORD thr_ccrinit(HWND hDlg)
{
    DWORD       err = LERR_SUCCESS;
    TCHAR       szLocal[256];

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

    // set log message
    thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_RINIT01, FALSE);

    memset(&ccr, 0, sizeof(ccr));
    if ((cport) && (cbr))
    {
        // open COM device; return LERR_SUCCESS, LERR_HARDWARE
        if ((err = link_comopen(cport, cbr, &ccr)) != LERR_SUCCESS) goto thr_exit;
    }
    else
    {
        // open USB device; return LERR_SUCCESS, LERR_HARDWARE
        if ((err = link_hidopen(0, NULL, &ccr)) != LERR_SUCCESS)
        {
            err = LERR_NOREADER;
            goto thr_exit;
        }
    }
    thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_RINIT01OK, FALSE);    // set "Ok" message
    // DIR-mode initialization
    if ((err = mfInit(&ccr, &r_model, &r_hw, &r_sn)) != LERR_SUCCESS)
    {
        if ((cport) && (cbr) && (err != LERR_HARDWARE)) err = LERR_NOREADER;
        goto thr_exit;
    }
    thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_RINIT02, FALSE);      // set "Reader:"
    wsprintf(szLocal, TEXT("RD-0%uAB, SN:%08X, HW:%u.%u"), r_model, r_sn, r_hw >> 4, r_hw & 15);
    SetWindowText(hMain, szLocal);  // set new caption to main window
    lstrcat(szLocal, TEXT("."));
    thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), szLocal);  // add string to log
    thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_RINIT02OK, FALSE);    // set "Ok" string
thr_exit:
    return err;
}

DWORD WINAPI t_typesrch(HWND hDlg)
{
    DWORD       err = LERR_SUCCESS;
    TCHAR       szLocal[256];

    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];

    DWORD       cnt;

    gui_res2stbar(STR_STPSRCH);     // set status bar message
    // ccr initialization
    thr_setpbar(hDlg, 1, 10);
    if ((err = thr_ccrinit(hDlg)) != LERR_SUCCESS) goto thr_exit;

    // card type detection
    thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DTPS01, FALSE);   // set "search" string
    thr_setpbar(hDlg, 8, 10);
    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 thr_exit;
    thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DTPS01OK, FALSE); // set "Ok" message
    // * build card identification string *
    // build standard ID
    thr_verbosetype(sc_type, szLocal);                          // card type to string
    if (sc_uidlen & MFDM_RWC) lstrcat(szLocal, TEXT("-RW"));    // set RW flag
    lstrcat(szLocal, TEXT(", UID="));                           // UID to string
    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);
    thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), szLocal);              // add string to log
    thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DTPS02, FALSE);   // set terminator
    // build ATS
    lstrcpy(szLocal, TEXT("ATS: "));
    if (sc_atslen == 0) wsprintf(&szLocal[lstrlen(szLocal)], TEXT("none"));
    else if (sc_atslen == 1) wsprintf(&szLocal[lstrlen(szLocal)], TEXT("none (ack=%02X)"), sc_ats[0]);
    else
    {
        for (cnt = 0; (cnt < ((DWORD)sc_atslen)) && (cnt < 30); cnt++)
            wsprintf(&szLocal[lstrlen(szLocal)], TEXT("%02X"), sc_ats[cnt]);
    }
    lstrcat(szLocal, TEXT("."));
    thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), szLocal);              // add string to log
    thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DTPS03, FALSE);   // set terminator
    // determine gui(!) type
    for (cnt = 0; cnt <= MAX_CCTYPE; cnt++)
    {
        if (c2rw_dsc[cnt].tp == sc_type)
        {
            c2rw_set.tp = (BYTE)cnt;    // set new gui(!) card type in dump
            if (sc_uidlen & MFDM_RWC) c2rw_set.flg |= MASK_RWCARD;  // set RW-mode
            else c2rw_set.flg &= ~MASK_RWCARD;
            gui_setqs();                // gui: set new selector
            gui_settp();                // gui: set new card type
            break;
        }
    }
thr_exit:
    thr_setpbar(hDlg, 10, 10);
    if (err != LERR_SUCCESS)
    {
        thr_verboseerr(err, szLocal);                           // verbose error code
        thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), &szLocal[1]);  // add error message to log
        gui_stbar(szLocal);                                     // add error message to status bar
    }
    else gui_ok2stbar();

    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);

    hThr = NULL;
    SendMessage(GetDlgItem(hDlg, IDE_LOGBOX), WM_KEYDOWN, VK_HOME, 0);
    SendMessage(GetDlgItem(hDlg, IDE_LOGBOX), WM_KEYDOWN, VK_END, 0);
    gui_disen(TRUE);
    return 0;
}


void thr_keysrch(HWND hDlg, BYTE tkey)
{
    DWORD       err = LERR_SUCCESS;

    TCHAR       szSample[128];
    TCHAR       szLocal[128];
    DWORD       idx, cnt;
    BOOL        fnd = FALSE;

    tkey &= 0x80;
    LoadString(hInst, STR_SKSRCH, szSample, sizeof(szSample) / sizeof(TCHAR));
    wsprintf(szLocal, szSample, (tkey & 0x80) ? 'B' : 'A');
    gui_stbar(szLocal);             // set status message
    // ccr initialization
    if ((err = thr_ccrinit(hDlg)) != LERR_SUCCESS) goto thr_exit;
    // * search auth key *
    LoadString(hInst, STR_DKSRC01, szSample, sizeof(szSample) / sizeof(TCHAR));
    wsprintf(szLocal, szSample, (tkey & 0x80) ? 'B' : 'A');
    thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), szLocal);  // add search message to log
    // search loop
    defKeys[0][0] = tkey;
    gui_gethex((tkey & 0x80) ? IDE_SCKEYB : IDE_SCKEYA, 6, &defKeys[0][1]);     // get key from dialog control
    for (idx = 0; idx < sizeof(defKeys) / sizeof(defKeys[0]); idx++)
    {
        thr_setpbar(hDlg, idx, sizeof(defKeys) / sizeof(defKeys[0]));
        if ((idx) && ((defKeys[idx][0] & 0x80) == (defKeys[0][0] & 0x80)) &&
            (!memcmp(&defKeys[idx][1], &defKeys[0][1], 6))) continue;           // skip ak if checked
        if (((defKeys[idx][0] ^ tkey) & 0x80) && ((defKeys[idx][0] & 0x40) == 0)) continue;

        LoadString(hInst, STR_DKSRC02, szSample, sizeof(szSample) / sizeof(TCHAR));     // "Probe key"
        wsprintf(szLocal, szSample, (tkey & 0x80) ? 'B' : 'A');
        thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), szLocal);  // add message to log
        for (szLocal[0] = 0, cnt = 0; cnt < 6; cnt++)                           // build key string
            wsprintf(&szLocal[lstrlen(szLocal)], TEXT("%02X"), defKeys[idx][cnt + 1]);
        thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), szLocal);                      // add key to log
        thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DKSRC02T, FALSE);         // terminator "..."
        // set authorization key to RAM
        if ((err = mfSetAK(&ccr, 0, &defKeys[idx][1])) != LERR_SUCCESS) goto thr_exit;
        // data block authorization only then card halted
        if (((err = mfAuth(&ccr, 4, (tkey & 0x80) ? PICC_AUTHENT1B : PICC_AUTHENT1A, 0, c2rw_set.qs << 2,
            NULL, NULL, NULL, NULL)) != LERR_SUCCESS) && (err != LERR_INVALIDKEY)) goto thr_exit;
        if (err != LERR_SUCCESS)
        {
            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DKSRC02N, FALSE);     //  "Fail."
        }
        else
        {
            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DKSRC02G, FALSE);     //  "Ok."
            break;
        }
    }
    thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DKSRC02E, FALSE);             //  terminator "---"
    if (err == LERR_SUCCESS)
    {
        fnd = TRUE;
        // copy key data to memory block (A or B)
        memcpy(&c2rw_set.mfd_cl[(((DWORD)qs2trailer(c2rw_set.qs)) << 4) + ((tkey & 0x80) ? 10 : 0)], &defKeys[idx][1], 6);
        gui_setdata();
        // build end message (ok)
        LoadString(hInst, STR_SKSRC03G, szSample, sizeof(szSample) / sizeof(TCHAR));
        wsprintf(szLocal, szSample, (tkey & 0x80) ? 'B' : 'A', qs2rs(c2rw_set.qs));
        thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), &szLocal[1]);  // add "key_found" message to log
        gui_stbar(szLocal);                                     // add message to status bar
    }
    else
    {
        // build end message (fail)
        LoadString(hInst, STR_SKSRC03F, szSample, sizeof(szSample) / sizeof(TCHAR));
        wsprintf(szLocal, szSample, (tkey & 0x80) ? 'B' : 'A', qs2rs(c2rw_set.qs));
        thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), &szLocal[1]);  // add "can't_find" message to log
        gui_stbar(szLocal);                                     // add message to status bar
    }
    err = LERR_SUCCESS;
thr_exit:
    thr_setpbar(hDlg, 10, 10);
    if (err != LERR_SUCCESS)
    {
        thr_verboseerr(err, szLocal);                           // verbose error code
        thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), &szLocal[1]);  // add error message to log
        gui_stbar(szLocal);                                     // add error message to status bar
    }
    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);

    hThr = NULL;
    SendMessage(GetDlgItem(hDlg, IDE_LOGBOX), WM_KEYDOWN, VK_HOME, 0);
    SendMessage(GetDlgItem(hDlg, IDE_LOGBOX), WM_KEYDOWN, VK_END, 0);
    gui_disen(TRUE);
}

DWORD WINAPI t_keyAsrch(HWND hDlg)
{
    thr_keysrch(hDlg, 0);
    return 0;
}

DWORD WINAPI t_keyBsrch(HWND hDlg)
{
    thr_keysrch(hDlg, 0x80);
    return 0;
}


// read real sector fn
DWORD thr_readrs(HWND hDlg, BYTE rs)
{
    BYTE        auth[40][7];
    DWORD       err = LERR_SUCCESS;
    TCHAR       szSample[128];
    TCHAR       szLocal[256];

    BYTE        sc_uidlen, sc_uid[SZ_UID];
    BYTE        keyA[6], keyB[6];
    BOOL        keyAok = FALSE, keyBok = FALSE;
    DWORD       blk, sblk, tblk, bcnt;
    DWORD       authlen;
    DWORD       kidx, cnt;

    // * prepare reading *
    sc_uidlen = 0;                                      // for any UID
    sblk = (DWORD)rs2sblk(rs);                          // calculate start block for sector rs
    tblk = (DWORD)rs2trailer(rs);                       // calculate trailer for sector rs
    // * build auth key dimension for sector *
    auth[0][0] = 0x00; memcpy(&auth[0][1], &c2rw_set.mfd_cl[((DWORD)tblk) << 4], 6);        // copy auth A data
    auth[1][0] = 0x80; memcpy(&auth[1][1], &c2rw_set.mfd_cl[(((DWORD)tblk) << 4) + 10], 6); // copy auth B data
    authlen = sizeof(auth) / sizeof(auth[0]);
    build_rsauth(rs, auth, &authlen);                   // build authkey list
    // * sector processing *
    LoadString(hInst, STR_DRDRS01, szSample, sizeof(szSample) / sizeof(TCHAR));
    wsprintf(szLocal, szSample, rs);                    // build status log message
    thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), szLocal);  // add message to log

    for (kidx = 0, blk = sblk; blk <= tblk; blk++)      // reset key idx, launch block loop
    {
        for (bcnt = 0; bcnt < 4; bcnt++)                // break loop when first invalid bcnt or state
        {
            if ((blk + bcnt) > tblk) break;             // break if out of sector
            if (((c2rw_set.flg & MASK_ALLDATA) == 0) &&
                (!blkstate((BYTE)(blk + bcnt)))) break; // break if di state
        }
        if (!bcnt) continue;                            // skip if zero datalen
        // * block reading *
        for (; kidx < authlen;)                         // select auth key from dimension
        {
            BYTE        sc_acm;                         // auth command
            if (c2rw_set.flg & MASK_RWCARD) sc_acm = 0; // ignore auth for RW mode
            else
            {
                if (auth[kidx][0])  sc_acm = PICC_AUTHENT1B;    // auth B command
                else sc_acm = PICC_AUTHENT1A;                   // auth A command

                if ((kidx != 0) && (!memcmp(auth[kidx], auth[0], 7))) { kidx++;  continue; } // skip key if tested
                if ((kidx != 1) && (!memcmp(auth[kidx], auth[1], 7))) { kidx++;  continue; } // skip key if tested
            }
            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DRDRS02, FALSE);  // "Read"
            if (bcnt == 1)
            {
                wsprintf(szLocal, TEXT("S%u:%u / B%u"), blk2rs((BYTE)blk), blk - sblk, blk);
            }
            else
            {
                wsprintf(szLocal, TEXT("S%u:%u-%u / B%u-%u"),
                    blk2rs((BYTE)blk), blk - sblk, blk - sblk + bcnt - 1, blk, blk + bcnt - 1);
            }
            thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), szLocal);                  // add message to log

            if (sc_acm)
            {
                LoadString(hInst, STR_DRDRS02K, szSample, sizeof(szSample) / sizeof(TCHAR));
                wsprintf(szLocal, szSample, (auth[kidx][0]) ? 'B' : 'A');       // build status log message
                for (cnt = 0; cnt < 6; cnt++)                                   // build key string
                    wsprintf(&szLocal[lstrlen(szLocal)], TEXT("%02X"), auth[kidx][cnt + 1]);
                thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), szLocal);              // add message to log
            }
            else
            {
                thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DRDRS02R, FALSE); // "RW-mode"
            }
            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DRDRS02T, FALSE);     // "..."
            // *** set new authorization key ***
            if ((sc_acm) && ((err = mfSetAK(&ccr, 0, &auth[kidx][1])) != LERR_SUCCESS)) goto thr_exit;
            // data read; sc_uidlen = 0 for any uid read (in first read)
            err = mfDataRead(&ccr, 7, ((sc_acm) ? MFDM_NMC : MFDM_RWC) | MFDM_BLOCK,
                sc_acm, 0, (BYTE)blk, (BYTE)bcnt, &c2rw_set.mfd_cl[blk << 4], &sc_uidlen, sc_uid, NULL, NULL);
            if ((err == LERR_FAIL) ||
                (err == LERR_HARDWARE) ||
                (err == LERR_TIMEOUT_TX) ||
                (err == LERR_TIMEOUT_RX) ||
                (err == LERR_NOTSYNC) ||
                (err == LERR_NOREADER)) goto thr_exit;
            else if (err != LERR_SUCCESS)
            {
                thr_verboseerr(err, szLocal);               // verbose error code
                lstrcat(szLocal, TEXT("\r\n"));             // add "next row" characters
                thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), &szLocal[1]);  // add error message to log
                if ((keyAok) && (keyBok)) goto thr_autherr; // stop key change, error!
                else kidx++;                                // change auth key
            }
            else // err == LERR_SUCCESS
            {
                blk += bcnt - 1;                            // increment blk counter and correct by -1
                thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DRDRS02G, FALSE); // "Ok."
                if (sc_acm)
                {
                    if (auth[kidx][0])
                    {
                        memcpy(keyB, &auth[kidx][1], 6);
                        keyBok = TRUE;
                    }
                    else
                    {
                        memcpy(keyA, &auth[kidx][1], 6);
                        keyAok = TRUE;
                    }
                }
                break;
            }
            if (sc_acm == 0) goto thr_autherr;
        }
        if (kidx >= authlen) goto thr_autherr;
    }
    if (err == LERR_SUCCESS)
    {
        if ((c2rw_set.flg & MASK_RWCARD) == 0)
        {
            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DRDRS02E, FALSE);         // terminator "--"
            if ((kidx < authlen) && ((!keyAok) || (!keyBok)))
            {
                if (!keyAok) thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DRDRS03A, FALSE);    // "key A not tested"
                if (!keyBok) thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DRDRS03B, FALSE);    // "key B not tested"
                thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DRDRS03C, FALSE);                 // "Continue..."
                for (; kidx < authlen; kidx++)                  // select auth key from dimension
                {
                    if ((kidx != 0) && (!memcmp(auth[kidx], auth[0], 7))) { kidx++;  continue; } // skip key if tested
                    if ((kidx != 1) && (!memcmp(auth[kidx], auth[1], 7))) { kidx++;  continue; } // skip key if tested

                    if ((!auth[kidx][0]) && (keyAok)) continue; // skip key A if prev ok
                    if ((auth[kidx][0]) && (keyBok)) continue;  // skip key B if prev ok

                    if ((err = thr_probekey(hDlg, (BYTE)tblk, auth[kidx])) == LERR_SUCCESS)
                    {
                        if (auth[kidx][0])
                        {
                            memcpy(keyB, &auth[kidx][1], 6);
                            keyBok = TRUE;
                        }
                        else
                        {
                            memcpy(keyA, &auth[kidx][1], 6);
                            keyAok = TRUE;
                        }
                        if ((keyAok) && (keyBok)) break;
                    }
                    if ((err == LERR_FAIL) ||
                        (err == LERR_HARDWARE) ||
                        (err == LERR_TIMEOUT_TX) ||
                        (err == LERR_TIMEOUT_RX) ||
                        (err == LERR_NOTSYNC) ||
                        (err == LERR_NOREADER)) goto thr_exit;
                }
            }
            if (keyAok)
            {
                if (memcmp(&auth[0][1], keyA, 6))
                    thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DRDRS04A, FALSE);     // "The key A is updated."
                memcpy(&c2rw_set.mfd_cl[((DWORD)tblk) << 4], keyA, 6);                  // copy new auth A data
            }
            else
            {
                memcpy(&c2rw_set.mfd_cl[((DWORD)tblk) << 4], &auth[0][1], 6);           // copy old auth A data
                thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DRDRS05A, FALSE);         // "The key A is not correct."
            }
            if (keyBok)
            {
                if (memcmp(&auth[1][1], keyB, 6))
                    thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DRDRS04B, FALSE);     // "The key B is updated."
                memcpy(&c2rw_set.mfd_cl[(((DWORD)tblk) << 4) + 10], keyB, 6);           // copy auth B data
            }
            else
            {
                memcpy(&c2rw_set.mfd_cl[(((DWORD)tblk) << 4) + 10], &auth[1][1], 6);    // copy old auth B data
                thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DRDRS05B, FALSE);         // "The key B is not correct."
            }
        }
        gui_setdata();  // gui update
        LoadString(hInst, STR_DRDRS06G, szSample, sizeof(szSample) / sizeof(TCHAR));
        wsprintf(szLocal, szSample, rs);                            // build status log message
        thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), szLocal);          // add message to log
        err = LERR_SUCCESS;
        goto thr_exit;
    }
thr_autherr:
    LoadString(hInst, STR_DRDRS06N, szSample, sizeof(szSample) / sizeof(TCHAR));
    wsprintf(szLocal, szSample, rs);                        // build log message
    thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), szLocal);      // add message to log
    err = LERR_DATAREAD;                                    // can't read data error
thr_exit:
    return err;
}

// read real sector
DWORD WINAPI t_rdrs(HWND hDlg)
{
    DWORD       err = LERR_SUCCESS;
    TCHAR       szSample[128];
    TCHAR       szLocal[256];
    BYTE        rs;

    rs = qs2rs(c2rw_set.qs);            // covert qs to real sector
    LoadString(hInst, STR_SRDRS, szSample, sizeof(szSample) / sizeof(TCHAR));
    wsprintf(szLocal, szSample, rs);    // build status bar message
    gui_stbar(szLocal);                 // set status message

    // *** initialization ***
    thr_setpbar(hDlg, 1, 10);
    if ((err = thr_ccrinit(hDlg)) != LERR_SUCCESS) goto thr_exit;
    // *** read sector ***
    thr_setpbar(hDlg, 5, 10);
    if (((err = thr_readrs(hDlg, rs)) != LERR_SUCCESS) && (err != LERR_DATAREAD)) goto thr_exit;
    thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), (err == LERR_SUCCESS) ? STR_DRDRS07G : STR_DRDRS07N, FALSE);  // "operation" message
    err = LERR_SUCCESS;
thr_exit:
    thr_setpbar(hDlg, 10, 10);
    if (err != LERR_SUCCESS)
    {
        thr_verboseerr(err, szLocal);                           // verbose error code
        thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), &szLocal[1]);  // add error message to log
        gui_stbar(szLocal);                                     // add error message to status bar
    }
    else gui_ok2stbar();
    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);

    hThr = NULL;
    SendMessage(GetDlgItem(hDlg, IDE_LOGBOX), WM_KEYDOWN, VK_HOME, 0);
    SendMessage(GetDlgItem(hDlg, IDE_LOGBOX), WM_KEYDOWN, VK_END, 0);
    gui_disen(TRUE);
    return 0;
}

// read card
DWORD WINAPI t_rdcard(HWND hDlg)
{
    DWORD       err = LERR_SUCCESS;
    TCHAR       szLocal[256];
    BYTE        rserr[40];
    BYTE        rs, rslen;

    memset(rserr, 0, sizeof(rserr));                // clear error flags
    gui_res2stbar(STR_SRDCARD);                     // set status message
    // *** initialization ***
    if ((err = thr_ccrinit(hDlg)) != LERR_SUCCESS) goto thr_exit;
    if (c2rw_dsc[c2rw_set.tp].tp == SCD_UL)
    {
        BYTE        sc_uidlen = 0, sc_uid[SZ_UID];
        rslen = 16;
        // *** read pages ***
        thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DRDPG01E, FALSE); // add "--" terminator
        for (rs = 0; rs < rslen; rs++)                                  // launch page loop
        {
            DWORD       bcnt;
            for (bcnt = 0; bcnt < (DWORD)rslen; bcnt++)                 // break loop when first invalid bcnt or state
            {
                if (((DWORD)rs + bcnt) > ((DWORD)rslen - 1)) break;     // break if out of sector
                if (((c2rw_set.flg & MASK_ALLDATA) == 0) &&
                    (!get_pgstate(rs + (BYTE)bcnt))) break;             // break if di state
                rserr[(DWORD)rs + bcnt] |= 2;                           // set "work" flag
            }
            if (!bcnt) continue;                                        // skip if zero datalen

            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DRDPG01, FALSE);  // "Read"
            if (bcnt == 1) wsprintf(szLocal, TEXT("P%u"), (DWORD)rs);
            else wsprintf(szLocal, TEXT("P%u-%u"), rs, (DWORD)rs + bcnt - 1);
            thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), szLocal);              // add message to log
            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DRDPG01T, FALSE); // "..."

            thr_setpbar(hDlg, (DWORD)rs + bcnt, (DWORD)rslen);              // set progress bar
            // data read; sc_uidlen = 0 for any uid read
            err = mfDataRead(&ccr, 7, ((c2rw_set.flg & MASK_RWCARD) ? MFDM_RWC : MFDM_NMC) | MFDM_PAGE,
                0, 0, (BYTE)rs, (BYTE)bcnt, &c2rw_set.mfd_ul[rs << 2], &sc_uidlen, sc_uid, NULL, NULL);
            gui_setdata();  // gui update
            if ((err == LERR_FAIL) ||
                (err == LERR_HARDWARE) ||
                (err == LERR_TIMEOUT_TX) ||
                (err == LERR_TIMEOUT_RX) ||
                (err == LERR_NOTSYNC) ||
                (err == LERR_NOREADER)) goto thr_exit;
            else if (err != LERR_SUCCESS)
            {
                DWORD       cnt;
                for (cnt = 0; cnt < bcnt; cnt++) rserr[rs + cnt] |= 1;  // set error flag
                thr_verboseerr(err, szLocal);                           // verbose error code
                lstrcat(szLocal, TEXT("\r\n"));                         // add "next row" characters
                thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), &szLocal[1]);  // add error message to log
                rs += (BYTE)bcnt - 1;   // increment blk counter and correct by -1
            }
            else // err == LERR_SUCCESS
            {
                thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DRDPG01G, FALSE); // "Ok"
                rs += (BYTE)bcnt - 1;   // increment blk counter and correct by -1
            }
        }
        thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DRDPG01E, FALSE); // add "--" terminator
        // * test for error message *
        for (rs = 0; rs < rslen; rs++)
            if (rserr[rs] & 1) break;
        if (rs != rslen)        // if <errors>
        {
            BOOL        ff;     // first flag
            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DRDPG02, FALSE);  // "Error reading pages"
            for (ff = TRUE, rs = 0; rs < rslen; rs++)
            {
                if (rserr[rs] & 1)
                {
                    if (ff) { wsprintf(szLocal, TEXT(" %u"), rs); ff = FALSE; }
                    else wsprintf(szLocal, TEXT(", %u"), rs);
                    thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), szLocal);      // add message to log
                }
            }
            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DRDPG02E, FALSE); // add terminator
            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DRDCR04N, FALSE); // add "Operation failed" message
            err = LERR_DATAREAD;
        }
        else
        {   // if success
            BOOL        ff;     // first flag
            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DRDPG03, FALSE);  // "Reading pages"
            for (ff = TRUE, rs = 0; rs < rslen; rs++)
            {
                if (rserr[rs] & 2)
                {
                    if (ff) { wsprintf(szLocal, TEXT(" %u"), rs); ff = FALSE; }
                    else wsprintf(szLocal, TEXT(", %u"), rs);
                    thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), szLocal);      // add message to log
                }
            }
            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DRDPG03E, FALSE); // add terminator
            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DRDCR04G, FALSE); // add "Operation complete" message
            err = LERR_SUCCESS;
        }
    }
    else
    {
        // *** read sectors ***
        thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DRDCR01T, FALSE);     // set termination row
        rslen = qs2rs((BYTE)c2rw_dsc[c2rw_set.tp].maxqs) + 1;               // calculate card len (in sectors)
        for (rs = 0; rs < rslen; rs++)
        {
            thr_setpbar(hDlg, (DWORD)rs, (DWORD)rslen);                     // set progress bar

            if (((c2rw_set.flg & MASK_ALLDATA) == 0) &&
                (!get_rsstate(rs2trailer(rs)))) continue;                   // skip if sector disable
            rserr[rs] |= 2;                                                 // set "work" flag
            if (((err = thr_readrs(hDlg, rs)) != LERR_SUCCESS) && (err != LERR_DATAREAD)) goto thr_exit;
            if (err != LERR_SUCCESS) rserr[rs] |= 1;                        // set error flag
            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DRDCR01T, FALSE); // set termination row
        }
        // * test for error message *
        for (rs = 0; rs < rslen; rs++)
            if (rserr[rs] & 1) break;
        if (rs != rslen)        // if <errors>
        {
            BOOL        ff;     // first flag
            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DRDCR02S, FALSE); // "Error at sectors"
            for (ff = TRUE, rs = 0; rs < rslen; rs++)
            {
                if (rserr[rs] & 1)
                {
                    if (ff) { wsprintf(szLocal, TEXT(" %u"), rs); ff = FALSE; }
                    else wsprintf(szLocal, TEXT(", %u"), rs);
                    thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), szLocal);      // add message to log
                }
            }
            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DRDCR02E, FALSE); // add terminator
            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DRDCR04N, FALSE); // add "Operation failed" message
            err = LERR_DATAREAD;
        }
        else
        {   // if success
            BOOL        ff;     // first flag
            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DRDCR03S, FALSE); // "Reading sectors"
            for (ff = TRUE, rs = 0; rs < rslen; rs++)
            {
                if (rserr[rs] & 2)
                {
                    if (ff) { wsprintf(szLocal, TEXT(" %u"), rs); ff = FALSE; }
                    else wsprintf(szLocal, TEXT(", %u"), rs);
                    thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), szLocal);      // add message to log
                }
            }
            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DRDCR03E, FALSE); // add terminator
            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DRDCR04G, FALSE); // add "Operation complete" message
            err = LERR_SUCCESS;
        }
    }
thr_exit:
    thr_setpbar(hDlg, 10, 10);
    if (err != LERR_SUCCESS)
    {
        thr_verboseerr(err, szLocal);                           // verbose error code
        thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), &szLocal[1]);  // add error message to log
        gui_stbar(szLocal);                                     // add error message to status bar
    }
    else gui_ok2stbar();
    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);

    hThr = NULL;
    SendMessage(GetDlgItem(hDlg, IDE_LOGBOX), WM_KEYDOWN, VK_HOME, 0);
    SendMessage(GetDlgItem(hDlg, IDE_LOGBOX), WM_KEYDOWN, VK_END, 0);
    gui_disen(TRUE);
    return 0;
}

// write real sector fn
DWORD thr_writers(HWND hDlg, BYTE rs, BYTE rwf)
{
    BYTE        auth[40][7];
    DWORD       err = LERR_SUCCESS;
    TCHAR       szSample[128];
    TCHAR       szLocal[256];

    BYTE        sc_uidlen, sc_uid[SZ_UID];
    BOOL        keyAok = FALSE, keyBok = FALSE;
    DWORD       blk, sblk, tblk, bcnt;
    DWORD       authlen;
    DWORD       kidx, cnt;

    // * prepare writing *
    sc_uidlen = 0;                      // for any UID
    sblk = (DWORD)rs2sblk(rs);          // calculate start block for sector rs
    tblk = (DWORD)rs2trailer(rs);       // calculate trailer for sector rs
    // * build auth key dimension for sector *
    auth[0][0] = 0x00; memcpy(&auth[0][1], &c2rw_set.mfd_cl[((DWORD)tblk) << 4], 6);        // copy auth A data
    auth[1][0] = 0x80; memcpy(&auth[1][1], &c2rw_set.mfd_cl[(((DWORD)tblk) << 4) + 10], 6); // copy auth B data
    authlen = sizeof(auth) / sizeof(auth[0]);
    build_rsauth(rs, auth, &authlen);   // build authkey list
    // * sector processing *
    LoadString(hInst, STR_DWRRS01, szSample, sizeof(szSample) / sizeof(TCHAR));
    wsprintf(szLocal, szSample, rs);                    // build status log message
    thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), szLocal);  // add message to log

    for (kidx = 0, blk = sblk; blk <= tblk; blk++)      // reset key idx, launch block loop
    {
        for (bcnt = 0; bcnt < 4; bcnt++)                // break loop when first invalid bcnt or state
        {
            if ((blk + bcnt) > tblk) break;             // break if out of sector
            if ((!blkstate((BYTE)(blk + bcnt)))) break; // break if di state
        }
        if (!bcnt) continue;                            // skip if zero datalen
        // * block writing *
        for (; kidx < authlen;)                         // select auth key from list
        {
            BYTE        sc_acm;                         // auth command
            if (rwf) sc_acm = 0;                        // ignore auth for RW mode
            else
            {
                if (auth[kidx][0])  sc_acm = PICC_AUTHENT1B;    // auth B command
                else sc_acm = PICC_AUTHENT1A;                   // auth A command

                if ((kidx != 0) && (!memcmp(auth[kidx], auth[0], 7))) { kidx++;  continue; } // skip key if tested
                if ((kidx != 1) && (!memcmp(auth[kidx], auth[1], 7))) { kidx++;  continue; } // skip key if tested
            }
            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DWRRS02, FALSE);      // "Write"
            if (bcnt == 1)
            {
                wsprintf(szLocal, TEXT("S%u:%u / B%u"), blk2rs((BYTE)blk), blk - sblk, blk);
            }
            else
            {
                wsprintf(szLocal, TEXT("S%u:%u-%u / B%u-%u"),
                    blk2rs((BYTE)blk), blk - sblk, blk - sblk + bcnt - 1, blk, blk + bcnt - 1);
            }
            thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), szLocal);                  // add message to log

            if (sc_acm)
            {
                LoadString(hInst, STR_DWRRS02K, szSample, sizeof(szSample) / sizeof(TCHAR));
                wsprintf(szLocal, szSample, (auth[kidx][0]) ? 'B' : 'A');       // build status log message
                for (cnt = 0; cnt < 6; cnt++)                                   // build key string
                    wsprintf(&szLocal[lstrlen(szLocal)], TEXT("%02X"), auth[kidx][cnt + 1]);
                thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), szLocal);              // add message to log
            }
            else
            {
                thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DWRRS02R, FALSE); // "RW-mode"
            }
            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DWRRS02T, FALSE);     // "..."
            // *** set new authorization key ***
            if ((sc_acm) && ((err = mfSetAK(&ccr, 0, &auth[kidx][1])) != LERR_SUCCESS)) goto thr_exit;
            // data write; sc_uidlen = 0 for any uid read (in first read)
            err = mfDataWrite(&ccr, 7, ((sc_acm) ? MFDM_NMC : MFDM_RWC) | MFDM_BLOCK,
                sc_acm, 0, (BYTE)blk, (BYTE)bcnt, &c2rw_set.mfd_cl[blk << 4], &sc_uidlen, sc_uid, NULL, NULL);
            if ((err == LERR_FAIL) ||
                (err == LERR_HARDWARE) ||
                (err == LERR_TIMEOUT_TX) ||
                (err == LERR_TIMEOUT_RX) ||
                (err == LERR_NOTSYNC) ||
                (err == LERR_NOREADER)) goto thr_exit;
            else if (err != LERR_SUCCESS)
            {
                thr_verboseerr(err, szLocal);               // verbose error code
                lstrcat(szLocal, TEXT("\r\n"));             // add "next row" characters
                thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), &szLocal[1]);  // add error message to log
                if ((keyAok) && (keyBok)) goto thr_autherr; // stop key change, error!
                else kidx++;                                // change auth key
            }
            else // err == LERR_SUCCESS
            {
                blk += bcnt - 1;                            // increment blk counter and correct by -1
                thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DWRRS02G, FALSE); // "Ok."
                if (sc_acm)
                {
                    if (auth[kidx][0]) keyBok = TRUE;
                    else keyAok = TRUE;
                }
                break;
            }
            if (sc_acm == 0) goto thr_autherr;
        }
        if (kidx >= authlen) goto thr_autherr;
    }
    if (err == LERR_SUCCESS)
    {
        LoadString(hInst, STR_DWRRS03G, szSample, sizeof(szSample) / sizeof(TCHAR));
        wsprintf(szLocal, szSample, rs);                            // build status log message
        thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), szLocal);          // add message to log
        err = LERR_SUCCESS;
        goto thr_exit;
    }
thr_autherr:
    LoadString(hInst, STR_DWRRS03N, szSample, sizeof(szSample) / sizeof(TCHAR));
    wsprintf(szLocal, szSample, rs);                        // build log message
    thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), szLocal);      // add message to log
    err = LERR_DATAREAD;                                    // can't read data error
thr_exit:
    return err;
}

// write real sector
DWORD WINAPI t_wrrs(HWND hDlg)
{
    DWORD       err = LERR_SUCCESS;
    TCHAR       szSample[128];
    TCHAR       szLocal[256];
    BYTE        rs, btmp;
    BYTE        rwf = 0;

    rs = qs2rs(c2rw_set.qs);                // covert qs to real sector
    LoadString(hInst, STR_SWRRS, szSample, sizeof(szSample) / sizeof(TCHAR));
    wsprintf(szLocal, szSample, rs);        // build status bar message
    gui_stbar(szLocal);                     // set status message
    // *** initialization ***
    thr_setpbar(hDlg, 1, 10);
    if ((err = thr_ccrinit(hDlg)) != LERR_SUCCESS) goto thr_exit;
    // *** test for RW-card ***
    thr_setpbar(hDlg, 4, 10);
    if ((err = thr_rwtest(hDlg, &rwf, &btmp)) != LERR_SUCCESS) goto thr_exit;
    if (!btmp)
    {
        gui_res2stbar(STR_SNRWWARN);        // set new status
        link_close(&ccr);                   // close link
        hThr = NULL;                        // hide thread handle
        SendMessage(hDlg, WM_CLOSE, 0, 0);  // close dialog
        goto thr_out;
    }
    thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DWRRS01T, FALSE); // set terminator to log
    // *** read sector ***
    thr_setpbar(hDlg, 6, 10);
    if (((err = thr_writers(hDlg, rs, rwf)) != LERR_SUCCESS) && (err != LERR_DATAREAD)) goto thr_exit;
    thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), (err == LERR_SUCCESS) ? STR_DWRRS04G : STR_DWRRS04N, FALSE);  // "operation" message
    err = LERR_SUCCESS;

thr_exit:
    thr_setpbar(hDlg, 10, 10);
    if (err != LERR_SUCCESS)
    {
        thr_verboseerr(err, szLocal);                           // verbose error code
        thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), &szLocal[1]);  // add error message to log
        gui_stbar(szLocal);                                     // add error message to status bar
    }
    else gui_ok2stbar();
    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);

    hThr = NULL;
    SendMessage(GetDlgItem(hDlg, IDE_LOGBOX), WM_KEYDOWN, VK_HOME, 0);
    SendMessage(GetDlgItem(hDlg, IDE_LOGBOX), WM_KEYDOWN, VK_END, 0);
thr_out:
    gui_disen(TRUE);
    return 0;
}

// write card
DWORD WINAPI t_wrcard(HWND hDlg)
{
    DWORD       err = LERR_SUCCESS;
    TCHAR       szLocal[256];
    BYTE        rserr[40];
    BYTE        rs = 0, rslen, btmp, rwf = 0;

    memset(rserr, 0, sizeof(rserr));                // clear error flags
    gui_res2stbar(STR_SWRCARD);                     // set status message

    // test for "nothing to write"
    if (c2rw_dsc[c2rw_set.tp].tp == SCD_UL)
    {
        rslen = 16;                                 // 16 pages for UL-card
        for (rs = 0; rs < rslen; rs++)
            if (get_pgstate(rs)) break;             // check data to write
    }
    else
    {
        rslen = qs2rs((BYTE)c2rw_dsc[c2rw_set.tp].maxqs) + 1;           // calculate card len (in sectors)
        for (rs = 0; rs < rslen; rs++)
            if (get_rsstate(rs2trailer(rs))) break;                     // check data to write
    }
    if (rs == rslen)
    {
        gui_res2stbar(STR_SWRCARDN);                                    // status "Nothing to write!"
        LoadString(hInst, STR_SWRCARDN, szLocal, sizeof(szLocal) / sizeof(TCHAR));
        thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), &szLocal[1]);          // add message to log
        goto thr_exitwost;
    }

    // *** initialization ***
    if ((err = thr_ccrinit(hDlg)) != LERR_SUCCESS) goto thr_exit;
    // *** test for RW-card ***
    if ((err = thr_rwtest(hDlg, &rwf, &btmp)) != LERR_SUCCESS) goto thr_exit;
    if (!btmp)
    {
        gui_res2stbar(STR_SNRWWARN);        // set new status
        link_close(&ccr);                   // close link
        hThr = NULL;                        // hide thread handle
        SendMessage(hDlg, WM_CLOSE, 0, 0);  // close dialog
        goto thr_out;
    }
    if (c2rw_dsc[c2rw_set.tp].tp == SCD_UL)
    {
        BYTE        sc_uidlen = 0, sc_uid[SZ_UID];
        // *** write pages ***
        thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DWRPG01E, FALSE); // add "--" terminator
        for (rs = 0; rs < rslen; rs++)                                  // launch page loop
        {
            DWORD       bcnt;
            for (bcnt = 0; bcnt < (DWORD)rslen; bcnt++)                 // break loop when first invalid bcnt or state
            {
                if (((DWORD)rs + bcnt) >((DWORD)rslen - 1)) break;      // break if out of sector
                if (!get_pgstate(rs + (BYTE)bcnt)) break;               // break if di state
                rserr[(DWORD)rs + bcnt] |= 2;                           // set "work" flag
            }
            if (!bcnt) continue;                                        // skip if zero datalen

            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DWRPG01, FALSE);  // "Write"
            if (bcnt == 1) wsprintf(szLocal, TEXT("P%u"), (DWORD)rs);
            else wsprintf(szLocal, TEXT("P%u-%u"), rs, (DWORD)rs + bcnt - 1);
            thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), szLocal);              // add message to log
            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DWRPG01T, FALSE); // "..."

            thr_setpbar(hDlg, (DWORD)rs + bcnt, (DWORD)rslen);              // set progress bar
            // data write; sc_uidlen = 0 for any uid read
            err = mfDataWrite(&ccr, 7, ((rwf) ? MFDM_RWC : MFDM_NMC) | MFDM_PAGE,
                0, 0, (BYTE)rs, (BYTE)bcnt, &c2rw_set.mfd_ul[rs << 2], &sc_uidlen, sc_uid, NULL, NULL);
            gui_setdata();  // gui update
            if ((err == LERR_FAIL) ||
                (err == LERR_HARDWARE) ||
                (err == LERR_TIMEOUT_TX) ||
                (err == LERR_TIMEOUT_RX) ||
                (err == LERR_NOTSYNC) ||
                (err == LERR_NOREADER)) goto thr_exit;
            // *** verify UL data ***
            if (err == LERR_SUCCESS)
            {
                BYTE        buf[64];
                err = mfDataRead(&ccr, 7, ((rwf) ? MFDM_RWC : MFDM_NMC) | MFDM_PAGE,
                    0, 0, (BYTE)rs, (BYTE)bcnt, &buf[rs << 2], &sc_uidlen, sc_uid, NULL, NULL);
                if ((err == LERR_FAIL) ||
                    (err == LERR_HARDWARE) ||
                    (err == LERR_TIMEOUT_TX) ||
                    (err == LERR_TIMEOUT_RX) ||
                    (err == LERR_NOTSYNC) ||
                    (err == LERR_NOREADER)) goto thr_exit;
                if (memcmp(&c2rw_set.mfd_ul[rs << 2], &buf[rs << 2], bcnt * SZ_MFPAGE)) err = LERR_DATAWRITE;
            }

            if (err != LERR_SUCCESS)
            {
                DWORD       cnt;
                for (cnt = 0; cnt < bcnt; cnt++) rserr[rs + cnt] |= 1;  // set error flag
                thr_verboseerr(err, szLocal);                           // verbose error code
                lstrcat(szLocal, TEXT("\r\n"));                         // add "next row" characters
                thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), &szLocal[1]);  // add error message to log
                rs += (BYTE)bcnt - 1;   // increment blk counter and correct by -1
            }
            else // err == LERR_SUCCESS
            {
                thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DWRPG01G, FALSE); // "Ok"
                rs += (BYTE)bcnt - 1;   // increment blk counter and correct by -1
            }
        }
        thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DWRPG01E, FALSE); // add "--" terminator
        // * test for error message *
        for (rs = 0; rs < rslen; rs++)
            if (rserr[rs] & 1) break;
        if (rs != rslen)        // if <errors>
        {
            BOOL        ff;     // first flag
            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DWRPG02, FALSE);  // "Error writing pages"
            for (ff = TRUE, rs = 0; rs < rslen; rs++)
            {
                if (rserr[rs] & 1)
                {
                    if (ff) { wsprintf(szLocal, TEXT(" %u"), rs); ff = FALSE; }
                    else wsprintf(szLocal, TEXT(", %u"), rs);
                    thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), szLocal);      // add message to log
                }
            }
            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DWRPG02E, FALSE); // add terminator
            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DWRCR04N, FALSE); // add "Operation failed" message
            err = LERR_DATAREAD;
        }
        else
        {   // if success
            BOOL        ff;     // first flag
            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DWRPG03, FALSE);  // "Writing pages"
            for (ff = TRUE, rs = 0; rs < rslen; rs++)
            {
                if (rserr[rs] & 2)
                {
                    if (ff) { wsprintf(szLocal, TEXT(" %u"), rs); ff = FALSE; }
                    else wsprintf(szLocal, TEXT(", %u"), rs);
                    thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), szLocal);      // add message to log
                }
            }
            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DWRPG03E, FALSE); // add terminator
            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DWRCR04G, FALSE); // add "Operation complete" message
            err = LERR_SUCCESS;
        }
    }
    else
    {
        // *** write sectors ***
        thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DWRCR01T, FALSE);     // set termination row
        for (rs = 0; rs < rslen; rs++)
        {
            thr_setpbar(hDlg, (DWORD)rs, (DWORD)rslen);                     // set progress bar
            if ((!get_rsstate(rs2trailer(rs)))) continue;                   // skip if sector disable
            rserr[rs] |= 2;                                                 // set "work" flag
            if (((err = thr_writers(hDlg, rs, rwf)) != LERR_SUCCESS) && (err != LERR_DATAREAD)) goto thr_exit;
            if (err != LERR_SUCCESS) rserr[rs] |= 1;                        // set error flag
            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DWRCR01T, FALSE); // set termination row
        }
        // * test for error message *
        for (rs = 0; rs < rslen; rs++)
            if (rserr[rs] & 1) break;
        if (rs != rslen)        // if <errors>
        {
            BOOL        ff;     // first flag
            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DWRCR02S, FALSE); // "Error writing sector"
            for (ff = TRUE, rs = 0; rs < rslen; rs++)
            {
                if (rserr[rs] & 1)
                {
                    if (ff) { wsprintf(szLocal, TEXT(" %u"), rs); ff = FALSE; }
                    else wsprintf(szLocal, TEXT(", %u"), rs);
                    thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), szLocal);      // add message to log
                }
            }
            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DWRCR02E, FALSE); // add terminator
            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DWRCR04N, FALSE); // add "Operation failed" message
            err = LERR_DATAREAD;
        }
        else
        {   // if success
            BOOL        ff;     // first flag
            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DWRCR03S, FALSE); // "Reading sectors"
            for (ff = TRUE, rs = 0; rs < rslen; rs++)
            {
                if (rserr[rs] & 2)
                {
                    if (ff) { wsprintf(szLocal, TEXT(" %u"), rs); ff = FALSE; }
                    else wsprintf(szLocal, TEXT(", %u"), rs);
                    thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), szLocal);      // add message to log
                }
            }
            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DWRCR03E, FALSE); // add terminator
            thr_res2log(GetDlgItem(hDlg, IDE_LOGBOX), STR_DWRCR04G, FALSE); // add "Operation complete" message
            err = LERR_SUCCESS;
        }
    }

thr_exit:
    if (err != LERR_SUCCESS)
    {
        thr_verboseerr(err, szLocal);                           // verbose error code
        thr_logadd(GetDlgItem(hDlg, IDE_LOGBOX), &szLocal[1]);  // add error message to log
        gui_stbar(szLocal);                                     // add error message to status bar
    }
    else gui_ok2stbar();
thr_exitwost:
    thr_setpbar(hDlg, 10, 10);
    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);

    hThr = NULL;
    SendMessage(GetDlgItem(hDlg, IDE_LOGBOX), WM_KEYDOWN, VK_HOME, 0);
    SendMessage(GetDlgItem(hDlg, IDE_LOGBOX), WM_KEYDOWN, VK_END, 0);
thr_out:
    gui_disen(TRUE);
    return 0;
}
