//////////////////////////////////////////////////////////
// CRESLIST.h
// CRESLISTクラス定義ファイル
// 概要：実行可能ファイルのリソースを調べ、一覧を出力する
//////////////////////////////////////////////////////////
#include	<strsafe.h>

#define 	RTTMAX	21	//種類テーブル配列数
#define 	LTMAX	6	//言語テーブル配列数

/////////////
//構造体定義
/////////////
typedef struct _RT_TABLE {	//リソースタイプ（RT）－種類テーブル
	TCHAR* macro;
	UINT id;
	TCHAR* name;
} RT_TABLE, *PRT_TABLE;

typedef struct _LOC_TABLE {	//ローカライゼィション－言語テーブル
	TCHAR* area;
	TCHAR* tag;
	UINT id_hex;
	UINT id;
} LOC_TABLE, *PLOC_TABLE;

///////////////////////////////
// CRESLISTクラス定義ファイル
// Copyright (c) August, 2024
//        By Ysama
///////////////////////////////
class CRESLIST
{
private:
	HMODULE m_hExe;							//EXEファイルのハンドル
	TCHAR m_ResFileName[MAX_PATH] = {'\0'};	//リソースファイル名（拡張子前FileName + "_resinfo.txt"）
	TCHAR* m_Data;							//リソースリストのデータポインター
	size_t m_DataLen;						//m_Dataの長さ
	RT_TABLE m_rtt[RTTMAX] = {
		//	マクロ名		整数ID						説明
		{"RT_CURSOR",		1,							"ハードウェア依存カーソル"},
		{"RT_BITMAP",		2,							"ビットマップ"},
		{"RT_ICON",			3,							"ハードウェア依存するアイコン"},
		{"RT_MENU",			4,							"メニュー"},
		{"RT_DIALOG",		5,							"ダイアログボックス"},
		{"RT_STRING",		6,							"ストリングテーブル"},
		{"RT_FONTDIR",		7,							"フォントディレクトリ"},
		{"RT_FONT",			8,							"フォント"},
		{"RT_ACCELERATOR",	9,							"アクセラレータ"},
		{"RT_RCDATA",		10,							"アプリケーション定義リソース"},
		{"RT_MESSAGETABLE",	11,							"メッセージテーブル"},
		{"RT_VERSION",		16,							"バージョン"},
		{"RT_DLGINCLUDE",	17,							"文字列関連付け"},
		{"RT_PLUGPLAY",		19,							"プラグアンドプレイ"},
		{"RT_VXD",			20,							"Vxd"},
		{"RT_ANICURSOR",	21,							"アニメーションカーソル"},
		{"RT_ANIICON",		22,							"アニメーションアイコン"},
		{"RT_HTML",			23,							"HTML"},
		{"RT_MANIFEST",		24,							"アセンブリ マニフェスト"},
		{"RT_GROUP_CURSOR",	(ULONG_PTR(RT_CURSOR) + 11),"ハードウェア非依存カーソル"},
		{"RT_GROUP_ICON",	(ULONG_PTR(RT_ICON) + 11),	"ハードウェア非依存アイコン"}
	};
	LOC_TABLE m_lt[LTMAX] = {
	//	言語/地域					言語/地域タグ	言語/地域ID		言語/地域10進ID
		{"中国語 (繁体字、台湾)",	"zh-TW",		0x0404,			1028},
		{"英語 (米国)",				"en-US",		0x0409,			1033},
		{"日本語 (日本)",			"ja-JP",		0x0411,			1041},
		{"韓国語 (韓国)",			"ko-KR",		0x0412,			1042},
		{"中国語 (簡体字、中国)",	"zh-CN",		0x0804,			2052},
		{"英語 (英国)",				"en-GB",		0x0809,			2057}
	};
public:
	CRESLIST();						//コンストラクター1
	CRESLIST(LPCTSTR);				//コンストラクター2
	~CRESLIST();					//デストラクター
	void Init();					//初期化処理
	bool BeginResList(LPCTSTR);		//実行可能ファイル名を与えて開始
	bool AddData(LPCTSTR, size_t);	//m_Dataに長さsize_tの文字列を追加する
	LPCTSTR GetData() {return m_Data;}	//m_Dataをコピーする(呼び出し側でdelete [] destが必須）
	bool SaveList();				//現在開いているファイルのリソースリストを保存する
	static bool EnumTypesFunc(HMODULE, LPCTSTR, LONG);					//リソース種類取得用コールバック関数
	static bool EnumNamesFunc(HMODULE, LPCTSTR, LPCTSTR, LONG);			//リソース名取得用コールバック関数
	static bool EnumLangsFunc(HMODULE, LPCTSTR, LPCTSTR, WORD, LONG);	//リソース言語取得用コールバック関数
};

//コンストラクター1
CRESLIST::CRESLIST() {

	Init();		//初期化処理
}

//コンストラクター2
CRESLIST::CRESLIST(LPCTSTR FileName) {

	Init();					//初期化処理
	BeginResList(FileName);	//実行可能ファイル名を与えて開始
}

//デストラクター
CRESLIST::~CRESLIST() {

	//「念の為」処理
	Init();
}

//初期化処理
void CRESLIST::Init() {

	if(*m_ResFileName) {
		//ロードした実行可能ファイルをアンロード
		FreeLibrary(m_hExe);
		//リソースリストのデータを開放
		delete [] m_Data;
		//リソース情報ファイルパス名を初期化
		m_ResFileName[0] = TCHAR('\0');
	}
	m_hExe = NULL;
	m_Data = new TCHAR[1] {'\0'};
	m_DataLen = 1;
}

//実行可能ファイル名を与えて開始
bool CRESLIST::BeginResList(LPCTSTR FileName) {

	TCHAR szBuffer[MAX_PATH];		//infoファイル用プリントバッファ
	size_t cbString;				//szBufferの文字列長
	HRESULT hResult;				//結果判定用変数

	//既に実行可能ファイルをロードしていれば一旦終了させる
	if(*m_ResFileName) {
		Init();
	}
	//szBuffer名の設定
	lstrcpyn(TEXT(m_ResFileName), TEXT(FileName), lstrlen(TEXT(FileName)) - 3);	//".exe"は4文字だが、lstrcpynはNULLを含むので(-4 + 1)となる
	lstrcat(TEXT(m_ResFileName), TEXT("_resinfo.txt"));
	//リソースを列挙する実行可能ファイルをロードする
	m_hExe = LoadLibrary(TEXT(FileName));
	if(m_hExe == NULL)
		return FALSE;
	//リストファイルデータの作成開始
	hResult = StringCchPrintf(szBuffer, MAX_PATH,
		TEXT("%s\r\nのファイルには以下のリソースがあります。\r\n"), FileName);
	if(FAILED(hResult))
		return FALSE;
	hResult = StringCchLength(szBuffer, MAX_PATH, &cbString);
	if(FAILED(hResult))
		return FALSE;
	//m_Dataの更新
	AddData(szBuffer, cbString);
	//リソース種類の列挙を行う
	EnumResourceTypes(m_hExe,				//読み込みモジュールのハンドル
		(ENUMRESTYPEPROC)EnumTypesFunc,		//コールバックファンクションアドレス
		(LONG)this);						//補足パラメーター
	return TRUE;
}

//m_Dataに文字列を追加する
bool CRESLIST::AddData(LPCTSTR text, size_t length) {

	bool flag = FALSE;
	//m_Dataの更新
	m_DataLen += length;				//mDataLen（NULL織り込み済）にtextの長さlengthを追加
	TCHAR* str = new TCHAR[m_DataLen];	//暫定文字列
	HRESULT res = StringCchCopy(str, m_DataLen, m_Data);	//旧m_Dataをコピー
	if(res == S_OK) {
		res = StringCchCat(str, m_DataLen, text);	//textを追加
		if(res == S_OK) {
			delete [] m_Data;						//旧m_Dataを解放
			m_Data = new TCHAR[m_DataLen];			//再初期化
			StringCchCopy(m_Data, m_DataLen, str);	//m_Dataにstrをコピー（手抜きでエラー処理なし）
			flag = TRUE;
		}
		else
			m_DataLen -= length;
	}
	else
		m_DataLen -= length;
	delete [] str;						//暫定文字列strを解放
	return flag;
}

//現在開いているファイルのリソースリストを保存する
bool CRESLIST::SaveList() {

	HANDLE hFile;									//szBufferハンドル
	hFile = CreateFile(TEXT(TEXT(m_ResFileName)),	//リソースファイル名
		GENERIC_READ | GENERIC_WRITE,				//アクセスモード
		0,											//シェアモード
		(LPSECURITY_ATTRIBUTES) NULL,				//デフォルトセキュリティ
		CREATE_ALWAYS,								//フラグの作成
		FILE_ATTRIBUTE_NORMAL,						//ファイルアトリビュート
		(HANDLE) NULL);								//テンプレート無し
	if(hFile == INVALID_HANDLE_VALUE)
		return FALSE;
	DWORD cbWritten;					//resource infoファイルへの書き込みサイズ
	WriteFile(hFile,					//resource infoを書き込むファイルハンドル
		m_Data,							//書き込みデータ
		(DWORD)m_DataLen,				//書き込みサイズ
		&cbWritten,						//書き込みバイト数
		NULL);							//オーバーラップドI/Oは無し
	CloseHandle(hFile);
	return TRUE;
}

//リソース種類取得用コールバック関数EnumTypesFunc(HMODULE, LPCTSTR, LONG)
bool CRESLIST::EnumTypesFunc(HMODULE hModule, LPCTSTR lpType, LONG lParam) {

	TCHAR szBuffer[MAX_PATH] = {'\0'};	//infoファイル用プリントバッファ
	size_t cbString;					//szBufferの文字列長
	HRESULT hResult;					//結果判定用変数

	//szBufferにリソース種類を書き込む
	//種類は文字列または符号なし整数であり、事前にテストを行う
	if(!IS_INTRESOURCE(lpType)) {
		hResult = StringCchPrintf(szBuffer, MAX_PATH, TEXT("種類：%s\r\n"), lpType);
		if(FAILED(hResult))
			return FALSE;
	}
	else
	{
		//m_rrt配列内リソース種類検索
		for(int i = 0; i < RTTMAX; i++) {
			if((UINT)lpType == ((CRESLIST*)lParam)->m_rtt[i].id) {
				StringCchCopy(szBuffer, MAX_PATH, TEXT("種類："));
				StringCchCat(szBuffer, MAX_PATH, ((CRESLIST*)lParam)->m_rtt[i].name);
				StringCchCat(szBuffer, MAX_PATH, TEXT("\r\n"));
				break;
			}
		}
		if(!*szBuffer) {	//szBuffer[0]が初期値（NULL）の場合
			hResult = StringCchPrintf(szBuffer, MAX_PATH, TEXT("種類：%u\r\n"), (USHORT)lpType);
			if(FAILED(hResult))
				return FALSE;
		}
	}
	hResult = StringCchLength(szBuffer, MAX_PATH, &cbString);
	if(FAILED(hResult))
		return FALSE;
	//m_Dataの更新
	((CRESLIST*)lParam)->AddData(szBuffer, cbString);
	//同種のリソースを総て検索する
	EnumResourceNames(hModule, lpType, (ENUMRESNAMEPROC)EnumNamesFunc, lParam);
	return TRUE;
}

//リソース名取得用コールバック関数EnumNamesFunc(HMODULE, LPCSTR, LPCSTR, LONG)
bool CRESLIST::EnumNamesFunc(HMODULE hModule, LPCTSTR lpType, LPCTSTR lpName, LONG lParam) {

	TCHAR szBuffer[MAX_PATH] = {'\0'};	//infoファイル用プリントバッファ
	size_t cbString;					//szBufferの文字列長
	HRESULT hResult;					//結果判定用変数

	//szBufferにリソース名を書き込む
	//リソース名は文字列または符号なし整数であり、事前にテストを行う
	if(!IS_INTRESOURCE(lpName)) {
		hResult = StringCchPrintf(szBuffer, MAX_PATH, TEXT("\tリソース名：%s\r\n"), lpName);
		if(FAILED(hResult))
			return FALSE;
	}
	else
	{
		hResult = StringCchPrintf(szBuffer, MAX_PATH, TEXT("\tリソース名：%s\r\n"), (USHORT)lpName);
		if(FAILED(hResult))
			return FALSE;
	}
	hResult = StringCchLength(szBuffer, MAX_PATH, &cbString);
	if(FAILED(hResult))
		return FALSE;
	//m_Dataの更新
	((CRESLIST*)lParam)->AddData(szBuffer, cbString);
	//同種のリソースの言語を総て検索する
	EnumResourceLanguages(hModule, lpType, lpName, (ENUMRESLANGPROC)EnumLangsFunc, lParam);
	return TRUE;
}

//リソース言語取得用コールバック関数EnumLangsFunc(HMODULE, LPCTSTR, LPCTSTR, WORD, LONG)
bool CRESLIST::EnumLangsFunc(HMODULE hModule, LPCTSTR lpType, LPCTSTR lpName, WORD wLang, LONG lParam) {

	HRSRC hResInfo;						//リソース情報ハンドル
	TCHAR szBuffer[MAX_PATH] = {'\0'};	//infoファイル用プリントバッファ
	size_t cbString;					//szBufferの文字列長
	HRESULT hResult;					//結果判定用変数

	hResInfo = FindResourceEx(hModule, lpType, lpName, wLang);
	//szBufferへリソース情報を書き込む
	//m_lt配列内リソース種類検索
	for(int i = 0; i < LTMAX; i++) {
		if((UINT)wLang == ((CRESLIST*)lParam)->m_lt[i].id) {
			StringCchCopy(szBuffer, MAX_PATH, TEXT("\t\tリソース言語: "));
			StringCchCat(szBuffer, MAX_PATH, ((CRESLIST*)lParam)->m_lt[i].area);
			StringCchCat(szBuffer, MAX_PATH, TEXT("\r\n"));
			break;
		}
	}
	if(!*szBuffer) {	//szBuffer[0]が初期値（NULL）の場合
		hResult = StringCchPrintf(szBuffer, MAX_PATH, TEXT("\t\tリソース言語: %u\r\n"), (USHORT) wLang);
		if(FAILED(hResult))
			return FALSE;
	}
	hResult = StringCchLength(szBuffer, MAX_PATH, &cbString);
	if(FAILED(hResult))
		return FALSE;
	//m_Dataの更新
	((CRESLIST*)lParam)->AddData(szBuffer, cbString);
	//リソースハンドルとサイズをバッファーに書き込む
	hResult = StringCchPrintf(szBuffer, MAX_PATH, TEXT("\t\tリソースハンドル：%lx、サイズ：%lu\r\n\r\n"), hResInfo, SizeofResource(hModule, hResInfo));
	if(FAILED(hResult))
		return FALSE;
	hResult = StringCchLength(szBuffer, MAX_PATH, &cbString);
	if (FAILED(hResult))
		return FALSE;
	//m_Dataの更新
	((CRESLIST*)lParam)->AddData(szBuffer, cbString);
	return TRUE;
}
