/*
 *	dlib33 - lib33ライブラリファイル情報表示ユーティリティ
 *	Copyright(C) 2006 Naoyuki Sawa
 *
 *	* Sun Jun 04 13:01:00 JST 2006 Naoyuki Sawa
 *	- 作成開始。
 *	* Mon Jun 05 05:45:00 JST 2006 Naoyuki Sawa
 *	- 1st リリース。
 */
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>

#define VERSION		"20060604"

#define BEWORD(p)	((unsigned int)(((unsigned char*)(p))[0]<<24|((unsigned char*)(p))[1]<<16|((unsigned char*)(p))[2]<<8|((unsigned char*)(p))[3]<<0))

const char sig_lib33[] = { '!', '<', 'l', 'i', 'b', '3', '3', '>', '\0' };	/* ライブラリヘッダシグネチャ */
const char sig_symdef[] = { '\x06', 'S', 'Y', 'M', 'D', 'E', 'F' };		/* シンボルテーブルシグネチャ */

/* エラーメッセージを表示して、終了します。
 */
void
die(const char* fmt, ...)
{
	va_list ap;
	va_start(ap, fmt);
	 fprintf(stderr, "### ");
	vfprintf(stderr, fmt, ap);
	 fprintf(stderr, "\n");
	va_end(ap);
	exit(-1);
}

/* 使い方を表示して、終了します。
 */
void
usage()
{
	fprintf(stderr, "dlib33 - lib33ライブラリファイル情報表示ユーティリティ (%s)\n", VERSION);
	fprintf(stderr, "Copyright(C) 2006 Naoyuki Sawa\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "USAGE: dlib33 filename[.lib]\n");
	exit(-1);
}

int
main(int argc, char* argv[])
{
	int retval;
	//
	char path[_MAX_PATH];
	char drive[_MAX_DRIVE];
	char dir[_MAX_DIR];
	char fname[_MAX_FNAME];
	char ext[_MAX_EXT];
	//
	FILE* fp;
	//
	unsigned char* lib;	/* ライブラリファイルを読み込むバッファ */
	int lib_size;		/* ライブラリファイルサイズ */
	//
	int pos;		/* 走査用 */
	//
	int symdef_start;	/* シンボルテーブルの先頭位置 */
	int symdef_end;		/* シンボルテーブルの終端位置 */
	int symdef_size;	/* シンボルテーブルサイズ */
	//
	int syminfo_start;	/* シンボル情報テーブルの先頭位置 */
	int syminfo_end;	/* シンボル情報テーブルの終端位置 */
	int syminfo_size;	/* シンボル情報テーブルサイズ */
	//
	int symname_start;	/* シンボル名称テーブルの先頭位置 */
	int symname_end;	/* シンボル名称テーブルの終端位置 */
	int symname_size;	/* シンボル名称テーブルサイズ */
	//
	int module_start;	/* モジュールテーブルの先頭位置 */
	int module_end;		/* モジュールテーブルの終端位置 */
	int modbody_size;	/* モジュール本体サイズ */
	int modname_size;	/* モジュール名称サイズ */
	//
	int i_symbol;		/* シンボルインデクス */
	int modname_pos;	/* モジュール名称位置 */
	int symname_pos;	/* シンボル名称位置 */
	//
	int i;			/* 作業用 */
	int tmp_pos;		/* 作業用 */
	int i_symbol_width;	/* シンボルインデクスの出力幅 */
	int symname_width;	/* シンボル名称の出力幅 */

	/*--------------------------------------------------------------------------------------------------------------*/
	/*	読み込み												*/
	/*--------------------------------------------------------------------------------------------------------------*/

	/* ライブラリファイルを開きます。 */
	if(argc != 1 + 1) {
		usage();
	}
	_splitpath(argv[1], drive, dir, fname, ext);
	if(!strlen(ext)) {
		strcpy(ext, ".lib");
	}
	_makepath(path, drive, dir, fname, ext);
	fp = fopen(path, "rb");
	if(!fp) {
		die("%sが開けません。", path);
	}

	/* ライブラリファイルサイズを取得します。 */
	retval = fseek(fp, 0, SEEK_END);
	if(retval < 0) {
		die("ライブラリファイルサイズの取得が失敗しました。");
	}
	lib_size = ftell(fp);
	if(lib_size < 0) {
		die("ライブラリファイルサイズの取得が失敗しました。");
	}
	rewind(fp); /* 忘れないで!! */

	/* ライブラリファイルを読み込むバッファを確保します。 */
	lib = malloc(lib_size);
	if(!lib) {
		die("ライブラリファイルを読み込むバッファが確保できません。");
	}

	/* ライブラリファイルを読み込みます。 */
	retval = fread(lib, 1, lib_size, fp);
	if(retval != lib_size) {
		die("ライブラリファイルの読み込みが失敗しました。");
	}

	/* ライブラリファイルを閉じます。 */
	fclose(fp);

	/*--------------------------------------------------------------------------------------------------------------*/
	/*	構造確認												*/
	/*--------------------------------------------------------------------------------------------------------------*/

	pos = 0;

	/* ライブラリヘッダシグネチャを確認します。 */
	if((pos + (int)sizeof sig_lib33 > lib_size) ||
	   (memcmp(lib + pos, sig_lib33, sizeof sig_lib33) != 0)) {
		die("ライブラリヘッダシグネチャが不正です。");
	}
	pos += sizeof sig_lib33;

	/* シンボルテーブルサイズを取得します。 */
	if(pos + 4 > lib_size) {
		die("シンボルテーブルサイズが取得できません。");
	}
	symdef_size = BEWORD(lib + pos);
	if(symdef_size < 0) {
		die("シンボルテーブルサイズが不正です。");
	}
	pos += 4;

	/* シンボルテーブルシグネチャを確認します。 */
	if((pos + (int)sizeof sig_symdef > lib_size) ||
	   (memcmp(lib + pos, sig_symdef, sizeof sig_symdef) != 0)) {
		die("シンボルテーブルシグネチャが不正です。");
	}
	pos += sizeof sig_symdef;

	/* シンボルテーブルの先頭を取得します。 */
	symdef_start = pos;

	/* シンボルテーブルの終端を取得します。 */
	symdef_end = pos + symdef_size;
	if(symdef_end > lib_size) {
		die("シンボルテーブルサイズが不正です。");
	}

	/* シンボル情報テーブルサイズを取得します。 */
	if(pos + 4 > symdef_end) {
		die("シンボル情報テーブルサイズが取得できません。");
	}
	syminfo_size = BEWORD(lib + pos);
	if(syminfo_size < 0) {
		die("シンボル情報テーブルサイズが不正です。");
	}
	pos += 4;

	/* シンボル情報テーブルの先頭を取得します。 */
	syminfo_start = pos;

	/* シンボル情報テーブルをスキップします。 */
	pos += syminfo_size;

	/* シンボル情報テーブルの終端を取得します。 */
	syminfo_end = pos;
	if(syminfo_end > symdef_end) {
		die("シンボル情報テーブルサイズが不正です。");
	}

	/* シンボル名称テーブルサイズを取得します。 */
	if(pos + 4 > symdef_end) {
		die("シンボル名称テーブルサイズが取得できません。");
	}
	symname_size = BEWORD(lib + pos);
	if(symname_size < 0) {
		die("シンボル名称テーブルサイズが不正です。");
	}
	pos += 4;

	/* シンボル名称テーブルの先頭を取得します。 */
	symname_start = pos;

	/* シンボル名称テーブルをスキップします。 */
	pos += symname_size;

	/* シンボル名称テーブルの終端を取得します。 */
	symname_end = pos;
	if(symname_end > symdef_end) {
		die("シンボル名称テーブルサイズが不正です。");
	}

	/* シンボルテーブルの終端が、シンボル名称テーブルの終端に一致することを確認します。 */
	if(symdef_end != symname_end) {
		die("シンボルテーブルの終端が不正です。");
	}

	/* モジュールテーブルの先頭を取得します。 */
	module_start = pos;

	/* 各モジュールについて... */
	while(pos < lib_size) {
		/* モジュール本体サイズを取得します。 */
		if(pos + 4 > lib_size) {
			die("モジュール本体サイズが取得できません。");
		}
		modbody_size = BEWORD(lib + pos);
		if(modbody_size < 0) {
			die("モジュール本体サイズが不正です。");
		}
		pos += 4;

		/* モジュール名称サイズを取得します。 */
		if(pos + 1 > lib_size) {
			die("モジュール名称サイズが取得できません。");
		}
		modname_size = lib[pos++];
		if(modname_size < 0) {
			die("モジュール名称サイズが不正です。");
		}

		/* モジュール名称とモジュール本体をスキップします。 */
		pos += modname_size + modbody_size;
		if(pos > lib_size) {
			die("モジュール名称サイズ、または、モジュール本体サイズが不正です。");
		}
	}

	/* モジュールテーブルの終端を取得します。 */
	module_end = pos;

	/* モジュールテーブルの終端が、ライブラリファイルの終端に一致することを確認します。 */
	if(module_end != lib_size) {
		die("モジュールテーブルの終端が不正です。");
	}

	/*--------------------------------------------------------------------------------------------------------------*/
	/*	情報表示												*/
	/*--------------------------------------------------------------------------------------------------------------*/

	i_symbol_width = 1;
	symname_width = 1;

	/* 一周目で出力幅を決定し、二周目で出力します。 */
	for(i = 0; i < 2; i++) {

		/* 各シンボルについて... */
		for(i_symbol = 0, pos = syminfo_start; pos < syminfo_end; i_symbol++) {

			/* モジュール位置を取得します。 */
			if(pos + 4 > syminfo_end) {
				die("モジュール位置が取得できません。");
			}
			tmp_pos = BEWORD(lib + pos); /* ファイル先頭からの絶対位置で格納されています */
			pos += 4;

			/* モジュール名称サイズとモジュール名称位置を取得します。 */
			tmp_pos += 4/*モジュール本体サイズ*/;
			if(tmp_pos < module_start) {
				die("モジュール位置が不正です。");
			}
			if(tmp_pos + 1/*モジュール名称サイズ*/ > module_end) {
				die("モジュール位置が不正です。");
			}
			modname_size = lib[tmp_pos++];
			if(modname_size < 0) {
				die("モジュール位置が不正です。");
			}
			modname_pos = tmp_pos;
			if(modname_pos + modname_size > module_end) {
				die("モジュール位置が不正です。");
			}

			/* シンボル名称オフセットを取得します。 */
			if(pos + 4 > syminfo_end) {
				die("シンボル名称位置が取得できません。");
			}
			tmp_pos = BEWORD(lib + pos); /* シンボル名称テーブル先頭からの相対位置で格納されています */
			pos += 4;

			/* シンボル名称位置を取得します。 */
			symname_pos = symname_start + tmp_pos;
			if((symname_pos < symname_start) ||
			   (symname_pos > symname_end - 1)) {
				die("シンボル名称位置が不正です。");
			}

			if(!i) { /* 一周目 */
				/* シンボル名称の出力幅を求めます。 */
				int len = strlen(lib + symname_pos);
				if(len > symname_width) {
					symname_width = len;
				}
			} else { /* 二周目 */
				/* シンボルインデクス、シンボル名称、モジュール名称を表示します。 */
				printf("%*d: %-*s %.*s\n",
					i_symbol_width, i_symbol,
					symname_width, lib + symname_pos,
					modname_size, lib + modname_pos);
			}
		}

		if(!i) { /* 一周目 */
			/* シンボルインデクスの出力幅を求めます。 */
			for(;;) {
				i_symbol /= 10;
				if(!i_symbol) {
					break;
				}
				i_symbol_width++;
			}
		}
	}

	return 0;
}
