//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "ProcessThread.h"
#include "Unit1.h"
#include "UserData.h"
#pragma package(smart_init)
//---------------------------------------------------------------------------

//   Important: Methods and properties of objects in VCL can only be
//   used in a method called using Synchronize, for example:
//
//      Synchronize(UpdateCaption);
//
//   where UpdateCaption could look like:
//
//      void __fastcall Unit2::UpdateCaption()
//      {
//        Form1->Caption = "Updated in a thread";
//      }
//---------------------------------------------------------------------------





__fastcall ProcessThread::ProcessThread(bool CreateSuspended)
        : TThread(CreateSuspended)
        , pSc(NULL)
        , iTerminateRequest(0)
        , bDllOpen(false)
{
    UpdateLinkStatus();
}


ProcessThread *_th = NULL;
int _iProgressDisplay = 1;
int _iSilentTransfer = 0;

unsigned long _srbytes = 0;             // for transfer speed
unsigned long _prev_callback_time = 0; // for transfer speed
char _strTransferSpeed[20];           // for transfer speed
void transfer_speed_init(void)
{
    _srbytes = 0;
    _prev_callback_time = GetTickCount();
    strcpy(_strTransferSpeed, "---");
    _th->UpdateTransferSpeed();
}

char sc_callback(char status, unsigned long adr, unsigned char* buffer, unsigned long buffpos, unsigned long bufflen, unsigned long exec_time)
{
    if(!_th) return SC_NOERROR;
    if(_th->iTerminateRequest) return SC_ERROR_ABORTED;

    if(_iProgressDisplay)
    {
        /*------------------------------------------------------------------------------------*/
        /* Set status bar base parameters. */
        Form1->pbTransfer->Min = 0;
        Form1->pbTransfer->Max = bufflen;
        /* Display transfer progress. */
        if(status == CLBK_START)
        {
            Form1->pbTransfer->Position = 0;
        }
        else if(status == CLBK_END)
        {
            Form1->pbTransfer->Position = 0;
        }
        else if((status == CLBK_SEND) || (status == CLBK_RECEIVE))
        {
            Form1->pbTransfer->Position = buffpos;
        }

        /*------------------------------------------------------------------------------------*/
        /* Display transfer speed. */
        if(status == CLBK_START)
        {
            scl_log(SCLT_HOSTPG, 5/*level*/, "--- Transfer startup.");
            _prev_callback_time = GetTickCount();
            _srbytes = 0;//buffpos;
        }
        else if(status == CLBK_END)
        {
            unsigned long dtime = exec_time;
            if(dtime >= 50)
            { /* Due to bad precision of windows timers, it doesn't makes sense to display transfer speed when transfer is very fast. */
                scl_log(SCLT_HOSTPG, 1/*level*/, "Transfer Time  = %d msec(s) (%d bytes).", dtime, buffpos);
                if(buffpos != 0)
                {
                    scl_log(SCLT_HOSTPG, 1/*level*/, "Average transmission speed = %f KB/s", ((float)buffpos)/dtime);
                    /* Display transfer speed in status bar. */
                    sprintf(_strTransferSpeed, "%6.2f KB/s", ((float)buffpos)/dtime); _th->UpdateTransferSpeed();
                }
            }
        }
        else if((status == CLBK_SEND) || (status == CLBK_RECEIVE))
        {
            unsigned long dtime = GetTickCount() - _prev_callback_time;
            if(dtime > 200)
            { /* Don't update more than five times per second */

                unsigned long dbytes = buffpos - _srbytes;
                if(dbytes <= 0) dbytes = 1;

                _prev_callback_time = GetTickCount();
                _srbytes = buffpos;

                const char* sr = (status == CLBK_SEND ? "Sending" : "Receiving");
                scl_log(SCLT_HOSTPG, 5/*level*/, "--- %s ... [%6.3f %%] T[%6.3f KB/s] I[%6.3f KB/s]", sr, 
                        ((float)(100*buffpos)) / ((bufflen <= 0 ? 1 : bufflen)), 
                        ((float)buffpos) / ((exec_time <= 20 ? 20 : exec_time)), 
                        ((float)dbytes) / ((dtime <= 20 ? 20 : dtime)));
                /* Display transfer speed in status bar. */
                sprintf(_strTransferSpeed, "[%6.2f KB/s]", ((float)dbytes) / ((dtime <= 0 ? 1 : dtime))); _th->UpdateTransferSpeed();
            }
        }
    } // if(_iProgressDisplay)

    return SC_NOERROR;
}

void error_print(SatCom* pSc)
{
    if(!_th) return;

    scl_log(SCLT_HOSTPG, 0/*level*/, " *** Error ! (Code = %d, Message = \"%s\")", pSc->errCode, pSc->GetLastErrMsg());
    /* Reset error */
    pSc->errCode = SC_NOERROR;
}


/* Callback used when receiving log messages for display on main log window. */
char scl_callback_main(unsigned char logtype, unsigned char loglevel, unsigned char prompt_on, unsigned char log_ofs, char* logmessage)
{
    if(!_th) return SC_NOERROR;

    _th->LogOut("%s", logmessage);

    return SC_NOERROR;
}
/* Callback used when receiving log messages for display on SatCom console log window. */
char scl_callback_dbgconsole(unsigned char logtype, unsigned char loglevel, unsigned char prompt_on, unsigned char log_ofs, char* logmessage)
{
    if(!_th) return SC_NOERROR;

    _th->DbgOut("ID%03d>\"%s\"", loglevel, logmessage+log_ofs);

    if(prompt_on != 0)
    {
        /* Turn on prompt indicator. */
        _th->UpdatePromptStatus(true/*bPromptOn*/);
        _th->LogOut("[PROMPT] Waiting for answer ...");
        _th->DbgOut("[PROMPT] Waiting for answer ...");
    }

    return SC_NOERROR;
}
/* Callback used when receiving log messages for display on SatCom log grid. */
char scl_callback_dbggrid(unsigned char logtype, unsigned char loglevel, unsigned char prompt_on, unsigned char log_ofs, char* logmessage)
{
    if(!_th) return SC_NOERROR;

    _th->DbgGridOut((loglevel == 0 ? 1 : loglevel)/*msg_id*/, "%s", logmessage+log_ofs);

    if(prompt_on != 0)
    {
        /* Turn on prompt indicator. */
        _th->UpdatePromptStatus(true/*bPromptOn*/);
        _th->LogOut("[PROMPT] Waiting for answer ...");
        _th->DbgOut("[PROMPT] Waiting for answer ...");
    }

    return SC_NOERROR;
}


//---------------------------------------------------------------------------
void __fastcall ProcessThread::Execute()
{
    srand(time(NULL));

    /* Initialize pointer to this thread (used for external functions) */
    _th = this;

    /* Init SatCom class with a dummy device. */
    pSc = new SatCom(); UpdateDeviceVersion();

    /* Init stuff for VDP1 viewer. */
    vdp1_view_init();

    /* Log messages that will be print in the main log window. */
    pSc->SetLogCallback(0, scl_callback_main);
    /* Log messages that will be print in the console debugger window. */
    pSc->SetLogCallback(1, scl_callback_dbgconsole);
    /* Log messages that will be print in the grid debugger window. */
    pSc->SetLogCallback(2, scl_callback_dbggrid);


    /* Update transfer status in status bar. */
    UpdateTransferStatus(false/*bTransferOn*/);

    /* Init transfer speed indicator. */
    transfer_speed_init();


    /* Init button/checkboxes/etc states from GUI. */
    GetUserData();
    RefreshMemViewData();
    SetDebugFolder(); /* Set SatCom's log folder. */
    UpdateSilentLogStatus(1); /* Silent log by default. */
    ptrDebuggerActivity = NULL;

    /* Display hello screen. */
    print_help(Form1->app_name);

    /* Parse command line parameters */
    if(Form1->argc >= 2)
    {
        scl_log(SCLT_HOSTPG, 0/*level*/, "[PT]Parse command line ...");
        ParseCommandLine();
        if(Terminated) goto thread_end;
        scl_log(SCLT_HOSTPG, 0/*level*/, "[PT]Parse command line end.");
        scl_log(SCLT_HOSTPG, 0/*level*/, "");
    }


    /* Start debugger */
    while(1)
    {
        /* Get button/checkboxes/etc states from GUI. */
        GetUserData();


        /*----------------------------*/
        /*--- Debugger stuff start ---*/
        if((udGUI.dbg_use) && (bDllOpen))
        {// scl_log(SCLT_HOSTPG, 7/*level*/, "[PT]Start poll debugger data.");
            SC_GUI_Options(0/*bProgressUse*/, 1/*bDebuggerUse*/);
            UpdateDebuggerStatus(true/*bDebuggerOn*/);

            /* Poll debug data. */
            SetCurrentDirectory(app_path);
            pSc->DbgPoll();
            SC_GUI_Options(1/*bProgressUse*/, 0/*bDebuggerUse*/);
            if(pSc->errCode != SC_NOERROR)
            {
                error_print(pSc);
            }

            /* Display debugger activity. */
            UpdateDebuggerActivity(pSc->DbgGetInfos());


        }
        else
        {
            UpdateDebuggerStatus(false/*bDebuggerOn*/);
        }

        /* Set debug folder. */
        if(udGUI.btn[BTN_DBGFOLDER])
        { scl_log(SCLT_HOSTPG, 7/*level*/, "[PT]Set debug folder.");

            SetDebugFolder();

            /* Clear request. */
            ClearButtonState(BTN_DBGFOLDER);
        }
        /* Send prompt's answer. */
        if(udGUI.btn[BTN_PROMPTSEND])
        { scl_log(SCLT_HOSTPG, 7/*level*/, "[PT]Send prompt answer.");
            SC_GUI_Options(0/*bProgressUse*/, 1/*bDebuggerUse*/);
            pSc->DbgSendPromptAnswer(udGUI.prompt_answer);
            SC_GUI_Options(1/*bProgressUse*/, 0/*bDebuggerUse*/);
            if(pSc->errCode != SC_NOERROR)
            {
                error_print(pSc);
            }
            else
            {
                scl_log(SCLT_HOSTPG, 0/*level*/, "[PROMPT] Sent answer=\"%s\"", udGUI.prompt_answer);
                DbgOut("[PROMPT] Sent answer=\"%s\"", udGUI.prompt_answer);
                /* Turn off prompt indicator. */
                UpdatePromptStatus(false/*bPromptOn*/);
            }

            /* Clear request. */
            ClearButtonState(BTN_PROMPTSEND);
        }

        /* Clear debugger log. */
        if(Terminated) goto thread_end;
        if(udGUI.btn[BTN_LOGCLEAR])
        { scl_log(SCLT_HOSTPG, 7/*level*/, "[PT]Clear debugger log.");
            /* Erase console log. */
            Form1->mDbgText->Lines->Clear();
            /* Erase grid log. */
            for(int i=1; i<256;  i++) DbgGridOut(i, "");
            /* Clear request. */
            ClearButtonState(BTN_LOGCLEAR);
        }
        /* Save Saturn debugger log. */
        if(udGUI.btn[BTN_LOGSAVE])
        { scl_log(SCLT_HOSTPG, 7/*level*/, "[PT]Save debugger log.");
            SetCurrentDirectory(app_path);
            FILE* f=fopen(udGUI.log_file, "wb");
            if(f)
            {
                fprintf(f, "%s\r\n", Form1->mDbgText->Lines->Text.c_str());
                fclose(f);// f=NULL;
                scl_log(SCLT_HOSTPG, 0/*level*/, "Saved %d lines from log to disk.", Form1->mDbgText->Lines->Count);
            }
            /* Clear request. */
            ClearButtonState(BTN_LOGSAVE);
        }
        /* Open log settings. */
        if(udGUI.btn[BTN_LOGLDSETTINGS])
        { scl_log(SCLT_HOSTPG, 7/*level*/, "[PT]Load log settings.");
            SetCurrentDirectory(app_path);
            scl_read_ini(udGUI.log_settings, "satlink_");
            UpdateSilentLogStatus(1); /* Silent log by default. */
            scl_log(SCLT_HOSTPG, 0/*level*/, "Read log settings from %s file.", udGUI.log_settings);
            /* Clear request. */
            ClearButtonState(BTN_LOGLDSETTINGS);
        }
        /*--- Debugger stuff end ---*/
        /*--------------------------*/


        /*-----------------------------*/
        /* Memory View write request ? */
        if(Terminated) goto thread_end;
        if(udGUI.btn[BTN_MVWRITE])
        { scl_log(SCLT_HOSTPG, 7/*level*/, "[PT]Write MemView data.");
            SC_GUI_Options(0/*bProgressUse*/, 0/*bDebuggerUse*/);

            /* Get data from Form1. */
            GetMemViewData();
            /* Send data to Saturn. */
            SC_Send((char*)(udGUI.mv_data), MEMVIEW_DATASIZE, udGUI.mv_address, SC_LOAD);

            /* Clear request. */
            ClearButtonState(BTN_MVWRITE);
            /* Read data from Saturn in order to see what has been written. */
            udGUI.btn[BTN_MVREAD] = 1;

            SC_GUI_Options(1/*bProgressUse*/, 0/*bDebuggerUse*/);
        }
        /* Memory View address Up/Down ? */
        if(udGUI.btn[BTN_ADDRDOWN])
        { scl_log(SCLT_HOSTPG, 7/*level*/, "[PT]Decrement MemView address.");
            udGUI.mv_address -= MEMVIEW_DATASIZE;
            ClearButtonState(BTN_ADDRDOWN);
            udGUI.btn[BTN_MVREAD] = 1;
        }
        if(udGUI.btn[BTN_ADDRUP])
        { scl_log(SCLT_HOSTPG, 7/*level*/, "[PT]Increment MemView address.");
            udGUI.mv_address += MEMVIEW_DATASIZE;
            ClearButtonState(BTN_ADDRUP);
            udGUI.btn[BTN_MVREAD] = 1;
        }
        /* Memory View read request ? */
        if(Terminated) goto thread_end;
        if((udGUI.btn[BTN_MVREAD]) || (udGUI.mv_autoread))
        { scl_log(SCLT_HOSTPG, 7/*level*/, "[PT]Read MemView data.");
            SC_GUI_Options(0/*bProgressUse*/, 0/*bDebuggerUse*/);

            /* Read data from Saturn. */
            SC_Receive(udGUI.mv_address, MEMVIEW_DATASIZE, (char*)(udGUI.mv_data));
            /* Send data to Form1. */
            RefreshMemViewData();

            /* Clear request. */
            ClearButtonState(BTN_MVREAD);

            SC_GUI_Options(1/*bProgressUse*/, 0/*bDebuggerUse*/);
        }
        /*-----------------------------*/


        /*-----------------------------*/
        /* Clear main log. */
        if(Terminated) goto thread_end;
        if(udGUI.btn[BTN_MAINLOGCLEAR])
        { scl_log(SCLT_HOSTPG, 7/*level*/, "[PT]Clear main log.");
            /* Erase console log. */
            Form1->mLog->Lines->Clear();
            /* Clear request. */
            ClearButtonState(BTN_MAINLOGCLEAR);
        }
        /* Save main log. */
        if(udGUI.btn[BTN_MAINLOGSAVE])
        { scl_log(SCLT_HOSTPG, 7/*level*/, "[PT]Save Main log.");
            SetCurrentDirectory(app_path);
            FILE* f=fopen(udGUI.main_log_file, "wb");
            if(f)
            {
                fprintf(f, "%s\r\n", Form1->mLog->Lines->Text.c_str());
                fclose(f);// f=NULL;
                scl_log(SCLT_HOSTPG, 0/*level*/, "Saved %d lines from main log to disk.", Form1->mLog->Lines->Count);
            }
            /* Clear request. */
            ClearButtonState(BTN_MAINLOGSAVE);
        }
        /* Adjust log level. */
        if(udGUI.btn[BTN_LOGLEVELSET])
        { scl_log(SCLT_HOSTPG, 7/*level*/, "[PT]Adjust log level.");
            unsigned char log_type;
            switch(udGUI.log_type)
            {
            default:
            case(0): // DLL
                log_type = SCLT_DLLPG;
                break;
            case(1): // Wrapper
                log_type = SCLT_DLLWRP;
                break;
            case(2): // Host program
                log_type = SCLT_HOSTPG;
                break;
            case(3): // Saturn debug console
                log_type = SCLT_SATMSG;
                break;
            case(4): // Saturn debug grid
                log_type = SCLT_SATGRD;
                break;
            }
            unsigned char log_output;
            switch(udGUI.log_output)
            {
            case(0): // DbgView
                log_output = SCLO_DBGVIEW;
                break;
            case(1): // File
                log_output = SCLO_FILE;
                break;
            default:
            case(2): // Main log
                log_output = SCLO_CBCK1;
                break;
            case(3): // Sub Log
                log_output = SCLO_CBCK2;
                break;
            case(4): // Grid Log
                log_output = SCLO_CBCK3;
                break;
            }
            unsigned char log_level;
            switch(udGUI.log_level)
            {
            case(-1):
            case(0): // OFF
                log_level = 0xF;
                break;
            default:
                log_level = (udGUI.log_level - 1)%10;
            }

            scl_log(SCLT_HOSTPG, 0/*level*/, "Adjust log : type=%d, out=%d, lvl=%d", log_type, log_output, log_level);
            pSc->AdjustLogLevel(log_type, log_output, log_level);

            /* Clear request. */
            ClearButtonState(BTN_LOGLEVELSET);
        }

        /* Turn silent log ON. */
        if(Terminated) goto thread_end;
        if(udGUI.btn[BTN_SILENTON])
        { scl_log(SCLT_HOSTPG, 1/*level*/, "[PT]Turn silent log ON.");

            UpdateSilentLogStatus(1);

            /* Clear request. */
            ClearButtonState(BTN_SILENTON);
        }
        /* Turn silent log OFF. */
        if(Terminated) goto thread_end;
        if(udGUI.btn[BTN_SILENTOFF])
        { scl_log(SCLT_HOSTPG, 1/*level*/, "[PT]Turn silent log OFF.");

            UpdateSilentLogStatus(0);

            /* Clear request. */
            ClearButtonState(BTN_SILENTOFF);
        }
        /*-----------------------------*/


        /*---------------------*/
        /* Extra stuff.        */
        /* Get target informations. */
        if(Terminated) goto thread_end;
        if(udGUI.btn[BTN_TARGETINFO])
        { scl_log(SCLT_HOSTPG, 7/*level*/, "[PT]Get target informations.");
            SC_GUI_Options(0/*bProgressUse*/, 0/*bDebuggerUse*/);

            target_info();

            /* Clear request. */
            ClearButtonState(BTN_TARGETINFO);

            SC_GUI_Options(1/*bProgressUse*/, 0/*bDebuggerUse*/);
        }
        /* Get VDP1 snapshot. */
        if(Terminated) goto thread_end;
        if(udGUI.btn[BTN_VDP1VIEW])
        { scl_log(SCLT_HOSTPG, 7/*level*/, "[PT]Get VDP1 snapshot.");
            //SC_GUI_Options(0/*bProgressUse*/, 0/*bDebuggerUse*/);

            vdp1_view();

            /* Clear request. */
            ClearButtonState(BTN_VDP1VIEW);

            //SC_GUI_Options(1/*bProgressUse*/, 0/*bDebuggerUse*/);
        }
        /*-----------------------------*/



        /* TEST1 */
        if(Terminated) goto thread_end;
        if(udGUI.btn[BTN_TEST1])
        { scl_log(SCLT_HOSTPG, 7/*level*/, "[PT]TEST1 button.");
            /* Perform action. */
#if 0
            //unsigned long diff = (unsigned long)(&(scdData.structsz)) - (unsigned long)(&scdData);
            //scl_log(SCLT_HOSTPG, 0/*level*/, "[PT]TEST1 (%d) !", diff);
            //scl_log(SCLT_HOSTPG, 0/*level*/, ""); 

            /* Test debugger log output. */
            DbgOut("DBG TEST %d %d", 42,53);

            /* Send dummy data to memview. */
            int i;
            for(i=0; i<MEMVIEW_DATASIZE; i+=2)
            {
                udGUI.mv_data[i+0] = 0xFF;
                udGUI.mv_data[i+1] = 'A' + rand()%26;
            }
            /* Send data to Form1. */
            RefreshMemViewData();
#else

            scl_log(SCLT_HOSTPG, 0/*level*/, "[TEST]log_silent = %d, _iProgressDisplay = %d, _iSilentTransfer = %d", udGUI.log_silent, _iProgressDisplay, _iSilentTransfer);

            scl_log(SCLT_HOSTPG, 0/*level*/, "[TEST]HOSTPG0.");
            scl_log(SCLT_HOSTPG, 3/*level*/, "[TEST]HOSTPG3.");

            scl_log(SCLT_SATMSG, 0/*level*/, "[TEST]SATMSG0.");
            scl_log(SCLT_SATMSG, 3/*level*/, "[TEST]SATMSG3.");

            scl_log(SCLT_SATGRD, 0/*level*/, "[TEST]SATGRD0.");
            scl_log(SCLT_SATGRD, 3/*level*/, "[TEST]SATGRD3.");

            scl_log(SCLT_DLLWRP, 0/*level*/, "[TEST]DLLWRP0.");
            scl_log(SCLT_DLLWRP, 3/*level*/, "[TEST]DLLWRP3.");



#endif

            /* Clear request. */
            ClearButtonState(BTN_TEST1);
        }


        /* Read/Write/Exec from GUI needed ? */
        /* Note : in the exec case, add a 10sec wait loop in order to skip startup logo. */


        /* Send data to Saturn ? */
        if(Terminated) goto thread_end;
        if((udGUI.btn[BTN_SEND]) || (udGUI.btn[BTN_FASTEXEC]))
        { scl_log(SCLT_HOSTPG, 7/*level*/, "[PT]Send data to Saturn.");
            unsigned long address;
            unsigned char exec_flag;
            char* up_file;
            if(udGUI.btn[BTN_SEND])
            {
                address   = udGUI.address;
                exec_flag = udGUI.exec_flag;
                up_file   = udGUI.up_file;
            }
            else //(udGUI.btn[BTN_FASTEXEC])
            {
                address   = udGUI.dbg_address;
                exec_flag = 1;
                up_file   = udGUI.dbg_file;
                /* Erase grid log. */
                for(int i=1; i<256;  i++) DbgGridOut(i, "");
            }


            ///* Reset and turn off debugger. */
            if(exec_flag) ResetDebugger();

            /* Perform action. */
            scl_log(SCLT_HOSTPG, 0/*level*/, "[PT]SC->SEND (%s, [0x%08X], f%d)", 
                        up_file, address, exec_flag);

            SendData(up_file, address, (exec_flag ? SC_EXEC : SC_LOAD));
            scl_log(SCLT_HOSTPG, 0/*level*/, "[PT]SC->SEND ended"); 
            scl_log(SCLT_HOSTPG, 0/*level*/, ""); 

            /* Clear requests. */
            ClearButtonState(BTN_SEND);
            ClearButtonState(BTN_FASTEXEC);

            ///* Reset and turn off debugger. */
            if(exec_flag) ResetDebugger();
        }
        /* Receive data from Saturn ? */
        if(Terminated) goto thread_end;
        if(udGUI.btn[BTN_RECEIVE])
        { scl_log(SCLT_HOSTPG, 7/*level*/, "[PT]Receive data from Saturn.");
            /* Perform action. */
            scl_log(SCLT_HOSTPG, 0/*level*/, "[PT]SC->RECEIVE (%s, [0x%08X, %d %s])", 
                        udGUI.down_file, udGUI.address, udGUI.length, (udGUI.length_is_KB ? "KB" : "B"));
            ReceiveData(udGUI.down_file, udGUI.address, udGUI.length * (udGUI.length_is_KB ? 1024 : 1));
            scl_log(SCLT_HOSTPG, 0/*level*/, "[PT]SC->RECEIVE ended"); 
            scl_log(SCLT_HOSTPG, 0/*level*/, ""); 

            /* Clear request. */
            ClearButtonState(BTN_RECEIVE);
        }


        /* Process ini file ? */
        if(Terminated) goto thread_end;
        if(udGUI.btn[BTN_OPEN])
        { scl_log(SCLT_HOSTPG, 7/*level*/, "[PT]Open ini settings file.");
            /* Retrieve parameters from ini file, then execute commands. */
            scl_log(SCLT_HOSTPG, 0/*level*/, "[PT]SC->OPEN (%s)", udGUI.ini_file);
            Form1->argc = 1;
            parse_satlink_inifile(Form1->inifilebuffer, udGUI.ini_file/*filename*/, &(Form1->argc), Form1->argv);
            ParseCommandLine();
            scl_log(SCLT_HOSTPG, 0/*level*/, "[PT]SC->OPEN ended");
            scl_log(SCLT_HOSTPG, 0/*level*/, ""); 

            /* Clear request. */
            ClearButtonState(BTN_OPEN);
        }
        /* Set parameter to SatCom ? */
        if(Terminated) goto thread_end;
        if(udGUI.btn[BTN_PARAMSET])
        { scl_log(SCLT_HOSTPG, 7/*level*/, "[PT]Set parameter to SatCom.");
            /* Perform action. */
            scl_log(SCLT_HOSTPG, 0/*level*/, "[PT]SC->SET (%s, %s)", udGUI.param_text, udGUI.param_value);
            SC_Set(udGUI.param_text, udGUI.param_value);
            scl_log(SCLT_HOSTPG, 0/*level*/, ""); 

            /* Clear request. */
            ClearButtonState(BTN_PARAMSET);
        }
        /* Close connection ? */
        if(Terminated) goto thread_end;
        if(udGUI.btn[BTN_CLOSE])
        { scl_log(SCLT_HOSTPG, 7/*level*/, "[PT]Close connection.");
            scl_log(SCLT_HOSTPG, 0/*level*/, "[PT]SC->CLOSE");
            bDllOpen = false;
            pSc->End(); delete pSc; pSc = new SatCom(); UpdateDeviceVersion();
            scl_log(SCLT_HOSTPG, 0/*level*/, "[PT]SC->CLOSE ended");
            scl_log(SCLT_HOSTPG, 0/*level*/, ""); 

            /* Clear request. */
            ClearButtonState(BTN_CLOSE);
        }



        /*-----------------------*/
        /* Menu bar stuff start. */

        /* Set USB Data Link parameters ? */
        // MNI_SETUDLPARAMS -> See unit1.cpp (problems when using InputQuery function from thread)

        /* Open USB Data Link ini file ? */
        if(Terminated) goto thread_end;
        if(udGUI.btn[MNI_OPENUDL])
        { scl_log(SCLT_HOSTPG, 7/*level*/, "[PT]Open USB Data Link ini settings file.");
            /* Retrieve parameters from ini file, then execute commands. */
            scl_log(SCLT_HOSTPG, 0/*level*/, "[PT]SC->OPEN USBDL");
            Form1->argc = 1;
            parse_satlink_inifile(Form1->inifilebuffer, "usbdl.ini"/*filename*/, &(Form1->argc), Form1->argv);
            ParseCommandLine();
            scl_log(SCLT_HOSTPG, 0/*level*/, "[PT]SC->OPEN ended");
            scl_log(SCLT_HOSTPG, 0/*level*/, ""); 

            /* Clear request. */
            ClearButtonState(MNI_OPENUDL);
        }

        /* Send backup memory to Saturn ? */
        if(Terminated) goto thread_end;
        if(udGUI.btn[MNI_SENDBRAM])
        { scl_log(SCLT_HOSTPG, 7/*level*/, "[PT]Send backup memory to Saturn.");
            /* Perform action. */
            SetCurrentDirectory(app_path);
            Form1->odFile->Filter = "Binary File (*.bin)|*.bin|Any File (*.*)|*.*";
            if(Form1->odFile->Execute() == true)
            {
                AnsiString str = Form1->odFile->FileName;
                SendData(str.c_str(), 0x00180000, SC_LOAD);
            }
            /* Clear request. */
            ClearButtonState(MNI_SENDBRAM);
        }

        /* Execute program to Saturn ? */
        if(Terminated) goto thread_end;
        if(udGUI.btn[MNI_EXECPROG])
        { scl_log(SCLT_HOSTPG, 7/*level*/, "[PT]Execute program to Saturn (0x06004000).");
            /* Perform action. */
            SetCurrentDirectory(app_path);
            Form1->odFile->Filter = "Binary File (*.bin)|*.bin|Any File (*.*)|*.*";
            if(Form1->odFile->Execute() == true)
            {
                AnsiString str = Form1->odFile->FileName;
                SendData(str.c_str(), 0x06004000, SC_EXEC);
            }
            /* Clear request. */
            ClearButtonState(MNI_EXECPROG);
        }

        /* Receive backup memory from Saturn ? */
        if(Terminated) goto thread_end;
        if(udGUI.btn[MNI_RCVBRAM])
        { scl_log(SCLT_HOSTPG, 7/*level*/, "[PT]Receive backup memory from Saturn.");
            /* Perform action. */
            SetCurrentDirectory(app_path);
            Form1->sdFile->Filter = "Binary File (*.bin)|*.bin|Any File (*.*)|*.*";
            if(Form1->sdFile->Execute() == true)
            {
                AnsiString str = Form1->sdFile->FileName;
                ReceiveData(str.c_str(), 0x00180000, 64*1024);
            }
            /* Clear request. */
            ClearButtonState(MNI_RCVBRAM);
        }

        /* Receive BIOS from Saturn ? */
        if(Terminated) goto thread_end;
        if(udGUI.btn[MNI_RCVBIOS])
        { scl_log(SCLT_HOSTPG, 7/*level*/, "[PT]Receive BIOS from Saturn.");
            /* Perform action. */
            SetCurrentDirectory(app_path);
            Form1->sdFile->Filter = "Binary File (*.bin)|*.bin|Any File (*.*)|*.*";
            if(Form1->sdFile->Execute() == true)
            {
                AnsiString str = Form1->sdFile->FileName;
                ReceiveData(str.c_str(), 0x00000000, 512*1024);
            }
            /* Clear request. */
            ClearButtonState(MNI_RCVBIOS);
        }

        /* Receive ARP FirmWare from Saturn ? */
        if(Terminated) goto thread_end;
        if(udGUI.btn[MNI_RCVARPFW])
        { scl_log(SCLT_HOSTPG, 7/*level*/, "[PT]Receive ARP FirmWare from Saturn.");
            /* Perform action. */
            SetCurrentDirectory(app_path);
            Form1->sdFile->Filter = "Binary File (*.bin)|*.bin|Any File (*.*)|*.*";
            if(Form1->sdFile->Execute() == true)
            {
                AnsiString str = Form1->sdFile->FileName;
                ReceiveData(str.c_str(), 0x02000000, 256*1024);
            }
            /* Clear request. */
            ClearButtonState(MNI_RCVARPFW);
        }

        /* Menu bar stuff end.   */
        /*-----------------------*/


        if(Terminated) goto thread_end;
        { /* Polling sleep. */
            unsigned long dbg_sleep = udGUI.dbg_sleep;
            if(dbg_sleep > 1000) dbg_sleep = 1000;
            if(dbg_sleep != 0) Sleep(dbg_sleep);
        }
        if(Terminated) goto thread_end;
    }

thread_end:
    scl_log(SCLT_HOSTPG, 0/*level*/, "[PT]Thread end");
    /* Delete SatCom object. */
    pSc->End(); delete pSc; pSc = new SatCom(); //UpdateDeviceVersion();
    /* Free VDP1 viewer stuff. */
    vdp1_view_end();
    /* Reset the global pointer to this thread. */
    _th = NULL;
}





// Get a snapshot of the VDP1 framebuffer
// Taken from vdp1view at the following address :
// http://cgfm2.emuviews.com/saturn.php
void putpixel(unsigned long* bmp, unsigned short width, unsigned short height, unsigned short x, unsigned short y, unsigned long color)
{
    bmp[(y % height)*width + (x % width)] = color;
}
void bswap(unsigned char *mem, int length)
{
    int i;
    for(i = 0; i < length; i += 2)
    {
        unsigned char temp = mem[i+0];
        mem[i+0] = mem[i+1];
        mem[i+1] = temp;
    }
}
char          _vdp1_name[0x30];
unsigned char _vdp1_frame[0x40000];
unsigned char _vdp1_cram[0x1000];
unsigned long _vdp1_palette[0x800];
unsigned long _vdp1_bmp[512*256];
unsigned long _vdp1_tmp[352*240];
unsigned long _vdp1_width2;
unsigned long _vdp1_height2;
void ProcessThread::vdp1_view_init(void)
{
    pBmpVDP1 = new Graphics::TBitmap;
    pBmpVDP1->PixelFormat = pf32bit;
    pBmpVDP1->Width  = Form1->imgVDP1->Width;
    pBmpVDP1->Height = Form1->imgVDP1->Height;
}
void ProcessThread::vdp1_view_end(void)
{
    delete pBmpVDP1;
    pBmpVDP1 = NULL;
}
void ProcessThread::vdp1_view(void)
{
    int clip = 0;
    int i, x, y, len;

    /* Read data from Saturn. */
    scl_log(SCLT_HOSTPG, 0/*level*/, "Reading program name, please wait...");
    SC_Receive(0x26002060, sizeof(_vdp1_name), (char*)_vdp1_name);

    scl_log(SCLT_HOSTPG, 0/*level*/, "Reading VDP color ram, please wait...");
    SC_Receive(0x25F00000, sizeof(_vdp1_cram), (char*)_vdp1_cram);

    scl_log(SCLT_HOSTPG, 0/*level*/, "Reading VDP frame, please wait...");
    SC_Receive(0x25C80000, sizeof(_vdp1_frame), (char*)_vdp1_frame);

    bswap(_vdp1_cram, sizeof(_vdp1_cram));
    len = sizeof(_vdp1_cram) / sizeof(unsigned short);
    for(i = 0; i < len; i++)
    {
        unsigned short c = *(unsigned short *)&_vdp1_cram[i << 1];
        int r = (c >>  0) & 0x1F;
        int g = (c >>  5) & 0x1F;
        int b = (c >> 10) & 0x1F;
        _vdp1_palette[i] = RGB24(r<<3,g<<3,b<<3);
    }

    /* Set up video display */
    //_vdp1_bmp = create_bitmap(512, 256);
    int width1, height1;
    width1 = 512, height1 = 256;


    /* Write out framebuffer data */
    bswap(_vdp1_frame, sizeof(_vdp1_frame));
    for(y = 0; y < height1; y++)
    for(x = 0; x < width1; x++)
    {
        unsigned short d = *(unsigned short *)&_vdp1_frame[(y*1024)+(x*2)];

        if(d & 0x8000)
        {
            int r = (d >>  0) & 0x1F;
            int g = (d >>  5) & 0x1F;
            int b = (d >> 10) & 0x1F;
            putpixel(_vdp1_bmp, width1, height1, x, y, RGB24(r<<3,g<<3,b<<3));
        }
        else
        {
            putpixel(_vdp1_bmp, width1, height1, x, y, _vdp1_palette[d & 0x07FF]);
        }

        /* Fill VDP2 graphics area with bright pink */
        if(d == 0x0000)
        {
            putpixel(_vdp1_bmp, width1, height1, x, y, RGB24(0xFF, 0x00, 0xFF));
        }

        /* Check for unused memory */
        if((d == 0xAAAA) || (d == 0x5555))
        {
            if((x >= 320) && (x < 352) && (y < 224))
                clip |= 1;
            if((y >= 224) && (y < 240) && (x < 320))
                clip |= 2;
        } 
    }

    /* Determine size of display area */
    _vdp1_width2 = (clip & 1) ? 320 : 352;
    _vdp1_height2 = (clip & 2) ? 224 : 240;

    /* Make BMP snapshot */
    //_vdp1_tmp = create_bitmap(width, height);
    //blit(_vdp1_bmp, _vdp1_tmp, 0, 0, 0, 0, width, height);
    {
        for(y = 0; y < _vdp1_height2; y++)
        for(x = 0; x < _vdp1_width2; x++)
        {
            _vdp1_tmp[y*_vdp1_width2 + x] = _vdp1_bmp[y*width1 + x];
        }
    }
    //get_palette(pal);

    /* Use cleaned game title as filename. */
    unsigned char conv_tbl[256] =
    {
        '\0','_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_',
        '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_',
        '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '-', '.', '_',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '_', '_', '_', '_', '_', '_',
        '_', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
        'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '_', '_', '_', '_', '_',
        '_', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
        'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '_', '_', '_', '_', '_',

        '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_',
        '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_',
        '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_',
        '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_',
        '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_',
        '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_',
        '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_',
        '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_'
    };
    for(i=0; i<sizeof(_vdp1_name); i++)
    {
        _vdp1_name[i] = conv_tbl[_vdp1_name[i]];
    }

    i = sizeof(_vdp1_name)-1 - 4; _vdp1_name[i] = '\0'; i--;
    /* Remove last useless characters. */
    while(_vdp1_name[i] == '_')
    {
        _vdp1_name[i] = '\0';
        i--;
        if(i <= 1) break;
    }
    strcat(_vdp1_name, ".bmp");


    scl_log(SCLT_HOSTPG, 0/*level*/, "Target filename = \"%s\"", _vdp1_name);
    save_raw_to_bitmap(_vdp1_tmp, _vdp1_width2, _vdp1_height2, _vdp1_name);
    Synchronize(DisplayVDP1_Internal);
}
void __fastcall ProcessThread::DisplayVDP1_Internal(void)
{
    int i, j, iWidth, iHeight;

    iWidth = 0;
    iWidth = pBmpVDP1->Width;
    iHeight = pBmpVDP1->Height;
    scl_log(SCLT_HOSTPG, 0/*level*/, "iWidth = %d/%d", iWidth, pBmpVDP1->Width);
    scl_log(SCLT_HOSTPG, 0/*level*/, "iHeight = %d/%d", iHeight, pBmpVDP1->Height);

    for(i=0; i<iHeight; i++)
    {
        RGBQUAD* pRaw = (RGBQUAD*)pBmpVDP1->ScanLine[i];
        for(j=0; j<iWidth; j++)
        {
            unsigned long ulData;
            if((j < _vdp1_width2) && (i < _vdp1_height2))
            {
                ulData = _vdp1_tmp[i*_vdp1_width2 + j];
            }
            else
            {
                ulData = 0;
            }
            pRaw[j].rgbBlue  = (ulData >>  0) & 0xFF;
            pRaw[j].rgbGreen = (ulData >>  8) & 0xFF;
            pRaw[j].rgbRed   = (ulData >> 16) & 0xFF;

        }
    }
    Form1->imgVDP1->Picture->Graphic = pBmpVDP1;
}

// Get target information
// Taken from http://cgfm2.emuviews.com/elec/comms.php
void ProcessThread::target_info(void)
{
    unsigned char buf[0x100];

    /* Read VDP1 MODR register */
    SC_Receive(0x25D00016, 2, (char*)buf);
    scl_log(SCLT_HOSTPG, 0/*level*/, "VDP1 chip version:   %d", (buf[0] >> 4) & 0x0F);

    /* Read VDP2 VRSIZE register */
    SC_Receive(0x25F80006, 2, (char*)buf);
    scl_log(SCLT_HOSTPG, 0/*level*/, "VDP2 chip version:   %d", buf[1] & 0x0F);

    /* Read SCU version register */
    SC_Receive(0x25FE00C8, 2, (char*)buf);
    scl_log(SCLT_HOSTPG, 0/*level*/, "SCU chip version:    %d", buf[1] & 0x0F);

    /* Read BIOS version string */
    SC_Receive(0x20000804, 4, (char*)buf);
    buf[4] = 0;
    scl_log(SCLT_HOSTPG, 0/*level*/, "Saturn BIOS version: %s", buf);

    /* Read Action Replay version string */
    SC_Receive(0x22004AF0, 0xFF, (char*)buf);
    buf[0xFF] = 0;
    scl_log(SCLT_HOSTPG, 0/*level*/, "Cartridge version:   %s", buf);

    /* Read memory cartridge ID register */
    SC_Receive(0x24000000, 2, (char*)buf);
    switch(buf[1])
    {
        case 0x5A: /* 1MB RAM cart. */
            scl_log(SCLT_HOSTPG, 0/*level*/, "Cartridge memory:    $%02X (1MB expansion memory)", buf[1]);
            break;

        case 0x5C: /* 4MB RAM cart. */
            scl_log(SCLT_HOSTPG, 0/*level*/, "Cartridge memory:    $%02X (4MB expansion memory)", buf[1]);
            break;

        default:/* Unknown ID or ID register not implemented. */
            scl_log(SCLT_HOSTPG, 0/*level*/, "Cartridge memory:    $%02X (Unknown ID)", buf[1]);
            break;
    }
}





//---------------------------------------------------------------------------
void __fastcall ProcessThread::UpdateDebuggerActivity(scd_info_t* ptr)
{
    ptrDebuggerActivity = ptr;
    Synchronize(UpdateDebuggerActivity_Internal);
}
void __fastcall ProcessThread::UpdateDebuggerActivity_Internal(void)
{
    char tmp[512];
    scd_info_t* ptr = ptrDebuggerActivity;
    if(ptrDebuggerActivity == NULL) return;

    // lblDbgInfos1
    sprintf(tmp, "Magic=0x%08X, struct ADR=0x%08X, VER=%d.%d", ptr->magic_number, ptr->struct_start_addr, ptr->struct_version[0], ptr->struct_version[1]);
    Form1->lblDbgInfos1->Caption = tmp;
    // lblDbgInfos2
    sprintf(tmp, "Data STT=0x%08X, LEN=%d bytes", ptr->data_start_addr, ptr->data_length);
    Form1->lblDbgInfos2->Caption = tmp;
    // lblDbgInfos3
    unsigned long usage_percent = 0;
    if(ptr->data_length !=   0) usage_percent = (100 * ptr->messages_size) / ptr->data_length;
    if(usage_percent    >= 100) usage_percent = 100;
    sprintf(tmp, "Offsets RD=0x%04X, WR=0x%04X (%3d%%), SZ=%d, CNT=%d", ptr->data_readptr, ptr->data_writeptr, usage_percent, ptr->messages_size, ptr->messages_count);
    Form1->lblDbgInfos3->Caption = tmp;

    /* Set item to "visible" only on first time. */
    if(ptr->cntr == 1)
    {
        Form1->lblDbgInfos1->Visible = true;
        Form1->lblDbgInfos2->Visible = true;
        Form1->lblDbgInfos3->Visible = true;
    }
}

void /*__fastcall*/ ProcessThread::LogOut(const char *string, ...)
{
    /* Format to memory */
    char str[sizeof(cLogBuff)];
    va_list argptr;
    va_start(argptr, string);
    vsnprintf(str, sizeof(str), string, argptr);
    va_end(argptr);

    /* Add date */
    if(strlen(str) != 0)
    {
        SYSTEMTIME sTime;
        GetLocalTime(&sTime);
        snprintf(cLogBuff, sizeof(cLogBuff), "[%02d:%02d:%02d:%03d]%s",
                    sTime.wHour, sTime.wMinute, sTime.wSecond, sTime.wMilliseconds, 
                    str);
    }
    else
    {
        snprintf(cLogBuff, sizeof(cLogBuff), "");
    }

    Synchronize(LogOut_Internal);
}
void __fastcall ProcessThread::LogOut_Internal(void)
{
    Form1->mLog->Lines->Add(cLogBuff);
}
void /*__fastcall*/ ProcessThread::DbgOut(const char *string, ...)
{
    /* Format to memory */
    char str[sizeof(cLogBuff)];
    va_list argptr;
    va_start(argptr, string);
    vsnprintf(str, sizeof(str), string, argptr);
    va_end(argptr);

    /* Add date. */
    if(strlen(str) != 0)
    {
        SYSTEMTIME sTime;
        GetLocalTime(&sTime);
        snprintf(cLogBuff, sizeof(cLogBuff), "[%02d:%02d:%02d:%03d]%s",
                    sTime.wHour, sTime.wMinute, sTime.wSecond, sTime.wMilliseconds, 
                    str);
    }
    else
    {
        snprintf(cLogBuff, sizeof(cLogBuff), "");
    }

    Synchronize(DbgOut_Internal);
}
void __fastcall ProcessThread::DbgOut_Internal(void)
{
    Form1->mDbgText->Lines->Add(cLogBuff);
}
void /*__fastcall*/ ProcessThread::DbgGridOut(unsigned char msg_id, const char *string, ...)
{
    /* Format to memory */
    char str[sizeof(cLogBuff)];
    va_list argptr;
    va_start(argptr, string);
    vsnprintf(str, sizeof(str), string, argptr);
    va_end(argptr);

    /* String grid ID. */
    log_msg_id = msg_id;

    /* Write string. */
    snprintf(cLogBuff, sizeof(cLogBuff), "%s", str);

    Synchronize(DbgGridOut_Internal);
}
void __fastcall ProcessThread::DbgGridOut_Internal(void)
{
    Form1->sgDbgText->Cells[1][log_msg_id] = cLogBuff;
}

//---------------------------------------------------------------------------

/* Resetting debugger is needed when executing data on Saturn
 * (debug structure address may have changed, and as we need to wait for boot screen, debugger is disabled)
 */
void __fastcall ProcessThread::ResetDebugger(void)
{
    Synchronize(ResetDebugger_Internal);
}
void __fastcall ProcessThread::ResetDebugger_Internal(void)
{
    pSc->DbgReset();

    Form1->udGUI.btn[BTN_MVREAD]  = 0;
    udGUI.btn[BTN_MVREAD]         = 0;
    Form1->udGUI.btn[BTN_MVWRITE] = 0;
    udGUI.btn[BTN_MVWRITE]        = 0;

    Form1->cbMemViewAuto->Checked = false;
    Form1->udGUI.mv_autoread      = 0;
    udGUI.mv_autoread             = 0;

    //Form1->cbDbgUse->Checked      = false;
    //Form1->udGUI.dbg_use          = 0;
    //udGUI.dbg_use                 = 0;

    UpdateDebuggerStatus(false/*bDebuggerOn*/);

    //memcpy((void*)(&udGUI), (void*)(&(Form1->udGUI)), sizeof(UserData));
    //Form1->mLog->Lines->Add(cLogBuff);
}







//---------------------------------------------------------------------------
void __fastcall ProcessThread::GetUserData(void)
{
    Synchronize(GetUserData_Internal);
}
void __fastcall ProcessThread::GetUserData_Internal(void)
{
    Form1->SaveToUserData();
    memcpy((void*)(&udGUI), (void*)(&(Form1->udGUI)), sizeof(UserData));
}

void __fastcall ProcessThread::ClearButtonState(unsigned char btn)
{
    _btn_clear_index = btn;
    Synchronize(ClearButtonState_Internal);
}
void __fastcall ProcessThread::ClearButtonState_Internal(void)
{
    Form1->udGUI.btn[_btn_clear_index] = 0;
}


void __fastcall ProcessThread::SetUserData(void)
{
    Synchronize(SetUserData_Internal);
}
void __fastcall ProcessThread::SetUserData_Internal(void)
{
    memcpy((void*)(&(Form1->udGUI)), (void*)(&udGUI), sizeof(UserData));
}

/* UserData->GUI */
void __fastcall ProcessThread::RefreshMemViewData(void)
{
    Synchronize(RefreshMemViewData_Internal);
}
void __fastcall ProcessThread::RefreshMemViewData_Internal(void)
{
    /* Refresh address. */
    Form1->udGUI.mv_address = udGUI.mv_address;
    AnsiString str;
    str.printf("%08X", udGUI.mv_address);
    Form1->edtMemViewAddress->Text = str;

    memcpy((void*)(Form1->udGUI.mv_data), (void*)(udGUI.mv_data), MEMVIEW_DATASIZE);

    /* Indicates that the contents has changed. */
    Form1->udGUI.mv_cntr = Form1->udGUI.mv_cver + 1;
}


/* Set debug folder. */
void __fastcall ProcessThread::SetDebugFolder(void)
{
    char dbg_folder[STR_LEN];
    /* Set path to requested file. */
    if(udGUI.dbg_folder[0] == '\0')
    { /* Use binary folder. */
        memcpy((void*)(dbg_folder), (void*)(udGUI.dbg_file), STR_LEN);
        AnsiString szFileName = dbg_folder; int pos = szFileName.LastDelimiter("\\");
        if(pos >= 1) dbg_folder[pos-1] = '\0';
    }
    else
    { /* Use debug folder */
        sprintf(dbg_folder, "%s", udGUI.dbg_folder);
    }

    pSc->DbgSetIOFolder(dbg_folder);
}

/* GUI->UserData */
void __fastcall ProcessThread::GetMemViewData(void)
{
    Synchronize(GetMemViewData_Internal);
}
void __fastcall ProcessThread::GetMemViewData_Internal(void)
{
    ///* Get MemView buffer */
    //{
    //    int rc = Form1->sgMemory->RowCount;
    //    int cc = Form1->sgMemory->ColCount;
    //    int index = 0;
    //    for(int j=0; j<rc; j++)
    //    {
    //        for(int i=0; i<cc; i++)
    //        {
    //            unsigned char dat;
    //            if(index < MEMVIEW_DATASIZE)
    //            {
    //                AnsiString str;
    //                str = Form1->sgMemory->Cells[i][j];
    //                dat = strtoul(str.c_str(), NULL, 16);
    //                udGUI.mv_data[index] = dat;
    //            }
    //
    //            index++;
    //        }
    //    }
    //}
}



void __fastcall ProcessThread::UpdateSilentLogStatus(unsigned char s)
{
    udGUI.log_silent = s;
    Synchronize(UpdateSilentLogStatus_Internal);

    SC_GUI_Options(1/*bProgressUse*/, 0/*bDebuggerUse*/);
}
void __fastcall ProcessThread::UpdateSilentLogStatus_Internal(void)
{
    Form1->udGUI.log_silent = udGUI.log_silent;
}

//---------------------------------------------------------------------------
void __fastcall ProcessThread::UpdateTransferStatus(bool bTransferOn)
{
    udGUI.transfer_on = (bTransferOn ? 1 : 0);
    Synchronize(UpdateTransferStatus_Internal);
}
void __fastcall ProcessThread::UpdateDebuggerStatus(bool bDebuggerOn)
{
    udGUI.debugger_on = (bDebuggerOn ? 1 : 0);
    Synchronize(UpdateTransferStatus_Internal);
}
void __fastcall ProcessThread::UpdatePromptStatus(bool bPromptOn)
{
    udGUI.prompt_on = (bPromptOn ? 1 : 0);
    Synchronize(UpdateTransferStatus_Internal);
}
void __fastcall ProcessThread::UpdateLinkStatus(void)
{
    Synchronize(UpdateTransferStatus_Internal);
}
void __fastcall ProcessThread::UpdateTransferStatus_Internal(void)
{
    Form1->udGUI.transfer_on = udGUI.transfer_on;
    Form1->udGUI.debugger_on = udGUI.debugger_on;

    if(udGUI.debugger_on) udGUI.debugger_count++;
    Form1->udGUI.debugger_count = udGUI.debugger_count;

    if(Form1->udGUI.prompt_on != udGUI.prompt_on)
    {
        ///* Display send button only when it is needed. */
        //Form1->btnPromptSend->Visible = (udGUI.prompt_on ? true : false);

        //if(udGUI.prompt_on == 0)
        //{ /* Flush data in prompt field. */
        //    Form1->edtDbgPrompt->Text = "";
        //}
    }
    Form1->udGUI.prompt_on = udGUI.prompt_on;
}
void __fastcall ProcessThread::UpdateDeviceVersion(void)
{
    Synchronize(UpdateDeviceVersion_Internal);
}
void __fastcall ProcessThread::UpdateDeviceVersion_Internal(void)
{
    AnsiString str = "Device: ";
    str += pSc->Version();
    Form1->StatusBar->Panels->Items[SB_VERSION_INDEX]->Text = str;
}
void __fastcall ProcessThread::UpdateTransferSpeed(void)
{
    Synchronize(UpdateTransferSpeed_Internal);
}
void __fastcall ProcessThread::UpdateTransferSpeed_Internal(void)
{
    Form1->StatusBar->Panels->Items[SB_SPEED_INDEX]->Text = _strTransferSpeed;
}




//---------------------------------------------------------------------------
void __fastcall ProcessThread::SC_OpenDevice(char* dev_name)
{
    bDllOpen = false;

    scl_log(SCLT_HOSTPG, 0/*level*/, "Open device (%s) ...", dev_name);
    if(!strcmp(dev_name, "usbdl"))
    { /* Open USB Data Link device. */
        pSc->End(); delete pSc; pSc = new SC_usbdl(); UpdateDeviceVersion();
    }
    else if(!strcmp(dev_name, "parlink"))
    { /* Open Parallel Port based device. */
        pSc->End(); delete pSc; pSc = new SC_parlink(); UpdateDeviceVersion();
    }
    else if(!strcmp(dev_name, "dummy"))
    { /* Open Dummy device. */
        pSc->End(); delete pSc; pSc = new SC_dummy(); UpdateDeviceVersion();
    }
    else
    { /* Unknown device. */
        scl_log(SCLT_HOSTPG, 0/*level*/, "Error: unknown device !");
    }


    if(pSc->errCode != SC_NOERROR)
    {
        error_print(pSc);
    }
    else
    {
        /* Callback that indicates the current download/upload progress. */
        pSc->SetCallback(sc_callback);

        scl_log(SCLT_HOSTPG, 0/*level*/, "Start ...");
        pSc->Start();
        if(pSc->errCode != SC_NOERROR)
        {
            error_print(pSc);
        }
        else
        {
            bDllOpen = true;
        }

        scl_log(SCLT_HOSTPG, 0/*level*/, "Open device (%s) success !", dev_name);
        scl_log(SCLT_HOSTPG, 0/*level*/, "Device version=%s", pSc->Version());
    }
}
void __fastcall ProcessThread::SC_Set(char* name, char* value)
{
    scl_log(SCLT_HOSTPG, 0/*level*/, "Set parameter (%s, %s) ...", name, value);
    pSc->Set(name, value);
}
void __fastcall ProcessThread::SC_Receive(unsigned long address, unsigned long size, char* buffer)
{
    if(!_iSilentTransfer) scl_log(SCLT_HOSTPG, 0/*level*/, "Receive %d bytes from address 0x%08X ...", size, address);
    if(!_iSilentTransfer) UpdateTransferStatus(true/*bTransferOn*/);
    pSc->Receive(address, buffer, size);
    if(!_iSilentTransfer) UpdateTransferStatus(false/*bTransferOn*/);
    if(pSc->errCode != SC_NOERROR)
    {
        error_print(pSc);
    }
}
void __fastcall ProcessThread::SC_Send(char* buffer, unsigned long size, unsigned long address, bool exec_flag)
{
    if(!_iSilentTransfer) scl_log(SCLT_HOSTPG, 0/*level*/, "Send to %d bytes to address 0x%08X ...", size, address);
    if(!_iSilentTransfer) UpdateTransferStatus(true/*bTransferOn*/);
    pSc->Send(address, buffer, size, (exec_flag ? SC_EXEC : SC_LOAD));
    if(!_iSilentTransfer) UpdateTransferStatus(false/*bTransferOn*/);
    if(pSc->errCode != SC_NOERROR)
    {
        error_print(pSc);
    }
}

/*
 * Hide/show progess bar and adjust log levels according to situation and user settings.
 */
char _strLogStatus[20];
void __fastcall ProcessThread::SC_GUI_Options(bool bProgressUse, bool bDebuggerUse)
{
    if(udGUI.log_silent)
    { /* Log is turned to silent. */
        scl_silentmode(1, (bProgressUse ? 1 : 0));
        _iProgressDisplay = (bProgressUse ? 1 : 0);
        _iSilentTransfer  = 1;

        sprintf(_strLogStatus, "Log: Silent");
    }
    else
    { /* Log is turned on. */
        if(udGUI.silent_dbg)
        { /* Turn off/on transfer-related logging settings. */
            scl_silentmode((bProgressUse ? 0 : 1), (bProgressUse ? 1 : 0));
            _iProgressDisplay = (bProgressUse ? 1 : 0);
            _iSilentTransfer  = (bProgressUse ? 0 : 1);

            sprintf(_strLogStatus, "Log: ON");
        }
        else
        { /* Force log display, even while requesting debug data. */
            scl_silentmode(0, 0/*level*/);
            _iProgressDisplay = 1;
            _iSilentTransfer  = 0;

            sprintf(_strLogStatus, "Log: ON+");
        }
    }

    /* Update Log status. */
    Synchronize(UpdateLogStatus_Internal);
}
void __fastcall ProcessThread::UpdateLogStatus_Internal(void)
{
    Form1->StatusBar->Panels->Items[SB_LOG_INDEX]->Text = _strLogStatus;
}



void __fastcall ProcessThread::print_help(char* filename)
{
    scl_log(SCLT_HOSTPG, 0/*level*/, "--- Saturn Communication Utility by cafe-alpha.      ---");
    scl_log(SCLT_HOSTPG, 0/*level*/, "--- This is a part of my S.A.T.U.R.N. contest entry. ---");
    scl_log(SCLT_HOSTPG, 0/*level*/, "Build date : %s, %s.", __DATE__, __TIME__);
    scl_log(SCLT_HOSTPG, 0/*level*/, "Usage: %s <ini_file> [<commands>]", filename);
    scl_log(SCLT_HOSTPG, 0/*level*/, "");
    scl_log(SCLT_HOSTPG, 0/*level*/, "Available command-line parameters are:");
    scl_log(SCLT_HOSTPG, 0/*level*/, " "CMDSW_LOGSETTS" <file>          Open log settings file.");
    scl_log(SCLT_HOSTPG, 0/*level*/, " "CMDSW_DEVICE" <parlink|usbdl>         Open the specified device.");
    scl_log(SCLT_HOSTPG, 0/*level*/, " "CMDSW_SETPARAM" <parameter> <value>       Set parameter to the specified value.");
    ///*deprecated*/scl_log(SCLT_HOSTPG, 0/*level*/, " "CMDSW_LOGFILE"  <filename>         Set log filename (`stdout'->write to stdout).");
    ///*deprecated*/scl_log(SCLT_HOSTPG, 0/*level*/, " "CMDSW_LOGBUFFER" <size>            Set log buffer size, in KBytes.");
    scl_log(SCLT_HOSTPG, 0/*level*/, " "CMDSW_LOGINFOS"                    Log help and commands given to program.");
    scl_log(SCLT_HOSTPG, 0/*level*/, "Basic commands:");
    scl_log(SCLT_HOSTPG, 0/*level*/, " "CMDSW_LOADFILE"  <file> <addr>            Load file to address");
    scl_log(SCLT_HOSTPG, 0/*level*/, " "CMDSW_EXECFILE"  <file> <addr>            Load file and JSR to load address");
    scl_log(SCLT_HOSTPG, 0/*level*/, " "CMDSW_DUMPFILE"  <file> <addr> <size>     Dump data to file");
    ///*deprecated*/scl_log(SCLT_HOSTPG, 0/*level*/, " "CMDSW_LOGLEVEL"  <value>           Display log messages which level over value.");
    ///*deprecated*/scl_log(SCLT_HOSTPG, 0/*level*/, "                               (0:disable log, 10:display all logs).");
    scl_log(SCLT_HOSTPG, 0/*level*/, "Some shortcuts for the above commands:");
    scl_log(SCLT_HOSTPG, 0/*level*/, " "CMDSW_DUMPBIOS" <file>                   Dump BIOS (assume 512K)");
    scl_log(SCLT_HOSTPG, 0/*level*/, " "CMDSW_DUMPFIRM" <file>                   Dump cheat cartridge firmware (assume 256K)");
    scl_log(SCLT_HOSTPG, 0/*level*/, " "CMDSW_BATSAVE" <file>                   Save battery backed RAM to file");
    scl_log(SCLT_HOSTPG, 0/*level*/, " "CMDSW_BATLOAD" <file>                   Load battery backed RAM from file (must be 32K)");
    scl_log(SCLT_HOSTPG, 0/*level*/, " "CMDSW_TGTINFO"                 Display target informations.");
    scl_log(SCLT_HOSTPG, 0/*level*/, " "CMDSW_VDP1VIEW"                    Get VPD1 framebuffer snapshot.");
    scl_log(SCLT_HOSTPG, 0/*level*/, "");
    scl_log(SCLT_HOSTPG, 0/*level*/, "Based on Saturn PAR utility by Charles MacDonald (http://cgfm2.emuviews.com)");
    scl_log(SCLT_HOSTPG, 0/*level*/, "Based on code by Bart Trzynadlowski, James Forshaw and Charles MacDonald.");
}


//---------------------------------------------------------------------------
void __fastcall ProcessThread::ParseCommandLine(void)
{
    /* Note: When ProcessThread is running, argc/argv are not
     * used in Form1, so we can directly acces to them.
     */
    int argc = Form1->argc;
    //scl_log(SCLT_HOSTPG, 0/*level*/, "argc = %d", argc);
    //for(int i=1; i<argc; i++)
    //{
    //    scl_log(SCLT_HOSTPG, 0/*level*/, "argv[%d] = %s", i, Form1->argv[i]);
    //}
    for(int index = 1; index < argc; index++)
    { //scl_log(SCLT_HOSTPG, 0/*level*/, "***parse[%d] = %s", index, argv[index]);

        /* DLL open function. */
        if(!stricmp(Form1->argv[index], CMDSW_DEVICE))
        {
            SC_OpenDevice(Form1->argv[index+1]);
            index += CMDCNT_DEVICE;
        }
        /* Set parameter function. */
        else if(!stricmp(Form1->argv[index], CMDSW_SETPARAM))
        {
            scl_log(SCLT_HOSTPG, 0/*level*/, "Init parameter \"%s\" = \"%s\"", Form1->argv[index+1], Form1->argv[index+2]);
            SC_Set(Form1->argv[index+1], Form1->argv[index+2]);
            index += CMDCNT_SETPARAM;
        }
        /* Option functions */
        else if(!stricmp(Form1->argv[index], CMDSW_LOGSETTS))
        {
            scl_log(SCLT_HOSTPG, 0/*level*/, "Set log setting from file: %s", Form1->argv[index+1]);
            scl_read_ini(Form1->argv[index+1], "satlink_"/*section name*/);
            UpdateSilentLogStatus(1); /* Silent log by default. */
            index += CMDCNT_LOGSETTS;
        }
        else if(!stricmp(Form1->argv[index], CMDSW_LOGLEVEL))
        {
            scl_log(SCLT_HOSTPG, 0/*level*/, "Warning: deprecated function: `%s'", Form1->argv[index]);
            index += CMDCNT_LOGLEVEL;
        }
        else if(!stricmp(Form1->argv[index], CMDSW_LOGFILE))
        {
            scl_log(SCLT_HOSTPG, 0/*level*/, "Warning: deprecated function: `%s'", Form1->argv[index]);
            index += CMDCNT_LOGFILE;
        }
        else if(!stricmp(Form1->argv[index], CMDSW_LOGBUFFER))
        {
            scl_log(SCLT_HOSTPG, 0/*level*/, "Warning: deprecated function: `%s'", Form1->argv[index]);
            index += CMDCNT_LOGBUFFER;
        }
        else if(!stricmp(Form1->argv[index], CMDSW_LOGINFOS))
        {
            int k;
            print_help(Form1->app_name);
            /* Log commands given to program. */
            scl_log(SCLT_HOSTPG, 0/*level*/, "Command line: `%s", Form1->app_name);
            for(k = 1; k < argc; k++) scl_log(SCLT_HOSTPG, 0/*level*/, " \"%s\"", Form1->argv[k]);
            scl_log(SCLT_HOSTPG, 0/*level*/, "'");
            index += CMDCNT_LOGINFOS;
        }

        /* Target informations function. */
        else if(!stricmp(Form1->argv[index], CMDSW_TGTINFO))
        {
            target_info();
            index += CMDCNT_TGTINFO;
        }

        /* Get VDP1 framebuffer. */
        else if(!stricmp(Form1->argv[index], CMDSW_VDP1VIEW))
        {
            vdp1_view();
            index += CMDCNT_VDP1VIEW;
        }

        /* Base send/receive functions. */
        else if(!stricmp(Form1->argv[index], CMDSW_LOADFILE))
        {
            SendData(Form1->argv[index+1], strtoul(Form1->argv[index+2], NULL, 0), SC_LOAD);
            index += CMDCNT_LOADFILE;
        }
        else if(!stricmp(Form1->argv[index], CMDSW_EXECFILE))
        {
            SendData(Form1->argv[index+1], strtoul(Form1->argv[index+2], NULL, 0), SC_EXEC);
            index += CMDCNT_EXECFILE;
        }
        else if(!stricmp(Form1->argv[index], CMDSW_DUMPFILE))
        {
            ReceiveData(Form1->argv[index+1], strtoul(Form1->argv[index+2], NULL, 0), strtoul(Form1->argv[index+3], NULL, 0));
            index += CMDCNT_DUMPFILE;
        }

        /* High-level functions. */
        else if(!stricmp(Form1->argv[index], CMDSW_DUMPBIOS))
        {
            scl_log(SCLT_HOSTPG, 0/*level*/, "Dumping BIOS to `%s'", Form1->argv[index+1]);
            ReceiveData(Form1->argv[index+1], 0x20000000, 0x80000);
            index += CMDCNT_DUMPBIOS;
        }
        else if(!stricmp(Form1->argv[index], CMDSW_DUMPFIRM))
        {
            scl_log(SCLT_HOSTPG, 0/*level*/, "Dumping cheat cartridge firmware to `%s'", Form1->argv[index+1]);
            ReceiveData(Form1->argv[index+1], 0x22000000, 0x40000);
            index += CMDCNT_DUMPFIRM;
        }
        else if(!stricmp(Form1->argv[index], CMDSW_BATSAVE))
        {
            scl_log(SCLT_HOSTPG, 0/*level*/, "Saving battery backed RAM to `%s'", Form1->argv[index+1]);
            ReceiveData(Form1->argv[index+1], 0x20180000, 0x10000);
            index += CMDCNT_BATSAVE;
        }
        else if(!stricmp(Form1->argv[index], CMDSW_BATLOAD))
        {
            scl_log(SCLT_HOSTPG, 0/*level*/, "Loading battery backed RAM from `%s'", Form1->argv[index+1]);
            SendData(Form1->argv[index+1], 0x20180000, SC_LOAD);
            index += CMDCNT_BATLOAD;
        }
        else
        {
            scl_log(SCLT_HOSTPG, 0/*level*/, "ERROR: unknown command: `%s' !", Form1->argv[index]);
        }

        /* Program end request ? */
        if(Terminated) return;
    }
}
void __fastcall ProcessThread::SendData(char* file, unsigned long address, int exec_flag)
{
    FILE *fd;
    unsigned char *buffer;
    unsigned long length;

    SetCurrentDirectory(app_path);
    fd = fopen(file, "rb");
    if(!fd)
    {
        scl_log(SCLT_HOSTPG, 0/*level*/, "Error: could not open file for reading (%s) !", file);
        return;
    }
    fseek(fd, 0, SEEK_END);
    length = ftell(fd);
    fseek(fd, 0, SEEK_SET);

    buffer = (unsigned char*)malloc(length);
    if(!buffer)
    {
        fclose(fd);
        scl_log(SCLT_HOSTPG, 0/*level*/, "Error: memory allocation failed !");
        return;
    }
    fread(buffer, length, 1, fd);
    fclose(fd);

    scl_log(SCLT_HOSTPG, 0/*level*/, "Loading file `%s' to address %08X", file, address);
    UpdateTransferStatus(true/*bTransferOn*/);
    pSc->Send(address, buffer, length, exec_flag);
    UpdateTransferStatus(false/*bTransferOn*/);
    free(buffer);

    if(pSc->errCode != SC_NOERROR)
    {
        error_print(pSc);
    }
}
void __fastcall ProcessThread::ReceiveData(char* file, unsigned long address, unsigned long length)
{
    FILE *fd;
    unsigned char *buffer;

    SetCurrentDirectory(app_path);
    fd = fopen(file, "wb");
    if(!fd)
    {
        scl_log(SCLT_HOSTPG, 0/*level*/, "Error: could not open file for writing (%s) !", file);
        return;
    }

    buffer = (unsigned char*)malloc(length);
    if(!buffer)
    {
        fclose(fd);
        scl_log(SCLT_HOSTPG, 0/*level*/, "Error: memory allocation failed !");
        return;
    }

    scl_log(SCLT_HOSTPG, 0/*level*/, "Reading data from %08X-%08X to `%s'", address, address+(length-1), file);
    UpdateTransferStatus(true/*bTransferOn*/);
    pSc->Receive(address, buffer, length);
    UpdateTransferStatus(false/*bTransferOn*/);

    fwrite(buffer, length, 1, fd);
    fclose(fd);
    free(buffer);

    if(pSc->errCode != SC_NOERROR)
    {
        unlink(file);
        error_print(pSc);
    }
}

