/*
 *	app.c
 *
 *	dPrpTblD.exe - プロパティテーブルダンプユーティリティ
 *	Copyright (C) 2015-2018 Naoyuki Sawa
 *
 *	* Wed Sep 30 21:58:39 JST 2015 Naoyuki Sawa
 *	- 1st リリース。
 *	* Thu Oct 08 21:07:24 JST 2015 Naoyuki Sawa
 *	- レジストリ構造を変更しました。具体的には、以下の通りです。
 *	  ・範囲型のタイプ定義において、これまではPrp_rangeキーの下にminとmaxの値を格納していましたが、
 *	    変更後はPrp_rangeをBLOBとしてminとmaxを格納するようにしました。
 *	  ・列挙型のタイプ定義において、これまではPrp_enumキーの下にPrp_for及びPrp_revサブキーを作成しその中に順方向と逆方向の変換辞書を格納していましたが、
 *	    変更後はPrp_enumをBLOBとして列挙値を配列として格納しました。
 *	    辞書ではなくリニアサーチで変換するようになったため、列挙値が多いと性能低下しますが、大抵は列挙値は10個程度以下だと思うので問題無いと思います。
 *	  ・配列変数の定義において、これまではPrp_dimキーの下に各次元の(要素数-1)を格納していましたが、
 *	    変更後は変更後はPrp_dimをBLOBとして各次元の(要素数-1)を格納するようにしました。
 *	  以上のように変更した理由は、プロパティテーブルの読み書き時のレジストリ検索回数を減らして、高速化するためです。
 *	- 上記の変更に伴い、当ツールをリビルドしました。
 *	  処理の変更はPrpTbl_dump()の中で吸収されていますので、当ツール自体のソースには変更有りません。
 *	* Mon Oct 19 21:21:27 JST 2015 Naoyuki Sawa
 *	- clipprp.cに属性を追加した事に伴い、レジストリ構造が変わったため、当ツールをリビルドしました。
 *	  当ツールは属性を表示しないので、当ツールの動作自体には変更有りません。
 *	* Mon Oct 19 22:41:11 JST 2015 Naoyuki Sawa
 *	- clipprp.cがレジストリ構造を変更して、PrpTblキーの下のtypeサブキーとtableサブキーを削除した事に伴い、当ツールもリビルドしました。
 *	  当ツールのソースには変更は有りませんが、clipprp.cの関数の変更を反映するためです。
 *	* Thu Oct 29 21:30:16 JST 2015 Naoyuki Sawa
 *	- clipreg.cモジュールを修正した事に伴い、当ツールもリビルドしました。
 *	  当ツールのソースは、変更していません。
 *	* Sun Nov 29 21:58:15 JST 2015 Naoyuki Sawa
 *	- 「Boehm GC」をマルチスレッド対応にした事,及び,それに伴ってclipbpe.cモジュールを修正した事に伴い、当ツールもプロジェクト設定の変更を行ってリビルドしました。
 *	  詳細は、/clip/tool/gc/README.TXT と /clip/clipbpe.c の2015/11/03のコメントを参照して下さい。
 *	- '-a'オプションを追加しました。
 *	* Mon Nov 30 21:58:52 JST 2015 Naoyuki Sawa
 *	- clipprp.cのPrpTbl_dump(),PrpTbl_dump_abs(),PrpHdr_dump(),PrpHdr_dump_abs()が、説明文も表示するようにした事に伴い、当ツールもリビルドしました。
 *	  当ツール自体のソースには変更有りません。
 *	* Mon Dec 07 21:18:38 JST 2015 Naoyuki Sawa
 *	- PrpTbl_dump()とPrpTbl_dump_abs()を統合した事に伴い、当ツールからの呼び出し方法も変更しました。
 *	  当ツールの動作内容自体は、変更有りません。
 *	* Thu Dec 10 21:13:28 JST 2015 Naoyuki Sawa
 *	- '-v'オプションを追加しました。
 *	* Tue Dec 29 21:41:52 JST 2015 Naoyuki Sawa
 *	- range型,及び,enum型の値の範囲を、これまでは符号無し16ビット(0～UINT16_MAX)でしたが、符号付き16ビット(INT16_MIN～INT16_MAX)に変更しました。
 *	  詳細は、dPrpTblC/dPrpTblC.exeの同日のコメントを参照して下さい。
 *	- 上記の変更に伴い、当ツールもリビルドしました。
 *	  当ツールのソースには変更は有りませんが、clipprp.cの関数の変更を反映するためです。
 *	* Sat Jan 02 21:26:28 JST 2016 Naoyuki Sawa
 *	- <"書式文字列">と<書式番号>の同時指定は不可としました。
 *	  詳細は、/clip/clipprp.h の同日のコメントを参照して下さい。
 *	- 上記の変更に伴い、当ツールもリビルドしました。
 *	  当ツールのソースには変更は有りません。
 *	* Sun Feb 21 21:02:01 JST 2016 Naoyuki Sawa
 *	- libclipのライブラリ化の伴い、以下の変更を行いました。
 *	  ・extclip.hとextclip.cを削除しました。extclip.hとextclip.cはlibclipに含まれるようになったからです。
 *	  ・"CLiP Files"を削除しました。CLiPライブラリの各モジュールはlibclipに含まれるようになったからです。
 *	  当ツールの動作には変更有りません。
 *	* Sun Feb 28 23:18:54 JST 2016 Naoyuki Sawa
 *	- range型の範囲の制限を緩和した事に伴い、ライブラリの更新を反映するために、当ツールもリビルドしました。
 *	  当ツールのソースには変更は有りません。
 *	  詳細は、/clip/tool/dPrpTblC/winapp.h と /clip/clipprp.h の同日のコメントを参照して下さい。
 *	* Tue May 09 22:14:45 JST 2017 Naoyuki Sawa
 *	- ライブラリのバグ修正に伴う、ツールのリビルドを行いました。
 *	  詳細は、clipmisc.cの'Sun May 07 09:13:01 JST 2017 Naoyuki Sawa'のコメントを参照して下さい。
 *	- 前回更新後～本日までの間に、他のツール群に対して行ったのと同じビルド設定の変更を、当ツールにも対しても適用しました。
 *	* Thu May 03 23:59:59 JST 2018 Naoyuki Sawa
 *	- Boehm GCを修正した事に伴い、当ツールもリビルドしました。
 *	  詳細は、/clip/tool/gc/README.TXTの'Wed May 02 23:59:59 JST 2018 Naoyuki Sawa'のコメントを参照して下さい。
 */
#include "app.h"
#define VERSION		"20180503"	//最終更新日
/****************************************************************************
 *	ローカル関数宣言
 ****************************************************************************/
static int subr(int argc, char* argv[]);
__declspec(noreturn) static void usage();
/****************************************************************************
 *	グローバル関数
 ****************************************************************************/
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;
}
/****************************************************************************
 *	ローカル関数
 ****************************************************************************/
static int subr(int argc, char* argv[]) {
	int opt, n, ofs_prptbl_bin, ofs_regtbl_bin, iPrpKey, iTbl;
	char *endptr, *fnm_prptbl_bin, *fnm_regtbl_bin;
	unsigned char *buf_prptbl_bin, *buf_regtbl_bin;
	const unsigned char* pPrpKey;
	FILE* fp;
	int fOpt = 0;
	//コマンドラインオプションを処理する。
	opterr = 0;	//オプションエラーは当プログラム内で処理するので、getopt()内でのエラー表示は不要。
	while((opt = getopt(argc, argv, "ah")) != -1) {
		switch(opt) {
		case 'a':
			fOpt |= PrpDmp_Abs;
			break;
		case 'v':
			fOpt |= PrpDmp_Vbs;
			break;
		case 'h':
		default:
			usage();
			break;
		}
	}
	//プロパティテーブルファイル名を取得する。
	if(optind >= argc) { usage(); }
	fnm_prptbl_bin = argv[optind++];
	//プロパティテーブルオフセットを取得する。
	ofs_prptbl_bin = 0;
	if(optind < argc) {
		if(*argv[optind] == '+') {
			ofs_prptbl_bin = strtoul(argv[optind++] + 1/*'+'*/, &endptr, 0);
			if(*endptr) { usage(); }
		}
	}
	//レジストリテーブルファイル名を取得する。
	if(optind >= argc) { usage(); }
	fnm_regtbl_bin = argv[optind++];
	//レジストリテーブルオフセットを取得する。
	ofs_regtbl_bin = 0;
	if(optind < argc) {
		if(*argv[optind] == '+') {
			ofs_regtbl_bin = strtoul(argv[optind++] + 1/*'+'*/, &endptr, 0);
			if(*endptr) { usage(); }
		}
	}
	//RegKey_PrpTblの値を取得する。
	if(optind >= argc) { usage(); }
	iPrpKey = strtoul(argv[optind++], &endptr, 0);
	if(*endptr) { usage(); }
	//テーブル名の値を取得する。
	if(optind >= argc) { usage(); }
	iTbl = strtoul(argv[optind++], &endptr, 0);
	if(*endptr) { usage(); }
	//余計な引数が残っていたら、使い方を表示して終了する。
	if(optind != argc) { usage(); }
	//プロパティテーブルファイルを読み込む。
	fp = fopen(fnm_prptbl_bin, "rb");	//バイナリモード
	if(!fp) { error(EXIT_FAILURE, errno, "%s が開けません。", fnm_prptbl_bin); }
	fseek(fp, 0, SEEK_END);
	n = ftell(fp);
	if((unsigned)ofs_prptbl_bin >= (unsigned)n) { error(EXIT_FAILURE, errno, "%s のオフセットが不正です。", fnm_prptbl_bin); }
	buf_prptbl_bin = malloc(n - ofs_prptbl_bin);
	if(!buf_prptbl_bin) { DIE(); }
	fseek(fp, ofs_prptbl_bin, SEEK_SET);
	fread(buf_prptbl_bin, 1, n - ofs_prptbl_bin, fp);
	fclose(fp);
	//レジストリテーブルファイルを読み込む。
	fp = fopen(fnm_regtbl_bin, "rb");	//バイナリモード
	if(!fp) { error(EXIT_FAILURE, errno, "%s が開けません。", fnm_regtbl_bin); }
	fseek(fp, 0, SEEK_END);
	n = ftell(fp);
	if((unsigned)ofs_regtbl_bin >= (unsigned)n) { error(EXIT_FAILURE, errno, "%s のオフセットが不正です。", fnm_regtbl_bin); }
	buf_regtbl_bin = malloc(n - ofs_regtbl_bin);
	if(!buf_regtbl_bin) { DIE(); }
	fseek(fp, ofs_regtbl_bin, SEEK_SET);
	fread(buf_regtbl_bin, 1, n - ofs_regtbl_bin, fp);
	fclose(fp);
	//プロパティテーブルキーを取得する。
	pPrpKey = REG_open_key(buf_regtbl_bin, iPrpKey);
	if(!pPrpKey) { error(EXIT_FAILURE, errno, "%s のプロパティテーブルキーが取得出来ません。", fnm_regtbl_bin); }
	//プロパティテーブルをダンプ表示する。
	PrpTbl_dump(stdout, pPrpKey, iTbl, buf_prptbl_bin, 0, NULL, fOpt);
	//メモリを開放する。
	free(buf_prptbl_bin);
	free(buf_regtbl_bin);
	return EXIT_SUCCESS;
}
/*---------------------------------------------------------------------------*/
//使い方を表示して終了する。
__declspec(noreturn) static void usage() {
	fprintf(stderr, "dPrpTblD.exe - プロパティテーブルダンプユーティリティ (%s)\n", VERSION);
	fprintf(stderr, "Copyright (C) 2015-2018 Naoyuki Sawa\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "USAGE:\n");
	fprintf(stderr, "  dPrpTblD.exe [option ...] prptbl.bin [+[0x]offset] regtbl.bin [+[0x]offset] [0x]iPrpKey [0x]iTbl\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "  prptbl.bin [+[0x]offset] プロパティテーブルファイル名。ファイル先頭からのバイトオフセットも指定出来ます。\n");
	fprintf(stderr, "  regtbl.bin [+[0x]offset] レジストリテーブルファイル名。ファイル先頭からのバイトオフセットも指定出来ます。\n");
	fprintf(stderr, "  [0x]iPrpKey              RegKey_PrpTblの値。RegTbl.hを参照して下さい。\n");
	fprintf(stderr, "  [0x]iTbl                 テーブル名の値(PrpTbl_～)。PrpTbl.hを参照して下さい。\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "OPTIONS:\n");
	fprintf(stderr, "  -a  絶対パス形式でダンプします。このオプションを指定しなければ階層形式でダンプします。\n");
	fprintf(stderr, "  -v  各行の先頭の二列に、変数のビット位置と変数の型を出力します。\n");
	fprintf(stderr, "  -h  使い方を表示します。\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "EXAMPLE:\n");
	fprintf(stderr, "  □sample.sym\n");
	fprintf(stderr, "  │ptTableX   work.o   bss  local  00112340\n");
	fprintf(stderr, "  │TBL_RegTbl RegTbl.o code global 00100ABC\n");
	fprintf(stderr, "  □RegTbl.h\n");
	fprintf(stderr, "  │#define RegKey_PrpTbl 23\n");
	fprintf(stderr, "  □PrpTbl.h\n");
	fprintf(stderr, "  │#define PrpTbl_TableX 4\n");
	fprintf(stderr, "  ①上記のptTableXの内容をダンプするには、以下のように実行して下さい。\n");
	fprintf(stderr, "  │isd.exe w* core. 100000 13FFFF\n");
	fprintf(stderr, "  │dPrpTblD.exe core +0x12340 core +0xABC 23 4\n");
	fprintf(stderr, "  ②RegTbl.binを保存してある場合は、以下のように実行する事も出来ます。\n");
	fprintf(stderr, "  │isd.exe w* core. 100000 13FFFF\n");
	fprintf(stderr, "  │dPrpTblD.exe core +0x12340 RegTbl.bin 23 4\n");
	exit(EXIT_FAILURE);
}
