


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winb.h>
#include <te.h>
#include <fntb.h>
#include <gui.h>
#include <file_dlg.h>
#include <tifflib.h>
#include <egb.h>
#include <mos.h>
#include <snd.h>

#include "makemov.h"

#define	MALLOC		TL_malloc
#define	FREE		TL_free

static struct {
	int		frame;
	int		total_byte;
	int		rough;
	int		premove;
	int		x0,y0,x1,y1;
	int		b,r,g;
	int		soft;
	int		color_rate, mode;		// 外部からいじってはだめ
	int		wait;
} svar = { 0,0,0,0, 0,0,319,239, 0,0,0, 0,0,0, 0 };

/*
	外部に必要な関数：
		int LoadFrame(char *buf, int nFrame, int *nWidth, int *nHeight);
			// buf : ３２０×２４０×２バイトのバッファ
			// nFrame : フレーム番号(0〜 total-1)
			// *nWidth, *nHeight : 用意したフレームの大きさを格納する
			//						(or NULL)
		返り値: 0=フレームの用意ができた -1:失敗
*/

static int nFrameWid, nFrameHt;
static char *pImageBuf;
static int scount0;

static char *rbuf,*bbuf;

#define	GETRGB(pixel, r,g,b)		\
{									\
	b = (pixel) & 0x1f;				\
	r = ((pixel) >>  5) & 0x1f;		\
	g = ((pixel) >> 10) & 0x1f;		\
}

#define INTSWAP(a,b)   {int t=a; a=b; b=t;}

/*--------------------------------------------------------*/
/*          pImageBuf の内容にソフトネスをかける          */
/*--------------------------------------------------------*/

	int mov_save_soft( char *buffer )
	// pImageBuf(320*240,word array) の内容にソフトネスをかけ、
	// 結果を buffer(320*240,word array) に出力
		// buffer: 320*240*2 bytes buffer
		//   ソフトネスをかけた結果の出力先
	{
		int x,y;
		for ( y=svar.y0 ; y<=svar.y1 ; y++ )
		{
			for( x=svar.x0 ; x<=svar.x1 ; x++ )
			{
				int b,r,g;
				int n;
				b = r = g = 0;
				n = 0;
			  // 各原色の強さの和をもとめる
				int ix,iy;
				for (iy=y-1; iy<=y+1; iy++)
				{
					if (iy < 0 || 240 <= iy)
						continue;
					for ( ix=x-1; ix<=x+1; ix++ )
					{
						if (0 <= ix && ix < 320)
						{
							int pix;
							pix = WORD(pImageBuf+iy*640+ix*2);
							b += (   pix         & 0x1f );
							r += ( ( pix >>  5 ) & 0x1f );
							g += ( ( pix >> 10 ) & 0x1f );
							n++;
						}
					}
				}
			  // 平均値を計算
				if (n > 0)
				{
					b = ( b + (b % n ) ) / n;
					r = ( r + (r % n ) ) / n;
					g = ( g + (g % n ) ) / n;
				}
			  // １ピクセルの計算結果をバッファに書き込む
				WORD( buffer+y*640+x*2 ) = b + (r << 5) + (g << 10);
			}
		}
		return 0;
	}

/*--------------------------------------------------------*/
/*       pImageBuf の絵に、buffer の内容をかぶせる        */
/*--------------------------------------------------------*/

	int mov_save_filter( int flt, char *buffer )
	{
		int x, y, flt1, flt2, flt3;
		int filt[][19] = {
			{ -9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9 },
			{ -9,-8,-7,-6,-5,-4,-3,-2, 0,0,0,2,3,4,5,6,7,8,9 },
			{ -9,-8,-7,-6,-5,-4,-3,-1, 0,0,0,1,3,4,5,6,7,8,9 },
			{ -9,-8,-7,-6,-5,-4,-2,-1, 0,0,0,1,2,4,5,6,7,8,9 },
			{ -9,-8,-7,-6,-5,-4,-2, 0, 0,0,0,0,2,4,5,6,7,8,9 },
			{ -9,-8,-7,-5,-4,-2,-1, 0, 0,0,0,0,1,2,4,5,7,8,9 },
		};
		int f1[] = { 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5 };
		int f2[] = { 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5 };
		int f3[] = { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5 };
			/* フィルター */
		if ( flt < 0 )
			flt = 0;
		if ( flt > 15 )
			flt = 15;
		flt1 = f1[ flt ];
		flt2 = f2[ flt ];
		flt3 = f3[ flt ];
		if (flt > 0)
		{
			for (y=svar.y0; y<=svar.y1; y++)
			{
				for (x=svar.x0; x<=svar.x1; x++)
				{
					int idx = y*640 + x*2;	/* buffer add */
					int px1,px2;
					int r1,g1,b1,r2,g2,b2;
					px1 = WORD( buffer+idx );
					px2 = WORD( pImageBuf+idx );
					GETRGB(px1,r1,g1,b1);
					GETRGB(px2,r2,g2,b2);
					if ((b2-b1 > -10) && (b2-b1 < 10) )
						b2 = b1 + filt[ flt1 ][b2-b1+9];
					if ((r2-r1 > -10) && (r2-r1 < 10) )
						r2 = r1 + filt[ flt2 ][r2-r1+9];
					if ((g2-g1 > -10) && (g2-g1 < 10) )
						g2 = g1 + filt[ flt3 ][g2-g1+9];
					WORD(pImageBuf+idx) = b2 + (r2 << 5) + (g2 << 10);
				}
			}
		}
		return 0;
	}

/*--------------------------------------------------------*/
/*        pImageBuf の、指定範囲の外側をクリアする        */
/*--------------------------------------------------------*/

	int mov_save_trim( cmpmode )
	int cmpmode;
	{
		int color, x, y;
		color = svar.b + (svar.r << 5) + (svar.g << 10);
		int i;
	  // 最上位ビットをクリア
		for (i=0; i<320*240*2; i+=4)
			DWORD( rbuf+i ) &= 0x7fff7fff;
		for (i=0; i<320*240*2; i+=4 )
			DWORD( pImageBuf+i ) &= 0x7fff7fff;
	  // 範囲指定値の整合性チェック
		if (svar.x0 > svar.x1)
			INTSWAP(svar.x0, svar.x1);
		if (svar.y0 > svar.y1)
			INTSWAP(svar.y0, svar.y1);
		if (svar.x0 == 0 && svar.y0 == 0 && svar.x1 == 319 && svar.y1 == 239)
			return 0;
	  // 
		for ( y=0 ; y < 240 ; y++ )
		{
			for ( x=0 ; x < 320 ; x++ )
			{
				if (x < svar.x0 || svar.x1 < x || y < svar.y0 || svar.y1 < y)
				{
					int i = y*640 + x*2;	/* buffer add */
					if ( cmpmode != 0 )
					{
						WORD(rbuf     +i) |= 0x8000;
						WORD(pImageBuf+i) ^= (unsigned int)0xffff;
					}
					else
						WORD(pImageBuf+i) = color;
				}
			}
		}
		return 0;
	}


	mov_save_end( namemov )
	char *namemov;
	{
		FILE *fps;
		int i, temp, data[2];

		if( svar.frame == 0 )
			return 0;
		for( i=0 ; i<6 ; i++ ){		/* disk write protect の場合6回必要 */ 
			if( ( fps = fopen( namemov, "r+b" ) ) != NULL )goto save2;
		}
		return 2;
	save2:
		data[0] = svar.total_byte;
		data[1] = svar.frame;			/* data長 page */
		fseek( fps, 8, SEEK_SET );
		temp = fwrite( (char *)data, 8, 1, fps );
		fclose( fps );
		if( temp < 1 ){
		/*	remove( namemov );	error */
			return 2;
		}
		return 0;
	}

/*--------------------------------------------------------*/
/*                圧縮処理のサブルーチン群                */
/*--------------------------------------------------------*/

/* 平均化したデータをrbufに書き込む */

	int mov_save_stosw1_write( p )
	int p;
	{
		int i, j, k, n, m, data, addre1, addre2, len;
		int a1, a2, vadd;

		data = WORD( bbuf + p ) | 0x8000;
		p += 2;
		n = WORD( bbuf + p );
		p += 2;
		if( n ){
			for( i=0 ; i<n ; i++ ){
			addre1 = WORD( bbuf + p ) << 16;
			p += 2;
			m = WORD( bbuf + p );
			p += 2;
			if( m ){
				for( j=0 ; j<m ; j++ ){
				addre2 = addre1 + WORD( bbuf + p );
				p += 2;
				len = BYTE( bbuf + p );
				p += 1;
				if( len == 0xff ){
					len = WORD( bbuf + p );
					p += 2;
				}
				if( len == 0xffff ){
					len = DWORD( bbuf + p );
					p += 4;
				}
				if( len ){
					for( k=0 ; k<len ; k++ ){
					WORD( rbuf + addre2 + k*2 ) = data;
					}
				}
					/* 両端okなら処理済みに */
				k = addre2 - 2;
				if( k > 0 && k < 153600 ){
					vadd = ( (k/640) << 10 ) + ( k % 640 );
					a1 = WORD(pImageBuf + k);
					a2 = WORD(rbuf      + k);
					if (mov_save_cmp_data(a1, a2, svar.rough) == 0
					    && (a2 & 0x8000) == 0 )
					{
						WORD(rbuf      + k) = a2 | 0x8000;
						WORD(pImageBuf + k) ^= 0xffff;
					}
				}
				k = addre2 + len*2;
				if( k > 0 && k < 153600 ){
					vadd = ( (k/640) << 10 ) + ( k % 640 );
					a1 = WORD(pImageBuf+k);
					a2 = WORD( rbuf+k );
					if( mov_save_cmp_data( a1, a2, svar.rough ) == 0 
					 && (a2 & 0x8000) == 0 ){
					WORD( rbuf+k ) = a2 | 0x8000;
					WORD(pImageBuf+k) ^= 0xffff;
					}
				}
				}
			}
			}
		}
		return 0;
	}

/* store 最初にdataを読み込むtype & data平均化付き(1992 1.17) */

	int mov_save_stosw1( i )
	int i;
	{
		int p1, p2, p3, p4, p5, p6;
		int j, j1, j2, j3;
		int k, k1;
		int data;
		int a1, a2;
		int n1, n2, n3;
		int bt, rt, gt, nt, dt;		/* data 平均値用 変数 */

		WORD( bbuf+i ) = 0x1222; i+=2;	/* stosw brock */
		p1 = i; i+=4;			/* size point */
		p2 = i; i+=2;			/* data count point */
		n1 = 0;				/* data count */
		for( j1=0 ; j1<153600 ; j1+=2 ){
			if( WORD( rbuf+j1 ) & 0x8000 )goto stos03;	/* 処理済skip */
			data = WORD(pImageBuf+j1);
			if(
			  mov_save_skip_check6( data, j1 )
			)goto stos03;		/* skip check */
			bt = 0; rt = 0; gt = 0; nt = 0;		/* for data 平均 */
			p3 = i;			/* data top point */
			WORD( bbuf+i ) = data; i+=2;	/* data */
			p4 = i; i+=2;		/* offset count point */
			n2 = 0;			/* offset count */
			for( j2=0 ; j2<3 ; j2++ ){ /* offset loop */
			p5 = i;			/* offset top point */
			WORD( bbuf+i ) = j2; i+=2;	/* offset */
			p6 = i; i+=2;		/* d-address count point */
			n3 = 0;			/* d-address count */
			k = 0; k1 = 0;		/* data長count係数 */
			for( j3=0x10000*j2 ; j3<0x10000*(j2+1) ; j3+=2 ){
				if( j3 >= 153600 )break;
				a2 = WORD( rbuf+j3 );
				if( a2 & 0x8000 ){			/* 処理済skip */
				if( k ){
					i = i + k1;
					k = 0; k1 = 0;
				}
				goto stos01;
				}
				j = ( (j3/640) << 10 ) + ( j3 % 640 );
				a1 = WORD(pImageBuf+j3);
				if( (a2 & 0x8000) == 0		 /* 手つかず */
					&& mov_save_cmp_data( data, a1, svar.rough ) == 0 
									/* 同色と認め */ 
				){
				if( k == 0 )if( 
				  mov_save_skip_check4(data,j3)
				)goto stos01;
				k++;

					/* for data 平均 */
				bt = bt + ( a1 & 0x1f );
				rt = rt + ( (a1 >> 5) & 0x1f );
				gt = gt + ( (a1 >> 10) & 0x1f );
				nt++;

				WORD( rbuf+j3 ) = 0x8000 | data;	/* 処理印 */
				WORD(pImageBuf+j3) ^= 0xffff;
				if( k == 1 ){
					n3++;
					WORD( bbuf+i ) = ( j3 & 0xffff );
				}
				if( k<0xff ){
					BYTE( bbuf+i+2 ) = k;
					k1 = 1+2;
				}
				if( (k>=0xff) && (k<0xffff) ){
					BYTE( bbuf+i+2 ) = 0xff;
					WORD( bbuf+i+3 ) = k;
					k1 = 3+2;
				}
				if( k>=0xffff ){
					BYTE( bbuf+i+2 ) = 0xff;
					WORD( bbuf+i+3 ) = 0xffff;
					DWORD( bbuf+i+5 ) = k;
					k1 = 7+2;
				}
				}
				else {
				if( k ){
					i = i + k1;
					k = 0; k1 = 0;
				}
				}
			stos01: ;
			}
		stos02:	i = i + k1;
			WORD( bbuf+p6 ) = n3;
			if( n3 )n2++;
			else i = p5;
			}
			WORD( bbuf+p4 ) = n2;
			if( n2 ){
			n1++;

			if( nt ){	/* for data 平均 */
				dt =  ( (bt + bt % nt) / nt )
				  + ( ( (rt + rt % nt) / nt ) << 5 )
				  + ( ( (gt + gt % nt) / nt ) << 10 );
				WORD( bbuf + p3 ) = dt;
				mov_save_stosw1_write( p3 );
			}

			}
			else i = p3;
		stos03: ;
		}
		WORD( bbuf+p2 ) = n1;
		if( n1 == 0 )i = p2;
		DWORD( bbuf+p1 ) = i - p2;
		return i;
	}

	int mov_save_skip_check2( data, n )
	int data, n;
	{			/* 4つdataと同じ色が続くもの以外はスキップ */
		int a, b, i, j, k;

		if( n > 153588 )return 1;
		if( WORD( rbuf+n ) & 0x8000 )return 1;
		for( i=0 ; i<4 ; i++ ){
			k = n + (i << 1);
			a = WORD(pImageBuf+k);
			b = WORD( rbuf + k );
			if( mov_save_cmp_data( data, a, svar.rough) )return 1;
		}
		return 0;
	}

/* skip_check1 = 1:skip ( for stosw ) */

	int mov_save_skip_check4( data, n )
	int data, n;
	{			/* 4つdataと同じ色が続くもの以外はスキップ */
		int a, b, i, j, k;

		if( n > 153588 )return 1;	/* ↓処理済直後はok */
		if( n && ( (DWORD( rbuf+n-2 ) & 0x80008000) == 0x8000 ) )return 0;
		if( WORD( rbuf+n ) & 0x8000 )return 1;
		for( i=0 ; i<4 ; i++ ){
			k = n + (i << 1);
			j = ( (k/640) << 10 ) + ( k % 640 );
			a = WORD(pImageBuf+k);
			b = WORD( rbuf + k );
			if( b & 0x8000 )return 0;
			if( mov_save_cmp_data( data, a, svar.rough) )return 1;
		}
		return 0;
	}

	int mov_save_skip_check6( data, n )
	int data, n;
	{			/* 6つdataと同じ色が続くもの以外はスキップ */
		int a, b, i, j, k;

		if( n > 153588 )return 1;
		if( DWORD( rbuf+n ) & 0x80008000 )return 1;
		if( DWORD( rbuf+n+4 ) & 0x80008000 )return 1;
		if( DWORD( rbuf+n+8 ) & 0x80008000 )return 1;
		for( i=0 ; i<6 ; i++ ){
			k = n + (i << 1);
			j = ( (k/640) << 10 ) + ( k % 640 );
			a = WORD(pImageBuf+k);
			b = WORD( rbuf + k );
			if( mov_save_cmp_data( data, a, svar.rough) )return 1;
		}
		return 0;
	}

/* movsw */

	int mov_save_movsw( i )
	int i;
	{
		int p1, p4, p5, p6;
		int j, j2, j3, j4;
		int k, k1, k2;
		int a1, a2;
		int n2, n3;

		WORD( bbuf+i ) = 0x2222; i+=2;	/* movsw brock */
		p1 = i; i+=4;			/* size point */
		p4 = i; i+=2;		/* offset count point */
		n2 = 0;			/* offset count */
		for( j2=0 ; j2<3 ; j2++ ){ /* offset loop */
			p5 = i;			/* offset top point */
			WORD( bbuf+i ) = j2; i+=2;	/* offset */
			p6 = i; i+=2;		/* d-address count point */
			n3 = 0;			/* d-address count */
			k = 0; k1 = 0;		/* data長count係数 */
			for( j3=0x10000*j2 ; j3<0x10000*(j2+1) ; j3+=2 ){
			if( j3 >= 153600 )break;
			j = ( (j3/640) << 10 ) + ( j3 % 640 );		/* vram add */
			a1 = WORD(pImageBuf+j3); a2 = WORD( rbuf+j3 );
			if( (a2 & 0x8000) == 0 ){
				k++;
				WORD( rbuf+j3 ) = 0x8000 | a1;		/* 処理印 */
				WORD(pImageBuf+j3) ^= 0xffff;
				if( k == 1 ){
				n3++;
				k2 = j3;			/* address記憶 */
				WORD( bbuf+i ) = ( j3 & 0xffff );
				}
				if( k<0xff ){
				BYTE( bbuf+i+2 ) = k;
				k1 = 1+2;
				}
				if( (k>=0xff) && (k<0xffff) ){
				BYTE( bbuf+i+2 ) = 0xff;
				WORD( bbuf+i+3 ) = k;
				k1 = 3+2;
				}
				if( k>=0xffff ){
				BYTE( bbuf+i+2 ) = 0xff;
				WORD( bbuf+i+3 ) = 0xffff;
				DWORD( bbuf+i+5 ) = k;
				k1 = 7+2;
				}
			}
			else {
				if( k ){
				i = i + k1;
				k = 0; k1 = 0;
				for( j4=k2 ; j4<j3 ; j4+=2 ){ /* data write */
					WORD( bbuf+i ) = WORD( rbuf+j4 ) & 0x7fff;
					i+=2;
				}
				}
			}
		movs01: ;
			}
		movs02: if( k ){
			i = i + k1;
			k = 0; k1 = 0;
			for( j4=k2 ; j4<j3 ; j4+=2 ){ /* data write */
				WORD( bbuf+i ) = WORD( rbuf+j4 ) & 0x7fff;
				i+=2;
			}
			}
			WORD( bbuf+p6 ) = n3;
			if( n3 )n2++;
			else i = p5;
		}
		WORD( bbuf+p4 ) = n2;
		if( n2 == 0 )i = p4;
		DWORD( bbuf+p1 ) = i - p4;
		return i;
	}

/* move box ( 16*16dot )指定されたboxを転送 */

	int mov_save_movebox16( i )
	int i;
	{
		int p1, p2, n;
		int j, j2, j3;
		int xmin, xmax, ymin, ymax, x, y, x1, y1, x2, y2, kx, ky, d;
		int max, a, a1, a2, b, r, g, cr, cb, yu, temp, temp2;
		int bfaddr, rbaddr, vraddr;
		char *offset1, *offset2;
		char vyuv[1024];		/* vram YUV data */

		offset1 = bbuf+0x1000;	/* 旧画面データをchar *offset1にもう1枚作る */
		for( j=0 ; j<153600 ; j+=4 )DWORD( offset1+j ) = DWORD( rbuf+j );
		offset2 = bbuf+0x30000;	/* 旧画面データのYUV data */
			/* rgb data (offset1) を yuv data (offset2) に */
		for( j=0 ; j<76800 ; j++ ){
			a = WORD( offset1 + (j << 1) );
			b = a & 0x1f;
			r = (a >> 5) & 0x1f;
			g = (a >> 10) & 0x1f;
				/* G,R,BをY,Cr,Cbに変換 */
			cr = -107*g + 128*r - 21*b + 3968;	/* 0〜256*31 */
			cb = -85*g - 43*r + 128*b + 3968;
			yu = ( 150*g + 77*r + 37*b ) >> 4;
				/* Cr(8), Cb(8), Y(16) */
			DWORD( offset2 +(j << 2) )
			 = (cr >> 5) + ((cb << 3)& 0x0ff00) + (yu << 16);
		}

		WORD( bbuf+i ) = 0x3010; i+=2;	/* move box 16 */
		p1 = i; i+=4;			/* size point */
		p2 = i; i+=2;		/* data 個数 count point */
		n = 0;			/* data 個数 count */
			/* trimming area を はずすためのもの */
		xmin = 16*( svar.x0/16 ); if( svar.x0%16 )xmin += 16;
		xmax = 16*( svar.x1/16 ); if( svar.x1%16 == 15 )xmax += 1;
		ymin = 16*( svar.y0/16 ); if( svar.y0%16 )ymin += 16;
		ymax = 16*( svar.y1/16 ); if( svar.y1%16 == 15 )ymax += 1;
		for( y=ymin ; y<ymax ; y+=16 ){
			for( x=xmin ; x<xmax ; x+=16 ){
				/* vram YUV */
			vraddr = y*1024 + x*2; 	/* vram address */
			j = 0;			/* count vyuv */
			for( j2=0 ; j2<16 ; j2++ ){
				for( j3=0 ; j3<32 ; j3+=2 ){
				a = WORD(pImageBuf+(y+j2)*640+x*2+j3);
				b = a & 0x1f;
				r = (a >> 5) & 0x1f;
				g = (a >> 10) & 0x1f;
					/* G,R,BをY,Cr,Cbに変換 */
				cr = -107*g + 128*r - 21*b + 3968;	/* 0〜256*31 */
				cb = -85*g - 43*r + 128*b + 3968;
				yu = ( 150*g + 77*r + 37*b ) >> 4;
					/* Cr(8), Cb(8), Y(16) */
				DWORD( vyuv +j )
				 = (cr >> 5) + ((cb << 3) & 0x0ff00) + (yu << 16);
				j+=4;
				}
				vraddr += 1024;
			}
			temp2 = mov_save_cmp_box16( offset2, x, y, vyuv, svar.rough );
			if( temp2 > 255-8 )  { goto mov02; }
			max = temp2;
			kx = x; ky = y;
				/* ずらして調べる */
			x1 = x; y1 = y;
			for( d=svar.premove ; d>0 ; d-- ){
			  for( j2 = -1 ; j2 <= 1 ; j2++ ){
				 for( j3 = -1 ; j3 <= 1 ; j3++ ){
				x2 = x1 + j3*d;
				y2 = y1 + j2*d;
				if( x2 < 0 )x2 = 0;	/* はみ出しを切る */
				if( x2 > 304 )x2 = 304;
				if( y2 < 0 )y2 = 0;
				if( y2 > 224 )y2 = 224;
				temp
				= mov_save_cmp_box16(
				 offset2, x2, y2, vyuv, svar.rough );
				if( temp > max ){
					max = temp;
					kx = x2; ky = y2;
				}
				 }
			  }
			  x1 = kx; y1 = ky;
			}
				/* 大まかに調べる */
			x1 = x - 32; if( x1 < 0 )x1 = 0;
			x2 = x + 32; if( x2 > 304 )x2 = 304;
			y1 = y - 32; if( y1 < 0 )y1 = 0;
			y2 = y + 32; if( y2 > 224 )y2 = 224;
			for( j2 = x1 ; j2 <= x2 ; j2+=16 ){
				for( j3 = y1 ; j3 <= y2 ; j3+=16 ){
				temp
				= mov_save_cmp_box16( offset2, j2, j3, vyuv, svar.rough );
				if( temp > max ){
					max = temp;
					kx = j2; ky = j3;
				}
				}
			}
			if( max > temp2+8 ){
				DWORD( bbuf+i ) = y*640 + x*2; i+=4;
				WORD( bbuf+i ) = kx; i+=2;
				WORD( bbuf+i ) = ky; i+=2;
				n++;
				bfaddr = ky*640 + kx*2; 	/* offset1 相対 address */
				rbaddr = y*640 + x*2; 	/* rbuf 相対 address */
				vraddr = y*1024 + x*2; 	/* vram address */
				for( j2=0 ; j2<16 ; j2++ ){
				for( j3=0 ; j3<32 ; j3+=2 ){
					a1 = WORD(pImageBuf+(y+j2)*640+x*2+j3);
					a2 = WORD( offset1+bfaddr+j3 ) & 0x7fff;
					WORD( rbuf+rbaddr+j3 ) = a2;
					if( mov_save_cmp_data( a1, a2, svar.rough ) == 0 
					&& (a1 & 0x8000) == 0 ){
					WORD( rbuf+rbaddr+j3 ) = a2 | 0x8000;
					WORD(pImageBuf+(y+j2)*640+x*2+j3) ^= 0xffff;
					}
				}
				bfaddr+=640; rbaddr+=640; vraddr+=1024;
				}
			mov02:  ;
			}
			}
		}
		WORD( bbuf+p2 ) = n;
		if( n == 0 )i = p2;
		DWORD( bbuf+p1 ) = i - p2;
		return i;
	}

/* YUV data buffer の char buffer(320*240)[開始座標(x,y)]と
char *vyuv(16*16)の中でYUV判定で同じものを数える */

	int mov_save_cmp_box16( buffer, x, y, vyuv, rate )
	char *buffer, *vyuv;
	int x, y, rate;
	{
		int d1, d2, i, j, n, rate2;

		buffer = buffer + 1280*y + 4*x;
		n = 0; rate2 = rate*rate;
		for( j=0 ; j<16 ; j++ ){
			for( i=0 ; i<64 ; i+=4 ){
				d1 = (WORD( buffer+i+2 ) - WORD( vyuv+2 ));
				if( d1 > rate )goto cmp01;
				if( d1 < -rate )goto cmp01;
				d1 = BYTE( buffer+i ) - BYTE( vyuv );
				d2 = BYTE( buffer+i+1 ) - BYTE( vyuv+1 );
				if( d1*d1 + d2*d2 <= rate2 )n++;
			cmp01:  vyuv += 4;
			}
			buffer += 1280;
		}
		return n;
	}

/* Y,Cr,CbによるCOLCOR DATA比較法 91 7 24 */

	int mov_save_cmp_data( a, b, rate )
	int a, b, rate;
	{
		int b1,r1,g1, b2,r2,g2, d1, d2;

		if( (a & 0x7fff) == (b & 0x7fff) )return 0; /* 明白なものは処理 */
		else if( rate == 0 )return 1;
			/* G,R,B算出 */
		b1 = a & 0x1f; b2 = b & 0x1f;
		r1 = (a >> 5) & 0x1f; r2 = (b >> 5) & 0x1f;
		g1 = (a >> 10) & 0x1f; g2 = (b >> 10) & 0x1f;
			/* G,R,BをY,Cr,Cbに変換 & 判定 */
		d1 = ( 150*(g2 - g1) + 77*(r2 - r1) + 37*(b2 - b1) ) >> 4;
		if( d1 > rate )return 1;
		if( d1 < -rate )return 1;
		d1 = ( -107*(g2-g1) + 128*(r2-r1) - 21*(b2-b1) ) >> 5;
		d2 = ( -85*(g2 -g1) - 43*(r2 -r1) + 128*(b2 -b1) ) >> 5;
		if( d1*d1 + d2*d2 > rate*rate )return 1;
		else return 0;
	}

/*--------------------------------------------------------*/
/*                      圧縮の心臓部                      */
/*--------------------------------------------------------*/

	int mov_save_page( char *pszMovName )
	{
		static int softAdjust[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,15};
		static int filtAdjust[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,15};

		int x,y;
		int cmpmode;
		FILE *fps;
		// int i, j, k, n, x, y, data, temp;
		// int xmin, ymin, xmax, ymax;
		// int a1, a2;
		int scount1;
		//, cmpmode ;
		// char para[64] ;
		char hread[256];	/* for head read */
		// 	/* mov head */
		char MovHead[256]; 
		static int MovHeadInit[] = { 0x32564f4d, 16, 0, 0, 320, 240, 640 };
		int pghead[8];

	  // x0 < x1,  y0 < y1 になるようにチェック
		if ( svar.x0 > svar.x1 )
			INTSWAP(svar.x0, svar.x1);
		if ( svar.y0 > svar.y1 )
			INTSWAP(svar.y0, svar.y1);
	  // 前の絵と同じ点をカウント
		scount1 = 0;
		for ( y = svar.y0 ; y <= svar.y1 ; y++ )
		{
			for( x = svar.x0 ; x <= svar.x1 ; x++ )
			{
				int i = y*640 + x*2;
				if (mov_save_cmp_data(WORD(rbuf+i),WORD(pImageBuf+i),
									  svar.rough) == 0)
					scount1++; 
			}
		}
		cmpmode = ((scount1 < (scount0 >> 1)) ? 0 : 1);
		scount0 = scount1;
	  // ソフトネスをかける
		if ( svar.soft > 0 )
		{
			mov_save_soft( bbuf );
			mov_save_filter( softAdjust[ svar.soft ], bbuf );
		}

	  // ヘッダを出力
		if ( svar.frame == 0 )
		{
			svar.frame = 0;
			svar.total_byte = 0;
			cmpmode = 0;
			scount0 = (svar.x1-svar.x0+1) * (svar.y1-svar.y0+1) / 8;
				/* 次回から1/16以上同じなら差分 */
			int k;
			for( k=0 ; k<256 ; k++ )
				MovHead[k] = (char)0;
			for( k=0 ; k<7 ; k++ )
				DWORD( MovHead+k*4 ) = MovHeadInit[k];
			DWORD(MovHead + 28 ) = svar.wait;
			WORD( MovHead + 32 ) = svar.x0;
			WORD( MovHead + 34 ) = svar.y0;
			WORD( MovHead + 36 ) = svar.x1;
			WORD( MovHead + 38 ) = svar.y1;
			MovHead[150] = (char)1;	/* タイミングフラグを表示してウエイト */
			if ((fps=fopen(pszMovName, "wb")) == NULL)
				return 2;
			fwrite( MovHead, 256, 1, fps );
			fclose( fps );
		}
	  // 画面のはみ出した部分をclear
		mov_save_trim( cmpmode );		/* trimming */
	  // リフレッシュ
		int temp; // エラーコード
		if ( cmpmode == 0 )
		{
			int k;
		  // 最多色検索
			for (k=0; k<32768; k++)
				DWORD( rbuf+k*4 ) = 0;
			for (k=0; k<320*240; k++)
			{
				int data = WORD(pImageBuf+k*2);
				DWORD(rbuf+data*4) += 1;
			}
			int cnt;	// 色の出現数
			int nCol;	// もっともよく現れる色
			cnt = 0;
			nCol = 0;
			for( k=0 ; k<32768 ; k++ )
				if ( DWORD(rbuf+k*4) > cnt )
					cnt = DWORD(rbuf+k*4), nCol = k;
		  // 塗りつぶし
			for (k=0; k<320*240*2; k+=2)
			{
				WORD(rbuf+k) = nCol;
				if (mov_save_cmp_data(nCol,WORD(pImageBuf+k),svar.rough) == 0)
				{
					WORD(rbuf+k) = nCol | 0x8000;	/* 処理印 */
					WORD(pImageBuf+k) ^= (unsigned int)0xffff;
				}
			}
		  // 孤立した点は処理解除
			for( k=2 ; k<153598 ; k+=2 )
			{
				if( (DWORD( rbuf+k ) & 0x80008000) == 0x8000
				    && (WORD( rbuf+k-2 ) & 0x8000) == 0 )
				{
					WORD( rbuf+k ) &= 0x7fff;
					WORD(pImageBuf+k) ^= 0xffff;
				}
			}
		  // ページヘッダの作成
			int i;
			WORD(bbuf    ) = 3;			/* brock 数 */
			WORD(bbuf+2  ) = 0x1422;	/* stosd brock */
			DWORD(bbuf+4 ) = 17;		/* data size */
			WORD(bbuf+8  ) = 1;			/* data 数 */
			DWORD(bbuf+10) = nCol + ( nCol << 16 ); /* data */
			WORD(bbuf+14 ) = 1;			/* offset 数 */
			WORD(bbuf+16 ) = 0x0000; 	/* offset */
			WORD(bbuf+18 ) = 1;			/* data 個数 */
			WORD(bbuf+20 ) = 0x0000;	/* 下位 address */
			BYTE(bbuf+22 ) = 0xff;		/* 長さ */
			WORD(bbuf+23 ) = 38400;
			i = 25;
			i = mov_save_stosw1( i );		/* stosw */
		  // 再びくっついてる点は復活
			for( k=2 ; k<153598 ; k+=2 ){
				if( (DWORD( rbuf+k ) & 0x80008000) == 0x80000000
				    || (DWORD( rbuf+k-2 ) & 0x80008000) == 0x8000 )
				{
					int a1,a2;
					a1 = WORD(pImageBuf+k);
					a2 = WORD(rbuf     +k);
					if (mov_save_cmp_data(a1,a2,svar.rough) == 0 
						&& (a2 & 0x8000) == 0 )
					{
						WORD( rbuf+k ) = a2 | 0x8000;	/* 処理印 */
						WORD( pImageBuf+k ) ^= 0xffff;
					}
				}
			}
		  // 
			i = mov_save_movsw( i );		/* movsw */
			pghead[0] = svar.frame;		/* page page head */
			pghead[1] = i;			/* dsize */
			pghead[2] = 0;			/* ox,oy */
			pghead[3] = 0;			/* wait & loop */
			pghead[4] = 0;			/* リザーブ */
			pghead[5] = 0;			/* sound data1 */
			pghead[6] = 0;			/* sound data2 */
			pghead[7] = 0;			/* sound data3 */
			if((fps=fopen( pszMovName, "r+b" )) == NULL )
				return 2;
			fread( hread, 256, 1, fps );
			fseek( fps, 256+DWORD(hread+8), SEEK_SET );
			fwrite( (char *)pghead, 32, 1, fps );
			fwrite( bbuf, i, 1, fps );
			temp = ferror( fps );
			fclose( fps );
			svar.total_byte += (i + 32);
		}
		else
		{
		/* 差分 */
			int i;
			i = 0;				/* data counter */
			WORD( bbuf+i ) = 2;
			if( svar.premove )
				WORD( bbuf+i ) += 1;
			i+=2;
			if( svar.premove )
			{
				i = mov_save_movebox16( i );
					/* clearしてやり直す */
				for( y=svar.y0 ; y<=svar.y1 ; y++ ){
					for( x=svar.x0 ; x<=svar.x1 ; x++ ){
						// j = (y << 10) + (x << 1);	/* vram */
						int k;
						int a1,a2;
						k = y*640 + x*2;		/* rbuf */
						a1 = WORD(pImageBuf+k);
						a2 = WORD(rbuf     +k);
						if( a1 & 0x8000 )
							WORD(pImageBuf + k) ^= 0xffff;
						if( a2 & 0x8000 )
							WORD( rbuf+k ) = a2 & 0x7fff;
					}
				}
			}
		  /* フィルター */
			//	if( svar[11] )mov_save_filter2( filtAdjust[ svar[11] ], svar, rbuf );
				/* check */
			for( y=svar.y0 ; y<=svar.y1 ; y++ ){
				for( x=svar.x0 ; x<=svar.x1 ; x++ ){
					int k,a1,a2;
					// j = (y << 10) + (x << 1);	/* vram */
					k = y*640 + x*2;		/* rbuf */
					a1 = WORD(pImageBuf+k);
					a2 = WORD( rbuf+k );
					if( mov_save_cmp_data( a1, a2, svar.rough ) == 0 
					 && (a2 & 0x8000) == 0 ){
						WORD( rbuf+k ) = a2 | 0x8000;	/* 処理印 */
						WORD(pImageBuf+k) ^= 0xffff;
					}
				}
			}
					/* 孤立した点は処理解除 */
			int k;
			for( k=2 ; k<153598 ; k+=2 ){
				if( (DWORD( rbuf+k ) & 0x80008000) == 0x8000
				&& (WORD( rbuf+k-2 ) & 0x8000) == 0 ){
					WORD( rbuf+k ) &= 0x7fff;
					// j = ( (k/640) << 10 ) + ( k % 640 );
					WORD(pImageBuf+k) ^= 0xffff;
				}
			}

			i = mov_save_stosw1( i );		/* stosw */

					/* 再びくっついてる点は復活 */
			for( k=2 ; k<153598 ; k+=2 ){
				if( (DWORD( rbuf+k ) & 0x80008000) == 0x80000000
				|| (DWORD( rbuf+k-2 ) & 0x80008000) == 0x8000 ){
					// j = ( (k/640) << 10 ) + ( k % 640 );
					int a1,a2;
					a1 = WORD(pImageBuf+k);
					a2 = WORD( rbuf+k );
					if( mov_save_cmp_data( a1, a2, svar.rough ) == 0 
					    && (a2 & 0x8000) == 0 ){
						WORD( rbuf+k ) = a2 | 0x8000;	/* 処理印 */
						WORD(pImageBuf+k) ^= 0xffff;
					}
				}
			}

			i = mov_save_movsw( i );		/* movsw */
			pghead[0] = svar.frame;		/* page page head */
			pghead[1] = i;			/* dsize */
			pghead[2] = 0;			/* ox,oy */
			pghead[3] = 0;			/* wait & loop */
			pghead[4] = 0;			/* リザーブ */
			pghead[5] = 0;			/* sound data1 */
			pghead[6] = 0;			/* sound data2 */
			pghead[7] = 0;			/* sound data3 */
			if( ( fps = fopen( pszMovName, "r+b" ) ) == NULL )
				return 2;
			fread( hread, 256, 1, fps );
			fseek( fps, 256+DWORD(hread+8), SEEK_SET );
			fwrite( (char *)pghead, 32, 1, fps );
			fwrite( bbuf, i, 1, fps );
			temp = ferror( fps );
			fclose( fps );
			svar.total_byte += i + 32;
		}

		svar.frame++;
		if( temp ){
			/*	remove( namemov );	disk full */
			return 2;
		}
		return mov_save_end( pszMovName );
	}

/*--------------------------------------------------------*/
/*          MOVヘッダの読み込み(追加保存のため)           */
/*--------------------------------------------------------*/

	int mov_head_read(char *pszMovName)
	{
		FILE *fp;
		int n, temp, page, size;
		char head[256];

		if ((fp = fopen( pszMovName, "rb")) == NULL)
			return 1;
		temp = fread( head, 256, 1, fp );
		if (temp < 1)
			goto movh10;
	  // MOVヘッダであるかどうか確認
		if (DWORD(head+0 ) != 0x32564f4d )
			goto movh10;
		if (DWORD(head+4 ) != 16 )
			goto movh10;
		if (DWORD(head+16) != 320 )
			goto movh10;
		if (DWORD(head+20) != 240 )
			goto movh10;
		svar.frame      = DWORD( head+12 );
		svar.total_byte = DWORD( head+ 8 );
		svar.x0 = WORD( head + 32 );			/* 録画領域 */
		svar.y0 = WORD( head + 34 );
		svar.x1 = WORD( head + 36 );
		svar.y1 = WORD( head + 38 );
		fclose( fp );
		return 0;
	movh10:
		fclose( fp );
		return 58;
	}

/*--------------------------------------------------------*/
/*                   MOVファイルの作成                    */
/*--------------------------------------------------------*/

	int mov_save( char *pszMovName, int nTotalFrame,
				   int (*pfnLoadFrame)(char*,int,int*,int*),
				   int (*pfnCheckFrame)(int),
				   MOVPARA *para)
	// 返り値:-2 メモリ不足
	{
		int err;
		err = 0;
	  // 画像読み込みバッファを確保
		if ((pImageBuf = MALLOC(320*240*2)) == NULL)
			return -2;
		if ((rbuf = MALLOC(153600)) == NULL )
		{
			FREE(pImageBuf);
			return -2;
		}
		if ((bbuf = MALLOC(524288)) == NULL )
		{
			FREE(rbuf);
			FREE(pImageBuf);
			return -2;
		}
	  // パラメータ設定
		if (!para->add)
		{
			svar.frame = 0 ;
			svar.total_byte = 0 ;
		}
		else
		{
			if ((err = mov_head_read(pszMovName)) != 0)
				goto END;
		}
		svar.rough = para->rough;
		svar.premove = (para->premove ? 1 : 0) * 8 ;
		svar.soft = para->soft;
		svar.wait = para->wait;

	  // 新規作成の場合、フレームの縦横ドット数を得る
		if ( nTotalFrame > 0 && svar.frame == 0 )
		{
			svar.x0 = 0 ;
			svar.y0 = 0 ;
			svar.x1 = 319;
			svar.y1 = 239;
			int ret = (*pfnLoadFrame)( pImageBuf, 0, &nFrameWid, &nFrameHt );
			if (ret == 0)
			{
				svar.x1 = _min(319, nFrameWid - 1);
				svar.y1 = _min(239, nFrameHt - 1);
			}
		}
	  // 各フレームについて圧縮保存
		int iFrame;
		for (iFrame = 0; iFrame < nTotalFrame; iFrame++)
		{
			if (pfnCheckFrame != 0)
				(*pfnCheckFrame)(iFrame);
			int ret;
			ret = (*pfnLoadFrame)(pImageBuf,iFrame,NULL,NULL);
			if (ret == 0) {
				if ((err = mov_save_page(pszMovName)) != 0)
					goto END;
				// memcpy(pImageBuf, rbuf, 320*240*2);
					// フィルタ処理後の画像を画面に表示??
			}
			// マウスのクリック（中止）のチェックをここでする
		}
	  END:
		FREE(bbuf);
		FREE(rbuf);
		FREE(pImageBuf);
		return err;
	}
