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

unsigned char getbyte(
	unsigned char *ptr
	)
{
	return *ptr;
}

unsigned short getword(
	unsigned char *ptr
	)
{
	return (unsigned short)((getbyte(ptr + 1) <<  8) + getbyte(ptr));
}

unsigned long getlong(
	unsigned char *ptr
	)
{
	return (unsigned long)((getword(ptr + 2) << 16) + getword(ptr));
}

_int64 getlonglong(
	unsigned char *ptr
	)
{
	return ((_int64)getlong(ptr + 4) << 32) + getlong(ptr);
}

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;
}

unsigned long getBitCount(
	unsigned long n
	)
{
	n = ((n & 0xaaaaaaaa) >> 1) + (n & 0x55555555); 
	n = ((n & 0xcccccccc) >> 2) + (n & 0x33333333); 
	n = ((n & 0xf0f0f0f0) >> 4) + (n & 0x0f0f0f0f); 
	n = ((n & 0xff00ff00) >> 8) + (n & 0x00ff00ff); 
	n = ((n & 0xffff0000) >> 16) + (n & 0x0000ffff); 
    return n;
}

void GetType(
	unsigned char *buf,
	unsigned long size,
	unsigned long i,
	InternalMusic* im
	)
{
	// "pQES" (for General)
	if(buf[i] == 0x70 && buf[i+1] == 0x51 &&
		buf[i+2] == 0x45 && buf[i+3] == 0x53) {
		if(buf[i+7] == 0x01) {
			_tcscpy(im->pszSeqExt, _T(".seq"));
		}
		else if(buf[i+7] == 0) {
			_tcscpy(im->pszSeqExt, _T(".sep"));
		}
	}
	// "SSsq" (for Sony)
	else if(
		(buf[i] == 0x53 && buf[i+1] == 0x53 &&
		buf[i+2] == 0x73 && buf[i+3] == 0x71) ||
		// "SS sq (for WildARMs2)
		(buf[i] == 0x53 && buf[i+1] == 0x53 &&
		buf[i+2] == 0x7F && buf[i+3] == 0x73)
		) {
		_tcscpy(im->pszSeqExt, _T(".sssq"));
	}
	// "KCET" (for Konami)
	else if(buf[i] == 0x4B && buf[i+1] == 0x43 &&
		buf[i+2] == 0x45 && buf[i+3] == 0x54) {
		if(i < size - 0x24) {
			//"TSDB" && "KDT2" && "KDT1"
			if(buf[i+0x08] == 0x54 && buf[i+0x08+1] == 0x53 &&
				buf[i+0x08+2] == 0x44 && buf[i+0x08+3] == 0x42 &&
				buf[i+0x14] == 0x4B && buf[i+0x14+1] == 0x44 &&
				buf[i+0x14+2] == 0x54 && buf[i+0x14+3] == 0x32 &&
				buf[i+0x24] == 0x4B && buf[i+0x24+1] == 0x44 &&
				buf[i+0x24+2] == 0x54 && buf[i+0x24+3] == 0x31) {
			}
			_tcscpy(im->pszSeqExt, _T(".kcet"));
		}
	}
	// "SNTp" (for Final Fantasy 6, Chrono Trigger)
	else if(buf[i] == 0x53 && buf[i+1] == 0x4E &&
		buf[i+2] == 0x54 && buf[i+3] == 0x70) {
		if(i > 11 && buf[i-12] == 0x02 && buf[i-11] == 0x00 &&
		buf[i-10] == 0x00 && buf[i-9] == 0x00) {
			_tcscpy(im->pszSeqExt, _T(".sntp"));
		}
	}
	// "SEQG" (for Gran Turismo 2)
	else if(buf[i] == 0x53 && buf[i+1] == 0x45 &&
		buf[i+2] == 0x51 && buf[i+3] == 0x47) {
		_tcscpy(im->pszSeqExt, _T(".seqg"));
	}
	// "qQES" (for Dragon Quest 4, 7)
	else if(buf[i] == 0x71 && buf[i+1] == 0x51 &&
		buf[i+2] == 0x45 && buf[i+3] == 0x53) {
		_tcscpy(im->pszSeqExt, _T(".seqq"));
	}
	// ".sqv" (for Brave Fencer Musashiden)
	else if(buf[i] == 0x2E && buf[i+1] == 0x73 &&
		buf[i+2] == 0x71 && buf[i+3] == 0x76) {
		_tcscpy(im->pszSeqExt, _T(".sqv"));
	}
	// "SSEQ" (for Bastard!! - Utsuronaru Kamigami no Utsuwa)
	else if(buf[i] == 0x53 && buf[i+1] == 0x53 &&
		buf[i+2] == 0x45 && buf[i+3] == 0x51 &&
		buf[i+4] == 0x28 && buf[i+5] == 0x00 &&
		buf[i+6] == 0x00 && buf[i+7] == 0x00) {
		_tcscpy(im->pszSeqExt, _T(".sseq"));
	}
	// "pBAV" (for General)
	else if(buf[i] == 0x70 && buf[i+1] == 0x42 &&
		buf[i+2] == 0x41 && buf[i+3] == 0x56) {
		if(buf[i+40] == 0xFF && buf[i+41] == 0xFF) {
			_tcscpy(im->pszVhExt, _T(".vh"));
		}
		// "pBAV" (for Bastard!! - Utsuronaru Kamigami no Utsuwa)
		else if(buf[i+34] == 0x00 && buf[i+35] == 0x00) {
			_tcscpy(im->pszVhExt, _T(".vh2"));
		}
	}
	// "SShd" (for Sony)
	else if(buf[i] == 0x53 && buf[i+1] == 0x53 &&
		buf[i+2] == 0x68 && buf[i+3] == 0x64) {
		if(buf[i-4] == 0 && buf[i-3] == 0 &&
		buf[i-2] == 0 && buf[i-1] == 0) {
		_tcscpy(im->pszVhExt, _T(".sshd"));
		}
	}
	// "INST" (for Gran Turismo 2)
	else if(buf[i] == 0x49 && buf[i+1] == 0x4E &&
		buf[i+2] == 0x53 && buf[i+3] == 0x54 && 
		buf[i+4] == 0 && buf[i+5] == 0 &&
		buf[i+6] == 0 && buf[i+7] == 0) {
		_tcscpy(im->pszVhExt, _T(".inst"));
	}
}

bool GetDataOffset(
	unsigned char *buf,
	unsigned long size,
	unsigned long startIdx,
	unsigned long maxSearchFileNum,
	bool bSkip,
	InternalMusic* im
	)
{
	unsigned long idx = 0;
	unsigned long allocSize = maxSearchFileNum * sizeof(unsigned long);
	unsigned long* offset = (unsigned long*)malloc(allocSize);
	if(!offset) {
		_tprintf(_T("Cannot alloc memory [F:%s][L:%d]\n"), 
			_T(__FUNCTION__), __LINE__);
		return false;
	}
	memset(offset, 0x7F, allocSize);

	unsigned long skipVhSize = sizeof(VH) + PROGRAM_ATTRIBUTES * 
		MAX_PROGRAM_ATTRIBUTES + VAG_OFFSET_SIZE - 1;
	unsigned long allToneAttr = TONE_ATTRIBUTES * MAX_TONE_ATTRIBUTES;
	_TCHAR seqtype[6];
	_TCHAR vhtype[6];
	memset(seqtype, 0, sizeof(seqtype));
	memset(vhtype, 0, sizeof(vhtype));

	for(unsigned long i = startIdx; i < size; i++) {
		if(idx >= maxSearchFileNum) {
			_tprintf(_T("\nReach max [F:%s][L:%d]\n"), 
				_T(__FUNCTION__), __LINE__);
			break;
		}
		GetType(buf, size, i, im);
		if(!_tcscmp(im->pszSeqExt, _T(".seq")) || !_tcscmp(im->pszSeqExt, _T(".sep")) ||
			!_tcscmp(im->pszSeqExt, _T(".sssq")) || !_tcscmp(im->pszSeqExt, _T(".kcet")) ||
			!_tcscmp(im->pszSeqExt, _T(".sntp")) ||	!_tcscmp(im->pszSeqExt, _T(".seqg")) ||
			!_tcscmp(im->pszSeqExt, _T(".seqq")) ||	!_tcscmp(im->pszSeqExt, _T(".sqv")) ||
			!_tcscmp(im->pszSeqExt, _T(".sseq"))) {
			im->nSeqIdx++;
			offset[idx++] = i;
			if(!_tcscmp(im->pszSeqExt, _T(".seq")) && bSkip) {
				i += sizeof(SEQ) + 4 - 1;
			}
			else if(!_tcscmp(im->pszSeqExt, _T(".sep")) && bSkip) {
				i += sizeof(SEQ) + 8 - 1;
			}
			else if(!_tcscmp(im->pszSeqExt, _T(".sssq")) && bSkip) {
				i += 260;
			}
			if(seqtype[0] == 0) {
				_tcscpy(seqtype, im->pszSeqExt);
			}
			memset(im->pszSeqExt, 0, 5);
			_tprintf(_T("\rSearchFile:%10d/%10d"), i + 1, size);
		}
		else if(!_tcscmp(im->pszVhExt, _T(".vh")) || !_tcscmp(im->pszVhExt, _T(".sshd")) ||
			!_tcscmp(im->pszVhExt, _T(".inst")) || !_tcscmp(im->pszVhExt, _T(".vh2"))) {
			im->nVhIdx++;
			offset[idx++] = i;
			if(!_tcscmp(im->pszVhExt, _T(".vh")) && bSkip) {
				i += skipVhSize + getword(buf + i + 18) * allToneAttr;
			}
			else if(!_tcscmp(im->pszVhExt, _T(".sshd")) && bSkip) {
				i += getlong(buf + i - 12) - 12;
			}
			if(vhtype[0] == 0) {
				_tcscpy(vhtype, im->pszVhExt);
			}
			memset(im->pszVhExt, 0, 5);
			_tprintf(_T("\rSearchFile:%10d/%10d"), i + 1, size);
		}
	}
	_tprintf(_T("\n"));

	if(im->nSeqIdx > 0) {
		size_t nSeqAllocSize = (im->nSeqIdx + 1) * sizeof(unsigned long);
		im->nSeqOffset = (unsigned long*)malloc(nSeqAllocSize);
		im->nSeqSize = (unsigned long*)malloc(nSeqAllocSize);
		im->isSeq = (unsigned long*)malloc(nSeqAllocSize);
		if(!im->nSeqOffset || !im->nSeqSize || !im->isSeq) {
			_tprintf(_T("Cannot alloc memory [F:%s][L:%d]\n"), 
				_T(__FUNCTION__), __LINE__);
			return false;
		}
		memset(im->isSeq, FALSE, nSeqAllocSize);
	}
	if(im->nVhIdx > 0) {
		size_t nVhAllocSize = (im->nVhIdx + 1) * sizeof(unsigned long);
		im->nVhOffset = (unsigned long*)malloc(nVhAllocSize);
		im->nVhSize = (unsigned long*)malloc(nVhAllocSize);
		im->isVh = (unsigned long*)malloc(nVhAllocSize);
		im->isVb = (unsigned long*)malloc(nVhAllocSize);
		if(!im->nVhOffset || !im->nVhSize || !im->isVh || !im->isVb) {
			_tprintf(_T("Cannot alloc memory [F:%s][L:%d]\n"), 
				_T(__FUNCTION__), __LINE__);
			return false;
		}
		memset(im->isVh, FALSE, nVhAllocSize);
		memset(im->isVb, FALSE, nVhAllocSize);
	}

	unsigned long idx_seq = 0;
	unsigned long idx_vh = 0;
	idx = 0;
	qsort(offset, maxSearchFileNum, sizeof(unsigned long), CompareUnsignedLong);

	for(unsigned long i = startIdx; i < size; i++, idx++) {
		if(idx < maxSearchFileNum && offset[idx] != 0x7F7F7F7F) {
			i = offset[idx];
		}
		else {
			break;
		}
		GetType(buf, size, i, im);
		if(!_tcscmp(im->pszSeqExt, _T(".seq")) || !_tcscmp(im->pszSeqExt, _T(".sep")) ||
			!_tcscmp(im->pszSeqExt, _T(".sssq")) || !_tcscmp(im->pszSeqExt, _T(".kcet")) ||
			!_tcscmp(im->pszSeqExt, _T(".sntp")) ||	!_tcscmp(im->pszSeqExt, _T(".seqg")) ||
			!_tcscmp(im->pszSeqExt, _T(".seqq")) ||	!_tcscmp(im->pszSeqExt, _T(".sqv")) ||
			!_tcscmp(im->pszSeqExt, _T(".sseq"))) {
			im->nSeqOffset[idx_seq++] = i;
			memset(im->pszSeqExt, 0, 5);
		}
		else if(!_tcscmp(im->pszVhExt, _T(".vh")) || !_tcscmp(im->pszVhExt, _T(".sshd")) ||
			!_tcscmp(im->pszVhExt, _T(".inst")) || !_tcscmp(im->pszVhExt, _T(".vh2"))) {
			im->nVhOffset[idx_vh++] = i;
			memset(im->pszVhExt, 0, 5);
		}
	}
	_tcscpy(im->pszSeqExt, seqtype);
	_tcscpy(im->pszVhExt, vhtype);
	FreeAndNull(offset);
	return true;
}
