/*
 *	PIC file saver coded by "C"
 *	1989-9 ver 0.00  by Akira-Y.
 *	1990.09.04 TOWNS版 Ver 0.1 by ＭＳどす
 *	1991.01.12 TOWNS版 Ver 0.21 by KXC KAROU･UOTA
 *	1991.01.23 TOWNS版 Ver 0.22 by KXC KAROU･UOTA
 *	1991.01.23 TOWNS版 Ver 0.23 by KXC KAROU･UOTA
 *
 *	　Ｖｅｒ　０．１，及び０．２２で色調が落ちる障害を無
 * 	くしました。
 *	　ＭＳ−ＤＯＳではレイヤ０に各種グラフィックを表示後、
 *	コマンドライン上で、
 *		RUN386 PICS [X Y X' Y'] FILENAME
 *	と入力して下さい。
 *	　Ｔ−ＭＥＮＵからは、各ローダーと組にしたバッチファイル
 *	で御使用下さい。
 *
 *	画面モード９、１０、１１のみで使用可能です。
 *	画面サイズは仮想領域のフルサイズ（５１２×２５６）
 *	迄対応しており、デフォルトのセーブ領域が、
 *		（０，０）−（５１１，２５５）
 *	となっております。
 *	画面モード１７の、５１２×５１２ドットモードをベー
 *	スにする際は、
 *		HS_psetm10
 *		HS_pointm10
 *	各々のセグメントの値を書換え、
 *		header_write()
 *	上記ルーチンのＰＩＣのヘッダフォーマットを５１２×
 *	５１２迄のＸ６（標準ＰＩＣフォーマット）とＴＯＷＮ
 *	Ｓの各モード値に変更して下さい。それ以外の値は使用
 *	しないで下さい。
 *
 *
 */

pragma	on ( Align_members ) ;
pragma	on ( Align_all_labels ) ;
pragma	on ( Align_labels ) ;
pragma	on ( Align_routines ) ;

#include	<stdio.h>
#include	<stdlib.h>
#include	<egb.h>

#define SHORT   short int
#define USHORT  unsigned short int
#define UCHAR   unsigned char
#define LONG	long

/*---------- Please rewrite --------------------------------------------*/
#define		SIZE_OF_X 512		/* your screen X max		*/
#define		SIZE_OF_Y 256		/* your screen Y max		*/
/*----------------------------------------------------------------------*/
#define		SIZE_OF_BUFF 512*256*2	/* file write buff size
					 * you can change this parameter
					 * value limit is 1 ~ max(integer)
					 */

FILE	*fp ;

int	bit_length,			/* *buff_p effective bit length	*/
	buff_length,			/* buff rest			*/
	x0,x1,y0,y1;			/* (x0,y0)-(x1,y1) save area	*/

char	*buff_p,			/* buff's read pointer		*/
	buff[SIZE_OF_BUFF];		/* file read buff		*/

struct	{				/* for use color cash table	*/
	int	color;			/* dual LIST struct		*/
	int	next;
	int	prev;
} 	table[128];
int	color_p;			/*index for new color cash table*/

void	diff_point_mark(),		/* diff. point marking		*/
	header_write(),			/* pic file header write	*/
	press(),			/* compress routine		*/
	press_chain(),			/* chain data write		*/
	write_length(),			/* diff. point length write	*/
	write_color(),			/* color code write		*/
	color_cash_init(),		/* color cash table init.	*/
	new_color(),			/* set new color to table	*/
	set_color(),			/* change top of table		*/
	bit_write(),			/*  n bits write to file	*/
	buff_next_w(),			/* pointer next and write	*/
	buff_flush(),			/* write buff flush		*/
	error(),			/* error mess type and exit	*/
	pset() ;			/* put color to graphic ram	*/

int	point(),			/* get color from graphic ram	*/
	search_col();			/* search color code from table */

extern	void HS_psetm10( int x,int y,int color ) ;
extern	int  HS_pointm10( int x,int y ) ;


/*
 * main procedure of pic saver
 *
 */
void
main(argc, argv)
int	argc;
char	**argv;
{
	char	*file;

	if (argc != 2 && argc != 6) {
		error("usage : pics [x y x' y'] <file>");
	}
	if (argc == 6) {
		x0 = atoi(argv[1]);
		y0 = atoi(argv[2]);
		x1 = atoi(argv[3]);
		y1 = atoi(argv[4]);
		file = argv[5];
		if (x0 > x1 || y0 > y1 || x1 >= SIZE_OF_X || y1 >= SIZE_OF_Y) {
			error( "size over" ) ;
		}
	} else {
		x0 = y0 = 0 ;
		x1 = SIZE_OF_X - 1 ;
		y1 = SIZE_OF_Y - 1 ;
		file = argv[1] ;
	}

	if ( (fp = fopen(file,"wb")) == NULL ){
		error("file can not open") ;	/* ERROR */
	}

	buff_p = buff ;			/* file pointer etc. initialize */
	buff_length = SIZE_OF_BUFF ;
	bit_length = 8 ;

	color_cash_init() ;	/* color cash table init.	*/
	header_write() ;	/* pic file header write	*/
	diff_point_mark() ;	/* color diff. point marking	*/
	press() ;		/* compress exec.		*/
	buff_flush() ;		/* write buff flush		*/
	fclose( fp ) ;
}

void
diff_point_mark()
{
	int	x,y,c,a ;

	c = 0 ;				/* 1st. color = 0 */
	for ( y = y0 ; y <= y1 ; y++ ) {
		for ( x = x0 ; x <= x1 ; x++ ) {
			if ((a = (point(x, y) & 0x7fff)) != c) {
				c = a ;
				pset( x,y,( c|0x8000 )); /* lsb mark */
			} else {
				pset( x,y,a ) ;     /* lsb clr */
			}
		}
	}
}

void
header_write()
{
	bit_write( 8, (LONG)'P');		/* write ID	*/
	bit_write( 8, (LONG)'I');
	bit_write( 8, (LONG)'C');
	bit_write( 8, (LONG)26 );		/* text eof	*/
	bit_write( 8, (LONG)0  );		/* separater	*/
						/* mode		*/
	bit_write(16, (LONG)0x0052 );		/* TOWNS_mode	*/
	bit_write(16, (LONG)15 );		/* color lenght */
	bit_write(16, (LONG)(x1 - x0 + 1));	/* width of x	*/
	bit_write(16, (LONG)(y1 - y0 + 1));	/* width of y	*/
}

void
press()
{
	LONG	l ;	/* for diff. length */
	int	x, y, a;

	l = 0;
	for (y = y0; y <= y1; y++) {
		for (x = x0; x <= x1; x++) {
			++l ;
			if ((a = point(x, y)) & 0x8000) {/* find mark point */
				write_length(l);
				write_color(a);
				press_chain(x, y, a);
				pset(x, y,( a&0x7fff ));
				l = 0;
			}
		}
	}
	write_length(l + 1);	/* end data */
	bit_write(8, (LONG)0);	/* オマケだけど必要 */
}

void
press_chain(x, y, c)
int	x, y, c;
{
	int	yy, d, f;

	f = 0;	/* chain exist flag */
	for (yy = y + 1; yy <= y1; yy++) {
		if (                       point(x, yy) == c) d = 2;
		else if (   --x   >= x0 && point(x, yy) == c) d = 1;
		else if ((x += 2) <= x1 && point(x, yy) == c) d = 3;
		else if ((x -= 3) >= x0 && point(x, yy) == c) d = 4;
		else if ((x += 4) <= x1 && point(x, yy) == c) d = 5;
		else break ;
		pset(x, yy,( c&0x7fff )) ;

		if (f == 0) bit_write(1, (LONG)1);
		if (d == 4) bit_write(4, (LONG)2);
		else if (d == 5) bit_write(4, (LONG)3);
		else if (d <= 3) bit_write(2, (LONG)d);
		f = 1;
	}
	if (f == 0) bit_write(1, (LONG)0);
	else	    bit_write(3, (LONG)0);
}

void
write_length(n)
LONG	n;
{
	int	a;
	LONG	b;

	a = 1 ;
	b = 4 ;
	while (n > b - 2) {
		++a ;
		b <<= 1 ;
	}
	bit_write(a, 0xfffffffe);	/* lsb = 0 , other 31bit = 1 */
	bit_write(a, (LONG)(n + 1 - b / 2));
}

void
write_color(c)
int	c;
{
	int	a;

	if ((a = search_col( c )) != -1) {	/* color exist table ? */
		bit_write(8, (LONG)( a+128 )) ;	/* color table's index */
	} else {
/*		bit_write(16, (LONG)((unsigned int)c / 2));***********/
		bit_write(16, (LONG)((unsigned int)c & 0x7fff));
	}
}
	
int
search_col(c)
int	c ;
{
	int	i ;

	c &= 0x07fff ;
	for ( i = 0 ; i < 128 ; i++ ) {
		if ( table[i].color == c )
			break ;
	}
	if ( i == 128 ) {
		new_color( c ) ;
		return( -1 ) ;
	} else {
		set_color( i ) ;
		return( i ) ;
	}
}

void
color_cash_init()
{
	int	i ;

	for ( i = 0; i < 128; i++ ) {
		table[i].color = 0;
		table[i].prev = i + 1;
		table[i].next = i - 1;
	}
	table[127].prev = 0;
	table[0].next = 127;
	color_p = 0;
}

void
new_color(c)
int	c;
{
	color_p = table[color_p].prev;
	table[color_p].color = c;
}

void
set_color( idx )
int	idx ;
{
	if ( color_p != idx ) {
		/* idx take off from table */
		table[table[idx].prev].next = table[idx].next;
		table[table[idx].next].prev = table[idx].prev;

		/* idx set to new table point*/
		table[table[color_p].prev].next = idx;
		table[idx].prev = table[color_p].prev;  
		table[color_p].prev = idx;
		table[idx].next = color_p;
		color_p = idx;
	}
}

/*
 * size bits write for file
 *
 */

void
bit_write(size, n)
SHORT	size;
LONG	n;
{
	int	i ;
	int	mask ;

	mask = ( 1<<size )-1 ;
	n &= mask;
	
	n <<= ( 32-size ) ;
	while (size > bit_length) {
		for (i = 0; i < bit_length; i++) {
/*			*buff_p = *buff_p + *buff_p + (n < 0);*/
			*buff_p <<= 1;
			*buff_p += ((n & 0x80000000) ? 1 : 0);
				/* if n's msb is set then +1 */
/*			n = n + n;
			size = size - 1;*/
			n <<= 1 ;
			--size ;
		}
		buff_next_w();
	}
	for (i = 0; i < size; i++) {
/*		*buff_p = *buff_p + *buff_p + (n < 0);*/
		*buff_p <<= 1;
		*buff_p += ((n & 0x80000000) ? 1 : 0);
			/* if n's msb is set then +1 */
/*		n = n + n;
		bit_length = bit_length - 1;*/
		n <<= 1 ;
		bit_length-- ;
	}
}

/*
 * buff pointer inc. and write next buff
 */
void
buff_next_w()
{
	if (--buff_length == 0) {
		if (fwrite(buff,1, SIZE_OF_BUFF,fp) != SIZE_OF_BUFF) {
			error("file write error buff_next_w");
		}
		buff_p = buff;
		buff_length = SIZE_OF_BUFF;
	} else {
		++buff_p;
	}
	bit_length = 8;
}

void
buff_flush()
{
	if (bit_length > 0) {
		*buff_p <<= bit_length;
		buff_next_w();
	}
	buff_length = SIZE_OF_BUFF - buff_length;
	if (buff_length > 0) {
		if (fwrite(buff,1, buff_length,fp) != buff_length){
			error("file write error buff_flush");
		}
	}
}

void
error( s )
char	*s ;
{

	{	/* 本複文は，デバッグ用ダンプとして使用可 */
		printf("%s", s);
/**
		DISP_stringRect(buf, CI_Blue, CI_Gray);
		MOU_waitClick(MouseLeftButton);
		MOU_waitRelease(MouseLeftButton, &x, &y);
***/
	}
	exit(1);
}

int	point( x,y )
int	x,y ;
{
	return( HS_pointm10( x,y )) ;
}

/*---------- Please rewrite --------------------------------------------
 *
 * c = point( x,y )
 * dot get to graphic screen
 *
 * in
 *	x : screen X (0~SIZE_OF_X-1)
 *	y : screen Y (0~SIZE_OF_Y-1)
 * out
 *	c : color code 16bit
 *		0     : bright
 *		1..5  : blue
 *		6..10 : red
 *		11..15: green
 */

/*---------- Please rewrite --------------------------------------------
 *
 * pset(x, y, c)
 * dot set to graphic screen
 *
 * in
 *	x : screen X (0~SIZE_OF_X-1)
 *	y : screen Y (0~SIZE_OF_Y-1)
 *	c : color code 16bit
 *		0     : bright
 *		1..5  : blue
 *		6..10 : red
 *		11..15: green
 */

void	pset( x,y,color )
int	x,y,color ;
{
	HS_psetm10( x,y,color ) ;
}
