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

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

#ifdef _DEBUG
#define OutputErrorString(str, ...) \
		{ \
			char c[256]; \
			sprintf(c, str, __VA_ARGS__); \
			OutputDebugStringA(c); \
		}
#else
#define OutputErrorString(str, ...) \
		{ \
			fprintf(stderr, str, __VA_ARGS__); \
		}
#endif

unsigned long getFilesize(
	FILE *fp
	)
{
	unsigned long filesize = 0;
	if (fp != NULL) {
		fseek(fp, 0, SEEK_END);
		filesize = (unsigned long)ftell(fp);
		rewind(fp);
	}
	return filesize;
}

int makeMidiEvent(
	unsigned long* lCurPos,
	unsigned char* pData, 
	unsigned char byCurStatus,
	bool bRunStatus,
	FILE* fp1
	)
{
	int ret = 1;
	unsigned char byMeta = 0;
	unsigned char byLength = 0;

	switch (byCurStatus & 0xF0) {
		case 0x80:
		case 0xE0:
			if(bRunStatus) {
				fputc(pData[(*lCurPos)++], fp1);
			}
			fputc(pData[(*lCurPos)++], fp1);
			fputc(00, fp1);
			break;
		case 0x90:
		case 0xA0:
		case 0xB0:
			if(bRunStatus) {
				fputc(pData[(*lCurPos)++], fp1);
			}
			fputc(pData[(*lCurPos)++], fp1);
			fputc(pData[(*lCurPos)++], fp1);
			break;
		case 0xC0:
		case 0xD0:
			if(bRunStatus) {
				fputc(pData[(*lCurPos)++], fp1);
			}
			fputc(pData[(*lCurPos)++], fp1);
			break;
		case 0xF0:
			fputc(pData[(*lCurPos)++], fp1);
			byMeta = pData[(*lCurPos)++];
			fputc(byMeta, fp1);
			switch (byMeta) {
				case 0x51:
					fputc(pData[(*lCurPos)++], fp1);
					fputc(pData[(*lCurPos)++], fp1);
					fputc(pData[(*lCurPos)++], fp1);
					fputc(pData[(*lCurPos)++], fp1);
					break;
				case 0x2F:
					fputc(pData[(*lCurPos)++], fp1);
					ret = 0;
					break;
				case 0x00:
				case 0x01:
				case 0x02:
				case 0x03:
				case 0x04:
				case 0x05:
				case 0x06:
				case 0x07:
				case 0x20:
				case 0x21:
				case 0x54:
				case 0x58:
				case 0x59:
				case 0x7F:
					byLength = pData[(*lCurPos)++];
					fputc(byLength, fp1);
					for(unsigned char i = 0; i < byLength; i++) {
						fputc(pData[(*lCurPos)++], fp1);
					}
					break;
				default:
					break;
			}
			break;
		default:
			break;
	}
	return ret;
}

int _tmain(int argc, _TCHAR* argv[])
{
	printf("Sqv2mid BuildDate:[%s %s]\n", __DATE__, __TIME__);
	if(argc < 2) {
		printf("Usage\n");
		printf("\tSqv2mid.exe <Sqvfile>\n");
		return -1;
	}
	FILE* fp = _tfopen(argv[1], _T("rb"));
	if(!fp) {
		_tprintf(_T("No file or already opened : %s\n"), argv[1]);
		return -1;
	}
	unsigned long datasize = getFilesize(fp);
	unsigned char* pData = (unsigned char*)malloc(datasize);
	if(!pData) {
		_tprintf(_T("Cannot alloc memory\n"));
		return -1;
	}
	fread(pData, datasize, 1, fp);
	fclose(fp);	

	_TCHAR drive[_MAX_DRIVE];
	_TCHAR dir[_MAX_DIR];
	_TCHAR fname[_MAX_FNAME];
	_TCHAR ext[_MAX_EXT];
	_TCHAR strfile[_MAX_PATH];
	memset(drive, 0, sizeof(drive));
	memset(dir, 0, sizeof(dir));
	memset(fname, 0, sizeof(fname));
	memset(ext, 0, sizeof(ext));
	_tsplitpath(argv[1], drive, dir, fname, ext);

	unsigned int idx[4];
	memset(idx, 0, sizeof(idx));
	idx[0] = 0x10;
	for(int i = 1; i < 4; i++) {
		if(*(pData + 4 * i) > 0) {
			idx[i] |= *(pData + 4 * i);
			idx[i] |= *(pData + 4 * i + 1) << 8;
			idx[i] |= *(pData + 4 * i + 2) << 16;
			idx[i] |= *(pData + 4 * i + 3) << 24;
			idx[i] += 0x4;
		}
	}
	for(int k = 1; k <= 3; k++) {
		if(idx[k-1] < datasize) {
			_stprintf(strfile, _T("%s%s%s_%d.mid"), drive, dir, fname, k);
			FILE* fp1 = _tfopen(strfile, _T("wb"));
			if(!fp1) {
				_tprintf(_T("No file or already opened : %s\n"), strfile);
				return -1;
			}
			fwrite(pData + idx[k-1], sizeof(unsigned char), 0x16, fp1);
			unsigned long lCurPos = idx[k-1] + 0x16;
			makeMidiEvent(&lCurPos, pData, pData[lCurPos], true, fp1);

			unsigned char byCurStatus = 0;
			unsigned char byRunStatus = 0;
			int nRet = 0;
			while(lCurPos <= datasize - 1) {
				bool bRunStatus = true;
				if((0x80 <= pData[lCurPos]) && (pData[lCurPos] <= 0xFF)) {
					// delta time
					fputc(pData[lCurPos++], fp1);
					continue;
				}
				// delta time
				fputc(pData[lCurPos++], fp1);
				byCurStatus = pData[lCurPos];

				if(byCurStatus < 0x80) {
					byCurStatus = byRunStatus;
					bRunStatus = false;
				}
				else if((0x80 <= byCurStatus) && (byCurStatus <= 0xEF)) {
					byRunStatus = byCurStatus;
				}
				else if((0xF0 <= byCurStatus) && (byCurStatus <= 0xF7)) {
					byRunStatus = 0;
				}
				nRet = makeMidiEvent(&lCurPos, pData, byCurStatus, bRunStatus, fp1);
				if(nRet == 0) {
					break;
				}
			}
			int dstDatasize = (int)ftell(fp1) - 0x16;
			fseek(fp1, 0x12, SEEK_SET);
			fputc(dstDatasize >> 24, fp1);
			fputc(dstDatasize >> 16, fp1);
			fputc(dstDatasize >> 8, fp1);
			fputc(dstDatasize >> 0, fp1);
			fclose(fp1);
			if(idx[k] == 0) {
				break;
			}
		}
		else {
			break;
		}
	}
	free(pData);
	return 0;
}

