//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ // //=============================================================================// #include #include #include "CardStats.h" //----------------------------------------------------------------------------- // Main stats loop //----------------------------------------------------------------------------- int main( int argc, char* argv[] ) { if ( argc != 3 ) // a unix style usage string { printf ( "Usage: \n" ); printf ( "Output file will be ..txt" ); return 0; } char* pszCardtype = argv[1]; // video card type we are looking for char* pszFilename = argv[2]; // file containing the data CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); int nFileSize = LoadFileIntoBuffer(buf, pszFilename); ParseHeader ( buf ); CUtlBuffer output ( 0, 255, CUtlBuffer::TEXT_BUFFER ); int binwidth=100; // video card stats over time //TimeSeriesVCard ( buf, output, pszCardtype, nFileSize, binwidth ); // cpu type stats over time //TimeSeriesCPU ( buf, output, pszCardtype, nFileSize, binwidth ); // CPU vs Videocard //ParseFile ( buf, output, pszCardtype, nFileSize, binwidth, 0); // CPU vs Memory //ParseFile2 ( buf, output, pszCardtype, nFileSize, binwidth ); //CPU vs network speed //ParseFile3 ( buf, output, pszCardtype, nFileSize, binwidth ); // histogram of video card distribution HistogramVidCards ( buf, output, pszCardtype, nFileSize, binwidth ); //HistogramNetSpeed ( buf, output, pszCardtype, nFileSize, binwidth ); //HistogramRam ( buf, output, pszCardtype, nFileSize, binwidth ); //HistogramCPU ( buf, output, pszCardtype, nFileSize, binwidth ); WriteOutputToFile ( output, pszFilename, pszCardtype ); return 0; } void HistogramCPU( CUtlBuffer &inbuf, CUtlBuffer &outbuf, char *pszSearchString, int size, int binwidth) { ParseFile ( inbuf, outbuf, "", size, binwidth, 1 ); } //----------------------------------------------------------------------------- // Reads entire file contents in to utlbuffer // input-buffer to load fileinto, filename // output-filesize //----------------------------------------------------------------------------- int LoadFileIntoBuffer(CUtlBuffer &buf, char *pszFilename) { // Open the file FILE *fh = fopen ( pszFilename, "rb" ); if (fh == NULL) { printf ("Unable to open datafile. Check path/name."); exit( 0 ); } fseek( fh, 0, SEEK_END ); int nFileSize = ftell( fh ); fseek ( fh, 0, SEEK_SET ); // Read the file in one gulp buf.EnsureCapacity( nFileSize ); int result=fread( buf.Base(), sizeof( char ), nFileSize, fh ); fclose( fh ); buf.SeekPut( CUtlBuffer::SEEK_HEAD, result ); return nFileSize; } //----------------------------------------------------------------------------- // Writes the contents of the utlbuffer to file // datafile name becomes name.card.txt for results //----------------------------------------------------------------------------- void WriteOutputToFile( CUtlBuffer &buf, char *pszFilename, char *pszCardtype ) { char pszOutFilename[500]; strncpy( pszOutFilename, pszFilename, strlen( pszFilename )-3 ); pszOutFilename[strlen(pszFilename)-3]='\0'; strcat ( pszOutFilename, pszCardtype ); strcat ( pszOutFilename, ".txt\0" ); FILE *fh = fopen ( pszOutFilename, "w" ); if ( fh == NULL ) { printf ( "Unable to open outputfile." ); exit(0); } fwrite ( buf.Base(), sizeof( char ), buf.TellPut(), fh ); fclose( fh ); } //----------------------------------------------------------------------------- // Strips off header fields from datafile //----------------------------------------------------------------------------- void ParseHeader( CUtlBuffer &buf ) { // remove first line of data (field labels) char pszTrash[256]; buf.Scanf( "%s ", pszTrash); buf.Scanf( "%s ", pszTrash); buf.Scanf( "%s ", pszTrash); buf.Scanf( "%s ", pszTrash); buf.Scanf( "%s ", pszTrash); buf.Scanf( "%s \n", pszTrash); } //----------------------------------------------------------------------------- // Counts number of users that have a given cpu bin speed for videocard string // // Parses buffer into fields and puts results into outbuf // size is size of the file read in //----------------------------------------------------------------------------- void ParseFile( CUtlBuffer &inbuf, CUtlBuffer &outbuf, char *pszSearchString, int size, int binwidth, int nofilter) { // Each bin is how many computers with the given card had cpus > cpubin // and less than the next bin so cpu 200 covers cpu's from 200 to 299 CUtlVector nCpuList; CUtlVector nQuantity; for ( int i=0; i <= 2400 ; i+=binwidth) // a reasonable cpu range { nCpuList.AddToTail(i); nQuantity.AddToTail(0); } int totalUsers=0; while ( inbuf.TellGet() < size ) { // Now parse the utlbuffer // file has fields of version, netspeed, ram, cpu and card type char pszVersion[300]; int nNetSpeed, nRam, nCpu; int nRead=inbuf.Scanf( "%s %d %d %d ", pszVersion, &nNetSpeed, &nRam, &nCpu ); char pszCard[300]; char chSentinel = ' '; int i=0; while (( chSentinel != '\n' )) { chSentinel = inbuf.GetChar(); pszCard[i] = chSentinel; ++i; } pszCard[i] = '\0'; if (nRead != 4) continue; // if card is our type if ( nofilter || Q_stristr ( pszCard, pszSearchString ) != NULL ) { InsertResult(nCpu, nCpuList, nQuantity); } totalUsers++; } if (nCpuList.Size()>65000) { printf ("Too many points, increase bin width\n"); } printf("TotalUsers %d\n", totalUsers); // write results to an output buffer int total=0; outbuf.Printf ( "Cpu\tQuantity\n" ); // headers for ( int i=0; i < nCpuList.Size(); ++i ) { outbuf.Printf ( "%d\t%d\n", nCpuList[i], nQuantity[i] ); total+=nQuantity[i]; } printf ("Users in this subset %d\n", total); printf ("Percent of dataset %.2f", ((float)total/(float)totalUsers)*100); } //----------------------------------------------------------------------------- // given cpu bin, plots number of users in a given memory bin // // Parses buffer into fields and puts results into outbuf // size is size of the file read in //----------------------------------------------------------------------------- #define NMEMBINS 6 //#define NCPUBINS 48 // binwidth 50 #define NCPUBINS 24 // binwidth 100 void ParseFile2( CUtlBuffer &inbuf, CUtlBuffer &outbuf, char *pszSearchString, int size, int binwidth) { binwidth=100; // Each bin is how many computers with the given card had cpus > cpubin // and less than the next bin so cpu 200 covers cpu's from 200 to 299 CUtlVector nCpuList; CUtlVector nMemList; int nQuantity[NMEMBINS][NCPUBINS]; for (int i=0; i < NMEMBINS; ++i) { for (int j=0; j< NCPUBINS; ++j) nQuantity[i][j]=0; } for ( int i=0; i <= 2400 ; i+=binwidth) // a reasonable cpu range { nCpuList.AddToTail(i); } nMemList.AddToTail(0); int basemem=64; while ( basemem < 2050 ) { nMemList.AddToTail(basemem); basemem<<=1; } int totalUsers=0; int maxram=0; while ( inbuf.TellGet() < size ) { // Now parse the utlbuffer // file has fields of version, netspeed, ram, cpu and card type char pszVersion[256]; int nNetSpeed, nRam, nCpu; inbuf.Scanf( "%s %d %d %d", pszVersion, &nNetSpeed, &nRam, &nCpu ); // scan through the rest of the junk char chSentinel = ' '; int i=0; while (( chSentinel != '\n' )) { chSentinel = inbuf.GetChar(); ++i; } // determine cpu bin if (nCpu < 0) // handle corrupt data continue; // handle outliers--bin is off the scale if ( nCpu > nCpuList[nCpuList.Size()-1] ) continue; i=0; while ( nCpu > nCpuList[i] ) { ++i; } int cpuIndex = i-1; assert(i nMemList[nMemList.Size()-1] ) continue; i=0; while ( nRam > nMemList[i] ) { ++i; } int memIndex = i-1; assert(i maxram) maxram=nRam; } if (nCpuList.Size()>65000) { printf ("Too many points, increase bin width\n"); } printf("TotalUsers %d\n", totalUsers); printf("Max ram %d\n", maxram); // write results to an output buffer outbuf.Printf ( "Cpu" ); // headers for (int j=1; j < nMemList.Size(); ++j) outbuf.Printf ("\tMemoryBin%d", nMemList[j]); outbuf.Printf ("\n"); for ( int i=0; i < NCPUBINS; ++i ) { outbuf.Printf ( "%d", nCpuList[i]); for (int j=0; j < NMEMBINS; ++j) outbuf.Printf ("\t%d", nQuantity[j][i]); outbuf.Printf ("\n"); } } //----------------------------------------------------------------------------- // given cpu bin, plots number of users in a given network speed bin // // Parses buffer into fields and puts results into outbuf // size is size of the file read in //----------------------------------------------------------------------------- #define NNETBINS 9 void ParseFile3( CUtlBuffer &inbuf, CUtlBuffer &outbuf, char *pszSearchString, int size, int binwidth) { binwidth=100; // Each bin is how many computers with the given card had cpus > cpubin // and less than the next bin so cpu 200 covers cpu's from 200 to 299 CUtlVector nCpuList; CUtlVector nNetList; int nQuantity[NNETBINS][NCPUBINS]; for (int i=0; i < NNETBINS; ++i) { for (int j=0; j< NCPUBINS; ++j) nQuantity[i][j]=0; } for (int i=0; i <= 2400 ; i+=binwidth) // a reasonable cpu range { nCpuList.AddToTail(i); } nNetList.AddToTail(0); nNetList.AddToTail(28); nNetList.AddToTail(33); nNetList.AddToTail(56); nNetList.AddToTail(112); nNetList.AddToTail(256); nNetList.AddToTail(512); nNetList.AddToTail(1000); nNetList.AddToTail(2000); int totalUsers=0; int maxspeed=0; while ( inbuf.TellGet() < size ) { // Now parse the utlbuffer // file has fields of version, netspeed, ram, cpu and card type char pszVersion[256]; int nNetSpeed, nRam, nCpu; inbuf.Scanf( "%s %d %d %d", pszVersion, &nNetSpeed, &nRam, &nCpu ); // scan through the rest of the junk char chSentinel = ' '; int i=0; while (( chSentinel != '\n' )) { chSentinel = inbuf.GetChar(); ++i; } // determine cpu bin if (nCpu < 0) // handle corrupt data continue; // handle outliers--bin is off the scale if ( nCpu > nCpuList[nCpuList.Size()-1] ) continue; i=0; while ( nCpu > nCpuList[i] ) { ++i; } int cpuIndex = i-1; assert(i nNetList[nNetList.Size()-1] ) continue; i=0; while ( nNetSpeed > nNetList[i] ) { ++i; } int netIndex = i; assert(i maxspeed) maxspeed=nNetSpeed; } if (nCpuList.Size()>65000) { printf ("Too many points, increase bin width\n"); } printf("TotalUsers %d\n", totalUsers); printf("Max ram %d\n", maxspeed); // write results to an output buffer outbuf.Printf ( "Cpu" ); // headers for (int j=0; j < nNetList.Size()-1; ++j) outbuf.Printf ("\t%d-%d", nNetList[j], nNetList[j+1]); outbuf.Printf ("\n"); for (int i=0; i < NCPUBINS; ++i ) { outbuf.Printf ( "%d", nCpuList[i]); for (int j=0; j < NNETBINS; ++j) outbuf.Printf ("\t%d", nQuantity[j][i]); outbuf.Printf ("\n"); } } //----------------------------------------------------------------------------- // given time bin, plots number of users in a given cpu TYPE over time, // as the numbers came into the server // // Parses buffer into fields and puts results into outbuf // size is size of the file read in //----------------------------------------------------------------------------- #define CPUBINS 3 // 0=AMD, 1=Intel, 2=Unknown #define MAXUSERS 750000 void TimeSeriesCPU( CUtlBuffer &inbuf, CUtlBuffer &outbuf, char *pszSearchString, int size, int binwidth) { binwidth=600; // unequally sized bins // Each bin is how many computers with the given card had cpus > cpubin // and less than the next bin so cpu 200 covers cpu's from 200 to 299 CUtlVector nTimeList; int nQuantity[30][CPUBINS]; for (int i=0; i < 30; ++i) { for (int j=0; j< CPUBINS; ++j) nQuantity[i][j]=0; } nTimeList.AddToTail(0); int bin=10000; int i=bin; for (i; i<=MAXUSERS; i=bin) { nTimeList.AddToTail(i); bin<<=1; } nTimeList.AddToTail(i); // for unequal bins int currentTimeBin=1; int totalUsers=0; while ( (inbuf.TellGet() < size) ) { // Now parse the utlbuffer // file has fields of version, netspeed, ram, cpu and card type char pszVersion[256]; int nNetSpeed, nRam, nCpu; inbuf.Scanf( "%s %d %d %d", pszVersion, &nNetSpeed, &nRam, &nCpu ); char pszCard[300]; char chSentinel = ' '; int i=0; while (( chSentinel != '\n' )) { chSentinel = inbuf.GetChar(); pszCard[i] = chSentinel; ++i; if (i >= 300) break; } // check for those blasted hackers if (i>= 300) { // read rest while (( chSentinel != '\n' )) { chSentinel = inbuf.GetChar(); } continue; } pszCard[i] = '\0'; int cpuIndex=2; // default is unknown if ( Q_stristr ( pszCard, "SSE" ) != NULL ) { cpuIndex=1; // intel } else if ( Q_stristr ( pszCard, "KNI" ) != NULL ) { cpuIndex=1; // intel } else if ( Q_stristr ( pszCard, "3DNOW" ) != NULL ) { cpuIndex=0; } // update nTimeList bin if necessary if (nTimeList[currentTimeBin] <= totalUsers) { currentTimeBin++; } nQuantity[currentTimeBin][cpuIndex]++; assert(nQuantity[currentTimeBin][cpuIndex]>0); totalUsers++; } printf("TotalUsers %d\n", totalUsers); // write results to an output buffer outbuf.Printf ( "CPU" ); // headers for (int j=1; j < nTimeList.Size(); ++j) outbuf.Printf ("\tUsersSoFar%d", nTimeList[j]); outbuf.Printf ("\n"); for (int i=0; i < CPUBINS; ++i ) { outbuf.Printf ( "-"); for (int j=1; j < nTimeList.Size(); ++j) // use NTIMEBINS for equal sized bins outbuf.Printf ("\t%d", nQuantity[j][i]); outbuf.Printf ("\n"); } } //----------------------------------------------------------------------------- // given time bin, plots number of users in a given video card TYPE over time, // as the numbers came into the server // // Parses buffer into fields and puts results into outbuf // size is size of the file read in //----------------------------------------------------------------------------- // 0=RIVA TNT // 1=GeForce2 MX // 2=Microsoft Corporation GDI Generic // 3=GeForce2 GTS // 4=Voodoo 3 // 5=Intel 810 // 6=GeForce3 #define CARDBINS 8 void TimeSeriesVCard( CUtlBuffer &inbuf, CUtlBuffer &outbuf, char *pszSearchString, int size, int binwidth) { // unequally sized bins CUtlVector nTimeList; int nQuantity[30][CARDBINS]; for (int i=0; i < 30; ++i) { for (int j=0; j< CARDBINS; ++j) nQuantity[i][j]=0; } nTimeList.AddToTail(0); int bin=10000; int i=bin; for (i; i<=MAXUSERS; i=bin) { nTimeList.AddToTail(i); bin<<=1; } nTimeList.AddToTail(i); //int currentTimeBin=1; // for unequal bins int currentTimeBin=1; int numTimeBins=nTimeList.Size(); int totalUsers=0; while ( (inbuf.TellGet() < size) ) { // Now parse the utlbuffer // file has fields of version, netspeed, ram, cpu and card type char pszVersion[256]; int nNetSpeed, nRam, nCpu; inbuf.Scanf( "%s %d %d %d", pszVersion, &nNetSpeed, &nRam, &nCpu ); char pszCard[300]; char chSentinel = ' '; int i=0; while (( chSentinel != '\n' )) { chSentinel = inbuf.GetChar(); pszCard[i] = chSentinel; ++i; if (i >= 300) break; } // check for those blasted hackers if (i>= 300) { // read rest while (( chSentinel != '\n' )) { chSentinel = inbuf.GetChar(); } continue; } pszCard[i] = '\0'; // 0=RIVA TNT // 1=GeForce2 MX // 2=Microsoft Corporation GDI Generic // 3=GeForce2 GTS // 4=Voodoo 3 // 5=Intel 810 // 6=GeForce3 // 7=All others int cardIndex=7; // default is other if ( Q_stristr ( pszCard, "RIVA TNT2" ) != NULL ) { //printf ("%s", pszCard); cardIndex=0; } else if ( Q_stristr ( pszCard, "GeForce2 MX" ) != NULL ) { cardIndex=1; } else if ( Q_stristr ( pszCard, "Microsoft Corporation GDI Generic" ) != NULL ) { cardIndex=2; } else if ( Q_stristr ( pszCard, "GeForce2 GTS" ) != NULL ) { cardIndex=3; } else if ( Q_stristr ( pszCard, "Voodoo3" ) != NULL ) { cardIndex=4; } else if ( Q_stristr ( pszCard, "Intel 810" ) != NULL ) { cardIndex=5; } else if ( Q_stristr ( pszCard, "GeForce3" ) != NULL ) { cardIndex=6; } // update nTimeList bin if necessary if (nTimeList[currentTimeBin] <= totalUsers) { currentTimeBin++; } nQuantity[currentTimeBin][cardIndex]++; totalUsers++; } printf("TotalUsers %d\n", totalUsers); // write results to an output buffer outbuf.Printf ( "Video Card" ); // headers for (int j=1; j < nTimeList.Size(); ++j) outbuf.Printf ("\tUsersSoFar%d", nTimeList[j]); outbuf.Printf ("\n"); for (int i=0; i < CARDBINS; ++i ) { outbuf.Printf ( "-"); for (int j=1; j < numTimeBins; ++j) // use NTIMEBINS for equal sized bins outbuf.Printf ("\t%d", nQuantity[j][i]); outbuf.Printf ("\n"); } } //----------------------------------------------------------------------------- // Increment the proper bin's counter //----------------------------------------------------------------------------- void InsertResult( int nCpu, CUtlVector &nCpuList, CUtlVector &nQuantity ) { if (nCpu < 0) // handle corrupt data return; // handle outliers--bin is off the scale if ( nCpu > nCpuList[nCpuList.Size()-1] ) { //nCpuList.AddToTail( nCpu ); //nQuantity.AddToTail( 1 ); return; } int i=0; while ( nCpu > nCpuList[i] ) { ++i; } nQuantity[i-1]++; } void HistogramVidCards( CUtlBuffer &inbuf, CUtlBuffer &outbuf, char *pszSearchString, int size, int binwidth) { #define CARDHISTBINS 15 // unequally sized bins int nQuantity[CARDHISTBINS]; for (int j=0; j< CARDHISTBINS; ++j) nQuantity[j]=0; int totalUsers=0; while ( (inbuf.TellGet() < size) ) { // Now parse the utlbuffer // file has fields of version, netspeed, ram, cpu and card type char pszVersion[256]; int nNetSpeed, nRam, nCpu; inbuf.Scanf( "%s %d %d %d", pszVersion, &nNetSpeed, &nRam, &nCpu ); char pszCard[300]; char chSentinel = ' '; int i=0; while (( chSentinel != '\n' )) { chSentinel = inbuf.GetChar(); pszCard[i] = chSentinel; ++i; if (i >= 300) break; } // check for those blasted hackers if (i>= 300) { // read rest while (( chSentinel != '\n' )) { chSentinel = inbuf.GetChar(); } continue; } pszCard[i] = '\0'; // 0=RIVA TNT2 // 1=GeForce2 MX // 2=Microsoft Corporation GDI Generic // 3=GeForce2 GTS // 4=Voodoo 3 // 5=Intel 810 // 6=GeForce3 // 7=Riva TNT // 8=GeForce 256 // 9=Rage 128 // 10=S3 Savage4 // 11=SiS 630 // 12=Radeon DDR // 13=Rage 128 Pro // 14=All others int cardIndex=14; // default is other if ( Q_stristr ( pszCard, "RIVA TNT2" ) != NULL ) { //printf ("%s", pszCard); cardIndex=0; } else if ( Q_stristr ( pszCard, "GeForce2 MX" ) != NULL ) { cardIndex=1; } else if ( Q_stristr ( pszCard, "Microsoft Corporation GDI Generic" ) != NULL ) { cardIndex=2; } else if ( Q_stristr ( pszCard, "GeForce2 GTS" ) != NULL ) { cardIndex=3; } else if ( Q_stristr ( pszCard, "Voodoo3" ) != NULL ) { cardIndex=4; } else if ( Q_stristr ( pszCard, "Intel 810" ) != NULL ) { cardIndex=5; } else if ( Q_stristr ( pszCard, "GeForce3" ) != NULL ) { cardIndex=6; } else if ( Q_stristr ( pszCard, "Riva TNT" ) != NULL ) { cardIndex=7; } else if ( Q_stristr ( pszCard, "GeForce 256" ) != NULL ) { cardIndex=8; } else if ( Q_stristr ( pszCard, "Rage 128 Pro" ) != NULL ) { cardIndex=13; } else if ( Q_stristr ( pszCard, "Rage 128" ) != NULL ) { cardIndex=9; } else if ( Q_stristr ( pszCard, "S3 Savage4" ) != NULL ) { cardIndex=10; } else if ( Q_stristr ( pszCard, "SiS 630" ) != NULL ) { cardIndex=11; } else if ( Q_stristr ( pszCard, "Radeon DDR" ) != NULL ) { cardIndex=12; } nQuantity[cardIndex]++; totalUsers++; } printf("TotalUsers %d\n", totalUsers); // write results to an output buffer outbuf.Printf ( "Video Card" ); // headers outbuf.Printf ("\tNumber of Users\n"); outbuf.Printf ("\n"); for ( int i=0; i < CARDHISTBINS; ++i ) { outbuf.Printf ( "-"); outbuf.Printf ("\t%d", nQuantity[i]); outbuf.Printf ("\n"); } } void HistogramNetSpeed( CUtlBuffer &inbuf, CUtlBuffer &outbuf, char *pszSearchString, int size, int binwidth) { #define NETHISTBINS 9 // unequally sized bins int nQuantity[NETHISTBINS]; for (int j=0; j< NETHISTBINS; ++j) nQuantity[j]=0; CUtlVector nNetList; nNetList.AddToTail(0); nNetList.AddToTail(28); nNetList.AddToTail(33); nNetList.AddToTail(56); nNetList.AddToTail(112); nNetList.AddToTail(256); nNetList.AddToTail(512); nNetList.AddToTail(1100); nNetList.AddToTail(2000); int totalUsers=0; while ( (inbuf.TellGet() < size) ) { // Now parse the utlbuffer // file has fields of version, netspeed, ram, cpu and card type char pszVersion[256]; int nNetSpeed, nRam, nCpu; inbuf.Scanf( "%s %d %d %d", pszVersion, &nNetSpeed, &nRam, &nCpu ); char pszCard[300]; char chSentinel = ' '; int i=0; while (( chSentinel != '\n' )) { chSentinel = inbuf.GetChar(); pszCard[i] = chSentinel; ++i; if (i >= 300) break; } // check for those blasted hackers if (i>= 300) { // read rest while (( chSentinel != '\n' )) { chSentinel = inbuf.GetChar(); } continue; } pszCard[i] = '\0'; if (nNetSpeed < 0) // handle corrupt data continue; // handle outliers--bin is off the scale if ( nNetSpeed > nNetList[nNetList.Size()-1] ) continue; i=0; while ( nNetSpeed > nNetList[i] ) { ++i; } int netIndex = i; nQuantity[netIndex]++; totalUsers++; } printf("TotalUsers %d\n", totalUsers); // write results to an output buffer outbuf.Printf ( "Network Speed" ); // headers for (int j=0; j < nNetList.Size()-1; ++j) outbuf.Printf ("\t%d-%d", nNetList[j], nNetList[j+1]); outbuf.Printf ("\n"); for ( int i=0; i < NETHISTBINS; ++i ) { outbuf.Printf ( "-"); outbuf.Printf ("\t%d", nQuantity[i]); outbuf.Printf ("\n"); } } void HistogramRam( CUtlBuffer &inbuf, CUtlBuffer &outbuf, char *pszSearchString, int size, int binwidth) { #define RAMHISTBINS 20 // unequally sized bins int nQuantity[RAMHISTBINS]; for (int j=0; j< RAMHISTBINS; ++j) nQuantity[j]=0; CUtlVector nRamList; nRamList.AddToTail(0); int basemem=16; while ( basemem < 2050 ) { nRamList.AddToTail(basemem); basemem<<=1; } int totalUsers=0; while ( (inbuf.TellGet() < size) ) { // Now parse the utlbuffer // file has fields of version, netspeed, ram, cpu and card type char pszVersion[256]; int nNetSpeed, nRam, nCpu; inbuf.Scanf( "%s %d %d %d", pszVersion, &nNetSpeed, &nRam, &nCpu ); char pszCard[300]; char chSentinel = ' '; int i=0; while (( chSentinel != '\n' )) { chSentinel = inbuf.GetChar(); pszCard[i] = chSentinel; ++i; if (i >= 300) break; } // check for those blasted hackers if (i>= 300) { // read rest while (( chSentinel != '\n' )) { chSentinel = inbuf.GetChar(); } continue; } pszCard[i] = '\0'; if (nRam < 0) // handle corrupt data continue; // handle outliers--bin is off the scale if ( nRam > nRamList[nRamList.Size()-1] ) continue; i=0; while ( nRam > nRamList[i] ) { ++i; } int ramIndex = i; nQuantity[ramIndex]++; totalUsers++; } printf("TotalUsers %d\n", totalUsers); // write results to an output buffer outbuf.Printf ( "RAM" ); // headers for (int j=0; j < nRamList.Size()-1; ++j) outbuf.Printf ("\t%d-%d", nRamList[j], nRamList[j+1]); outbuf.Printf ("\n"); for ( int i=0; i < nRamList.Size(); ++i ) { outbuf.Printf ( "-"); outbuf.Printf ("\t%d", nQuantity[i]); outbuf.Printf ("\n"); } }