/****************************************************************
	    Map data generator for CAR RACE  version 1.01
		  Programmed by Y.Nishida
					[ May.18, 1989 ]
 ****************************************************************/

#include	<stdio.h>
#include	<strings.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		4096
#define		MAXSLT		2048
#define		MAXROM		256
#define		MAXWLD		256
#define		WLDLNX		16
#define		WLDLNY		16

#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		*/
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	*/
unshort	sltofs[MAXSLT] ;		/* slit offset address	*/

int	pnlcnt  = 0 ;			/* panel counter	*/
int	sltcnt  = 0 ;			/* slit counter		*/
int	romcnt  = 0 ;			/* room counter		*/
int	wldcnt  = 0 ;			/* world counter	*/
int	sltlen  = 0 ;			/* slit length		*/
int	clrcode = 0 ;			/* clear code		*/

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

	puts( "\nMap data generator for CAR RACE  version 1.01" ) ;
	if ( argc != 2 && argc != 3 ) {
		fputs( "usage: carmap <map name> [clear code]\n",stderr ) ;
		exit( 1 ) ;
	}
	strcpy( tbl_name,*(++argv)) ;
	strcat( tbl_name,".TBL" ) ;
	if (( tp = fopen( tbl_name,"r" )) == NULL ) {
		perror( tbl_name ) ;
		exit( 1 ) ;
	}
	if ( argc == 3 ) {
		clrcode = atoi( *(argv+1)) & 0xff ;
	}

	while ( fgets( srn_name,60,tp ) != NULL ) {
		set_sname( srn_name ) ;
		makemap( ct++,srn_name ) ;	/*  make map data	*/
	}
	fclose( tp ) ;

	arslit() ;				/* slit data archive	*/

	strcpy( map_name,*argv ) ;
	strcat( map_name,".MAP" ) ;
	genmap( map_name ) ;			/* ganerate map file	*/
	exit( 0 ) ;
}


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

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


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

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

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

	pn = pnlcnt ;
	sn = sltcnt ;
	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++ ) {
				 pa   =  pnldat ;
				*pa++ = *(dat+ 0) ;
				*pa++ = *(dat+64) ;
				*pa++ = *(dat+ 2) ;
				*pa++ = *(dat+66) ;
				 dat += 4 ;
				sltdat[pct] = (unshort)setpnl( pnldat ) ;
			}
			dat += 64 ;
			romdat[sct] = (unshort)setslt( sltdat ) ;
		}
		setwld((unshort)setroom( romdat )) ;
	}

	fclose( rp ) ;
	printf( "create %4d panels  %4d slits\n", pnlcnt-pn, sltcnt-sn ) ;
}


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

chkclr( buf )
unchar	*buf ;
{
	static	int	ofs[4] = { 0x2100,0x2104,0x2200,0x2204 } ;
	auto	unchar	*da,*fg ;
	auto	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 ;
		}
	}
}


/*******  set panel data into panel buffer **********************/

setpnl( pnl )
unchar	*pnl ;
{
	int	pnum ;

	if ( pnlcnt == 0 ) {
		memcpy( pnlbuf[pnlcnt++],pnl,sizeof(unchar)*4 ) ;
		return( 0 ) ;
	}

	for ( pnum=0 ; pnum<pnlcnt ; pnum++ ) {
		if ( !memcmp( pnlbuf[pnum],pnl,sizeof(unchar)*4 ))
			return( pnum ) ;
	}
	if ( pnlcnt == MAXPNL ) {
		fputs( "panel data buffer over.\n",stderr ) ;
		exit( 1 ) ;
	}
	memcpy( pnlbuf[pnlcnt++],pnl,sizeof(unchar)*4 ) ;
	return( pnum ) ;
}


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

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

	if ( sltcnt == 0 ) {
		memcpy( sltbuf[sltcnt++],slt,sizeof(unshort)*16 ) ;
		return( 0 ) ;
	}

	for ( snum=0 ; snum<sltcnt ; snum++ ) {
		if ( !memcmp( sltbuf[snum],slt,sizeof(unshort)*16 ))
			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 ;
{
	int	rnum ;

	if ( romcnt == 0 ) {
		memcpy( rombuf[romcnt++],rom,sizeof(unshort)*16 ) ;
		return( 0 ) ;
	}

	for ( rnum=0 ; rnum<romcnt ; rnum++ ) {
		if ( !memcmp( rombuf[rnum],rom,sizeof(unshort)*16 ))
			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 ;
{
	int	ix,iy ;

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


/******* memory compere *****************************************/

memcmp( s1,s2,nn )
register unchar	*s1,*s2 ;
register int	nn ;
{
	register int	st ;

	while ( nn-- ) {
		if ( st = *s1++ - *s2++ ) break ;
	}
	return( st ) ;
}


/******* memory copy ********************************************/

memcpy( dd,ss,nn )
register unchar	*dd,*ss ;
register int	nn ;
{
	while ( nn-- ) {
		*dd++ = *ss++ ;
	}
}


/****************************************************************/
/*	Archive slit data					*/
/****************************************************************/

arslit()
{
	int	ct,ln ;

	puts( "archive slit data." ) ;
	int_slpt() ;			/*  initialize slit pointer	*/

	for ( ln=15 ; ln>0 ; ln-- ) {
		printf( "fit %d 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\045\n", (sltlen*100)/(sltcnt*32)) ;
}

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

fit_slit( ln,sc )
int	ln,sc ;
{
	arptr	*sp,*dp,*bp ;
	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()
{
	arptr	*p ;
	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()
{
	int	ct,nn ;
	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( fname )
char	*fname ;
{
	FILE	*wp ;
	int	pnl,ad ;

	if (( wp = fopen( fname,"w" )) == NULL ) {
		perror( fname ) ;
		exit( 1 ) ;
	}
	ad  = 0x8000 ;
	ad  = genwld( wp,ad ) ;
	ad  = genrom( wp,ad ) ;
	pnl = ad ;
	ad  = genpnl( wp,ad ) ;

	if ( ad > 0xffff ) {
		fputs( "panel data over the bank.\n",stderr ) ;
		exit( 1 ) ;
	}
	while ( ad++ < 0x10000 ) fputc( 0x00,wp ) ;

	genslt( wp,pnl ) ;
	fclose( wp ) ;
}


/*******  generate panel data ***********************************/

genpnl( wp,ad )
FILE	*wp ;
int	ad ;
{
	int	i ;

	printf( "panel start address $%05x\t", ad ) ;

	for ( i=0 ; i<pnlcnt ; i++ ) {
		fwrite( pnlbuf[i],sizeof(unchar),4,wp ) ;
	}
	printf( "total %d panels write.\n", pnlcnt ) ;
	return ( ad + pnlcnt*4 ) ;
}


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

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

	printf( " slit start address $18000\t" ) ;

	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 = sltbuf[nn][i] * 4 + ad ;
					fputc(  low( d ), wp ) ;
					fputc( high( d ), wp ) ;
				}
				nn = sltptr[nn].lnk ;
			}
		}
	}
	printf( "total %d slits write. ( length=$%05x )\n", sltcnt,sltlen ) ;
}


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

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

	printf( " room start address $%05x\t", ad ) ;

	for ( p=rombuf[0],i=0 ; i<romcnt ; i++ ) {
		for ( j=0 ; j<16 ; j++ ) {
			n = sltofs[*p++] + 0x8000 ;
			fputc(  low( n ), wp ) ;
			fputc( high( n ), wp ) ;
		}
	}
	printf( "total %d rooms write.\n", romcnt ) ;
	return( ad + romcnt*32 ) ;
}


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

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

	printf( "\nworld start address $%05x\t", ad ) ;

	for ( p=wldbuf[0],i=0 ; i<MAXWLD ; p++,i++ ) {
		fputc(  low( *p ), wp ) ;
		fputc( high( *p ), wp ) ;
	}
	printf( "set %d rooms\n",MAXWLD ) ;
	return ( ad + MAXWLD*2 ) ;
}
