/************************************************************************
		     ＣＧＸファイルを圧縮する。

		Copyright (c) 1994.2.9 by Nintendo Co.,ltd.

		      Programmed by K.Ohta

 ************************************************************************/

#include	<stdlib.h>
#include	<stdio.h>
#include	<strings.h>

#define		TEN_KAZU	(128*128*4)	/* 圧縮する点の数 */


static	unsigned char	data_buff[TEN_KAZU] ;
static	int		data_count, data_index ;

static	unsigned char	palet_color[16] ;


/***********************************************************************
		１ビットデータを data_buff に下位から順に書き込む
		戻り値：なし
***********************************************************************/
void		write_bit( bit )
int		bit ;
{
	data_buff[ data_index ] &= 0xFF >> ( 8 - data_count ) ;

	if ( bit )
	data_buff[ data_index ] |= 1 << data_count ;

	data_count = ++ data_count & 7 ;

	if ( 0 == data_count )	++ data_index ;
}


/***********************************************************************
		数値データを data_buff に書き込む
		戻り値：なし
***********************************************************************/
void		write_number( number )
int		number ;
{
	unsigned int		bit_count = 15, bit_flag = 0x8000 ;

	while ( ( number & bit_flag ) == 0 ) {
		bit_flag >>= 1 ;
		-- bit_count ;
	}

	bit_flag = 0x0001 ;
	while ( bit_count > 0 ) {
		write_bit( 1 ) ;
		write_bit( number & bit_flag ) ;
		bit_flag <<= 1 ;
		-- bit_count ;
	}

	write_bit( 0 ) ;
}


/***********************************************************************
		パレットカラーデータを data_buff に書き込む
		戻り値：なし
***********************************************************************/
void		write_palet_color( color )
int		color ;
{
	write_bit( color & 0x01 ) ;
	write_bit( color & 0x02 ) ;
	write_bit( color & 0x04 ) ;
	write_bit( color & 0x08 ) ;
}


/***********************************************************************
		カラーデータを data_buff に書き込む
		戻り値：なし
***********************************************************************/
void		write_color( color )
int		color ;
{
	write_bit( color & 0x08 ) ;
	write_bit( color & 0x04 ) ;
	write_bit( color & 0x02 ) ;
	write_bit( color & 0x01 ) ;
}


/***********************************************************************
		パレットデータを data_buff に書き込む
		戻り値：なし
***********************************************************************/
void		write_palet( palet )
int		palet ;
{
	write_bit( palet & 0x04 ) ;
	write_bit( palet & 0x02 ) ;
	write_bit( palet & 0x01 ) ;
}


/***********************************************************************
		フラグデータを data_buff に書き込む
		戻り値：なし
***********************************************************************/
void		write_flag( flag )
int		flag ;
{
	write_bit( flag & 0x01 ) ;
	write_bit( flag & 0x02 ) ;
}


/***********************************************************************
		単純圧縮
		戻り値：圧縮データのビット数
***********************************************************************/
int		as_line_0( f, buff, line )
int		f ;
unsigned char	*buff, *line ;
{
	int		c, n, x, i ;
	int		count, color ;

	data_index = data_count = 0 ;
	write_bit( 0 ) ;

	/*****  ライン圧縮 */
	for ( x = 127 ; x >= 0 ; ) {

		for ( i = x-1, count = 1, color = line[x] ;
			i >= 0 && color == line[i] ;
			-- i, ++ count ) ;

		write_number( count ) ;

		for ( i = 0 ; i < 7 ; ++ i ) {
			if ( color == palet_color[ i ] ) {
				write_palet( i ) ;
				break ;
			}
		}
		if ( i == 7 ) {
			write_palet( 7 ) ;
			write_color( color ) ;
		}
		x -= count ;
	}

	/*****  合計ビット数 */
	x = ( data_index << 3 ) + data_count ;

	/*****  合計バイト数 */
	n = x + 7 >> 3 ;

	/*****  バッファコピー */
	for ( i = 0 ; i < n ; ++ i )
		buff[ i ] = data_buff[ i ] ;

	return( x ) ;
}


/***********************************************************************
		複雑圧縮
		戻り値：圧縮データのビット数
***********************************************************************/
int		as_line_1( f, buff, now_line, old_line )
int		f ;
unsigned char	*buff, *now_line, *old_line ;
{
	int		c, n, x, i ;
	int		old_count, now_count, old_color, now_color ;

	data_index = data_count = 0 ;
	if ( f )  write_bit( 1 ) ;

	/*****  ライン圧縮 */
	for ( x = 127 ; x >= 0 ; ) {

		/*****  今の区間パラメータ */
		for ( i = x-1, now_count = 1, now_color = now_line[x] ;
			i >= 0 && now_color == now_line[i] ;
			-- i, ++ now_count ) ;

		/*****  過去の区間パラメータ */
		for ( i = x-1, old_count = 1, old_color = old_line[x] ;
			i >= 0 && old_color == old_line[i] ;
			-- i, ++ old_count ) ;

		/*****  新区間作成 */
		if ( now_color != old_color ) {
			write_number( now_count ) ;
			write_flag( 2 ) ;
			for ( i = 0 ; i < 7 ; ++ i ) {
				if ( now_color == palet_color[ i ] ) {
					write_palet( i ) ;
					break ;
				}
			}
			if ( i == 7 ) {
				write_palet( 7 ) ;
				write_color( now_color ) ;
			}
			x -= now_count ;
			continue ;
		}

		/***** 伸び縮み区間作成 */
		if ( now_count != old_count ) {
			n = now_count - old_count ;
			if ( n > 0 ) {
				/*****  伸びた区間 */
				write_number( n ) ;
				write_flag( 1 ) ;

				/*****  伸びた先にゴマを付ける */
				i = x - now_count ;
				if ( i >= 0 ) {
					c = old_line[ x - old_count ] ;
					old_line[ i ] = c ;
				}
			} else {
				/*****  縮んだ区間 */
				write_number( -n ) ;
				write_flag( 3 ) ;

				/*****  縮んだ先にゴマを付ける */
				i = x - old_count ;
				if ( i >= 0 ) {
					c = old_line[ i ] ;
				} else {
					c = 0 ;
				}
				old_line[ x - now_count ] = c ;
			}
			x -= now_count ;
			continue ;
		}

		/*****  連続する同じ区間作成 */
		n = 0 ;
		while ( now_color == old_color && now_count == old_count ) {
			++ n ;
			if ( ( x -= now_count ) < 0 )  break ;

			/*****  今の区間パラメータ */
			for ( i = x-1, now_count = 1, now_color = now_line[x] ;
				i >= 0 && now_color == now_line[i] ;
				-- i, ++ now_count ) ;

			/*****  過去の区間パラメータ */
			for ( i = x-1, old_count = 1, old_color = old_line[x] ;
				i >= 0 && old_color == old_line[i] ;
				-- i, ++ old_count ) ;
		}
		write_number( n ) ;
		write_flag( 0 ) ;
	}

	/*****  合計ビット数 */
	x = ( data_index << 3 ) + data_count ;

	/*****  合計バイト数 */
	n = x + 7 >> 3 ;

	/*****  バッファコピー */
	for ( i = 0 ; i < n ; ++ i )
		buff[ i ] = data_buff[ i ] ;

	return( x ) ;
}


/***********************************************************************
		パレットデータを作る。
		戻り値：なし
***********************************************************************/
make_palet( picture, line_su, color )
unsigned char	*picture, *color ;
int		line_su ;
{
	unsigned char	now_line[128], old_line[128] ;
	int		c, n, x, y, i, count[16] ;
	int		old_count, now_count, old_color, now_color ;


	/*****  カラーカウント初期化 */
	for ( n = 0 ; n < 16 ; ++ n ) {
		color[ n ] = n ;
		count[ n ] = 0 ;
	}

	/*****  過去のライン初期化 */
	for ( n = 0 ; n < 128 ; ++ n ) {
		now_line[ n ] = 0 ;
	}

	/*****  ライン毎に圧縮 */
	for ( y = 0 ; y < line_su ; ++ y ) {

		/*****  新ライン取り込み */
		for ( n = 0 ; n < 128 ; ++ n ) {
			old_line[ n ] = now_line[ n ] ;
			now_line[ n ] = picture[ ( y << 7 ) | n ] ;
		}

		/*****  ライン圧縮 */
		for ( x = 127 ; x >= 0 ; ) {

			/*****  今の区間パラメータ */
			for ( i = x-1, now_count = 1, now_color = now_line[x] ;
				i >= 0 && now_color == now_line[i] ;
				-- i, ++ now_count ) ;

			/*****  過去の区間パラメータ */
			for ( i = x-1, old_count = 1, old_color = old_line[x] ;
				i >= 0 && old_color == old_line[i] ;
				-- i, ++ old_count ) ;

			/*****  新区間作成 */
			if ( now_color != old_color ) {
				++ count[ now_color ] ;
				x -= now_count ;
				continue ;
			}

			/***** 伸び縮み区間作成 */
			if ( now_count != old_count ) {
				n = now_count - old_count ;
				if ( n > 0 ) {
					/*****  伸びた先にゴマを付ける */
					i = x - now_count ;
					if ( i >= 0 ) {
						c = old_line[ x - old_count ] ;
						old_line[ i ] = c ;
					}
				} else {
					/*****  縮んだ先にゴマを付ける */
					i = x - old_count ;
					if ( i >= 0 ) {
						c = old_line[ i ] ;
					} else {
						c = 0 ;
					}
					old_line[ x - now_count ] = c ;
				}
				x -= now_count ;
				continue ;
			}

			/*****  連続する同じ区間作成 */
			n = 0 ;
			while ( now_color == old_color && now_count == old_count ) {
				++ n ;
				if ( ( x -= now_count ) < 0 )  break ;

				/*****  今の区間パラメータ */
				for ( i = x-1, now_count = 1, now_color = now_line[x] ;
					i >= 0 && now_color == now_line[i] ;
					-- i, ++ now_count ) ;

				/*****  過去の区間パラメータ */
				for ( i = x-1, old_count = 1, old_color = old_line[x] ;
					i >= 0 && old_color == old_line[i] ;
					-- i, ++ old_count ) ;
			}
		}
	}

	/*****  多く使われている色順にソート */
	for ( y = 0 ; y < 15 ; ++ y ) {
	for ( x = y + 1 ; x < 16 ; ++ x ) {
		if ( count[ color[y] ] < count[ color[x] ] ) {
			n = color[y] ;
			color[y] = color[x];
			color[x] = n ;
		}
	}
	}
}


/***********************************************************************
		データをファイルに書き出す
		戻り値：なし
***********************************************************************/
write_data( buff, bit_su, fp )
unsigned char	*buff ;
int		bit_su ;
FILE		*fp ;
{
	static unsigned char	bit_data=0 ;
	static int		bit_count=0 ;
	unsigned char		flag ;
	int			bit, i, index ;


	if ( bit_su == 0 ) {
		if ( bit_count != 0 )  putc( bit_data, fp ) ;
		bit_data = bit_count = 0 ;
		return ;
	}

	for ( i = 0 ; i < bit_su ; ++ i ) {
		flag = 1 << ( i & 7 ) ;
		index = i >> 3 ;
		bit = buff[ index ] & flag ;

		flag = 1 << bit_count ;
		if ( bit )  bit_data |= flag ;
		bit_count = ++ bit_count & 7 ;
		if ( bit_count == 0 ) {
			putc( bit_data, fp ) ;
			bit_data = 0 ;
		}
	}
}


/***********************************************************************
		圧縮データ作成
		戻り値：圧縮データのバイト数
***********************************************************************/
int		make_ascgx( picture, line_su, fp )
unsigned char	*picture ;
int		line_su ;
FILE		*fp ;
{
	unsigned char	now_line[128], old_line[128] ;
	unsigned char	buff0[512], buff1[512] ;
	int		n0, n1, n, y, bit_su ;


	/*****  パレットカラー作成 */
	make_palet( picture, line_su, palet_color ) ;
	data_count = data_index = 0 ;
	for ( n = 0 ; n < 7 ; ++ n ) {
		write_palet_color( palet_color[n] ) ;
	}
	write_data( data_buff, 28, fp ) ;
	bit_su = 28 ;

	/*****  過去のライン初期化 */
	for ( n = 0 ; n < 128 ; ++ n ) {
		now_line[ n ] = 0 ;
	}

	/*****  ライン毎に圧縮 */
	for ( y = 0 ; y < line_su ; ++ y ) {

		/*****  新ライン取り込み */
		for ( n = 0 ; n < 128 ; ++ n ) {
			old_line[ n ] = now_line[ n ] ;
			now_line[ n ] = picture[ ( y << 7 ) | n ] ;
		}

		/*****  ライン圧縮 */
		if ( 1 ) {	/* 毎ライン判断 */
			n0 = as_line_0( 1, buff0, now_line ) ;
			n1 = as_line_1( 1, buff1, now_line, old_line ) ;
			if ( n0 <= n1 ) {
				write_data( buff0, n0, fp ) ;
				bit_su += n0 ;
			} else {
				write_data( buff1, n1, fp ) ;
				bit_su += n1 ;
			}
		} else {
			n1 = as_line_1( 0, buff1, now_line, old_line ) ;
			write_data( buff1, n1, fp ) ;
			bit_su += n1 ;
		}
	}

	write_data( buff0, 0, fp ) ;
	return ( bit_su + 7 >> 3 ) ;
}


/***********************************************************************
		１６色のポイントデータを得る
		戻り値：なし
***********************************************************************/
void		get_picture16( picture, fp )
unsigned char	*picture ;
FILE		*fp ;
{
	unsigned char	chr_data[0x20], flag[8], page0, page1, page2, page3 ;
	int		x, y, n, ni, yi, xi ;

	flag[0] = 0x80 ;
	flag[1] = 0x40 ;
	flag[2] = 0x20 ;
	flag[3] = 0x10 ;
	flag[4] = 0x08 ;
	flag[5] = 0x04 ;
	flag[6] = 0x02 ;
	flag[7] = 0x01 ;

	for ( n = 0 ; n < TEN_KAZU ; ++n ) {
		picture[n] = 0 ;
	}

	for ( n = 0 ; n < TEN_KAZU/64 ; ++n ) {
		ni = (n & 0xFFF0) << 6 | (n & 0x000F) << 3 ;
		if ( fread( chr_data, sizeof(char), 0x20, fp ) != 0x20 )
			for ( y = 0 ; y < 0x20 ; ++y )  chr_data[ y ] = 0 ;

		for ( y = 0 ; y < 8 ; ++y ) {
			yi = ni | y << 7 ;
			page0 = chr_data[ ( y << 1 ) + 00 ] ;
			page1 = chr_data[ ( y << 1 ) + 01 ] ;
			page2 = chr_data[ ( y << 1 ) + 16 ] ;
			page3 = chr_data[ ( y << 1 ) + 17 ] ; 

			for ( x = 0 ; x < 8 ; ++x ) {
				xi = yi | x ;
				if ( page0 & flag[x] )  picture[ xi ] |= 1 ;
				if ( page1 & flag[x] )  picture[ xi ] |= 2 ;
				if ( page2 & flag[x] )  picture[ xi ] |= 4 ;
				if ( page3 & flag[x] )  picture[ xi ] |= 8 ;
			}
		}
	}
}


/***********************************************************************
		メインプログラム
***********************************************************************/
main( argc,argv )
int	argc ;
char	**argv ;
{
	unsigned char	in_fname[64], out_fname[64], picture[TEN_KAZU] ;
	FILE		*in_fp, *out_fp ;
	int		m, n, s, id ;
	int		start_n, end_n, step_n ;


	if ( argc < 3 ) {
		printf( "｜16色のＣＧＸファイルを圧縮します。\n") ;
		printf( "｜使用方法：\n" ) ;
		printf( "｜ ascgx 入力ファイル 出力ファイル [開始(0-63)] [終了(0-63)] [幅(1-16)]\n" ) ;
		printf( "｜                                 （16キャラクタを1とする。）\n" ) ;
		exit( 1 ) ;
	}

	/*****  ファイルオープン */
	strcpy( in_fname, *++argv ) ;
	if ( (in_fp = fopen( in_fname, "r" )) == NULL ) {
		perror( in_fname ) ;
		exit( 1 ) ;
	}
	strcpy( out_fname, *++argv ) ;
	if ( (out_fp = fopen( out_fname, "w" )) == NULL ) {
		perror( out_fname ) ;
		exit( 1 ) ;
	}

	/*****  パラメータ決定 */
	if ( argc > 3 )	start_n = atoi( *++argv ) ;
	else		start_n = 0 ;

	if ( argc > 4 )	end_n = atoi( *++argv ) ;
	else		end_n = ( TEN_KAZU - 1 ) / 1024 ;

	if ( argc > 5 ) {
		step_n = atoi( *++argv ) ;
	} else {
		if ( ( end_n - start_n + 1 ) >= 16 )  step_n = 16 ;
		else	step_n = end_n - start_n + 1 ;
	}

	if ( ( end_n - start_n + 1 ) % step_n != 0 ) {
		printf( "Warning !! [開始]から[終了]を[幅]で割りきれません。\n" ) ;
	}
	if ( start_n > end_n || step_n < 1 || step_n > 16 ) {
		printf( "パラメータが異常です。\n" ) ;
		exit( 1 ) ;
	}

	/*****  １点１バイトのピクチャーデータに変換 */
	get_picture16( picture, in_fp ) ;
	fclose( in_fp ) ;

	/*****  圧縮データ作成 */
	start_n *= 1024 ;
	step_n *= 1024 ;
	end_n = 1024 * ( end_n + 1 ) ;

	m = s = 0 ;
	for ( id = start_n ; id < end_n ; id += step_n ) {
		n = make_ascgx( picture + id, step_n / 128, out_fp ) ;
		printf("Address%5X hex :%6d byte (%5.1f %% )\n",m,n,200.0*n/step_n ) ;
		m += n ;
		s += step_n ;
	}
	printf("  Total%5X hex :%6d byte (%5.1f %% )\n",m,m,200.0*m/s ) ;

	/*****  終了 */
	if ( fclose( out_fp ) == EOF ) {
		printf( "Data write error !!\n" ) ;
		exit( 1 ) ;
	}

	exit( 0 ) ;
}


