#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <stdlib.h>
#include "cablinux.h"

// structure -----------------------------------------------------------------
struct cabheader
{
	BYTE sig[4];			
	DWORD res1;				
	DWORD size;			
	DWORD res2;				
	DWORD offsetfiles;		
	DWORD res3;				
	BYTE versionMIN;	
	BYTE versionMAJ;		
	WORD nfolders;			
	WORD nfiles;			
	WORD flags;				
	WORD setID;			
	WORD cabID;				
} cheader;

struct cabfolder
{
	DWORD offsetdata;		
	WORD ndatab;			
	WORD typecomp;			
} cfolder;

struct cabfile
{
	DWORD usize;			
	DWORD uoffset;			
	WORD index;				
	WORD date;				
	WORD time;				
	WORD fattr;				
	BYTE name[MAXFILENAME];		
} cfile[MAXFILES];

struct cabdata
{
	DWORD checksum;			
	WORD ncbytes;			
	WORD nubytes;			
} cdata[MAXDATABLOCKS];


// global variables ----------------------------------------------------------
char *files[MAXFILES];
char *outfile;
FILE *fpout;
FILE *tempfile;
int tempoffset=0;

// misc ----------------------------------------------------------------------
int sizefile(char *filename)
{
	int size=0;
	FILE *fp=fopen(filename,"rb");
	if(!fp) { printf("<< error (sizefile): could not open %s >>\n", filename); return -1; }
	while(fgetc(fp)!=EOF) { ++size; }
	fclose(fp);
	return size;
}

int number_of_datablocks(int nof)
{
	int i, size=0;
	for(i=0;i<nof;++i)
	{ size+=sizefile(files[i]); }
	return size/DATABLOCKSIZE+1 ;
}

int maketempfile(int nof)
{
  int i;
  FILE *fp;
  char *tmp;

  tempfile=fopen("tmp007","wb");
  if(!tempfile) { printf("<< error tempfile: could not open tempfile >>\n"); return -1; }
  for(i=0;i<nof;++i)
    {
      fp=fopen(files[i],"rb");
      if(!fp) { printf("<< error: could not open %s >>\n", files[i]); return -1; }
      tmp = (char *) calloc(sizefile(files[i]),sizeof(char));
      fread(tmp,sizefile(files[i]), 1, fp);
      fwrite((char *) tmp, sizefile(files[i]), 1, tempfile);
      free(tmp);
      fclose(fp);
    }
  fclose(tempfile);

  return 1;
}



// cheader related functions --------------------------------------------------
void cheader_init(char *sign, int res_fields, int vmaj, int vmin, int nfolders, int nfiles, int flags, int setID, int cabID)
{
	int i;

	for(i=0;i<4;++i) { cheader.sig[i]=*(sign+i); }
	cheader.res1=res_fields;
	cheader.res2=res_fields;
	cheader.res3=res_fields;
	cheader.versionMAJ=vmaj;
	cheader.versionMIN=vmin;
	cheader.nfolders=nfolders;
	cheader.nfiles=nfiles;
	cheader.flags=flags;
	cheader.setID=setID;
	cheader.cabID=cabID;
}

void cheader_size(int size) { cheader.size=size; }

void cheader_offsetfiles(int offset) { cheader.offsetfiles=offset; }

// cfolder related functions --------------------------------------------------
void cfolder_init(int nod, int type)
{
  cfolder.ndatab=nod; 
  cfolder.typecomp=type;
}

void cfolder_offsetdata(int offset) { cfolder.offsetdata=offset; }


// cfile related functions ----------------------------------------------------
void cfile_init(int findex, int usize, int index, char *filename)
{
	int i;
	char year[4], month[3], day[2];
	char hour[2], minute[2], second[2];
	int jaar, maand, dag;
	int h, m, s;
	struct stat attributes;
	time_t tmp;
	//char lijn[26];
	char *lijn;

	cfile[findex].usize=usize;
	cfile[findex].index=index;
		stat(filename,&attributes);
		tmp=attributes.st_mtime;
		lijn=ctime(&tmp);
		for(i=0;i<4;++i) { *(year+i)=*(lijn+i+20); }
		for(i=0;i<3;++i) { *(month+i)=*(lijn+i+4); }
		for(i=0;i<2;++i) { *(day+i)=*(lijn+i+8); }
			jaar=atoi((const char *)year);
			dag=atoi((const char *)day); // next: way too long source-code for 'converting' Apr->4, May->5, Jan->1 (you get the picture)
			if(month[0]=='J') { if(month[1]=='a') { maand=1;} else if(month[1]=='u') { if(month[2]=='n') { maand=6; } else maand=7; } }
			else if(month[0]=='F') { maand=2; }
			else if(month[0]=='M') { if(month[1]=='a') { if(month[2]=='r') maand=3; else maand=5; } }
			else if(month[0]=='A') { if(month[1]=='p') maand=4; else maand=8; }
			else if(month[0]=='S') maand=9;
			else if(month[0]=='O') maand=10;
			else if(month[0]=='N') maand=11;
			else if(month[0]=='D') maand=12;
	cfile[findex].date=((jaar-1980)<<9)+(maand<<5)+(dag); 
		stat(filename,&attributes);
		tmp=attributes.st_mtime;
		lijn=ctime(&tmp);
		for(i=0;i<2;++i) { *(hour+i)=*(lijn+i+11); }
		for(i=0;i<2;++i) { *(minute+i)=*(lijn+i+14); }
		for(i=0;i<2;++i) { *(second+i)=*(lijn+i+17); }
			h=atoi((const char *)hour);
			m=atoi((const char *)minute);
			s=atoi((const char *)second);
	cfile[findex].time=(h<<11)+(m<<5)+(s/2); 
	cfile[findex].fattr=ATTR_e|ATTR_a;  
		for(i=0;i<strlen(filename);++i) { cfile[findex].name[i]=*(filename+i); } 
		cfile[findex].name[strlen(filename)]=STRINGTERM;
}

void cfile_uoffset(int findex, int offset) { cfile[findex].uoffset=offset; }


// cdata related functions ----------------------------------------------------
void cdata_init(int dindex, int checksum) { cdata[dindex].checksum=checksum; }

void cdata_ncbytes(int dindex, int nob, int nof) 
{ 
	int i,size=0;
	if(dindex<nob-1) { cdata[dindex].ncbytes=DATABLOCKSIZE; } 
	else {	for(i=0;i<nof;++i) { size+= sizefile(files[i]); }
			cdata[dindex].ncbytes=size-dindex*DATABLOCKSIZE; }	
}

void cdata_nubytes(int dindex, int nob, int nof) 
{
	int i,size=0;
	if(dindex<nob-1) { cdata[dindex].nubytes=DATABLOCKSIZE; } 
	else {	for(i=0;i<nof;++i) { size+= sizefile(files[i]); }
			cdata[dindex].nubytes=size-dindex*DATABLOCKSIZE; }	
}

//writing -------------------------------------------------------------------
int writecheader()
{
  if(  fwrite((char *) &cheader.sig, 4, 1, fpout) &&
       fwrite((char *) &cheader.res1, 4, 1, fpout) &&
       fwrite((char *) &cheader.size, 4, 1, fpout) &&
       fwrite((char *) &cheader.res2, 4, 1, fpout) &&
       fwrite((char *) &cheader.offsetfiles, 4, 1, fpout) &&
       fwrite((char *) &cheader.res3, 4, 1, fpout) &&
       fwrite((char *) &cheader.versionMIN, 1, 1, fpout) &&
       fwrite((char *) &cheader.versionMAJ, 1, 1, fpout) &&
       fwrite((char *) &cheader.nfolders, 2, 1, fpout) &&
       fwrite((char *) &cheader.nfiles, 2, 1, fpout) &&
       fwrite((char *) &cheader.flags, 2, 1, fpout) &&
       fwrite((char *) &cheader.setID, 2, 1, fpout) &&
       fwrite((char *) &cheader.cabID, 2, 1, fpout) ) return 1;
  else return -1;
}      

int writecfolder()
{
  if(  fwrite((char *) &cfolder.offsetdata, 4, 1, fpout) &&
       fwrite((char *) &cfolder.ndatab, 2, 1, fpout) &&
       fwrite((char *) &cfolder.typecomp, 2, 1, fpout) ) return 1;
  else return -1;
}

int writecfile(int index)
{
  if(  fwrite((char *) &cfile[index].usize, 4, 1, fpout) &&
       fwrite((char *) &cfile[index].uoffset, 4, 1, fpout) &&
       fwrite((char *) &cfile[index].index, 2, 1, fpout) &&
       fwrite((char *) &cfile[index].date, 2, 1, fpout) &&
       fwrite((char *) &cfile[index].time, 2, 1, fpout) &&
       fwrite((char *) &cfile[index].fattr, 2, 1, fpout) &&
       fwrite((char *) &cfile[index].name, strlen(files[index])+1, 1, fpout) ) return 1;
  else return -1;
}

int writedata(int index)
{
  FILE *fp;
  char *tmp;
  
  fp=fopen("tmp007","rb");
  if(!fp) { printf("<< error: could not open tempfile >>\n"); return -1; }
  fseek(fp,tempoffset,SEEK_SET);
  tmp = (char *) calloc(cdata[index].nubytes,sizeof(char));
  fread(tmp,cdata[index].nubytes,1,fp);
  fwrite((char *) tmp, cdata[index].nubytes, 1, fpout);
  free(tmp);
  tempoffset=ftell(fp);
  fclose(fp);
  return 1;
}

int writecdata(int index)
{
  if(  fwrite((char *) &cdata[index].checksum, 4, 1, fpout) &&
       fwrite((char *) &cdata[index].ncbytes, 2, 1, fpout) &&
       fwrite((char *) &cdata[index].nubytes, 2, 1, fpout) &&
       writedata(index) ) return 1;
  else return -1;
}

//----------------------------------------------------------------------------
// main
//----------------------------------------------------------------------------
int main(int argc, char *argv[])
{
	int nof=0;	// number of files
	int nod=0;	// number of datablocks	
	int i,offsetdata=0,size=0;

	if(argc<3) { printf("usage: cab_c infiles file.cab\n"); return 0; }

	nof=argc-2; 
	for(i=0;i<nof;++i) { files[i]=argv[1+i]; printf("file [%d] = %s\n", i+1, files[i]);}
	nod=number_of_datablocks(nof);
	outfile=argv[argc-1]; printf("=> to: %s ", outfile); 
	
	// cheader
	cheader_init("MSCF",0,1,3,1,nof,0,1234,0);
	cheader_offsetfiles(CFI_start);
	// cfolder
	cfolder_init(nod,0);
	for(i=0;i<nof;++i) { offsetdata+=16+strlen(files[i])+1; }
	cfolder_offsetdata(CFI_start+offsetdata);
	// cfile
	for(i=0;i<nof;++i)
	{
		cfile_init(i,sizefile(files[i]),0,files[i]);
		if(i==0) cfile_uoffset(i,0);
		else cfile_uoffset(i,cfile[i-1].uoffset+sizefile(files[i-1]));
	}
	// cdata
	for(i=0;i<nod;++i)
	{
		cdata_init(i,0);
		cdata_ncbytes(i,nod,nof);
		cdata_nubytes(i,nod,nof); 
	}
	// cabinet size
	size=36+8+nof*16; 
	for(i=0;i<nof;++i) { size+=strlen(files[i])+1; } 
	size+=nod*8; 
	for(i=0;i<nod;++i) { size+=cdata[i].nubytes; } 
	cheader_size(size); printf("[%ld bytes] (", cheader.size);
	
	// write
	fpout=fopen(outfile,"wb");
	if(!fpout) { printf("<< error outfile: could not open %s >>\n", outfile); return 0; }
	   // cheader
	if( writecheader() ) printf("H");
	else { printf("error writing cheader\n"); return 0; }
	   // cfolder
	if( writecfolder() )  printf("F");
	else { printf("error writing cfolder\n"); return 0; }
	   // cfile
	for(i=0;i<nof;++i) 
	  { 
	    if( writecfile(i) ) printf("."); 
	    else { printf("error writing cfile\n"); return 0; } 
	  }
	if(maketempfile(nof)) ; else return 0;
	   // cdata
	for(i=0;i<nod;++i) 
	  { 
	    if( writecdata(i) ) printf("*"); 
	    else { printf("error writing cdata\n"); return 0; }
	  } 
	fclose(fpout);

	printf(") done.\n");
	// delete .tmp

	system("rm tmp007");
	return 1;
}






 

