/*
 *	r.c
 *
 *	r.exe - 「run.bat」の問題を修正したコマンド
 *	Copyright (C) 2014-2015 Naoyuki Sawa
 *
 *	* Thu Nov 13 20:56:04 JST 2014 Naoyuki Sawa
 *	- 1st リリース。
 *	- P/ECE開発環境付属の「isd.exe」及び「run.bat」には、以下の不具合があります。
 *	  ■P/ECEのプログラムが、pceAppProc()の中で長時間の処理を行って、5秒間ぐらい処理を返さない時に、PC上で「run.bat」を実行すると、P/ECEがハングアップする。
 *	  不具合の原因は、「isd.exe」が、P/ECEのプログラムが終了するのをきちんと待たずに、見切り発車でsrfファイルを書き込み、実行開始してしまっていることでした。
 *	  具体的には、「\usr\PIECE\tools\isd\isdsub.c」のismWriteSrfFile()の処理が、適切ではありません。
 *	- 厳密には、「isd.exe」「run.bat」の不具合ではなく、「pieceif.dll」の不具合なのですが、今回は、「run.bat」を代替する修正プログラムを作ることにしました。
 *	  理由は、「pieceif.dll」を差し替えると影響が大きいですし、「pieceif.dll」は32ビット環境と64ビット環境で異なるので、差替えの手間がわずらわしいからです。
 *	- 今回作った「r.exe」の使用方法は、P/ECE開発環境付属の「run.bat」と同じです。
 *	  「r.exe」を、実行パスの通ったフォルダに、置いておいてください。
 *	  srfファイルの在るフォルダで、「r」を実行すると、srfファイルをP/ECEに転送して、実行します。srfファイル名の指定は不要です。
 *	- P/ECEのプログラムを開発中に、頻繁に使用しますので、名前はごく短い「r」にしました。
 *	  P/ECE開発環境に入っているコマンドも、「s.bat」とか「pm.exe」とか、短い名前が多いですね。
 *	  他のツールと名前が衝突したらその時変えよう、という感じの割り切りが良いと思います。
 *	* Sun Oct 18 10:20:51 JST 2015 Naoyuki Sawa
 *	- srfファイルを実行する時にコマンドライン引数を指定出来るようにしました。/clip/test.pce/app.cの'USE_ARGS'と連携しています。
 *	- [使い方]
 *	  ①コマンドライン引数を指定しなければ、これまでと同じ動作となります。
 *	  ＞r.exe
 *	  ②コマンドライン引数を指定すると、/clip/test.pce/app.cのmain()のcmdline[128]にコマンドライン引数を書き込んでから、実行します。
 *	  ＞r.exe one two three
 *	  単純に実装しているので、コマンドライン引数の要素内に空白文字が含まれていると、正しく処理出来ません。
 *	  コマンドライン引数の文字数は、連結後のサイズで127バイト以下でなければなりません。
 *	- ②の場合、カレントフォルダにシンボルファイル(.sym)が必要です。
 *	  ①の場合、シンボルファイルは不要です。
 *	* Sat Oct 31 21:07:07 JST 2015 Naoyuki Sawa
 *	- srfファイルを実行する時にP/ECEの時刻をPCの時刻と同期するようにしました。
 *	  /clip/test.pce/app.cで、不揮発性ログバッファを初期化する時に開始時刻を書き込むようにしたので、なるべくP/ECEの時刻が正確な方が良いと思ったからです。
 */
#define STRICT
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <io.h>
#include "C:/usr/PIECE/tools/isd/pieceif.h"			//┬P/ECE開発環境が、デフォルトのインストールフォルダにインストールされていると仮定しました。
#pragma comment(lib,"C:/usr/PIECE/tools/isd/pieceif.lib")	//┘P/ECE開発環境を、別のフォルダにインストールした環境でビルドする場合は、変更してください。

//{{2015/10/18追加:srfファイルを実行する時にコマンドライン引数を指定出来るようにしました。/clip/test.pce/app.cの'USE_ARGS'と連携しています。
/* [NOTE]
 * * コマンドライン引数のワイルドカードを展開するために、
 *   [プロジェクトの設定]->[リンク]->[オブジェクト/ライブラリモジュール]に
 *   「setargv.obj」を追加しています。
 */
//}}2015/10/18追加:srfファイルを実行する時にコマンドライン引数を指定出来るようにしました。/clip/test.pce/app.cの'USE_ARGS'と連携しています。

//{{2015/10/18変更:srfファイルを実行する時にコマンドライン引数を指定出来るようにしました。/clip/test.pce/app.cの'USE_ARGS'と連携しています。
/* 当プログラムは、「isd.exe -run」相当の動作を行う。
 * - 簡略化のために、コマンドライン引数を確認しないことにした。
 *   厳密には、コマンドライン引数が指定されていないことを確認せよ。 */
//int main() {
//↓2015/10/18変更:srfファイルを実行する時にコマンドライン引数を指定出来るようにしました。/clip/test.pce/app.cの'USE_ARGS'と連携しています。
int main(int argc, char* argv[]) {
//}}2015/10/18変更:srfファイルを実行する時にコマンドライン引数を指定出来るようにしました。/clip/test.pce/app.cの'USE_ARGS'と連携しています。
	int exitCode = EXIT_FAILURE;//終了コードを「失敗」としておく。
	struct _finddata_t fileinfo;
	long handle;
	/* P/ECEに接続する。
	 * - 簡略化のために、接続するP/ECEは1台固定とし、接続待ち時間もデフォルトとした。
	 *   2台目以降に対応したり、接続待ち時間を変更する場合は、ismInitEx()に変更せよ。
	 * - 簡略化のために、P/ECEのOSバージョンは確認しないことにした。
	 *   最新のOSバージョンであると仮定する。
	 *   OSバージョンを確認する場合は、ismGetVersion()も使用せよ。 */
	if(ismInit()) {
		fputs("### P/ECEに接続できません。\n", stderr);
		goto L_EXIT;
	}
	/* 現在のフォルダに在る、srfファイルを取得する。
	 * - 複数在る場合は、最初に見つけたsrfファイルを使用する。 */
	handle = _findfirst("*.srf", &fileinfo);
	if(handle == -1) {
		fputs("### srfファイルが在りません。\n", stderr);
		goto L_EXIT;
	}
	_findclose(handle);
//{{P/ECE開発環境付属の「isd.exe」は、この範囲の処理が「ismWriteSrfFile(…,1)」の一命令で行われている。当プログラムでは、問題の修正を行うために、「ismAppStop()」「ismWriteSrfFile(…,0)」「ismAppStart()」に分離して処理することにした。
  //{{
  //×	/* P/ECEのプログラムを終了する。 */
  //×	if(ismAppStop()) {
  //×		fputs("### P/ECEのプログラムを終了できません。\n", stderr);	//┬P/ECE開発環境付属の「isd.exe」は、左記の処理が不足しているようだ。P/ECEのプログラムが長時間の処理中で、停止できなかった場合にも、srfファイルの書き込みを行っていた。
  //×		goto L_EXIT;							//┘そのため、P/ECEのプログラムが長時間の処理中に「run」バッチスクリプトを実行すると、P/ECEがハングアップするという問題が生じていた。停止できなかった場合は、処理を中止することにした。
  //×	}
  //↓一度でもismAppStop()を発行したら、P/ECEはプログラム終了フェーズに入るので、処理を中断してはいけない。処理を中断すると、P/ECEが「終了」状態(=画面が消えた状態)で停止してしまう。「\usr\PIECE\sysdev\pcekn\runapp.c」のAppStart()の「while ( 1 ) { ～」を参照せよ。
  //↓一度でもismAppStop()を発行したら、P/ECEのプログラムが終了するまで、繰り返す必要がある。
  //↓尚、厳密には、2回目以降の「ismAppStop()」は無効なので(「\usr\PIECE\sysdev\pcekn\usbcom.c」のusbcom()の「case 4: // Set App Status.」を参照せよ)、2回目以降はismAppStop()ではなくismSetAppStat()を使うのが正しいのだが、ismAppStop()を使用しても動作上は問題無い。
	/* P/ECEのプログラムを終了する。 */
	while(ismAppStop()) {
		fputs("### P/ECEのプログラムを終了できません。再試行中…\n", stderr);
	}
  //}}
	/* P/ECEに、srfファイルを書き込む。 */
	if(ismWriteSrfFile(fileinfo.name, 0)) {
		fputs("### srfファイルを書き込めません。\n", stderr);		//┬P/ECE開発環境付属の「isd.exe」は、左記の処理が不足しているようだ。srfファイルの書き込み失敗した場合も、P/ECEのプログラムを開始していた。
		goto L_EXIT;							//┘実際には、処理がここまで進んだ後にsrfファイルの書き込み失敗することは無いはずであるが、安全のために、srfファイルの書き込み失敗した場合は、処理を中止することにした。
	}
  //{{2015/10/18追加:srfファイルを実行する時にコマンドライン引数を指定出来るようにしました。/clip/test.pce/app.cの'USE_ARGS'と連携しています。
	//コマンドライン引数が指定されていたら…
	if(argc >= (1 + 1)) {
		char path[_MAX_PATH], drive[_MAX_DRIVE], dir[_MAX_DIR], fname[_MAX_FNAME];
		FILE* fp;
		//srfファイルの拡張子をsymに変えたファイル名を、シンボルファイルのファイル名とします。
		_splitpath(fileinfo.name, drive, dir, fname, NULL);
		_makepath(path, drive, dir, fname, ".sym");
		//シンボルファイルを開きます。
		fp = fopen(path, "r");
		if(!fp) {
			fputs("### symファイルが在りません。\n", stderr);
		} else {
			char sBuf[1024], sSymbol[256], sFile[256], sSection[256], sType[256];
			int i, iAddress = 0;
			//シンボルファイルの各行を読み込んで…
			while(fgets(sBuf, sizeof sBuf, fp)) {
				if(sscanf(sBuf, "%s%s%s%s%x", sSymbol, sFile, sSection, sType, &iAddress) == 5) {
					if(!strncmp(sSymbol,  "cmdline.", 8) &&
					   !strcmp( sFile,    "app.o"      ) &&
					   !strcmp( sSection, "data"       ) &&
					   !strcmp( sType,    "local"      )) { break; }	//cmdline.～のシンボル定義を見つけたら抜ける。
				}
				iAddress = 0;
			}
			if(!iAddress) {
				fputs("### cmdline.～のシンボル定義が在りません。\n", stderr);
			} else {
				//コマンドライン引数を連結します。
				strcpy(sBuf, fname);	//argv[0]はsrfファイル名の拡張子を取り除いたファイル名とする事にしました。
				for(i = 1; i < argc; i++) {
					strcat(sBuf, " ");
					strcat(sBuf, argv[i]);
				}
				i = strlen(sBuf);
				if(i > 127) {
					fputs("### コマンドライン引数が長過ぎます。\n", stderr);
				} else {
					//コマンドライン引数を書き込みます。
					if(ismWriteMem(sBuf, iAddress, i + 1/*nul*/)) {
						fputs("### コマンドライン引数を書き込めません。\n", stderr);
					}
				}
			}
			//シンボルファイルを閉じます。
			fclose(fp);
		}
	}
  //}}2015/10/18追加:srfファイルを実行する時にコマンドライン引数を指定出来るようにしました。/clip/test.pce/app.cの'USE_ARGS'と連携しています。
  //{{2015/10/31追加:srfファイルを実行する時にP/ECEの時刻をPCの時刻と同期するようにしました。
	{
		//PCの時刻を求める。
		time_t t = time(NULL);
		struct tm* tm = localtime(&t);
		//P/ECEの時刻を設定する。
		ismRTCSet(			//localtime()     ismRTCSet()
			tm->tm_year + 1900,	//1900ベース  ⇒  0ベース
			tm->tm_mon  + 1,	//   1ベース  ⇒  0ベース
			tm->tm_mday,		//   0ベース  ⇒  0ベース
			tm->tm_hour,		//   0ベース  ⇒  0ベース
			tm->tm_min,		//   0ベース  ⇒  0ベース
			tm->tm_sec);		//   0ベース  ⇒  0ベース	秒は無視されます。
	}
  //}}2015/10/31追加:srfファイルを実行する時にP/ECEの時刻をPCの時刻と同期するようにしました。
  //{{
  //△	/* P/ECEのプログラムを開始する。 */
  //△	if(ismAppStart()) {
  //△		fputs("### P/ECEのプログラムを開始できません。\n", stderr);
  //△		goto L_EXIT;
  //△	}
  //↓この変更は必須では無い。一度でもismAppStart()を発行したら、P/ECEはプログラム開始フェーズに入るので、プログラムが開始したことを確認しなくても、動作上は問題ない。
  //↓しかし、「P/ECEのプログラムを開始できません。」というエラーメッセージを表示したまま、「r.exe」が終了してしまうのは紛らわしいし、
  //↓「r.exe」をバッチスクリプトの中で使って、P/ECEのプログラムが開始した後に別の通信コマンドを実行するような場合のために、P/ECEのプログラムが開始するまで待つことにした。
  //↓具体的には、pceAppInit()の処理時間が長い場合に、上記のケースが起こり得るので、そのための対策である。
  //↓尚、厳密には、2回目以降の「ismAppStart()」は無効なので(「\usr\PIECE\sysdev\pcekn\usbcom.c」のusbcom()の「case 4: // Set App Status.」を参照せよ)、2回目以降はismAppStart()ではなくismSetAppStat()を使うのが正しいのだが、ismAppStart()を使用しても動作上は問題無い。
	/* P/ECEのプログラムを開始する。 */
	while(ismAppStart()) {
		fputs("### P/ECEのプログラムを開始できません。再試行中…\n", stderr);
	}
  //}}
//}}P/ECE開発環境付属の「isd.exe」は、この範囲の処理が「ismWriteSrfFile(…,1)」の一命令で行われている。当プログラムでは、問題の修正を行うために、「ismAppStop()」「ismWriteSrfFile(…,0)」「ismAppStart()」に分離して処理することにした。
	exitCode = EXIT_SUCCESS;//終了コードを「成功」に変更する。
L_EXIT:
	ismExit();//この関数の呼出しは常に必要。ismInit()に失敗しても必要。
	return exitCode;
}
