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

FILE* CreateOrOpenFile(
	LPCTSTR strSrcPath,
	LPTSTR pszOutPath,
	LPTSTR pszFileNameWithoutPath,
	LPCTSTR strExt,
	LPCTSTR strMode,
	unsigned long nFileCnt,
	unsigned long nMaxFileNum
	)
{
	_TCHAR szDstPath[_MAX_PATH];
	_TCHAR drive[_MAX_DRIVE];
	_TCHAR dir[_MAX_DIR];
	_TCHAR fname[_MAX_FNAME];
	_TCHAR ext[_MAX_EXT];
	memset(szDstPath, 0, sizeof(szDstPath));
	memset(drive, 0, sizeof(drive));
	memset(dir, 0, sizeof(dir));
	memset(fname, 0, sizeof(fname));
	memset(ext, 0, sizeof(ext));

	_tsplitpath(strSrcPath, drive, dir, fname, ext);
	if(nMaxFileNum <= 1) {
		_stprintf(szDstPath, _T("%s%s"), strSrcPath, strExt);
	}
	else if(2 <= nMaxFileNum && nMaxFileNum < 10) {
		_stprintf(szDstPath, _T("%s_%d%s"), strSrcPath, nFileCnt + 1, strExt);
	}
	else if(10 <= nMaxFileNum && nMaxFileNum < 100) {
		_stprintf(szDstPath, _T("%s_%02d%s"), strSrcPath, nFileCnt + 1, strExt);
	}
	else if(100 <= nMaxFileNum && nMaxFileNum < 1000) {
		_stprintf(szDstPath, _T("%s_%03d%s"), strSrcPath, nFileCnt + 1, strExt);
	}
	else if(1000 <= nMaxFileNum && nMaxFileNum < 10000) {
		_stprintf(szDstPath, _T("%s_%04d%s"), strSrcPath, nFileCnt + 1, strExt);
	}

	if(pszFileNameWithoutPath != NULL) {
		// size of pszFileNameWithoutPath must be _MAX_PATH.
		memset(pszFileNameWithoutPath, 0, _MAX_FNAME);
		_tsplitpath(szDstPath, drive, dir, fname, ext);
		_stprintf(pszFileNameWithoutPath, _T("%s%s"), fname, ext);
	}
	if(pszOutPath != NULL) {
		// size of pszOutPath must be _MAX_PATH.
		_tcsncpy(pszOutPath, szDstPath, _MAX_PATH);
	}
	return _tfopen(szDstPath, strMode);
}

void writeCsv(
	_TCHAR* filetype,
	unsigned long no,
	unsigned long offset,
	unsigned long size,
	unsigned long nMaxIdx,
	FILE* csvFp
	)
{
	_TCHAR buf[256];
	memset(buf, 0, sizeof(buf));
	if(1 <= nMaxIdx && nMaxIdx < 10) {
		_sntprintf(buf, sizeof(buf) / sizeof(_TCHAR), _T("%s,%01d,%d-%d,%x-%x,%d,%x\n"),
		filetype, no + 1, offset, offset + size - 1, offset, offset + size - 1, size, size);
	}
	else if(10 <= nMaxIdx && nMaxIdx < 100) {
		_sntprintf(buf, sizeof(buf) / sizeof(_TCHAR), _T("%s,%02d,%d-%d,%x-%x,%d,%x\n"),
		filetype, no + 1, offset, offset + size - 1, offset, offset + size - 1, size, size);
	}
	else if(100 <= nMaxIdx && nMaxIdx < 1000) {
		_sntprintf(buf, sizeof(buf) / sizeof(_TCHAR), _T("%s,%03d,%d-%d,%x-%x,%d,%x\n"),
		filetype, no + 1, offset, offset + size - 1, offset, offset + size - 1, size, size);
	}
	else if(1000 <= nMaxIdx && nMaxIdx < 10000) {
		_sntprintf(buf, sizeof(buf) / sizeof(_TCHAR), _T("%s,%04d,%d-%d,%x-%x,%d,%x\n"),
		filetype, no + 1, offset, offset + size - 1, offset, offset + size - 1, size, size);
	}
	buf[255] = 0;
	fwrite(buf, sizeof(_TCHAR), _tcslen(buf), csvFp);
	fflush(csvFp);
}

void paddingSeq(
	unsigned long size,
	FILE* outFp
	)
{
	// for PSFLab
	size_t paddingSize = 4 - size % 4;
	if(paddingSize < 4) {
		char paddingBuf[3];
		memset(paddingBuf, 0, sizeof(paddingBuf));
		fwrite(paddingBuf, sizeof(char), paddingSize, outFp);
	}
}

bool CreateSeq(
	_TCHAR* path, 
	unsigned char* data, 
	unsigned long datasize, 
	FILE* csvFp, 
	InternalMusic* im
	)
{
	SEQ* seq = (SEQ*)malloc(im->nSeqIdx * sizeof(SEQ));
	if(!seq) {
		_tprintf(_T("Cannot alloc memory [F:%s][L:%d]\n"), 
			_T(__FUNCTION__), __LINE__);
		return false;
	}
	for(unsigned long i = 0; i < im->nSeqIdx; i++) {
		memcpy(&seq[i], data + im->nSeqOffset[i], sizeof(SEQ));
		unsigned long id = seq[i].SEQUENCE_ID & 0x0000FFFF;
		if(id != 0) {
			_tprintf(_T("No split for compress or non seq[%d] : offset -> %X\n"), 
				i + 1, im->nSeqOffset[i]);
			im->nSeqSize[i] = 4;
			continue;
		}
		unsigned short seq_id = seq[i].SEQUENCE_ID >> 16;
		for(unsigned long idx = sizeof(SEQ); idx < MAX_SEQ_SIZE; idx++) {
			if(datasize < im->nSeqOffset[i] + idx) {
				break;
			}
			unsigned char* src = data + im->nSeqOffset[i] + idx;
			unsigned long longbyte = getlong(src);
			// search eof
			if((longbyte & 0xFFFFFF00) == 0x002FFF00) {
				unsigned long seqsize = idx + sizeof(longbyte);

				if(seq_id == 0x0000) {
					if(_tcscmp(im->pszSeqExt, _T(".sep"))) {
						_tcscpy(im->pszSeqExt, _T(".sep"));
					}
					_tcscpy(im->pszSeqExt, _T(".sep"));
					unsigned short prev_seq_id = seq_id;
					unsigned char* next_src = src + sizeof(longbyte);

					for(int j = 0; j < MAX_SEQ_IN_SEP; j++) {
						unsigned short next_seq_id = getword(next_src);

						if(next_seq_id == prev_seq_id + 0x0100) {
							prev_seq_id += 0x0100;
							for(int k = 0; k < MAX_SEQ_SIZE; k++) {
								longbyte = getlong(next_src + sizeof(next_seq_id) + k);
								// search eof
								if((longbyte & 0xFFFFFF00) == 0x002FFF00) {
									next_src += sizeof(next_seq_id) + k + sizeof(longbyte);
									seqsize += sizeof(next_seq_id) + k + sizeof(longbyte);
									break;
								}
							}
						}
						else {
							break;
						}
					}
				}
				else {
					if(_tcscmp(im->pszSeqExt, _T(".seq"))) {
						_tcscpy(im->pszSeqExt, _T(".seq"));
					}
				}
				writeCsv(im->pszSeqExt, i, im->nSeqOffset[i], seqsize, im->nSeqIdx, csvFp);
				FILE* outFp = CreateOrOpenFile(path, NULL, NULL, im->pszSeqExt, _T("wb"), i, im->nSeqIdx);
				if(outFp) {
					fwrite(data + im->nSeqOffset[i], sizeof(char), seqsize, outFp);
					paddingSeq(seqsize, outFp);
					FcloseAndNull(outFp);
					im->nSeqSize[i] = seqsize;
					im->isSeq[i] = TRUE;
					_tprintf(_T("\rCreate %s %4d/%4d"), im->pszSeqExt, i + 1, im->nSeqIdx);
				}
				break;
			}
		}
	}
	_tprintf(_T("\n"));
	FreeAndNull(seq);
	return true;
}

bool CreateSSsq(
	_TCHAR* path, 
	unsigned char* data, 
	unsigned long datasize, 
	FILE* csvFp, 
	InternalMusic* im
	)
{
	for(unsigned long i = 0; i < im->nSeqIdx; i++) {
		//maxsize SSsq (no evidence)
		for(int idx = 0; idx < 65535; idx++) {
			unsigned char* src = data + im->nSeqOffset[i] + idx;
			unsigned long longbyte = getlong(src);
			// search eof
			if((longbyte & 0xFFFFFF00) == 0x002FFF00) {
				unsigned long sssqsize = idx + sizeof(longbyte);
				if(sssqsize != 0 && sssqsize <= datasize) {
					writeCsv(im->pszSeqExt, i, im->nSeqOffset[i] - 12, sssqsize, im->nSeqIdx, csvFp);
					FILE* outFp = 
						CreateOrOpenFile(path, NULL, NULL, im->pszSeqExt, _T("wb"), i, im->nSeqIdx);
					if(outFp) {
						unsigned long realSssqsize = sssqsize + 12;
						fwrite(data + im->nSeqOffset[i] - 12, sizeof(char), realSssqsize, outFp);
						paddingSeq(realSssqsize, outFp);
						FcloseAndNull(outFp);
						im->nSeqSize[i] = realSssqsize;
						im->isSeq[i] = TRUE;
						_tprintf(_T("\rCreate %s %4d/%4d"), im->pszSeqExt, i + 1, im->nSeqIdx);
					}
					break;
				}
			}
		}
	}
	_tprintf(_T("\n"));
	return true;
}

bool CreateKcet(
	_TCHAR* path, 
	unsigned char* data, 
	unsigned long datasize, 
	FILE* csvFp, 
	InternalMusic* im
	)
{
	for(unsigned long i = 0; i < im->nSeqIdx; i++) {
		unsigned char* src = data + im->nSeqOffset[i];
		unsigned int kcetSize = getlong(src + 4);
		if(kcetSize != 0 && kcetSize <= datasize) {
			unsigned int realKcetSize = kcetSize + 8;
			writeCsv(im->pszSeqExt, i, im->nSeqOffset[i], realKcetSize, im->nSeqIdx, csvFp);
			FILE* outFp = CreateOrOpenFile(path, NULL, NULL, im->pszSeqExt, _T("wb"), i, im->nSeqIdx);
			if(outFp) {
				fwrite(src, sizeof(char), realKcetSize, outFp);
				FcloseAndNull(outFp);
				im->nSeqSize[i] = realKcetSize;
				im->isSeq[i] = TRUE;
				_tprintf(_T("\rCreate %s %4d/%4d"), im->pszSeqExt, i + 1, im->nSeqIdx);
			}
		}
	}
	_tprintf(_T("\n"));
	return true;
}

bool CreateSntp(
	_TCHAR* path, 
	unsigned char* data, 
	unsigned long datasize, 
	FILE* csvFp,
	InternalMusic* im
	)
{
	for(unsigned long i = 0; i < im->nSeqIdx; i++) {
		unsigned char* src = data + im->nSeqOffset[i] - 12;
		unsigned long sntpHeaderSize = getlong(data + im->nSeqOffset[i] - 4);
//		unsigned short attrNum = getword(data + im->nSeqOffset[i] + 8);

		unsigned long allHeaderSize = 0;
		unsigned long tmp = 0;
		unsigned long smfOffset = 0;
		unsigned long smfOffsetNum = 0;
		for(smfOffset = 4; ; smfOffset+=4, smfOffsetNum++) {
			if(sntpHeaderSize + smfOffset >= datasize) {
				break;
			}
			tmp = getlong(src + sntpHeaderSize + smfOffset);
			if(smfOffset == 8 && tmp == 0x00000008) {
				smfOffsetNum--;
			}
			if(tmp == 0x00200000) {
				allHeaderSize = sntpHeaderSize + smfOffset;
				smfOffsetNum--;
				break;
			}
		}
		unsigned long dataSize = getlong(src + allHeaderSize - 4);
		if((dataSize & 0x00FF0000) > 0) {
			dataSize = getlong(src + allHeaderSize - 8);
		}
#ifdef _DEBUG
		FILE* logFp = CreateOrOpenFile(path, NULL, NULL, _T(".txt"), _T("wb"), i, im->nSeqIdx);
		if(logFp) {
			for(unsigned long longData = 4, idx = 0; ; longData += 4) {
				tmp = getlong(src + allHeaderSize + longData);
				char buf[64];
				_snprintf(buf, sizeof(buf), 
					"%04X : %04X : %02X %02X %02X %02X\r\n", 
					im->nSeqOffset[i] - 12 + allHeaderSize + longData, 
					allHeaderSize + longData,
					(tmp & 0x000000FF), (tmp & 0x0000FF00) >> 8, 
					(tmp & 0x00FF0000) >> 16, (tmp & 0xFF000000) >> 24);
				buf[64-1] = 0;
				fwrite(buf, sizeof(char), strlen(buf), logFp);
				if(tmp == 0x00200000) {
					idx++;
					if(idx == smfOffsetNum) {
						break;
					}
				}
			}
			FcloseAndNull(logFp);
		}
#endif
		for(unsigned long lastData = 4; ; lastData+=4) {
			tmp = getlong(src + allHeaderSize + dataSize + lastData);
			if(tmp == 0x00200000) {
				unsigned long allSize = allHeaderSize + dataSize + lastData + 4;
				writeCsv(im->pszSeqExt, i, im->nSeqOffset[i] - 12, allSize, im->nSeqIdx, csvFp);
				FILE* outFp = CreateOrOpenFile(path, NULL, NULL, im->pszSeqExt, _T("wb"), i, im->nSeqIdx);
				if(outFp) {
					fwrite(src, sizeof(char), allSize, outFp);
					FcloseAndNull(outFp);
					im->nSeqSize[i] = allSize;
					im->isSeq[i] = TRUE;
					_tprintf(_T("\rCreate %s %4d/%4d"), im->pszSeqExt, i + 1, im->nSeqIdx);
				}
				break;
			}
		}
	}
	_tprintf(_T("\n"));
	return true;
}

bool CreateSeqg(
	_TCHAR* path, 
	unsigned char* data, 
	FILE* csvFp,
	InternalMusic* im
	)
{
	for(unsigned long i = 0; i < im->nSeqIdx; i++) {
		unsigned char* src = data + im->nSeqOffset[i];
		unsigned long size = getlong(src + 80) + 8;
		writeCsv(im->pszSeqExt, i, im->nSeqOffset[i], size, im->nSeqIdx, csvFp);
		FILE* outFp = CreateOrOpenFile(path, NULL, NULL, 
			im->pszSeqExt, _T("wb"), i, im->nSeqIdx);
		if(outFp) {
			fwrite(src, sizeof(char), size, outFp);
			FcloseAndNull(outFp);
			im->nSeqSize[i] = size;
			im->isSeq[i] = TRUE;
			_tprintf(_T("\rCreate %s %4d/%4d"), im->pszSeqExt, i + 1, im->nSeqIdx);
		}
	}
	_tprintf(_T("\n"));
	return true;
}

bool CreateSeqq(
	_TCHAR* path, 
	unsigned char* data, 
	FILE* csvFp,
	InternalMusic* im
	)
{
	for(unsigned long i = 0; i < im->nSeqIdx; i++) {
		unsigned long seqqPos = im->nSeqOffset[i] - 0x3C;
		unsigned char* src = data + seqqPos;
		unsigned long size = getlong(data + seqqPos);
		writeCsv(im->pszSeqExt, i, seqqPos, size + 0x3C, im->nSeqIdx, csvFp);
		FILE* outFp = CreateOrOpenFile(path, NULL, 
			NULL, im->pszSeqExt, _T("wb"), i, im->nSeqIdx);
		if(outFp) {
			fwrite(src, sizeof(char), size + 0x3C, outFp);
			FcloseAndNull(outFp);
			im->nSeqSize[i] = size;
			im->isSeq[i] = TRUE;
			_tprintf(_T("\rCreate %s %4d/%4d"), im->pszSeqExt, i + 1, im->nSeqIdx);
		}
	}
	_TCHAR ext[_MAX_EXT];
	_tsplitpath(path, NULL, NULL, NULL, ext);
	FILE* outFp = CreateOrOpenFile(path, NULL, NULL, _T(".vb"), _T("wb"), 0, 2);
	if(outFp) {
		if(!_tcscmp(ext, _T(".Q41"))) {
			fwrite(data + 0xce4f16c, sizeof(char), 0xce89e3c - 0xce4f16c, outFp);
		}
		else if(!_tcscmp(ext, _T(".Q71"))) {
			fwrite(data + 0x11eb114c, sizeof(char), 0x11efdbdc - 0x11eb114c, outFp);
		}
		FcloseAndNull(outFp);
	}
	outFp = CreateOrOpenFile(path, NULL, NULL, _T(".vb"), _T("wb"), 1, 2);
	if(outFp) {
		if(!_tcscmp(ext, _T(".Q41"))) {
			fwrite(data + 0xce8a8c8, sizeof(char), 0xceb96f8 - 0xce8a8c8, outFp);
		}
		else if(!_tcscmp(ext, _T(".Q71"))) {
			fwrite(data + 0x11efe7dc, sizeof(char), 0x11f1a36c - 0x11efe7dc, outFp);
		}
		FcloseAndNull(outFp);
	}
	_tprintf(_T("\n"));
	return true;
}

bool CreateSqv(
	_TCHAR* path, 
	unsigned char* data, 
	FILE* csvFp,
	InternalMusic* im
	)
{
	for(unsigned long i = 0; i < im->nSeqIdx; i++) {
		int max = 1;
		int cnt = 0;
		for(unsigned long idx = 8; idx < 16; idx+=4) {
			unsigned char* src = data + im->nSeqOffset[i] + idx;
			unsigned long longbyte = getlong(src);
			if(longbyte > 0) {
				max++;
			}
		}
		for(unsigned long idx = 0; idx < MAX_SEQ_SIZE; idx++) {
			unsigned char* src = data + im->nSeqOffset[i] + idx;
			unsigned long longbyte = getlong(src);
			// search eof
			if((longbyte & 0xFFFFFF00) == 0x002FFF00) {
				cnt++;
				if(cnt == max) {
					unsigned long seqsize = sizeof(longbyte) + idx;
					writeCsv(im->pszSeqExt, i,
						im->nSeqOffset[i], seqsize, im->nSeqIdx, csvFp);
					FILE* outFp = CreateOrOpenFile(path, NULL, 
						NULL, im->pszSeqExt, _T("wb"), i, im->nSeqIdx);
					if(outFp) {
						fwrite(data + im->nSeqOffset[i], sizeof(char), seqsize, outFp);
						paddingSeq(seqsize, outFp);
						FcloseAndNull(outFp);
						im->nSeqSize[i] = seqsize;
						im->isSeq[i] = TRUE;
						_tprintf(_T("\rCreate %s %4d/%4d"),
							im->pszSeqExt, i + 1, im->nSeqIdx);
					}
					break;
				}
			}
		}
	}
	_tprintf(_T("\n"));
	return true;
}

bool CreateSseq(
	_TCHAR* path, 
	unsigned char* data, 
	FILE* csvFp,
	InternalMusic* im
	)
{
	for(unsigned long i = 0; i < im->nSeqIdx; i++) {
		for(unsigned long idx = 0; idx < MAX_SEQ_SIZE; idx++) {
			unsigned char* src = data + im->nSeqOffset[i] + idx;
			if(*src == 0xf3) {
				unsigned long seqsize = idx + 1;
				writeCsv(im->pszSeqExt, i,
					im->nSeqOffset[i], seqsize, im->nSeqIdx, csvFp);
				FILE* outFp = CreateOrOpenFile(path, NULL,
					NULL, im->pszSeqExt, _T("wb"), i, im->nSeqIdx);
				if(outFp) {
					fwrite(data + im->nSeqOffset[i], sizeof(char), seqsize, outFp);
					paddingSeq(seqsize, outFp);
					FcloseAndNull(outFp);
					im->nSeqSize[i] = seqsize;
					im->isSeq[i] = TRUE;
					_tprintf(_T("\rCreate %s %4d/%4d"),
						im->pszSeqExt, i + 1, im->nSeqIdx);
				}
				break;
			}
		}
	}
	_tprintf(_T("\n"));
	return true;
}

bool CreateVh(
	_TCHAR* path, 
	unsigned char* data, 
	unsigned long datasize, 
	FILE* csvFp, 
	InternalMusic* im,
	VH* vh,
	unsigned short* vag_offset
	)
{
	unsigned char* prog_attr = 
		(unsigned char*)malloc(im->nVhIdx * PROGRAM_ATTRIBUTES * 
		MAX_PROGRAM_ATTRIBUTES * sizeof(unsigned char*));
	unsigned char** tone_attr = 
		(unsigned char**)malloc(im->nVhIdx * sizeof(unsigned char**));
	if(!prog_attr || !tone_attr) {
		_tprintf(_T("Cannot alloc memory [F:%s][L:%d]\n"), 
			_T(__FUNCTION__), __LINE__);
		return false;
	}
	memset(prog_attr, 0, im->nVhIdx * PROGRAM_ATTRIBUTES * 
		MAX_PROGRAM_ATTRIBUTES * sizeof(unsigned char*));
	memset(tone_attr, 0, im->nVhIdx * sizeof(unsigned char**));

	for(unsigned long i = 0; i < im->nVhIdx; i++) {
		unsigned long vhsize = 0;
		unsigned long id = getlong(data + im->nVhOffset[i] + 
			sizeof(vh[i].FORMAT_ID) + sizeof(vh[i].FORMAT_VERSION));
		if(id != 0) {
			_tprintf(_T("No split for compress or non vh[%d] : offset -> %X\n"), 
				i + i, im->nVhOffset[i]);
			im->nVhSize[i] = 0;
			continue;
		}
		vh[i].FORMAT_ID = getlong(data + im->nVhOffset[i]);
		vhsize += sizeof(vh[i].FORMAT_ID);
		if(datasize <= im->nVhOffset[i] + vhsize) {
			im->nVhIdx--;
			break;
		}
		vh[i].FORMAT_VERSION = getlong(data + im->nVhOffset[i] + vhsize);
		vhsize += sizeof(vh[i].FORMAT_VERSION);
		if(datasize <= im->nVhOffset[i] + vhsize) {
			im->nVhIdx--;
			break;
		}
		vh[i].VAB_ID = getlong(data + im->nVhOffset[i] + vhsize);
		vhsize += sizeof(vh[i].VAB_ID);
		if(datasize <= im->nVhOffset[i] + vhsize) {
			im->nVhIdx--;
			break;
		}
		vh[i].VAB_SIZE = getlong(data + im->nVhOffset[i] + vhsize);
		vhsize += sizeof(vh[i].VAB_SIZE);
		if(datasize <= im->nVhOffset[i] + vhsize) {
			im->nVhIdx--;
			break;
		}
		vh[i].SYSTEM_RESERVED1 = getword(data + im->nVhOffset[i] + vhsize);
		vhsize += sizeof(vh[i].SYSTEM_RESERVED1);
		if(datasize <= im->nVhOffset[i] + vhsize) {
			im->nVhIdx--;
			break;
		}
		vh[i].TOTAL_NUM_OF_PROGRAMS = getword(data + im->nVhOffset[i] + vhsize);
		vhsize += sizeof(vh[i].TOTAL_NUM_OF_PROGRAMS);
		if(datasize <= im->nVhOffset[i] + vhsize) {
			im->nVhIdx--;
			break;
		}
		vh[i].TOTAL_NUM_OF_TONES = getword(data + im->nVhOffset[i] + vhsize);
		vhsize += sizeof(vh[i].TOTAL_NUM_OF_TONES);
		if(datasize <= im->nVhOffset[i] + vhsize) {
			im->nVhIdx--;
			break;
		}
		vh[i].TOTAL_NUM_OF_VAGS = getword(data + im->nVhOffset[i] + vhsize);
		vhsize += sizeof(vh[i].TOTAL_NUM_OF_VAGS);
		if(datasize <= im->nVhOffset[i] + vhsize) {
			im->nVhIdx--;
			break;
		}
		vh[i].MASTER_VOLUME = getbyte(data + im->nVhOffset[i] + vhsize);
		vhsize += sizeof(vh[i].MASTER_VOLUME);
		if(datasize <= im->nVhOffset[i] + vhsize) {
			im->nVhIdx--;
			break;
		}
		vh[i].MASTER_PAN = getbyte(data + im->nVhOffset[i] + vhsize);
		vhsize += sizeof(vh[i].MASTER_PAN);
		if(datasize <= im->nVhOffset[i] + vhsize) {
			im->nVhIdx--;
			break;
		}
		vh[i].BANK_ATTRIBUTE_1 = getbyte(data + im->nVhOffset[i] + vhsize);
		vhsize += sizeof(vh[i].BANK_ATTRIBUTE_1);
		if(datasize <= im->nVhOffset[i] + vhsize) {
			im->nVhIdx--;
			break;
		}
		vh[i].BANK_ATTRIBUTE_2 = getbyte(data + im->nVhOffset[i] + vhsize);
		vhsize += sizeof(vh[i].BANK_ATTRIBUTE_2);
		if(datasize <= im->nVhOffset[i] + vhsize) {
			break;
		}
		vh[i].SYSTEM_RESERVED2 = getlong(data + im->nVhOffset[i] + vhsize);
		vhsize += sizeof(vh[i].SYSTEM_RESERVED2);
		if(datasize <= im->nVhOffset[i] + vhsize) {
			im->nVhIdx--;
			break;
		}
		memcpy(prog_attr + i * PROGRAM_ATTRIBUTES * MAX_PROGRAM_ATTRIBUTES, 
			data + im->nVhOffset[i] + vhsize, PROGRAM_ATTRIBUTES * MAX_PROGRAM_ATTRIBUTES);
		vhsize += PROGRAM_ATTRIBUTES * MAX_PROGRAM_ATTRIBUTES;
		if(datasize <= im->nVhOffset[i] + vhsize) {
			im->nVhIdx--;
			break;
		}
		tone_attr[i] = (unsigned char*)malloc(TONE_ATTRIBUTES * 
			MAX_TONE_ATTRIBUTES * vh[i].TOTAL_NUM_OF_PROGRAMS * sizeof(unsigned char*));
		if(!tone_attr[i]) {
			_tprintf(_T("Cannot alloc memory [F:%s][L:%d]\n"), 
				_T(__FUNCTION__), __LINE__);
			FreeAndNull(prog_attr);
			for(unsigned long k = 0; k < i; k++) {
				if(im->isVh[k] == TRUE) {
					FreeAndNull(tone_attr[k]);
				}
			}
			FreeAndNull(tone_attr);
			return false;
		}
		memcpy(tone_attr[i], data + im->nVhOffset[i] + vhsize, 
			(size_t)(TONE_ATTRIBUTES * MAX_TONE_ATTRIBUTES * vh[i].TOTAL_NUM_OF_PROGRAMS));
		vhsize += TONE_ATTRIBUTES * MAX_TONE_ATTRIBUTES * vh[i].TOTAL_NUM_OF_PROGRAMS;
		if(datasize <= im->nVhOffset[i] + vhsize) {
			im->nVhIdx--;
			break;
		}
		memcpy(vag_offset + i * VAG_OFFSET_NUM, data + im->nVhOffset[i] + vhsize, VAG_OFFSET_SIZE);
		vhsize += VAG_OFFSET_SIZE;

		writeCsv(im->pszVhExt, i, im->nVhOffset[i], vhsize, im->nVhIdx, csvFp);
		FILE* outFp = CreateOrOpenFile(path, NULL, NULL, im->pszVhExt, _T("wb"), i, im->nVhIdx);
		if(outFp) {
			fwrite(&vh[i], sizeof(char), sizeof(VH), outFp);
			fwrite(prog_attr + i * PROGRAM_ATTRIBUTES * MAX_PROGRAM_ATTRIBUTES, 
				sizeof(char), PROGRAM_ATTRIBUTES * MAX_PROGRAM_ATTRIBUTES, outFp);
			fwrite(tone_attr[i], sizeof(char), (size_t)(TONE_ATTRIBUTES * 
				MAX_TONE_ATTRIBUTES * vh[i].TOTAL_NUM_OF_PROGRAMS), outFp);
			fwrite(vag_offset + i * VAG_OFFSET_NUM, sizeof(char), VAG_OFFSET_SIZE, outFp);
			FcloseAndNull(outFp);
			im->nVhSize[i] = vhsize;
			im->isVh[i] = TRUE;
			_tprintf(_T("\rCreate %s %4d/%4d"), im->pszVhExt, i + 1, im->nVhIdx);
		}
	}
	_tprintf(_T("\n"));
	FreeAndNull(prog_attr);
	for(unsigned long i = 0; i < im->nVhIdx; i++) {
		if(im->isVh[i] == TRUE) {
			FreeAndNull(tone_attr[i]);
		}
	}
	FreeAndNull(tone_attr);
	return true;
}

bool CreateVhForBastard(
	_TCHAR* path,
	unsigned char* data,
	FILE* csvFp,
	InternalMusic* im,
	VH* vh,
	unsigned short* vag_offset
	)
{
	for(unsigned long i = 0; i < im->nVhIdx; i++) {
		unsigned char* src = data + im->nVhOffset[i];
		unsigned int vhSize = getlong(src + 32);
		writeCsv(im->pszVhExt, i, im->nVhOffset[i], vhSize, im->nVhIdx, csvFp);
		FILE* outFp = 
			CreateOrOpenFile(path, NULL, NULL, im->pszVhExt, _T("wb"), i, im->nVhIdx);
		if(outFp) {
			fwrite(src, sizeof(char), vhSize, outFp);
			unsigned long vbSize = getlong(src + 36);
			unsigned long offset[VAG_OFFSET_NUM];
			memset(offset, 0, sizeof(offset));
			size_t cnt = 0;
			for(int k = 4; ; k += 4, cnt++) {
				offset[cnt] = getlong(src + vhSize - k);
				vh[i].TOTAL_NUM_OF_VAGS++;
				if(!offset[cnt]) {
					offset[vh[i].TOTAL_NUM_OF_VAGS] = vbSize;
					break;
				}
			}
			qsort(offset, vh[i].TOTAL_NUM_OF_VAGS, 
				sizeof(unsigned long), CompareUnsignedLong);
			*(vag_offset + i * VAG_OFFSET_NUM) = (unsigned short)(offset[0] / 8);
			for(size_t j = 1; j <= vh[i].TOTAL_NUM_OF_VAGS; j++) {
				*(vag_offset + i * VAG_OFFSET_NUM + j) = 
					(unsigned short)((offset[j] - offset[j-1]) / 8);
			}
			FcloseAndNull(outFp);
			im->nVhSize[i] = vhSize;
			im->isVh[i] = TRUE;
			_tprintf(_T("\rCreate %s %4d/%4d"), im->pszVhExt, i + 1, im->nVhIdx);
		}
	}
	_tprintf(_T("\n"));
	return true;
}

bool CreateSShd(
	_TCHAR* path, 
	unsigned char* data, 
	unsigned long datasize, 
	unsigned long* vbsize, 
	FILE* csvFp, 
	InternalMusic* im
	)
{
	for(unsigned long i = 0; i < im->nVhIdx; i++) {
		unsigned char* src = data + im->nVhOffset[i] - 12;
		unsigned int sshdSize = getlong(src);
		vbsize[i] = getlong(src + sizeof(unsigned long));

		if(sshdSize != 0 && sshdSize <= datasize && 
			vbsize[i] != 0 && vbsize[i] <= datasize) {
			writeCsv(im->pszVhExt, i, im->nVhOffset[i] - 12, sshdSize, im->nVhIdx, csvFp);
			FILE* outFp = 
				CreateOrOpenFile(path, NULL, NULL, im->pszVhExt, _T("wb"), i, im->nVhIdx);
			if(outFp) {
				fwrite(src, sizeof(char), sshdSize, outFp);
				FcloseAndNull(outFp);
				im->nVhSize[i] = sshdSize;
				im->isVh[i] = TRUE;
				_tprintf(_T("\rCreate %s %4d/%4d"), im->pszVhExt, i + 1, im->nVhIdx);
			}
		}
	}
	_tprintf(_T("\n"));
	return true;
}

bool CreateInst(
	_TCHAR* path, 
	unsigned char* data, 
	FILE* csvFp,
	InternalMusic* im
	)
{
	for(unsigned long i = 0; i < im->nVhIdx; i++) {
		unsigned char* src = data + im->nVhOffset[i];
		unsigned long size = getlong(src + 8);
		writeCsv(im->pszVhExt, i, im->nVhOffset[i], size, im->nVhIdx, csvFp);
		FILE* outFp = CreateOrOpenFile(path, NULL, NULL, 
			im->pszVhExt, _T("wb"), i, im->nVhIdx);
		if(outFp) {
			fwrite(src, sizeof(char), size, outFp);
			FcloseAndNull(outFp);
			im->nVhSize[i] = size;
			im->isVh[i] = TRUE;
			_tprintf(_T("\rCreate %s file %4d/%4d"), im->pszVhExt, i + 1, im->nSeqIdx);
		}
	}
	_tprintf(_T("\n"));
	return true;
}

bool CreateVbForVh(
	_TCHAR* path,
	unsigned char* data,
	unsigned long datasize,
	int is_vb_head_0byte,
	FILE* csvFp,
	InternalMusic* im,
	VH* vh,
	unsigned short* vag_offset
	)
{
	if(!im->nSeqIdx && !im->nVhIdx) {
		return false;
	}
	FILE* logVagFp = CreateOrOpenFile(path, NULL, NULL, 
		_T("_vag_offset.txt"), _T("w, ccs=UNICODE"), 0, 0);
	if(!logVagFp) {
		_tprintf(_T("No file or already opened [F:%s][L:%d]:%s\n"), 
			_T(__FUNCTION__), __LINE__, path);
		return false;
	}
	unsigned long longSeqSize = im->nSeqIdx * sizeof(unsigned long);
	unsigned long longVhSize = im->nVhIdx * sizeof(unsigned long);
	unsigned long longVbSize = im->nVhIdx * sizeof(unsigned long);
	size_t size = longSeqSize + longVhSize + longVbSize;
	unsigned long* offset_begin = (unsigned long*)malloc(size);
	unsigned long* offset_end = (unsigned long*)malloc(size);
	if(!offset_begin || !offset_end) {
		_tprintf(_T("Cannot alloc memory [F:%s][L:%d]\n"), 
			_T(__FUNCTION__), __LINE__);
		return false;
	}
	memset(offset_begin, 0x7F, size);
	if(im->nSeqIdx > 0) {
		memcpy(offset_begin, im->nSeqOffset, longSeqSize);
		memcpy(offset_begin + im->nSeqIdx, im->nVhOffset, longVhSize);
	}
	else {
		memcpy(offset_begin, im->nVhOffset, longVhSize);
	}
	qsort(offset_begin, im->nSeqIdx + im->nVhIdx, sizeof(unsigned long), CompareUnsignedLong);

	memset(offset_end, 0x7F, size);
	for(unsigned long sum = 0; sum < im->nSeqIdx; sum++) {
		offset_end[sum] = im->nSeqOffset[sum] + im->nSeqSize[sum] - 1;
	}
	for(unsigned long sum = 0; sum < im->nVhIdx; sum++) {
		offset_end[im->nSeqIdx + sum] = im->nVhOffset[sum] + im->nVhSize[sum] - 1;
	}
	qsort(offset_end, im->nSeqIdx + im->nVhIdx, sizeof(unsigned long), CompareUnsignedLong);

	_TCHAR buffer[128];
	memset(buffer, 0, sizeof(buffer));
#ifdef _DEBUG
	for(unsigned long sum = 0; sum < im->nSeqIdx; sum++) {
		_stprintf(buffer, _T("SeqOffset[%04d] %10d, size %8d\n"),
			sum + 1, im->nSeqOffset[sum], im->nSeqSize[sum]);
		OutputDebugString(buffer);
	}
	for(unsigned long sum = 0; sum < im->nVhIdx; sum++) {
		_stprintf(buffer, _T("VhOffset[%04d] %10d, size %8d\n"),
			sum + 1, im->nVhOffset[sum], im->nVhSize[sum]);
		OutputDebugString(buffer);
	}
	for(unsigned long sum = 0; sum < im->nSeqIdx + im->nVhIdx; sum++) {
		_stprintf(buffer, _T("SkipOffset[%04d] %10d-%10d(dec), %8X-%8X(hex)\n"), sum + 1,
			offset_begin[sum], offset_end[sum], offset_begin[sum], offset_end[sum]);
		OutputDebugString(buffer);
	}
#endif
	for(unsigned long i = 0; i < im->nVhIdx; i++) {
		if(im->isVh[i] == FALSE) {
			continue;
		}
		unsigned long all_vag_size = 0;
		unsigned int vag_size = 0;
		// multiple vag is vb
		for(int k = 1; k <= vh[i].TOTAL_NUM_OF_VAGS; k++) {
			vag_size = (unsigned int)((*((vag_offset + i * VAG_OFFSET_NUM) + k) << 3));
			_sntprintf(buffer, sizeof(buffer) / sizeof(_TCHAR), 
				_T("VbIndex:%d, VagNum:%02X/%02X, VagSize:%05X\n"), 
				i + 1, k, vh[i].TOTAL_NUM_OF_VAGS, vag_size);
			buffer[_tcslen(buffer)+1] = NULL;
			fwrite(buffer, sizeof(_TCHAR), _tcslen(buffer), logVagFp);
			all_vag_size += vag_size;
		}
		_sntprintf(buffer, sizeof(buffer) / sizeof(_TCHAR), 
			_T("VbIndex:%d, AllVag(=vb)Size:%08X\n"), i + 1, all_vag_size);
		buffer[_tcslen(buffer)+1] = NULL;
		fwrite(buffer, sizeof(_TCHAR), _tcslen(buffer), logVagFp);

		if(datasize < all_vag_size || all_vag_size == 0) {
			continue;
		}
		unsigned long seq_or_vh_idx = 0;
		for(unsigned long idx = 0; idx <= datasize - all_vag_size; idx++) {
			// skip seq & vh & vb data
			if(offset_begin[seq_or_vh_idx] <= idx && idx <= offset_end[seq_or_vh_idx]) {
				idx = offset_end[seq_or_vh_idx];
				if(seq_or_vh_idx < im->nSeqIdx + im->nVhIdx * 2) {
					seq_or_vh_idx++;
				}
				continue;
			}
			int cnt = 0;
			unsigned char* src = data + idx;
			unsigned char buf[36];
			memcpy(buf, src, sizeof(buf));

			unsigned int filter_num = 0;
			unsigned int filter_coefficient = 0;
			unsigned int adpcm_flag = 0;
			// 1st:check that 16byte is 0
			if(is_vb_head_0byte == TRUE) {
				for(int j = 0; j < sizeof(buf); j++) {
					if(buf[j] == 0x00) {
						cnt++;
					}
					else {
						break;
					}
				}
				if(cnt < 16) {
					continue;
				}
				filter_num = (unsigned int)((buf[16] >> 4) & 0x0F);
				filter_coefficient = (unsigned int)(buf[16] & 0x0F);
				adpcm_flag = buf[17];
			}
			else {
				filter_num = (unsigned int)((buf[0] >> 4) & 0x0F);
				filter_coefficient = (unsigned int)(buf[0] & 0x0F);
				adpcm_flag = buf[1];
			}
			// 2nd:check that first(16 + 2 byte) byte is VAG data
			if(filter_num <= 0x04 && filter_coefficient <= 0x0C &&
				adpcm_flag <= 0x07) {
				if((is_vb_head_0byte == TRUE && buf[16] == 0x00 && buf[17] == 0x00) ||
					(is_vb_head_0byte == FALSE && buf[0] == 0x00 && buf[1] == 0x00)) {
					continue;
				}
				BOOL bRet = TRUE;
				unsigned int vag_offset_real = 0;
				for(int k = 1; k <= vh[i].TOTAL_NUM_OF_VAGS; k++) {
					cnt = 0;
					vag_size = (unsigned int)((*((vag_offset + i * VAG_OFFSET_NUM) + k) << 3));
					vag_offset_real += vag_size;
					src = data + idx + vag_offset_real;
					unsigned char* endAdpcmFlag = src - 15;
					unsigned char* prevEndAdpcmFlag = endAdpcmFlag - 16;
					// 3rd:check that "end VAG data"
					//     almost 0x03 or 0x07, rare 0x01. 
					//     but sometimes contained garbage data.
					if(*endAdpcmFlag != 0x01 && *endAdpcmFlag != 0x03 && *endAdpcmFlag != 0x07 &&
						*prevEndAdpcmFlag != 0x03 && *prevEndAdpcmFlag != 0x07) {
						bRet = FALSE;
						break;
					}
					// 4th: check that all(except first & end) VAG data
					for(unsigned int p = 16; p < vag_size - 16; p += 16) {
						unsigned char byte = getbyte(src - vag_size + p);
						filter_num = (unsigned int)((byte >> 4) & 0x0F);
						filter_coefficient = (unsigned int)(byte & 0x0F);
						adpcm_flag = getbyte(src - vag_size + p + 1);

						if(filter_num > 0x04 || filter_coefficient > 0x0C ||
							adpcm_flag > 0x07) {
							unsigned long offset1 = 
								idx + vag_offset_real - vag_size + p;
							unsigned long offset2 = 
								idx + vag_offset_real - vag_size + p + 1;
							_sntprintf(buffer, sizeof(buffer) / sizeof(_TCHAR), 
								_T("[NotVag]VbIndex:%d, FileOffset:%08X, Offset1:%08X Value1:%02X, Offset2:%08X Value2:%02X\n"), 
								i + 1, idx, offset1, byte, offset2, adpcm_flag);
							fwrite(buffer, sizeof(_TCHAR), _tcslen(buffer), logVagFp);
							fflush(logVagFp);
							bRet = FALSE;
							break;
						}
					}
					if(bRet == FALSE || vag_offset_real >= all_vag_size) {
						break;
					}
					_sntprintf(buffer, sizeof(buffer) / sizeof(_TCHAR), 
						_T("VbIndex:%d, VagNum:%02X/%02X, FileOffset:%08X, NextOffset:%08X\n"), 
						i + 1, k, vh[i].TOTAL_NUM_OF_VAGS, idx + vag_offset_real - vag_size, idx + vag_offset_real);
					fwrite(buffer, sizeof(_TCHAR), _tcslen(buffer), logVagFp);
					fflush(logVagFp);
				}
				if(bRet == FALSE) {
					continue;
				}
				_sntprintf(buffer, sizeof(buffer) / sizeof(_TCHAR), 
					_T("VbIndex:%d, VagNum:%02X/%02X, FileOffset:%08X\n"), 
					i + 1, vh[i].TOTAL_NUM_OF_VAGS, vh[i].TOTAL_NUM_OF_VAGS,
					idx + vag_offset_real - vag_size);
				fwrite(buffer, sizeof(_TCHAR), _tcslen(buffer), logVagFp);
				fflush(logVagFp);

				// for skip vb data
				offset_begin[im->nSeqIdx + im->nVhIdx + i] = idx;
				offset_end[im->nSeqIdx + im->nVhIdx + i] = idx + all_vag_size - 1;
				qsort(offset_begin, im->nSeqIdx + im->nVhIdx + i + 1, 
					sizeof(unsigned long), CompareUnsignedLong);
				qsort(offset_end, im->nSeqIdx + im->nVhIdx + i + 1, 
					sizeof(unsigned long), CompareUnsignedLong);
#ifdef _DEBUG
				for(unsigned long sum = 0; sum < im->nSeqIdx + im->nVhIdx + i + 1; sum++) {
					_stprintf(buffer, _T("SkipOffset[%04d] %10d-%10d(dec), %8X-%8X(hex)\n"), 
						sum + 1, offset_begin[sum], offset_end[sum], 
						offset_begin[sum], offset_end[sum]);
					OutputDebugString(buffer);
				}
#endif
				writeCsv(_T(".vb"), i, idx, all_vag_size, im->nVhIdx, csvFp);
				FILE* outFp = CreateOrOpenFile(path, NULL, NULL, _T(".vb"), _T("wb"), i, im->nVhIdx);
				if(outFp) {
					fwrite(data + idx, sizeof(char), all_vag_size, outFp);
					FcloseAndNull(outFp);
					im->isVb[i] = TRUE;
					_tprintf(_T("\rCreate .vb %4d/%4d"), i + 1, im->nVhIdx);
				}
				break;
			}
		}
	}
	_tprintf(_T("\n"));
	FreeAndNull(offset_begin);
	FreeAndNull(offset_end);
	FcloseAndNull(logVagFp);
	return true;
}

bool CreateVbForSShd(
	_TCHAR* path, 
	unsigned char* data, 
	unsigned long datasize, 
	unsigned long* vbsize, 
	int is_vb_head_0byte, 
	FILE* csvFp, 
	InternalMusic* im
	)
{
	unsigned char buf[36];
	unsigned char* src = NULL;
	unsigned char* endAdpcmFlag = NULL;
	int cnt = 0;
	unsigned long idx = 0;
	unsigned long prev_idx = 0;
	int isVB = FALSE;
	FILE* outFp = NULL;
	_TCHAR strOutfile[_MAX_PATH];

	UNREFERENCED_PARAMETER(is_vb_head_0byte);
	memset(strOutfile, 0, sizeof(strOutfile));
	for(unsigned long i = 0; i < im->nVhIdx; i++) {
		if(datasize < vbsize[i] || vbsize[i] == 0) {
			continue;
		}
		if(isVB == FALSE) {
			idx = prev_idx;
		}
		else {
			isVB = FALSE;
		}
		for(; idx <= datasize - vbsize[i]; idx++) {
			cnt = 0;
			src = data + idx;
			memcpy(buf, src, sizeof(buf));
			for(int j = 0; j < sizeof(buf); j++) {
				if(buf[j] == 0x00) {
					cnt++;
				}
				else {
					break;
				}
			}
			if(cnt >= 16 && buf[16] <= 0x4F && (buf[17] == 0x00 || 
												buf[17] == 0x02 || 
												buf[17] == 0x04 || 
												buf[17] == 0x05 || 
												buf[17] == 0x06)) {
				cnt = 0;
				if(buf[16] == 0x00 && buf[17] == 0x00) {
					continue;
				}
				int bRet = TRUE;
				src = data + idx + vbsize[i];
				endAdpcmFlag = src - 15;

				if(*endAdpcmFlag == 0x01 || 
					*endAdpcmFlag == 0x03 ||
					*endAdpcmFlag == 0x07) {
					for(unsigned int p = 0; p < vbsize[i]; p += 16) {
						if(getbyte(src - vbsize[i] + p) >= 0x4F ||
							getbyte(src - vbsize[i] + p + 1) >= 0x08) {
							bRet = FALSE;
							break;
						}
					}
					if(bRet == FALSE) {
						continue;
					}
					writeCsv(_T(".vb"), i, idx, vbsize[i], im->nVhIdx, csvFp);
					outFp = CreateOrOpenFile(path, NULL, NULL, _T(".vb"), _T("wb"), i, im->nVhIdx);
					if(outFp) {
						fwrite(data + idx, sizeof(char), vbsize[i], outFp);
						FcloseAndNull(outFp);
						im->isVb[i] = TRUE;
						idx += vbsize[i];
						prev_idx = idx;
						isVB = TRUE;
						_tprintf(_T("\rCreate .vb %4d/%4d"), i + 1, im->nVhIdx);
						break;
					}
				}
			}
		}
	}
	_tprintf(_T("\n"));
	return true;
}

bool CreateVbForInst(
	_TCHAR* path, 
	unsigned char* data, 
	FILE* csvFp,
	InternalMusic* im
	)
{
	for(unsigned long i = 0; i < im->nVhIdx; i++) {
		unsigned char* src = data + im->nVhOffset[i];
		unsigned long size = getlong(src + 8);
		unsigned char* vb = src + size;
		unsigned long vbSize = getlong(src + 16);
		writeCsv(_T(".vb"), i, im->nVhOffset[i] + size, im->nVhOffset[i] + size, im->nVhIdx, csvFp);
		FILE* outFp = CreateOrOpenFile(path, NULL, NULL, _T(".vb"), _T("wb"), i, im->nVhIdx);
		if(outFp) {
			fwrite(vb, sizeof(char), vbSize, outFp);
			FcloseAndNull(outFp);
			im->isVb[i] = TRUE;
			_tprintf(_T("\rCreate .vb file %4d/%4d"), i + 1, im->nSeqIdx);
		}
	}
	_tprintf(_T("\n"));
	return true;
}
