/****************************************************************
	    Map data generator for CAR RACE  version 3.20
		  Programmed by Y.Nishida
					[ May.10, 1990 ]
 ****************************************************************/

#include	<stdio.h>
#include	<memory.h>
#include	<strings.h>
#include	<ctype.h>

#define		unchar		unsigned char
#define		unshort		unsigned short
#define		TRUE		1
#define		FALSE		0
#define		SLTTOP		1
#define		SLTNXT		0
#define		LNKBTM		-1

#define		MAXPNL		0x3000
#define		MAXSLT		2048
#define		MAXROM		256
#define		MAXWLD		576
#define		WLDLNX		32
#define		WLDLNY		18

#define		high(n)		(((n)>>8)&0xff)
#define		low(n)		((n)&0xff)

typedef	struct	{
		short	flg ;
		short	lnk ;
		short	btm ;
		short	len ;
	} arptr ;

arptr	sltptr[MAXSLT] ;		/* slit pointer		*/
unshort	sltofs[MAXSLT] ;		/* slit offset address	*/
unshort	pnlofs[MAXPNL] ;		/* panel offset address	*/
unchar	pnlbuf[MAXPNL*4] ;		/* panel buffer		*/
unshort	sltbuf[MAXSLT][16] ;		/* slit data buffer	*/
unshort	rombuf[MAXROM][16] ;		/* room data buffer	*/
unshort	wldbuf[WLDLNY][WLDLNX] ;	/* world data buffer	*/

int	pnlcnt  = 0 ;			/* panel counter	*/
int	sltcnt  = 0 ;			/* slit counter		*/
int	romcnt  = 0 ;			/* room counter		*/
int	wldcnt  = 0 ;			/* world counter	*/
int	pnllen  = 0 ;			/* panel length		*/
int	sltlen  = 0 ;			/* slit length		*/
int	errcnt  = 0 ;			/* panel error counter	*/
int	slttop  = 0x8000 ;		/* slit top address	*/
int	clrcode ;			/* clear code		*/

main( argc,argv )
int	argc ;
char	**argv ;
{
	FILE	*tp ;
	char	pnl_name[65] ;
	char	tbl_name[65] ;
	char	srn_name[65] ;
	char	slt_name[65] ;
	char	wld_name[65] ;
	int	i,ct = 1 ;

	puts( "\nMap data generator for CAR RACE  version 3.20 ( 32*16 world )" ) ;
	if ( argc != 3 ) {
		fputs( "usage: carmap <panel name> <map name>\n",stderr ) ;
		exit( 1 ) ;
	}
	strcpy( pnl_name,*(++argv)) ;
	strcat( pnl_name,".PCK" ) ;
	readpnl( pnl_name ) ;

	strcpy( tbl_name,*(++argv)) ;
	strcat( tbl_name,".WTB" ) ;
	if (( tp = fopen( tbl_name,"r" )) == NULL ) {
		perror( tbl_name ) ;
		exit( 1 ) ;
	}
	while ( fgets( srn_name,60,tp ) != NULL ) {
		if ( srn_name[0] == '#' ) {
			slttop = getaddr( srn_name+1 ) ;
		}
		else if ( srn_name[0] == '*' ) {
			for ( i=0 ; i<4 ; i++ ) setwld( 0 ) ;
		}
		else if ( srn_name[0] != '\n' ) {
			set_sname( srn_name ) ;
			makemap( ct++,srn_name ) ;	/*  make map data	*/
		}
	}
	fclose( tp ) ;

	prsslt() ;				/* compress slit data	*/
	strcpy( slt_name,*argv ) ;
	strcat( slt_name,".SLT" ) ;
	strcpy( wld_name,*argv ) ;
	strcat( wld_name,".WLD" ) ;
	genmap( wld_name,slt_name ) ;			/* ganerate map file	*/
	exit( 0 ) ;
}

/******* set screen file name ***********************************/

set_sname( ss )
char	*ss ;
{
	while ((*ss != '\0') && (*ss != '\n')) ss++ ;
	strcpy( ss,".SCR" ) ;
}

/******* get start address **************************************/

int getaddr( str )
char	*str ;
{
	int	ch,nn=0 ;

	ch = *str ;
	while ( isxdigit( ch )) {
		     if ( ch <= '9' ) ch -= '0' ;
		else if ( ch <= 'F' ) ch -= 'A'-10 ;
		else		      ch -= 'a'-10 ;
		nn = nn * 16 + ch ;
		ch = *++str ;
	}
	return ( nn ) ;
}

/****************************************************************/
/*	Read panel data form file				*/
/****************************************************************/

readpnl( fname )
char	*fname ;
{
	FILE	*rp ;

	if (( rp = fopen( fname,"r" )) == NULL ) {
		perror( fname ) ;
		exit( 1 ) ;
	}
	puts( "read panel data." ) ;
	fread( &clrcode,sizeof(    int),     1,rp ) ;
	fread( &pnlcnt ,sizeof(    int),     1,rp ) ;
	fread( &pnllen ,sizeof(    int),     1,rp ) ;
	fread( pnlofs  ,sizeof(unshort),pnlcnt,rp ) ;
	fread( pnlbuf  ,sizeof( unchar),pnllen,rp ) ;
	fclose( rp ) ;
}

/****************************************************************/
/*	Make map data from screen file				*/
/****************************************************************/

makemap( nn,fname )
int	nn ;
char	*fname ;
{
	FILE	*rp ;
	unchar	redbuf[0x2300],pnldat[ 4] ;
	unshort	sltdat[16],romdat[16] ;
	int	snn ;
	register unchar	*dat,*pnl ;
	register int	pct,sct,rct ;

	if (( rp = fopen( fname,"r" )) == NULL ) {
		perror( fname ) ;
		exit( 1 ) ;
	}

	snn = sltcnt ;
	pnl = pnldat ;

	printf( "%2d screen file \"%s\"\t", nn,fname ) ; 
	fread( redbuf,sizeof(unchar),0x2300,rp ) ;
	chkclr( redbuf ) ;

	dat = redbuf ;
	for ( rct=0 ; rct<4 ; rct++ ) {
		for ( sct=0 ; sct<16 ; sct++ ) {
			for ( pct=0 ; pct<16 ; pct++ ) {
				*(pnl+0) = *(dat+ 0) ;
				*(pnl+1) = *(dat+64) ;
				*(pnl+2) = *(dat+ 2) ;
				*(pnl+3) = *(dat+66) ;
				 dat += 4 ;
				sltdat[pct] = (unshort)chkpnl( pnldat ) ;
			}
			dat += 64 ;
			romdat[sct] = (unshort)setslt( sltdat ) ;
		}
		setwld((unshort)setroom( romdat )) ;
	}

	fclose( rp ) ;
	printf( "create %4d slit(s)\n", sltcnt-snn ) ;
}

/*******  check clear code **************************************/

chkclr( buf )
unchar	*buf ;
{
	static	int	ofs[4] = { 0x2100,0x2104,0x2200,0x2204 } ;
	register unchar	*da,*fg ;
	register int	cd,i,j,k,nn ;

	for ( da=buf,nn=0 ; nn<4 ; nn++ ) {
		fg = buf + ofs[nn] ;

		for ( i=0 ; i<32 ; i++ ) {
			for ( j=0 ; j<4 ; j++ ) {
				cd = *fg++ ;
				for ( k=0 ; k<8 ; k++ ) {
					if ( !(cd & 0x80)) *da = (unchar)clrcode ;
					cd <<= 1 ;
					da  += 2 ;
				}
			}
			fg += 4 ;
		}
	}
}

/*******  check panel data  *************************************/

chkpnl( pnl )
unchar	*pnl ;
{
	register int	pnum ;

	if (( pnum = schpnl( pnlofs,pnlbuf,pnl,pnlcnt )) < pnlcnt ) return ( pnum ) ;

	printf( "\nundefined panel ($%02x,$%02x,$%02x,$%02x)\t",
			*pnl,*(pnl+1),*(pnl+2),*(pnl+3)) ;

	if ( ++errcnt > 15 ) {
		puts( "\ntoo meny undefined panel.\naborted." ) ;
		exit( 1 ) ;
	}
	return ( 0 ) ;
}

/*******  set slit data into slit buffer ************************/

setslt( slt )
unshort	*slt ;
{
	register int	snum ;

	if ( sltcnt == 0 ) {
		memcpy( sltbuf[sltcnt++],slt,sizeof(unshort)*16 ) ;
		return( 0 ) ;
	}
	if (( snum = cmpbuf( sltbuf,slt,sltcnt )) < sltcnt ) return ( snum ) ;
	if ( sltcnt == MAXSLT ) {
		fputs( "slit data buffer over.\n",stderr ) ;
		exit( 1 ) ;
	}
	memcpy( sltbuf[sltcnt++],slt,sizeof(unshort)*16 ) ;
	return( snum ) ;
}

/*******  set room data into room buffer ************************/

setroom( rom )
unshort	*rom ;
{
	register int	rnum ;

	if ( romcnt == 0 ) {
		memcpy( rombuf[romcnt++],rom,sizeof(unshort)*16 ) ;
		return( 0 ) ;
	}
	if (( rnum = cmpbuf( rombuf,rom,romcnt )) < romcnt ) return ( rnum ) ;
	if ( romcnt == MAXROM ) {
		fputs( "room data buffer over.\n",stderr ) ;
		exit( 1 ) ;
	}
	memcpy( rombuf[romcnt++],rom,sizeof(unshort)*16 ) ;
	return( rnum ) ;
}

/******* set world data into world buffer ***********************/

setwld( wld )
unshort	wld ;
{
	register int	ix,iy ;

	if ( wldcnt == MAXWLD ) {
		fputs( "world data buffer over.\n",stderr ) ;
		exit( 1 ) ;
	}
	ix = (( wldcnt % 64 ) / 4 ) * 2 + wldcnt % 2 ;
	iy = ( wldcnt / 64 ) * 2 + ( wldcnt % 4 ) / 2 ;
	wldbuf[iy][ix] = wld ;
	wldcnt++ ;
}

/****************************************************************/
/*	Compress slit data					*/
/****************************************************************/

prsslt()
{
	register int	ct,ln ;

	int_slpt() ;			/*  initialize slit pointer	*/

	for ( ln=15 ; ln>0 ; ln-- ) {
		printf( "compress %2d panel(s)\n",ln ) ;
		for ( ct=0 ; ct<sltcnt ; ct++ ) {
			if ( sltptr[ct].flg == SLTTOP ) {
				fit_slit( ln,ct ) ;
			}
		}
	}
	st_slofs() ;
	printf( "compress ratio %2d%%\n", (sltlen*100)/(sltcnt*32)) ;
}

/******* search fitted slit *************************************/

fit_slit( ln,sc )
int	ln,sc ;
{
	register arptr	*sp,*dp,*bp ;
	register int	dc ;

	sp = &sltptr[sc] ;
	dp = sltptr ;

	for ( dc=0 ; dc<sltcnt ; dp++,dc++ ) {
		if ( sc != dc && dp->flg == SLTTOP ) {
			if ( !memcmp( sltbuf[sc],&sltbuf[dp->btm][16-ln],sizeof(unshort)*ln )) {
				bp = &sltptr[dp->btm] ;
				bp->lnk = sc ;
				bp->len = 16-ln ;
				dp->btm = sp->btm ;
				sp->flg = SLTNXT ;
				break ;
			}
		}
	}
}

/******* initialize slit pointer ********************************/

int_slpt()
{
	register arptr	*p ;
	register int	i ;

	for ( p=sltptr,i=0 ; i<sltcnt ; p++,i++ ) {
		p->flg = SLTTOP ;
		p->lnk = LNKBTM ;
		p->btm = i ;
		p->len = 16 ;
	}
}

/******* set slit offset ****************************************/

st_slofs()
{
	register int	 ct,nn ;
	register unshort adrs = 0 ;

	for ( ct=0 ; ct<sltcnt ; ct++ ) {
		if ( sltptr[ct].flg == SLTTOP ) {
			nn = ct ;
			while ( nn != LNKBTM ) {
				sltofs[nn]  = adrs ;
				adrs       += sltptr[nn].len * 2 ;
				nn          = sltptr[nn].lnk ;
			}
		}
	}
	sltlen = adrs ;
}

/****************************************************************/
/*	Generate map data into map file				*/
/****************************************************************/

genmap( wld_name,slt_name )
char	*wld_name,*slt_name ;
{
	FILE	*wp ;

	if (( wp = fopen( wld_name,"w" )) == NULL ) {
		perror( wld_name ) ;
		exit( 1 ) ;
	}
	genwld( wp ) ;
	genrom( wp ) ;
	fclose( wp ) ;

	if (( wp = fopen( slt_name,"w" )) == NULL ) {
		perror( slt_name ) ;
		exit( 1 ) ;
	}
	genslt( wp ) ;
	fclose( wp ) ;

	printf( "\nworld data file    total $%05X bytes write.\n", romcnt*32+0x200 ) ;
	printf( "slit  data file    total $%05X bytes write.\n", sltlen ) ;
	printf( "undefined panel %d.\n", errcnt ) ;
}

/******* generate slit data *************************************/

genslt( wp )
FILE	*wp ;
{
	unshort	d ;
	int	ct,nn,i ;

	for ( ct=0 ; ct<sltcnt ; ct++ ) {
		if ( sltptr[ct].flg == SLTTOP ) {
			nn = ct ;
			while ( nn != LNKBTM ) {
				for ( i=0 ; i<sltptr[nn].len ; i++ ) {
					d = pnlofs[sltbuf[nn][i]] ;	/*  +0x8000  */
					fputc(  low( d ), wp ) ;
					fputc( high( d ), wp ) ;
				}
				nn = sltptr[nn].lnk ;
			}
		}
	}
	printf( "slit  start address $%05X    total %4d slits write.\n", slttop,sltcnt ) ;
}

/******* generate room data *************************************/

genrom( wp )
FILE	*wp ;
{
	unshort	*p,n ;
	int	i,j ;

	for ( p=rombuf[0],i=0 ; i<romcnt ; i++ ) {
		for ( j=0 ; j<16 ; j++ ) {
			n = sltofs[*p++] + slttop ;
			fputc(  low( n ), wp ) ;
			fputc( high( n ), wp ) ;
		}
	}
	printf( "room  start offset  $00200    total %4d rooms write.  ( $%04X )\n",romcnt,romcnt*32 ) ;
}

/******* generate world data ************************************/

genwld( wp )
FILE	*wp ;
{
	unshort	*p ;
	int	i ;

	for ( p=wldbuf[0],i=0 ; i<512 ; p++,i++ ) {
		fputc((int)(*p), wp ) ;
	}
	puts( "\nscreen change room" ) ;
	for ( i=0 ; i<32 ; p++,i++ ) printf( "%02X ", (int)(*p)) ;
	putchar( '\n' ) ;
	for ( i=0 ; i<32 ; p++,i++ ) printf( "%02X ", (int)(*p)) ;

	puts( "\n\nworld start offset  $00000    set 32*16 rooms.         ( $0200 )" ) ;
}
