static gctBOOL
ReadSource(
    IN gcoOS Os,
    IN gctCONST_STRING FileName,
    OUT gctSIZE_T * SourceSize,
    OUT gctSTRING * Source
    )
{
    gceSTATUS   status;
    gctFILE     file;
    gctUINT32   count;
    gctSIZE_T   byteRead;
    gctSTRING   source;

    gcmASSERT(FileName);

    status = gcoOS_Open(Os, FileName, gcvFILE_READ, &file);

    if (gcmIS_ERROR(status))
    {
        printf("*ERROR* Failed to open input file: %s\n", FileName);
        return gcvFALSE;
    }

    gcmVERIFY_OK(gcoOS_Seek(Os, file, 0, gcvFILE_SEEK_END));
    gcmVERIFY_OK(gcoOS_GetPos(Os, file, &count));

    status = gcoOS_Allocate(Os, count + 1, (gctPOINTER *) &source);
    if (!gcmIS_SUCCESS(status))
    {
        printf("*ERROR* Not enough memory\n");
        gcmVERIFY_OK(gcoOS_Close(Os, file));
        return gcvFALSE;
    }

    gcmVERIFY_OK(gcoOS_SetPos(Os, file, 0));
    status = gcoOS_Read(Os, file, count, source, &byteRead);
    if (!gcmIS_SUCCESS(status) || byteRead != count)
    {
        printf("*ERROR* Failed to open input file: %s\n", FileName);
        gcmVERIFY_OK(gcoOS_Close(Os, file));
        return gcvFALSE;
    }

    source[count] = '\0';
    gcmVERIFY_OK(gcoOS_Close(Os, file));

    *SourceSize = count;
    *Source = source;
    return gcvTRUE;
}
static gctBOOL
OutputShaderData(
    IN gcoOS Os,
    IN gctCONST_STRING FileName,
    IN gctSIZE_T Size,
    IN gctCONST_STRING Data
    )
{
    gceSTATUS   status;
    gctSTRING   dataFileName;
    gctSIZE_T   length;
    gctFILE     file;

    gcmASSERT(FileName);

    gcmVERIFY_OK(gcoOS_StrLen(FileName, &length));
    length += 6;
    gcmVERIFY_OK(gcoOS_Allocate(Os, length, (gctPOINTER *) &dataFileName));
    gcmVERIFY_OK(gcoOS_StrCopySafe(dataFileName, length, FileName));
    gcmVERIFY_OK(gcoOS_StrCatSafe(dataFileName, length, ".gcSL"));

    status = gcoOS_Open(Os, dataFileName, gcvFILE_CREATE, &file);

    if (gcmIS_ERROR(status))
    {
        gcoOS_Free(Os, dataFileName);
        printf("*ERROR* Failed to open the data file: %s\n", dataFileName);
        return gcvFALSE;
    }
    gcoOS_Free(Os, dataFileName);

    gcmASSERT(file);
    status = gcoOS_Write(Os, file, Size, Data);

    if (!gcmIS_SUCCESS(status))
    {
        printf("*ERROR* Failed to write the data file: %s\n", dataFileName);
        goto ErrorExit;
    }

    gcmVERIFY_OK(gcoOS_Close(Os, file));
    return gcvTRUE;

ErrorExit:
    gcmVERIFY_OK(gcoOS_Close(Os, file));
    return gcvFALSE;
}
gceSTATUS
gcoPROFILER_Initialize(
	IN gcoHAL Hal
	)
{
    gceSTATUS status = gcvSTATUS_OK;
    char* fileName;
    char* filter = gcvNULL;
    gctSTRING portName;
    gctINT port;
	gcsHAL_INTERFACE iface;

    gcmHEADER();

    /* Check if already initialized. */
   	if (gcPLS.hal->profiler.enable)
    {
        gcPLS.hal->profiler.enable++;
        gcmFOOTER();
        return status;
    }

	/* Get profile setting. */
	iface.command = gcvHAL_GET_PROFILE_SETTING;

	/* Call the kernel. */
	status = gcoOS_DeviceControl(gcvNULL,
								 IOCTL_GCHAL_INTERFACE,
								 &iface, gcmSIZEOF(iface),
								 &iface, gcmSIZEOF(iface));

	if (gcmIS_ERROR(status) || !iface.u.GetProfileSetting.enable)
	{
    	gcPLS.hal->profiler.enable = 0;
        status = gcvSTATUS_GENERIC_IO;

        gcmFOOTER();
        return status;
	}

	gcmVERIFY_OK(gcoOS_ZeroMemory(&gcPLS.hal->profiler, gcmSIZEOF(gcPLS.hal->profiler)));

    gcoOS_GetEnv(gcvNULL,
                 "VP_COUNTER_FILTER",
                 &filter);

    /* Enable/Disable specific counters. */
    if ((filter != gcvNULL))
    {
        gctSIZE_T bitsLen;
        gcoOS_StrLen(filter, &bitsLen);
        if (bitsLen > 2)
        {
            gcPLS.hal->profiler.enableHal = (filter[2] == '1');
        }
        else
        {
            gcPLS.hal->profiler.enableHal = gcvTRUE;
        }

        if (bitsLen > 3)
        {
            gcPLS.hal->profiler.enableHW = (filter[3] == '1');
        }
        else
        {
            gcPLS.hal->profiler.enableHW = gcvTRUE;
        }

        if (bitsLen > 8)
        {
            gcPLS.hal->profiler.enableSH = (filter[8] == '1');
        }
        else
        {
            gcPLS.hal->profiler.enableSH = gcvTRUE;
        }
    }
    else
    {
        gcPLS.hal->profiler.enableHal = gcvTRUE;
        gcPLS.hal->profiler.enableHW = gcvTRUE;
        gcPLS.hal->profiler.enableSH = gcvTRUE;
    }

	gcoOS_GetEnv(gcvNULL,
				 "VPROFILER_OUTPUT",
				 &fileName);

    gcPLS.hal->profiler.useSocket = gcvFALSE;
	if (fileName && *fileName != '\0' && *fileName != ' ')
	{
        /* Extract port info. */
        gcoOS_StrFindReverse(fileName, ':', &portName);

        if (portName)
        {
            gcoOS_StrToInt(portName + 1, &port);

            if (port > 0)
            {
                /*status = gcoOS_Socket(gcvNULL, AF_INET, SOCK_STREAM, 0, &gcPLS.hal->profiler.sockFd);*/
                status = gcoOS_Socket(gcvNULL, 2, 1, 0, &gcPLS.hal->profiler.sockFd);

                if (gcmIS_SUCCESS(status))
                {
                    *portName = '\0';
                    status = gcoOS_Connect(gcvNULL,
                            gcPLS.hal->profiler.sockFd, fileName, port);
                    *portName = ':';

                    if (gcmIS_SUCCESS(status))
	                {
                        gcPLS.hal->profiler.useSocket = gcvTRUE;
                    }
                }
            }
        }
	}
    else
    {
		fileName = iface.u.GetProfileSetting.fileName;
    }

    if (! gcPLS.hal->profiler.useSocket)
	{
		status = gcoOS_Open(gcvNULL,
							fileName,
#ifdef gcdNEW_PROFILER_FILE
							gcvFILE_CREATE,
#else
							gcvFILE_CREATETEXT,
#endif
							&gcPLS.hal->profiler.file);
	}

    if (gcmIS_ERROR(status))
	{
    	gcPLS.hal->profiler.enable = 0;
        status = gcvSTATUS_GENERIC_IO;

        gcmFOOTER();
        return status;
	}

    gcPLS.hal->profiler.enable = 1;
	gcoOS_GetTime(&gcPLS.hal->profiler.frameStart);
	gcPLS.hal->profiler.frameStartTimeusec = gcPLS.hal->profiler.frameStart;
	gcPLS.hal->profiler.prevVSInstCount = 0;
	gcPLS.hal->profiler.prevVSBranchInstCount = 0;
	gcPLS.hal->profiler.prevVSTexInstCount = 0;
	gcPLS.hal->profiler.prevVSVertexCount = 0;
	gcPLS.hal->profiler.prevPSInstCount = 0;
	gcPLS.hal->profiler.prevPSBranchInstCount = 0;
	gcPLS.hal->profiler.prevPSTexInstCount = 0;
	gcPLS.hal->profiler.prevPSPixelCount = 0;

#if gcdNEW_PROFILER_FILE
    gcmWRITE_CONST(VPHEADER);
    gcmWRITE_BUFFER(4, "VP12");
#else
	gcmWRITE_STRING("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<VProfile>\n");
#endif

    /* Success. */
    gcmFOOTER();
    return status;
}
/*******************************************************************************
**
**  gcoDUMP_Control
**
**  Control dumping.
**
**  INPUT:
**
**      gcoDUMP Dump
**          Pointer to a gcoDUMP object.
**
**      gctSTRING FileName
**          If 'FileName' is not gcvNULL, it points to the filename to be used for
**          capturing all data.  If 'FileName' is gcvNULL, turn off any current
**          capturing.
**
**  OUTPUT:
**
**      Nothing.
*/
gceSTATUS
gcoDUMP_Control(
    IN gcoDUMP Dump,
    IN gctSTRING FileName
    )
{
    gceSTATUS status = gcvSTATUS_OK;
    gcsDUMP_FILE header;
    gctUINT32 pos;

    gcmHEADER_ARG("Dump=0x%x FileName=0x%x", Dump, FileName);

    /* Verify the arguments. */
    gcmVERIFY_OBJECT(Dump, gcvOBJ_DUMP);

    do
    {
        if (FileName != gcvNULL)
        {
            /* Need to create a new dump file. */
            if (Dump->file == gcvNULL)
            {
                /* Create the dump file. */
                gcmERR_BREAK(gcoOS_Open(gcvNULL,
                                    FileName,
                                    gcvFILE_CREATE,
                                    &Dump->file));

                /* Write the file header. */
                header.signature   = gcvDUMP_FILE_SIGNATURE;
                header.length      = Dump->length     = 0;
                header.frames      = Dump->frameCount = 0;

                gcmERR_BREAK(gcoOS_Write(gcvNULL,
                                     Dump->file,
                                     sizeof(header),
                                     &header));

                /* Frame is not yet started. */
                Dump->frameStart = 0;
            }
        }
        else
        {
            /* Need to close any current dump file. */
            if (Dump->file != gcvNULL)
            {
                /* Close any open frame. */
                if (Dump->frameStart != 0)
                {
                    gcoDUMP_FrameEnd(Dump);
                    gcoDUMP_FrameBegin(Dump);
                }

                /* Get the current position. */
                gcmERR_BREAK(gcoOS_GetPos(gcvNULL, Dump->file, &pos));

                /* Seek to the beginnnig of the file. */
                gcmERR_BREAK(gcoOS_SetPos(gcvNULL, Dump->file, 0));

                /* Make sure we have the correct size. */
                gcmASSERT(pos == Dump->length + sizeof(header));

                /* Update the file header. */
                header.signature = gcvDUMP_FILE_SIGNATURE;
                header.length    = Dump->length;
                header.frames    = Dump->frameCount;

                gcmERR_BREAK(gcoOS_Write(gcvNULL,
                                     Dump->file,
                                     sizeof(header),
                                     &header));

                /* Seek to the end of the file. */
                gcmERR_BREAK(gcoOS_SetPos(gcvNULL, Dump->file, pos));

                /* Close the file. */
                gcmERR_BREAK(gcoOS_Close(gcvNULL, Dump->file));

                /* Mark the file as closed. */
                Dump->file = gcvNULL;
            }
        }
    }
    while (gcvFALSE);

    /* Return the status. */
    gcmFOOTER();
    return status;
}
static gcSHADER
CompileFile(
    IN gcoOS Os,
    IN gctCONST_STRING FileName,
    IN gcoHAL Hal,
    IN gctUINT Option,
    IN gctBOOL DumpLog,
    IN gctBOOL DumpCompiledShader
    )
{
    gceSTATUS   status;
    gctINT      shaderType;
    gctSIZE_T   sourceSize;
    gctSTRING   source          = gcvNULL;
    gcSHADER    binary;
    gctSTRING   log             = gcvNULL;
    gctSIZE_T   bufferSize;
    gctSTRING   buffer;
    gctSTRING   dataFileName    = gcvNULL;
    gctSIZE_T   length;
    gctFILE     logFile         = gcvNULL;

    gcmASSERT(FileName);

    shaderType = GetShaderType(FileName);

    if (!ReadSource(Os, FileName, &sourceSize, &source)) return gcvNULL;

    status = gcCompileShader(Hal,
                             shaderType,
                             sourceSize, source,
                             &binary,
                             &log);

    if (log != gcvNULL)
    {
        printf("<LOG>\n");
        printf("%s", log);
        printf("</LOG>\n");
    }

    if (gcmIS_ERROR(status))
    {
        gcmASSERT(binary == gcvNULL);
        binary = gcvNULL;

        printf("*ERROR* Failed to compile %s (error: %d)\n", FileName, status);
        goto Exit;
    }

    gcmASSERT(binary != gcvNULL);

    if (DumpLog)
    {
        gcmVERIFY_OK(gcoOS_StrLen(FileName, &length));
        length += 5;
        gcmVERIFY_OK(gcoOS_Allocate(Os, length, (gctPOINTER *) &dataFileName));
        gcmVERIFY_OK(gcoOS_StrCopySafe(dataFileName, length, FileName));
        gcmVERIFY_OK(gcoOS_StrCatSafe(dataFileName, length, ".log"));

        status = gcoOS_Open(Os, dataFileName, gcvFILE_CREATETEXT, &logFile);
        if (gcmIS_ERROR(status))
        {
            logFile = gcvNULL;

            printf("*ERROR* Failed to open the log file: %s\n", dataFileName);
        }
        gcoOS_Free(Os, dataFileName);
    }

    gcmVERIFY_OK(gcSHADER_SetOptimizationOption(binary, Option));

    status = gcOptimizeShader(binary, logFile);

    if (!gcmIS_SUCCESS(status))
    {
        printf("*ERROR* Failed to optimize %s (error: %d)\n", FileName, status);
    }

    if (logFile != gcvNULL)
    {
        gcmVERIFY_OK(gcoOS_Close(Os, logFile));
    }

    if (DumpCompiledShader)
    {
        status = gcSHADER_Save(binary, gcvNULL, &bufferSize);
        if (gcmIS_ERROR(status))
        {
            printf("*ERROR* Failed to get the buffer size of the shader\n");
            goto Exit;
        }

        status = gcoOS_Allocate(Os, bufferSize, (gctPOINTER *) &buffer);
        if (!gcmIS_SUCCESS(status))
        {
            printf("*ERROR* Not enough memory\n");
            goto Exit;
        }

        status = gcSHADER_Save(binary, buffer, &bufferSize);
        if (status != gcvSTATUS_OK)
        {
            printf("*ERROR* Failed to get the buffer size of the shader\n");
            gcoOS_Free(Os, buffer);
            goto Exit;
        }

        OutputShaderData(Os, FileName, bufferSize, buffer);

        gcoOS_Free(Os, buffer);
    }


Exit:
    if (DumpLog && log != gcvNULL)
    {
        printf("**************** Compile Log ****************");
        printf("%s", log);
        printf("*********************************************");
    }

    if (log != gcvNULL) gcoOS_Free(Os, log);

    if (source != gcvNULL) gcoOS_Free(Os, source);

    return binary;
}