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

FILE* CreateOrOpenFile(
	LPCTSTR pszSrcPath,
	LPTSTR pszSrcPathWithoutFileName,
	LPTSTR pszOutPath,
	LPTSTR pszFileNameWithoutPath,
	LPCTSTR pszExt,
	LPCTSTR pszMode,
	DWORD nFileCnt,
	DWORD nMaxFileNum
	)
{
	_TCHAR szDstPath[_MAX_PATH] = { 0 };
	_TCHAR drive[_MAX_DRIVE] = { 0 };
	_TCHAR dir[_MAX_DIR] = { 0 };
	_TCHAR fname[_MAX_FNAME] = { 0 };
	_TCHAR ext[_MAX_EXT] = { 0 };

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

	if (pszSrcPathWithoutFileName != NULL) {
		_tmakepath(pszSrcPathWithoutFileName, drive, dir, NULL, NULL);
	}
	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, pszMode);
}

void writeCsv(
	_TCHAR* filetype,
	DWORD no,
	DWORD ofs,
	DWORD size,
	DWORD nMaxIdx,
	DWORD seqNum,
	FILE* csvFp
	)
{
	DWORD endOfs = ofs + size - 1;
	if (1 <= nMaxIdx && nMaxIdx < 10) {
		if (!_tcscmp(filetype, _T(".sep"))) {
			_ftprintf(csvFp, _T("%s,%01u,%9u-%9u,%8x-%8x,%6u,%5x,%2u\n"),
				filetype, no + 1, ofs, endOfs, ofs, endOfs, size, size, seqNum + 1);
		}
		else {
			_ftprintf(csvFp, _T("%s,%01u,%9u-%9u,%8x-%8x,%6u,%5x,\n"),
				filetype, no + 1, ofs, endOfs, ofs, endOfs, size, size);
		}
	}
	else if (10 <= nMaxIdx && nMaxIdx < 100) {
		if (!_tcscmp(filetype, _T(".sep"))) {
			_ftprintf(csvFp, _T("%s,%02u,%9u-%9u,%8x-%8x,%6u,%5x,%2u\n"),
				filetype, no + 1, ofs, endOfs, ofs, endOfs, size, size, seqNum + 1);
		}
		else {
			_ftprintf(csvFp, _T("%s,%02u,%9u-%9u,%8x-%8x,%6u,%5x,\n"),
				filetype, no + 1, ofs, endOfs, ofs, endOfs, size, size);
		}
	}
	else if (100 <= nMaxIdx && nMaxIdx < 1000) {
		if (!_tcscmp(filetype, _T(".sep"))) {
			_ftprintf(csvFp, _T("%s,%03u,%9u-%9u,%8x-%8x,%6u,%5x,%2u\n"),
				filetype, no + 1, ofs, endOfs, ofs, endOfs, size, size, seqNum + 1);
		}
		else {
			_ftprintf(csvFp, _T("%s,%03u,%9u-%9u,%8x-%8x,%6u,%5x,\n"),
				filetype, no + 1, ofs, endOfs, ofs, endOfs, size, size);
		}
	}
	else if (1000 <= nMaxIdx && nMaxIdx < 10000) {
		if (!_tcscmp(filetype, _T(".sep"))) {
			_ftprintf(csvFp, _T("%s,%04u,%9u-%9u,%8x-%8x,%6u,%5x,%2u\n"),
				filetype, no + 1, ofs, endOfs, ofs, endOfs, size, size, seqNum + 1);
		}
		else {
			_ftprintf(csvFp, _T("%s,%04u,%9u-%9u,%8x-%8x,%6u,%5x,\n"),
				filetype, no + 1, ofs, endOfs, ofs, endOfs, size, size);
		}
	}
	fflush(csvFp);
}

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

BOOL CreateSeq(
	_TCHAR* path, 
	LPBYTE data, 
	DWORD dataSize, 
	FILE* csvFp, 
	PINTERNAL_MUSIC im
	)
{
	PSEQ_HDR seq = (PSEQ_HDR)calloc(im->nSeqIdx, sizeof(SEQ_HDR));
	if (!seq) {
		OutputLastErrorNumAndString(_T(__FUNCTION__), __LINE__);
		return FALSE;
	}
	DWORD maxSeqBodySize = MAX_SEQ_SIZE - sizeof(SEQ_HDR);
	for (DWORD i = 0; i < im->nSeqIdx; i++) {
		memcpy(&seq[i], data + im->nSeqOfs[i], sizeof(SEQ_HDR));
		DWORD id = seq[i].id & 0x0000FFFF;
		if (id != 0) {
			OutputErrorLog(
				_T("Not split for compress or non seq[%u]. ofs: %x\n"), 
				i + 1, im->nSeqOfs[i]);
			im->nSeqSize[i] = 4;
			continue;
		}
		for (DWORD ofs = sizeof(SEQ_HDR); ofs <= maxSeqBodySize; ofs++) {
			if (dataSize < im->nSeqOfs[i] + ofs) {
				break;
			}
			LPBYTE src = data + im->nSeqOfs[i] + ofs;
			DWORD longByte = getlong(src);
			// search eof
			if ((longByte & 0xFFFFFFFF) == 0x002FFF00) {
				DWORD seqSize = ofs + sizeof(longByte);
				DWORD seqNum = 0;
				WORD seqId = seq[i].id >> 16;
				if (seqId == 0x0000) {
					if (_tcscmp(im->pszSeqExt, _T(".sep"))) {
						_tcscpy(im->pszSeqExt, _T(".sep"));
					}
					_tcscpy(im->pszSeqExt, _T(".sep"));
					WORD prevSeqId = seqId;
					LPBYTE nextSrc = src + sizeof(longByte);

					for (int j = 0; j < MAX_SEQ_IN_SEP; j++) {
						WORD nextSeqId = getword(nextSrc);

						if (nextSeqId == prevSeqId + 0x0100) {
							prevSeqId += 0x0100;
							for (int k = 0; k <= MAX_SEQ_SIZE; k++) {
								longByte = getlong(nextSrc + sizeof(nextSeqId) + k);
								// search eof
								if ((longByte & 0xFFFFFF00) == 0x002FFF00) {
									nextSrc += sizeof(nextSeqId) + k + sizeof(longByte);
									seqSize += sizeof(nextSeqId) + k + sizeof(longByte);
									seqNum++;
									break;
								}
							}
						}
						else {
							break;
						}
					}
				}
				else {
					if (_tcscmp(im->pszSeqExt, _T(".seq"))) {
						_tcscpy(im->pszSeqExt, _T(".seq"));
					}
				}
				writeCsv(im->pszSeqExt, i, 
					im->nSeqOfs[i], seqSize, im->nSeqIdx, seqNum, csvFp);
				FILE* outFp = CreateOrOpenFile(path, NULL, 
					NULL, NULL, im->pszSeqExt, _T("wb"), i, im->nSeqIdx);
				if (!outFp) {
					OutputLastErrorNumAndString(_T(__FUNCTION__), __LINE__);
					return FALSE;
				}
				fwrite(data + im->nSeqOfs[i], sizeof(CHAR), seqSize, outFp);
				paddingSeq(seqSize, outFp);
				FcloseAndNull(outFp);
				im->nSeqSize[i] = seqSize;
				im->isSeq[i] = TRUE;
				OutputString(
					_T("\rCreate %s %4u/%4u"), im->pszSeqExt, i + 1, im->nSeqIdx);
				break;
			}
		}
		if (!im->isSeq[i]) {
			OutputErrorLog(_T("Invalid seq. ofs: %u, %#x\n"), im->nSeqOfs[i], im->nSeqOfs[i]);
		}
	}
	OutputString(_T("\n"));
	FreeAndNull(seq);
	return TRUE;
}

BOOL CreateSSsq(
	_TCHAR* path, 
	LPBYTE data, 
	DWORD dataSize, 
	FILE* csvFp, 
	PINTERNAL_MUSIC im
	)
{
	for (DWORD i = 0; i < im->nSeqIdx; i++) {
		//maxsize SSsq (no evidence)
		for (int ofs = 0; ofs < 65535; ofs++) {
			LPBYTE src = data + im->nSeqOfs[i] + ofs;
			DWORD longByte = getlong(src);
			// search eof
			if ((longByte & 0xFFFFFF00) == 0x002FFF00) {
				DWORD sssqSize = ofs + sizeof(longByte);
				if (sssqSize != 0 && sssqSize <= dataSize) {
					writeCsv(im->pszSeqExt, i, 
						im->nSeqOfs[i] - 12, sssqSize, im->nSeqIdx, 0, csvFp);
					FILE* outFp = CreateOrOpenFile(path, NULL, 
						NULL, NULL, im->pszSeqExt, _T("wb"), i, im->nSeqIdx);
					if (!outFp) {
						OutputLastErrorNumAndString(_T(__FUNCTION__), __LINE__);
						return FALSE;
					}
					DWORD realSssqSize = sssqSize + 12;
					fwrite(data + im->nSeqOfs[i] - 12, sizeof(CHAR), realSssqSize, outFp);
					paddingSeq(realSssqSize, outFp);
					FcloseAndNull(outFp);
					im->nSeqSize[i] = realSssqSize;
					im->isSeq[i] = TRUE;
					OutputString(
						_T("\rCreate %s %4u/%4u"), im->pszSeqExt, i + 1, im->nSeqIdx);
					break;
				}
			}
		}
	}
	OutputString(_T("\n"));
	return TRUE;
}

BOOL CreateKcet(
	_TCHAR* path, 
	LPBYTE data, 
	DWORD dataSize, 
	FILE* csvFp, 
	PINTERNAL_MUSIC im
	)
{
	for (DWORD i = 0; i < im->nSeqIdx; i++) {
		LPBYTE src = data + im->nSeqOfs[i];
		DWORD kcetSize = getlong(src + 4);
		if (kcetSize != 0 && kcetSize <= dataSize) {
			DWORD realKcetSize = kcetSize + 8;
			writeCsv(im->pszSeqExt, i, 
				im->nSeqOfs[i], realKcetSize, im->nSeqIdx, 0, csvFp);
			FILE* outFp = CreateOrOpenFile(path, NULL, NULL, 
				NULL, im->pszSeqExt, _T("wb"), i, im->nSeqIdx);
			if (!outFp) {
				OutputLastErrorNumAndString(_T(__FUNCTION__), __LINE__);
				return FALSE;
			}
			fwrite(src, sizeof(CHAR), realKcetSize, outFp);
			FcloseAndNull(outFp);
			im->nSeqSize[i] = realKcetSize;
			im->isSeq[i] = TRUE;
			OutputString(
				_T("\rCreate %s %4u/%4u"), im->pszSeqExt, i + 1, im->nSeqIdx);
		}
	}
	OutputString(_T("\n"));
	return TRUE;
}

BOOL CreateSntp(
	_TCHAR* path, 
	LPBYTE data, 
	DWORD dataSize, 
	FILE* csvFp,
	PINTERNAL_MUSIC im
	)
{
	for (DWORD i = 0; i < im->nSeqIdx; i++) {
		LPBYTE src = data + im->nSeqOfs[i] - 12;
		DWORD sntpHeaderSize = getlong(data + im->nSeqOfs[i] - 4);
//		WORD attrNum = getword(data + im->nSeqOfs[i] + 8);

		DWORD allHeaderSize = 0;
		DWORD tmp = 0;
		DWORD smfOfs = 0;
		DWORD smfOfsNum = 0;
		for (smfOfs = 4; ; smfOfs+=4, smfOfsNum++) {
			if (sntpHeaderSize + smfOfs >= dataSize) {
				break;
			}
			tmp = getlong(src + sntpHeaderSize + smfOfs);
			if (smfOfs == 8 && tmp == 0x00000008) {
				smfOfsNum--;
			}
			if (tmp == 0x00200000) {
				allHeaderSize = sntpHeaderSize + smfOfs;
				smfOfsNum--;
				break;
			}
		}
		DWORD sntpSize = getlong(src + allHeaderSize - 4);
		if ((sntpSize & 0x00FF0000) > 0) {
			sntpSize = getlong(src + allHeaderSize - 8);
		}
#ifdef _DEBUG
		FILE* logFp = CreateOrOpenFile(path, NULL, NULL, 
			NULL, _T(".txt"), _T("wb"), i, im->nSeqIdx);
		if (!logFp) {
			OutputLastErrorNumAndString(_T(__FUNCTION__), __LINE__);
			return FALSE;
		}
		for (DWORD longData = 4, idx = 0;; longData += 4) {
			tmp = getlong(src + allHeaderSize + longData);
			_ftprintf(logFp, _T("%04x : %04x : %02x %02x %02x %02x\n"), 
				im->nSeqOfs[i] - 12 + allHeaderSize + longData, 
				allHeaderSize + longData,
				(tmp & 0x000000FF), (tmp & 0x0000FF00) >> 8, 
				(tmp & 0x00FF0000) >> 16, (tmp & 0xFF000000) >> 24);
			if (tmp == 0x00200000) {
				idx++;
				if (idx == smfOfsNum) {
					break;
				}
			}
		}
		FcloseAndNull(logFp);
#endif
		for (DWORD lastData = 4; ; lastData += 4) {
			tmp = getlong(src + allHeaderSize + sntpSize + lastData);
			if (tmp == 0x00200000) {
				DWORD allSize = allHeaderSize + sntpSize + lastData + 4;
				writeCsv(im->pszSeqExt, i, 
					im->nSeqOfs[i] - 12, allSize, im->nSeqIdx, 0, csvFp);
				FILE* outFp = CreateOrOpenFile(path, NULL, NULL, 
					NULL, im->pszSeqExt, _T("wb"), i, im->nSeqIdx);
				if (!outFp) {
					OutputLastErrorNumAndString(_T(__FUNCTION__), __LINE__);
					return FALSE;
				}
				fwrite(src, sizeof(CHAR), allSize, outFp);
				FcloseAndNull(outFp);
				im->nSeqSize[i] = allSize;
				im->isSeq[i] = TRUE;
				OutputString(
					_T("\rCreate %s %4u/%4u"), im->pszSeqExt, i + 1, im->nSeqIdx);
				break;
			}
		}
	}
	OutputString(_T("\n"));
	return TRUE;
}

BOOL CreateSeqg(
	_TCHAR* path, 
	LPBYTE data, 
	FILE* csvFp,
	PINTERNAL_MUSIC im
	)
{
	for (DWORD i = 0; i < im->nSeqIdx; i++) {
		LPBYTE src = data + im->nSeqOfs[i];
		DWORD size = getlong(src + 80) + 8;
		writeCsv(im->pszSeqExt, i, 
			im->nSeqOfs[i], size, im->nSeqIdx, 0, csvFp);
		FILE* outFp = CreateOrOpenFile(path, NULL, NULL, 
			NULL, im->pszSeqExt, _T("wb"), i, im->nSeqIdx);
		if (!outFp) {
			OutputLastErrorNumAndString(_T(__FUNCTION__), __LINE__);
			return FALSE;
		}
		fwrite(src, sizeof(CHAR), size, outFp);
		FcloseAndNull(outFp);
		im->nSeqSize[i] = size;
		im->isSeq[i] = TRUE;
		OutputString(
			_T("\rCreate %s %4u/%4u"), im->pszSeqExt, i + 1, im->nSeqIdx);
	}
	OutputString(_T("\n"));
	return TRUE;
}

BOOL CreateSeqq(
	_TCHAR* path, 
	LPBYTE data, 
	FILE* csvFp,
	PINTERNAL_MUSIC im
	)
{
	for (DWORD i = 0; i < im->nSeqIdx; i++) {
		DWORD seqqPos = im->nSeqOfs[i] - 0x3C;
		LPBYTE src = data + seqqPos;
		DWORD size = getlong(data + seqqPos);
		writeCsv(im->pszSeqExt, i, seqqPos, size + 0x3C, im->nSeqIdx, 0, csvFp);
		FILE* outFp = CreateOrOpenFile(path, NULL, NULL,
			NULL, im->pszSeqExt, _T("wb"), i, im->nSeqIdx);
		if (!outFp) {
			OutputLastErrorNumAndString(_T(__FUNCTION__), __LINE__);
			return FALSE;
		}
		fwrite(src, sizeof(CHAR), size + 0x3C, outFp);
		FcloseAndNull(outFp);
		im->nSeqSize[i] = size;
		im->isSeq[i] = TRUE;
		OutputString(
			_T("\rCreate %s %4u/%4u"), im->pszSeqExt, i + 1, im->nSeqIdx);
	}
	_TCHAR ext[_MAX_EXT] = { 0 };
	_tsplitpath(path, NULL, NULL, NULL, ext);
	FILE* outFp = 
		CreateOrOpenFile(path, NULL, NULL, NULL, _T(".vb"), _T("wb"), 0, 2);
	if (!outFp) {
		OutputLastErrorNumAndString(_T(__FUNCTION__), __LINE__);
		return FALSE;
	}
	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, NULL, _T(".vb"), _T("wb"), 1, 2);
	if (!outFp) {
		OutputLastErrorNumAndString(_T(__FUNCTION__), __LINE__);
		return FALSE;
	}
	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);
	OutputString(_T("\n"));
	return TRUE;
}

BOOL CreateSqv(
	_TCHAR* path, 
	LPBYTE data, 
	FILE* csvFp,
	PINTERNAL_MUSIC im
	)
{
	for (DWORD i = 0; i < im->nSeqIdx; i++) {
		INT max = 1;
		INT cnt = 0;
		for (DWORD ofs = 8; ofs < 16; ofs += 4) {
			LPBYTE src = data + im->nSeqOfs[i] + ofs;
			DWORD longByte = getlong(src);
			if (longByte > 0) {
				max++;
			}
		}
		for (DWORD ofs = 0; ofs < MAX_SEQ_SIZE; ofs++) {
			LPBYTE src = data + im->nSeqOfs[i] + ofs;
			DWORD longByte = getlong(src);
			// search eof
			if ((longByte & 0xFFFFFF00) == 0x002FFF00) {
				cnt++;
				if (cnt == max) {
					DWORD seqSize = sizeof(longByte) + ofs;
					writeCsv(im->pszSeqExt, i,
						im->nSeqOfs[i], seqSize, im->nSeqIdx, 0, csvFp);
					FILE* outFp = CreateOrOpenFile(path, NULL, NULL,
						NULL, im->pszSeqExt, _T("wb"), i, im->nSeqIdx);
					if (!outFp) {
						OutputLastErrorNumAndString(_T(__FUNCTION__), __LINE__);
						return FALSE;
					}
					fwrite(data + im->nSeqOfs[i], sizeof(CHAR), seqSize, outFp);
					paddingSeq(seqSize, outFp);
					FcloseAndNull(outFp);
					im->nSeqSize[i] = seqSize;
					im->isSeq[i] = TRUE;
					OutputString(_T("\rCreate %s %4u/%4u"),
						im->pszSeqExt, i + 1, im->nSeqIdx);
					break;
				}
			}
		}
	}
	OutputString(_T("\n"));
	return TRUE;
}

BOOL CreateSseq(
	_TCHAR* path, 
	LPBYTE data, 
	FILE* csvFp,
	PINTERNAL_MUSIC im
	)
{
	for (DWORD i = 0; i < im->nSeqIdx; i++) {
		for (DWORD ofs = 0; ofs < MAX_SEQ_SIZE; ofs++) {
			LPBYTE src = data + im->nSeqOfs[i] + ofs;
			if (*src == 0xf3) {
				DWORD seqSize = ofs + 1;
				writeCsv(im->pszSeqExt, i,
					im->nSeqOfs[i], seqSize, im->nSeqIdx, 0, csvFp);
				FILE* outFp = CreateOrOpenFile(path, NULL, NULL,
					NULL, im->pszSeqExt, _T("wb"), i, im->nSeqIdx);
				if (!outFp) {
					OutputLastErrorNumAndString(_T(__FUNCTION__), __LINE__);
					return FALSE;
				}
				fwrite(data + im->nSeqOfs[i], sizeof(CHAR), seqSize, outFp);
				paddingSeq(seqSize, outFp);
				FcloseAndNull(outFp);
				im->nSeqSize[i] = seqSize;
				im->isSeq[i] = TRUE;
				OutputString(_T("\rCreate %s %4u/%4u"),
					im->pszSeqExt, i + 1, im->nSeqIdx);
				break;
			}
		}
	}
	OutputString(_T("\n"));
	return TRUE;
}

BOOL CreateVh(
	_TCHAR* path, 
	LPBYTE data, 
	DWORD dataSize, 
	FILE* csvFp, 
	PINTERNAL_MUSIC im,
	PVH vh,
	LPWORD vagSizeTbl
	)
{
	LPBYTE progAttr =
		(LPBYTE)calloc(im->nVhIdx * MAX_PROG_ATTR_SIZE, sizeof(LPBYTE));
	if (!progAttr) {
		OutputLastErrorNumAndString(_T(__FUNCTION__), __LINE__);
		return FALSE;
	}
	LPBYTE* toneAttr = (LPBYTE*)calloc(im->nVhIdx, sizeof(LPBYTE*));
	if (!toneAttr) {
		FreeAndNull(progAttr);
		OutputLastErrorNumAndString(_T(__FUNCTION__), __LINE__);
		return FALSE;
	}

	for (DWORD i = 0; i < im->nVhIdx; i++) {
		DWORD ofs = im->nVhOfs[i] +
			sizeof(vh[i].form) + sizeof(vh[i].ver) +
			sizeof(vh[i].id) + sizeof(vh[i].fsize) +
			sizeof(vh[i].reserved0);
		WORD tmpTotalNumOfPrograms = getword(data + ofs);
		DWORD maxToneAttrSize = (DWORD)(MIN_TONE_ATTR_SIZE * tmpTotalNumOfPrograms);
		DWORD vhSize = sizeof(VH) + 
			MAX_PROG_ATTR_SIZE + maxToneAttrSize + VAG_SIZE_TBL_SIZE;

		toneAttr[i] = (LPBYTE)calloc(maxToneAttrSize, sizeof(LPBYTE));
		if (!toneAttr[i]) {
			OutputLastErrorNumAndString(_T(__FUNCTION__), __LINE__);
			break;
		}
		if (im->nVhOfs[i] + vhSize > dataSize) {
			im->nVhIdx = i;
			im->isVh[i] = FALSE;
			break;
		}
		ofs = im->nVhOfs[i] + sizeof(vh[i].form) + sizeof(vh[i].ver);
		DWORD tmpid = getlong(data + ofs);
		if (tmpid != 0) {
			OutputErrorLog(
				_T("Not split for compress or non vh[%u]. ofs: %x\n"), 
				i + i, im->nVhOfs[i]);
			im->nVhSize[i] = 0;
			im->isVh[i] = FALSE;
			continue;
		}
		ofs = im->nVhOfs[i];
		vh[i].form = (LONG)getlong(data + ofs);

		ofs += sizeof(vh[i].form);
		vh[i].ver = (LONG)getlong(data + ofs);

		ofs += sizeof(vh[i].ver);
		vh[i].id = (LONG)tmpid;

		ofs += sizeof(vh[i].id);
		vh[i].fsize = getlong(data + ofs);

		ofs += sizeof(vh[i].fsize);
		vh[i].reserved0 = getword(data + ofs);

		ofs += sizeof(vh[i].reserved0);
		vh[i].TotalNumOfPrograms = tmpTotalNumOfPrograms;

		ofs += sizeof(vh[i].TotalNumOfPrograms);
		vh[i].TotalNumOfTones = getword(data + ofs);

		ofs += sizeof(vh[i].TotalNumOfTones);
		vh[i].TotalNumOfVags = getword(data + ofs);

		ofs += sizeof(vh[i].TotalNumOfVags);
		vh[i].MasterVolume = getbyte(data + ofs);

		ofs += sizeof(vh[i].MasterVolume);
		vh[i].MasterPan = getbyte(data + ofs);

		ofs += sizeof(vh[i].MasterPan);
		vh[i].BankAttribute1 = getbyte(data + ofs);

		ofs += sizeof(vh[i].BankAttribute1);
		vh[i].BankAttribute2 = getbyte(data + ofs);

		ofs += sizeof(vh[i].BankAttribute2);
		vh[i].SystemReserved2 = getlong(data + ofs);

		ofs += sizeof(vh[i].SystemReserved2);
		memcpy(progAttr + i * MAX_PROG_ATTR_SIZE, data + ofs, MAX_PROG_ATTR_SIZE);

		ofs += MAX_PROG_ATTR_SIZE;
		memcpy(toneAttr[i], data + ofs, maxToneAttrSize);

		ofs += maxToneAttrSize;
		DWORD ofsOfVag = i * VAG_SIZE_TBL_NUM;
		memcpy(vagSizeTbl + ofsOfVag, data + ofs, VAG_SIZE_TBL_SIZE);

		writeCsv(im->pszVhExt, i, im->nVhOfs[i], vhSize, im->nVhIdx, 0, csvFp);
		FILE* outFp = CreateOrOpenFile(path, NULL, NULL,
			NULL, im->pszVhExt, _T("wb"), i, im->nVhIdx);
		if (!outFp) {
			OutputLastErrorNumAndString(_T(__FUNCTION__), __LINE__);
			return FALSE;
		}
		fwrite(&vh[i], sizeof(CHAR), sizeof(VH), outFp);
		fwrite(progAttr + i * MAX_PROG_ATTR_SIZE, sizeof(CHAR), MAX_PROG_ATTR_SIZE, outFp);
		fwrite(toneAttr[i], sizeof(CHAR), maxToneAttrSize, outFp);
		fwrite(vagSizeTbl + ofsOfVag, sizeof(CHAR), VAG_SIZE_TBL_SIZE, outFp);
		FcloseAndNull(outFp);
		im->nVhSize[i] = vhSize;
		im->isVh[i] = TRUE;
		OutputString(
			_T("\rCreate %s %4u/%4u"), im->pszVhExt, i + 1, im->nVhIdx);
	}
	OutputString(_T("\n"));
	FreeAndNull(progAttr);
	for (DWORD i = 0; i < im->nVhIdx; i++) {
		FreeAndNull(toneAttr[i]);
	}
	FreeAndNull(toneAttr);
	return TRUE;
}

BOOL CreateVhForBastard(
	_TCHAR* path,
	LPBYTE data,
	FILE* csvFp,
	PINTERNAL_MUSIC im,
	PVH vh,
	LPWORD vagSizeTbl
	)
{
	for (DWORD i = 0; i < im->nVhIdx; i++) {
		LPBYTE src = data + im->nVhOfs[i];
		DWORD vhSize = getlong(src + 32);
		writeCsv(im->pszVhExt, i, im->nVhOfs[i], vhSize, im->nVhIdx, 0, csvFp);
		FILE* outFp = CreateOrOpenFile(path, NULL, NULL, 
			NULL, im->pszVhExt, _T("wb"), i, im->nVhIdx);
		if (!outFp) {
			OutputLastErrorNumAndString(_T(__FUNCTION__), __LINE__);
			return FALSE;
		}
		fwrite(src, sizeof(CHAR), vhSize, outFp);
		DWORD vbSize = getlong(src + 36);
		DWORD ofs[VAG_SIZE_TBL_NUM] = { 0 };
		size_t cnt = 0;
		for (int k = 4; ; k += 4, cnt++) {
			ofs[cnt] = getlong(src + vhSize - k);
			vh[i].TotalNumOfVags++;
			if (!ofs[cnt]) {
				ofs[vh[i].TotalNumOfVags] = vbSize;
				break;
			}
		}
		qsort(ofs, vh[i].TotalNumOfVags, sizeof(DWORD), CompareUnsignedLong);
		*(vagSizeTbl + i * VAG_SIZE_TBL_NUM) = (WORD)(ofs[0] / 8);
		for (size_t j = 1; j <= vh[i].TotalNumOfVags; j++) {
			*(vagSizeTbl + i * VAG_SIZE_TBL_NUM + j) = 
				(WORD)((ofs[j] - ofs[j-1]) / 8);
		}
		FcloseAndNull(outFp);
		im->nVhSize[i] = vhSize;
		im->isVh[i] = TRUE;
		OutputString(
			_T("\rCreate %s %4u/%4u"), im->pszVhExt, i + 1, im->nVhIdx);
	}
	OutputString(_T("\n"));
	return TRUE;
}

BOOL CreateSShd(
	_TCHAR* path, 
	LPBYTE data, 
	DWORD dataSize, 
	LPDWORD vbSize, 
	FILE* csvFp, 
	PINTERNAL_MUSIC im
	)
{
	for (DWORD i = 0; i < im->nVhIdx; i++) {
		LPBYTE src = data + im->nVhOfs[i] - 12;
		DWORD sshdSize = getlong(src);
		vbSize[i] = getlong(src + sizeof(DWORD));

		if (sshdSize != 0 && sshdSize <= dataSize && 
			vbSize[i] != 0 && vbSize[i] <= dataSize) {
			writeCsv(im->pszVhExt, i, 
				im->nVhOfs[i] - 12, sshdSize, im->nVhIdx, 0, csvFp);
			FILE* outFp = CreateOrOpenFile(path, NULL, NULL, 
				NULL, im->pszVhExt, _T("wb"), i, im->nVhIdx);
			if (!outFp) {
				OutputLastErrorNumAndString(_T(__FUNCTION__), __LINE__);
				return FALSE;
			}
			fwrite(src, sizeof(CHAR), sshdSize, outFp);
			FcloseAndNull(outFp);
			im->nVhSize[i] = sshdSize;
			im->isVh[i] = TRUE;
			OutputString(
				_T("\rCreate %s %4u/%4u"), im->pszVhExt, i + 1, im->nVhIdx);
		}
	}
	OutputString(_T("\n"));
	return TRUE;
}

BOOL CreateInst(
	_TCHAR* path, 
	LPBYTE data, 
	FILE* csvFp,
	PINTERNAL_MUSIC im
	)
{
	for (DWORD i = 0; i < im->nVhIdx; i++) {
		LPBYTE src = data + im->nVhOfs[i];
		DWORD size = getlong(src + 8);
		writeCsv(im->pszVhExt, i, im->nVhOfs[i], size, im->nVhIdx, 0, csvFp);
		FILE* outFp = CreateOrOpenFile(path, NULL, NULL, NULL,
			im->pszVhExt, _T("wb"), i, im->nVhIdx);
		if (!outFp) {
			OutputLastErrorNumAndString(_T(__FUNCTION__), __LINE__);
			return FALSE;
		}
		fwrite(src, sizeof(CHAR), size, outFp);
		FcloseAndNull(outFp);
		im->nVhSize[i] = size;
		im->isVh[i] = TRUE;
		OutputString(
			_T("\rCreate %s %4u/%4u"), im->pszVhExt, i + 1, im->nVhIdx);
	}
	OutputString(_T("\n"));
	return TRUE;
}

BOOL CreateVbForVh(
	_TCHAR* path,
	LPBYTE data,
	DWORD dataSize,
	BOOL isVbHead0byte,
	SEARCH_TYPE searchType,
	FILE* csvFp,
	PINTERNAL_MUSIC im,
	PVH vh,
	LPWORD vagSizeTbl
	)
{
	DWORD allVbSize = 0;
	for (INT i = 0; im->isVh[i] == TRUE; i++) {
		allVbSize += vh[i].fsize - im->nVhSize[i];
	}
	if (dataSize < allVbSize) {
		OutputString(_T("This file doesn't exist vb data\n"));
		return TRUE;
	}
	FILE* logVagFp = CreateOrOpenFile(path, NULL, NULL, 
		NULL, _T("_VagOfs.txt"), _T(WFLAG), 0, 0);
	if (!logVagFp) {
		OutputLastErrorNumAndString(_T(__FUNCTION__), __LINE__);
		return FALSE;
	}
	DWORD longSeqSize = im->nSeqIdx * sizeof(DWORD);
	DWORD longVhSize = im->nVhIdx * sizeof(DWORD);
	DWORD longVbSize = im->nVhIdx * sizeof(DWORD);
	size_t size = longSeqSize + longVhSize + longVbSize;
	LPDWORD ofsBegin = (LPDWORD)malloc(size);
	if (!ofsBegin) {
		OutputLastErrorNumAndString(_T(__FUNCTION__), __LINE__);
		return FALSE;
	}
	LPDWORD ofsEnd = (LPDWORD)malloc(size);
	if (!ofsEnd) {
		OutputLastErrorNumAndString(_T(__FUNCTION__), __LINE__);
		return FALSE;
	}
	memset(ofsBegin, 0x7F, size);
	if (im->nSeqIdx > 0) {
		memcpy(ofsBegin, im->nSeqOfs, longSeqSize);
		memcpy(ofsBegin + im->nSeqIdx, im->nVhOfs, longVhSize);
	}
	else {
		memcpy(ofsBegin, im->nVhOfs, longVhSize);
	}
	qsort(ofsBegin, im->nSeqIdx + im->nVhIdx, sizeof(DWORD), CompareUnsignedLong);

	memset(ofsEnd, 0x7F, size);
	for (DWORD sum = 0; sum < im->nSeqIdx; sum++) {
		ofsEnd[sum] = im->nSeqOfs[sum] + im->nSeqSize[sum] - 1;
	}
	for (DWORD sum = 0; sum < im->nVhIdx; sum++) {
		ofsEnd[im->nSeqIdx + sum] = im->nVhOfs[sum] + im->nVhSize[sum] - 1;
	}
	qsort(ofsEnd, im->nSeqIdx + im->nVhIdx, sizeof(DWORD), CompareUnsignedLong);

#ifdef _DEBUG
	for (DWORD sum = 0; sum < im->nSeqIdx; sum++) {
		OutputFileLog(_T("SeqOfs[%04u] %10u, size %8u\n"),
			sum, im->nSeqOfs[sum], im->nSeqSize[sum]);
	}
	for (DWORD sum = 0; sum < im->nVhIdx; sum++) {
		OutputFileLog(_T("VhOfs[%04u] %10u, size %8u\n"),
			sum, im->nVhOfs[sum], im->nVhSize[sum]);
	}
	for (DWORD sum = 0; sum < im->nSeqIdx + im->nVhIdx; sum++) {
		OutputFileLog(_T("SkipOfs[%04u] %10u-%10u(dec), %8x-%8x(hex)\n"), 
			sum, ofsBegin[sum], ofsEnd[sum], ofsBegin[sum], ofsEnd[sum]);
	}
#endif
	DWORD firstVbOfs = 0;
	DWORD nextVbOfs = 0;
	DWORD lastVbOfs = 0;
	DWORD tmpVbIdx = 0;
	for (DWORD i = 0; i < im->nVhIdx; i++) {
		if (im->isVh[i] == FALSE) {
			continue;
		}
		DWORD allVagSize = 0;
		UINT vagSize = 0;
		// multiple vag is vb
		for (WORD k = 1; k <= vh[i].TotalNumOfVags; k++) {
			vagSize = (UINT)((*((vagSizeTbl + i * VAG_SIZE_TBL_NUM) + k) << 3));
			_ftprintf(logVagFp, 
				_T("VbIdx: %u, VagNum: %02u/%02u, VagSize: %06u, %05x\n"), 
				i + 1, k, vh[i].TotalNumOfVags, vagSize, vagSize);
			allVagSize += vagSize;
		}
		_ftprintf(logVagFp, 
			_T("VbIdx: %u, AllVag(=Vb)Size: %08u, %07x\n"), 
			i + 1, allVagSize, allVagSize);

		if (allVagSize == 0 ||
			dataSize < allVagSize ||
			vh[i].fsize - im->nVhSize[i] != allVagSize) {
			OutputErrorLog(
				_T("Invalid vag size: idx %u, %08u, %07x\n"), 
				i + 1, allVagSize, allVagSize);
			continue;
		}

		BOOL bReadTop = FALSE;
		DWORD tmpSeqidx = im->nSeqIdx + im->nVhIdx + i - 1;
		DWORD maxOfs = dataSize - allVagSize;
		if (maxOfs <= nextVbOfs) {
			nextVbOfs = lastVbOfs;
		}
		for (DWORD ofs = nextVbOfs; ofs <= maxOfs; ofs++) {
			// skip seq & vh & /*vb data*/
			BOOL bAlreadyRegist = FALSE;
			while (tmpVbIdx <= tmpSeqidx) {
				if (ofs < ofsBegin[tmpVbIdx]) {
					break;
				}
				else if (ofsBegin[tmpVbIdx] <= ofs && ofs <= ofsEnd[tmpVbIdx]) {
					if (ofsEnd[tmpVbIdx] > maxOfs) {
						ofs = firstVbOfs;
					}
					else {
						ofs = ofsEnd[tmpVbIdx++];
						bAlreadyRegist = TRUE;
					}
					break;
				}
				tmpVbIdx++;
			}
			if (bAlreadyRegist) {
				continue;
			}

			if (ofs == maxOfs && i + 1 < im->nVhIdx) {
				if (!bReadTop) {
					ofs = firstVbOfs;
					for (DWORD r = 0; r < im->nSeqIdx + im->nVhIdx + i; r++) {
						if (ofs < ofsBegin[r]) {
							tmpVbIdx = r;
							break;
						}
					}
					bReadTop = TRUE;
				}
				else {
					break;
				}
			}
			int cnt = 0;
			LPBYTE src = data + ofs;
			BYTE buf[36] = { 0 };
			memcpy(buf, src, sizeof(buf));
			
			BYTE filterNum = 0;
			BYTE filterCoefficient = 0;
			BYTE adpcmFlag = 0;
			// 1st: Check "16byte is 0"
			if (isVbHead0byte) {
				for (int j = 0; j < sizeof(buf); j++) {
					if (buf[j] == 0x00) {
						cnt++;
					}
					else {
						break;
					}
				}
				if (cnt < 16) {
					continue;
				}
				filterNum = (BYTE)((buf[16] >> 4) & 0x0F);
				filterCoefficient = (BYTE)(buf[16] & 0x0F);
				adpcmFlag = buf[17];
			}
			else {
				filterNum = (BYTE)((buf[0] >> 4) & 0x0F);
				filterCoefficient = (BYTE)(buf[0] & 0x0F);
				adpcmFlag = buf[1];
			}
			// 2nd: Check "first(16 + 2 byte) byte is VAG data"
			if (filterNum <= 0x04 && filterCoefficient <= 0x0C &&
				adpcmFlag <= 0x07) {
				if ((isVbHead0byte == TRUE && buf[16] == 0x00 && buf[17] == 0x00) ||
					(isVbHead0byte == FALSE && buf[0] == 0x00 && buf[1] == 0x00)) {
					continue;
				}
				BOOL bRet = TRUE;
				UINT vagSizeTblReal = 0;
				for (WORD k = 1; k <= vh[i].TotalNumOfVags; k++) {
					cnt = 0;
					vagSize = (UINT)((*((vagSizeTbl + i * VAG_SIZE_TBL_NUM) + k) << 3));
					vagSizeTblReal += vagSize;
					src = data + ofs + vagSizeTblReal;
					LPBYTE endAdpcmFlag = src - 15;
					LPBYTE prevEndAdpcmFlag = endAdpcmFlag - 16;
					// 3rd: Check "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 "all(except first & end) VAG data"
					for (UINT p = 16; p < vagSize - 16; p += 16) {
						BYTE filter = getbyte(src - vagSize + p);
						filterNum = (BYTE)((filter >> 4) & 0x0F);
						filterCoefficient = (BYTE)(filter & 0x0F);
						adpcmFlag = getbyte(src - vagSize + p + 1);

						if (filterNum > 0x04 || filterCoefficient > 0x0C ||
							adpcmFlag > 0x07) {
							DWORD ofs1 = ofs + vagSizeTblReal - vagSize + p;
							DWORD ofs2 = ofs + vagSizeTblReal - vagSize + p + 1;
							_ftprintf(logVagFp,
								_T("[NotVag] VbIdx: %u, Ofs: %08x, Ofs1: %08x Filter: %02x, Ofs2: %08x AdpcmFlag: %02x\n"),
								i + 1, ofs, ofs1, filter, ofs2, adpcmFlag);
							bRet = FALSE;
							break;
						}
					}
					if (bRet == FALSE || vagSizeTblReal >= allVagSize) {
						break;
					}
					_ftprintf(logVagFp,
						_T("VbIdx: %u, VagNum: %02u/%02u, Ofs: %08x, NextOfs: %08x\n"),
						i + 1, k, vh[i].TotalNumOfVags, ofs + vagSizeTblReal - vagSize, ofs + vagSizeTblReal);
				}
				if (bRet == FALSE) {
					continue;
				}
				_ftprintf(logVagFp,
					_T("VbIdx: %u, VagNum: %02u/%02u, Ofs: %08x\n"),
					i + 1, vh[i].TotalNumOfVags, vh[i].TotalNumOfVags,
					ofs + vagSizeTblReal - vagSize);

				writeCsv(_T(".vb"), i, ofs, allVagSize, im->nVhIdx, 0, csvFp);
				FILE* outFp = CreateOrOpenFile(path, NULL, NULL,
					NULL, _T(".vb"), _T("wb"), i, im->nVhIdx);
				if (!outFp) {
					OutputLastErrorNumAndString(_T(__FUNCTION__), __LINE__);
					return FALSE;
				}
				fwrite(data + ofs, sizeof(CHAR), allVagSize, outFp);
				FcloseAndNull(outFp);
				im->isVb[i] = TRUE;

				lastVbOfs = ofs + allVagSize - 1;
#if 0
				// regist ofs for skip vb data
				ofsBegin[im->nSeqIdx + im->nVhIdx + i] = ofs;
				ofsEnd[im->nSeqIdx + im->nVhIdx + i] = lastVbOfs;
				qsort(ofsBegin, im->nSeqIdx + im->nVhIdx + i + 1, 
					sizeof(DWORD), CompareUnsignedLong);
				qsort(ofsEnd, im->nSeqIdx + im->nVhIdx + i + 1, 
					sizeof(DWORD), CompareUnsignedLong);
#endif
#if 0
				for (DWORD sum = 0; sum < im->nSeqIdx + im->nVhIdx + i + 1; sum++) {
					OutputFileLog(
						_T("SkipOfs[%04u] %10u-%10u(dec), %8x-%8x(hex)\n"),
						sum, ofsBegin[sum], ofsEnd[sum], ofsBegin[sum], ofsEnd[sum]);
				}
#endif
				if (im->nVhIdx > 1) {
					if (i == 0) {
						firstVbOfs = ofs;
					}
					// Search pattern 1: vh and vb is continuity
					if (searchType == SEARCH_TYPE::vab) {
						// There is a next vh
						if (im->nVhIdx > i + 1) {
							// There is a vb data after a vh data
							if (ofs > im->nVhOfs[i]) {
								nextVbOfs = im->nVhOfs[i + 1] + im->nVhSize[i + 1] - 1;
							}
							// There is a vb data before a vh data
							else if (ofs < im->nVhOfs[i]) {
								nextVbOfs = im->nVhOfs[i] + im->nVhSize[i] - 1;
							}
						}
					}
					// Search pattern 2: vh and vb isn't continuity
					else if (searchType == SEARCH_TYPE::vb) {
						nextVbOfs = lastVbOfs + 1;
					}
#if 0
					for (DWORD r = 0; r < im->nSeqIdx + im->nVhIdx + i; r++) {
						if (ofsBegin[r] == ofs) {
							tmpVbIdx = r;
							break;
						}
					}
#endif
				}
				OutputString(_T("\rCreate .vb %4u/%4u"), i + 1, im->nVhIdx);
				break;
			}
		}
	}
	OutputString(_T("\n"));
	FreeAndNull(ofsBegin);
	FreeAndNull(ofsEnd);
	FcloseAndNull(logVagFp);
	return TRUE;
}

BOOL CreateVbForSShd(
	_TCHAR* path, 
	LPBYTE data, 
	DWORD dataSize, 
	LPDWORD vbSize, 
	BOOL isVbHead0byte, 
	FILE* csvFp, 
	PINTERNAL_MUSIC im
	)
{
	DWORD idx = 0;
	DWORD prevIdx = 0;
	BOOL isVB = FALSE;

	UNREFERENCED_PARAMETER(isVbHead0byte);
	for (DWORD i = 0; i < im->nVhIdx; i++) {
		if (dataSize < vbSize[i] || vbSize[i] == 0) {
			continue;
		}
		if (isVB == FALSE) {
			idx = prevIdx;
		}
		else {
			isVB = FALSE;
		}
		for (; idx <= dataSize - vbSize[i]; idx++) {
			int cnt = 0;
			LPBYTE src = data + idx;
			BYTE buf[36] = { 0 };
			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;
				}
				BOOL bRet = TRUE;
				src = data + idx + vbSize[i];
				LPBYTE endAdpcmFlag = src - 15;

				if (*endAdpcmFlag == 0x01 || 
					*endAdpcmFlag == 0x03 ||
					*endAdpcmFlag == 0x07) {
					for (UINT 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, 0, csvFp);
					FILE* outFp = CreateOrOpenFile(path, NULL, NULL, 
						NULL, _T(".vb"), _T("wb"), i, im->nVhIdx);
					if (!outFp) {
						OutputLastErrorNumAndString(_T(__FUNCTION__), __LINE__);
						return FALSE;
					}
					fwrite(data + idx, sizeof(CHAR), vbSize[i], outFp);
					FcloseAndNull(outFp);
					im->isVb[i] = TRUE;
					idx += vbSize[i];
					prevIdx = idx;
					isVB = TRUE;
					OutputString(
						_T("\rCreate .vb %4u/%4u"), i + 1, im->nVhIdx);
					break;
				}
			}
		}
	}
	OutputString(_T("\n"));
	return TRUE;
}

BOOL CreateVbForInst(
	_TCHAR* path, 
	LPBYTE data, 
	FILE* csvFp,
	PINTERNAL_MUSIC im
	)
{
	for (DWORD i = 0; i < im->nVhIdx; i++) {
		LPBYTE src = data + im->nVhOfs[i];
		DWORD size = getlong(src + 8);
		LPBYTE vb = src + size;
		DWORD vbSize = getlong(src + 16);
		writeCsv(_T(".vb"), i, im->nVhOfs[i] + size, 
			im->nVhOfs[i] + size, im->nVhIdx, 0, csvFp);
		FILE* outFp = CreateOrOpenFile(path, NULL, NULL, 
			NULL, _T(".vb"), _T("wb"), i, im->nVhIdx);
		if (!outFp) {
			OutputLastErrorNumAndString(_T(__FUNCTION__), __LINE__);
			return FALSE;
		}
		fwrite(vb, sizeof(CHAR), vbSize, outFp);
		FcloseAndNull(outFp);
		im->isVb[i] = TRUE;
		OutputString(
			_T("\rCreate .vb file %4u/%4u"), i + 1, im->nSeqIdx);
	}
	OutputString(_T("\n"));
	return TRUE;
}

VOID OutputLastErrorNumAndString(
	LPCTSTR pszFuncName,
	LONG lLineNum
	)
{
	LPVOID lpMsgBuf;
	FormatMessage(
		FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
		NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL);

	OutputErrorString(_T("[F:%s][L:%d] GetLastError: %u, %s\n"),
		pszFuncName, lLineNum, GetLastError(), (LPCTSTR)lpMsgBuf);
	LocalFree(lpMsgBuf);
}
