/*
 *	dpexres - PEXリソース管理ツール
 *	Copyright (C) 2006 Naoyuki Sawa
 *
 *	* Mon Jul 17 18:26:13 JST 2006 Naoyuki Sawa
 *	- 作成開始。
 *	* Mon Jul 17 21:12:19 JST 2006 Naoyuki Sawa
 *	- 1st リリース。
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

#define VERSION		"20060717"

/*{{/usr/PIECE/include/piece.hより*/
typedef struct _pffsFileHEADER {
	unsigned char mark;		// + 0 マーク ('X'固定)
	unsigned char type;		// + 1 ファイル・タイプ
	unsigned short ofs_data;	// + 2 実データへのオフセット
	unsigned short ofs_name;	// + 4 キャプションへのオフセット
	unsigned short ofs_icon;	// + 6 アイコンへのオフセット
	unsigned long resv2;		// + 8 予約
	unsigned long top_adrs;		// +12 先頭のアドレス
	unsigned long length;		// +16 長さ
	unsigned long crc32;		// +20 CRC32
} pffsFileHEADER;
/*}}/usr/PIECE/include/piece.hより*/

char cmd;			/* コマンド (a/d/e/t) */
char pex_path[_MAX_PATH];	/* PEXファイル名 */
char res_path[_MAX_PATH];	/* リソースファイル名 */

#define MAX_SIZE	(2 * 1024 * 1024)		/* ファイルサイズ上限 (2MB版P/ECEを想定) */
unsigned char pex_data[MAX_SIZE + 1/*超過検出用*/];	/* PEXファイルの内容 */
int pex_size;						/* PEXファイルサイズ */
unsigned char res_data[MAX_SIZE + 1/*超過検出用*/];	/* リソースファイルの内容 */
int res_size;						/* リソースファイルサイズ */

pffsFileHEADER* const hdr = (pffsFileHEADER*)pex_data;

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

/* 使い方を表示して終了します。
 */
void
usage()
{
	fprintf(stderr, "dpexres - PEXリソース管理ツール (%s)\n", VERSION);
	fprintf(stderr, "Copyright (C) 2006 Naoyuki Sawa\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "使い方①：PEXファイルにリソースを添付する\n");
	fprintf(stderr, "　　　　　dpexres a (pex filename[.pex]) (resource filename)\n");
	fprintf(stderr, "　　　　　※既にリソースが添付されている場合、置き換えます。\n");
	fprintf(stderr, "使い方②：PEXファイルのリソースを削除する\n");
	fprintf(stderr, "　　　　　dpexres d (pex filename[.pex])\n");
	fprintf(stderr, "　　　　　※リソースが添付されていない場合、何も行いません。\n");
	fprintf(stderr, "使い方③：PEXファイルのリソースを抽出する\n");
	fprintf(stderr, "　　　　　dpexres e (pex filename[.pex]) (output filename)\n");
	fprintf(stderr, "　　　　　※リソースが添付されていない場合、エラーとします。\n");
	fprintf(stderr, "使い方④：PEXファイルのリソースを表示する\n");
	fprintf(stderr, "　　　　　dpexres t (pex filename[.pex])\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "例①－１：dpexres a game music.fpk\n");
	fprintf(stderr, "　　　　　game.pexの末尾に、music.fpkを添付します。\n");
	fprintf(stderr, "例③　　：dpexres e game unknown.dat\n");
	fprintf(stderr, "　　　　　game.pexに添付されている、music.fpkの内容を、unknown.datという名前で保存します。\n");
	fprintf(stderr, "例①－２：dpexres a game movie.fpk\n");
	fprintf(stderr, "　　　　　game.pexから、上で添付したmusic.fpkの内容を削除し、代りにmovie.fpkを添付します。\n");
	fprintf(stderr, "例④　　：dpexres t game\n");
	fprintf(stderr, "　　　　　game.pexに添付されている、movie.fpkの情報を表示します。\n");
	fprintf(stderr, "例②　　：dpexres d game\n");
	fprintf(stderr, "　　　　　game.pexから、上で添付したmovie.fpkの内容を削除します。\n");
	abort();
}

/* PEXファイルを読み込みます。
 * [in]
 *	pex_path	ファイル名。
 * [out]
 *	pex_data	ファイルの内容。
 *	pex_size	ファイルサイズ。
 */
void
load_pex()
{
	FILE* fp;

	if(!strlen(pex_path)) {
		err("PEXファイル名が指定されていません。");
	}

	fp = fopen(pex_path, "rb");
	if(!fp) {
		err("%sが開けません。", pex_path);
	}

	pex_size = fread(pex_data, 1, sizeof pex_data, fp);
	if(pex_size > MAX_SIZE) {
		err("%sのサイズが大き過ぎます。", pex_path);
	}

	if((hdr->mark != 'X') ||
	   (pex_size < (int)(hdr->ofs_data + hdr->length))) {
		err("%sは不正なPEXファイルです。", pex_path);
	}

	fclose(fp);
}

/* リソースファイルを読み込みます。
 * [in]
 *	res_path	ファイル名。
 * [out]
 *	res_data	ファイルの内容。
 *	res_size	ファイルサイズ。
 */
void
load_res()
{
	FILE* fp;

	if(!strlen(res_path)) {
		err("リソースファイル名が指定されていません。");
	}

	fp = fopen(res_path, "rb");
	if(!fp) {
		err("%sが開けません。", res_path);
	}

	res_size = fread(res_data, 1, sizeof res_data, fp);
	if(res_size > MAX_SIZE) {
		err("%sのサイズが大き過ぎます。", res_path);
	}

	fclose(fp);
}

/* PEXファイルを書き込みます。
 * [in]
 *	pex_path	ファイル名。
 *	pex_data	ファイルの内容。
 *	pex_size	ファイルサイズ。
 */
void
save_pex()
{
	FILE* fp;

	if(!strlen(pex_path)) {
		err("PEXファイル名が指定されていません。");
	}

	fp = fopen(pex_path, "wb");
	if(!fp) {
		err("%sが作成できません。", pex_path);
	}

	fwrite(pex_data, 1, pex_size, fp);

	fclose(fp);
}

/* リソースファイルを書き込みます。
 * [in]
 *	res_path	ファイル名。
 *	res_data	ファイルの内容。
 *	res_size	ファイルサイズ。
 */
void
save_res()
{
	FILE* fp;

	if(!strlen(res_path)) {
		err("リソースファイル名が指定されていません。");
	}

	fp = fopen(res_path, "wb");
	if(!fp) {
		err("%sが作成できません。", res_path);
	}

	fwrite(res_data, 1, res_size, fp);

	fclose(fp);
}

int
main(int argc, char* argv[])
{
	char drive[_MAX_DRIVE];
	char dir[_MAX_DIR];
	char fname[_MAX_FNAME];
	char ext[_MAX_EXT];
	int ofs_res;

	/* 引数無しで起動されたら、使い方を表示します。 */
	if(argc <= 1 + 0) {
		usage();
	}

	/* コマンドを取得します。 */
	if(argc >= 1 + 1) {
		cmd = tolower(argv[1][0]);
	}

	/* PEXファイル名を取得します。 */
	if(argc >= 1 + 2) {
		_splitpath(argv[2], drive, dir, fname, ext);
		if(!strlen(ext)) {
			strcpy(ext, ".pex");
		}
		_makepath(pex_path, drive, dir, fname, ext);
	}

	/* リソースファイル名を取得します。 */
	if(argc >= 1 + 3) {
		_splitpath(argv[3], drive, dir, fname, ext);
		if(!strlen(ext)) {
			/** no job **/
		}
		_makepath(res_path, drive, dir, fname, ext);
	}

	/* PEXファイルを読み込みます。 */
	load_pex();

	/* コマンドによって... */
	switch(cmd) {
	case 'a':
		/* リソースファイルを読み込みます。 */
		load_res();

		/* PEXファイルの内容から、リソースを削除します。 */
		pex_size = hdr->ofs_data + hdr->length;

		/* リソースをワード境界に配置するため、パディングを行います。 */
		while(pex_size & 3) {
			pex_data[pex_size++] = 0; /* 置き換え前のゴミが残ると紛らわしいので */
		}

		/* PEXファイルの内容に、リソースを添付します。 */
		if(pex_size + res_size > MAX_SIZE) {
			err("%sと%sの合計サイズが大き過ぎます。", pex_path, res_path);
		}
		memcpy(pex_data + pex_size, res_data, res_size);
		pex_size += res_size;

		/* PEXファイルを書き込みます。 */
		save_pex();

		break;
	case 'd':
		/* PEXファイルの内容から、リソースを削除します。 */
		pex_size = hdr->ofs_data + hdr->length;

		/* PEXファイルを書き込みます。 */
		save_pex();

		break;
	case 'e':
		/* リソースの先頭オフセットを求めます。 */
		ofs_res = (hdr->ofs_data + hdr->length + 3) & ~3;

		/* リソースを取得します。 */
		res_size = pex_size - ofs_res;
		if(res_size <= 0) {
			err("%sにはリソースが添付されていません。", pex_path);
		}
		memcpy(res_data, pex_data + ofs_res, res_size);

		/* リソースファイルを書き込みます。 */
		save_res();

		break;
	case 't':
		/* リソースの先頭オフセットを求めます。 */
		ofs_res = (hdr->ofs_data + hdr->length + 3) & ~3;

		/* リソースの情報を表示します。 */
		res_size = pex_size - ofs_res;
		if(res_size <= 0) {
			printf("%sにはリソースが添付されていません。\n", pex_path);
		} else {
			printf("%sには%dバイトのリソースが添付されています。\n", pex_path, res_size);
		}

		break;
	default:
		err("コマンドが不正です。");
	}

	return 0;
}

