/*	
 *	dbin2o.exe - バイナリ⇒srf33オブジェクトファイル変換ユーティリティ
 *
 *	Copyright (C) 2015-2016 Naoyuki Sawa
 *
 *	* Thu Jun 11 01:41:08 JST 2015 Naoyuki Sawa
 *	- 1st リリース。
 *	  clip/tool/dbin2c/を元にして作成しました。
 *	* Tue Sep 29 21:13:48 JST 2015 Naoyuki Sawa
 *	- clip/tool/dbin2c/を元にしていたプロジェクトを破棄し、clip/test.win/を元にしたプロジェクトに移行しました。
 *	- エクスターン情報を生成する'-eシンボル名'オプションを追加しました。
 *	* Sun Feb 21 21:02:01 JST 2016 Naoyuki Sawa
 *	- 「Boehm GC」をマルチスレッド対応にした事,及び,それに伴ってclipbpe.cモジュールを修正した事に伴い、当ツールもプロジェクト設定の変更を行ってリビルドしました。
 *	  詳細は、/clip/tool/gc/README.TXT と /clip/clipbpe.c の2015/11/03のコメントを参照して下さい。
 *	- libclipのライブラリ化の伴い、以下の変更を行いました。
 *	  ・extclip.hとextclip.cを削除しました。extclip.hとextclip.cはlibclipに含まれるようになったからです。
 *	  ・"CLiP Files"を削除しました。CLiPライブラリの各モジュールはlibclipに含まれるようになったからです。
 *	  当ツールの動作には変更有りません。
 */
#include "app.h"
#define VERSION		"20160221"	//最終更新日
/****************************************************************************
 *	ローカル関数宣言
 ****************************************************************************/
__declspec(noreturn) static void usage();
static int subr(int argc, char* argv[]);
/****************************************************************************
 *	main
 ****************************************************************************/
int main(int argc, char* argv[]) {
	int exitCode;
//	putenv("GC_LOG_FILE=CON");//ログ出力先を指定する。
//	GC_set_find_leak(1);//メモリリーク検出を開始する。
	exitCode = subr(argc, argv);
//	CHECK_LEAKS();//メモリリークを検出する。
//	cputs("ok");
//	getch();
	return exitCode;
}
/****************************************************************************
 *	ローカル関数
 ****************************************************************************/
__declspec(noreturn) static void usage() {
	fprintf(stderr, "dbin2o.exe - バイナリ⇒srf33オブジェクトファイル変換ユーティリティ (%s)\n", VERSION);
	fprintf(stderr, "Copyright (C) 2015-2016 Naoyuki Sawa\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "USAGE:\n");
	fprintf(stderr, "  dbin2o.exe [option ...] infile [outfile[.o]]\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "  infile         入力バイナリファイル名を指定します。(必須)\n");
	fprintf(stderr, "  [outfile[.o]]  出力オブジェクトファイル名を指定します。(省略可)\n");
	fprintf(stderr, "                 出力オブジェクトファイル名を省略した場合は、入力ファイル名の拡張子を.oに変えたファイル名とします。\n");
	fprintf(stderr, "                 出力オブジェクトファイル名のベース名を指定して拡張子を省略した場合は、自動的に拡張子.oを補います。\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "OPTIONS:\n");
	fprintf(stderr, "  -eシンボル名   エクスターン情報を生成します。\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "注意点: '-eシンボル名'オプションを指定しなかった場合、エクスターン情報の無いsrf33オブジェクトファイルを生成します。\n");
	fprintf(stderr, "        その場合、アプリケーションプログラムがデータのアドレスを得るには、__START_outfile_code を参照してください。\n");
	exit(EXIT_FAILURE);
}
/*--------------------------------------------------------------------------*/
unsigned char hdr[]={
//srf33制御ヘッダ
0x00,0x01,			//c_fatt	ファイル制御フラグ
0x00,0x00,			//c_pentry	エントリーアドレス
0x33,0x00,			//c_ver		srf33バージョン情報
0x00,0x03,			//c_scncnt	セクション情報数			//CODE,DATA,BSSが全て必須であり、空でも出力しなければならない。どれか1つでも足りないと、lk33.exeを実行した時にエラーになる。
0x00,0x00,0x00,0x10,		//c_scnptr	セクション情報チェーン
0x00,0x00,0x00,0x00,		//c_debptr	デバッグ制御情報チェーン
//セクション情報(CODE)
0x00,0x00,0x00,0x3C,		//s_nxptr	次のセクション情報へのチェーン
0x00,0x01,			//s_scntyp	セクションタイプ
0x00,0x00,			//s_lnktyp	リンク方法
0x00,0x01,			//s_scnatt	セクション属性
0x00,0x00,0x00,0x00,		//s_off		セクションのスタートアドレス
0x00,0x00,0x00,0x00,		//s_rcptr	リロケーション情報チェーン
0x00,0x00,0x00,0x00,		//s_rcsiz	リロケーション情報バイトサイズ
0x00,0x00,0x00,0x00,		//s_exptr	エクスターン情報チェーン		←←←エクスターンデータ有りの場合は、ここを 148          に書き換える。バイトオフセット=38。ビッグエンディアン。
0x00,0x00,0x00,0x00,		//s_exsiz	エクスターン情報バイトサイズ		←←←エクスターンデータ有りの場合は、ここを( 13+e_namsiz)に書き換える。バイトオフセット=42。ビッグエンディアン。
0x00,0x00,0x00,0x00,		//s_excnt	エクスターン情報の個数			←←←エクスターンデータ有りの場合は、ここを   1          に書き換える。バイトオフセット=46。ビッグエンディアン。
0x00,0x00,0x00,0x94,		//s_rdptr	実データへのチェーン			←←←エクスターンデータ有りの場合は、ここを(161+e_namsiz)に書き換える。バイトオフセット=50。ビッグエンディアン。
0x00,0x00,0x00,0x00,		//s_dsiz	実データバイトサイズ			←←←ここを書き換える。バイトオフセット=54、ビッグエンディアン。
0x00,0x00,			//s_scnndx	セクションID
//セクション情報(DATA)									┐
0x00,0x00,0x00,0x68,		//s_nxptr	次のセクション情報へのチェーン		│
0x00,0x02,			//s_scntyp	セクションタイプ			│
0x00,0x00,			//s_lnktyp	リンク方法				│
0x00,0x01,			//s_scnatt	セクション属性				│
0x00,0x00,0x00,0x00,		//s_off		セクションのスタートアドレス		│
0x00,0x00,0x00,0x00,		//s_rcptr	リロケーション情報チェーン		│
0x00,0x00,0x00,0x00,		//s_rcsiz	リロケーション情報バイトサイズ		│
0x00,0x00,0x00,0x00,		//s_exptr	エクスターン情報チェーン		│
0x00,0x00,0x00,0x00,		//s_exsiz	エクスターン情報バイトサイズ		│
0x00,0x00,0x00,0x00,		//s_excnt	エクスターン情報の個数			│
0x00,0x00,0x00,0x00,		//s_rdptr	実データへのチェーン			│
0x00,0x00,0x00,0x00,		//s_dsiz	実データバイトサイズ			│
0x00,0x01,			//s_scnndx	セクションID				│
//セクション情報(BSS)									│CODE,DATA,BSSが全て必須であり、空でも出力しなければならない。どれか1つでも足りないと、lk33.exeを実行した時にエラーになる。
0x00,0x00,0x00,0x00,		//s_nxptr	次のセクション情報へのチェーン		│
0x00,0x03,			//s_scntyp	セクションタイプ			│
0x00,0x00,			//s_lnktyp	リンク方法				│
0x00,0x01,			//s_scnatt	セクション属性				│
0x00,0x00,0x00,0x00,		//s_off		セクションのスタートアドレス		│
0x00,0x00,0x00,0x00,		//s_rcptr	リロケーション情報チェーン		│
0x00,0x00,0x00,0x00,		//s_rcsiz	リロケーション情報バイトサイズ		│
0x00,0x00,0x00,0x00,		//s_exptr	エクスターン情報チェーン		│
0x00,0x00,0x00,0x00,		//s_exsiz	エクスターン情報バイトサイズ		│
0x00,0x00,0x00,0x00,		//s_excnt	エクスターン情報の個数			│
0x00,0x00,0x00,0x00,		//s_rdptr	実データへのチェーン			│
0x00,0x00,0x00,0x00,		//s_dsiz	実データバイトサイズ			│
0x00,0x02,			//s_scnndx	セクションID				┘
//エクスターン情報									←←←エクスターンデータ無しの場合は、ここに実データを追加する。　バイトオフセット=148
0x00,0x00,0x00,0x00,		//e_scnoff	セクション内のオフセット
0x00,0x00,0x00,0x00,		//e_size	シンボルのサイズ
0x00,0x00,			//e_scnndx	参照するエクスターン情報が所属するセクションのID	//【注意】P/ECE開発環境のコンパイラマニュアル「S5U1C33000C Manual」(\usr\PIECE\docs\datasheet\EPSON\S5U1C33000C_J.pdf)の、「Appendix srf33ファイルの構造 A-1 srf33オブジェクトファイルの構造」(p.475〜479)には、p.478の「(4)エクスターン情報」の表の、e_scnndxが『4Byte』と記載されていますが、正しくは『2Byte』です。
0x00,0x01,			//e_extyp	エクスターンタイプ
0x00,				//e_namsiz	シンボル名の長さ			←←←エクスターンデータ有りの場合は、ここを書き換える。　　　　　バイトオフセット=160。終端にヌル文字を追加しない。
				//e_exnam	シンボル名				←←←エクスターンデータ有りの場合は、ここにシンボル名を追加する。バイトオフセット=161。終端にヌル文字を追加しない。
//実データ
				//							←←←エクスターンデータ有りの場合は、ここに実データを追加する。　バイトオフセット=(161+e_namsiz)
};
/*--------------------------------------------------------------------------*/
int subr(int argc, char* argv[]) {
	int opt, dsiz;
	FILE* fp;
	unsigned char* buf;
	char* sym = NULL;		//シンボル名が指定されたらここに記憶する。シンボル名が指定されなければNULLのままになる。
	char infile[_MAX_PATH];		//入力バイナリファイル名
	char outfile[_MAX_PATH];	//出力オブジェクトファイル名
	char drive[_MAX_DRIVE], dir[_MAX_DIR], fname[_MAX_FNAME], ext[_MAX_EXT];
	opterr = 0;	//オプションエラーは当プログラム内で処理するので、getopt()内でのエラー表示は不要。
	while((opt = getopt(argc, argv, "he:")) != -1) {
		switch(opt) {
		default:
		case 'h':
			usage();
			break;
		case 'e':
			if(sym) { usage(); }	//シンボル名を複数回指定してはいけない。
			sym = optarg;		//シンボル名を記憶する。
			break;
		}
	}
	//入力バイナリファイル名のみ指定された場合…
	if(optind == (argc - 1)) {
		//入力バイナリファイル名を格納します。
		strlcpy(infile, argv[optind + 0], sizeof infile);
		//出力オブジェクトファイル名を作成します。
		_splitpath(infile, drive, dir, fname, ext);
		_makepath(outfile, drive, dir, fname, ".o");
	//入力バイナリファイル名と出力オブジェクトファイル名が指定された場合…
	} else if(optind == (argc - 2)) {
		//入力バイナリファイル名を格納します。
		strlcpy(infile, argv[optind + 0], sizeof infile);
		//出力オブジェクトファイル名を格納します。
		strlcpy(outfile, argv[optind + 1], sizeof outfile);
		_splitpath(outfile, drive, dir, fname, ext);
		if(!ext[0]) { _makepath(outfile, drive, dir, fname, ".o"); }
	} else {
		usage();
	}
	//入力バイナリファイルを読み込みます。
	fp = fopen(infile, "rb");	//バイナリモード必須!!
	if(!fp) { error(EXIT_FAILURE, errno, "%s が開けません。", infile); }
	fseek(fp, 0, SEEK_END);
	dsiz = ftell(fp);
	rewind(fp);
	buf = malloc(dsiz);
	if(!buf) { DIE(); }
	fread(buf, 1, dsiz, fp);
	fclose(fp);
	//出力オブジェクトファイルを作成します。
	fp = fopen(outfile, "wb");	//バイナリモード必須!!
	if(!fp) { error(EXIT_FAILURE, errno, "%s が作成できません。", outfile); }
	//シンボル名が指定されていたら…
	if(sym) {
		//ヘッダを出力します。
		int namsiz = strlen(sym);
		int exsiz  =  13 + namsiz;
		int rdptr  = 161 + namsiz;
		PUT_BEWORD(hdr +  38, 148);	//s_exptr
		PUT_BEWORD(hdr +  42, exsiz);	//s_exsiz
		PUT_BEWORD(hdr +  46, 1);	//s_excnt
		PUT_BEWORD(hdr +  50, rdptr);	//s_rdptr
		PUT_BEWORD(hdr +  54, dsiz);	//s_dsiz
		PUT_BEBYTE(hdr + 160, namsiz);	//e_namsiz
		fwrite(hdr, 1, 161, fp);
		fwrite(sym, 1, namsiz, fp);
	//シンボル名が指定されていなければ…
	} else {
		//ヘッダを出力します。
		PUT_BEWORD(hdr +  54, dsiz);	//s_dsiz
		fwrite(hdr, 1, 148, fp);
	}
	//データを出力します。
	fwrite(buf, 1, dsiz, fp);
	//出力オブジェクトファイルを閉じます。
	fclose(fp);
	return EXIT_SUCCESS;
}
