/*
	Free Cell for WonderSwan
		H.Iwasaki
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/bios.h>
#include <sys/process.h>
#include <fcntl.h>
#include "hima.h"
#include "fcell.h"

/* ゲーム用変数 */
static char	cards[52], free[4], home[4], field[8][20];
static int 	freebuf, freefield, rest;
static int 	cursor, sel_cur;
static u_short	undo, recBuf[UNDO_BUF], recP;

void fcell_main( void )
{
	static char	s[10];
	int 	i, ret, loop;

	/* card set */
	for( i = 0; i < 52; i++ )
		cards[i] = (char)i;
	shuffl();
	do {
		init_game();
		dispScreen();
		ret = play_game();
		if( ret == 0 ) {
			win[3] += !draw[3] && !undo;
			screen_set_scroll(SCREEN1, 0, 0);
			putSprite33(0, 224, 0);
			msg(5,9,"Congratulations!!",0,0,CHAR_STR);
			sprintf(s,"%3d step",recP);
			msg(10,12,s,0,0,502);
			play_game_clear();
			sys_wait(150);
			screenFadeOut();
			screen_fill_char(SCREEN1, 5, 9, 17, 4, CHAR_SPACE);
			screenFadeIn();
			loop = 1;
			while( loop ) {
				static char *list[]
					= { "SAVE Opr", " REPLAY " };
				sprintf(s,"%3d step",recP);
				msg(10,12,s,0,0,502);
				switch( exitMenu(9, 7 + scroll_y / 8, 10, 4, list, 2, 0) ) {
					case 0:	step_write();	break;
					case 1:	back(1); putSprite33(0, 224, 0); break;
					case 2:	loop = 0;
				}
			}
		}
		else if( ret != -1 )
			lose[3] += !draw[3];
	} while( ret == -1 );
}

static void init_game_screen( void )
{
	int 	i, j, k;

	lcd_off();	/* ディスプレイを消す */

	/* FONT LOAD */
	font_set_colordata(CHAR_CARDS, cards_width * cards_height, bmp_cards);
	font_set_colordata(CHAR_WAKU, 9, waku);

	/* card character set */
	/* screen_set_char(SCREEN1, x, y, 3, 3, card_char[0-3][0-12]); */
	for (i = 0; i < 4; i++)
		for( j = 0; j < 13; j++ )
			for( k = 0; k < 9; k++ )
				card_char[i][j][k] = (0 << CFSFT_PALETTE)
						| (i * 13 * 3 * 3 + j * 3 + k + k / 3 * 36);
	/* clear screen */
	screen_fill_char(SCREEN1, 0, 0, 28, 30, CHAR_SPACE);

	screen_set_scroll(SCREEN1, 0, 0);

	for( i = 0; i < 9; i++ ) {
		sprite_set_char(i, ((12 - 8) << CFSFT_PALETTE) | (CHAR_WAKU + i));
		cell_char[i] = (2 << CFSFT_PALETTE) | (i + i / 3 * 36);
	}

	sprite_set_range(0,9);			/* スプライト表示ON */

	display_control(DCM_SCR1 | DCM_SPR);	/* ディスプレイ設定 */

	lcd_on();	/* ディスプレイを点ける */
}

/**********************************************
 *　ゲームメインループ
 * 　戻り値 　　-1　:リスタート
 * 　　　　 それ以外:残りカード枚数
 **********************************************/
static int play_game( void )
{
	static int	n;
	static char	card;
	int		key;

	while( rest != 0 ) {
		key = key_wait();
		if( key & KEY_LEFT1 ) {
			cursor = cursor / 8 * 8 + (cursor + 7) % 8;
			play_key_push();
		}
		else if( key & KEY_RIGHT1 ) {
			cursor = cursor / 8 * 8 + (cursor + 1) % 8;
			play_key_push();
		}
		else if( key & KEY_UP1 ) {
			cursor += (cursor < 8) * 8;
			play_key_push();
		}
		else if( key & KEY_DOWN1 ) {
			cursor -= cursor / 8 * 8;
			play_key_push();
		}
		else if( key & KEY_B )
			sel_cur = -1;
		else if( (key & KEY_Y2) && cursor < 8 ) {
			sel_cur = -1;
			dispHydeCard(cursor);
		}
		else if( (key & KEY_Y4) && (cursor < 8 || sel_cur != -1) ) {	/* カードサーチ */
			if( sel_cur != -1 ) {
				cardSearch2();
			}
			else {
				cardSearch();
				sel_cur = -1;
			}
		}
		else if( key == KEY_START ) {
			static char *list[]
				= { " UNDO  ", "RESTART", "GIVE UP", " YIELD " };	/* 28文字 */
			if( recP > 0 && recP < UNDO_BUF )
				switch( exitMenu(9, 7 + scroll_y / 8, 9, 6, list, 4, 0) ) {
					case 0:	back(0); dispScreen(); break;
					case 1:	return -1;
					case 2:	return rest;
					case 3:	procIL->_yield();
					case 4: ;
				}
			else
				switch( exitMenu(9, 7 + scroll_y / 8, 9, 4, list + 2, 2, 0) ) {
					case 0:	return rest;
					case 1:	procIL->_yield();
					case 2: ;
				}
		}
		else if( key & (KEY_Y1 | KEY_Y3) ) {	/* スクロール */
			if( key & KEY_Y1 )
				scroll_y = 0;
			else if( scroll_y < 72 )
				scroll_y += 4;
			screen_set_scroll(SCREEN1, 0, scroll_y);
		}
		else if( key & KEY_A ) {
			if( sel_cur == -1 ) {	/* カードが選択されてない */
				if( cursor < 8 ) {
					select(cursor,&n,&card);
					if( card != -1 )
						sel_cur = cursor;
				}
				else if( cursor < 12 && free[cursor - 8] != -1 )
					sel_cur = cursor;
			}
			else {		/* されてる */
				play_move();
				sel_cur = -1;
			}
		}
		automove(1);
	}
	return rest;
}

/**********************************************
 *　カード(データのみ)を移動させる
 *
 **********************************************/
static void play_move( void )
{
	static char	card, card2, s[81];
	static int	n, n2;
	int 		i, j, mai, key;

	if( sel_cur < 8 )
		select(sel_cur,&n,&card);
	else
		card = free[sel_cur - 8];

	if( cursor == sel_cur && cursor < 8 ) {		/* Aボタン２度押し */
		for( i = 0; i < 4 && free[i] != -1; i++ )
			;
		if( i < 4 ) {
			cardup(cursor,i);
			if( recP < UNDO_BUF )
				recBuf[recP++] = CMDENC(CUP, cursor, i + 8);
		}
	}
	else if( cursor < 8 ) {		/* フィールドへ */
		select(cursor,&n2,&card2);
		if( sel_cur > 7 && sel_cur < 12 ) {	/* フリーセルから */
			if( n2 == 0 || putok(card,card2) ) {
				carddown(sel_cur - 8,cursor);
				if( recP < UNDO_BUF )
					recBuf[recP++] = CMDENC(CDOWN, sel_cur, cursor);
			}
		}
		else {		/* フィールドから */
			for( i = 0, mai = freebuf + 1; i < freefield; i++ )	/* まとめて移動可能な枚数 */
				mai *= 2;
			for( i = n - 1; i > 0 && !numok(field[sel_cur][i],card2); i-- )	/* 連続してる枚数(n-iに) */
				if( !putok(field[sel_cur][i],field[sel_cur][i-1]) )
					break;
			if( n2 == 0 ) {
				mai /= 2;
				if( n - i > 1 && putok(card,field[sel_cur][n - 2]) ) {
					dispWaku(6, 7 + scroll_y / 8, 15, 4, 0);
					putSprite33( 0, 224, 0 );
					j = msg(7,8 + scroll_y / 8,"Move column ?", 0, 0, CHAR_STR);
					j = msg(8,9 + scroll_y / 8,"(A:yes/else)", 0, 0, j);	/* 25文字 */
					play_disp_msg();
					key = key_wait();
					dispScreen();
					if( key & KEY_A )
						if( mai < n - i ) {
							movecol(sel_cur,cursor,mai,1);
							if( recP < UNDO_BUF )
								recBuf[recP++] = ((u_short)mai << 12)
								| CMDENC(MOVC, sel_cur, cursor);
						}
						else {
							movecol(sel_cur,cursor,n - i,1);
							if( recP < UNDO_BUF )
								recBuf[recP++] = ((u_short)(n - i) << 12)
								| CMDENC(MOVC, sel_cur, cursor);
						}
					else {
						cardmove(sel_cur,cursor);
						if( recP < UNDO_BUF )
							recBuf[recP++] = CMDENC(CMOV, sel_cur, cursor);
					}
				}
				else {
					cardmove(sel_cur,cursor);
					if( recP < UNDO_BUF )
						recBuf[recP++] = CMDENC(CMOV, sel_cur, cursor);
				}
			}
			else {
				if( putok(field[sel_cur][i],card2) )
					if( n - i > mai ) {
						dispWaku(3, 7 + scroll_y / 8, 23, 4, 0);
						putSprite33( 0, 224, 0 );
						j = msg(4,8 + scroll_y / 8,"Sorry.", 0, 0, CHAR_STR);
						sprintf(s,"You can move %d card%s"
							,mai,(mai == 1) ? "." : "s.");
						j = msg(4,9 + scroll_y / 8,s, 0, 0, j);	/* 27文字 */
						play_disp_msg();
						key_wait();
						dispScreen();
					}
					else {
						movecol(sel_cur,cursor,n - i,1);
						if( recP < UNDO_BUF )
							recBuf[recP++] = ((u_short)(n - i) << 12)
							| CMDENC(MOVC, sel_cur, cursor);
					}
			}
		}
	}
	else if( cursor > 7 && cursor < 12 ) {		/* フリーセルへ */
		if( free[cursor - 8] == -1 )
			if( sel_cur < 8 ) {
				cardup(sel_cur,cursor - 8);
				if( recP < UNDO_BUF )
					recBuf[recP++] = CMDENC(CUP, sel_cur, cursor);
			}
			else {
				free[cursor - 8] = free[sel_cur - 8];
				free[sel_cur - 8] = -1;
				if( recP < UNDO_BUF )
					recBuf[recP++] = CMDENC(FTOF, sel_cur, cursor);
			}
	}
	else {		/* ホームセルへ */
		if( putdok(home[cursor - 12],card) )
			if( sel_cur < 8 ) {
				field2home(sel_cur);
				if( recP < UNDO_BUF )
					recBuf[recP++] = CMDENC(FDTOH, sel_cur, cursor);
			}				
			else {
				free2home(sel_cur - 8);
				if( recP < UNDO_BUF )
					recBuf[recP++] = CMDENC(FRTOH, sel_cur, cursor);
			}
	}
}

/**********************************************
 *　カーソル(枠)の表示
 *
 **********************************************/
static void dispCursor( void )
{
	static int 	n;
	static char	card;

	if( cursor > 7 ) {
		if( cursor < 12 )
			putSprite33( 0, ((cursor - 8) * 3 + 1) * 8, 1 * 8 );
		else
			putSprite33( 0, ((cursor - 8) * 3 + 3) * 8, 1 * 8 );
	}
	else {
		select(cursor, &n, &card);
		if( n == 0 )
			n = 1;
		putSprite33( 0, (cursor * 3 + 2) * 8, (n - 1 + 5) * 8 );
	}
}

/**********************************************
 *　セレクトマーカを表示
 *
 **********************************************/
static void dispMark( void )
{
	static int	n;
	static char	card;

	if( sel_cur > 7 )
		screen_set_char(SCREEN1, (sel_cur - 8) * 3 + 1, 1, 3, 3,
				revCardFont( free[sel_cur - 8] ));
	else {
		select(sel_cur, &n, &card);
		if( card == -1 )
			return;
		screen_set_char(SCREEN1, sel_cur * 3 + 2, n - 1 + 5, 3, 3,
				revCardFont( card ));
	}
}

/**********************************************
 * 指定されたカードの階調を反転
 *　戻り値:反転させたフォント
 **********************************************/
static u_short far *revCardFont( char card )
{
	static u_short	buf[9];
	u_short		*p;
	int 		i;

	p = card_char[card & 3][card / 4];
	for( i = 0; i < 9; i++ )
		buf[i] = *(p + i) | (1 << CFSFT_PALETTE);
	return buf;
}

/**********************************************
 *　画面表示
 *
 **********************************************/
static void dispScreen( void )
{
	static char	s[3];

	sys_wait(1);
	screen_fill_char(SCREEN1, 0, 0, 28, 30, CHAR_SPACE);

	/* カードを表示 */
	dispField();

	/* セレクトマーカの表示 */
	if( sel_cur != -1 )
		dispMark();

	/* カーソル(枠)の表示 */
	dispCursor();

	/* 残り枚数を表示 */
	sprintf(s,"%2d",rest);
	msg(13,2,s,0,0,510);
}

/**********************************************
 *　画面上の全カードを表示
 *
 **********************************************/
static void dispField( void )
{
	int 		i;

	/* 最上段のカード */
	for( i = 0; i < 4; i++ ) {
		if( free[i] != -1 )
			screen_set_char(SCREEN1, i * 3 + 1, 1, 3, 3,
				card_char[ free[i] & 3 ][ free[i] / 4 ]);
		else
			screen_set_char(SCREEN1, i * 3 + 1, 1, 3, 3, cell_char);
		if( home[i] != -1 )
			screen_set_char(SCREEN1, i * 3 + 15, 1, 3, 3,
				card_char[ home[i] & 3 ][ home[i] / 4 ]);
		else
			screen_set_char(SCREEN1, i * 3 + 15, 1, 3, 3, cell_char);
	}
	/* 各列のカード */
	for( i = 0; i < 8; i++ )
		dispCol( i );
}

/**********************************************
 *　指定された列のカードを表示
 *
 **********************************************/
static void dispCol( int col )
{
	int 	i;

	for( i = 0; i < 20; i++ )
		if( field[col][i] != -1 )
			screen_set_char(SCREEN1, col * 3 + 2, i + 5, 3, 3,
				card_char[ field[col][i] & 3 ][ field[col][i] / 4 ]);
		else
			break;
}


/**********************************************
 *　隠れているカードを表示する
 *
 **********************************************/
static void dispHydeCard( int col )
{
	int 		i;

	putSprite33( 0, 224, 0 );
	for( i = 19; i >= 0 && field[col][i] == -1; i-- )
		;
	for( i--; i >= 0; i-- ) {
		screen_set_char(SCREEN1, col * 3 + 2, i + 5, 3, 3,
			card_char[ field[col][i] & 3 ][ field[col][i] / 4 ]);
		if( key_wait() != KEY_Y2 )
			break;
	}
	dispCol(col);
}

/**********************************************
 *　カーソル位置のカードの上に重ねられるカードを
 *　　反転表示する
 **********************************************/
static void cardSearch( void )
{
	static int	n;
	static char	card;
	int	 	i, j;

	select(cursor, &n, &card);
	if( card == -1 )
		return;
	for( i = 0; i < 4; i++ )
		if( putok(free[i], card) )
			screen_set_char(SCREEN1, i * 3 + 1, 1, 3, 3,
				revCardFont(free[i]));
	for( i = 0; i < 8; i++ )
		for( j = 0; j < 20; j++ ) {
			if( field[i][j] == -1 )
				break;
			if( putok(field[i][j], card) )
				screen_set_char(SCREEN1, i * 3 + 2, j + 5, 3, 3,
					revCardFont(field[i][j]));
		}
	key_wait();
}

/**********************************************
 *　選択されたカードを重ねられるカードを
 *　　反転表示する
 **********************************************/
static void cardSearch2( void )
{
	static int	n;
	static char	card;
	int	 	i, j;

	if( sel_cur > 7 )
		card = free[sel_cur - 8];
	else {
		select(sel_cur, &n, &card);
		for( i = n - 1; i > 0; i-- )
			if( !putok(field[sel_cur][i],field[sel_cur][i - 1]) )
				break;
		card = field[sel_cur][i];
	}
	for( i = 0; i < 4; i++ )
		if( putok(card, free[i]) )
			screen_set_char(SCREEN1, i * 3 + 1, 1, 3, 3,
				revCardFont(free[i]));
	for( i = 0; i < 8; i++ )
		for( j = 0; j < 20; j++ ) {
			if( field[i][j] == -1 )
				break;
			if( putok(card, field[i][j]) )
				screen_set_char(SCREEN1, i * 3 + 2, j + 5, 3, 3,
					revCardFont(field[i][j]));
		}
	key_wait();
}

/**********************************************
 *　ゲームの初期化
 *
 **********************************************/
static void init_game( void )
{
	int	i, j;

	freebuf = 4;
	freefield = 0;
	rest = 52;
	cursor = 0;
	sel_cur = -1;
	scroll_y = 0;
	undo = recP = 0;
	/* フィールド初期化 */
	for( i = 0; i < 8; i++ )
		for( j = 0; j < 20; j++ )
			field[i][j] = -1;
	for( i = 0; i < 4; i++ )
		free[i] = home[i] = -1;
	if( draw[3] )
		card_read();
	/* カードをフィールドに配置 */
	for( i = 0; i < 52; i++ )
		field[i % 8][i / 8] = cards[i];
	init_game_screen();
}

/**********************************************
 *　カードをシャッフル
 *
 **********************************************/
static void shuffl( void )
{
	int	i, j;
	char	wk;

	for( i = 0; i < 200; i++ ) {
		j = rand() % 52;
		wk = cards[0];
		cards[0] = cards[j];
		cards[j] = wk;
	}
}

/**********************************************
 *　指定された列の一番下のカードを取り出す
 *　 nにその列のカードの枚数が設定される
 **********************************************/
static void select( int col, int *n, char *card )
{
	*n = 0;
	while( field[col][(*n)++] != -1 )
		;
	(*n)--;
	if( *n > 0 )
		*card = field[col][*n - 1];
	else
		*card = -1;
}

/**********************************************
 *　指定された列からフリーセルへカードを移動
 *
 **********************************************/
static int cardup( int col, int idx )
{
	static int 	n;
	static char	card;

	if( idx == -1 )
		for( idx = 0; idx < 4; idx++ )
			if( free[idx] == -1 )
				break;
	freebuf--;
	select( col, &n, &card );
	free[idx] = card;
	if( n == 1 )
		freefield++;
	field[col][n - 1] = -1;
	play_card_put();
	return idx;
}

/**********************************************
 *　フリーセルから指定された列へカードを移動
 *
 **********************************************/
static void carddown( int idx, int col )
{
	static int 	n;
	static char	card;

	freebuf++;
	select(col,&n,&card);
	field[col][n] = free[idx];
	free[idx] = -1;
	if( n == 0 )
		freefield--;
	play_card_put();
}

/**********************************************
 *　フリーセルからホームセルへカードを移動
 *
 **********************************************/
static void free2home( int n )
{
	home[free[n] & 3] = (char)free[n];
	free[n] = -1;
	freebuf++;
	rest--;
	play_card_put();
}

/**********************************************
 *　指定された列からホームセルへカードを移動
 *
 **********************************************/
static void field2home( int col )
{
	static int 	n;
	static char	card;

	select(col,&n,&card);
	home[card & 3] = (char)card;
	if( n == 1 )
		freefield++;
	field[col][n - 1] = -1;
	rest--;
	play_card_put();
}

/**********************************************
 *　列から列へカードを移動
 *
 **********************************************/
static void cardmove( int from, int to )
{
	static int 	n, n2;
	static char	card;

	select(to,&n2,&card);
	select(from,&n,&card);
	field[from][n - 1] = -1;
	field[to][n2] = card;
	if( n == 1 )
		freefield++;
	if( n2 == 0 )
		freefield--;
	play_card_put();
}

/**********************************************
 *　列から列へカードを複数枚移動
 *
 **********************************************/
static void movecol( int from, int to, int mai, int disp )
{
	static int	n;
	static char	card;
	int 		i, j, n2, bak[4], bak2[4];

	if( mai <= freebuf + 1 ) {
		for( i = 0; i < mai - 1; i++ ) {
			bak[i] = cardup(from, -1);
			if( disp )
				dispScreen();
		}
		cardmove(from,to);
		if( disp )
			dispScreen();
		for( i = mai - 2; i >= 0; i-- ) {
			carddown(bak[i],to);
			if( disp )
				dispScreen();
		}
	}
	else {
		bak[0] = freebuf + 1;
		for( n2 = 0; bak[n2] < mai; n2++ )
			bak[n2 + 1] = bak[n2] * 2;
		for( i = 0, j = n2 - 1; j >= 0; j-- ) {
			for( ; i < 8; i++ ) {
				if( i == from || i == to )
					continue;
				select(i,&n,&card);
				if( n != 0 )
					continue;
				movecol(from,i,bak[j],disp);
				bak2[j] = i;
				mai -= bak[j];
				break;
			}
			if( mai <= freebuf + 1 )
				break;
		}
		movecol(from,to,mai,disp);
		for( ; j < n2; j++ )
			movecol(bak2[j],to,bak[j],disp);
	}
}

/**********************************************
 *　自動でホームセルに移動させる
 *
 **********************************************/
static void automove( int disp )
{
	static int	n;
	static char	card;
	int	 	i, sw;
	char		min = 13;

	do {
		sw = 0;
		for( i = 0; i < 4; i++ ) {
			if( home[i] == -1 ) {
				min = -1;
				break;
			}
			if( min > home[i] >> 2 )
				min = home[i] >> 2;
		}

		min += 3;
		for( i = 0; i < 4; i++ )
			if( !(free[i] >> 2) || (free[i] >> 2) < min
					&& putdok(home[free[i] & 3],free[i]) ) {
				free2home(i);
				if( disp )
					dispScreen();
				sw = 1;
			}
		for( i = 0; i < 8; i++ ) {
			select(i,&n,&card);
			if( !(card >> 2) || (card >> 2) < min
					&& putdok(home[card & 3],card) ) {
				field2home(i);
				if( disp )
					dispScreen();
				sw = 1;
			}
		}
	} while( sw != 0 );
	if( disp )
		dispScreen();
}

/**********************************************
 *　カードの配置をエディット
 *
 **********************************************/
void edit( void )
{
	int 	i, key, x = 0, y = 0, sel_x = -1, sel_y = -1;
	char	wk;

	for( i = 0; i < 52; i++ )
		cards[i] = (char)i;
	init_game();

	screen_fill_char(SCREEN1, 0, 0, 28, 18, CHAR_SPACE);

	while( 1 ) {
		msg(9, 2, "EDIT MODE", 1, 0, 512 - 9);
		for( i = 0; i < 8; i++ )	/* 各列のカード */
			dispCol( i );
		if( sel_x != -1 )		/* セレクトマーカの表示 */
			screen_set_char(SCREEN1, sel_x * 3 + 2, sel_y + 5, 3, 3,
				revCardFont( field[sel_x][sel_y] ));
		putSprite33(0, x * 3 * 8 + 16, y * 8 + 40);		/* カーソル(枠)の表示 */

		key = key_wait();
		x += (key == KEY_RIGHT1) + (key == KEY_LEFT1) * 7;
		x %= 8;
		y += (key == KEY_DOWN1) + (key == KEY_UP1) * 6;
		y %= 7;
		y -= ((key == KEY_DOWN1) && (y == 6) && (x > 3)) * 6;
		y -= (y == 6) && (x > 3);
		if( key == KEY_B )
			sel_x = -1;
		if( key == KEY_A ) {
			if( sel_x == -1 ) {
				sel_x = x;
				sel_y = y;
			}
			else {
				wk = field[x][y];
				field[x][y] = field[sel_x][sel_y];
				field[sel_x][sel_y] = wk;
				sel_x = -1;
			}
		}
		if( key == KEY_START ) {
			static char *list[]
				= { " SAVE ", " EXIT ", "YIELD " };	/* 18文字 */
			switch( exitMenu(10, 7, 8, 5, list, 3, 0) ) {
				case 0:	for( i = 0; i < 52; i++ )
						cards[i] = field[i % 8][i / 8];
					card_write();
					break;
				case 1:	return;
				case 2:	procIL->_yield();
				case 3:	;
			}
		}
	}
}

static void card_read( void )
{
	int 		fd;
	static char	path[22];

	sprintf(path, "%s/%s", DATA_DIR, fnam[draw[3]]);
	if( (fd = open(path, O_RDONLY, 0)) < 0 )
		rw_error(path, "file read open error.");
	else {
		if( read(fd, cards, sizeof(cards)) != sizeof(cards) )
			rw_error(path, "file read error.");
		close(fd);
	}
}

static void card_write( void )
{
	int 		fd;
	static char	path[22];
	static char	buf[6];
	static char	info[24];

	if( makeName(8, 7, buf) == -1 )
		return;
	sprintf(path, "%s/%s", DATA_DIR, buf);
	if( (fd = open(path, O_RDONLY, 0)) < 0 ) {
		if( creat(path, FMODE_R | FMODE_W, 1) != E_FS_SUCCESS ) {
			rw_error(path, "file create error.");
			return;
		}
		sprintf(info, "%s%s", DATA_INFO, buf);
		fs_setinfo(path, info);
	}
	else
		close(fd);
	if( (fd = open(path, O_WRONLY, 0)) < 0 )
		rw_error(path, "file write open error.");
	else {
		lseek(fd,0,0);
		if( write(fd, cards, sizeof(cards)) != sizeof(cards) )
			rw_error(path, "file write error.");
		close(fd);
		screen_fill_char(SCREEN1, 9, 8, 9, 3, CHAR_SPACE);
		msg(9, 9, "FINISHED!",0,0,CHAR_STR);
		draw[3] = fnam_read(fnam);
		for( ; draw[3] > 0; draw[3]-- )
			if( strcmp(buf, fnam[draw[3]]) == 0 )
				break;
		key_wait();
	}
}

static int makeName( int x, int y, char *name )
{
	int 	i, n, key, pos = 0;
	static char csr[2];

	csr[1] = '\0';
	for( i = 0; i < 8; i++ )	/* 各列のカード */
		dispCol( i );
	dispWaku(x, y, 11, 5, 0);
	if( draw[3] )
		strcpy(name, fnam[draw[3]] );
	else
		strcpy(name,"00000");
	while( 1 ) {
		n = msg(x + 1, y + 1, "FILE NAME", 0, 0, CHAR_STR);
		n = msg(x + 3, y + 3, name, 0, 0, n);
		csr[0] = name[pos];
		n = msg(x + 3 + pos, y + 3, csr, 1, 0, n);
		key = key_wait();
		pos += (key == KEY_RIGHT1) + (key == KEY_LEFT1) * 4;
		pos %= 5;
		switch( key ) {
			case KEY_A:
				strcpy(fnam[1], name);
				draw[3] = 1;
				return 0;
			case KEY_B:
				return -1;
			case KEY_UP1:
				if( ++name[pos] > '9' )
					name[pos] = '0';
				break;
			case KEY_DOWN1:
				if( --name[pos] < '0' )
					name[pos] = '9';
		}
	}		
}

/*
static void movecol2( int from, int to, int mai )
{
	static int	n, n2;
	static char	card;
	int		i;

	select(from, &n, &card);
	select(to, &n2, &card);
	for( i = 0; i < mai; i++ ) {
		field[to][n2 + i] = field[from][n - mai];
		field[from][n - mai] = -1;
	}
}
*/

static void back( int replay )
{
	int 		i, j;
	static char	s[17];

	dispScreen();
	if( !replay ) {
		undo = 1;
		recP--;
	}
	freebuf = 4;
	freefield = 0;
	rest = 52;
	sel_cur = -1;
	for( i = 0; i < 8; i++ )
		for( j = 0; j < 20; j++ )
			field[i][j] = -1;
	for( i = 0; i < 4; i++ )
		free[i] = home[i] = -1;
	if( draw[3] )
		card_read();
	for( i = 0; i < 52; i++ )
		field[i % 8][i / 8] = cards[i];
	automove(0);
	for( i = 0; i < recP; i++ ) {
		if( replay ) {
			sel_cur = ARG1(recBuf[i]);
			cursor = ARG2(recBuf[i]);
			dispScreen();
			sprintf(s, "REPLAY  %3d step", i + 1);
			msg(1, 4, s, 1, 0, CHAR_STR);
			if( key_wait() & KEY_START )
				break;
		}
		switch( CMD(recBuf[i]) ) {
			case CUP:
				cardup( ARG1(recBuf[i]), ARG2(recBuf[i]) - 8 );
				break;
			case CDOWN:
				carddown( ARG1(recBuf[i]) - 8, ARG2(recBuf[i]) );
				break;
			case CMOV:
				cardmove( ARG1(recBuf[i]), ARG2(recBuf[i]) );
				break;
			case FDTOH:
				field2home( ARG1(recBuf[i]) );
				break;
			case FRTOH:
				free2home( ARG1(recBuf[i]) - 8 );
				break;
			case FTOF:
				free[ ARG2(recBuf[i]) - 8] = free[ ARG1(recBuf[i]) - 8];
				free[ ARG1(recBuf[i]) - 8] = -1;
				break;
			case MOVC:
				if( !replay )
					movecol(ARG1(recBuf[i]), ARG2(recBuf[i]), MAI(recBuf[i]), 0);
				else
					movecol(ARG1(recBuf[i]), ARG2(recBuf[i]), MAI(recBuf[i]), 1);
		}
		if( !replay )
			automove(0);
		else {
			sel_cur = -1;
			automove(1);
			if( rest ) {
				msg(1, 4, s, 0, 0, CHAR_STR);
				if( key_wait() & KEY_START )
					break;
			}
		}
	}
	screen_fill_char(SCREEN1, 1, 4, 17, 1, CHAR_SPACE);
}

static rw_error( char *s1, char *s2 )
{
	int	j;

	screen_fill_char(SCREEN1, 0, 0, 28, 18, CHAR_SPACE);
	j = msg(0,0,s1,0,0,CHAR_STR);
	msg(1,1,s2,0,0,j);
	key_wait();
	screen_fill_char(SCREEN1, 0, 0, 18, 2, CHAR_SPACE);
}	

static void step_write( void )
{
	int 		fd, i, j;
	static int	n;
	static char	card, buf[14], card1[5], card2[5];

	if( (fd = open(STEP_FILE, O_RDONLY, 0)) < 0 ) {
		if( creat(STEP_FILE, FMODE_R | FMODE_W, 11) != E_FS_SUCCESS ) {
			rw_error(STEP_FILE, "file create error.");
			return;
		}
		fs_setinfo(STEP_FILE, STEP_INFO);
	}
	else
		close(fd);
	if( (fd = open(STEP_FILE, O_WRONLY, 0)) < 0 )
		rw_error(STEP_FILE, "file write open error.");
	else {
		lseek(fd, 0, 0);
		freebuf = 4;
		freefield = 0;
		rest = 52;
		sel_cur = -1;
		for( i = 0; i < 8; i++ )
			for( j = 0; j < 20; j++ )
				field[i][j] = -1;
		for( i = 0; i < 4; i++ )
			free[i] = home[i] = -1;
		if( draw[3] )
			card_read();
		for( i = 0; i < 52; i++ )
			field[i % 8][i / 8] = cards[i];
		automove(0);
		for( i = 0; i < recP; i++ ) {
			if( ARG1(recBuf[i]) < 8 )
				select(ARG1(recBuf[i]), &n, &card);
			else
				card = free[ARG1(recBuf[i]) - 8];
			getCard(card, card1);
			if( ARG2(recBuf[i]) < 8 ) {
				select(ARG2(recBuf[i]), &n, &card);
				if( n == 0 )
					strcpy(card2, "空列");
				else
					getCard(card, card2);
			}
			else {
				card = free[ARG2(recBuf[i]) - 8];
				getCard(card, card2);
			}
			switch( CMD(recBuf[i]) ) {
				case CUP:
					sprintf(buf,"%s->UP\n", card1);
					cardup( ARG1(recBuf[i]), ARG2(recBuf[i]) - 8 );
					break;
				case CDOWN:
					sprintf(buf,"%s->%s\n", card1, card2);
					carddown( ARG1(recBuf[i]) - 8, ARG2(recBuf[i]) );
					break;
				case CMOV:
					sprintf(buf,"%s->%s\n", card1, card2);
					cardmove( ARG1(recBuf[i]), ARG2(recBuf[i]) );
					break;
				case FDTOH:
					sprintf(buf,"%s->右UP\n", card1);
					field2home( ARG1(recBuf[i]) );
					break;
				case FRTOH:
					sprintf(buf,"%s->右UP\n", card1);
					free2home( ARG1(recBuf[i]) - 8 );
					break;
				case FTOF:
					sprintf(buf,"%s->UP\n", card1);
					free[ ARG2(recBuf[i]) - 8] = free[ ARG1(recBuf[i]) - 8];
					free[ ARG1(recBuf[i]) - 8] = -1;
					break;
				case MOVC:
					if( MAI(recBuf[i]) > 1 )
						sprintf(buf,"%s*%d->%s\n", card1, MAI(recBuf[i]), card2);
					else
						sprintf(buf,"%s->%s\n", card1, card2);
					movecol(ARG1(recBuf[i]), ARG2(recBuf[i]), MAI(recBuf[i]), 0);
			}
			if( write(fd, buf, strlen(buf)) != strlen(buf) ) {
				rw_error(STEP_FILE, "file write error.");
				break;
			}
			automove(0);
			sprintf(buf,"%3d step",i + 1);
			msg(10,12,buf,0,0,502);

		}
		close(fd);
	}
}

static void getCard( char n, char *buf )
{
	static char sute[] = "SHCD";
	static char rank[] = "JQK";

	buf[0] = sute[n & 3];
	buf[1] = (n >> 2) + 1;
	buf[2] = '\0';
	switch( buf[1] ) {
		case 1:	 buf[1] = 'A';	break;
		case 10: strcpy(buf + 1, "10"); break;
		case 11:
		case 12:
		case 13: buf[1] = rank[buf[1] - 11]; break;
		default: buf[1] += '0';
	}
}
