// CDHelper.cpp: implementation of the CCDHelper class.
//
//////////////////////////////////////////////////////////////////////

#include "CDHelper.h"
#include "DUiMan.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

/*  The CD-ROM access routines are based on the sample by Marcus Comstedt
    and some of Dan Potters modifications */

BOOL g_bInKernelMode = FALSE;

CCDHelper::CCDHelper()
{
#ifdef _WIN32_WCE
    m_hCoreDLL = LoadLibrary(L"coredll.dll");
    if (m_hCoreDLL)
        pSetK = (lSetKMode) GetProcAddress(m_hCoreDLL,L"SetKMode");
#else
    pSetK = NULL;
    m_hCoreDLL = NULL;
#endif
}

CCDHelper::~CCDHelper()
{
    if (m_hCoreDLL)
    {
        FreeLibrary(m_hCoreDLL);
    }
}

int CCDHelper::InitDrive()
{
    int i, r;
    DWORD param[4];
    int cdxa;

    m_pDUiMan->DisplayDebugDataf(L"InitDrive - Enter");
    m_pDUiMan->UpdateFrame();
    /*
    * Retry the init disc command a few times if it fails,
    * for example if the disc has been changed
    */
    for(i=0; i<8; i++)
    {
        m_pDUiMan->DisplayDebugDataf(L"InitDrive - Execing command");
        m_pDUiMan->UpdateFrame();
        if(!(r = ExecuteCommand(24, NULL)))
            break;
    }
    /* If it failed all times, give up */
    if(r)
        return r;
    
    m_pDUiMan->DisplayDebugDataf(L"InitDrive - Getting drive status params");
    m_pDUiMan->UpdateFrame();
    /* Check type of disc */
    GetDriveStatus(param);
    
    /* CD/XA? */
    cdxa = (param[1] == 32);
    
    /* Select disc access mode */
    param[0] = 0;                   /* 0 = set, 1 = get */
    param[1] = 8192;                /* ?                */
    param[2] = (cdxa? 2048 : 1024); /* mode 1/2         */
    param[3] = 2048;                /* sector size      */

    m_pDUiMan->DisplayDebugDataf(L"InitDrive - Changing data type");
    m_pDUiMan->UpdateFrame();
    if(ChangeDataType(param)<0)
        return 3;//ERR_SYSERR;
    
    m_pDUiMan->DisplayDebugDataf(L"InitDrive - Exit");
    m_pDUiMan->UpdateFrame();
    /* All done */
    return 0;
}

int CCDHelper::InitCDFS()
{
    int iRetVal = 0;
    EXCEPTION_RECORD ExRecord;
    CONTEXT ContextRecord;
    _EXCEPTION_POINTERS *pExcept;

    m_pDUiMan->DisplayDebugErrorf(L"InitCDFS - Enter");
    if (pSetK)
    {
        pSetK(TRUE);
        g_bInKernelMode = TRUE;
        __try
        {
            register unsigned long p, x;
        
            /* Reactivate GD-ROM drive */
            *((volatile unsigned long *)0xa05f74e4) = 0x1fffff;
            for(p=0; p<0x200000/4; p++)
            {
                x = ((volatile unsigned long *)0xa0000000)[p];
            }

            m_pDUiMan->DisplayDebugErrorf(L"InitCDFS - Making initsystem call");
            /* Reset GD system functions */
            iRetVal = this->InitializeSystem();
        }
        __except (pExcept = GetExceptionInformation() , memcpy(&ExRecord,pExcept->ExceptionRecord,sizeof(ExRecord)),memcpy(&ContextRecord,pExcept->ContextRecord,sizeof(ContextRecord)),1)
        {
            m_pDUiMan->DisplayDebugErrorf(L"InitCDFS failed Ex Code %x (%x) at %x",ExRecord.ExceptionCode,ExRecord.ExceptionFlags,ExRecord.ExceptionAddress);
        }
        g_bInKernelMode = FALSE;
        pSetK(FALSE);
    }

    m_pDUiMan->DisplayDebugErrorf(L"InitCDFS - Exit");
    return iRetVal;
}

int CCDHelper::ExecuteCommand(int iCommand, void *vParam)
{
    int status[4];
    int f, n;
    int iRetVal = 1;
    
    if (pSetK)
    {
        pSetK(TRUE);
        g_bInKernelMode = TRUE;
        /* Submit the command to the system */
        f = RequestCommand(iCommand, vParam);
    
        /* Wait for the command to complete */
        do 
        {
//            m_pDUiMan->DisplayDebugErrorf(L"EC - ES-ing id %d ret %d",f,n);
//            m_pDUiMan->UpdateFrame();
            ExecServer(); 
        } while((n = GetCommandStatus(f, status))==1);
    
        /* If command completed normally, return 0 */
        if(n == 2)
        {
            iRetVal = 0;
        }
        /* Detect conditions that have special error codes */
        else 
        { 
            switch(status[0]) 
            {
            case 2:
                iRetVal = 1;//ERR_NODISK;
                break;
            case 6:
                iRetVal = 2;//ERR_DISKCHG;
                break;
            default:
                iRetVal = 3;//ERR_SYSERR;
                break;
            }
        }
        g_bInKernelMode = FALSE;
        pSetK(FALSE);
    }
    else
    {
        iRetVal = 0;
    }
    return iRetVal;
}

int CCDHelper::RequestCommand(int iCommand, void *vParam)
{
    return SystemCall(iCommand,(int)vParam,0,0);
}
//iStatus is an array of four ints
int CCDHelper::GetCommandStatus(int iCommandHandle, int *iStatus)
{
    return SystemCall(iCommandHandle,(int)iStatus,0,1);
}

int CCDHelper::ExecServer(void)
{
    return SystemCall(0,0,0,2);
}

int CCDHelper::InitializeSystem(void)
{
    return SystemCall(0,0,0,3);
}
int CCDHelper::GetDriveStatus(DWORD *pStatus)
{
    return SystemCall((int)pStatus,0,0,4);
}
int CCDHelper::G1DMAEnd()
{
    return SystemCall(0,0,0,5);
}
int CCDHelper::RequestDMATransfer()
{
    return SystemCall(0,0,0,6);
}
int CCDHelper::CheckDMATransfer()
{
    return SystemCall(0,0,0,7);
}
int CCDHelper::ReadAbort()
{
    return SystemCall(0,0,0,8);
}
int CCDHelper::Reset()
{
    return SystemCall(0,0,0,9);
}
int CCDHelper::ChangeDataType(DWORD *pdwTypeInfo)
{
    return SystemCall((int)pdwTypeInfo,0,0,10);
}

int CCDHelper::SystemCall(int iParam1, int iParam2, int iParam3, int iParam4)
{
    int iRetVal = 1;
    int (*pFunc) (int,int,int,int);
    int *piTemp;
    EXCEPTION_RECORD ExRecord;
    CONTEXT ContextRecord;
    _EXCEPTION_POINTERS *pExcept;

    if (pSetK)
    {
        if (!g_bInKernelMode)
            pSetK(TRUE);
        piTemp = (int *)0x8c0000bc;
        pFunc = (int (*) (int, int, int, int)) (*piTemp);
        __try
        {
            iRetVal = pFunc(iParam1,iParam2,iParam3,iParam4);
/*        __asm(  //"mov.l  #0x8c0000bc ,r0;\n"
//                "mov.l  #h'8c0000bc ,r0;\n"
                "mov    r6,r0;\n"
                "mov    #0,r6;\n"
                "mov.l  @r0,r0;\n"
                "jmp    @r0;\n"
                "nop;\n",
                iParam1,iParam2,0x8c0000bc,iParam4);
        //now get the return value
        __asm(  "mov.l  r0,@r4;\n",&iRetVal);
*/
        }
        __except (pExcept = GetExceptionInformation() , memcpy(&ExRecord,pExcept->ExceptionRecord,sizeof(ExRecord)),memcpy(&ContextRecord,pExcept->ContextRecord,sizeof(ContextRecord)),1)
        {
            m_pDUiMan->DisplayDebugErrorf(L"system call failed Ex Code %x (%x) at %x",ExRecord.ExceptionCode,ExRecord.ExceptionFlags,ExRecord.ExceptionAddress);
        }

        if (!g_bInKernelMode)
            pSetK(FALSE);
    }
    else
    {
        iRetVal = 0;
    }
 
    return iRetVal;
}

