/*
*	Yamana's Otomeza Plug-in Tool
*		Ｐｉ セーバ
*	
*	1995.08.06	
*	1995.08.27	部分セーブへの対処をあきらめた(~〜~)
*	1995.09.01	パターンチェック部のバグ除去
*	
*	問題点
*	・32ドット単位でしか保存できない。
*	  メモリがふんだんにあるか、高速マシンであるか、多少圧縮率を犠牲にする
*	  か、このうちどれかを選ばない限り無理。
*	
*	"Pi" (c) Yanagisawa
*/

#include	"otome_pi.h"

const char	longname[]	= "ぱいせーば";
const char	extend[]	= "PI";

#define		USE_FILE	PI_SAVE
#include	"otome_pi.c"

char	def_comment[]= "PiSAVER/OTOMEZA";

typedef
	struct
	{	char	mode;		/* パレットなし= 0x80 */
		char	n;
		char	m;			/* 縦横比 n/m */
		char	pix;		/* 4 or 8 */
		char	name[4];	/* 機種名 */
		short	mdat_len;	/* 機種予約コードサイズ */
		char	*mdat;		/* 機種予約コード */
		short	x;
		short	y;
	}
	PiHeader;


#define	MAX_PIXPAGESIZE		(32*1024)
int 	pixPageSize= MAX_PIXPAGESIZE;
int 	pixPage    = 0;

int		Pi_countBit = 0;
int		Pi_maxBit   = MAX_PIXPAGESIZE*8 ;
char	*ColTbl;

int 	precol;
char	**pixel;

char	*user	='\0';
char	*comment='\0';

/********************************************************************/

int 	Pi_getColor256();
int 	Pi_getColor16();
int 	Pi_getColor256D();
int 	Pi_getColor16D();
void	Pi_putColor256();
void	Pi_putColor16();
void	Pi_putPos();
void	Pi_putLen();

int 	Width;
char	*f2buf;

int 	Pi_posTable[][2] = 
			{	{-2, 0 },	/* 0 :前のピクセルにより変化するので注意 */
				{ 0,-1 },	/* 1: 1ライン上				*/
				{ 0,-2 },	/* 2: 2ライン上				*/
				{ 1,-1 },	/* 3: 1ライン上の1ドット右	*/
				{-1,-1 }	/* 4: 1ライン上の1ドット左	*/
			};

int Pi_encode( head,image )
PiHeader	*head;
char		*image;
{
	int 	i;
	int 	c0,c1,l[5];
	int 	Height,imagemax;
	int 	prepos,pos, off, len;
	int 	(*Pi_getColor)();
	void	(*Pi_putColor)();
	
	/* 32ドット単位で渡される */
	Width    = (head->x+31) & 0xffe0 ;
	Height   =  head->y ;
	imagemax = Width*Height;
	
	if( (f2buf=(char*)PI_MALLOC( Width*2+2 ))==NULL )
		return PI_ERROR_NO_MEMORY;
	
	if( head->pix == 8 )
	{	Pi_getColor	= Pi_getColor256D;
		Pi_putColor	= Pi_putColor256;
	}else
	{	Pi_getColor	= Pi_getColor16D;
		Pi_putColor	= Pi_putColor16;
	}
	for( i=1; i<=4 ; i++ )
		Pi_posTable[i][0] += Pi_posTable[i][1]*Width;
	
	/**************************/
	
	/* (1) 始めの２色で先頭２ラインを埋める */
	c0 = Pi_getColor( 0 );
	for ( i = 0; i<=Width ; i++ )	/* 余分に埋めるのは長さを調べるとき */
		WORD( f2buf+i*2 ) = c0;		/* pos=3,4では1ドットずれているため */
	
	precol = 0;
	Pi_putColor( c0 & 0xff );
	Pi_putColor( c0 >>8    );
	
	/* 一番最初のパターン */
	/* 上２ラインがみんな同じなので一番長いものは分かっている */
	len = 2;
	while( c0 == Pi_getColor(len) && len<imagemax )
		len += 2;
	
	pos = prepos = 2;
	Pi_putPos( pos );
	Pi_putLen( len/2 );
	off = len;
	
	/* 高速化のため最初の２ラインだけは別に処理する */
	while( off< Width*2 )
	{
		pos = Pi_search( &l, off,prepos, imagemax, Pi_getColor );
		
		if( (l[0] | l[1] | l[2] | l[3] | l[4]) == 0 )
		{	Pi_putPos( prepos );
			precol = Pi_getColor( off-1 )& 0xff;
			while(1)
			{	c0 = Pi_getColor( off );	off+=2;
				Pi_putColor( c0 & 0xff );
				Pi_putColor( c0 >> 8 );
				
				if ( off > imagemax )	break;
				
				prepos=(-1);
				pos = Pi_search( &l, off,prepos, imagemax, Pi_getColor );
				if( (l[0] | l[1] | l[2] | l[3] | l[4]) )
					break;
				Pi_putBit( 1, 1 );
			}
			Pi_putBit( 0,1 );
			if ( off > imagemax )
				break;
		}
		Pi_putPos( pos );
		Pi_putLen( l[pos] );
		prepos = pos;
		off   += l[pos]*2;
	}
	if( head->pix == 8 )	Pi_getColor	= Pi_getColor256;
					else	Pi_getColor	= Pi_getColor16;
	
	while( off< imagemax )
	{	
		/* (3)連続している最長パターンを探す */
		pos = Pi_search( &l, off,prepos, imagemax, Pi_getColor );
		
		/* 同じパターンが見つからないとき */
		if( (l[0] | l[1] | l[2] | l[3] | l[4]) == 0 )
		{
			/* (5)前回と同じ位置を記録 */
			Pi_putPos( prepos );
			precol = Pi_getColor( off-1 )& 0xff;
			
			/* (6)２点を記録し注目点移動 */
			while(1)
			{	c0 = Pi_getColor( off );	off+=2;
				Pi_putColor( c0 & 0xff );
				Pi_putColor( c0 >> 8 );
				
				if ( off > imagemax )
					break;
				
				/* (7)連続しているパターンがあれば終わり */
				prepos=(-1);
				pos = Pi_search( &l, off,prepos, imagemax, Pi_getColor );
				
				if( (l[0] | l[1] | l[2] | l[3] | l[4]) )
					break;
				
				Pi_putBit( 1, 1 );
			}
			Pi_putBit( 0,1 );	/* 0を書き込む */
			if ( off > imagemax )
				continue;
			
		}
		/* 同じパターンが見つかったとき */
		/* (4')一番長くなる位置と長さを記録 */
		Pi_putPos( pos );
		Pi_putLen( l[pos] );
		prepos = pos;
		off += l[pos]*2;
	}
	
	Pi_putBit( 0, 32 );
	
	return NOERR;
}

/*********************/

/* 周囲５点から同じパターンを探す */
int Pi_search( l,  off,prepos, imagemax,getColor )
int 	*l,off,imagemax,prepos;
int 	(*getColor)();
{
	int 	i,len,max;
	int 	bufoff;
	
	max = 0;
	l[0]= 0;
	if( prepos!=0 )
	{	
		i = getColor(off-2);
		/* 直前のパターンを比較 */
		if( ((i>>8)& 0x0ff) == (i & 0xff) )	bufoff = off-2;
									else	bufoff = off-4;
		len = 0;
		while( getColor(bufoff) == getColor(off+len) )
		{	len 	+= 2;
			bufoff  += 2;
			if( off+len >= imagemax )	break;
		}
		l[0] = len/2;
	}
	
	/** pos=1〜4 **/
	for( i=1; i<5; i++ )
	{	if( prepos == i )
		{	l[i]=0;
			continue;
		}
		bufoff = off + Pi_posTable[i][0];
		len    = 0;
		while( getColor( off+len )==getColor( bufoff ) )
		{	len 	+= 2;
			bufoff	+= 2;
			
			if( off+len >= imagemax )	break;
		}
		l[i] = len/2 ;
		if( l[max] < l[i] )	max = i;
	}
//	for(i=0;i<5;i++)	printf("l[%d]=%d,",i,l[i] );printf("\r\n");
	
	return max;
}

/*********************/

int Pi_getColor256D( off )
int off;
{
	if( off < 0 )	return WORD( f2buf+ Width*2 + off );
	
	return WORD( PI_IMAGEBUF + off );
}

int Pi_getColor16D( off )
int off;
{
	if( off < 0 )	return WORD( f2buf+ Width*2 + off );
	
	if( off & 1 )
	{	off >>= 1;
		return  ((PI_IMAGEBUF[off  ] & 0xf0 )>>4)
			  | ((PI_IMAGEBUF[off+1] & 0x0f )*256);
	}else
	{	off >>= 1;
		return  ((PI_IMAGEBUF[off  ] & 0x0f )	)
			  | ((PI_IMAGEBUF[off  ] & 0xf0 )<<4);
	}
}

int Pi_getColor256( off )
int off;
{
	return WORD( PI_IMAGEBUF + off );
}

int Pi_getColor16( off )
int off;
{
	if( off & 1 )
	{	off >>= 1;
		return  ((PI_IMAGEBUF[off  ] & 0xf0 )>>4)
			  | ((PI_IMAGEBUF[off+1] & 0x0f )*256);
	}else
	{	off >>= 1;
		return  ((PI_IMAGEBUF[off  ] & 0x0f )	)
			  | ((PI_IMAGEBUF[off  ] & 0xf0 )<<4);
	}
}

/*********************/

#define	chk_bufpage()	\
		if( Pi_countBit >= Pi_maxBit )\
		{	if( remallocPixBuf() )	return ERROR;\
			Pi_countBit = 0;\
		}

/* 任意ビット長のデータを格納する */
int 	Pi_putBit( dat,bit )
int 	dat,bit;
{
	char	restBit[]= {	0x00,0x01,0x03,0x07,0x0f,0x1f,0x3f,0x7f,0xff };
	char	usedBit[]= {	0x00,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe };
	int 	rest;
	char	*p;
	
	chk_bufpage();
	
	rest = 8-(Pi_countBit & 7);
	while( rest<bit )
	{	
		p = &pixel[pixPage][ (Pi_countBit>>3) ];
		
		*p &= usedBit[ (Pi_countBit & 7) ];
		*p |= ((dat>>(bit - rest)) & restBit[rest]);
		bit -= rest;
		Pi_countBit += rest;
		chk_bufpage();
		
		rest = 8-(Pi_countBit & 7);
	}
	if( rest>=bit )
	{
		p = &pixel[pixPage][ (Pi_countBit>>3) ];
		
		*p &= usedBit[ (Pi_countBit & 7) ];
		*p |= ((dat & restBit[bit])<<(rest-bit));
		
		Pi_countBit += bit;
	}
	
	return NOERR;
}


void	Pi_putPos( pos )
int pos;
{
	switch( pos )
	{
	case 0:		Pi_putBit( 0x00, 2 );	break;
	case 1:		Pi_putBit( 0x01, 2 );	break;
	case 2:		Pi_putBit( 0x02, 2 );	break;
	case 3:		Pi_putBit( 0x06, 3 );	break;
	case 4:		Pi_putBit( 0x07, 3 );	break;
	}
}

void	Pi_putLen( len )
unsigned int 	len;
{
	unsigned int i;
	
	i=1;
	while( (len>>i) )	i++;
	
	Pi_putBit( 0xfffffffe, i );
	if( i==1 )	return;
	
	Pi_putBit( len, i-1 );
}

/*************************/

void	Pi_initColorTable(col)
int 	col;
{
	int 	i, j;
	
	for( i=0; i<col; i++ )
		for( j=0; j<col; j++ )
			ColTbl[ j + i*col ] = ((i + col-j) & (col-1));
	
}

void	Pi_putColor16( col )
int 	col;
{
	int 	i,j,k;
	char	*p;
	
	p = ColTbl + precol*16 ;
	j = p[0];
	for( i=1; j!=col ; i++ )
	{	k    = p[i];
		p[i] = j;
		j    = k;
	}
	p[0]   = col;
	precol = col;
	i--;
		 if( i<2 )	Pi_putBit( 1, 1 ),Pi_putBit( i, 1 );
	else if( i<4 )	Pi_putBit( 0, 2 ),Pi_putBit( i, 1 );
	else if( i<8 )	Pi_putBit( 2, 3 ),Pi_putBit( i, 2 );
	else			Pi_putBit( 3, 3 ),Pi_putBit( i, 3 );
	
}
void	Pi_putColor256( col )
int 	col;
{
	int 	i,j,k;
	char	*p;
	
	p = ColTbl + precol*256 ;
	j = p[0];
	for( i=1; j!=col ; i++ )
	{	k    = p[i];
		p[i] = j;
		j    = k;
	}
	p[0]   = col;
	precol = col;
	i--;
		 if( i<  2 )	Pi_putBit( 0x01, 1 ),Pi_putBit( i, 1 );
	else if( i<  4 )	Pi_putBit( 0x00, 2 ),Pi_putBit( i, 1 );
	else if( i<  8 )	Pi_putBit( 0x02, 3 ),Pi_putBit( i, 2 );
	else if( i< 16 )	Pi_putBit( 0x06, 4 ),Pi_putBit( i, 3 );
	else if( i< 32 )	Pi_putBit( 0x0e, 5 ),Pi_putBit( i, 4 );
	else if( i< 64 )	Pi_putBit( 0x1e, 6 ),Pi_putBit( i, 5 );
	else if( i<128 )	Pi_putBit( 0x3e, 7 ),Pi_putBit( i, 6 );
	else				Pi_putBit( 0x3f, 7 ),Pi_putBit( i, 7 );
	
}

/********************************************************/
void freePixBuf()
{
	int i;
	
	for( i=0; i<=pixPage; i++ )
		PI_FREE( pixel[i] );
	PI_FREE( pixel );
}

int mallocPixBuf( Width,Height )
int 	Width,Height;
{
	int 	all,page;
	
	all  = Width * Height ;				/* 全画面データに必要なメモリ */
	page = (all / pixPageSize )+1 ;		/* 最大ページサイズ */
	
	if( (pixel=(char**)PI_MALLOC( sizeof(char*)*page ))==NULL )
		return ERROR;
	
	/* 1ページだけ確保 */
	if( (pixel[0] = (char*)PI_MALLOC( pixPageSize + 2 ))==NULL )
	{	free( pixel );
		return ERROR;
	}
	
	return NOERR;
}

int remallocPixBuf()
{
	if( (pixel[pixPage+1] = (char*)PI_MALLOC( pixPageSize + 2 ))==NULL )
	{	freePixBuf();
		return ERROR;
	}
	pixPage++;
	return NOERR;
}

/*************************************************************/
/*	乙女座から得たコメントを分解する	*/

#define	iskanji(c)	( (0x81<=(c) && (c)<=0x9f) || (0xe0<=(c) && (c)<=0xfc))
#define	iskanji2(c)	( (0x40<=(c) && (c)<=0x7e) || (0x80<=(c) && (c)<=0xfc))

int Pi_setComment()
{
	int 	i,len;
	
	if( PI_COMMENT==NULL || (len=strlen( PI_COMMENT ))<1 )
		return NOERR;
	
	/* 面倒なのでメモリチェックは無視。これくらいで失敗するかな?? */
	
	for( i=0; i<len ; i++ )
	{	if( PI_COMMENT[i]==',' )
		{	user=PI_MALLOC( i+1 );
			strncpy( user, PI_COMMENT, i ),user[i]='\0';
			
			for( i++;i<len; i++ )
				if( PI_COMMENT[i]!=' ' && PI_COMMENT[i]!='\t' )	break;
			if( i<len )
			{	comment=PI_MALLOC( len-i+2 );
				strcpy( comment, PI_COMMENT+i );
			}
			return NOERR;
		}
	}
	user=PI_MALLOC( len+1 );
	strcpy( user, PI_COMMENT );
	
	return NOERR;
}

#define	ENV_MAX		80

char	*my_getenv( name )
char	*name;
{
	char	envbuf[ ENV_MAX+1 ];
	int 	i;
	
	/* '='で始まって末尾に '\\' を追加したものが返される */
	if( (i=TOS_getPath( envbuf, name, strlen(name) ))==0 )
		return NULL;
	
	if( i>2 )
	{	if( !iskanji( envbuf[i-2] ) )	/* 漢字コード2バイト目でない */
			envbuf[i-1] = '\0';
	}
	else if( i>0 )
		envbuf[i-1] = '\0';
	
	if( i<1 || (user = PI_MALLOC( strlen( &envbuf[1] )+1 ))==NULL )
		return '\0';
	
	strcpy( user, &envbuf[1] );		/* '=' のぶんスキップ */
	
	return user;
}

/********************************************************/

int Pi_writeBuffers( fp,head,palette )
FILE		*fp;
PiHeader	*head;
char		*palette;
{
	int 	i;
	char	*p;
	
	/****/
	if( 2 != fwrite( "Pi", 1,2, fp ) )
		return ERROR;
	
	p = '\0';
	if( user != '\0' )	p = user;
				else	p = my_getenv("USER");
	if( p != '\0' )
	{	fwrite( "User:", 1,5, fp );
		fwrite( p, 1, strlen(p), fp );
	}
	if( comment == '\0' )	p = def_comment;
					else	p = comment;
	fwrite( "\r\n", 1, 2, fp);
	fwrite( p, 1, strlen(p), fp);
	fwrite( "\x1a\0", 1, 2, fp);
	
	/****/
	fwrite( head,			1, 4, fp );
	fwrite( head->name,		1, 4, fp );
	fputc ( head->mdat_len>>8	, fp );
	fputc ( head->mdat_len		, fp );
	if( head->mdat_len>0 )	fwrite( head->mdat, 1, head->mdat_len, fp );
	fputc ( head->x >>8			, fp );
	fputc ( head->x 			, fp );
	fputc ( head->y >>8			, fp );
	fputc ( head->y 			, fp );
	
	fwrite( palette, 1, (1<<head->pix)*3 ,fp);
	
	/****/
	for( i=0; i<pixPage; i++ )
		if( pixPageSize != fwrite( &pixel[i][0], 1, pixPageSize, fp ) )
			return ERROR;
	if( (i = ((Pi_countBit+7)>>3)) >0 )
		if( i != fwrite( &pixel[pixPage][0], 1, i, fp ) )
			return ERROR;
	
	
	return NOERR;
}

void Pi_head( head,palette,size,pal )
char		*palette;
PiHeader	*head;
POINT		*size;
char		*pal;
{
	int 	i,j,col;
	
	head->mode	= 0;
	head->n		= 1;
	head->m		= 1;
	head->pix	= pi_imge->pix;
	
	strncpy( head->name, "TOWN",4 );
	
	head->mdat_len = 0;
	head->x = (size->x+31)& 0xffe0 ;	/* う〜む。仕方がない。*/
	head->y = size->y;
	
	/*** パレットデータ ***/
	
	col = (1 << head->pix);
	for( i=0,j=8; i<col; i++,j+=8 )
	{
		palette[ i*3   ] = pal[ j+1 ] ;		/* R */
		palette[ i*3+1 ] = pal[ j+2 ] ;		/* G */
		palette[ i*3+2 ] = pal[ j   ] ; 	/* B */
	}
	
}

int Pi_save( fp, buf, size, pix, pal )
FILE	*fp;
char	*buf,*pal;
int 	pix;
POINT	*size;
{
	PiHeader	head;
	int 		col,ret;
	char		*palette;
	
	if( pix ==8 )	col = 256;
			else	col = 16;
	
	if( (palette=(char*)PI_MALLOC( col*3 + col*col ))==NULL )
		return PI_ERROR_NO_MEMORY;
	ColTbl  = palette  + col*3 ;
	
	if( mallocPixBuf( PI_SIZE_X,PI_SIZE_Y ) )
	{	PI_FREE(palette);
		return PI_ERROR_NO_MEMORY;
	}
	
	/*** ヘッダ作成 ***/
	Pi_head( &head, palette, &PI_SIZE, PI_COLMAP );
	
	/*** 圧縮 ***/
	Pi_initColorTable( col );
	ret=Pi_encode( &head, buf );
	ret = 0;
	
	/*** 書き出し ***/
	if( ret==NOERR )
		ret=Pi_writeBuffers( fp, &head, palette );
	
	
	PI_FREE( palette );
	freePixBuf();
	
	return ret;
}

/*******************************************************************/
/**  プラグイン呼び出し **/

int APL_exec()
{
	FILE	*fp;
	int 	ret;
	
	if( PI_PIX >8 )	return ERROR;
	
	Pi_setComment();
	
	if( (fp = fopen( PI_FNAME , "wb" )) == NULL )
		return PI_ERROR_FILE_OPEN;
	
	ret=Pi_save( fp, PI_IMAGEBUF, &PI_SIZE, PI_PIX, PI_COLMAP );
	fclose( fp );
	if( ret )	remove( PI_FNAME );
	
	if( user    != '\0' )	PI_FREE( user );
	if( comment != '\0' )	PI_FREE( comment );
	return ret;
}

