/*
*	Yamana's Otomeza Plug-in Tool
*		ぱいろーだ
*	
*	1995.07.23	
*	1995.07.30	16色に暫定対応
*	1995.08.06	デフォルトパレットの修正, ビット操作部の改良
*	1995.08.13	16色の横幅の半端なものの読み込みに対応
*	1995.08.20	↑大嘘だったので修正
*	
*	問題点
*	・16色画像を256色とみなして処理するので、無駄なメモリを食う。
*	
*	
*	"Pi" (c) A.Yanagisawa
*/
#include "otome_pi.h"

const char	longname[]	= "File  :Pi ローダ";
const char	extend[]	= "PI";

#define 	USE_FILE	PI_LOAD
#include	"otome_pi.c"

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;
		char	*palt;		/* 0 or 3*16 or 3*256 */
	}
	PiHeader;

unsigned int	Pi_getBit();

int		Pi_countBit = 0;
int 	Pi_maxBit;
char	*ColTbl;

int 	precol;

/********************************************************************/

extern	int	Pi_getColor256();
extern	int	Pi_getColor16();

int 	Pi_decode( decbuf, buf, head )
char		*decbuf,*buf;
PiHeader	*head;
{
	int 	i;
	char	c0,c1,c2,c3;
	char	*image;
	int 	pos,len,Width,Height;
	int 	prepos,Pi_all;
	int 	(*Pi_getColor)();
	
	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ドット左	*/
				};
	
	Width  = head->x;
	Height = head->y;	
	Pi_all = (Width * (Height+2)) ;
	
	for( i=1; i<=4 ; i++ )
		Pi_posTable[i][0] += Pi_posTable[i][1]*Width;
	
	if( head->pix == 8 )	Pi_getColor = Pi_getColor256;
					else	Pi_getColor = Pi_getColor16;
	
	image = decbuf;
	precol = 0;
	c0 = Pi_getColor( buf );
	c1 = Pi_getColor( buf );
	
	for( i=0; i<Width; i++ )	/* 先頭２ラインを先に埋める */
	{
		*image++ = c0;
		*image++ = c1;		/* 常に２ドットごと扱う */
	}
	
	prepos = -1;
	while( Pi_countBit < Pi_maxBit )
	{
		/* まず位置データを得る。2 or 3 ビット長 */
		pos = Pi_getBit( buf, 2 );
		if( pos == 3 )	pos += Pi_getBit( buf, 1 );
		
		/* 位置が前に記録した位置と同じとき */
		if( pos == prepos )
		{	do
			{	*image++ = Pi_getColor( buf );
				*image++ = Pi_getColor( buf );
				
			}while( Pi_getBit(buf,1) );	/* (7) 'b' 同じパターンがない限り */
			
			prepos = -1;
			continue;
		}
		prepos = pos;
		
		/* (4') 長さを求める */
		len = Pi_getLen(buf);
		
		/* (3) */
		if( pos )	/*  pos==(1〜4) */
		{
			if( pos>4 )	break;
			
			memcpy( image, image + Pi_posTable[pos][0], len*2 );
			image += len*2 ;
		}
		else		/*  pos==0 : 直前色と同じなら2,違うとき4ドット右 */
		{
			c1 = *(image-2);
			c0 = *(image-1);
			
			if ( c0 == c1 )
			{	while( len-- )		/* 同じパターンが続く限り繰り返す */
				{
					*image++ = c0;
					*image++ = c0;
				}
			}else
			{	c3 = *(image-4);
				c2 = *(image-3);
				
				while( len-- )
				{
					*image++ = c3;
					*image++ = c2;
					if( (len--) <= 0 )	break;	/* 2ドット単位 */
					*image++ = c1;
					*image++ = c0;
				}
			}
			
		}/* (3) */
		
		precol = *(image-1);
		
	}/* LOOP */
	
	
	return NOERR;
}

/**************************************/
/*	    0		 1
	   10x		 2-3
	  110xx		 4-7
	 1110xxx	 8-15
	11110xxxx	16-31
	    :		  :
*/
int		Pi_getLen(buf)		/* 長さ符号の解析 */
char	*buf;
{
	int 	i;
	
	for( i=0; Pi_getBit(buf,1) ; i++ );
	
	if( i==0 )	return 1;
	
	return (Pi_getBit(buf,i) | (1<<i));
	
}

/* 任意ビット長のデータを返す */
unsigned int Pi_getBit( buf,bit )
char	*buf;
int 	bit;
{
	char	restBit[]= {	0xff,0x7f,0x3f,0x1f,0x0f,0x07,0x03,0x01	};
	register unsigned int	rest,low;
	
	if( Pi_countBit+bit >= Pi_maxBit )
	{	Pi_countBit += bit;
		return 0;
	}
	rest = (buf[ (Pi_countBit>>3) ] & restBit[ (Pi_countBit & 7) ]);
	low  = (8 - (Pi_countBit & 7) );
	
	while( bit > low )	/* 足りないときは追加する */
	{	
		rest = (rest<<8) | buf[ (Pi_countBit>>3)+1 ];
		low += 8;
	}
	Pi_countBit += bit ;
	
	return (rest >>(low-bit) );
}


/**************************************/

int 	Pi_getColor256( buf )
char	*buf;
{
	register unsigned int 	col;
	char	*p;
	
		 if( Pi_getBit(buf,1)      )	col = Pi_getBit(buf,1)        ;
	else if( Pi_getBit(buf,1) == 0 )	col = Pi_getBit(buf,1) | 0x02 ;
	else if( Pi_getBit(buf,1) == 0 )	col = Pi_getBit(buf,2) | 0x04 ;
	else if( Pi_getBit(buf,1) == 0 )	col = Pi_getBit(buf,3) | 0x08 ;
	else if( Pi_getBit(buf,1) == 0 )	col = Pi_getBit(buf,4) | 0x10 ;
	else if( Pi_getBit(buf,1) == 0 )	col = Pi_getBit(buf,5) | 0x20 ;
	else if( Pi_getBit(buf,1) == 0 )	col = Pi_getBit(buf,6) | 0x40 ;
	else								col = Pi_getBit(buf,7) | 0x80 ;
	
	col &= 0xff;
	
	/* 色テーブルのシフト */
	p   = ColTbl+ precol*256 + col ;
	precol = *p;
	
	for( ; col ; col--,p-- )
		*p = *(p-1);
	*p = precol;
	
	return precol;
}

int 	Pi_getColor16( buf )
char	*buf;
{
	register unsigned int 	col;
	char	*p;
	
		 if( Pi_getBit(buf,1) == 1 )	col = Pi_getBit(buf,1)     ;
	else if( Pi_getBit(buf,1) == 0 )	col = Pi_getBit(buf,1) | 2 ;
	else if( Pi_getBit(buf,1) == 0 )	col = Pi_getBit(buf,2) | 4 ;
	else 								col = Pi_getBit(buf,3) | 8 ;
	
	/* 色テーブルのシフト */
	
	p   = ColTbl + precol*16 + col ;
	precol = *p ;
	for( ; col>0 ; col--,p-- )
		*p = *(p-1);
	*p = precol;
	
	return precol;
}

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));
	
}
/*
	0fedcba987654321
	10fedcba98765432
	210fedcba9876543
	  :
*/

/**************************************/


int 	Pi_readHeader( head, fp )
PiHeader	*head;
FILE		*fp;
{
	int 	i=0;
	
	if( fgetc(fp)!= 'P' || fgetc(fp)!= 'i' )
		return ERROR;
	
	while( fgetc(fp) != 0x1a && ferror(fp)==0 );
	while( fgetc(fp) != 0x00 && ferror(fp)==0 );
	if( ferror(fp) )	return ERROR;
	
	head->mode		= (char)fgetc( fp );
	head->n			= (char)fgetc( fp );
	head->m			= (char)fgetc( fp );
	head->pix		= (char)fgetc( fp );
	
	fread( head->name ,1,4 ,fp );
	
	head->mdat_len	= (fgetc( fp )<<8) + (char)fgetc( fp );
	for( i=0; i< head->mdat_len ; i++ )
		fgetc( fp );
	
	head->x			= (short)(fgetc( fp )<<8 | (char)fgetc( fp ) );
	head->y			= (short)(fgetc( fp )<<8 | (char)fgetc( fp ) );
	
//	printf("(x,y)=(%d,%d),pix=%d\r\n",head->x,head->y,head->pix);
	
	return ferror(fp);
}


long	getFileSize( fp )
FILE	*fp;
{
	unsigned long	from,to;
	
	from = ftell(fp);
	fseek( fp, 0, SEEK_END );
	to = ftell(fp);
	fseek( fp, from, SEEK_SET );
	
	return (to-from);
}

/*******************************************************************/

void	Pi_setPalette( pal, col, fp )
char	*pal;
int 	col;
FILE	*fp;
{
	int 	i,j;
	
	DWORD( pal ) = col;
	for( i=0,j=4; i<col; i++,j+=8 )
	{
		WORD( pal+j ) = i ;
		pal[ j+5 ] = (char)fgetc( fp );	/* R */
		pal[ j+6 ] = (char)fgetc( fp );	/* G */
		pal[ j+4 ] = (char)fgetc( fp );	/* B */
	}
	
}

void Pi_setDefaultPalette( pal,col )
char	*pal;
int 	col;
{
	int 	i,j;
	
	DWORD( pal ) = col;
	
	if( col==16 )	/* TOWNSのdefaultと同じ */
		for( i=0,j=4; i<16; i++,j+=8 )
		{	WORD( pal+ j ) = (unsigned short)i;
			
			pal[ j+5 ] = (i & 2) * ( ((i & 8) | 7 )<<3 );
			pal[ j+6 ] = (i & 4) * ( ((i & 8) | 7 )<<2 );
			pal[ j+4 ] = (i & 1) * ( ((i & 8) | 7 )<<4 );
		}
	else			/* TOWNSのdefaultよりちょっと暗い */
		for( i=0,j=4; i<256; i++,j+=8 )
		{	WORD( pal+ j ) = (unsigned short)i;
			
			pal[ j+5 ] = (((i>>2 ) & 7) << 5);	/* R */
			pal[ j+6 ] = (((i>>5 ) & 7) << 5);	/* G */
			pal[ j+4 ] = (( i      & 3) << 6);	/* B */
		}
	
}



/*******************************************************************/
/**  プラグイン呼び出し **/

#define	FNAME	(pi_data->fname)

int APL_exec()
{
	FILE	*fp;
	char	*buf,*mem,	*pal,*image;
	int 	col;
	long 	l;
	PiHeader	head;
	
	if( (fp = fopen( FNAME , "rb" )) == NULL )
		return PI_ERROR_FILE_OPEN;
	
	/** ヘッダ読み込み **/
	if( Pi_readHeader( &head, fp ) )
	{	fclose( fp );
		return ERROR;
	}
//	printf("size=(%d,%d)", head.x, head.y );
	
	
	if( head.pix > pi_imge->pix )		/* 減色処理などはしない */
		return ERROR;
	
	if( head.pix < 8 )	col = 16;
				else	col = 256;
	
	/** パレット情報読み取り **/
	if( (pal=(char*)PI_MALLOC( 4+col*8 ))==NULL )
	{	fclose( fp );
		return PI_ERROR_NO_MEMORY;
	}
	
	if( head.mode & 0x80 )	/* パレットなし */
		Pi_setDefaultPalette( pal,col );
	else
		Pi_setPalette( pal, col, fp );
	
	/***/
	if( (l=getFileSize(fp))< 0
		|| (mem=(char *)PI_MALLOC( l + col*col ))==NULL )
	{	fclose(fp);
		PI_FREE( pal );
		return PI_ERROR_NO_MEMORY;
	}
	buf    = mem;
	ColTbl = mem+l;
	Pi_initColorTable( col );
	
	if( fread( buf, 1, l, fp )< l )		/* 一気に読み込み */
	{	fclose(fp);
		PI_FREE( pal );
		PI_FREE( mem );
		return PI_ERROR_FILE_OPEN;
	}
	fclose( fp );
	/***/
	
	Pi_maxBit = l*8;
	
	/***  展開用メモリ確保 **********************************************/
	/* 16色モードの場合横16ドット単位、256色モードの場合横32ドット単位	*/
	/* でメモリを確保し、乙女座に渡さなくてはならない。					*/
	/********************************************************************/
	
	/* 256色として展開する */
	if( (image=PI_MALLOC( ((head.x+31) & 0xffe0) * (head.y+2)+32 ))==NULL )
	{	
		PI_FREE( pal );
		PI_FREE( mem );
		return PI_ERROR_NO_MEMORY;
	}
#if 1
	/** データ展開 **/
	if( Pi_decode( image, buf, &head ) )
	{
		PI_FREE( pal );
		PI_FREE( mem );
		
		return ERROR;
	}
#endif
	
	PI_FREE(mem);
	
	pi_imge->image	= image;
	pi_imge->clut	= pal;
	pi_imge->pix	= head.pix;
	pi_imge->size.x = head.x ;
	pi_imge->size.y = head.y ;
	
	if( head.pix == 8 )	conv_vram256( image, &head );
	if( head.pix == 4 )	conv_vram16 ( image, &head );
	
	return NOERR;
}



/***** 展開したバッファを TOWNS の VRAM に合うよう変換する *****/

void conv_vram( image, head )
char		*image;
PiHeader	*head;
{
	int 	Width = (head->x+31)& 0xffe0 ;
	int 	y;
	char	*p = image + head->x * 2 ;
	
	for( y=head->y-1; y>0 ; y-=2 )
	{	_rmemcpy( p+Width*(y  ), p+head->x*(y  ), head->x );
		_rmemcpy( p+Width*(y-1), p+head->x*(y-1), head->x );
	}
	if( y==0 )
		_rmemcpy( p+Width*(0  ), p+head->x*(0  ), head->x );
	
}

int conv_vram256( char *image,PiHeader *head )
{
	int 	Width;
	
	Width = (head->x +31)& 0xffe0;	/* 256色の場合は32ドット単位 */
	
	if( Width != head->x )
		conv_vram( image, head );
	
	/* 上 2 ラインは必要ない */
	memcpy( image, image+(head->x*2), (Width*head->y) );
	
	return NOERR;
}

int conv_vram16( char *image,PiHeader *head )
{
	int 	x,y,Width;
	char	*p,*pp;
	
	Width = (head->x+31)& 0xffe0;		/* 16色は横16ドット単位 */
	
	if( head->x != Width )
		conv_vram( image, head );
	
	p = image + head->x*2 ;
	pp= image ;
	for( y=0 ; y<head->y ; y++ )
	{	
		for(x=0; x< Width ; x+=8 )		/* ループ展開すれば少しは速い? */
		{	*pp++ = (*p++ & 0x0f) | (*p++ & 0x0f)<<4 ;
			*pp++ = (*p++ & 0x0f) | (*p++ & 0x0f)<<4 ;
			*pp++ = (*p++ & 0x0f) | (*p++ & 0x0f)<<4 ;
			*pp++ = (*p++ & 0x0f) | (*p++ & 0x0f)<<4 ;
		}
	}
	return NOERR;
}

