/*
**	The C64 emulator utilities
**
**	Copyright 1996 by ALE.
**	written by Lutz Sammer.
**
**	Convert binary files to p00 format.
**-----------------------------------------------------------------------------
** $Id: bin2p00.c,v 1.1 1996/05/25 02:06:35 johns Exp root $
** $Log: bin2p00.c,v $
** Revision 1.1  1996/05/25 02:06:35  johns
** Initial revision
**
**-----------------------------------------------------------------------------
*/

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>

/* FIXME: use of gnu features! (I like them very much :) */

#ifndef O_BINARY
#define O_BINARY 0
#endif

/*
**	Convert CBM name to P00 name.
**	(16 character C64 character set to 8 characters limited ASCII set).
*/
char* Cbm2P00Name(const char* cbm,char p00[9])
{
    char buf[16];
    char *dst;
    int i;
    int l;

    dst=buf;
    /*
    **	1) Convert to lowercase, underscore and numbers only.
    */
    for( ;; ++cbm ) {
	switch( *cbm ) {
	    case '\0':
		break;
	    case '-':
	    case ' ':			/* spaces -> underscore */
		*dst++='_';
		continue;
	    case 'a' ... 'z':
	    case '0' ... '9':		/* lowercase and numbers ok */
		*dst++=*cbm;
		continue;
	    case 'A' ... 'Z':		/* uppercase -> lowercase */
		*dst++=*cbm+' ';
		continue;
	    default:
		continue;
	}
	break;
    }
    l=dst-buf;

    if( l>8 ) {
	/*
	**	2) Reduce to 8 characters. (remove from end)
	*/
	memset(buf+l,0,16-l);

	/*
	**	2a) Remove underscores.
	*/
	for( i=15; i>=0 && l>8; --i ) {
	    if( buf[i]=='_' ) {
		buf[i]='\0';
		--l;
	    }
	}
	
	/*
	**	2b) Remove 'a', 'e', 'o' and 'u'.
	*/
	for( i=15; i>=0 && l>8; --i ) {
	    if( buf[i]=='a' || buf[i]=='e' || buf[i]=='o' || buf[i]=='u' ) {
		buf[i]='\0';
		--l;
	    }
	}
	
	/*
	**	2c) Characters.
	*/
	for( i=15; i>=0 && l>8; --i ) {
	    if( isalpha(buf[i]) ) {
		buf[i]='\0';
		--l;
	    }
	}

	/*
	**	2d) Numbers.
	*/
	for( i=15; i>=0 && l>8; --i ) {
	    if( isdigit(buf[i]) ) {
		buf[i]='\0';
		--l;
	    }
	}

	/*
	**	2e) Pack the name.
	*/
	dst=buf;
	for( i=0; i<16; ++i ) {
	    if( buf[i] ) {
		*dst++=buf[i];
	    }
	}
	if( dst-buf!=l )
	    abort();
    }
    if( l==0 ) {
	/*
	**	Empty name.
	*/
	p00[0]='_';
	p00[1]='\0';
	return p00;
    }

    memcpy(p00,buf,l);
    p00[l]='\0';
	    
    return p00;
}

#define STAT_MASK	(S_ISUID | S_ISGID)
#define STAT_PRG	0
#define STAT_SEQ	(S_ISUID)
#define STAT_USR	(S_ISGID)
#define STAT_REL	(S_ISUID|S_ISGID)

/*
**	ALE binary format file types.
*/
int AleFileType(char* name)
{
    struct stat st;
    mode_t m;

    if( stat(name,&st) ) {
	return 0;
    }

    if( S_ISDIR(st.st_mode) ) {
	return 0;
    }

    if( st.st_mode==S_IFREG ) {
	return 'd';
    }
    m=st.st_mode&STAT_MASK;
    switch( m ) {
	case STAT_PRG:
	    return 'p';
	case STAT_SEQ:
	    return 's';
	case STAT_USR:
	    return 'u';
	case STAT_REL:
	    return 'r';
	default:
	    return 'p';
    }
}

/*
**	ALE binary format file types.
*/
int AleFileLocked(char* name)
{
    struct stat st;
    mode_t m;

    if( stat(name,&st) ) {
	return 0;
    }
    if( st.st_mode&S_IWUSR ) {
	return 0;
    }
    return 1;
}

/*
**	Convert file binary to p00 format.
*/
void Convert(char* name)
{
    char p00name[8+4+1];
    char buffer[1024];
    char* e;
    int i;
    int fi;
    int fo;

    if( strlen(name)>16 ) {
	fprintf(stderr,"file name '%s' to long!\n",name);
	return;
    }

    if( (i=AleFileType(name))=='\0' ) {
	fprintf(stderr,"Illegal file '%s'\n",name);
	return;
    }

    printf("Converting %s -> ",name);
    Cbm2P00Name(name,p00name);
    e=strchr(p00name,'\0');
    e[0]='.'; e[1]=i; e[2]='0'; e[3]='0'; e[4]='\0';
    printf("%s\n",p00name);

    /*
    **	Try all possible file names.
    */
    for( e[2]='0'; e[2]<='9'; ++e[2] ) {
	for( e[3]='0'; e[3]<='9'; ++e[3] ) {
	    if( access(p00name,0) ) {
		// printf("Found %s\n",p00name);
		goto found;
	    }
	}
    }
    fprintf(stderr,"Can't convert '%s': out of filenames\n",p00name);
    return;
found:
    if( (fi=open(name,O_RDONLY|O_BINARY,0))==-1 ) {
	fprintf(stderr,"Can't open source '%s'\n",name);
	return;
    }
    if( (fo=open(p00name,O_WRONLY|O_BINARY|O_CREAT,0666))==-1 ) {
	fprintf(stderr,"Can't open destination '%s'\n",p00name);
	close(fi);
	return;
    }
    memset(buffer,0,1024);
    memcpy(buffer,"C64File",7);
    strcpy(buffer+8,name);
    write(fo,buffer,8+17+1);

    for( ;; ) {
	i=read(fi,buffer,sizeof(buffer));
	if( !i ) {
	    break;
	}
	if( write(fo,buffer,i)!=i ) {
	    fprintf(stderr,"Can't write to '%s'\n",p00name);
	    break;
	}
    }

    close(fi);
    close(fo);

    if( AleFileLocked(name) ) {
	chmod(p00name,0444);
    }
}

/*
**	Display the usage.
*/
volatile void Usage(FILE* fp)
{
    fprintf(fp,"bin2p00 Version 1.00 as of %s, Copyright 1996 by ALE\n",
	__DATE__); 
    fprintf(fp,"\tC64 emulator utility written by Lutz Sammer.\n");
    fprintf(fp,"Usage: bin2p00 [-h] files\n");
    fprintf(fp,"\t-h\tPrint this help page.\n");
    fprintf(fp,"\tfiles\tBinary file(s) to be converted to p00 format.\n");
    exit(fp == stderr ? 1 : 0);
}

/*
**	Entry point.
*/
int main(int argc,char** argv)
{
    int i;

    /*
    **	Parse the arguments.
    */
    for( ;; ) {
	switch( i=getopt(argc,argv,"h") ) {
	    case 'h':			/* help */
		Usage(stdout);
	    case ':':
	    case '?':
		Usage(stderr);
	    case -1:
		break;
	    default:
		fprintf(stderr,"Please correct the code\n");
		abort();
	}
	break;
    }

    /*
    **	Do the remaining arguments.
    */
    if( optind>=argc ) {
	fprintf(stderr,"missing files!\n");
	Usage(stderr);
    }
    while( optind<argc ) {
	Convert(argv[optind++]);
    }
    exit(0);
}
