/****************************************************************************\
;                                                                            ;
;  ulmp.c - project LMP control center                                       ;
;  implementation                                                            ;
;                                                                            ;
;  general LMP routines                                                      ;
;                                                                            ;
;  Uwe Girlich                                                               ;
;  Erika-von-Brockdorff-Strasse 2                                            ;
;  04159 Leipzig                                                             ;
;  Deutschland / Germany                                                     ;
;  E-mail: girlich@aix520.informatik.uni-leipzig.de                          ;
;                                                                            ;
\****************************************************************************/


#include <errno.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include "tools.h"
#include "ulmp.h"


char *PlayerColor[] = {
                        "green",
                        "indigo",
                        "brown",
                        "red"
                      };

char *WeaponName[] = {
                       "Fist/Chainsaw", "Pistol", "Shotgun", "Chaingun", "Rocket Launcher", "Plasma Rifle", "BFG 9000", "Chainsaw",
                       "Fist/Chainsaw", "Pistol", "Shotgun/Super Shotgun", "Chaingun", "Rocket Launcher", "Plasma Rifle", "BFG 9000", "Chainsaw",
                       "Staff/Gauntlets of the Necromancer", "Crystal", "Ethereal Crossbow", "Dragon Claw", "Hellstaff", "Phoenix Rod", "Mace", "Gauntlets of the Necromancer"
                     };

char *ArtifactName[] = {
                         "Ring of Invincibility",
                         "Shadowsphere",
                         "Quartz Flask",
                         "Chaos Device",
                         "Tome of Power",
                         "Torch",
                         "Time Bomb of the Ancients",
                         "Morph Ovum",
                         "Wings of Wrath",
                         "Mystic Urn"
                       };


VK VersionKey[] = {
                  { "1.0"     , HERETIC,          0, 1},
		  { "1.1"     , DOOM_old,         0, 1},
                  { "1.2"     , DOOM_old,         0, 2},
		  { "<1.4"    , DOOM_old,         0, 4},
		  { "1.4beta" , DOOM_new,       104, 1},
                  { "1.5beta" , DOOM_new,       105, 1},
		  { "1.6beta" , DOOM_new,       106, 1},
		  { "1.666"   , DOOM_new,       106, 2},
		  { "1.666"   , DOOM2,          106, 2},
		  { "1.7"     , DOOM2,          107, 1},
		  { "1.7a"    , DOOM_new,       107, 2},
		  { "1.7a"    , DOOM2,          107, 2},
		  { "1.8"     , DOOM_new,       108, 1},
		  { "1.8"     , DOOM2,          108, 1},
		  { "1.9"     , DOOM_new,       109, 1},
		  { "1.9"     , DOOM2,          109, 1}
                };


#define MYBUFSIZE 65536


/******************************************************************************/
/** LMP ***********************************************************************/
/******************************************************************************/

void LMP_init(LMP_t *l, char *filename, char *mode)
{
  struct stat buf;

  if ((l->filename = strdup(filename))==NULL) syserror(errno,"strdup");
  if ((l->file=fopen(l->filename, mode))==NULL) syserror(errno,l->filename);
  if (setvbuf(l->file,NULL,_IOFBF,BUFSIZ>MYBUFSIZE?BUFSIZ:MYBUFSIZE)!=0) syserror(errno,"setvbuf");
  if (stat(l->filename,&buf)==-1) syserror(errno,l->filename);
  l->filesize=buf.st_size;
}

int LMP_checkgametype(LMP_t *l)
{
  signed char buffer[12];
  long num, i;
  double av_h, av_d;
  long int n_h, n_d;
 
  fprintf(stderr,"determine game type");
  fseek(l->file,l->headersize,SEEK_SET);
  num=l->datasize/12;
  if (num<200) return DOOM_old | HERETIC;
  if (num>5000) num=5000; 
  av_h = av_d = 0;
  n_h = n_d = 0;
  for (i=0;i<num;i++) {
    if (fread(buffer,1,12,l->file)==0) syserror(FREAD,l->filename);
    av_h += buffer[2] + buffer[8]; 
    n_h += 2;
    av_d += buffer[2] + buffer[6] + buffer[10];
    n_d += 3;
  }
  av_h /= n_h;
  av_d /= n_d;
  fseek(l->file,l->headersize,SEEK_SET);
  if (fabs(av_h)<fabs(av_d)) {
    fprintf(stderr,": HERETIC\n"); 
    return HERETIC;
  }
  else {
    fprintf(stderr,": old DOOM\n");
    return DOOM_old;
  }
}

void LMP_readheader(LMP_t *l)
{
  int game, i;

  rewind(l->file);
  if (fread(l->header,1,SHORT_SIZE,l->file)==0) syserror(FREAD,l->filename);
  if (l->header[0]<104) {
    l->headersize  = SHORT_SIZE;
    l->versionbyte = 0; 
    l->skill       = l->header[0];
    l->episode     = l->header[1];
    l->map         = l->header[2];
    l->multirule   = 0;
    l->respawn     = 0;
    l->fast        = 0;
    l->nomonsters  = 0;
    l->mainplayer  = 0;
  }
  else {
    rewind(l->file);
    if (fread(l->header,1,LONG_SIZE,l->file)==0) syserror(FREAD,l->filename);
    l->headersize = LONG_SIZE;
    l->versionbyte = l->header[0];
    l->skill       = l->header[1];
    l->episode     = l->header[2];
    l->map         = l->header[3];
    l->multirule   = l->header[4];
    l->respawn     = l->header[5];
    l->fast        = l->header[6];
    l->nomonsters  = l->header[7];
    l->mainplayer  = l->header[8];
  }
  for (i=0, l->playernum=0;i<MAXPLAYER;i++) 
    if (l->header[l->headersize-MAXPLAYER+i]) l->num[l->playernum++] = i;
  l->datasize=l->filesize-l->headersize-1;
  
  /* determine possible game type */
  if (l->headersize==SHORT_SIZE) {
    /* DOOM_old or HERETIC */
    if (l->datasize % (SHORT_TIC*l->playernum)) {
      /* not DOOM_old */
      /* only HERETIC or WRONG */
      if (l->datasize % (LONG_TIC*l->playernum)) {
        game=WRONG; 
      }
      else {
        /* HERETIC */
        game=HERETIC;
      }
    }
    else {
      /* DOOM_old or HERETIC */
      if (l->datasize % (LONG_TIC*l->playernum)) {
        /* DOOM_old */
        game=DOOM_old;
        }
      else {
        /* DOOM_old or HERETIC */
        game=LMP_checkgametype(l);
      }
    } 
  }
  else {
    /* DOOM_new or DOOM2 */
    if (l->episode>1) 
      game=DOOM_new;
    else 
      if (l->map>9)
        game=DOOM2;
      else
        game=DOOM_new|DOOM2;
    if (l->datasize % (SHORT_TIC*l->playernum)) game=WRONG;
  }
  
  if (game==WRONG) syserror(WLMP,l->filename); 
  if (l->game==(DOOM_old|DOOM_new))
    l->game=(l->headersize==SHORT_SIZE?DOOM_old:DOOM_new);
  if (game==DOOM_old || 
      game==DOOM_new || 
      game==DOOM2    ||  
      game==HERETIC  ||
      game==(DOOM_new|DOOM2)) {
    if (l->game==UNKNOWN) 
      l->game=game;
    else {
      if (!(l->game & game)) 
        syswarning(DEFTYPE,l->filename);
    }
  }      
  else {
    if (l->game==UNKNOWN)
      syserror(UKTYPE,l->filename);
    else {
      if (!(l->game & game)) 
        syswarning(DEFTYPE,l->filename);
    }    
  }
  getversion(l->versionbyte,&(l->game),l->filename,&l->gs,&l->ns);  
  l->ticsize=((l->game==HERETIC)?LONG_TIC:SHORT_TIC);
  l->tics=l->datasize / (l->playernum * l->ticsize); 
  l->time=l->tics*TICTIME;
}

void LMP_writeheader(LMP_t *l)
{
  int i;

  switch (l->game) {
    case HERETIC:
    case DOOM_old:       l->headersize=SHORT_SIZE; break;
    case DOOM_new:
    case DOOM2:
    case DOOM_new|DOOM2: l->headersize=LONG_SIZE;
  }
  if (l->headersize==SHORT_SIZE) {
    l->header[0] = l->skill;
    l->header[1] = l->episode;
    l->header[2] = l->map;
  }
  else {
    l->header[0] = l->versionbyte;
    l->header[1] = l->skill;
    l->header[2] = l->episode;
    l->header[3] = l->map;
    l->header[4] = l->multirule;
    l->header[5] = l->respawn;
    l->header[6] = l->fast;
    l->header[7] = l->nomonsters;
    l->header[8] = l->mainplayer;
  }

  for (i=0;i<MAXPLAYER;i++) l->header[l->headersize-MAXPLAYER+i]=0;
  for (i=0;i<l->playernum;i++) l->header[l->headersize-MAXPLAYER+l->num[i]] = 1;
  
  if (fwrite(l->header,1,l->headersize,l->file)==0) syserror(FWRITE,l->filename);
}  

void LMP_writequitbyte(LMP_t *l)
{
  unsigned char a=0x80;

  if (fwrite(&a,1,1,l->file)==0) syserror(FWRITE,l->filename);
}

void LMP_putgametic(LMP_t *l, TIC_t *t)
{
  if (fwrite(t,1,l->ticsize,l->file)==0) syserror(FWRITE,l->filename);
}

void LMP_getgametic(LMP_t *l, TIC_t *t)
{
  if (fread(t,1,l->ticsize,l->file)==0) syserror(FREAD,l->filename);
}

void LMP_done(LMP_t *l)
{
  if (fclose(l->file)!=0) syserror(errno,l->filename);
  free(l->filename);
}


/******************************************************************************/
/** LS ************************************************************************/
/******************************************************************************/

void LS_init(LS_t *l, char *filename, char *mode)
{
  struct stat buf;

  if ((l->filename = strdup(filename))==NULL) syserror(errno,"strdup");
  if ((l->file=fopen(l->filename, mode))==NULL) syserror(errno,l->filename);
  if (setvbuf(l->file,NULL,_IOFBF,BUFSIZ>MYBUFSIZE?BUFSIZ:MYBUFSIZE)!=0) syserror(errno,"setvbuf");
  if (stat(l->filename,&buf)==-1) syserror(errno,l->filename);
  l->filesize=buf.st_size;
}


void LS_done(LS_t *l)
{
  if (fclose(l->file)!=0) syserror(errno,l->filename);
  free(l->filename);
}


/******************************************************************************/
/** STAT **********************************************************************/
/******************************************************************************/

void STAT_init(STAT_t *s, char *command, int maxm)
{
  unsigned char i;

  for (i=0;i<128;i++) s->hist[i]=0;
  s->sum=0;
  if ((s->command=strdup(command))==NULL) syserror(errno,"strdup");
  s->maxm=maxm;
}


void STAT_calc(STAT_t *s)
{
  unsigned char i;

  s->m[0]=s->m[1]=s->m[2];
  s->sum = 0;
  for (i=0;i<128;i++) {
    s->sum+=s->hist[i];
    if (s->hist[i]>s->hist[s->m[0]]) {
      s->m[2]=s->m[1]; s->m[1]=s->m[0]; s->m[0]=i;
    }
    else if (s->hist[i]>s->hist[s->m[1]]) {
      s->m[2]=s->m[1]; s->m[1]=i;
    }
    else if (s->hist[i]>s->hist[s->m[2]]) {
      s->m[2]=i;
    }  
    if (s->hist[i]>0) s->last=i;
  }
  if (s->hist[s->m[0]]+s->hist[s->m[1]]+s->hist[s->m[2]]==s->sum) 
    s->control = C_KEY;
  else
    s->control = C_MOUSE;
}


int STAT_turncheck(STAT_t *s, unsigned char t0, unsigned char t1, unsigned char t2)
{
  return    (s->m[0]==t0 || s->m[0]==t1 || s->m[0]==t2 || s->m[0]==0)
         && (s->m[1]==t0 || s->m[1]==t1 || s->m[1]==t2 || s->m[1]==0)
         && (s->m[2]==t0 || s->m[2]==t1 || s->m[2]==t2 || s->m[2]==0);
}


char *STAT_writehist(STAT_t *s, char *buf)
{
  int empty;
  unsigned char i;

  empty=TRUE;
  strcpy(buf,"");
   
  for (i=0;i<s->maxm;i++) {
    if (s->hist[s->m[i]]!=0) {
      if (!empty) strcat(buf,", ");
      sprintf(buf+strlen(buf),"%lu * %s%d",s->hist[s->m[i]],s->command,s->m[i]);
      empty=FALSE;
    }
  }
  return buf;
}


void STAT_done(STAT_t *s)
{
}

/******************************************************************************/
/** Outside *******************************************************************/
/******************************************************************************/

void getversion(unsigned char versionbyte, int *game, char *filename, 
                char **gs, char **ns) 
{
  int i;
  unsigned int n;

  *gs=(char*)malloc(100*sizeof(char));
  *gs[0]='\0';
  *ns=(char*)malloc(100*sizeof(char));
  *ns[0]='\0';

  /* calculate version number string */
  n=0; /* no version strings */
  for (i=0;i<VersionKeys;i++) {
    if (VersionKey[i].versionbyte==versionbyte) {
      if (VersionKey[i].game & (*game)) {
        if (!(n & VersionKey[i].string_no)) {
          if(strlen(*ns)) strcat(*ns," or ");
          strcat(*ns, VersionKey[i].versionstring);
          n|=VersionKey[i].string_no;
        } /* string not used */
      } /* game ok */
    } /* versionbyte ok */
  } /* for i */

  /* calculate game string */
  if ((*game & DOOM_old) || (*game & DOOM_new)) {
    if (strlen(*gs)) strcat(*gs," or ");
    strcat(*gs,"DOOM");
  }
  if (*game & DOOM2) {
    if (strlen(*gs)) strcat(*gs," or ");
    strcat(*gs,"DOOM ][");
  }
  if (*game & HERETIC) {
    if (strlen(*gs)) strcat(*gs," or ");
    strcat(*gs,"HERETIC");
  }
}


unsigned char strtoversionbyte(char *s)
{
  long int i,l;
  char *res;

  for (i=0;i<VersionKeys;i++) {
    l=strlen(VersionKey[i].versionstring)>strlen(s)?strlen(VersionKey[i].versionstring):strlen(s);
    if (l>3) l=3;
    if (!strncmp(VersionKey[i].versionstring,s,l))
      return VersionKey[i].versionbyte;
  }   
  i=strtol(s,&res,0)%256;
  if (res[0]!='\0') syserror(EINVAL,res);
  return i;
}


/*- file end ulmp.c ----------------------------------------------------------*/

