// bin2seq.cpp : コンソール アプリケーションのエントリ ポイントを定義します。
//

/*
 * This code is released under the Microsoft Public License (MS-PL). See license.txt, below.
 */
#include "stdafx.h"

typedef enum _ExecType {
	a,
	l,
	b,
	g,
} ExecType;

void usage(void)
{
	_tprintf(_T("Usage\n"));
	_tprintf(_T("\t-a <file> <startOffset of file(hex)> <maxSearchFileNum(dec)> [nocomp] [no16]\n"));
	_tprintf(_T("\t\tsplit data automatically\n"));
	_tprintf(_T("\t\tmaxSearchFileNum:sum of seq&vh or sssq&sshd\n"));
	_tprintf(_T("\t\tnocomp:if compare file 1, otherwise 0.\n"));
	_tprintf(_T("\t\tno16:if first 16byte of vbdata isn't zero, use this.\n"));
	_tprintf(_T("\t-l <file> <serial number(5digit)>\n"));
	_tprintf(_T("\t\tuncompress seq for lzss\n"));
	_tprintf(_T("\t\t\tCrime Crackers 2(SCPS-10037)\n"));
	_tprintf(_T("\t\t\tLeading Jockey Hybrid(SLPS-00348)\n"));
	_tprintf(_T("\t\t\tWILD ARMS(SCPS-10028)\n"));
	_tprintf(_T("\t\t\tEberouge(SLPS-00844)\n"));
	_tprintf(_T("\t\t\tMeitantei Conan - 3nin no Meisuiri(SLPS-02804)\n"));
	_tprintf(_T("\t\t\tMeitantei Conan - Saikou no Partner(SLPS-03425)\n"));
	_tprintf(_T("\t\tuncompress sssq for lzss\n"));
	_tprintf(_T("\t\t\tWILD ARMS 2nd IGNITION(SCPS-10089)\n"));
	_tprintf(_T("\t-b <file>\n"));
	_tprintf(_T("\t\tuncompress seq for Brightis(SCPS-10105)\n"));
	_tprintf(_T("\t-g <file>\n"));
	_tprintf(_T("\t\tuncompress seq for Gust game\n"));
	_tprintf(_T("\t\t\tMarie no Atelier(SLPS-00856, SLPS-01418)\n"));
	_tprintf(_T("\t\t\tElie no Atelier(SLPS-01751, SLPS-91180)\n"));
}

int exec(int argc, _TCHAR* argv[], ExecType execType)
{
	FILE* fp = _tfopen(argv[2], _T("rb"));
	if(!fp) {
		_tprintf(_T("No file or already opened [F:%s][L:%d]:%s\n"), 
			_T(__FUNCTION__), __LINE__, argv[2]);
		return -1;
	}
	unsigned long datasize = getFilesize(fp);
	unsigned char* data = (unsigned char*)malloc(datasize);
	if(!data) {
		_tprintf(_T("Cannot alloc memory [F:%s][L:%d]\n"), 
			_T(__FUNCTION__), __LINE__);
		return -1;
	}
	fread(data, sizeof(char), datasize, fp);
	FcloseAndNull(fp);

	bool ret = false;
	InternalMusic im;
	memset(&im, 0, sizeof(im));
	if(!_tcscmp(argv[1], _T("-a"))) {
		ret = GetDataOffset(data, datasize, 
			_tcstoul(argv[3], NULL, 16), _tcstoul(argv[4], NULL, 10), true, &im);
	}
	else {
		ret = GetDataOffset(data, datasize, 0, 1024, false, &im);
	}

	if(!ret) {
		return FALSE;
	}
	else if(im.nSeqIdx == 0 && im.nVhIdx == 0) {
		_tprintf(_T("No seq & vh found [F:%s][L:%d]:%s\n"), 
			_T(__FUNCTION__), __LINE__, argv[2]);
		return -1;
	}

	if(execType == a) {
		FILE* csvFp = CreateOrOpenFile(argv[2], NULL, 
			NULL, _T(".csv"), _T("w, ccs=UNICODE"), 0, 0);
		if(!csvFp) {
			_tprintf(_T("No file or already opened [F:%s][L:%d]:%s\n"), 
				_T(__FUNCTION__), __LINE__, argv[2]);
			return -1;
		}
		_TCHAR header[] = 
			{_T("Ext,No,Offset(dec),Offset(hex),Size(dec),Size(hex)\n")};
		header[sizeof(header) / sizeof(_TCHAR) - 1] = '\0';
		fwrite(header, sizeof(_TCHAR), _tcslen(header), csvFp);

		if(im.nSeqIdx > 0 && 
			(!_tcscmp(im.pszSeqExt, _T(".seq")) || !_tcscmp(im.pszSeqExt, _T(".sep")))) {
			ret = CreateSeq(argv[2], data, datasize, csvFp, &im);
		}
		else if(im.nSeqIdx > 0 && !_tcscmp(im.pszSeqExt, _T(".kcet"))) {
			ret = CreateKcet(argv[2], data, datasize, csvFp, &im);
		}
		else if(im.nSeqIdx > 0 && !_tcscmp(im.pszSeqExt, _T(".sssq"))) {
			ret = CreateSSsq(argv[2], data, datasize, csvFp, &im);
		}
		else if(im.nSeqIdx > 0 && !_tcscmp(im.pszSeqExt, _T(".sntp"))) {
			ret = CreateSntp(argv[2], data, datasize, csvFp, &im);
		}
		else if(im.nSeqIdx > 0 && !_tcscmp(im.pszSeqExt, _T(".seqg"))) {
			ret = CreateSeqg(argv[2], data, csvFp, &im);
		}
		else if(im.nSeqIdx > 0 && !_tcscmp(im.pszSeqExt, _T(".seqq"))) {
			ret = CreateSeqq(argv[2], data, csvFp, &im);
		}
		else if(im.nSeqIdx > 0 && !_tcscmp(im.pszSeqExt, _T(".sqv"))) {
			ret = CreateSqv(argv[2], data, csvFp, &im);
		}
		else if(im.nSeqIdx > 0 && !_tcscmp(im.pszSeqExt, _T(".sseq"))) {
			ret = CreateSseq(argv[2], data, csvFp, &im);
		}

		BOOL is_vb_head_0byte = TRUE;
		if(argc == 7 && !_tcscmp(argv[6], _T("no16"))) {
			is_vb_head_0byte = FALSE;
		}
		if(im.nVhIdx > 0 &&
			(!_tcscmp(im.pszVhExt, _T(".vh")) || !_tcscmp(im.pszVhExt, _T(".vh2")))) {
			size_t allocVhSize = im.nVhIdx * sizeof(VH);
			VH* vh = (VH*)malloc(allocVhSize);
			size_t allocVagOffsetSize =
				im.nVhIdx * VAG_OFFSET_NUM * sizeof(unsigned short*);
			unsigned short* vag_offset = (unsigned short*)malloc(allocVagOffsetSize);
			if(!vh || !vag_offset) {
				_tprintf(_T("Can't alloc memory [F:%s][L:%d]\n"),
					_T(__FUNCTION__), __LINE__);
				return -1;
			}
			memset(vh, 0, allocVhSize);
			memset(vag_offset, 0, allocVagOffsetSize);
			if(!_tcscmp(im.pszVhExt, _T(".vh"))) {
				ret = CreateVh(argv[2], data, datasize, csvFp, &im, vh, vag_offset);
			}
			else if(!_tcscmp(im.pszVhExt, _T(".vh2"))) {
				ret = CreateVhForBastard(argv[2], data, csvFp, &im, vh, vag_offset);
			}
			if(ret) {
				ret = CreateVbForVh(argv[2], data, datasize,
					is_vb_head_0byte, csvFp, &im, vh, vag_offset);
			}
			FreeAndNull(vh);
			FreeAndNull(vag_offset);
		}
		else if(im.nVhIdx > 0 && !_tcscmp(im.pszVhExt, _T(".sshd"))) {
			size_t allocVbSize = (im.nVhIdx + 1) * sizeof(unsigned long);
			unsigned long* vbsize = (unsigned long*)malloc(allocVbSize);
			if(!vbsize) {
				_tprintf(_T("Can't alloc memory [F:%s][L:%d]\n"),
					_T(__FUNCTION__), __LINE__);
				return -1;
			}
			memset(vbsize, 0, allocVbSize);
			ret = CreateSShd(argv[2], data, datasize, vbsize, csvFp, &im);
			if(ret) {
				ret = CreateVbForSShd(argv[2], data, datasize,
					vbsize, is_vb_head_0byte, csvFp, &im);
			}			
			FreeAndNull(vbsize);
		}
		else if(im.nVhIdx > 0 && !_tcscmp(im.pszVhExt, _T(".inst"))) {
			ret = CreateInst(argv[2], data, csvFp, &im);
			if(ret) {
				ret = CreateVbForInst(argv[2], data, csvFp, &im);
			}			
		}
		FcloseAndNull(csvFp);
		if(argc >= 6 && !_tcscmp(argv[5], _T("1"))) {
			_TCHAR logPath[_MAX_PATH];
			memset(logPath, 0, sizeof(logPath));
			FILE* logFp = CreateOrOpenFile(argv[2], logPath, NULL, 
				_T("_duplicated_file.txt"), _T("w, ccs=UNICODE"), 0, 0);
			if(!logFp) {
				_tprintf(_T("No file or already opened [F:%s][L:%d]:%s\n"), 
					_T(__FUNCTION__), __LINE__, argv[2]);
				return -1;
			}
			CompareFile(argv[2], im.nSeqIdx, im.pszSeqExt, im.isSeq, logFp);
			CompareFile(argv[2], im.nVhIdx, im.pszVhExt, im.isVh, logFp);
			CompareFile(argv[2], im.nVhIdx, _T(".vb"), im.isVb, logFp);

			unsigned long filesize = getFilesize(logFp);
			FcloseAndNull(logFp);
			if(filesize == 0) {
				_tremove(logPath);
			}
		}
		FreeAndNull(im.isSeq);
		FreeAndNull(im.isVb);
		FreeAndNull(im.isVh);
		FreeAndNull(im.nSeqOffset);
		FreeAndNull(im.nSeqSize);
		FreeAndNull(im.nVhOffset);
		FreeAndNull(im.nVhSize);
	}
	else if(execType == l) {
		if(
			!_tcscmp(argv[3], _T("00348")) // Leading Jockey Hybrid
			) {
			uncompressLzss(data, argv[2], 5, 1, Zero, 0x0f,
				Uncompress, LzssType1, _T(".seq"), 1, &im);
		}
		else if(
			!_tcscmp(argv[3], _T("10028")) || // WILD ARMS
			!_tcscmp(argv[3], _T("10037")) // Crime Crackers 2
			) {
			uncompressLzss(data, argv[2], 5, 1, Zero, 0x0f,
				Uncompress, LzssType1, _T(".seq"), 0, &im);
		}
#if 0
		else if(!_tcscmp(argv[3], _T("10124"))) { // LOVE & DESTROY
			uncompressLzss(data, argv[2], 6, 1, Zero, 0x0f,
				Uncompress, LzssType1, _T(".seq"), 0, &im);
		}
#endif
#if 0
		else if(!_tcscmp(argv[3], _T("10053"))) { // Yarudora Series Vol.1 - Double Cast
			uncompressLzss(data, argv[2], 5, 1, One, 0x0f,
				Uncompress, LzssType1, _T(".seq"), 0, &im);
		}
#endif
		else if(!_tcscmp(argv[3], _T("00844"))) { // Eberouge
			uncompressLzss(data, argv[2], 9, 1, One, 0x0f,
				Uncompress, LzssType1, _T(".seq"), 0, &im);
		}
		else if(!_tcscmp(argv[3], _T("10089"))) { // WILD ARMS 2nd IGNITION
			uncompressLzss(data, argv[2], 12, 8, Zero, 0x0f,
				Uncompress, LzssType1, _T(".sssq"), 0, &im);
		}
		else if(
			!_tcscmp(argv[3], _T("02804")) || // Meitantei Conan - 3nin no Meisuiri
			!_tcscmp(argv[3], _T("03425")) // Meitantei Conan - Saikou no Partner
			) {
			uncompressLzss(data, argv[2], 5, 1, Zero, 0x0f,
				Compress, LzssType1, _T(".seq"), 0, &im);
		}
#if 0
		else if(!_tcscmp(argv[3], _T("10074"))) { // Ore no Shikabane wo Koete Yuke
			uncompressLzss(data, argv[2], 14, 10, Zero, 0xc0,
				Uncompress, LzssType2, _T(".sssq"), 0, &im);
		}
#endif
		else {
			_tprintf(_T("unsupported number\n"));
		}
	}
	else if(execType == b) {
		uncompressBrightis(data, argv[2], &im);
	}
	else if(execType == g) {
		uncompressGust(data, datasize, argv[2], &im);
	}
	else {
		usage();
	}
	return ret;
}

int checkArg(int argc, _TCHAR* argv[], ExecType* execType)
{
	_TCHAR* endptr = NULL;
	ULONG ul = 0;
	if((argc == 6 || argc == 7 && !_tcscmp(argv[6], _T("no16"))) &&
		!_tcscmp(argv[1], _T("-a"))) {
		ul = _tcstoul(argv[3], &endptr, 10);
		if(*endptr) {
			return FALSE;
		}
		ul = _tcstoul(argv[4], &endptr, 10);
		if(*endptr) {
			return FALSE;
		}
		*execType = a;
	}
	else if(argc == 4 && !_tcscmp(argv[1], _T("-l"))) {
		ul = _tcstoul(argv[3], &endptr, 10);
		if(*endptr) {
			return FALSE;
		}
		*execType = l;
	}
	else if(argc == 3 && !_tcscmp(argv[1], _T("-b"))) {
		*execType = b;
	}
	else if(argc == 3 && !_tcscmp(argv[1], _T("-g"))) {
		*execType = g;
	}
	else {
		return FALSE;
	}
	return TRUE;
}

int _tmain(int argc, _TCHAR* argv[])
{
	_tprintf(_T("bin2seq BuildDate:[%s %s]\n"), _T(__DATE__), _T(__TIME__));
	ExecType execType;
	if(!checkArg(argc, argv, &execType)) {
		usage();
		return -1;
	}
	time_t now;
    struct tm  *ts;
    _TCHAR buf[80];
	memset(buf, 0, sizeof(buf));
	now = time(NULL);
    ts = localtime(&now);
    _tcsftime(buf, sizeof(buf) / sizeof(_TCHAR), _T("%a %Y-%m-%d %H:%M:%S %Z"), ts);
    _tprintf(_T("Start %s\n"), buf);

	int ret = exec(argc, argv, execType);

	now = time(NULL);
    ts = localtime(&now);
    _tcsftime(buf, sizeof(buf) / sizeof(_TCHAR), _T("%a %Y-%m-%d %H:%M:%S %Z"), ts);
    _tprintf(_T("End %s\n"), buf);
	return ret;
}
