Home

Resume

Blog

Teikitu


/* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */
/*  »Project«   Teikitu Gaming System (TgS) (∂)
    »File«      TgS (WIN) Common - Base - API - Platform.c
    »Author«    Andrew Aye (EMail: mailto:andrew.aye@gmail.com, Web: http://www.andrewaye.com)
    »Version«   4.51 / »GUID« A9981407-3EC9-42AF-8B6F-8BE6DD919615                                                                                                        */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
/*  Copyright: © 2002-2017, Andrew Aye.  All Rights Reserved.
    This software is free for non-commercial use.  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
    following conditions are met:
    Redistribution of source code must retain this copyright notice, this list of conditions and the following disclaimers.
    Redistribution in binary form must reproduce this copyright notice, this list of conditions and the following disclaimers in the documentation and other materials
    provided with the distribution.
    The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission.
    The intellectual property rights of the algorithms used reside with Andrew Aye.
    You may not use this software, in whole or in part, in support of any commercial product without the express written consent of the author.
    There is no warranty or other guarantee of fitness of this software for any purpose. It is provided solely "as is".                                                   */
/* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */

MSVC_WARN_DISABLE_PUSH( 4770 )


/* == Common ============================================================================================================================================================ */

/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */
/*  Forward Declarations                                                                                                                                                  */
/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */

#if TgS_STAT_COMMON
    #define STAT(...)                       __VA_ARGS__
#else
    #define STAT(...)
#endif

#if !defined(TgCOMPILE_PLATFORM_UWP)
#ifdef UNICODE
#define LoadLibrary                         LoadLibraryW
#else
#define LoadLibrary                         LoadLibraryA
#endif
__declspec(dllimport) TgMS_HINSTANCE        __stdcall LoadLibraryW( CP_TgWIDECHAR );
__declspec(dllimport) TgMS_HINSTANCE        __stdcall LoadLibraryA( CP_TgANSICHAR );
/*# !defined(TgCOMPILE_PLATFORM_UWP) */
#else
__declspec(dllimport) TgMS_HINSTANCE        __stdcall LoadPackagedLibrary( CP_TgWIDECHAR, TgUINT32 );
/*# !defined(TgCOMPILE_PLATFORM_UWP) */
#endif

__declspec(dllimport) TgSINT32              __stdcall FreeLibrary( TgMS_HINSTANCE );
__declspec(dllimport) TgMS_FARPROC          __stdcall GetProcAddress( TgMS_HINSTANCE, CP_TgANSICHAR );




/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */
/*  File Local Functions and Data                                                                                                                                         */
/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */
#if TgS_STAT_COMMON
static volatile TgATOMIC_SINT64             s_iStat_File_Read, s_iStat_File_Write;
#endif

#if TgCOMPILE_THREAD
static TgVOID                               tgTR_Thread_Set_Name( C_TgSINT32 );
static unsigned long __stdcall              tgTR_Thread_Start( void* pParam );
static TgUINT32                             s_auiTR_Thread_OS_Id[KTgMAX_THREAD_ENTRIES];
/*# TgCOMPILE_THREAD */
#endif


/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */
/*  Thread Local Data                                                                                                                                                     */
/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */

#if TgCOMPILE_THREAD && TgCOMPILE_THREAD_LOCAL
static TgTLS TgSINT32                       tls_iThread_Index = -1;
/*# TgCOMPILE_THREAD && TgCOMPILE_THREAD_LOCAL */
#endif




/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */
/*  Public Functions                                                                                                                                                      */
/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */

#if TgCOMPILE_THREAD

/* ---- tgTR_Register_Main_Thread --------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgTHREAD_ID tgTR_Register_Main_Thread( CPC_TgCHAR szName )
{
    TgSINT32                            iIndex;

    tgCM_UTM_SN_Lock_Spin( &g_sTR_Lock.m_sLock );
    for (iIndex = 0; iIndex < KTgMAX_THREAD_ENTRIES; ++iIndex)
    {
        if (TgTRUE == tgTHREAD_ID_Is_Valid( g_atiTR_Thread[iIndex] ))
        {
            continue;
        };

        g_auiTR_Thread_Stack[iIndex] = tgTR_Stack_Size();
        g_afnTR_Thread_Function[iIndex] = nullptr;
        g_auiTR_Thread_Param[iIndex] = 0;
        g_aenTR_Thread_Priority[iIndex] = ETgTHREAD_PRIORITY__NORMAL;
        g_pfnDuplicateHandle( g_pfnGetCurrentProcess(), g_pfnGetCurrentThread(), g_pfnGetCurrentProcess(), &(g_aTR_Thread[0]), 0, 0, 0x2 );

    #if TgCOMPILE_THREAD_INFO
        g_aszTR_Name[iIndex] = szName;
    #endif

        g_aTR_Thread[iIndex] = nullptr;

        s_auiTR_Thread_OS_Id[iIndex] = g_pfnGetCurrentThreadId();
        tls_iThread_Index = iIndex;
        tgInit_THREAD_ID( g_atiTR_Thread + iIndex, iIndex );

        tgTR_Thread_Set_Name( iIndex );

        tgCM_UTM_SN_Signal( &g_sTR_Lock.m_sLock );
        return (g_atiTR_Thread[iIndex]);
    };
    tgCM_UTM_SN_Signal( &g_sTR_Lock.m_sLock );
    return (KTgTHREAD_ID__INVALID);
}


/* ---- tgTR_Create ----------------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgTHREAD_ID tgTR_Create( TgPLATFORM_THREAD_FCN pfnThread, C_TgUINTPTR uiParam, C_TgUINT32 uiStack, C_ETgTHREAD_PRIORITY enPriority, CPC_TgCHAR szName )
{
    TgSINT32                            iIndex;

    if (0 == pfnThread)
    {
        return (KTgTHREAD_ID__INVALID);
    }

    tgCM_UTM_SN_Lock_Spin( &g_sTR_Lock.m_sLock );
    for (iIndex = 0; iIndex < KTgMAX_THREAD_ENTRIES; ++iIndex)
    {
        if (TgTRUE == tgTHREAD_ID_Is_Valid( g_atiTR_Thread[iIndex] ))
        {
            continue;
        };

        g_auiTR_Thread_Stack[iIndex] = 0;
        g_afnTR_Thread_Function[iIndex] = pfnThread;
        g_auiTR_Thread_Param[iIndex] = uiParam;
        g_aenTR_Thread_Priority[iIndex] = enPriority;

    #if TgCOMPILE_THREAD_INFO
        g_aszTR_Name[iIndex] = szName;
    #endif

        g_aTR_Thread[iIndex] = (P_TgVOID)g_pfnCreateThread( 0, uiStack, tgTR_Thread_Start, (P_TgVOID)(TgSINTPTR)iIndex, 0, s_auiTR_Thread_OS_Id + iIndex );

        tgInit_THREAD_ID( g_atiTR_Thread + iIndex, iIndex );

        tgCM_UTM_SN_Signal( &g_sTR_Lock.m_sLock );
        return (g_atiTR_Thread[iIndex]);
    };
    tgCM_UTM_SN_Signal( &g_sTR_Lock.m_sLock );
    return (KTgTHREAD_ID__INVALID);
}


/* ---- tgTR_Query_Id --------------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgTHREAD_ID tgTR_Query_Id( TgVOID )
{
#if !defined(TGS_FINAL) ||  !TgCOMPILE_THREAD_LOCAL
    TgUINT32                            uiThread_Id;
    TgSINT32                            iIndex;

    tgCM_UTM_SN_Lock_Spin( &g_sTR_Lock.m_sLock );

    uiThread_Id = g_pfnGetCurrentThreadId();
    for (iIndex = 0; iIndex < KTgMAX_THREAD_ENTRIES; ++iIndex)
    {
        if (uiThread_Id == s_auiTR_Thread_OS_Id[iIndex])
        {
            tgCM_UTM_SN_Signal( &g_sTR_Lock.m_sLock );
            TgERROR(tls_iThread_Index == iIndex);
            return (g_atiTR_Thread[iIndex]);
        };
    };

    tgCM_UTM_SN_Signal( &g_sTR_Lock.m_sLock );
    TgCRITICAL_MSGF( 0, TgT("%-16.16s(%-32.32s): Thread not found in thread pool!\n"), TgT("Thread"), TgT("Query Id") );
    return (KTgTHREAD_ID__INVALID);
/*# !defined(TGS_FINAL) ||  !TgCOMPILE_THREAD_LOCAL */
#else
    return (g_atiTR_Thread[tls_iThread_Index]);
/*# !defined(TGS_FINAL) ||  !TgCOMPILE_THREAD_LOCAL */
#endif
}


/* ---- tgTR_Close ------------------------------------------------------------------------------------------------------------------------------------------------------ */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgVOID tgTR_Close( C_TgTHREAD_ID tiThread )
{
    P_TgVOID                            hThread;

    TgPARAM_CHECK(tgTHREAD_ID_Is_Valid( tiThread ));

    /* Get the os handle */
    tgCM_UTM_SN_Lock_Spin( &g_sTR_Lock.m_sLock );
    TgERROR(0 != g_afnTR_Thread_Function[tiThread.m.iI]);
    hThread = g_aTR_Thread[tiThread.m.iI];
    tgCM_UTM_SN_Signal( &g_sTR_Lock.m_sLock );

    while (0UL != g_pfnWaitForSingleObjectEx( (P_TgVOID)hThread, 0xFFFFFFFF, TgTRUE )); /* Wait for thread to close */
    TgVERIFY( 0 != g_pfnCloseHandle( (P_TgVOID)hThread ) ); /* Close the handle */

    /* Mark the thread slot as available */
    tgCM_UTM_SN_Lock_Spin( &g_sTR_Lock.m_sLock );
    g_afnTR_Thread_Function[tiThread.m.iI] = 0;
    g_aTR_Thread[tiThread.m.iI] = 0;
    g_atiTR_Thread[tiThread.m.iI] = KTgTHREAD_ID__INVALID;
    tgCM_UTM_SN_Signal( &g_sTR_Lock.m_sLock );
}


/* ---- tgTR_Stack_Size ------------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
ETgTHREAD_STATUS tgTR_Status( C_TgTHREAD_ID tiThread )
{
    if (!tgTHREAD_ID_Is_Valid( tiThread ))
    {
        return (ETgTHREAD_STATUS__INVALID);
    }
    else
    {
        if (TgTRUE != tgEQ_THREAD_ID( tiThread, g_atiTR_Thread[tiThread.m.iI] ))
        {
            return (ETgTHREAD_STATUS__INVALID);
        }
        else if (0 == g_auiTR_Thread_Stack[tiThread.m.iI])
        {
            return (ETgTHREAD_STATUS__INIT);
        }
        else
        {
            return (ETgTHREAD_STATUS__EXEC);
        };
    };
}


/* ---- tgTR_Stack_Size ------------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgUINT32 tgTR_Stack_Size( TgVOID )
{
    /*
    struct
    {
        P_TgVOID                            DoNotUse00;
        P_TgVOID                            StackBase;
        P_TgVOID                            StackLimit;
        P_TgVOID                            DoNotUse01;
        P_TgVOID                            DoNotUse02;
        P_TgVOID                            DoNotUse03;
        P_TgVOID                            Self;
    }                                   *pTEB;

    pTEB = (P_TgVOID)(TgUINTPTR)__readgsqword( 48 );

    return ((TgUINT32)((P_TgUINT08)(pTEB->StackBase) - (P_TgUINT08)(pTEB->StackLimit)));
    */
    return (0);
}


/*# TgCOMPILE_THREAD */
#endif




/* == Time Functions ==================================================================================================================================================== */

static TgFLOAT64                    s_fFreq;
static LARGE_INTEGER                s_iInit_Start;
static LARGE_INTEGER                s_iStart[ETgTIME_CHANNEL__COUNT - 1];
static TgFLOAT32                    s_fLast[ETgTIME_CHANNEL__COUNT - 1];
static TgFLOAT32                    s_fElapsed[ETgTIME_CHANNEL__COUNT - 1];
static TgFLOAT32                    s_fStop[ETgTIME_CHANNEL__COUNT - 1];


/* ---- tgTM_Init ------------------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgVOID tgTM_Init( TgVOID )
{
    s_fFreq = tgSI_SYSTEM_Seconds_Per_CounterTick();
    TgERROR(s_fFreq > 0.0);

    g_pfnQueryPerformanceCounter( &s_iInit_Start );
}


/* ---- tgTM_Query_Counter_Tick ----------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgSINT64 tgTM_Query_Counter_Tick( TgVOID )
{
    LARGE_INTEGER                       iTime;
    g_pfnQueryPerformanceCounter( &iTime );
    return (iTime.QuadPart - s_iInit_Start.QuadPart);
}


/* ---- tgTM_Counter_To_MSec -------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgFLOAT32 tgTM_Counter_Tick_To_MSec( C_TgSINT64 iTime )
{
    return ((TgFLOAT32)((TgFLOAT64)(iTime) * s_fFreq * 1000.0));
}


/* ---- tgTM_Query_Time ------------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgFLOAT32 tgTM_Query_Time( TgVOID )
{
    return (tgTM_Counter_Tick_To_MSec( tgTM_Query_Counter_Tick()  ));
}


/* ---- tgTM_Channel_Query_Time ----------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgFLOAT32 tgTM_Channel_Query_Time( C_ETgTIMER_CHANNEL enChannel )
{
    TgPARAM_CHECK(enChannel < ETgTIME_CHANNEL__WALL);
    TgPARAM_CHECK_INDEX( enChannel, s_fStop );
    return (tgPM_FSEL_F32( -s_fStop[enChannel], tgTM_Query_Time(), s_fStop[enChannel] ));
}


/* ---- tgTM_Channel_Elapsed_Time --------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgFLOAT32 tgTM_Channel_Elapsed_Time( C_ETgTIMER_CHANNEL enChannel )
{
    TgPARAM_CHECK(enChannel < ETgTIME_CHANNEL__WALL);
    return (s_fElapsed[enChannel]);
}


/* ---- tgTM_Channel_Reset ---------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgVOID tgTM_Channel_Reset( C_ETgTIMER_CHANNEL enChannel )
{
    TgPARAM_CHECK(enChannel < ETgTIME_CHANNEL__WALL);
    g_pfnQueryPerformanceCounter( s_iStart + enChannel );
    s_fLast[enChannel] = tgTM_Query_Time();
    s_fElapsed[enChannel] = 0.0F;
    s_fStop[enChannel] = 0.0F;
}


/* ---- tgTM_Channel_Start ---------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgVOID tgTM_Channel_Start( C_ETgTIMER_CHANNEL enChannel )
{
    TgPARAM_CHECK(enChannel < ETgTIME_CHANNEL__WALL);
    if (!(s_fStop[enChannel] <= 0.0F))
    {
        s_fLast[enChannel] = tgTM_Query_Time();
        s_fStop[enChannel] = 0.0F;
    };
}


/* ---- tgTM_Channel_Stop ----------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgVOID tgTM_Channel_Stop( C_ETgTIMER_CHANNEL enChannel )
{
    TgPARAM_CHECK(enChannel < ETgTIME_CHANNEL__WALL);
    if (!(s_fStop[enChannel] > 0.0F))
    {
        s_fStop[enChannel] = s_fLast[enChannel];
    };
}


/* ---- tgTM_Channel_Update --------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgVOID tgTM_Channel_Update( C_ETgTIMER_CHANNEL enChannel )
{
    TgPARAM_CHECK(enChannel < ETgTIME_CHANNEL__WALL);
    if (!(s_fStop[enChannel] > 0.0F))
    {
        C_TgFLOAT32 fTime = tgTM_Query_Time();
        s_fElapsed[enChannel] = tgCM_MAX_F32( KTgEPS_F32, fTime - s_fLast[enChannel] );
        s_fLast[enChannel] = fTime;
        s_fStop[enChannel] = 0.0F;
    };
}


/* ---- tgTM_Channel_Step ----------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgVOID tgTM_Channel_Step( C_ETgTIMER_CHANNEL enChannel, C_TgFLOAT32 fTimeAdvance )
{
    TgPARAM_CHECK(enChannel < ETgTIME_CHANNEL__WALL);
    if (!(s_fStop[enChannel] <= 0.0F))
    {
        s_fElapsed[enChannel] = fTimeAdvance;
        s_fStop[enChannel] += fTimeAdvance;
    };
}




/* == IO Functions ====================================================================================================================================================== */

/* ---- File_Error ------------------------------------------------------------------------------------------------------------------------------------------------------ */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
static TgVOID File_Error( C_TgSINTPTR iFile )
{
    TgCHAR                              szBuf[256];
    P_TgVOID                            pMsgBuf;
    C_TgUINT32                          dwError = g_pfnGetLastError();

    TgERROR(((TgSINTPTR)-1) != iFile);

    g_pfnFormatMessage( 0x00001100, 0, dwError, (1 << 10), (P_TgCHAR)&pMsgBuf, 0, nullptr );
    tgSZ_PrintF( szBuf, 256, TgT("File_Read failed with error %d: %s"), dwError, pMsgBuf );

#if (_WIN32_WINNT >= 0x0A00 )
    g_pfnHeapFree( g_pfnGetProcessHeap(), 0, pMsgBuf );
#else
    g_pfnLocalFree( pMsgBuf );
#endif

    g_pfnOutputDebugString( szBuf );
    TgCRITICAL_MSGF( 0, TgT("%s\n"), szBuf );
}


/* ---- tgIO_File_Open -------------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgRESULT tgIO_File_Open( P_TgSINTPTR piFile, CPC_TgCHAR pszFile, ETgFILE_IO_ACCESS enAccess )
{
    TgUINT32                            dwDesiredAccess = 0, dwShareMode = 0;
    P_TgVOID                            hFile;

    TgERROR((0 != piFile) && (0 != pszFile));

    if ((ETgFILE_IO_ACCESS__READ | ETgFILE_IO_ACCESS__WRITE) == (enAccess & (ETgFILE_IO_ACCESS__READ | ETgFILE_IO_ACCESS__WRITE)))
    {
        if (enAccess & ETgFILE_IO_ACCESS__SHARED)
        {
            dwShareMode = 0x00000003L;
        };
        dwDesiredAccess = 0x0012019FL;
    }
    else  if (enAccess & ETgFILE_IO_ACCESS__READ)
    {
        if (enAccess & ETgFILE_IO_ACCESS__SHARED)
        {
            dwShareMode = 0x00000001L;
        };
        dwDesiredAccess = 0x00120089L;
    }
    else if (enAccess & ETgFILE_IO_ACCESS__WRITE)
    {
        if (enAccess & ETgFILE_IO_ACCESS__SHARED)
        {
            dwShareMode = 0x00000002L;
        };
        dwDesiredAccess = 0x00120116L;
    };

#if !defined(TgCOMPILE_PLATFORM_UWP)
    hFile = g_pfnCreateFile( pszFile, dwDesiredAccess, dwShareMode, 0, 4, 0x10000080L, 0 );
#else
    hFile = g_pfnCreateFile2( pszFile, dwDesiredAccess, dwShareMode, 4, nullptr );
#endif
    *piFile = (TgSINTPTR)hFile;
    return (hFile != ((P_TgVOID)(TgSINTPTR)-1) ? KTgS_OK : KTgE_FAIL);
}


/* ---- tgIO_File_Read -------------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgSIZE tgIO_File_Read( C_TgSINTPTR iFile, P_TgVOID pDest, TgSIZE uiTotalBytesToRead )
{
    TgSIZE                              uiTotalBytesRead;

    TgERROR(((TgSINTPTR)-1) != iFile);

    uiTotalBytesRead = 0;
    while (uiTotalBytesToRead > 0)
    {
        DWORD                               uiBytesToRead;
        DWORD                               uiBytesRead;

        uiBytesToRead = uiTotalBytesToRead % KTgMAX_U32;
        if (!(g_pfnReadFile( (P_TgVOID)iFile, (P_TgUINT08)pDest + uiTotalBytesRead, uiBytesToRead, &uiBytesRead, nullptr )))
        {
            File_Error( iFile );
        };
        uiTotalBytesToRead -= uiBytesToRead;
        uiTotalBytesRead += uiBytesRead;
    };

    STAT( tgAM64_XADD( &s_iStat_File_Read, (TgSINT64)uiTotalBytesRead ) );
    return (uiTotalBytesRead);
}


/* ---- tgIO_File_Write ------------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgSIZE tgIO_File_Write( C_TgSINTPTR iFile, CP_TgVOID pSource, TgSIZE uiTotalBytesToWrite )
{
    TgSIZE                              uiTotalBytesWriten;

    TgERROR(((TgSINTPTR)-1) != iFile);

    uiTotalBytesWriten = 0;
    while (uiTotalBytesToWrite > 0)
    {
        DWORD                               uiBytesWriten;
        DWORD                               uiBytesWrite;

        uiBytesWrite = uiTotalBytesToWrite % KTgMAX_U32;
        if (!(g_pfnWriteFile( (P_TgVOID)iFile, pSource, uiBytesWrite, &uiBytesWriten, nullptr )))
        {
            File_Error( iFile );
        };
        uiTotalBytesToWrite -= uiBytesWrite;
        uiTotalBytesWriten += uiBytesWriten;
        pSource = (CP_TgUINT08)pSource + uiBytesWrite;
    };

    STAT( tgAM64_XADD( &s_iStat_File_Write, (TgSINT64)uiTotalBytesWriten ) );
    return (uiTotalBytesWriten);
}


/* ---- tgIO_File_Flush ------------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgRESULT tgIO_File_Flush( C_TgSINTPTR iFile )
{
    TgERROR(((TgSINTPTR)-1) != iFile);
    TgVERIFY( 0 != g_pfnFlushFileBuffers( (P_TgVOID)iFile ) );
    return (KTgS_OK);
}


/* ---- tgIO_File_Close ------------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgRESULT tgIO_File_Close( C_TgSINTPTR iFile )
{
    TgERROR(((TgSINTPTR)-1) != iFile);
    TgVERIFY( 0 != g_pfnCloseHandle( (P_TgVOID)iFile ) );
    return (KTgS_OK);
}


/* ---- tgIO_File_Valid ------------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgBOOL tgIO_File_Valid( C_TgSINTPTR iFile )
{
    TgERROR(((TgSINTPTR)-1) != iFile);
    return (((TgSINTPTR)-1) != iFile ? TgTRUE : TgFALSE);
}


/* ---- tgIO_File_Seek -------------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgRESULT tgIO_File_Seek( C_TgSINTPTR iFile, C_ETgIO_SEEK enSeek_Mode, C_TgSINT64 iPos )
{
    TgBOOL                              bSuccess;
    LARGE_INTEGER                       sNew_Position;

    sNew_Position.QuadPart = iPos;

    TgERROR(((TgSINTPTR)-1) != iFile);

    switch (enSeek_Mode)
    {
        case ETgIO_SEEK__BEGIN:
            bSuccess = 0 != g_pfnSetFilePointerEx( (P_TgVOID)iFile, sNew_Position, nullptr, 0 );
            break;
        case ETgIO_SEEK__END:
            bSuccess = 0 != g_pfnSetFilePointerEx( (P_TgVOID)iFile, sNew_Position, nullptr, 2 );
            break;
        case ETgIO_SEEK__CURRENT:
            bSuccess = 0 != g_pfnSetFilePointerEx( (P_TgVOID)iFile, sNew_Position, nullptr, 1 );
            break;

        default:
            TgS_NO_DEFAULT( return (KTgE_FAIL) );
    }

    return (bSuccess ? KTgS_OK : KTgE_FAIL);
}


/* ---- tgIO_File_Size -------------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgSIZE tgIO_File_Size( C_TgSINTPTR iFile )
{
    struct
    {
        LARGE_INTEGER AllocationSize;
        LARGE_INTEGER EndOfFile;
        DWORD NumberOfLinks;
        BOOL DeletePending;
        BOOL Directory;
        BOOL Pad;
    }                                   sFileStandardInfo;

    if (0 == g_pfnGetFileInformationByHandleEx( (P_TgVOID)iFile, FileStandardInfo, &sFileStandardInfo, sizeof(sFileStandardInfo) ))
        return (TgFALSE);
    return ((TgSIZE)sFileStandardInfo.EndOfFile.QuadPart);
}


/* ---- tgIO_Directory_Make --------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgRESULT tgIO_Directory_Make( CPC_TgCHAR pszPath )
{
    return (g_pfnCreateDirectory( pszPath, 0 ) ? KTgS_OK : KTgE_FAIL);
}


/* ---- tgIO_Directory_Remove ------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgRESULT tgIO_Directory_Remove( CPC_TgCHAR pszPath )
{
    return (g_pfnRemoveDirectory( pszPath ) ? KTgS_OK : KTgE_FAIL);
}


/* ---- tgIO_Directory_Exists ------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgBOOL tgIO_Directory_Exists( CPC_TgCHAR pszPath )
{
    WIN32_FILE_ATTRIBUTE_DATA           sFileData;

    if (0 == g_pfnGetFileAttributesEx( pszPath, GetFileExInfoStandard, &sFileData ))
        return (TgFALSE);
    return (0 != (0x10 & sFileData.dwFileAttributes) ? TgTRUE : TgFALSE);
}


/* ---- tgIO_Directory_Find_First -------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgRESULT tgIO_Directory_Find_First( P_TgCHAR pszName, C_TgSIZE nuiName, PP_TgVOID ppFind, CPC_TgCHAR pszPath )
{
    WIN32_FIND_DATA                     sFind_Data;
    HANDLE                              hFind;

    hFind = INVALID_HANDLE_VALUE;
    hFind = g_pfnFindFirstFile( pszPath, &sFind_Data );

    if (INVALID_HANDLE_VALUE == hFind)
    {
        return (KTgE_FAIL);
    };

    *ppFind = hFind;

    do
    {
        if (0 == tgSZ_Compare( sFind_Data.cFileName, TgT(".") ))
            continue;
        if (0 == tgSZ_Compare( sFind_Data.cFileName, TgT("..") ))
            continue;

        if (sFind_Data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
        {
            tgSZ_Copy( pszName, nuiName, sFind_Data.cFileName );
            return (KTgS_OK);
        }
    }
    while (g_pfnFindNextFile( hFind, &sFind_Data ) != 0);

    return (KTgE_FAIL);
}


/* ---- tgIO_Directory_Find_Next --------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgBOOL tgIO_Directory_Find_Next( P_TgCHAR pszName, C_TgSIZE nuiName, P_TgVOID pFind )
{
    WIN32_FIND_DATA                     sFind_Data;

    while (g_pfnFindNextFile( pFind, &sFind_Data ) != 0)
    {
        if (sFind_Data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
        {
            tgSZ_Copy( pszName, nuiName, sFind_Data.cFileName );
            return (TgTRUE);
        };
    };

    return (TgFALSE);
}


/* ---- tgIO_Directory_Find_Close -------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgVOID tgIO_Directory_Find_Close( P_TgVOID pFind )
{
    g_pfnFindClose( pFind );
}


/* ---- tgIO_File_Delete ------------------------------------------------------------------------------------------------------------------------------------------------ */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgRESULT tgIO_File_Delete( CPC_TgCHAR pszFile )
{
#if !defined(TgCOMPILE_PLATFORM_UWP)
    return (g_pfnDeleteFile( pszFile ) ? KTgS_OK : KTgE_FAIL);
#else
    return (KTgE_FAIL);
#endif
}


/* ---- tgIO_File_Copy -------------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgRESULT tgIO_File_Copy( CPC_TgCHAR pszExisting, CPC_TgCHAR pszDest )
{
#if !defined(TgCOMPILE_PLATFORM_UWP)
    return (g_pfnCopyFile( pszExisting, pszDest, TgTRUE ) ? KTgS_OK : KTgE_FAIL);
#else
    return ((0 == g_pfnCopyFile2( pszExisting, pszDest, nullptr )) ? KTgS_OK : KTgE_FAIL);
#endif
}


/* ---- tgIO_File_Move -------------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgRESULT tgIO_File_Move( CPC_TgCHAR pszExisting, CPC_TgCHAR pszDest )
{
    return (g_pfnMoveFileEx( pszExisting, pszDest, 0xA ) ? KTgS_OK : KTgE_FAIL);
}


/* ---- tgIO_File_Exists ------------------------------------------------------------------------------------------------------------------------------------------------ */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgBOOL tgIO_File_Exists( CPC_TgCHAR pszFile )
{
    WIN32_FILE_ATTRIBUTE_DATA           sFileData;

    if (0 == g_pfnGetFileAttributesEx( pszFile, GetFileExInfoStandard, &sFileData ))
        return (TgFALSE);
    return (0 == (0x10 & sFileData.dwFileAttributes) ? TgTRUE : TgFALSE);
}


/* ---- tgIO_File_Find_First -------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgRESULT tgIO_File_Find_First( P_TgCHAR pszName, C_TgSIZE nuiName, PP_TgVOID ppFind, CPC_TgCHAR pszPath )
{
    WIN32_FIND_DATA                     sFind_Data;
    HANDLE                              hFind;

    hFind = INVALID_HANDLE_VALUE;
    hFind = g_pfnFindFirstFile( pszPath, &sFind_Data );

    if (INVALID_HANDLE_VALUE == hFind)
    {
        return (KTgE_FAIL);
    };

    *ppFind = hFind;

    do
    {
        if (0 == (sFind_Data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
        {
            tgSZ_Copy( pszName, nuiName, sFind_Data.cFileName );
            return (KTgS_OK);
        }
    } while (g_pfnFindNextFile( hFind, &sFind_Data ) != 0);

    return (KTgE_FAIL);
}


/* ---- tgIO_File_Find_Next --------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgBOOL tgIO_File_Find_Next( P_TgCHAR pszName, C_TgSIZE nuiName, P_TgVOID pFind )
{
    WIN32_FIND_DATA                     sFind_Data;

    while (g_pfnFindNextFile( pFind, &sFind_Data ) != 0)
    {
        if (0 == (sFind_Data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
        {
            tgSZ_Copy( pszName, nuiName, sFind_Data.cFileName );
            return (TgTRUE);
        };
    };

    return (TgFALSE);
}


/* ---- tgIO_File_Find_Close -------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgVOID tgIO_File_Find_Close( P_TgVOID pFind )
{
    g_pfnFindClose( pFind );
}




/* == Synchronization Functions ========================================================================================================================================= */

/* ---- tgCM_MP_CS_Init ------------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgRESULT tgCM_MP_CS_Init( PCU_STg1_MP_CS psCS )
{
    psCS->m_pcsTimeLock = _aligned_malloc( sizeof( RTL_CRITICAL_SECTION ), TgCOMPILE_CACHE_LINE_SIZE );
    if (nullptr == psCS->m_pcsTimeLock)
    {
        return (KTgE_FAIL);
    }
    g_pfnInitializeCriticalSectionEx( psCS->m_pcsTimeLock, 0, 0 );
    return (KTgS_OK);
}


/* ---- tgCM_MP_CS_Free ------------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgVOID tgCM_MP_CS_Free( PCU_STg1_MP_CS psCS )
{
    g_pfnDeleteCriticalSection( psCS->m_pcsTimeLock );
    _aligned_free( psCS->m_pcsTimeLock );
    psCS->m_pcsTimeLock = nullptr;
}




/* == DLL Functions ===================================================================================================================================================== */

/* ---- tgGB_Load_Lib --------------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgBOOL tgGB_Load_Lib( TgMS_HINSTANCE *phLib, CPCU_TgCHAR pszFileName )
{
    STg2_Output                         tgOutCon;

    TgPARAM_CHECK((nullptr != pszFileName) && (0 != pszFileName[0]));

    tgOutCon.m_pfnWrite = tgPM_DBG_ERR_Write;
    tgOutCon.m_pfnClose = nullptr;

#if !defined(TgCOMPILE_PLATFORM_UWP)
    if (nullptr == (*phLib = LoadLibrary( pszFileName )))
#else
    if (nullptr == (*phLib = LoadPackagedLibrary( pszFileName, 0 )))
#endif
    {
        tgIO_PrintF( &tgOutCon, TgT("%-16.16s(%-32.32s): Unable to load %s.\n"), TgT("Global"), TgT("Platform"), pszFileName );
        return (TgFALSE);
    };

    return (TgTRUE);
}


/* ---- tgGB_Free_Lib --------------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgVOID tgGB_Free_Lib( TgMS_HINSTANCE *phLib )
{
    TgPARAM_CHECK(phLib);

    if (nullptr != *phLib)
    {
        FreeLibrary( *phLib );
        *phLib = nullptr;
    };
}


/* ---- tgGB_Get_Func_Address ------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgBOOL tgGB_Get_Func_Address( TgMS_FARPROC *pfnFunc, TgMS_HINSTANCE hLib, CPCU_TgANSICHAR pszFuncName )
{
    STg2_Output                         tgOutCon;

    TgPARAM_CHECK((nullptr != pszFuncName) && (0 != pszFuncName[0]));

    tgOutCon.m_pfnWrite = tgPM_DBG_ERR_Write;
    tgOutCon.m_pfnClose = nullptr;

    if (nullptr == (*pfnFunc = GetProcAddress( hLib, pszFuncName )))
    {
    #if defined(TgCOMPILE_WIDE_CHAR)
        TgCHAR                              szBuffer[1024];
        CP_TgCHAR                           pszOutFuncName;

        pszOutFuncName = szBuffer;
        tgSZ_AnsiToWideChar( szBuffer, 1024, pszFuncName, 1023 );
    /*# defined(TgCOMPILE_WIDE_CHAR) */
    #else
        CP_TgCHAR                           pszOutFuncName;

        pszOutFuncName = pszFuncName;
    /*# defined(TgCOMPILE_WIDE_CHAR) */
    #endif
        tgIO_PrintF(
            &tgOutCon, TgT("%-16.16s(%-32.32s): Unable to find function %s.\n"), TgT("Global"), TgT("Platform"), pszOutFuncName );
        return (TgFALSE);
    };

    return (TgTRUE);
}




/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */
/*  File Local Functions                                                                                                                                                  */
/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */

/* ---- tgTR_Thread_Set_Name -------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
#if TgCOMPILE_THREAD
static TgVOID tgTR_Thread_Set_Name( C_TgSINT32 iIndex )
{
#if defined(TgCOMPILE_WIDE_CHAR)
    TgANSICHAR                          szNameAnsi[1024];
#endif
#pragma pack(push,4)
    struct
    {
        TgUINT32                            dwType; /* Must be 0x1000. */
        CP_TgANSICHAR                       szName; /* Pointer to name (in user addr space). */
        TgUINT32                            dwThreadID; /* Thread ID (-1=caller thread). */
        TgUINT32                            dwFlags; /* Reserved for future use, must be zero. */
    }                                   tgName_Data;
#pragma pack(pop)

    g_auiTR_Thread_Stack[iIndex] = tgTR_Stack_Size();

#if TgCOMPILE_THREAD_INFO
    #if defined(TgCOMPILE_WIDE_CHAR)
        tgSZ_ToAnsiChar( szNameAnsi, TgARRAY_COUNT( szNameAnsi ), g_aszTR_Name[iIndex], tgSZ_Length( g_aszTR_Name[iIndex] ) );
        tgName_Data.szName = szNameAnsi;
    #else 
        tgName_Data.szName = g_aszTR_Name[iIndex];
    #endif
/*# TgCOMPILE_THREAD_INFO */
#else
    tgName_Data.szName = nullptr;
/*# TgCOMPILE_THREAD_INFO */
#endif

    tgName_Data.dwType = 0x1000;
    tgName_Data.dwThreadID = g_pfnGetCurrentThreadId();
    tgName_Data.dwFlags = 0;

    g_pfnSleep( 10 );
    TgERROR( (TgUINT32)s_auiTR_Thread_OS_Id[iIndex] == g_pfnGetCurrentThreadId() ); /* OS race condition for the win - sometimes not flushed */

    __try
    {
        g_pfnRaiseException( 0x406D1388, 0, sizeof( tgName_Data ) / sizeof( TgUINTPTR ), (P_TgVOID)&tgName_Data );
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        (void)0;
    };
}

/*# TgCOMPILE_THREAD */
#endif


/* ---- tgTR_Thread_Start ----------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
#if TgCOMPILE_THREAD
static unsigned long __stdcall tgTR_Thread_Start( void* pParam )
{
    TgSINTPTR                           iParam;
    P_TgVOID                            hThread;
    TgUINT32                            uiRet = 1;
    TgSINT32                            iIndex;

    iParam = (TgSINTPTR)pParam;
    TgERROR(iParam >= KTgMIN_S32 && iParam <= KTgMAX_S32);
    iIndex = (TgSINT32)iParam;

    tgTR_Thread_Set_Name( iIndex );
    hThread = g_pfnGetCurrentThread();

    switch (g_aenTR_Thread_Priority[iIndex])
    {
        case ETgTHREAD_PRIORITY__CRITICAL:
            TgVERIFY( 0 != g_pfnSetThreadPriority( hThread, 15 ) );
            break;
        case ETgTHREAD_PRIORITY__HIGHEST:
            TgVERIFY( 0 != g_pfnSetThreadPriority( hThread, 2 ) );
            break;
        case ETgTHREAD_PRIORITY__HIGH:
            TgVERIFY( 0 != g_pfnSetThreadPriority( hThread, 1 ) );
            break;
        case ETgTHREAD_PRIORITY__NORMAL:
            TgVERIFY( 0 != g_pfnSetThreadPriority( hThread, 0 ) );
            break;
        case ETgTHREAD_PRIORITY__LOW:
            TgVERIFY( 0 != g_pfnSetThreadPriority( hThread, -1 ) );
            break;
        case ETgTHREAD_PRIORITY__LOWEST:
            TgVERIFY( 0 != g_pfnSetThreadPriority( hThread, -2 ) );
            break;
        case ETgTHREAD_PRIORITY__INVALID:
        default:
            TgS_NO_DEFAULT(break);
    };

    tls_iThread_Index = iIndex;

    uiRet = g_afnTR_Thread_Function[iIndex]( g_auiTR_Thread_Param[iIndex] );

    g_pfnExitThread( uiRet );
    return (uiRet);
}

/*# TgCOMPILE_THREAD */
#endif


MSVC_WARN_DISABLE_POP( 4770 )