www.pudn.com > ilib > ifraggraph.c


/*

  Report generator for QuakeWorld frag log file

  Usage:
    frag_graph player fraglogfile [fraglogfile ...] > out.gif

  NOTE:
    Player names are case-sensitive!


  18-May-98	Craig Knudsen	cknudsen@radix.net
		Better error handling of command line args
		Don't crash if user not found in logs
  23-May-97	Craig Knudsen	cknudsen@radix.net
		Created


***************************************************************************/


#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 

#include "helvB18.h"
#include "courR10.h"


#ifndef min
#define min(a,b) ( (a) < (b) ? (a) : (b) )
#define max(a,b) ( (a) > (b) ? (a) : (b) )
#endif

#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif

#define FRAG	1
#define DEATH	2
#define SUICIDE	3


static void read_file (
#ifndef _NO_PROTO
  char *filename
#endif
);
static void add_stat (
#ifndef _NO_PROTO
  int type
#endif
);
static void generate_gif ();
static int calc_max (
#ifndef _NO_PROTO
  int cur_max
#endif
);



/*
** Define start and stop times.  Use YYMMDDHHMMSS so that we can just
** use strcmp() to compare times.
*/
static char *player = NULL;
static int *stats = NULL;
static int num_stats = 0;	/* total number of frags counted */


static int display_header = TRUE;
static int line_width = 2;	/* for line graphs (can use 1-3) */



/*
** Main
*/
int main ( argc, argv )
int argc;
char *argv[];
{
  int loop;

  stats = (int *) malloc ( 1 );

  for ( loop = 1; loop < argc; loop++ ) {
    if ( *argv[loop] == '-' ) {
      fprintf ( stderr, "%s: unrecognized argument \"%s\"\n",
        argv[0], argv[loop] );
      exit ( 1 );
    }
    else if ( player == NULL ) {
      player = argv[loop];
    }
    else {
      read_file ( argv[loop] );
    }
  }

  if ( player == NULL ) {
    fprintf ( stderr, "Usage: frag_frag playername frag_1.log ...\n" );
    exit ( 1 );
  }

  /* create output */
  generate_gif ();

  /* exit */
  return ( 0 );
}





/****************************************************************************
*
* Read in the data file
*
****************************************************************************/
static void read_file ( filename )
char *filename;
{
  FILE *fp;
  char text[1024], *ptr;

  fp = fopen ( filename, "r" );
  if ( ! fp ) {
    fprintf ( stderr, "Unable to open log file %s\n", filename );
    return;
  }
  while ( fgets ( text, 10240, fp ) ) {
    ptr = strtok ( text, "\\" );
    if ( ! ptr )
      continue;
    if ( strcmp ( player, ptr ) == 0 ) {
      ptr = strtok ( NULL, "\\" );
      if ( ! ptr )
        continue;
      if ( strcmp ( player, ptr ) == 0 )
        add_stat ( SUICIDE );
      else
        add_stat ( FRAG );
    } else {
      ptr = strtok ( NULL, "\\" );
      if ( ! ptr )
        continue;
      if ( strcmp ( player, ptr ) == 0 )
        add_stat ( DEATH );
    }
  }
  fclose ( fp );
}



static void add_stat ( type )
int type;
{
  num_stats++;
  stats = (int *) realloc ( stats, ( num_stats * sizeof ( int * ) ) );
  stats[num_stats-1] = type;
}





/****************************************************************************
*
* Generate the output image
*
****************************************************************************/
#define LEFT_PAD	80
#define TOP_PAD		50
#define RIGHT_PAD	50
#define BOTTOM_PAD	75
#define DATA_WIDTH	20
#define DATA_HEIGHT	200
static void generate_gif ()
{
  int loop;
  char temp[200];
  int x, y, lastx, lasty;
  int r_x, r_y, r_lastx, r_lasty;
  IImage im_out;	/* output image */
  IFont helvB18, courR10;
  IColor red, green, blue, black, grey;
  IGC gc;
  int height, width;
  int max_gibs;
  int gib_step, column;
  int frags = 0, deaths = 0, suicides = 0;
  int r_frags = 0, r_deaths = 0, r_suicides = 0;
  double eff;

  max_gibs = calc_max ( num_stats );
  gib_step = max_gibs / 20;

  width = LEFT_PAD + ( 20 * DATA_WIDTH ) + RIGHT_PAD;
  height = TOP_PAD + DATA_HEIGHT + BOTTOM_PAD;

  /* allocate image */
  im_out = ICreateImage ( width, height, IOPTION_NONE );
  /* first color is background color */
  black = IAllocColor ( 0, 0, 0 );
  grey = IAllocColor ( 192, 192, 192 );
  ISetTransparent ( im_out, grey );
  red = IAllocColor ( 255, 0, 0 );
  green = IAllocColor ( 0, 150, 0 );
  blue = IAllocColor ( 0, 0, 255 );
  gc = ICreateGC ();
  ISetForeground ( gc, grey );
  IFillRectangle ( im_out, gc, 0, 0, width, height );

  /* draw black border */
  ISetForeground ( gc, black );
  IDrawRectangle ( im_out, gc, 0, 0, width - 1, height - 1 );

  /* draw title */
  sprintf ( temp, "Frag Efficiency Graph 1.0: %s", player );
  ILoadFontFromData ( "courR10", courR10_font, &courR10 );
  ILoadFontFromData ( "helvB18", helvB18_font, &helvB18 );
  
  /* Draw title */
  ISetFont ( gc, helvB18 );
  ISetForeground ( gc, grey );
  IDrawString ( im_out, gc, LEFT_PAD + 31, 31, temp, strlen ( temp ) );
  ISetForeground ( gc, black );
  IDrawString ( im_out, gc, LEFT_PAD + 30, 30, temp, strlen ( temp ) );

  /* write "Efficiency" to the left of the y axis */
  ISetFont ( gc, courR10 );
  IDrawString ( im_out, gc, 5, TOP_PAD + DATA_HEIGHT / 2 - 5,
    "Efficiency", strlen ( "Efficiency" ) );

  /* label the y axis  with the top value */
  strcpy ( temp, "100%" );
  IDrawString ( im_out, gc, 5, TOP_PAD + 3, temp, strlen ( temp ) );
  ISetForeground ( gc, blue );
  IDrawLine ( im_out, gc, LEFT_PAD - 3, TOP_PAD, LEFT_PAD, TOP_PAD );

  /* draw x and y axis in blue */
  IDrawLine ( im_out, gc, LEFT_PAD, TOP_PAD, LEFT_PAD, TOP_PAD + DATA_HEIGHT );
  IDrawLine ( im_out, gc, LEFT_PAD, TOP_PAD + DATA_HEIGHT, width - RIGHT_PAD,
    TOP_PAD + DATA_HEIGHT );

  /* draw dashed lines horizontally */
  ISetLineStyle ( gc, ILINE_ON_OFF_DASH );
  for ( loop = 0; loop <= 10; loop++ ) {
    y = TOP_PAD + ( ( DATA_HEIGHT / 10 ) * loop );
    IDrawLine ( im_out, gc, LEFT_PAD, y, width - RIGHT_PAD, y );
  }
  ISetLineStyle ( gc, ILINE_SOLID );

  ISetForeground ( gc, black );
  IDrawString ( im_out, gc, LEFT_PAD + 80, height - BOTTOM_PAD + 22,
    "Frags + Deaths + Suicides", strlen ( "Frags + Deaths + Suicides" ) );

  column = 0;
  for ( loop = 0; loop <= num_stats; loop++ ) {
    switch ( stats[loop] ) {
      case FRAG: frags++; r_frags++; break;
      case DEATH: deaths++; r_deaths++; break;
      case SUICIDE: suicides++; r_suicides++; break;
    }
    if ( gib_step && loop % gib_step == 0 && loop ) {
      column++;
      r_x = x = LEFT_PAD + ( DATA_WIDTH * column );
      eff = ( (double)frags / (double) loop );
      y = TOP_PAD + (int)
        ( (double)( 1.0 - eff ) * (double)DATA_HEIGHT );
      eff = ( (double)r_frags / (double)gib_step );
      r_y = TOP_PAD + (int)
        ( (double)( 1.0 - eff ) * (double)DATA_HEIGHT );
      /* draw the line graph */
      if ( column > 1 ) {
        ISetForeground ( gc, red );
        ISetLineWidth ( gc, line_width );
        IDrawLine ( im_out, gc, lastx, lasty, x, y );
        ISetLineWidth ( gc, 1 );
        ISetForeground ( gc, green );
        ISetLineWidth ( gc, line_width );
        IDrawLine ( im_out, gc, r_lastx, r_lasty, r_x, r_y );
        ISetLineWidth ( gc, 1 );
      }
      /* draw a tic mark */
      ISetForeground ( gc, blue );
      IDrawLine ( im_out, gc, x, TOP_PAD + DATA_HEIGHT, x, 
        TOP_PAD + DATA_HEIGHT + 3 );
      /* label the x axis */
      if ( column % 2 == 0 ) {
        ISetForeground ( gc, black );
        sprintf ( temp, "%d", loop );
        IDrawString ( im_out, gc, x - 5, TOP_PAD + DATA_HEIGHT + 11,
           temp, strlen ( temp ) );
      }
      /* save x and y for the next point */
      lastx = x;
      lasty = y;
      r_lastx = r_x;
      r_lasty = r_y;
      r_deaths = r_frags = r_suicides = 0;
    }
  }

  if ( display_header ) {
    sprintf ( temp, "Total gibs for interval: %d", num_stats );
    ISetForeground ( gc, black );
    IDrawString ( im_out, gc, 5, height - 38, temp, strlen ( temp ) );
  }
  ISetForeground ( gc, red );
  IFillRectangle ( im_out, gc, 5, height - 33, 10, 10 );
  sprintf ( temp, "Cumulative efficiency" );
  IDrawString ( im_out, gc, 20, height - 23, temp, strlen ( temp ) );
  ISetForeground ( gc, green );
  IFillRectangle ( im_out, gc, 5, height - 20, 10, 10 );
  sprintf ( temp, "Efficiency over last %d gibs", gib_step );
  IDrawString ( im_out, gc, 20, height - 10, temp, strlen ( temp ) );

  /*
  ** make output interlaced
  */

  /*
  ** Write GIF output file.
  */
  IWriteImageFile ( stdout, im_out, IFORMAT_GIF, IOPTION_INTERLACED );
  IFreeImage ( im_out );
}









/****************************************************************************
*
* Calculate a nice round number to use as the maximum for the y axis.
*
****************************************************************************/
static int calc_max ( cur_max )
int cur_max; 	/* the current max value */
{
  int ret_val;
  int next_round, tens, div_val;

  if ( cur_max == 0 )
    return ( 1 );

  tens = (int) log10 ( (double) cur_max );
  next_round = (int) pow ( (double) 10.0, (double) (tens+1) );
  if ( next_round % 10 == 9 )
    next_round++; // strange behavior under Linux
  div_val = (int) ( next_round / (int) cur_max );
  switch ( div_val ) {
    case 10:
    case 9:
    case 8:
    case 7:
    case 6:
    case 5:
    next_round /= 5;
      break;
    case 4:
      next_round /= 4;
      break;
    case 3:
      next_round = (int) ( 0.4 * (float) next_round );
      break;
    case 2:
      next_round /= 2;
      break;
    case 1:
    case 0:
      break;
  }

  ret_val = (int) ( max ( 5.0, (float) next_round ) );

  if ( ret_val < cur_max )
    ret_val = cur_max;	/* floating point round error */

  return ( ret_val );
}