/*    DOS2WW:MS-DOSからWonderWitchへ ファイル転送する ver 1.3 */
/*         2001.01.28 (c) ささお<sasao@dais.is.tohoku.ac.jp>  */
/*    コンパイラは、Turbo C++  Version 1.01 を使用しています  */
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<dir.h>
#include<conio.h>
#include<signal.h>
#include<dos.h>
#include<time.h>

#include "com_func.h"
#define MAXLEN 512
#define D_ROM0 0
#define D_RAM0 1

/* #define PC98 */

char recv_buf[2048], send_buf[2048];			/* 送受信バッファ */
char tmp_path[MAXLEN];
int CH = 2,KEY_FLAG=0,DIFF_FLAG = 0,RUN_FLAG = 0,QUIT_FLAG = 0;
int LOCAL_FLAG = 0,DEST_DIR = D_ROM0, ERR_DIS_FLAG = 0,BYTE_WAIT =0;
int WAIT_FLAG = 1;
long SEND_ERROR=0,SEND_WAIT,SEND_FLAG=0;
struct com_init_data data;
FILE *fp_out;
FILE *fp_in1,*fp_in2;
long int line;
char DIR_NAME[3][10]={"/rom0","/ram0"};
unsigned char ww_header[128]={
  0x23,0x21,0x77,0x73,0xff,0xff,0xff,0xff,
  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,

  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,

  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff
};

										/* 通信パラメータ設定 */
char S_OPTION[27][10]={
    "/9600","/19200","/38400","/57600","/115200",
    "/COM1","/COM2","/COM3","/COM4",
    "/ROM0","/RAM0",
    "/D","/DX",
    "/R","/RX",
    "/K","/KX",
    "/L","/E",
    "/B",
    "/H","/HX",
    "/?"
};

char SS_KEY_CODE[25]={
  0x08, 0x09, 0x02, 0x16, 27,
  0x04, 0x13, 0x05, 0x18, 24,
  0x07,224,225,226,227,
  228,229,230,231,232,
  233,234,235,0x17,0x1A
};

#ifdef PC98
char PC_KEY_CODE[26][8]={
  {0x08},  {0x09},  {0x1a},  {0x00,0x52},  {0x1B},
  {0x1b,0x3e}, {0x1b,0x3c}, {0x1b,0x5e}, {0x1b, 0x5f},
  {0x1b,0x49}, {0x1b,0x44},
  {0x1b, 0x53}, {0x1b,0x54},{0x1b,0x55},{0x1b,0x56}, {0x1b,0x57},
  {0x1b,0x45},{0x1b,0x4a},{0x1b,0x50},{0x1b,0x51},{0x1b,0x21},
  {0x1b,0x7b},{0x1b,0x7d},
  {11}
};
#else
char PC_KEY_CODE[26][8]={
  {0x08},  {0x09},  {0x00,0x47},  {0x00,0x52},
  {0x1B},  {0x00,0x4D}, {0x00,0x4B},
  {0x00,0x48}, {0x00, 0x50}, {0x00,0x4F}, {0x00,0x53},
  {0x00, 0x3B}, {0x00,0x3C}, {0x00,0x3D},{0x00,0x3E},
  {0x00,0x3F},{0x00,0x40},{0x00,0x41},{0x00,0x42},
  {0x00,0x43},{0x00,0x44},{0x00,0x85},{0x00,0x86},
  {0x00,0x49},{0x00,0x51},{0x00,0x75}
};
#endif

void ComSend2(int c){
	long i=0;
	int j;

	while(ComSendBufSt(CH)>0 && i++ < (SEND_WAIT+1) )for(j=0;j<100;j++);
	if ( BYTE_WAIT ){
		for(j=0;j<100;j++)for(i=0;i<SEND_WAIT;i++);
	}	
	ComSend(CH,c);
	if ( LOCAL_FLAG ){
		i = 0;
		do{
			if ( ComBufSt(CH)>0 ){
				c = ComRead(CH) & 255;
				break;
			}
			for(j=0;j<100;j++);
		}while( i++ < SEND_WAIT );
	}
}

int CheckHex(char *name, char *src){
	char buf[MAXLEN],*p;
	int ln,i;
	
	ln = strlen(name);
	if ( strncmpi(name,src,ln) )return -1;
	sscanf(&src[ln],"%x",&i);
	return i;
}

long CheckDec(char *name, char *src){
	char buf[MAXLEN],*p;
	int ln;
	long i;
	
	ln = strlen(name);
	if ( strncmpi(name,src,ln) )return -1;
	sscanf(&src[ln],"%ld",&i);
	return i;
}

void SetOption(char *src){
    char buf[MAXLEN],tmp[MAXLEN],*p,*q;
	long i=0,j;

	p = tmp;
	q = src;
	do{
      *p++ = *q++;
    }while( *q != '/' && *q != '\0' && *q != ' ' );
	*p = '\0';

    if ( (i=CheckDec("/W",src)) != -1 ){
    	SEND_WAIT = i;
    	return;
    }
#ifndef PC98
    if ( (i=CheckHex("/IRQ=",src)) != -1 ){
    	data.irq = i;
    	return;
    }
    if ( (i=CheckHex("/ADDRESS=",src)) != -1 ){
    	data.address = i;
    	return;
    }
#endif
	for (i=0;i<23;i++){
	  if ( strcmpi(tmp,S_OPTION[i]) == 0 )break;
	}

	switch(i){
		case 0:	data.baud =7; break;	/* 9600 bps */
		case 1: data.baud =8; break;	/* 19200 bps */
		case 2: data.baud =9; break;	/* 38400 bps */
		case 3: data.baud =10; break;	/* 57600 bps */
		case 4: data.baud =11; break;	/* 115200 bps */
#ifdef PC98
		case 5: break;	/* COM1 */
		case 6: break;	/* COM2 */
		case 7: break;	/* COM3 */
		case 8: break;	/* COM4 */
#else
		case 5: data.address = 0x3f8; data.irq = 4; break;	/* COM1 */
		case 6: data.address = 0x2f8; data.irq = 3; break;	/* COM2 */
		case 7: data.address = 0x3e8; data.irq = 4; break;	/* COM3 */
		case 8: data.address = 0x2e8; data.irq = 3; break;	/* COM4 */
#endif
		case 9:  DEST_DIR = D_ROM0; break;
		case 10: DEST_DIR = D_RAM0; break;
		
		case 11: DEST_DIR = D_ROM0; break;
		case 12: DEST_DIR = D_RAM0; break;
		
		case 13: RUN_FLAG = 1; break;
		case 14: RUN_FLAG = 0; break;

		case 15: KEY_FLAG = 1; break;
		case 16: KEY_FLAG = 0; break;

	    case 17: LOCAL_FLAG = 1; break;
	    case 18: ERR_DIS_FLAG = 1; break;
	    case 19: BYTE_WAIT = 1; break;

		case 20: WAIT_FLAG = 1; break;
		case 21: WAIT_FLAG = 0; break;

	    case 22: ;/* ? オプション */

	    default:
	    	if (i!=22)printf("エラー: %s は無効なオプションです\n\n",tmp);

printf("■ オプション [] はデフォルトです\n");

printf("通信速度設定: ");
printf("  [/9600], /38400\n");
printf(" ◆ 指定した速度で通信を行います。\n");

#ifndef PC98
printf("COMポートの指定: ");
printf(" [/COM1], /COM2, /COM3, /COM4\n");
printf(" ◆ 指定したCOMポートをします。\n");
#endif

printf("転送先の指定: ");
printf(" [/ROM0], /RAM0\n");
printf(" ◆ 指定した転送先にファイルを転送します。\n");

printf("その他の設定:\n");

printf("  /R   ,[/RX]  ファイル転送後プログラムを実行 します/[しません] \n");
printf("  /K   ,[/KX]  ファイル送信後キー送信を 行います/ [行いません]\n");
printf(" [/H]  , /HX   半二重通信を [行います]/ 行いません\n");
printf("  /L           ローカルエコーをキャンセルします\n");
printf("               キー送信で文字が重複する場合に指定してください\n");
printf("  /B           1バイト送信毎にウェイトを付加します\n");
printf("  /E           XMODEM送信時のエラーチェックを無効にします\n");

#ifdef PC98
printf("  /Wn          (nは0以上の整数) ウェイトを設定します。転送エラーが\n"
       "                起きる場合にはこの値を大きくしてください。[/W50]\n");
#else
printf("  /Wn          (nは0以上の整数) ウェイトを設定します。転送エラーが\n"
       "                起きる場合にはこの値を大きくしてください。[/W500]\n");
printf("  /IRQ=nn       割込み要求設定をします (nn : 03, 04, 08 など)\n");
printf("  /ADDRESS=nnnn I/Oのアドレス設定をします (nnnn : 03f8, 02f8, 03e8, 02e8など)\n");
#endif
	    	exit(1);
	}
}


void CheckBuf(void){
	long tm;

	tm = time(NULL);

    while(ComSendBufSt(CH)>0 && time(NULL)-tm < 1 );
    if ( ComSendBufSt(CH)>0 ){
    	printf("通信ポートが使用できません。COMポートが正しいか、\n"
    	       "ほかのソフトが使用中でないかを確かめてください。\n");
		ComRet(CH);
		exit(1);
    }
}

void ctrl_c_none(void){
	return;
}

void ctrl_c_send(void){
	ComSend2(3);
	signal(SIGINT,ctrl_c_send);
}

void ctrl_c(void){
	time_t tm;

	disable();
    signal(SIGINT,ctrl_c_send);
	printf("強制終了しました\n");
	ComSend2(3);
	tm = time(NULL);
	while( time(NULL) - tm < 1*WAIT_FLAG );
	if ( SEND_FLAG ){
		ComRet(CH);
		SEND_FLAG = 0;
	}
	enable();

	exit(1);
}

void SendLine(char *src){
    char *p;
    int i;
    long j,k;

    p = src;

	while( *p!='\0' ){
		CheckBuf();
		if ( *p != '\r' && *p != '\n' ) ComSend2( *p);
		for (k=0;k<100;k++)for(j=0;j<SEND_WAIT;j++);
		p++;
	}

	ComSend2( 0x0D );
}

/*          */
/* キー送信 */
/*          */
int KeySend(void){
  unsigned int i,j,endflag=0;
  int ti;
  long tj,tk;
  char key,*p;
  unsigned char c;
#ifdef PC98
  printf("[キー送信]\nF1 で終了します\n");
#else
  printf("[キー送信]\nCTRL + END で終了します\n");
#endif
  signal(SIGINT,ctrl_c_send);

  do{

    /* 受信バッファに蓄積した文字列を表示 */
	
	while( ComBufSt(CH)>0 ){
		c = ComRead(CH) & 255;
		if ( c<32 && c!=0x0d && c!=0x0a && c!=0x08 ){
			printf("[%02X]",c);
		}else printf("%c",c);
	}

	/* キー入力受付 */

	if (kbhit()){
  		key = getch();
		CheckBuf();
		i=0;
		while(ComSendBufSt(CH)>0 && i++ < (SEND_WAIT+1)*500 );

#ifdef PC98
  	if ( key == 0x1b ){
		key = getch();
  		for ( i=0; i<24; i++){
  			p = PC_KEY_CODE[i];
  			if ( *p++ == 0x1b ) if ( *p == key )break;
  		}
  	}else{
  		for ( i=0; i<24; i++){
  			p = PC_KEY_CODE[i];
  			if ( *p == key )break;
  		}
  	}
  	if ( i == 11 ) endflag = 1;

  	else if ( i <  23 ){
		j = SS_KEY_CODE[i]&255;
		if( j >=224 & j<=235 ){
			j=j-224;
			i=0;
		}else ComSend2( SS_KEY_CODE[i]&255 );
  	}

#else
  	if ( key == 0 ){
		key = getch();
  		for ( i=0; i<26; i++){
  			p = PC_KEY_CODE[i];
  			if ( *p++ == 0 ) if ( *p == key )break;
  		}
  	}else{
  		for ( i=0; i<26; i++){
  			p = PC_KEY_CODE[i];
  			if ( *p == key )break;
  		}
  	}
  	if ( i == 25 ) endflag = 1;

  	else if ( i <  25 ){
		j = SS_KEY_CODE[i]&255;
		if( j >=224 & j<=235 ){
			j=j-224;
			i=0;
		}else ComSend2( SS_KEY_CODE[i]&255 );
  	}
#endif
  	else{
  	    ComSend2( key&255);
  	}
    for (tk=0;tk<100;tk++){
   		for(tj=0;tj<SEND_WAIT;tj++);
		if( ComBufSt(CH)>0 ){
			ti = ComRead(CH)&255;
			if ( ti == 13 || ti == 10 || ti>31 ){
				printf("%c",ti);
			}else printf("[%02X]",ti);
		}
	 }
	}
  }while( !endflag );
  return 0;
}


/* シェルからの応答を待つ */
void WaitRes(void){
	long tm,c1,c;


	tm = time(NULL);c1=0; 
	while( time(NULL)-tm < 3 ){
		if ( ComBufSt(CH)>0 ){
			c = ComRead(CH) & 255;
			printf("%c",c);
			if ( (c1=='E' || c1=='K' || c1=='O' )&& c==13)break; 
		}
		c1 = c;
		if (kbhit()){
			printf("送信をキャンセルしました\n");
			ComRet(CH);
			exit(1);
		}
	}
}

int main(int argc, char **argv){
  struct ffblk ffblk;
  short sum,endflag,tmp1_flag, tmp2_flag, tmp1_inc, tmp2_inc;
  short seq,header_flag=0;
  unsigned long f_size,f_time,tmp1_ln, tmp2_ln,i,j,k,send_size=0;
  static char c,buf[MAXLEN],buf2[MAXLEN];
  static char filename[MAXLEN];
  char *p,*q,c1,c2;
  time_t tm;
  int ii,done,w_flag;
  unsigned short far *pv;


  data.baud = 7;						/* 通信速度を9600bpsに */

#ifdef PC98
  data.command = 0x4c;						/* N81 */
#else
  data.command = 3;						/* N81 */
#endif

  data.flow = 3;
  data.send_buf = send_buf;
  data.recv_buf = recv_buf;
  data.send_size = 2048;
  data.recv_size = 2048;

#ifdef PC98
  data.msb = 0;
  data.vend = 0;
#else
  data.address = 0x3f8;				/* COM1 adr:0x3f8, irq:4 */
  data.irq = 4;	
  data.fifo = 1;
#endif

  pv = (unsigned short far*) 0xFFFF0003L;
#ifdef PC98
  printf("dos2ww98  ver. 1.3          2001.01.28 (c) ささお\n");
  if ( *pv != 0xfd80 )
  {
    printf("* DOS/V上では実行できません。dos2ww.exe を使用してください。");
	exit(1);
  }
#else
  printf("dos2ww  ver. 1.3            2001.01.28 (c) ささお\n");
  if ( *pv == 0xfd80 )
  {
  	printf("* PC9801シリーズ上では実行できません。dos2ww98.exe を使用してください。");
  	exit(1);
  }
#endif

  signal(SIGINT,ctrl_c);

#ifdef PC98
  CH = 0;
  SEND_WAIT = 50;
#else
  CH = 1;
  SEND_WAIT = 500;
#endif

  tmp_path[0]='\0';

	/* 環境変数から設定を読み込む */

  if ( (p=getenv("DOS2WW"))!= NULL ){
	do{
	  while( *p!='/' && *p!='\0' )p++;
	  if ( *p == '\0' ) break;
	  SetOption(p++);
	}while( *p != '\0' );
  }

	/* 引数から設定を読み込む */

  filename[0]='\0';
  for ( i=1; i < argc; i++){
  	if ( argv[i][0] == '/' )SetOption(&argv[i][0]);
  	else{
  	  p = &argv[i][0];
  	  q = filename;
  	  while( (*p & 255)>32 )*q++ = *p++;
  	  *q = '\0';
  	}
  }


  if (filename[0]=='\0'){
     if ( KEY_FLAG ){
        if ( ComInit(CH, &data) ){
			while(ComSendBufSt(CH)>0 && i++ < (SEND_WAIT+1)*500 );
        	printf("通信ポートの初期化ができませんでした\n");
			ComRet(CH);
  			exit(1);
  		}
  		SEND_FLAG=1;
     	KeySend();
  		ComRet(CH);
  		exit(1);
  	}else{
    	 printf("ファイル名を指定してください\n");
    	 exit(1);
    }
  }

/*
 * COM ポートオープン
 */

  if ( ComInit(CH, &data) ){
  	printf("通信ポートの初期化ができませんでした\n");
	ComRet(CH);
  	exit(1);
  }


  /* ファイル名を読み込む */

for ( ii=1; ii < argc; ii++){
    filename[0]='\0';
  if ( argv[ii][0] != '/' ){
  done = findfirst( argv[ii],&ffblk,0 );

do{
  p = argv[ii]; w_flag = 0;
  while ( *p != '\0' ){
  	if ( *p=='?' || *p=='*' )w_flag=1;
  	p++;
  }
  
  q = filename;
  if ( !w_flag ){
  	p = argv[ii];
  	while( (*p & 255)>32 )*q++ = *p++;
  } else {
  	p = ffblk.ff_name;
  	while( (*p & 255)>32 ){
  		if ( *p>='A' && *p<='Z' )*p=*p-'A'+'a';
  		*q++ = *p++;
  	}
  }
  *q = '\0';


  printf("file: %s\n",filename);
  if ( (fp_in1 = fopen(filename,"rb"))== NULL ){
  	fprintf(stderr,"ファイルが開けませんでした \n");
  	exit (1);
  }

  SendLine("\n");
/*
 * WonderWitch転送形式のヘッダを作成
 */
  for(i=0x40;i<0x6c;i++)ww_header[i]='\0';
  i=0;
  while(filename[i]!='\0'){
  	ww_header[0x40+i]=filename[i];
  	ww_header[0x50+i]=filename[i];
  	i++;
  }

  f_size = ffblk.ff_fsize;
  for(i=0;i<4;i++){
  	ww_header[0x6c+i]=(char)f_size & 0xff;
  	f_size = f_size>>8;
  }

  f_size = (ffblk.ff_fsize-1)/128 + 1;
  for(i=0;i<2;i++){
  	ww_header[0x70+i]=(char)f_size & 0xff;
  	f_size = f_size>>8;
  }


  f_time = ffblk.ff_ftime;
  for(i=0;i<2;i++){
  	ww_header[0x74+i]=(char)f_time & 0xff;
  	f_time = f_time>>8;
  }
  f_time = ffblk.ff_fdate-10240;
  for(i=0;i<2;i++){
  	ww_header[0x76+i]=(char)f_time & 0xff;
  	f_time = f_time>>8;
  }


/*
 * 転送ファイルの形式を検査
 */

  fseek( fp_in1, 0, 0 );
  fread(buf2,1,128,fp_in1);
  if ( buf2[0]!='#' || buf2[1]!='!' || buf2[2]!='w' || buf2[3]!='s'){
    header_flag=1;
  }else{
    header_flag=0;
  }

  SEND_FLAG=1;

  fseek( fp_in1, 0x40, 0 );
  fread(buf2,1,128,fp_in1);
  fseek( fp_in1, 0, 0 );


/*
 * delete コマンド発行
 */


if(header_flag){
  sprintf(buf,"delete \"%s/%s\"\n",DIR_NAME[DEST_DIR],&ww_header[0x40]);
}else{
  sprintf(buf,"delete \"%s/%s\"\n",DIR_NAME[DEST_DIR],buf2);
}
  WaitRes();
  SendLine(buf);

/*
 * put コマンドを送信
 */
  WaitRes();
  sprintf(buf,"put %s\n",DIR_NAME[DEST_DIR]);
  SendLine(buf);

/*
 * レスポンス待ち
 */

endflag = 0;
tm = time(NULL);
do{
	while( ComBufSt(CH)>0 ){
		c = ComRead(CH) & 255;
		if ( c<32 && c!=0x0d && c!=0x0a ){
			/* printf("[%02X]",c); */
		}else printf("%c",c);
		if ( c == 0x15 ){
			endflag = 5;
			break;
		}
		if ( c == 0x18 ){
			endflag = 2;
		}
	}

	if (kbhit()){
		getch();
		endflag = 1;
	}
}while( !endflag && (time(NULL)-tm < 10));

if ( endflag != 5 ){ 
	if ( time(NULL)-tm>=10 )printf("タイムアウトしました\n");
	if ( endflag == 2 )	printf("送信がキャンセルされました\n");
	if ( endflag == 1 )	printf("送信をキャンセルしました\n");
 	ComRet(CH);
	exit(1);
}

/* パケット送信 */

for (i=0;i<100;i++)for(j=0;j<SEND_WAIT;j++);
for (i=0;i<100;i++)for(j=0;j<SEND_WAIT;j++);

printf("<XMODEM 送信>\n");
for (i=0;i<128;i++)buf[i]=0x1a;
seq = 1;

while( fread(buf,1,128,fp_in1)>0 ){
if (header_flag){
	for(i=0;i<128;i++)buf[i]=ww_header[i];
    fseek( fp_in1, 0, 0 );
	header_flag = 0;
}

/* 1 ブロック送信 */
  ComSend2( 1);			/* SOH */
  ComSend2( seq&255);
  ComSend2( (seq^255)&255);

  for (k=0;k<100;k++){
   		for(j=0;j<SEND_WAIT;j++);
		if( ComBufSt(CH)>0 ){
			i = ComRead(CH)&255;
		}
  }


	/*  128 バイト送信 */
	sum = 0;
	for (i=0;i<128;i++){
  		ComSend2( buf[i]&255);
    	sum = (sum+(buf[i]&255))&255;
 	}

	/* チェックサム送信 */
	ComSend2( sum);

  /* ACK を待つ */
  tm = time(NULL);
  endflag = 0;
  do{
  	if ( ComBufSt(CH)>0 ){
		c = ComRead(CH) & 255;
		if ( ERR_DIS_FLAG )printf("[%02X]",c);
  		endflag = 1;
  		break;
  	}
  }while( time(NULL)-tm <2 );
  for (i=0;i<100;i++)for(j=0;j<SEND_WAIT;j++);

  for (i=0;i<128;i++)buf[i]=0x1a;

  if ( endflag == 0 ){
  	printf("\nタイムアウトしました\n");
  	break;
  }
  if ( c == 0x15 ){
    fseek( fp_in1, seq*128-128, 0 );
  	continue;
  }
  printf("\r%ld bytes",send_size+=128);
  if ( c == 0x06 || ERR_DIS_FLAG ){
    seq++;
  	continue;
  }
 else{
    printf("\n[送信エラーが発生しました]\n");
    break;
  }
}
  for (i=0;i<100;i++)for(j=0;j<SEND_WAIT;j++);
  ComSend2(4);
  WaitRes();
  printf("\n<送信終了>\n");

  for (i=0;i<100;i++)for(j=0;j<SEND_WAIT;j++);
  done = findnext(&ffblk);
}while(!done);
}}

	if (RUN_FLAG){
		printf("<プログラム実行>\n");
		sprintf(buf,"exec %s/%s\n",DIR_NAME[DEST_DIR],buf2);
		SendLine(buf);
	}
	if (KEY_FLAG) KeySend();

  i = 0;
  while(ComSendBufSt(CH)>0 && i++ < (SEND_WAIT+1)*500 );
  ComRet(CH);				/* 232cライブラリの利用を終了する */

  return 0;

}