/*
*	garmin.c for WonderSwan 
*
*	designed by nak@pluto.dti.ne.jp
*
*	2001/01/09	nakamura	UI部分から作成開始
*	2001/01/10				filer追加
*							接続テスト処理から開始(UART割込追加)
*	2001/01/12				timer割込追加
*	2001/01/14				受信処理の開始
*	2001/01/16				送信処理の開始
*	2001/01/18				カシミール3D/GarAtlasで送受信動作(D300)確認OK
*	2001/01/20				eTrexへの対応(D301)
*	2001/01/23				カシミール3D/GarAtlasで送受信動作(D301)確認OK
*	2001/01/24				WWCへの対応は簡易カラー化のみ
*	2001/01/25				beep音 有効/無効選択の追加
*	2001/01/30				ver.1.00 fixed
*
*/

#include <sys/bios.h>
#include <sys/process.h>
#include <sys/fcntl.h>
#include <stdio.h>
#include <string.h>
#include "std.h"
#include "protocol.h"
#include <sys/libwwc.h>

#define USER_FILENAME		"/ram0/garmin.ini"
#define	USER_FILE_INFO		"GARMIN設定ファイル"
#define USER_FILE_BLOCK		1

static char msgbuff[TEXT_SCREEN_WIDTH+1];

#define GAR_MENU_RECV	5
#define GAR_MENU_SEND	6
#define GAR_MENU_CONF	7
#define GAR_MENU_TEST	8
#define GAR_MENU_EXIT	10

enum { SEL_RECV, SEL_SEND, SEL_CONF, SEL_TEST, SEL_EXIT };

static char *lbl_gar_menu[] ={
	"Track受信←GPS",
	"Track送信→PC",
	"Config",
	"GPS接続テスト",
	"EXIT"
};
static BOOL menu_limit = FALSE;	/* メニュー規制 */

#define CONF_MODEL	5
#define CONF_UPDATE	6
#define CONF_TMOUT	7
#define CONF_SOUND	8

static char *lbl_gar_conf[] = {
	"GARMIN機種",
	"更新間隔*未使用",
	"タイムアウト",
	"beep音"
};

enum { SET_MODEL, SET_UPDATE, SET_TMOUT, SET_SOUND };

static char *mmodel[] = { "GPS12","eTrex" };
enum { MODEL_GPS12, MODEL_eTrex };

/* default Product data 2001/01/15 */
static BYTE p_pro[2][8] = {
	{ Pid_Product_Data,0x06,0x57,0x00,0xC6,0x01,0x20,0x00 }, /* GPS12 ID=87  Ver=4.58 */
	{ Pid_Product_Data,0x06,0x82,0x00,0xD2,0x00,0x20,0x00 }	 /* eTrex ID=130 Ver=2.10 */
};

static char *mupdate[] = { "01","05","10","15","30","60" };
enum { UPDATE_01, UPDATE_05, UPDATE_10, UPDATE_15, UPDATE_30, UPDATE_60 };

enum { TMOUT_3s, TMOUT_5s, TMOUT_10s };
static char *mtmout[] = { "03","05","10" };

enum { SOUND_OFF, SOUND_ON };
static char *msound[] = { "OFF","ON " };

struct t_menu{
	int		model;		/* GPS MODEL */
	int		update;		/* 更新間隔(秒) */
	int		tmout;		/* タイムアウト(秒) */
	int		sound;		/* BEEP有効/無効 */
};
static struct t_menu menu ={
	MODEL_eTrex, UPDATE_30, TMOUT_3s, SOUND_ON	/* 初期値：iniファイルから展開する */
};

enum {
	ERRM_NONE,			/* error message */
	ERRM_USER_CANCEL,	
	ERRM_HANDSHAKE_NG,	
	ERRM_TIMEOUT,		
	ERRM_NAK_RECEIVED,	
	ERRM_FILE_OPEN_NG,	
	ERRM_FILE_READ_NG,	
	ERRM_FILE_WRITE_NG,	
	ERRM_FILE_TYPE		
};

static char *errm_msg[] ={
	"                         ",
	"Cancel key pushed.       ",
	"Handshake NG.            ",
	"GPS not respond.(Timeout)",
	"GPS answered NAK.        ",
	"Can't open Logfile.      ",
	"Can't read Logfile.      ",
	"Can't write Logfile.     ",
	"This is not Logfile.     "
};

#define TIMEOUT_OF_SEND	60	/* 送信時タイムアウト設定(秒) */

/* タイマ割り込み */
enum { TIMER_STATE_NONE, TIMER_STATE_WAIT, TIMER_STATE_BUSY };
static int tim_status;
void far timer_callback(void);
void tmout_enable(void);
void tmout_disable(void);
void tmout_on_off(BOOL,int);

int atoi(char far *);
char *itoa(int);

void beep(void);
void init_sound(void);

void create_inifile(void);
BOOL write_inifile(void);
BOOL read_inifile(void);
static char pathbuff[MAXPATHLEN];

int gar_menu(void);
void disp_gar_menu(int, int);
int gar_config(void);
void disp_cf_menu(int);

BOOL send_menu(void);
extern BYTE *_heap;

void gar_send(void);
void gar_recv(void);
void gar_test(void);

int make_p_fix2( BYTE, BYTE, BYTE );
int make_p_nnn( BYTE far * );
void make_p_Trk_Hdr(void);

static BYTE	packet[RXD_BUFF_SIZE];
static BYTE p_1st[RXD_BUFF_SIZE];

extern int rxd_cnt;
extern int rxd_status;
extern int rxd_buff[];
void connect_port( BOOL );

/* Track Log Record */
#define TRK_POINT_SIZE_D300	15
#define TRK_POINT_SIZE_D301	26
static BYTE trackdat[TRK_POINT_SIZE_D301];
typedef struct {
	int model;		/* model */
	int size;		/* track point size */
} trk_type;

/* Trackログファイル判別文字列 */
static const char appmark[4] = { '#','T','R','K' };

static int file_status;
enum{ LOGFILE_CLOSE, LOGFILE_OPEN };

BOOL make_logfile(WORD);
void disp_errmsg(int);
void send_nak(void);
void send_ack(void);

void init_color(void);

/*
* GARMIN GPS TOOL main
*/
void main(void)
{
	int	val;
	text_set_screen(SCREEN2);
	display_control(DCM_SCR2);
	init_color();			/* for WWC */
	init_sound();			/* BEEP */
	create_inifile();
	read_inifile();
	tmout_enable();			/* タイマ割り込み設定 */
	comm_enable();			/* UART割り込み設定 */
	
	while(1){
		switch( val = gar_menu() ){
		case SEL_RECV:
			gar_recv();
			break;
		case SEL_SEND:
			if( send_menu() ) gar_send();
			break;
		case SEL_CONF:
			gar_config();
			break;
		case SEL_TEST:
			gar_test();
			break;
		case SEL_EXIT:
			goto EXIT_APP;
		}
	}
	
EXIT_APP:
	tmout_disable();
	comm_disable();
    if( is_colorswan() ) wwc_set_color_mode(COLOR_MODE_GRAYSCALE);

}

/*
*  GPS接続テスト
*/
void gar_test()
{
	int i,j,k,key;
	int output = ERRM_NONE;
	WORDREG	pro_id, sw_ver;
	
	text_screen_init();
	disp_status("GARMIN GPS TOOL 接続テスト",0);
	disp_status(" B:中断",TEXT_SCREEN_HEIGHT-1); 
	text_put_string(0,3,"Hit any key to start.");
	key = key_wait();
	if(  key == KEY_B || key == KEY_START ){ output = ERRM_USER_CANCEL; goto EXIT_TEST; }
	text_put_string(0,3,"プロダクト情報リクエスト・・・       ");
	
	connect_port( TRUE );
	tmout_on_off( TRUE, atoi(mtmout[menu.tmout]) );	/* タイムアウト計測の開始 */
	comm_send_block( packet, make_p_fix2(Pid_Product_Rqst,0x00,0x00) );	/* Product情報リクエスト */
	
	while(1){
		/* タイムアウト発生またはKey入力で受信を中止する */
		key = key_press_check();
		if( key == KEY_B || key == KEY_START ) output = ERRM_USER_CANCEL;
		if( tim_status == TIMER_STATE_BUSY ) output = ERRM_TIMEOUT;
		if( rxd_status == COMM_STATE_WAIT ){
			/* 受信したパケットのチェックサム判定は行わない */
			/* 受信内容の確定 */
			for(i=0; i<rxd_cnt; i++){ p_1st[i] = (BYTE)rxd_buff[i];	}
			rxd_status = COMM_STATE_READY;
			/* ACKPacketの場合は次にデータが送られてくるのでそのまま待機、
			   それ以外はhandshake待ちになるのでパケット分解に移行する */
			/* ACKパケットはACKを確認するだけで即廃棄 */
			if( p_1st[0] == Pid_Ack_Byte ) continue;
			if( p_1st[0] == Pid_Nak_Byte ) output = ERRM_NAK_RECEIVED;
			if( p_1st[0] == Pid_Product_Data ) break;	/* Complete(0) */
			else output = ERRM_HANDSHAKE_NG;
		}
		if( output != ERRM_NONE ) goto EXIT_TEST;
	}
	
	/*  パケット分解処理 */
EXIT_TEST:
	tmout_on_off( FALSE, 0 );
	if( menu.sound == SOUND_ON ) beep();
	
	if( output != ERRM_NONE ){
		disp_errmsg(output);
		text_put_string(0,7,"Hit any key to return.");
	}else{
		text_put_string(0,5,"Packet(GPS->WS) is ");
		for(i=0,j=0,k=6 ;i<rxd_cnt ;i++){
			text_put_numeric(j,k,2,NUM_HEXA,p_1st[i]);	/* monitor */
			j += 2;	if( j > 27) j=0,++k;
		}
		
		pro_id.bt.al = p_1st[2];
		pro_id.bt.ah = p_1st[3];
		sw_ver.bt.al = p_1st[4];
		sw_ver.bt.ah = p_1st[5];
		
		sprintf(msgbuff,"Product: ID=%u,SW=%u",pro_id.wd, sw_ver.wd);
		text_put_string(0,k+2,msgbuff);
		text_put_string(0,k+3,"Connection is SUCCESS!");
		text_put_string(0,k+5,"Hit any key to return.");
	}
	connect_port( FALSE );
	key_wait();
}

/*
*  TrackLogファイル送信
*/
void gar_send(void)
{
	int i,key;
	int output = ERRM_NONE;
	int sendcnt;
	int rcvseq = 0;
	FILE far *file;
	static WORDREG	record;
	static BYTE type;
	trk_type trk = { MODEL_GPS12, TRK_POINT_SIZE_D300 };	/* D300 */
	
	text_screen_init();
	disp_status("GARMIN GPS TOOL Trk:WS→PC",0);
	disp_status(" B:中断",TEXT_SCREEN_HEIGHT-1);
	
	file_status = LOGFILE_CLOSE;
	if( (file = fopen(pathbuff,"r")) == NULL ){
		output = ERRM_FILE_OPEN_NG;	goto EXIT_SEND;
	}
	file_status = LOGFILE_OPEN;
	menu_limit = TRUE;			/* Track受信選択は禁止とするメニュー規制 */
	
	/* ログファイル先頭4byteでファイルタイプを判別 */
	fread(msgbuff,sizeof(BYTE),4,file);
	for(i=0; i<4; i++){
		if( msgbuff[i] != appmark[i] ){ output = ERRM_FILE_TYPE; goto EXIT_SEND; }
	}
	/* 次の2byteでTrack全データ数を読み込む */
	record.wd = 0;
	fread(&record.wd,sizeof(WORD),1,file);
	if( record.wd < 1 ){								/* record数=0 で中止 */
		output = ERRM_FILE_READ_NG;	goto EXIT_SEND;
	}
	/* 次の1byteで保存形式(D300/D301)を判別 */
	fread(&type,sizeof(BYTE),1,file);
	if( type == Pid_Trk_Hdr ){
		trk.model = MODEL_eTrex;		/* D301 */
		trk.size = TRK_POINT_SIZE_D301;
	}
	sprintf(msgbuff,"送信ファイル %s",pathbuff);
	text_put_string(0,1,msgbuff);
	
	text_put_string(0,3,"Hit any key to start.");
	key = key_wait();
	if(  key == KEY_B || key == KEY_START ){ output = ERRM_USER_CANCEL; goto EXIT_SEND; }
	text_put_string(0,3,"Negotiating・・・        ");
	
	connect_port( TRUE );
	tmout_on_off( TRUE, TIMEOUT_OF_SEND );	 /* タイムアウト計測の開始 */
	
	while(1){
		key = key_press_check();
		if( key == KEY_B || key == KEY_START ) output = ERRM_USER_CANCEL;	/* CANCEL入力(1) */
		if( tim_status == TIMER_STATE_BUSY ) output = ERRM_TIMEOUT;			/* TIMEOUT(3)	 */
		if( output != ERRM_NONE ) goto EXIT_SEND;
		
		if( rxd_status == COMM_STATE_WAIT ){						/* received ? */
			for(i=0; i<rxd_cnt; i++){ p_1st[i] = (BYTE)rxd_buff[i]; }
#ifdef notdef
				int j,k;
				for(i=0,j=0,k=11+m ;i<rxd_cnt ;i++){
					text_put_numeric(j,k,2,NUM_HEXA,p_1st[i]);	/* debug */
					j += 2;	if( j > 27) j=0,++k;
				}
				m++;
				if(m>5) m=0;
#endif
			/* 受信したパケットの分析 */
			/* HOSTからのコマンドに対してそれぞれの応答を行う */
			switch(rcvseq){
			case 0:
				/* コマンド待ち */
				switch( p_1st[0] ){
				case Pid_Product_Rqst:
					/* 送信ファイルのタイプ(D300/D301)によりProduct(GPS12/eTrex)を選択する */
					send_ack();
					comm_send_block( packet, make_p_nnn(p_pro[trk.model]) );
					break;
				case Pid_Command_Data:
					switch( p_1st[2] ){
					case Cmnd_Abort_Transfer:
						send_ack();
						rcvseq = 0;		/* 中断処理 */
						break;
					case Cmnd_Transfer_Trk:
						text_put_string(0,3,"Connected.          ");
						send_ack();
						comm_send_block( packet, make_p_fix2(Pid_Records,record.bt.al,record.bt.ah) );
						sys_wait(20);
						if( fread(trackdat,sizeof(BYTE),trk.size,file) > 0 ){
							comm_send_block( packet, make_p_nnn(trackdat) );
						}else{
							comm_send_block( packet, make_p_fix2(Pid_Xfer_Cmplt,Cmnd_Transfer_Trk,0x00));
							output = ERRM_FILE_READ_NG;	goto EXIT_SEND;	/* recordがreadできない場合は送信完了とする */
						}
						sendcnt = 1;
						rcvseq = 10;
						break;
					default:;	/* 未実装(NAK応答) */ /* あえて応答しないでホストのタイムアウトに従う */
					}
					break;
				default:;	/* 未実装(NAK応答) */
				}
				break;
				
			case 10:	/* TrackLog系処理 */
				if( p_1st[0] == Pid_Ack_Byte ){
					if( sendcnt++ < record.wd && fread(trackdat,sizeof(BYTE),trk.size,file) > 0 ){
						/* ファイルから読み出したログデータを送信する */
						comm_send_block( packet, make_p_nnn(trackdat) );
						sprintf(msgbuff,"Transferd: %u/%u   ",sendcnt,record.wd);
						text_put_string(0,3,msgbuff);
					}else{
						comm_send_block( packet, make_p_fix2(Pid_Xfer_Cmplt,Cmnd_Transfer_Trk,0x00) );
						output = ERRM_NONE;	goto EXIT_SEND;	/* 送信終了 */
					}
				}else{
					;/* 未実装(NAK応答) */ /* あえて応答しないでホストのタイムアウトに従う */
				}
				break;
			default:
				send_nak();	/* 未実装(NAK応答) */
			}
			rxd_status = COMM_STATE_READY;
			tmout_on_off( TRUE, TIMEOUT_OF_SEND );
		}
	}
	
EXIT_SEND:
	tmout_on_off( FALSE,0 );
	if( file_status == LOGFILE_OPEN ) fclose(file);
	if( menu.sound == SOUND_ON ) beep();
	if( output != ERRM_NONE ) disp_errmsg(output);
	else {
		text_put_string(0,4,"TrackLog 送信完了!");
	}
	text_put_string(0,7,"Hit any key to return.");
	connect_port( FALSE );
	key_wait();
}


/*
*  TrackLog 受信
*/
void gar_recv(void)
{
	int i,key;
	int output = ERRM_NONE;
	int rcvcnt;
	FILE far *file;
	static WORDREG	record;
	trk_type trk = { MODEL_GPS12, TRK_POINT_SIZE_D300 };
	BOOL first_p = FALSE;
	
	text_screen_init();
	disp_status("GARMIN GPS TOOL Trk:WS←GPS",0);
	disp_status(" B:中断",TEXT_SCREEN_HEIGHT-1);
	text_put_string(0,3,"Hit any key to start.");
	key = key_wait();
	if(  key == KEY_B || key == KEY_START ){ output = ERRM_USER_CANCEL; goto EXIT_RECV; }
	text_put_string(0,3,"Negotiating・・・        ");
	
	connect_port( TRUE );
	tmout_on_off( TRUE, atoi(mtmout[menu.tmout]) );	/* タイムアウト計測の開始 */
	comm_send_block(packet, make_p_fix2( Pid_Command_Data,Cmnd_Transfer_Trk,0x00 ) );
	
	while(1){
		key = key_press_check();
		if( key == KEY_B || key == KEY_START ) output = ERRM_USER_CANCEL;
		if( tim_status == TIMER_STATE_BUSY ) output = ERRM_TIMEOUT;
		if( rxd_status == COMM_STATE_WAIT ){
			for(i=0; i<rxd_cnt; i++){ p_1st[i] = (BYTE)rxd_buff[i]; }
			rxd_status = COMM_STATE_READY;
			if( p_1st[0] == Pid_Ack_Byte ) continue;
			if( p_1st[0] == Pid_Nak_Byte ) output = ERRM_NAK_RECEIVED;
			if( p_1st[0] == Pid_Records ) break;
			else output = ERRM_HANDSHAKE_NG;
		}
		if( output != ERRM_NONE ) goto EXIT_RECV;
	}
	tmout_on_off( FALSE,0 );
	
	text_put_string(0,3,"Connected.          ");
	record.bt.al = p_1st[2];
	record.bt.ah = p_1st[3];
	
	/* TrackRecord File Open */
	file_status = LOGFILE_CLOSE;
	if( make_logfile(record.wd) && (file = fopen(pathbuff,"w")) != NULL ){
		sprintf(msgbuff,"受信ファイル %s",pathbuff);
		text_put_string(0,1,msgbuff);
		file_status = LOGFILE_OPEN;
	}else{
		output = ERRM_FILE_OPEN_NG;	goto EXIT_RECV;	/* file create/open NG */
	}
	
	/* ファイルタイプ判別文字列と受信予定レコード数の書き込み */
	if( fwrite(appmark,sizeof(BYTE),4,file) != 4
			|| fwrite(&record.wd,sizeof(WORD),1,file) != 1 ){
		output = ERRM_FILE_WRITE_NG; goto EXIT_RECV;
	 }
	/* ACK Handshake、TrackLog受信開始 */
	rcvcnt = 0;
	tmout_on_off( TRUE, atoi(mtmout[menu.tmout]) );
	comm_send_block(packet, make_p_fix2( Pid_Ack_Byte,Pid_Records,0x00 ) );
	
	while(1){
		key = key_press_check();
		if( key == KEY_B || key == KEY_START ) output = ERRM_USER_CANCEL;
		if( tim_status == TIMER_STATE_BUSY )output = ERRM_TIMEOUT;
		if( rxd_status == COMM_STATE_WAIT ){
			for(i=0; i<rxd_cnt; i++){ p_1st[i] = (BYTE)rxd_buff[i];	}
			
			if( ! first_p ){
				fwrite(p_1st,sizeof(BYTE),1,file);		/* record type 判別byte */
				first_p = TRUE;
			}
			if( p_1st[0] == Pid_Xfer_Cmplt ) break;		/* Complete(0)	*/
			if( p_1st[0] == Pid_Trk_Data ){
				/* GPS12 --- D300 */
				sprintf(msgbuff,"Received: %u/%u      ",++rcvcnt,record.wd);
				text_put_string(0,3,msgbuff);
				/* TrackLog Save */
				if( fwrite(p_1st,sizeof(BYTE),trk.size,file) != trk.size )
														output = ERRM_FILE_WRITE_NG;
			}else if( p_1st[0] == Pid_Trk_Hdr ){
				/* eTrex --- D301 */
				++rcvcnt;
				trk.model = MODEL_eTrex;
				trk.size = TRK_POINT_SIZE_D301;	/* max51byteだが簡略化のため24byteへ制限 */
				make_p_Trk_Hdr();
				/* TrackLog Handler Save */
				if( fwrite(trackdat,sizeof(BYTE),trk.size,file) != trk.size )
														output = ERRM_FILE_WRITE_NG;
			}else{
				/* NAKの場合は、Transfer中止を送信する必要があるかも */
				if( p_1st[0] == Pid_Nak_Byte ) output = ERRM_NAK_RECEIVED;
				else output = ERRM_HANDSHAKE_NG;
			}
			if( output != ERRM_NONE ) goto EXIT_RECV;
			rxd_status = COMM_STATE_READY;
			tmout_on_off( TRUE, atoi(mtmout[menu.tmout]) );
			comm_send_block(packet, make_p_fix2( Pid_Ack_Byte,Pid_Records,0x00 ) );
		}
	}
	
EXIT_RECV:
	tmout_on_off( FALSE,0 );
	if( file_status == LOGFILE_OPEN ) fclose(file);
	if( menu.sound == SOUND_ON ) beep();
	if( output != ERRM_NONE ){
		remove(pathbuff);		/* 中断された受信ログファイル削除 */
		disp_errmsg(output);
	}else{
		text_put_string(0,4,"TrackLog 受信完了!");
	}
	connect_port( FALSE );
	text_put_string(0,7,"Hit any key to return.");
	key_wait();
}

/*
*  communication start/stop
*/
void connect_port(BOOL flg)
{
	if( flg ){
		comm_open();
		rxd_status = COMM_STATE_READY;
	}else{
		if( rxd_status != COMM_STATE_NONE ) comm_close();
		rxd_status = COMM_STATE_NONE;
	}
}

/*
*  Handshake NAK/ACK応答
*/
void send_nak()
{
	comm_send_block( packet, make_p_fix2(Pid_Nak_Byte,p_1st[0],0x00) );
	sys_wait(20);
}
void send_ack()
{
	comm_send_block( packet, make_p_fix2(Pid_Ack_Byte,p_1st[0],0x00) );
	sys_wait(20);
}

/*
*  エラーメッセージ表示
*/

void disp_errmsg(int mode)
{
	text_put_string(0,4,errm_msg[mode]);
	text_put_string(0,5,"Connection is FAILURE.");
	
	switch( mode ){
	case ERRM_USER_CANCEL:
		if( rxd_status != COMM_STATE_NONE )
			comm_send_block(packet, make_p_fix2( Pid_Command_Data,Cmnd_Abort_Transfer,0x00 ) );
		break;
	case ERRM_HANDSHAKE_NG:
		if( rxd_status != COMM_STATE_NONE )
			comm_send_block(packet, make_p_fix2( Pid_Nak_Byte,p_1st[0],0x00 ) );
#ifdef notdef
		int i,j,k;
		/* ------------------------------------------------------ debug */
		for(i=0,j=0,k=12 ;i<rxd_cnt ;i++){
			text_put_numeric(j,k,2,NUM_HEXA,p_1st[i]);
			j += 2;	if( j > 27) j=0,++k;
		}
		/* ------------------------------------------------------ debug */
#endif
		break;
	default:;
	}
}

/*
*  Tracklogファイル作成
*/
BOOL make_logfile(WORD records)
{
	int i, fn_cnt, blocksize;
	struct stat statbuf;
	static char fileinfo[20];
	static char filename[20];
	
	i = records * TRK_POINT_SIZE_D301;	/* 大きいサイズ(D301)側でサイズ確保する */
	blocksize = i / 128;				/* 必要ブロック数の算出 */
	if( (i % 128) > 0 ) blocksize++;
	
	fn_cnt = 0;
	while( fn_cnt != 999 ){
		strcpy(filename,"trk");
		text_store_numeric(msgbuff,3,NUM_PADZERO+NUM_STORE,fn_cnt);
		strcat(filename, msgbuff);
		strcat(filename,".log");
		
		strcpy(pathbuff,"/ram0/");
		strcat(pathbuff,filename);
		
		strcpy(fileinfo,"GARMINTRK");
		text_store_numeric(msgbuff,3,NUM_PADZERO+NUM_STORE,fn_cnt);
		strcat(fileinfo, msgbuff);
		
		if( stat(pathbuff, &statbuf) == E_FS_FILE_NOT_FOUND ){
			/* 存在しない場合に新規作成(必要なブロック数だけ使う) */
			if(creat(pathbuff, FMODE_W|FMODE_R, blocksize) == E_FS_SUCCESS){
				fs_setinfo(pathbuff, fileinfo);
			}else{
				return FALSE;	/* ファイル作成に失敗 */
			}
			fn_cnt = 999;
		}else{
			fn_cnt++;	/* ファイルが存在していた場合は次のファイル名で再実施、最大999まで */
		}
	}
	return TRUE;
}

/*
*  送信ファイル選択
*/
BOOL send_menu(void)
{
	char *path = NULL;
	size_t fsize;
	
	text_screen_init();
	if( select_file(pathbuff) ){
		path = pathbuff;
		if(fsize = read_file(pathbuff, _heap)) {
			return TRUE;
		}else{
			return FALSE;
		}
	}else{
		return FALSE;
	}
}

/*
*  D301 -- TrackHandler パケット作成
*	データサイズ24byteのパケットへ拡張する
*/
void make_p_Trk_Hdr()
{
	int i,size;
	
	trackdat[0] = p_1st[0];
	trackdat[1] = 24;
	trackdat[2] = p_1st[2];
	trackdat[3] = p_1st[3];
	
	size = (int)p_1st[1];
	if( size >= 24 ){
		for(i=4; i<=24  ; i++) trackdat[i] = p_1st[i];
	}else{
		for(i=4; i<=24  ; i++) trackdat[i] = 0x20;			/* 空白で初期化 */
		for(i=1; i<=size; i++) trackdat[i+1] = p_1st[i+1];
	}
	trackdat[25] = 0x00;
}

/*
*  SIZE=2 固定 送信パケット作成
*  送信byte数を戻す
*  データのDLE Stuffingは考慮しない(cmndは0x10以外であること!)
*/
int make_p_fix2( BYTE res, BYTE cmnd0, BYTE cmnd1 )
{
	int j;
	BYTE checksum = 0;
	
	packet[0] = DLE;
	packet[1] = res;
	packet[2] = 0x02;
	packet[3] = cmnd0;
	packet[4] = cmnd1;
	
	checksum = packet[1] + packet[2] + packet[3] + packet[4];
	checksum = ~checksum;
	++ checksum;
	packet[5] = checksum;
	j = ( checksum==DLE ? 1 : 0 );
	packet[5+j] = checksum;
	packet[6+j] = DLE;
	packet[7+j] = ETX;
	return 8+j;
}

/*
*  n byte 送信パケット作成
*  PacketDataSize,PaketDataにDLE Stuffing を考慮！
*  送信byte数を戻す
*/
int make_p_nnn( BYTE far *p )
{
	int i,j,k;
	BYTE checksum = 0;
	
	packet[0] = DLE;
	packet[1] = p[0];
	packet[2] = p[1];
	
	checksum = packet[1] + packet[2];
	for(i=0,j=3; i<p[1]; i++,j++){
		packet[j] = p[i+2];
		if( p[i+2] == DLE ) packet[++j] = DLE;	/* DLE Stuffing */
		checksum += packet[j];
	}
	
	checksum = ~checksum;
	++ checksum;
	packet[j] = checksum;
	k = ( checksum==DLE ? 1 : 0 );
	packet[j+0+k] = checksum;
	packet[j+1+k] = DLE;
	packet[j+2+k] = ETX;
	return j+3+k;
}

/*
*  MAIN MENU
*/
int gar_menu()
{
	int key,limit;
	static int menu_sel = SEL_RECV;
	
	text_screen_init();
	disp_status("GARMIN GPS TOOL",0);
	disp_status(" ",TEXT_SCREEN_HEIGHT-1);
	
	text_put_string(6,3,"A:決定/START:終了");
	if( menu_limit ){
		font_set_color(CLR_MESSAGE);
		text_put_string(6,GAR_MENU_RECV,lbl_gar_menu[SEL_RECV]);
		text_put_string(13,GAR_MENU_RECV,"(選択不可)");		/* 送信後の受信選択禁止 */
	}else{
		text_put_string(6,GAR_MENU_RECV,lbl_gar_menu[SEL_RECV]);
	}
	font_set_color(CLR_NORMAL);
	text_put_string(6,GAR_MENU_SEND,lbl_gar_menu[SEL_SEND]);
	text_put_string(6,GAR_MENU_CONF,lbl_gar_menu[SEL_CONF]);
	text_put_string(6,GAR_MENU_TEST,lbl_gar_menu[SEL_TEST]);
	text_put_string(6,GAR_MENU_EXIT,lbl_gar_menu[SEL_EXIT]);
	disp_gar_menu( menu_sel, 1 );
	
	if( menu_limit ) limit = SEL_SEND;	/* メニュー規制 */
	else limit = SEL_RECV;
	
	while( key = key_wait() ) {
		disp_gar_menu( menu_sel, 0 );
		switch( key ) {
		case KEY_X1:		/* move up */
			if( --menu_sel<limit ) menu_sel = SEL_EXIT;
			break;
		case KEY_X3:		/* move dpwn */
			if( ++menu_sel>SEL_EXIT ) menu_sel = limit;
			break;
		case KEY_A:			/* select */
			return	menu_sel; 
			/* break; */
		case KEY_START:		/* cancel */
			return SEL_EXIT;
			/* break; */
		}
		disp_gar_menu( menu_sel, 1 );
	}
}

/*
 *	MAIN MENU 描画
 */
void disp_gar_menu( int sel, int mode )
{
	static int cy=1;
	
	font_set_color(CLR_NORMAL);
	text_put_char(5, cy, CHAR_SPACE);	/* 矢印の表示クリア */
	if( is_colorswan() && mode==1 ) font_set_color(CLR_SELECT_WWC);
	else font_set_color( mode == 1 ? CLR_SELECT : CLR_NORMAL );
	switch( sel ){
	case SEL_RECV:
	 	cy = GAR_MENU_RECV;	break;
	case SEL_SEND:
		cy = GAR_MENU_SEND;	break;
	case SEL_CONF:
		cy = GAR_MENU_CONF;	break;
	case SEL_TEST:
		cy = GAR_MENU_TEST;	break;
	default:
		cy = GAR_MENU_EXIT;
	}
 	text_put_string(6,cy,lbl_gar_menu[sel]);
	font_set_color(CLR_NORMAL);
	text_put_char(5, cy, CHAR_MARK);	/* 矢印の表示 */
}

/*
*  Configuration
*/
int gar_config()
{
	int k,key;
	BOOL write_req;				/* config設定書き込み要求 */
	int cf_sel = SET_MODEL;
	
	text_screen_init();
	disp_status("GARMIN GPS TOOL Config",0);
	disp_status(" ",TEXT_SCREEN_HEIGHT-1);
	text_put_string(6,3,"A:選択/B:戻る");
	
	text_put_string(6,CONF_MODEL,lbl_gar_conf[0]);
	text_put_string(6,CONF_UPDATE,lbl_gar_conf[1]);
	text_put_string(16+2,CONF_UPDATE,"秒");
	text_put_string(6,CONF_TMOUT,lbl_gar_conf[2]);
	text_put_string(16+2,CONF_TMOUT,"秒");
	text_put_string(6,CONF_SOUND,lbl_gar_conf[3]);
	text_put_string(19,TEXT_SCREEN_HEIGHT-2,"ver.1.00");	/* version表示 */
	
	/* MENU描画 */
	for(k=SET_MODEL; k<=SET_SOUND; k++){ disp_cf_menu( k ); }
	text_put_char(5, cf_sel+CONF_MODEL, CHAR_MARK);
	font_set_color( is_colorswan() ? CLR_SELECT_WWC : CLR_SELECT );
	disp_cf_menu( cf_sel );
	
	write_req = FALSE;
	while( key = key_wait() ) {
		/* pre MENU描画 */
		font_set_color(CLR_NORMAL);
		text_put_char(5, cf_sel+CONF_MODEL, CHAR_SPACE);
		disp_cf_menu( cf_sel );
		
		switch( key ){
		case KEY_X1:		/* move up */
			if( --cf_sel<SET_MODEL ) cf_sel=SET_SOUND;
			break;
		case KEY_X3:		/* move dpwn */
			if( ++cf_sel>SET_SOUND ) cf_sel=SET_MODEL;
			break;
		case KEY_X2:		/* select */
		case KEY_A:
			write_req = TRUE;
			switch( cf_sel ){
			case SET_MODEL:
	    		if( ++menu.model>MODEL_eTrex ) menu.model=MODEL_GPS12;
	    		break;
	    	case SET_UPDATE:
	    		if( ++menu.update>UPDATE_60 ) menu.update=UPDATE_01;
	    		break;
	    	case SET_TMOUT:
	    		if( ++menu.tmout>TMOUT_10s ) menu.tmout=TMOUT_3s;
	    		break;
	    	case SET_SOUND:
	    		if( ++menu.sound>SOUND_ON ) menu.sound=SOUND_OFF;
	    		break;
	    	}
			break;
		case KEY_X4:
  			write_req = TRUE;
			switch( cf_sel ){
			case SET_MODEL:
	    		if( --menu.model<MODEL_GPS12 ) menu.model=MODEL_eTrex;
	    		break;
	    	case SET_UPDATE:
	    		if( --menu.update<UPDATE_01 ) menu.update=UPDATE_60;
	    		break;
	    	case SET_TMOUT:
	    		if( --menu.tmout<TMOUT_3s ) menu.tmout=TMOUT_10s;
	    		break;
	    	case SET_SOUND:
	    		if( --menu.sound<SOUND_OFF ) menu.sound=SOUND_ON;
	    		break;
	    	}
			break;
		case KEY_B:
		case KEY_START:
			if( write_req ) write_inifile();	/* Write inifile */
			return 0;
			/* break; */
		}
		/* MENU描画 */
		font_set_color( is_colorswan() ? CLR_SELECT_WWC : CLR_SELECT );
		disp_cf_menu( cf_sel );
		font_set_color(CLR_NORMAL);
		text_put_char(5, cf_sel+CONF_MODEL, CHAR_MARK);
	}
}

/*
 *	Config MENU 描画
 */
void disp_cf_menu( int sel )
{
	switch( sel ){
	case SET_MODEL:
	 	text_put_string(16,CONF_MODEL,mmodel[menu.model]);
		break;
	case SET_UPDATE:
	 	text_put_string(16,CONF_UPDATE,mupdate[menu.update]);
		break;
	case SET_TMOUT:
	 	text_put_string(16,CONF_TMOUT,mtmout[menu.tmout]);
		break;
	case SET_SOUND:
	 	text_put_string(16,CONF_SOUND,msound[menu.sound]);
		break;
	}
}

/*
 *	初期化ファイルの新規作成 (1block = 128byte)
 */
void create_inifile()
{
	struct stat statbuf;
	if( stat(USER_FILENAME, &statbuf) == E_FS_FILE_NOT_FOUND ){
		/* 存在しない場合に新規作成(必要なブロック数だけ使う) */
		if(creat(USER_FILENAME, FMODE_W|FMODE_R, USER_FILE_BLOCK) == E_FS_SUCCESS) {
			fs_setinfo(USER_FILENAME, USER_FILE_INFO);	/* ファイル情報セット */
		}
	}
}

/*
 *	初期化ファイルの書き込み (Config menu 4×2byte  = 8byte)
 */
BOOL write_inifile()
{
	FILE far *file;
	if( (file = fopen(USER_FILENAME,"wb")) != NULL ){
		fwrite(&menu,sizeof(struct t_menu),1,file);
		fclose(file);
		return TRUE;
	}else{
		return FALSE;
	}
}

/*
 *	初期化ファイルの読み込み
 */
BOOL read_inifile()
{
	FILE far *file;
	if( (file = fopen(USER_FILENAME,"rb")) != NULL ){
		fread(&menu,sizeof(struct t_menu),1,file);
		fclose(file);
		return TRUE;
	}else{
		return FALSE;
	}
}

/*
 *	status line 表現
 */
void disp_status(char far *str, int y2)
{
	int x2;
	if( is_colorswan() ) font_set_color(CLR_STATUS_WWC);
	else font_set_color(CLR_STATUS);
	x2 = text_put_string(0, y2, str);
	text_fill_char(x2, y2, TEXT_SCREEN_WIDTH - x2, CHAR_SPACE);
	font_set_color(CLR_NORMAL);
}

/*
 *	atoi
 */
int atoi(char far *s)
{
	int val = 0;
	char c;
	while(c = *s++) {
		if(c < '0' || c > '9') break;
		val = val * 10 + (c - '0');
	}
	return val;
}

/*
 *	itoa
 */
char *itoa(int n)
{
	static char retbuf[10];
	sprintf(retbuf, "%d", n);
	return retbuf;
}

/*
 *	サウンド(擬似BEEP)
 */
void beep()
{
	sound_set_pitch(0, 2000);
	sound_set_volume(0, 88);
	sys_wait(3);
	sound_set_volume(0, 0);
	sound_set_pitch(0, 0);
}

/*
 *	サウンド初期化
 */
void init_sound()
{
	static BYTE kukei_wave[] = {
		0xff, 0xff, 0xff, 0xff,	0x00, 0x00, 0x00, 0x00
	};
	sound_init();							/* サウンドの初期化 */
	sound_set_wave(0, kukei_wave);			/* 波形RAMセット */
	sound_set_output(0x0f);					/* SND_OUT_CTRL */
	sound_set_channel(0x01);				/* SNC_CH_CTRL */
}


/*
*  タイマ割り込み処理
*/
void far timer_callback()
{
	if( tim_status == TIMER_STATE_WAIT ){
		tim_status = TIMER_STATE_BUSY;
	}
}

/*
*  タイマ割り込み設定
*/
void tmout_enable()
{
	static intvector_t vector;
	
	tim_status = TIMER_STATE_NONE;
#ifdef LSI_C
	vector.callback = (void (near *)())FP_OFF(timer_callback);
#else
	vector.callback = (void (near *)())timer_callback;
#endif
	vector.cs = _CS;
	vector.ds = _DS;
	sys_interrupt_set_hook(SYS_INT_TIMER_COUNTUP, &vector, NULL);	/* VBLANK使用 */
}

/*
*  タイマ割り込み設定解除
*/
void tmout_disable()
{
	sys_interrupt_reset_hook(SYS_INT_TIMER_COUNTUP, NULL);
	timer_disable(TIMER_VBLANK);
}

/*
*  タイマ割り込み許可/禁止
*  ONESHOT TYPE, VBLANK使用
*/
void tmout_on_off( BOOL flg, int tmout_sec )
{
	if( flg ){
		tim_status = TIMER_STATE_WAIT;
		timer_enable(TIMER_VBLANK,TIMER_ONESHOT,VBLANK_1s*tmout_sec);
	}else{
		tim_status = TIMER_STATE_NONE;
		timer_disable(TIMER_VBLANK);
	}
}

/*
*	for WWC（簡易カラー化）
*/
BOOL is_colorswan()
{
    return wwc_get_hardarch();
}

void init_color()
{
    if( is_colorswan() ){
		wwc_set_color_mode(COLOR_MODE_4COLOR);
		wwc_palette_set_color(0, CLR_WHITE, 0x0fff);	/* 白 */
		wwc_palette_set_color(0, CLR_BLUE, 0x000f);		/* 青 */
		wwc_palette_set_color(0, CLR_HAIIRO, 0x0667);	/* 灰色 */
		wwc_palette_set_color(0, CLR_BLACK, 0x0000);	/* 黒 */
    }
}

