/*  pdats.c                            jha    5/93
**                      release 1.0    eej    6/93
**                      release 1.1    eej    5/94
**
**  Produces PDATS format compressed traces from
**  any of the following:  DAS, dinero, Schieber,
**  GreenStamp, and straight binary with or without
**  time stamps.  Employs repeat records.
**
**  Copyright 1994 Eric E. Johnson, Jiheng Ha
**
**  Permission to use, copy, modify, and distribute this
**  software and its documentation for any purpose and without
**  fee is hereby granted, provided that the above copyright
**  notice appears in all copies.  The authors make no
**  representations about the suitability of this software
**  for any purpose.  It is provided "as is" without expressed
**  or implied warranty.
*/

#include <stdio.h>
#include <strings.h>
#include <ctype.h>
#include "das_pack.h"
#define true 1
#define false 0
#define LIM 10  /* increased from 8 to allow for 0x... notation */
#define SEEK_SET 0

unsigned long StoredAddr[8],CurrentTS;
unsigned long OffsetOflo = 0;

char token[LIM],token2[LIM],token3[LIM];
int numchar, numchar2, numchar3;
int  das,UncompBin,HasTS,fbyte,sbyte;
char Schieber;

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

knowsize(offset)
long offset;
{ 
    if((offset >= -128) && ( offset <= 127)) return 1;
    if((offset >= -32768) && ( offset <= 32767)) return 2;
    if((offset >= -8388608) && ( offset <= 8388607)) return 3;
    if((offset >= -2147483648) && (offset <= 2147483647)) return 4;
    OffsetOflo++;
    return 4;
}         

/******************************************************************/
unsigned long t1[LIM/2],t2[LIM/2],t[LIM];

unsigned long char2int(token,numchar,hexdec)
char token[],hexdec;
int numchar;
{
    unsigned long c,k,mask;
    int i,j;
    unsigned long ReturnValue;

    ReturnValue=0;
    for(i=0;i<LIM;i++)
      t[i]='0';
 
    for(i=numchar-1,j=LIM-1;i>=0;i--,j--)
      t[j]=token[i];

    for(i=0;i<LIM;i++){
	if(t[i]>='A' && t[i]<='F')
	  t[i]-=55;
	else
	  t[i]-=48;
    }
    if(hexdec=='x'){
	for(i=0,j=0;i<LIM;i+=2,j++){
	    t2[j]=(t1[j]=(t[i] <<= 4) + t[i+1]);
	}
	for(i=0;i< LIM/2 ;i++)
	  ReturnValue += (t2[i] <<= ((LIM/2 -1 -i)*8));
    }
    else{
	if(hexdec=='d'){
	    c=0;
	    for(i=0;i<LIM;i++){
		for(k=1,j=0;j<LIM-1-i;j++)
		  k *= 10;
		c+= t[i]*k;
	    }
	    ReturnValue = c;
	    for(j=0;j<LIM/2;j++){
		t1[j]= 255 & (c >> ((LIM/2-1-j)*8));
	    }
	}
	else   exit(-1);
    }
    return ReturnValue;
}

/******************************************************************/
/*                                                                */
/*     This program compresses Das, Schieber, GreenStamp, Dinero  */
/*     and Binary format traces using Repeated scheme.            */
/*                                                                */
/******************************************************************/

main(argc,argv)
     int argc;
     char *argv[];
{
    int c,i;

    if (UncompBin = (fbyte=getchar())==0xff){
	switch (sbyte=getchar()) {
	  case 2: HasTS=true;
	  case 0: break;
	  default: fprintf(stderr,"pdats: File already in PDATS format.\n");
	           exit(0);
	}
    }

    if (das = (fbyte == 0x2e)) {
	fseek(stdin,0,SEEK_SET);
	HasTS = true;
    }

    c=initial(); 

 /******************************************/
 /*     N - Uncompressed binary format     */
 /*     S - Schieber    format             */
 /*     D - Dinero      format             */
 /*     G - Green Stamp format             */
 /*     A - DAS         format             */
 /******************************************/ 

    switch(c){
      case 'B':           /* uncompressed binary */
	if(HasTS)
	  binary_w_TS();
	else
	  binary_no_TS();
	break;
      case 'A':           /* DAS format */
	DAS_format();
	break;
      case 'S':           /* Schieber format */
      case 'D':           /* dinero format */
	ASCII_no_TS();
	break;
      case 'G':           /* GreenStamp format */
	GreenStamp();
	break;
      default:
	fprintf(stderr, "pdats: Unknown file format\n");
	exit(-1);
    }
    if (OffsetOflo) 
      fprintf(stderr, "pdats:  offset overflowed %ld times\n", OffsetOflo);
}

/*******************************************************************/
gettoken(token,numchar)
 char token[LIM];
 int *numchar;
{ int c,i;
  while((c=getchar())==' ' || c=='\n')
            ;
  islower(c) ? (token[0]=toupper(c)) : (token[0]=c);

  for(i=1;(c=getchar())!=' ' && c!=EOF && c!='\n';i++)
      islower(c) ? (token[i]=toupper(c)) : (token[i]=c);
  *numchar=i;
  return(c);
} 
/**********************************************************************/
initial()    /*  initialize globals and write PDATS file header */
{
    int c, i, format;

    Schieber=false;
    for(i=0;i<8;i++){
	StoredAddr[i]= 0;
    }
    CurrentTS=0;

    putchar(0xff);    /* PDATS flag */

    if (das) {
	putchar(format = 3);
	return ('A');
    }
    else format=1;

    if(UncompBin){
	if(HasTS)
	  format |= 2;
	putchar(format);  
	return('B');
    }

    /****  must be ASCII format; count fields to determine which  ****/

    if((c=gettoken(token2,&numchar2))=='\n'){  /**** 2 FIELD ****/
	putchar(format);  
	return('D');    /* dinero */
    }
    else{ 
	if((c=gettoken(token3,&numchar3))=='\n'){  /**** 3 FIELD ****/
	    format |= 2;
	    putchar(format);  
	    return('G');  /* GreenStamp */
	}
	else{
	    gettoken(token,&numchar);
	    if((c=gettoken(token,&numchar))=='\n'){  /**** 5 FIELD ****/
		putchar(format);
		Schieber=true;
		return('S');
	    }
	    else{
		fprintf(stderr, "Unknown file format\n");
		exit(-1);
	    }
	} 
    }
}

/********************************************************************/
DAS_format(){

int OffsetSize1,OffsetSize2,NumByteTS1,NumByteTS2,i,j,c,NumRep;
int PrevType,NewType;
unsigned long TempAddr,PrevAddr,NewAddr,TempTS,PrevTS,NewTS;
unsigned long *NextAddrptr,*NextTSptr;
char tokenA[LIM];
int numcharA,*typeptr;
unsigned long numref, RefCount;

numref=get_size(stdin);
next_ref(stdin); RefCount=1;
NewType= current_ref.type;
NewAddr= current_ref.address;
NewTS= current_ref.time;
for(;;){
  PrevType=NewType;
  PrevAddr=NewAddr;
  PrevTS=NewTS;
  TempAddr=PrevAddr;
  TempTS=PrevTS;
  NumRep=1;
  for(;;){
    next_ref(stdin);
    if(++RefCount >= numref){
        handleoffT(PrevType,PrevAddr,PrevTS,NumRep);
        return(0);  /* This omits last reference! */
    }
    else{
        NewType= current_ref.type;
        NewAddr= current_ref.address;
        NewTS= current_ref.time;
    }
    if(PrevType==NewType && (PrevAddr-StoredAddr[PrevType])==(NewAddr-TempAddr)
                    && (PrevTS-CurrentTS)==(NewTS-TempTS)){
       NumRep++;
       TempAddr=NewAddr;
       TempTS=NewTS;   
       continue;
    }
    else{
       handleoffT(PrevType,PrevAddr,PrevTS,NumRep);
       break; 
    }
  }
}
}
/***************************************************************/

binary_w_TS(){
int OffsetSize1,OffsetSize2,NumByteTS1,NumByteTS2,i,j,c,NumRep;
int PrevType,NewType;
unsigned long TempAddr,PrevAddr,NewAddr,TempTS,PrevTS,NewTS;
char tokenA[LIM];
int numcharA;

NewType= (c=getchar()) & 0x07;
NewAddr=0;
NewTS=0;
for(i=4;i>0;i--)
   NewAddr |= (c=getchar())<< 8*(i-1);

for(i=4;i>0;i--)
   NewTS |= (c=getchar())<< 8*(i-1);

for(;;){
  PrevType=NewType;
  PrevAddr=NewAddr;
  PrevTS=NewTS;
  TempAddr=PrevAddr;
  TempTS=PrevTS;
  NumRep=1;
  for(;;){
    if((c=getchar())==EOF){
        handleoffT(PrevType,PrevAddr,PrevTS,NumRep);
        return(0);
    }
    else{
        NewType= c & 0x07;
        NewAddr=0;
        NewTS=0;        
        for(i=4;i>0;i--)
           NewAddr |= (c=getchar())<< 8*(i-1);
        
        for(i=4;i>0;i--)
           NewTS |= (c=getchar())<< 8*(i-1);
    }
    if(PrevType==NewType && (PrevAddr-StoredAddr[PrevType])==(NewAddr-TempAddr)
                    && (PrevTS-CurrentTS)==(NewTS-TempTS)){
       NumRep++;
       TempAddr=NewAddr;
       TempTS=NewTS;   
       continue;
    }
    else{
       handleoffT(PrevType,PrevAddr,PrevTS,NumRep);
       break; 
    }
  }
}
}
/********************************************************************/
binary_no_TS(){
int OffsetSize1,OffsetSize2,i,j,c,NumRep;
int PrevType,NewType;
unsigned long TempAddr,PrevAddr,NewAddr;
char tokenA[LIM];
int numcharA;

NewType= (c=getchar()) & 0x07;
NewAddr=0;
for(i=4;i>0;i--)
   NewAddr |= (c=getchar())<< 8*(i-1);

for(;;){
  PrevType=NewType;
  PrevAddr=NewAddr;
  TempAddr=PrevAddr;
  NumRep=1;
  for(;;){
    if((c=getchar())==EOF){
        handleoff(PrevType,PrevAddr,NumRep);
        return(0);
    }
    else{
        NewType= c & 0x07;
        NewAddr=0;
        for(i=4;i>0;i--)
           NewAddr |= (c=getchar())<< 8*(i-1);
    }
    if(PrevType==NewType && (PrevAddr-StoredAddr[PrevType])==(NewAddr-TempAddr)){
       NumRep++;
       TempAddr=NewAddr;
       continue;
    }
    else{
       handleoff(PrevType,PrevAddr,NumRep);
       break; 
    }
  }
}
}
/********************************************************************/

GreenStamp(){
    int OffsetSize1,OffsetSize2,NumByteTS1,NumByteTS2,i,j,c,NumRep;
    int PrevType,NewType;
    unsigned long TempAddr,PrevAddr,NewAddr,TempTS,PrevTS,NewTS;
    char tokenA[LIM];
    int numcharA;

    NewType=fbyte-48;
    NewAddr = char2int(token2,numchar2,'x');

    NewTS = char2int(token3,numchar3,'d');

    for(;;){
	PrevType=NewType;
	PrevAddr=NewAddr;
	PrevTS=NewTS;
	TempAddr=PrevAddr;
	TempTS=PrevTS;
	NumRep=1;
	for(;;){
	    if((c=getset2T(&NewType,&NewAddr,&NewTS,tokenA,&numcharA))==EOF){
		handleoffT(PrevType,PrevAddr,PrevTS,NumRep);
		return(0);
	    }
	    if(PrevType==NewType 
	       && (PrevAddr-StoredAddr[PrevType])==(NewAddr-TempAddr)
	       && (PrevTS-CurrentTS)==(NewTS-TempTS)) {
		NumRep++;
		TempAddr=NewAddr;
		TempTS=NewTS;   
		continue;
	    }
	    else{
		handleoffT(PrevType,PrevAddr,PrevTS,NumRep);
		break; 
	    }
	}
    }
}

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

ASCII_no_TS(){
int OffsetSize1,OffsetSize2,i,j,c,NumRep;
int PrevType,NewType;
unsigned long TempAddr,PrevAddr,NewAddr;
char tokenA[LIM];
int numcharA;

NewType=fbyte-48;
NewAddr = char2int(token2,numchar2,'x');

for(;;){
  PrevType=NewType;
  PrevAddr=NewAddr;
  TempAddr=PrevAddr;
  NumRep=1;
  for(;;){
    if((c=getset2(&NewType,&NewAddr,tokenA,&numcharA))==EOF){
        handleoff(PrevType,PrevAddr,NumRep);
        return(0);
    }
    if(PrevType==NewType 
       && (PrevAddr-StoredAddr[PrevType])==(NewAddr-TempAddr)) {
	NumRep++;
	TempAddr=NewAddr;
	continue;
    }
    else{
       handleoff(PrevType,PrevAddr,NumRep);
       break; 
    }
  }
}
}
/****************************************************************/
handleoff(PrevType,PrevAddr,TotalReps)
int PrevType,TotalReps;
unsigned long PrevAddr;

{
    int HeaderByte,OffsetSize,i,j,c, NumRep;
    long addroff;

    HeaderByte=PrevType;
    addroff=PrevAddr-StoredAddr[PrevType];
    StoredAddr[PrevType] += TotalReps * addroff;
    OffsetSize=knowsize(addroff);

    if(addroff==4)
         ;             /* address code = '00' */
    else if((OffsetSize==1) || (OffsetSize==2))
      HeaderByte |= ((j=OffsetSize) <<5);
    else 
      HeaderByte |= ((j=3) <<5);

    while (NumRep = (TotalReps > 255) ? 255 : TotalReps) {
      TotalReps -= NumRep;
      if(NumRep==1)
	putchar(HeaderByte);
      else {
	putchar(HeaderByte | (c=1 <<7));
	putchar(NumRep);
      }
      if (NumRep > 255) {
	fprintf(stderr, "Repetitions overflow:  %d\n", NumRep);
	exit (-47);
      }

      if(addroff !=4){
	if(OffsetSize==1 || OffsetSize==2)
	  for(i=OffsetSize-1;i>=0;i--){
	    c=(addroff >> 8*i) & 0xFF;
	    putchar(c);
	  }
	else
	  for(i=3;i>=0;i--){
	    c=(addroff >> 8*i) & 0xFF;
	    putchar(c);
	  }
      }
    }
}

/****************************************************************/
getset2(typeptr,NextAddrptr,tokenA,numcharA)
int *typeptr;
unsigned long *NextAddrptr;
char tokenA[];
int *numcharA;
{ 
    int i;

    if(gettoken(token,&numchar)==EOF)
      return(EOF);
    *typeptr=token[0]-48;
    gettoken(tokenA,&numcharA);
    *NextAddrptr = char2int(tokenA,numcharA,'x');
    if(Schieber)
      for(i=0;i<=2;i++)
	gettoken(token,&numchar);
}
/****************************************************************/
handleoffT(type,NextAddr,NextTS, TotalReps)
int type, TotalReps;
unsigned long NextAddr,NextTS;

{
    int HeaderByte,OffsetSize,NumByteTS,i,j,c, NumRep;
    long addroff,tsoff;

    HeaderByte=type;
    addroff=NextAddr-StoredAddr[type];
    StoredAddr[type] += TotalReps * addroff;
    tsoff=NextTS-CurrentTS;
    CurrentTS += TotalReps * tsoff;
    OffsetSize=knowsize(addroff);
    NumByteTS=knowsize(tsoff);

    if(addroff==4)
         ;             /* address code = '00' */
    else if((OffsetSize==1) || (OffsetSize==2))
      HeaderByte |= ((j=OffsetSize) <<5);
    else 
      HeaderByte |= ((j=3) <<5);

    if(tsoff==1)
      HeaderByte |= ((i=1)<<3);
    else
      HeaderByte |= ((i=NumByteTS+1)<<3); 

    while (NumRep = (TotalReps > 255) ? 255 : TotalReps) {
      TotalReps -= NumRep;
      if(NumRep==1)
	putchar(HeaderByte);
      else{
	putchar(HeaderByte | (c=1 << 7));
	putchar(NumRep);
      }
      if (NumRep > 255) {
	fprintf(stderr, "Repetitions overflow:  %d\n", NumRep);
	exit (-48);
      }
      
      if(addroff !=4){
	if(OffsetSize==1 || OffsetSize==2)
	  for(i=OffsetSize-1;i>=0;i--){
	    c=(addroff >> 8*i) & 0xFF;
	    putchar(c);
	  }
	else
	  for(i=3;i>=0;i--){
	    c=(addroff >> 8*i) & 0xFF;
	    putchar(c);
	  }
      }

      if(tsoff >1){
	for(i=NumByteTS-1;i>=0;i--){
	  c=(tsoff >> 8*i) & 0xFF;
	  putchar(c);
	}
      }
    }
}

/****************************************************************/
getset2T(typeptr,NextAddrptr,NextTSptr,tokenA,numcharA)
int *typeptr;
unsigned long *NextAddrptr,*NextTSptr;
char tokenA[];
int *numcharA;
{ 
    if(gettoken(token,&numchar)==EOF)
      return(EOF);
    *typeptr=token[0]-48;
    gettoken(tokenA,&numcharA);
    *NextAddrptr = char2int(tokenA,numcharA,'x');
    gettoken(token,&numchar);
    *NextTSptr = char2int(token,numchar,'d');
}
