// Sssq2mid.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;
}

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

	switch (byCurStatus & 0xF0) {
		case 0x80:
		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 0xE0:
			if(bRunStatus) {
				fputc(pData[(*lCurPos)++], fp1);
			}
			fputc(pData[(*lCurPos)++], fp1);
			fputc(0x00, fp1);
			break;
		case 0xF0:
			fputc(pData[(*lCurPos)++], fp1);
			byMeta = pData[(*lCurPos)++];
			fputc(byMeta, fp1);
			switch (byMeta) {
				case 0x51:
					fputc(0x03, fp1);
					if(pData[(*lCurPos)] == 0) {
						long pos = ftell(fp1);
						OutputErrorString(
							"tempo is zero! currentPosition of sssq:%d, 0x%x, mid:%d, 0x%x\n",
							*lCurPos, *lCurPos, pos, pos);
					}
					else {
						val = 60000000 / pData[(*lCurPos)++];
					}
					fputc(val >> 16 & 0xFF, fp1);
					fputc(val >> 8 & 0xFF, fp1);
					fputc(val >> 0 & 0xFF, fp1);
					(*lCurPos)++;
					break;
				case 0x2F:
					fputc(pData[(*lCurPos)++], fp1);
					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;
	}
}

int _tmain(int argc, _TCHAR* argv[])
{
	printf("Sssq2mid BuildDate:[%s %s]\n", __DATE__, __TIME__);
	if(argc < 2) {
		printf("Usage\n");
		printf("\tSssq2mid.exe <Sssqfile>\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);

	_stprintf(strfile, _T("%s%s%s.mid"), drive, dir, fname);
	FILE* fp1 = _tfopen(strfile, _T("wb"));
	if(!fp1) {
		_tprintf(_T("No file or already opened : %s\n"), strfile);
		return -1;
	}
	//MThd
	fputc(0x4D, fp1);
	fputc(0x54, fp1);
	fputc(0x68, fp1);
	fputc(0x64, fp1);
	//Header Size
	fputc(0x00, fp1);
	fputc(0x00, fp1);
	fputc(0x00, fp1);
	fputc(0x06, fp1);
	//Format
	fputc(0x00, fp1);
	fputc(0x00, fp1);
	//Truck Num
	fputc(0x00, fp1);
	fputc(0x01, fp1);
	//Delta Time
	fputc(*(pData + 3), fp1);
	fputc(*(pData + 2), fp1);
	//MTrk
	fputc(0x4D, fp1);
	fputc(0x54, fp1);
	fputc(0x72, fp1);
	fputc(0x6B, fp1);
	//Data Size(tmp)
	fputc(0x00, fp1);
	fputc(0x00, fp1);
	fputc(0x00, fp1);
	fputc(0x00, fp1);
	//Tempo
	fputc(0x04, fp1);
	fputc(0xFF, fp1);
	fputc(0x51, fp1);
	fputc(0x03, fp1);
	int val = 0;
	if(*(pData + 4)) {
		val = 60000000 / *(pData + 4);
	}
	fputc(val >> 16 & 0xFF, fp1);
	fputc(val >> 8 & 0xFF, fp1);
	fputc(val >> 0 & 0xFF, fp1);
	//TimeSig
	fputc(0x08, fp1);
	fputc(0xFF, fp1);
	fputc(0x58, fp1);
	fputc(0x04, fp1);
	fputc(0x04, fp1);
	fputc(0x02, fp1);
	fputc(0x18, fp1);
	fputc(0x08, fp1);
	// data
	fputc(0x00, fp1);
	unsigned long lCurPos = 0x110;
	makeMidiEvent(&lCurPos, pData, pData[lCurPos], true, fp1);

	unsigned char byCurStatus = 0;
	unsigned char byRunStatus = 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((0x80 <= byCurStatus) && (byCurStatus <= 0xEF)) {
			byRunStatus = byCurStatus;
		}
		else if((0xF0 <= byCurStatus) && (byCurStatus <= 0xF7)) {
			byRunStatus = 0;
		}
		else if(byCurStatus < 0x80) {
			byCurStatus = byRunStatus;
			bRunStatus = false;
		}
		makeMidiEvent(&lCurPos, pData, byCurStatus, bRunStatus, fp1);
	}
	int dstDatasize = (int)getFilesize(fp1) - 0x16;
	fseek(fp1, 0x12, SEEK_SET);
	fputc(dstDatasize >> 24, fp1);
	fputc(dstDatasize >> 16, fp1);
	fputc(dstDatasize >> 8, fp1);
	fputc(dstDatasize >> 0, fp1);

	free(pData);
	fclose(fp1);
	return 0;
}

