/*
*	Yamana's Otomeza Plug-in Tool
*		ＭＡＧセーバ
*	
*	1995.07.17	
*	1995.07.26	２倍程度の高速化
*	1995.08.06	メモリの浪費を抑えるためバッファのページ化を行った
*	1995.08.09	乙女座V1.3b 仕様に変更
*	1995.08.13	ページ化を強化しメモリ不足に対処
*	
*	バグ
*		・フラグＢのサイズが大きくなる。なんで？？
*	
*	"MAG" (c) Woddy-RINN
*/

#include	"otome_pi.h"

const char	longname[] = "FILE  : まぐせーば";
const char	extend[] = "MAG";

#define		USE_FILE	PI_SAVE
#include	"otome_pi.c"

					/*  01234567012340123456789012345678 */
char	MAG_check[]  = "MAKI02  TOWN >謎<               ";
char	def_comment[]= "MAGSAVER/OTOMEZA";

#define		POS_USER	13
#define		LEN_USER	18
#define		LEN_TOPDATA	(8+4+1+LEN_USER+1)

typedef struct
{	
	char	head;
	char	machine;
	char	machine_dat;
	char	screen;
	short	sx;
	short	sy;
	short	ex;
	short	ey;
	long	flgA;
	long	flgB;
	long	flgB_size;
	long	pixel;
	long	pixel_size;
}MagHeader;

char	*user='\0',*comment='\0';

extern char	*my_getenv();

#define	MAX_PIXPAGESIZE		(32*1024)
int 	pixPageSize= MAX_PIXPAGESIZE;
int 	pixPage    = 0;

int 	flgBPageSize= MAX_PIXPAGESIZE;
int 	flgBPage    = 0;

char	*mem,*flg[2],*flgA,**flgB,**pixel;

/**********************************************************************/
/*** 圧縮用メモリ確保 ***/

int MAG_mallocBuffers( vramWidth,Height )
int 	vramWidth,Height;
{
	extern void freePixBuf();
	
	if( (mem=PI_MALLOC(
				 (vramWidth+8)*2			/* 作業用フラグバッファ */
				+(vramWidth*Height+7)/8+8	/* フラグＡ */
			))==NULL )
	{
		return PI_ERROR_NO_MEMORY;
	}
	if( mallocPixBuf( vramWidth,Height ) )
	{	PI_FREE( mem );
		return PI_ERROR_NO_MEMORY;
	}
	if( mallocFlgBBuf( vramWidth,Height ) )
	{	freePixBuf();
		PI_FREE( mem );
		return PI_ERROR_NO_MEMORY;
	}
	
	/* メモリ割り当て */
	flg[0]= mem ;
	flg[1]= mem + (vramWidth+8) ;
	flgA  = mem + (vramWidth+8)*2 ;
	
	return NOERR;
}

void MAG_freeBuffers()
{	
	extern void freePixBuf();
	
	PI_FREE( mem );
	freePixBuf();
	
}

/****** MAG フォーマット圧縮部 ******/

int 	MAG_encode( head,buf )
MagHeader	*head;
char		*buf;
{
	char Bit[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
	
	/* フラグのチェック順序で圧縮効率が左右される */
#if 0
	char flgOrder[] = { 0,1,4,5,6,7,9,10,2,8,11,12,13,14,3,15 };
	char flgTable[][2] =
	{	{ 0, 0},
		{ 2, 0},{ 0, 1},{ 2, 1},{ 0, 2},{ 2, 2},	/*  1, 4, 5, 6, 7 */
		{ 0, 4},{ 2, 4},{ 4, 0},{ 4, 2},{ 4, 4},	/*  9,10, 2, 8,11 */
		{ 0, 8},{ 2, 8},{ 4, 8},{ 8, 0},{ 0,16} 	/* 12,13,14, 3,15 */
	};
#else	/* こっちは yamana-a オリジナルの順番 */
	char flgOrder[] = { 0,1,4,2,6,5,7,9,8,10,11,12,13,14,3,15 };
	char flgTable[][2] =
	{	{ 0, 0},
		{ 2, 0},{ 0, 1},{ 4, 0},{ 0, 2},{ 2, 1},	/*  1, 4, 5, 6, 7 */
		{ 2, 2},{ 0, 4},{ 4, 2},{ 2, 4},{ 4, 4},	/*  9,10, 2, 8,11 */
		{ 0, 8},{ 2, 8},{ 4, 8},{ 8, 0},{ 0,16} 	/* 12,13,14, 3,15 */
	};
#endif
	
	int 	Width,Height, flgWidth, vramWidth, realWidth;
	int 	i,j;
	register int	x,y,x1,y1;
	int 	p0,p1, upper;
	int 	flgAi,flgBi,pixi;
	
	realWidth	= (head->ex - head->sx +1 );
	Height		= (head->ey - head->sy +1 );
	
	/***
		16色
		2ドット=1バイト
		4ドット=2バイト=1ピクセル
		8ドット=4バイト=2ピクセル=1フラグ
		
		256色
		1ドット=1バイト
		2ドット=2バイト=1ピクセル
		4ドット=4バイト=2ピクセル=1フラグ
	***/
	
	/* バッファには32ドット単位で格納されているので注意 */
	if( head->screen == 0 )
	{	vramWidth	=((realWidth+31)& 0xffe0)>>1 ;
		Width 		=((realWidth+ 7)& 0xfff8)>>1 ;	/* 8ドット単位で記録 */
		head->ex	= (head->sx + (Width<<1) - 1);
	}else
	{	vramWidth	= (realWidth+31)& 0xffe0 ;
		Width		= (realWidth+ 7)& 0xfff8 ;	/* ほんとは4ドット単位だが */
		head->ex	= (head->sx + Width - 1);	/* この際だから統一させる  */
	}
	flgWidth = Width>>2 ;				/* フラグ単位のときの幅 */
	
	if( MAG_mallocBuffers( vramWidth,Height ) )
		return ERROR;
	
	/******** フラグデータ作成 ********/
	
	pixi = flgAi = flgBi = 0;
	flgA[0] = 0;
	for( y=0; y<Height ; y++ )
	{
		for( x=0; x<Width; x+=2 )
			flg[y & 1][ x>>2 ] = 0;			/* 1フラグ=2ピクセル=4バイト*/
		
		for( x=0; x<Width; x+=2 )			/* 2バイト単位 */
		{	
#if 0
			if( x+4 >= Width )				/* 右端の4バイトについては */
			{	j = (x+2)-realWidth/2;		/* 黒の塗りつぶしチェック */
				if( j > 1 )
					p0 = 0,					/* ゴミが出ないよう埋める  */
					WORD( buf + x + y*vramWidth ) = 0;
				else if( j==1 )
					p0 = BYTE( buf + x + y*vramWidth ),
					WORD( buf + x + y*vramWidth ) &= 0x00ff ;
				else
					p0 = WORD( buf + x + y*vramWidth );
			}else
#endif
			p0 = WORD( buf + x + y*vramWidth );
			
			for( i=1; i<16; i++ )		/* 同じピクセルを探す */
			{	
				x1= (x - flgTable[i][0]);
				y1= (y - flgTable[i][1]);
				if( x1<0 || y1<0 )	continue;
				
				p1 = WORD( buf + x1 + y1*vramWidth );
				
				/* 近くに同じピクセルがある？ */
				if( p0 == p1 )
				{	flg[ y&1 ][ x>>2 ]
						|= ( (int)flgOrder[i] << (((x & 2)==0)? 4:0 ));
					break;
				}
			}
			if( i==16 )		/* 見つからないときは、その点を記録 */
			{	
				if( pixi >= pixPageSize )		/* ページ切替え */
				{	if( remallocPixBuf() )
						return PI_ERROR_NO_MEMORY;
					pixi = 0;
				}
				if( head->screen )	/* 256色 */
				{	pixel[pixPage][ pixi++ ] = ( p0     & 0xff);
					pixel[pixPage][ pixi++ ] = ((p0>>8) & 0xff);
				}else
				{	/* 上位下位入替え */
					p1 = (( p0 & 0xf0f0 )>>4 );
					p0 = (( p0 & 0x0f0f )<<4 );
					p0 |= p1 ;
					
					pixel[pixPage][ pixi++ ] = (char)( p0    );
					pixel[pixPage][ pixi++ ] = (char)((p0>>8));
					
				}
			}
		}/* x<Width */
		
		/*  ここまでで1ラインぶんのフラグ作成完了	*/
		/*  続いてフラグＡ、Ｂを作成する			*/
		
		for ( x=0; x< flgWidth; x++ )
		{	
			if( y>0 )	/* 上のラインとXORをとる */
				upper = (flg[y & 1][x] ^ flg[(y-1) & 1][x] );
			else
				upper = (flg[y & 1][x] );
			
			if( upper != 0 )
			{	/* flgA は 8ビット単位なので、ビットカウンタflgAiの */
				/* 下位3ビットは無視する */
				flgA[ flgAi>>3] |= Bit[ flgAi & 7 ] ;
				
				if( flgBi >= flgBPageSize )		/* ページ切替え */
				{	if( remallocFlgBBuf() )
						return PI_ERROR_NO_MEMORY;
					flgBi = 0;
				}
				flgB[flgBPage][ flgBi++ ]  = upper ;
			}
			if( (flgAi & 7)== 7 )		/* 次のフラグを初期化 */
				flgA[ (flgAi>>3)+1 ] = 0 ;
			
			flgAi++;
			
		}
	}/* y<Height */
	
	flgAi = (flgAi+7)/8 ;		/* flgAi はビットカウンタ */
	if( flgAi & 1 )	flgAi++;
	if( flgBi & 1 )	flgBi++;
	
	
	/** ヘッダの書換えを行う **/
	
	head->flgA		= (sizeof(MagHeader)+(head->screen==0 ? 16*3:256*3));
	head->flgB		= (head->flgA + flgAi);
	head->flgB_size	= (flgBPage*flgBPageSize + flgBi);
	head->pixel		= (head->flgB + head->flgB_size);
	head->pixel_size= (pixPage*pixPageSize + pixi);
	
	return NOERR;
}

/*********************************************/
/*	ピクセルデータのページ管理
**
**	MAG エンコードのとき圧縮後のデータを格納するため、最悪の場合 vram と
**	同じ大きさのピクセル用メモリが必要となる。
**	ただし、最悪の場合を想定していては無駄なメモリを増加させるだけなので
**	ページ単位でメモリを確保し、足りなくなったら追加確保するようにする。
**	
*/

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;
}

/** flgB バッファのメモリ対策 **/

void freeFlgBBuf()
{
	int i;
	
	for( i=0; i<=flgBPage; i++ )
		PI_FREE( flgB[i] );
	PI_FREE( flgB );
}

int mallocFlgBBuf( Width,Height )
int 	Width,Height;
{
	int 	all,page;
	
	all  = Width * Height ;				/* 全画面データに必要なメモリ */
	page = (all / flgBPageSize )+1 ;		/* 最大ページサイズ */
	
	if( (flgB=(char**)PI_MALLOC( sizeof(char*)*page ))==NULL )
		return ERROR;
	
	/* 1ページだけ確保 */
	if( (flgB[0] = (char*)PI_MALLOC( flgBPageSize + 2 ))==NULL )
	{	free( flgB );
		return ERROR;
	}
	
	return NOERR;
}

int remallocFlgBBuf()
{
	if( (flgB[flgBPage+1] = (char*)PI_MALLOC( flgBPageSize + 2 ))==NULL )
	{	freeFlgBBuf();
		return ERROR;
	}
	flgBPage++;
	return NOERR;
}

/**********************************************************************/

int MAG_head( head,palette,fr,pal )
char		*palette;
MagHeader	*head;
FRAME		*fr;
char		*pal;
{
	int 	i,j,col;
	char	*p;
	
	/** ファイル識別部分 **/
	
	p = '\0';
	if( user != '\0' )	p = user;
				else	p = my_getenv("USER");
	
	if( p != '\0' )
	{	for( i=0; i<LEN_USER && p[i]!='\0' ; i++ )	/* ユーザ名 */
			MAG_check[ POS_USER+i ] = p[i];
		for(    ; i<LEN_USER ; i++ )
			MAG_check[ POS_USER+i ] = ' ';
	}
	
	/** ヘッダ **/
	
	head->head		 = 0x00;
	head->machine	 = 0x00;
	head->machine_dat= 0x00;
	head->sx = fr->lupx;
	head->sy = fr->lupy;
	head->ex = fr->rdwx;	/* ←圧縮の際に必要なのでまだ変更しない! */
	head->ey = fr->rdwy;
	
	if( (col = DWORD(pal))!=16 && col!=256 )
		return ERROR;
	
	if( col<256 )	head->screen = 0x00;
			else	head->screen = 0x80;
	
	/*** パレットデータ ***/
	
	for( i=0,j=8; i<col; i++,j+=8 )
	{
		palette[ i*3   ] = pal[ j+2 ] ;		/* G */
		palette[ i*3+1 ] = pal[ j+1 ] ;		/* R */
		palette[ i*3+2 ] = pal[ j   ] ; 	/* B */
	}
	
	return NOERR;
}

int MAG_writeBuffers( fp,head,palette )
FILE		*fp;
MagHeader	*head;
char		*palette;
{
	int 	i;
	char	*p;
	
	/****/
	
	i = strlen( MAG_check );
	if( i != fwrite( MAG_check, 1, i, fp) )
		return ERROR;
	
	if( comment == '\0' )	p = def_comment;
					else	p = comment;
	fwrite( p, 1, strlen(p), fp);
	fputc( '\x1a' , fp);
	
	/****/
	
	fwrite( head,  sizeof(MagHeader), 1, fp);
	fwrite( palette, 1, (head->screen ==0 ? 16*3:256*3) ,fp);
	
	/****/		/* ここまで慎重にならなくてよいかも */
	
	i = (head->flgB - head->flgA);
	if( i != fwrite( flgA, 1, i, fp) )
		return ERROR;
	
	for( i=0; i<flgBPage; i++ )
		if( flgBPageSize != fwrite( &flgB[i][0], 1, flgBPageSize, fp ) )
			return ERROR;
	if( (i = head->flgB_size % flgBPageSize) >0 )
		if( i != fwrite( &flgB[flgBPage][0], 1, i, fp ) )
			return ERROR;
	
	for( i=0; i<pixPage; i++ )
		if( pixPageSize != fwrite( &pixel[i][0], 1, pixPageSize, fp ) )
			return ERROR;
	if( (i = head->pixel_size % pixPageSize) >0 )
		if( i != fwrite( &pixel[pixPage][0], 1, i, fp ) )
			return ERROR;
	
	return NOERR;
}

int 	MAG_save( fp, buf,fr,pix,pal )
char	*buf,*pal;
FILE	*fp;
int 	pix;
FRAME	*fr;
{
	int 	ret;
	int 	col;
	
	MagHeader	head;
	char		*palette;
	
	col = ((pix==8) ? 256 : 16);
	
	/* パレット用メモリ確保 */
	if( (palette = PI_MALLOC( 3*col ))==NULL )
		return PI_ERROR_NO_MEMORY;
	
	/*** ヘッダ作成 ***/
	if( MAG_head( &head, palette,fr,pal ) )
	{	free(palette);
		return ERROR ;
	}
	/*** 圧縮 ***/
	
	ret = MAG_encode( &head, buf );
	if( ret!=NOERR )
		goto savequit;
	
	/*** 書き出し ***/
	
	ret = MAG_writeBuffers( fp, &head, palette );
	
savequit:
	PI_FREE( palette );
	MAG_freeBuffers();
	
	return ret;
}


/**********************************************************************/
/** プラグイン呼び出し関数 **/

int 	APL_exec()
{
	FILE	*fp;
	int 	ret;
	FRAME	fr;
	
	if( pi_imge->pix >8 ) return NOERR;
	
	fr.lupx = fr.lupy = 0;
	fr.rdwx = pi_imge->size.x-1;
	fr.rdwy = pi_imge->size.y-1;
	
	
//	if( my_getenv("USER")==NULL )
	MAG_setComment();
	
	/**/
	if( (fp=fopen( PI_FNAME,"wb" ))==NULL )
		return PI_ERROR_FILE_OPEN;
	
	ret=MAG_save( fp, pi_imge->image ,	&fr,
					  pi_imge->pix,
					  pi_imge->clut );
	
	fclose( fp );
	
	if( ret )	remove( PI_FNAME );
	/**/
	
	if( user    != '\0' )	PI_FREE( user );
	if( comment != '\0' )	PI_FREE( comment );
	return ret;
}



/*************************************************************/
/*	乙女座から得たコメントを分解する	*/

#define	iskanji(c)	( (0x81<=(c) && (c)<=0x9f) || (0xe0<=(c) && (c)<=0xfc))
#define	iskanji2(c)	( (0x40<=(c) && (c)<=0x7e) || (0x80<=(c) && (c)<=0xfc))

int MAG_setComment()
{
	int 	i,j,len;
	
	if( PI_COMMENT==NULL || (len=strlen( PI_COMMENT ))<1 )
		return NOERR;
	
	/* 面倒なのでメモリチェックは無視。これくらいで失敗するかな?? */
	
	for( i=0,j=0; i < LEN_USER ; 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_USER+1 );
	if( len> LEN_USER )
		comment=PI_MALLOC( len - LEN_USER +1);
	else
	{	strcpy( user, PI_COMMENT );
		return NOERR;
	}
	j=0;
	/* 区切りに漢字コードが重なっている場合  念のため処理しておく */
	if( iskanji( PI_COMMENT[ LEN_USER-1 ] )
		 && iskanji2( PI_COMMENT[ LEN_USER ] ) )
		j = 1;
	strncpy( user,		PI_COMMENT, LEN_USER-j );user[LEN_USER-j]='\0';
	strcpy ( comment,	PI_COMMENT+ LEN_USER-j );
	
}

/**********************************************************************
**	環境変数を取得する
**	
**	プラグイン内部からは getenv 関数によって環境変数が獲得できないので
**	TOS_getPath 関数により獲得する。
**	ただしこの関数は High-C のマニュアルでは全く触れられていない！
**	
**	int TOS_getPath( char *buf, const char *name, int len )
**		buf  格納用バッファ（十分な大きさが必要）
**		name 得たい環境変数名（末尾に '=' も追加しておくこと）
**		len  name の文字長
**	返り値
**		実際に得た文字長。存在しなかったとき 0 となる。
**		ただしこの関数はパスを得るものであるため、末尾が'\\'でない場合には
**		勝手に'\\'が追加される。（漢字コードの区別は行われていない）
**		また、環境変数名と'='は区別されないので、name に'='を追加しておか
**		ないと'='も変数の内容として返される。
*/

#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;
}

