/**************************************************
 *
 * FileID.c
 *
 *    Identify picture/IFF file
 *      and give info about it.
 *
 *    Copyright(c), 1990 Lloyd B. Eldred
 *                     & Fredrick R Homan
 **************************************************/
#include <stdio.h>
#include <math.h>

typedef unsigned char UBYTE;     /* 8 bits unsigned */
typedef unsigned short UWORD;    /* 16 bits unsigned */
typedef short WORD;		 /* 16 bits signed */
typedef long LONG;               /* 32 bits signed */

#define GIF 1
#define IFF 2
#define ANIM 3
#define HIRES 0x8000
#define LACE 0x4
#define HAM 0x800
#define HALFBRITE 0x80
#define TRUE 1
#define FALSE 0

void main(argc, argv)
char **argv;
{
	FILE *picfile;
	char buf[256],buf2[256];
   char *filename; 
	int type,width,height,color,i,planes,start,start2,start3,camg,offset;
	UBYTE c,c1,c2,c3,c4,gct,cr,sf,bci,p;
	long vpmode,volume,dt;
	UWORD sps,tempo,par;
	WORD numframes;
	unsigned long reltime;
	LONG size,makelong(char,char,char,char);
   UBYTE VERBOSE, TERSE,COMMENT;
   int cfind(char * , int , char * , int);
   int cfindws(char * , int , char * , int, int);
   void Gif_PlainText(FILE *, UBYTE),Gif_Application(FILE *, UBYTE),
      Gif_Comment(FILE *, UBYTE),Gif_Image(FILE *, UBYTE),
      Gif_Graphic(FILE *, UBYTE),fi_getc(FILE *,char *),
      fi_exit();
   float ar,tempo2;
   
  	if ((argc < 2) | (argc > 3)) {
		printf(
		"FileID -- File IDentifier. V 1.20\n"
      "Copyright (c) 1990, Lloyd B. Eldred and Fredrick R Homan.\n"
		"Gives information about GIF and IFF files\n"
		"Usage: FileID [-flag] filename\n"
      "Valid flags: -c : Give GIF89a included comments only\n"
      "             -t : terse mode, gives one line output\n"
      "             -v : verbose mode, gives lots of extra GIF information\n");
		fi_exit();
	}
   
   COMMENT = FALSE;
   VERBOSE = FALSE;
   TERSE = FALSE;
   filename = argv[1];
   
   c1 = argv[1][0];
   c2 = argv[1][1];
   if(c1 == '-'){
      if(c2 == 'v' || c2 == 'V'){
          VERBOSE=TRUE;
          if(argc == 3) filename = argv[2];
          else{
             printf("No file specified\n");
             fi_exit();
             }
       }
       else if(c2 == 't' || c2 == 'T'){
          TERSE=TRUE;
          if(argc == 3) filename = argv[2];
          else{
             printf("No file specified\n");
             fi_exit();
             }
       }
       else if(c2 == 'c' || c2 == 'C'){
          COMMENT=TRUE;
          if(argc == 3) filename = argv[2];
          else{
             printf("No file specified\n");
             fi_exit();
             }
       } 
       else{
            printf("Invalid flag -%c\n",c2);
            fi_exit();
       }
      }   
      
	if ((picfile = fopen(filename, "rb")) == NULL) {
			perror(filename);
			fi_exit();
   		}
 
  
	/* search for header line */
	for (;;) {
		if (fread(buf, sizeof(buf), 1,picfile) != 1) {
			fprintf(stderr, "Unrecognized filetype\n");
			fclose(picfile);
			fi_exit();
		}
		start = cfind(buf,sizeof(buf),"GIF",3);
		if (start != -1){
			type=GIF;
			break;
		}
		
		start = cfind(buf,sizeof(buf),"FORM",4);
		if (start != -1){
			type=IFF;
			break;
		}
		
		start = cfind(buf,sizeof(buf),"CAT ",4);
		if (start != -1){
			type=IFF;
			break;
		}

		start = cfind(buf,sizeof(buf),"LIST",4);
		if (start != -1){
			type=IFF;
			break;
		}


	        
	}
	
	fclose(picfile);

/********************************************************************/
/*                          GIF analysis                            */
/********************************************************************/
	
	if(type == GIF){

		if(start != 0){
			printf("Padding encountered at the beginning of the file.\n" 
                "Some viewers may not like this file.\n");
		   start2 = cfindws(buf,sizeof(buf),"GIF",3,128);
		   if (start2 != -1){
			   printf("Macintosh resource fork found. Recommend using"
                " StripGif to remove the header.\n");
            start=start2;
         }
      }

 		c1 = buf[start+3];
		c2 = buf[start+4];
		c3 = buf[start+5];

		if(!TERSE){
         printf("GIF -- Version %c%c%c:",c1,c2,c3);
		   if( c1 != '8' || (c2 != '7' && c2 != '9') || c3 != 'a'){
		      printf(" non recognized version, the following are guesses");
            }
    		if (COMMENT) printf("\n");
         if(!COMMENT) printf("\n  Image Header\n");
      }
      else printf("GIF%c%c%c",c1,c2,c3);
         
		c1 = buf[start+6];
		c2 = buf[start+7];
		c3 = buf[start+8];
		c4 = buf[start+9];
		c  = buf[start+10];
      bci= buf[start+11];
      p= buf[start+12];
			
  /* Expand Logical Screen Descriptor packed fields */
      gct = (UBYTE)(c & 0x80);
      cr =  (UBYTE)(c & 0x70)>>4;
      cr++;
      sf =  (UBYTE)(c & 0x08);
		planes = (int)(c & 0x7) + 1;
      par=(UWORD)p+15;

      ar = par/64.0;
         
      if(VERBOSE){
         if(gct && !sf)
           printf("    Global Color Table Flag set (table unsorted)\n");
         if(gct && sf)
           printf("    Global Color Table Flag set (table sorted)\n");
         printf("    Color resolution: %d bits of RG&B per palette color\n",
               cr);
         if(gct)
            printf("    Background color is color number %d\n",bci);
         if(!p)
            printf("    No pixel aspect ratio information given.\n");
         else
            printf("    Aspect ratio (pixel's width/height) is %g\n",ar);
      }
      
		color=1;
		
		for(i=0; i<planes ; i++)
		  color = color * 2;
		
		width=c1 + 256 * c2;
		
		height=c3 + 256 * c4;
		
		if(!TERSE && !COMMENT) 
                 printf("    Width : %5d\n"
		                  "    Height: %5d\n"
		                  "    Colors: %5d\n",width,height,color);
                        
      if(TERSE) printf(" %5d %5d %5d\n",width,height,color);              
		
      /* 
       *Done with logical screen descriptor, continue
       * only if user has selected verbose mode
       */
   if(!VERBOSE && !COMMENT) fi_exit();
   
   offset=start+13;
   
   if(gct){ /* if Global Color Table, skip it... */
      offset += 3 * color; 
      }
   
	if ((picfile = fopen(filename, "rb")) == NULL) {
			perror(filename);
			fi_exit();
   		}
   if (fseek(picfile,offset,1) == -1){
         printf(" Unexpected end of file. (1)\n");  
         fclose(picfile);
         fi_exit();
      }
   
   for(;;){
   
      fi_getc(picfile,&c1);
      
      switch(c1){
         case 0x2c:
            Gif_Image(picfile,!COMMENT);
            break;
         
         case 0x21:
            if (!COMMENT) printf("  Extension:");
            fi_getc(picfile,&c2);
            
            if(c2 == 0xf9) Gif_Graphic(picfile,!COMMENT);
            if(c2 == 0xfe) {
             if (!COMMENT) printf("Comment\n");
             Gif_Comment(picfile,1);
            }
            if(c2 == 0x01) Gif_PlainText(picfile,!COMMENT);
            if(c2 == 0xff) Gif_Application(picfile,!COMMENT);

            break;
              
         case 0x3b:
            printf("  Normal End of GIF reached.\n");               
            fclose(picfile);
            fi_exit();
            break;
/***/            
         default:
            printf("  Unknown block type: (Label 0x%x)\n",c1);
            fclose(picfile);
            fi_exit();
            break;
         
         } /* end of switch */

   } /* end of for(;;) */
 
   
	} /* End of GIF analysis section */

/********************************************************************/
/*                          IFF analysis                            */
/********************************************************************/

	if(type == IFF){
		printf(" IFF file:");

/* ILBM analysis */

		start3 = cfind(buf,sizeof(buf),"ANIM",4);
		if (start3 != -1){
			printf(" ANIM (Cel Animation)\n");
			type = ANIM;
			start = cfind(buf,sizeof(buf),"ILBM",4);
			if(start != -1 && start < sizeof(buf) + 5){
				c1 = buf[start-4];
				c2 = buf[start-3];
				c3 = buf[start-2];
				c4 = buf[start-1];
				size = makelong(c1,c2,c3,c4);
				if ((picfile = fopen(filename, "rb")) == NULL) {
					perror(argv[1]);
					fi_exit();
				}
				if(fseek(picfile,(long)(start+size),0)
					== -1){
					printf(" Can't find animation info. (fseek past end of file)\n");
					fi_exit();
				}
			if (fread(buf2, sizeof(buf2), 1,picfile) != 1) {
				fprintf(stderr, 
					" Can't find animation info. (fread past end of file)\n");
				}
				
			}
			start = cfind(buf2,sizeof(buf2),"ANHD",4);
			if(start != -1 && start < sizeof(buf2) - 25){
				c1 = buf2[start+8];
				c2 = buf2[start+9];
				c3 = buf2[start+23];
				c4 = buf2[start+24];
				
				c = buf2[start+25];

				printf("   Compression Mode      :");
				if(c1 == 0) printf(" None\n");
				if(c1 == 1) printf(" XOR ILBM mode\n");
				if(c1 == 2) printf(" Long Delta mode\n");
				if(c1 == 3) printf(" Short Delta mode\n");
				if(c1 == 4) printf(
					" Gen. short/long Delta mode\n");
				if(c1 == 5) printf(
					" Byte Vertical Delta mode\n");
				if(c1 == 'J') printf(
					" Eric Graham's technique\n");
			
				reltime = (unsigned long)
					makelong(c2,c3,c4,c);
				printf("   Frame timing (jiffies): %d\n"
					,reltime);
				printf("   (1 jiffy=1/60 sec)\n");
			}
			else
				printf(
				" Can't find animation information. (Can't find ANHD)\n");
		}
		start = -1;
		if(type != ANIM){
			start = cfind(buf,sizeof(buf),"ILBM",4);
			if(start != -1)
				printf(
				" ILBM (Picture: InterLeaved Bit Map)\n");
			start2 = cfind(buf,sizeof(buf),"ACBM",4);
			if(start2 != -1)
				printf(
				" ACBM (Picture: Amiga Contiguous Bit Map)\n");
		}
			
		if (start != -1 || start2 != -1 || start3 != -1){
			start = cfind(buf,sizeof(buf),"BMHD",4);
			
			if(start == -1){
				printf(" Color information not found\n");
				fi_exit();
			}
			
			
			if(start > (sizeof(buf)-12)){
				printf(
				" Incomplete color information found\n");
				fi_exit();
			}
			c1 = buf[start+8];
			c2 = buf[start+9];
			c3 = buf[start+10];
			c4 = buf[start+11];
			c  = buf[start+16];

			width = (int)c2 + (int)(c1<<8);
			height= (int)c4 + (int)(c3<<8);
						
			planes=(int)c;
			color=1;
			for(i=0; i<planes ; i++)
		  		color = color * 2;
  /* HAM or Extra Half Bright? */
  /* Search for CAMG block */
			camg=0;
			start = cfind(buf,sizeof(buf),"CAMG",4);
							
			if(start != -1) { /* check vpmode flag */
				camg=1;
				c1 = buf[start+8];
				c2 = buf[start+9];
				c3 = buf[start+10];
				c4 = buf[start+11]; 
				vpmode = makelong(c1,c2,c3,c4);

				if(vpmode & HAM) /* HAM flag */
						color = 4096;
			} /* end if(start != -1) */
				 	
			printf("   Width : %5d",width);
			if(camg == 1 && (vpmode & HIRES))
				printf("  Hi-Res");
			printf("\n");
			
			printf("   Height: %5d",height);
			if(camg == 1 && (vpmode & LACE))
				printf("  Interlaced");
			printf("\n");
						
			printf("   Colors: %5d",color);
			if(camg == 1 && (vpmode & HAM))
				printf("  HAM");
			if(camg == 1 && (vpmode & HALFBRITE))
				printf("  Extra-Halfbright");
			printf("\n");
			fi_exit();
			
		} /* end if ILBM */
		
/* check for other IFF file types */

		start = cfind(buf,sizeof(buf),"FTXT",4);
		if (start != -1){
			printf(" FTXT (Formatted Text)\n");
			fi_exit();
		}
		
		start = cfind(buf,sizeof(buf),"SMUS",4);
		if (start != -1){
			printf(" SMUS (Simple MUsical Score)\n");
			start = cfind(buf,sizeof(buf),"SHDR",4);
			if (start == -1 || start > sizeof(buf) - 11){
				printf(" Can't read music info.\n");
				fi_exit();
			}
			c1 = buf[start+8];
			c2 = buf[start+9];
			
			tempo = (UWORD)c2 + (UWORD)(c1<<8);

         tempo2 = tempo/128.0; 
						
			c3 = buf[start+10];

			volume = ((int)c3 * 100)/127;

			c = buf[start+11];
			
			printf("   Tempo  (1/4 notes/min): %g\n",tempo2);
			printf("   Volume (percent)      : %d\n",volume);
			printf("   Tracks                : %d\n",(int)c);
	      
         fi_exit();
		}
		
		start = cfind(buf,sizeof(buf),"8SVX",4);
		if (start != -1){
			printf(" 8SVX (8-Bit Sample Voice)\n");
			start = cfind(buf,sizeof(buf),"VHDR",4);
			if (start == -1 || start > sizeof(buf)-27){
				printf(" Can't read voice info\n");
				fi_exit();
			}
			c1 = buf[start+20];
			c2 = buf[start+21];
			
			sps = (UWORD)c2 + (UWORD)(c1<<8);
			
			c1 = buf[start+24];
			c2 = buf[start+25];
			c3 = buf[start+26];
			c4 = buf[start+27];
			
			volume = makelong(c1,c2,c3,c4);
			volume = (volume * 100) / 65536;
			printf("   Samples per second: %d\n",sps);
			printf("   Volume (percent)  : %d\n",volume);
			
			fi_exit();
		}

		start = cfind(buf,sizeof(buf),"AIFF",4);
		if (start != -1){
			printf(" AIFF (Apple Audio IFF)\n");
			fi_exit();
		}

		start = cfind(buf,sizeof(buf),"ANBM",4);
		if (start != -1){
			printf(" ANBM (Animated bitmap -- Deluxe Video)\n");
			start = cfind(buf,sizeof(buf),"FSQN",4);
			if(start == -1 || start > sizeof(buf) - 13 ){
				printf(" Can't read animation info\n");
				fi_exit();
			}
			c1 = buf[start+8];
			c2 = buf[start+9];
			
			numframes = (WORD)c2 + (WORD)(c1<<8);
			
			c1 = buf[start+10];
			c2 = buf[start+11];
			c3 = buf[start+12];
			c4 = buf[start+13];
			
			dt = makelong(c1,c2,c3,c4);
			
			printf("   Number of Frames              : %d\n",
				numframes);
			printf("   Time between frames (jiffies) : %d\n",
				dt);
			printf("   (1 jiffy=1/60 sec)\n");
			
			fi_exit();
		}

		start = cfind(buf,sizeof(buf),"BANK",4);
		if (start != -1){
			printf(" BANK (SoundQuest MIDI data-dump)\n");
			fi_exit();
		}

		start = cfind(buf,sizeof(buf),"HEAD",4);
		if (start != -1){
			printf(" HEAD (Flow Idea Processor form)\n");
			fi_exit();
		}

		start = cfind(buf,sizeof(buf),"MIDI",4);
		if (start != -1){
			printf(" MIDI\n");
			fi_exit();
		}

		start = cfind(buf,sizeof(buf),"PGTB",4);
		if (start != -1){
			printf(" PGTB (ProGram TraceBack diagnostic dump image)\n");
			fi_exit();
		}

		start = cfind(buf,sizeof(buf),"SYTH",4);
		if (start != -1){
			printf(" SYTH (SoundQuest Master Librarian file)\n");
			fi_exit();
		}

		start = cfind(buf,sizeof(buf),"WORD",4);
		if (start != -1){
			printf(" WORD (ProWrite data file)\n");
			fi_exit();
		}
		
		start = cfind(buf,sizeof(buf),"C100",4);
		if (start != -1){
			printf(" C100 (Cloanto Italia, private word processing form)\n");
			fi_exit();
		}

		start = cfind(buf,sizeof(buf),"PDEF",4);
		if (start != -1){
			printf(" PDEF (Deluxe Print page definition)\n");
			fi_exit();
		}

		start = cfind(buf,sizeof(buf),"SHAK",4);
		if (start != -1){
			printf(" SHAK (Shakespeare data file)\n");
			fi_exit();
		}

		start = cfind(buf,sizeof(buf),"VDEO",4);
		if (start != -1){
			printf(" VDEO (Deluxe Video file)\n");
			fi_exit();
		}

		start = cfind(buf,sizeof(buf),"SAMP",4);
		if (start != -1){
			printf(" SAMP (Sound Sample)\n");
			fi_exit();
		}

		start = cfind(buf,sizeof(buf),"TDDD",4);
		if (start != -1){
			printf(" TDDD (Turbo Silver file)\n");
			fi_exit();
		}

		start = cfind(buf,sizeof(buf),"SC3D",4);
		if (start != -1){
			printf(" SC3D (Sculpt 3-D file)\n");
			fi_exit();
		}

		start = cfind(buf,sizeof(buf),"TEXT",4);
		if (start != -1){
			printf(" TEXT (unformatted ASCII text)\n");
			fi_exit();
		}

		start = cfind(buf,sizeof(buf),"FNTR",4);
		if (start != -1){
			printf(" FNTR (Raster Font)\n");
			fi_exit();
		}

		start = cfind(buf,sizeof(buf),"FNTV",4);
		if (start != -1){
			printf(" FNTV (Vector Font)\n");
			fi_exit();
		}

		start = cfind(buf,sizeof(buf),"GSCR",4);
		if (start != -1){
			printf(" GSCR (General-use musical SCoRe)\n");
			fi_exit();
		}
		
		start = cfind(buf,sizeof(buf),"PICS",4);
		if (start != -1){
			printf(" PICS (Macintosh picture)\n");
			fi_exit();
		}

		start = cfind(buf,sizeof(buf),"PLBM",4);
		if (start != -1){
			printf(" PLBM (Obsolete)\n");
			fi_exit();
		}

		start = cfind(buf,sizeof(buf),"USCR",4);
		if (start != -1){
			printf(" USCR (Uhura Sound software musical score)\n");
			fi_exit();
		}

		start = cfind(buf,sizeof(buf),"UVOX",4);
		if (start != -1){
			printf(" UVOX (Uhura Sound software Macintosh Voice)\n");
			fi_exit();
		}

		start = cfind(buf,sizeof(buf),"CLIP",4);
		if (start != -1){
			printf(" CLIP (Clipboard data)\n");
			fi_exit();
		}

		start = cfind(buf,sizeof(buf),"ARC ",4);
		if (start != -1){
			printf(" ARC  (Archive form)\n");
			fi_exit();
		}

		start = cfind(buf,sizeof(buf),"ATXT",4);
		if (start != -1){
			printf(" ATXT\n");
			fi_exit();
		}

		start = cfind(buf,sizeof(buf),"PTXT",4);
		if (start != -1){
			printf(" PTXT\n");
			fi_exit();
		}

		start = cfind(buf,sizeof(buf),"RGBX",4);
		if (start != -1){
			printf(" RGBX\n");
			fi_exit();
		}

		start = cfind(buf,sizeof(buf),"CDAT",4);
		if (start != -1){
			printf(" CDAT\n");
			fi_exit();
		}

		start = cfind(buf,sizeof(buf),"MSMP",4);
		if (start != -1){
			printf(" MSMP\n");
			fi_exit();
		}

		start = cfind(buf,sizeof(buf),"FIGR",4);
		if (start != -1){
			printf(" FIGR\n");
			fi_exit();
		}

		start = cfind(buf,sizeof(buf),"MOVI",4);
		if (start != -1){
			printf(" MOVI\n");
			fi_exit();
		}

		printf(" Unknown type\n");

	} /* End of IFF code */
	
} /* end of main() */

int cfind(buffer,blength,string,slength)
char *buffer;
char *string;
int blength,slength;
{
	int i,start;
	int found;
	
	start = 0;
	
	while(start < (blength-slength)){
		found = 1;
		
		for(i=0;i<slength;i++)
			if(buffer[start+i] != string[i]){
				found = 0;
				break;
			}
		
		if(found == 1) return(start);
		
		start++;
	}
	
	return(-1);  /* string not found */
		
} /* end of cfind() */	

int cfindws(buffer,blength,string,slength,init)
char *buffer;
char *string;
int blength,slength,init;
{
	int i,start;
	int found;
	
	start = init;
	
	while(start < (blength-slength)){
		found = 1;
		
		for(i=0;i<slength;i++)
			if(buffer[start+i] != string[i]){
				found = 0;
				break;
			}
		
		if(found == 1) return(start);
		
		start++;
	}
	
	return(-1);  /* string not found */
		
} /* end of cfindws() */	

LONG makelong(c1,c2,c3,c4)
char c1,c2,c3,c4;
{
	LONG value;
	
	value = (LONG)c4 + (LONG)(c3<<8) + (LONG)(c2<<16) + (LONG)(c1<<24);
	
	return(value);
	
} /* end of makelong() */

void fi_exit()
{
   exit(0);
}   