// Id3tagv1.cpp: CId3tagv1 クラスのインプリメンテーション
//
//////////////////////////////////////////////////////////////////////

/*
 * Modified by Atmark
 * - MFC関係をQtに置き換える
 *  - char* -> QString
 */

#include <cstdio>
#include <cstring>
#include <cstdlib>

#include "id3tagv1.h"

static unsigned char SCMPX_GENRE_NULL = 247;
static unsigned char WINAMP_GENRE_NULL = 255;
static char	szId3gnr[256][30] =
	{
	"Blues","Classic Rock","Country","Dance","Disco","Funk","Grunge",
	"Hip-Hop","Jazz","Metal","New Age","Oldies","Other","Pop","R&B","Rap",
	"Reggae","Rock","Techno","Industrial","Alternative","Ska","Death Metal",
	"Pranks","Soundtrack","Euro-Techno","Ambient","Trip-Hop","Vocal",
	"Jazz+Funk","Fusion","Trance","Classical","Instrumental","Acid","House",
	"Game","Sound Clip","Gospel","Noise","Alt. Rock","Bass","Soul","Punk",
	"Space","Meditative","Instrumental Pop","Instrumental Rock","Ethnic",
	"Gothic","Darkwave","Techno-Industrial","Electronic","Pop-Folk",
	"Eurodance","Dream","Southern Rock","Comedy","Cult","Gangsta Rap",
	"Top 40","Christian Rap","Pop/Funk","Jungle","Native American","Cabaret",
	"New Wave","Psychedelic","Rave","Showtunes","Trailer","Lo-Fi","Tribal",
	"Acid Punk","Acid Jazz","Polka","Retro","Musical","Rock & Roll",
	"Hard Rock","Folk","Folk/Rock","National Folk","Swing","Fast-Fusion",
	"Bebob","Latin","Revival","Celtic","Bluegrass","Avantgarde","Gothic Rock",
	"Progressive Rock","Psychedelic Rock","Symphonic Rock","Slow Rock",
	"Big Band","Chorus","Easy Listening","Acoustic","Humour","Speech",
	"Chanson","Opera","Chamber Music","Sonata","Symphony","Booty Bass",
	"Primus","Porn Groove","Satire","Slow Jam","Club","Tango","Samba",
	"Folklore","Ballad","Power Ballad","Rhythmic Soul","Freestyle","Duet",
	"Punk Rock","Drum Solo","A Cappella","Euro-House","Dance Hall","Goa",
	"Drum & Bass","Club-House","Hardcore","Terror","Indie","BritPop",
	"Negerpunk","Polsk Punk","Beat","Christian Gangsta Rap","Heavy Metal",
	"Black Metal","Crossover","Contemporary Christian","Christian Rock",
	"Merengue","Salsa","Thrash Metal","Anime","JPop","Synthpop",
	"","","","","","","","","","",
	"","","","","","","","","","",
	"","","","","","","","","","",
	"","","","","","","","","","",
	"","","","","","","","","","",
	"","","","","","","","","","",
	"","","","","","","","","","",
	"","","","","","","","","","",
	"","","","","","","","","","",
	"","","","","","","","","","",
	"Heavy Rock(J)","Doom Rock(J)","J-POP(J)","Seiyu(J)","Tecno Ambient(J)","Moemoe(J)","Tokusatsu(J)","Anime(J)"
	};

//////////////////////////////////////////////////////////////////////
// 構築/消滅
//////////////////////////////////////////////////////////////////////

CId3tagv1::CId3tagv1(bool bScmpxGenre)
{
	m_bScmpxGenre = bScmpxGenre;
	Release();
}

CId3tagv1::~CId3tagv1()
{}

void CId3tagv1::Release()
{
	m_bEnable = false;
	strcpy(m_szTitle, "");
	strcpy(m_szArtist, "");
	strcpy(m_szAlbum, "");
	strcpy(m_szYear, "");
	m_cGenre = (m_bScmpxGenre ? SCMPX_GENRE_NULL : WINAMP_GENRE_NULL);
	strcpy(m_szComment, "");
	m_cTrackNo = 0;
}

void CId3tagv1::SetScmpxGenre(bool bSwitch)
{
	m_bScmpxGenre = bSwitch;
}

bool CId3tagv1::IsEnable()
{
	return m_bEnable;
}

const char* CId3tagv1::title() const
{
	return m_szTitle;
}

void CId3tagv1::setTitle(const char *title)
{
	m_bEnable = true;
	long length = check2ByteLength(title, 30);
	strncpy(m_szTitle, title, length);
	m_szTitle[length] = '\0';
}

const char* CId3tagv1::artist() const
{
	return m_szArtist;
}

void CId3tagv1::setArtist(const char *artist)
{
	m_bEnable = true;
	long length = check2ByteLength(artist, 30);
	strncpy(m_szArtist, artist, length);
	m_szArtist[length] = '\0';
}

const char* CId3tagv1::album() const
{
	return m_szAlbum;
}

void CId3tagv1::setAlbum(const char *album)
{
	m_bEnable = true;
	long length = check2ByteLength(album, 30);
	strncpy(m_szAlbum, album, length);
	m_szAlbum[length] = '\0';
}

int CId3tagv1::year() const
{
	return atoi(m_szYear);
}

void CId3tagv1::setYear(const char *year)
{
	m_bEnable = true;
	long length = check2ByteLength(year, 4);
	strncpy(m_szYear, year, length);
	m_szYear[length] = '\0';
}

unsigned char CId3tagv1::genreNum() const
{
	if(!m_bScmpxGenre && (m_cGenre >= SCMPX_GENRE_NULL))
		return WINAMP_GENRE_NULL;
	return m_cGenre;
}

const char* CId3tagv1::genre() const
{
	if(!m_bScmpxGenre && (m_cGenre >= SCMPX_GENRE_NULL))
		return "";
	return szId3gnr[m_cGenre];
}

void CId3tagv1::setGenre(unsigned char cGenre)
{
	m_bEnable = true;
	m_cGenre = cGenre;
}

void CId3tagv1::setGenre(const char *szGenre)
{
	m_bEnable = true;
	for(unsigned int i = 0; i < 256; i++)
	{
		if(strcmp(szId3gnr[i], szGenre) == 0)
		{
			m_cGenre = i;
			break;
		}
	}
}

unsigned char CId3tagv1::trackNoNum() const
{
	return m_cTrackNo;
}

int CId3tagv1::track() const
{
	return m_cTrackNo;
}

void CId3tagv1::setTrackNo(unsigned char cTrackNo)
{
	m_cTrackNo = cTrackNo;
}

void CId3tagv1::setTrackNo(const char *szTrackNo)
{
	if(strlen(szTrackNo) == 0)
		m_cTrackNo = 0;
	else
		m_cTrackNo = atoi(szTrackNo);
}

const char* CId3tagv1::comment() const
{
	return m_szComment;
}

void CId3tagv1::setComment(const char *comment)
{
	m_bEnable = true;
	int len = 30;
	if (m_cTrackNo)
		len = 28;
	long length = check2ByteLength(comment, len);
	strncpy(m_szComment, comment, length);
	m_szComment[length] = '\0';
}

const char* CId3tagv1::GenreNum2String(unsigned char cGenre) const
{
	if (!m_bScmpxGenre && (cGenre >= SCMPX_GENRE_NULL))
		return NULL;
	return szId3gnr[cGenre];
}

long CId3tagv1::GenreString2Num(const char *szGenre)
{
	long cGenre = -1;
	if (strlen(szGenre) == 0)
	{
		if(m_bScmpxGenre)
			return SCMPX_GENRE_NULL;
		else
			return WINAMP_GENRE_NULL;
	}
	for(unsigned long i = 0; i < 256; i++)
	{
		if(strcmp(szId3gnr[i], szGenre) == 0)
		{
			cGenre = i;
			break;
		}
	}
	return cGenre;
}

int CId3tagv1::load(const char *szFileName)
{
	Release();
	FILE *fp;
	if ((fp = fopen(szFileName, "rb")) == NULL)
		return -1;

	//ID3タグがあるはずの位置までSEEK
	if (fseek(fp, -128, SEEK_END) != 0)
	{
		fclose(fp);
		return -1;
	}

	//ID3タグかどうかをチェック
	char szTmp[128];
	fread(szTmp, 1, 128, fp);
	fclose(fp);
	if(strncmp("TAG", szTmp, 3) != 0)
		return -1;
	
	m_bEnable = true;
	//情報の採取
	char *p;
	int i;
	p = szTmp+3;
	// mbsncpy2((unsigned char *)m_szTitle, (unsigned char *)p, 30);
 	strncpy(m_szTitle, p, 30);
	m_szTitle[30] = '\0';
	for (i = 29; i >= 0; i--)
	{
		if (m_szTitle[i] == ' ')
			m_szTitle[i] = '\0';
		else
			break;
	}
	p += 30;
	// mbsncpy2((unsigned char *)m_szArtist, (unsigned char *)p, 30);
 	strncpy(m_szArtist, p, 30);
	m_szArtist[30] = '\0';
	for (i = 29; i >= 0; i--)
	{
		if (m_szArtist[i] == ' ')
			m_szArtist[i] = '\0';
		else
			break;
	}
	p += 30;
	// mbsncpy2((unsigned char *)m_szAlbum, (unsigned char *)p, 30);
 	strncpy(m_szAlbum, p, 30);
	m_szAlbum[30] = '\0';
	for (i = 29; i >= 0; i--)
	{
		if (m_szAlbum[i] == ' ')
			m_szAlbum[i] = '\0';
		else
			break;
	}
	p += 30;
	// mbsncpy2((unsigned char *)m_szYear, (unsigned char *)p, 4);
 	strncpy(m_szYear, p, 4);
	m_szYear[4] = '\0';
	for (i = 3; i >= 0; i--)
	{
		if (m_szYear[i] == ' ')
			m_szYear[i] = '\0';
		else
			break;
	}
	p += 4;
	if ((szTmp[125] == '\0') && szTmp[126])
	{
		m_cTrackNo = szTmp[126];
		// mbsncpy2((unsigned char *)m_szComment, (unsigned char *)p, 28);
	 	strncpy(m_szComment, p, 28);
		m_szComment[28] = '\0';
		for (i = 27; i >= 0; i--)
		{
			if (m_szComment[i] == ' ')
				m_szComment[i] = '\0';
			else
				break;
		}
	}
	else
	{
		m_cTrackNo = 0;
		// mbsncpy2((unsigned char *)m_szComment, (unsigned char *)p, 30);
	 	strncpy(m_szComment, p, 30);
		m_szComment[30] = '\0';
		for (i = 29; i >= 0; i--)
		{
			if (m_szComment[i] == ' ')
				m_szComment[i] = '\0';
			else
				break;
		}
	}
	p += 30;
	m_cGenre = *p;
	return 0;
}

/*
int CId3tagv1::LoadMulti(const char *szFileName)
{
	// int dwError;
	CId3tagv1 tag;
	tag.SetScmpxGenre(m_bScmpxGenre);
	// dwError = tag.Load(szFileName);
	if(tag.Load(szFileName) == -1)
		return -1;
	
	if(tag.IsEnable())
	{
		if(!strcmp(this->GetTitle(), tag.GetTitle()))
			this->SetTitle("");
		if(!strcmp(this->GetArtist(), tag.GetArtist()))
			this->SetArtist("");
		if(!strcmp(this->GetAlbum(), tag.GetAlbum()))
			this->SetAlbum("");
		if(!strcmp(this->GetYear(), tag.GetYear()))
			this->SetYear("");
		if(!(this->GetGenreNum() == tag.GetGenreNum()))
			if(m_bScmpxGenre)
				this->SetGenre(SCMPX_GENRE_NULL);
			else
				this->SetGenre(WINAMP_GENRE_NULL);
		if(!strcmp(this->GetComment(), tag.GetComment()))
			this->SetComment("");
		if(!strcmp(this->GetTrackNo(), tag.GetTrackNo()))
			this->SetTrackNo("");
	}
	if(tag.IsEnable())
	{
		if(!this->GetTitle().compare(tag.GetTitle()))
			this->SetTitle("");
		if(!this->GetArtist().compare(tag.GetArtist()))
			this->SetArtist("");
		if(!this->GetAlbum().compare(tag.GetAlbum()))
			this->SetAlbum("");
		if(!this->GetYear().compare(tag.GetYear()))
			this->SetYear("");
		if(!(this->GetGenreNum() == tag.GetGenreNum()))
			if(m_bScmpxGenre)
				this->SetGenre(SCMPX_GENRE_NULL);
			else
				this->SetGenre(WINAMP_GENRE_NULL);
		if(!this->GetComment().compare(tag.GetComment()))
			this->SetComment("");
		if(!this->GetTrackNo().compare(tag.GetTrackNo()))
			this->SetTrackNo("");
	}
	else
	{
		this->SetTitle("");
		this->SetArtist("");
		this->SetAlbum("");
		this->SetYear("");
		if(m_bScmpxGenre)
			this->SetGenre(SCMPX_GENRE_NULL);
		else
			this->SetGenre(WINAMP_GENRE_NULL);
		this->SetComment("");
		this->SetTrackNo("");
	}

	return 0;
}
*/

/*
int CId3tagv1::Save(HWND hWnd,const char *szFileName)
{
	// int	dwWin32errorCode = ERROR_SUCCESS;
	FILE	*fp;
	char	szTmp[128];
	char	szTagTmp[4];
	char	*p;

	//情報の保存
	p = szTmp;
	memset(p, 0x00, (128 - 3));
	strncpy(p, m_szTitle, strlen(m_szTitle));
	p += 30;
	strncpy(p, m_szArtist, strlen(m_szArtist));
	p += 30;
	strncpy(p, m_szAlbum, strlen(m_szAlbum));
	p += 30;
	strncpy(p, m_szYear, strlen(m_szYear));
	p += 4;
	strncpy(p, m_szComment, strlen(m_szComment));
	p += 28;
	if(m_cTrackNo)
	{
		*p = '\0';
		*(p + 1) = m_cTrackNo;
	}
	p += 2;
	*p = m_cGenre;

	if((fp = fopen(szFileName, "r+b")) == NULL)
		return -1;
	//ID3タグがあるはずの位置までSEEK
	if(fseek(fp, -128, SEEK_END) != 0)
	{
		fclose(fp);
		return -1;
	}
	//ID3タグかどうかをチェック
	if(fread(szTagTmp, 1, 3, fp) < 3)
	{
		fclose(fp);
		return -1;
	}
	if(strncmp("TAG", szTagTmp, 3) != 0)
	{
		//ID3TAGが見つからない
		if(fseek(fp, 0, SEEK_END))
		{
			fclose(fp);
			return -1;
		}
		fwrite("TAG", 1, 3, fp);
	}
	if(fseek(fp, 0, SEEK_CUR))
	{
		fclose(fp);
		return -1;
	}
	if(fwrite(szTmp, 1, (128-3),, fp) < (128-3))
	{
		fclose(fp);
		return -1;
	}
	if(fclose(fp) == EOF)	//ライトプロテクトはここで検出
		return -1;

	return 0;
}

int CId3tagv1::DelTag(HWND hWnd,const char *szFileName)
{
	// int	dwWin32errorCode = ERROR_SUCCESS;
	FILE	*fp;
	char	szTag[4];

	//開く
	if((fp=fopen(szFileName, "r+b")) == NULL)
		return -1;
	//ID3タグがあるはずの位置までSEEK
	if(fseek(fp, -128, SEEK_END) != 0)
	{
		fclose(fp);
		return -1;
	}
	//ID3タグかどうかをチェック
	if(fread(szTag, 1, 3, fp) < 3)
	{
		fclose(fp);
		return -1;
	}
	if(strncmp("TAG", szTag, 3) != 0)
	{
		fclose(fp);
		//ID3タグが無いならそれもOK
		return -1;
	}
	if(fseek(fp, -3, SEEK_CUR))
	{
		fclose(fp);
		return -1;
	}
	//切りつめ
	if(_chsize(fileno(fp), ftell(fp)) == -1)
	{
		fclose(fp);
		return -1;
	}

	if(fclose(fp) == EOF)	//ライトプロテクトはここで検出
		return -1;

	Release();

	return 0;
}
*/

void CId3tagv1::getId3tagString(char *szTag)
{
	memset(szTag, 0x00, 128);
	strncpy(szTag, "TAG", 3);
	if(m_bScmpxGenre)
		szTag[127] = (char)SCMPX_GENRE_NULL;
	else
		szTag[127] = (char)WINAMP_GENRE_NULL;
}

/*
 * Win32 APIばりばり
 */
/*
int CId3tagv1::MakeTag(HWND hWnd,const char *szFileName)
{
	// int	dwWin32errorCode = ERROR_SUCCESS;
	HANDLE	hFile;
	int	dwWritten = 0;
	char	szTag[128];
	char	szDefaultName[MAX_PATH];
	
	hFile = CreateFile(
				szFileName,
				GENERIC_WRITE|GENERIC_READ,
				FILE_SHARE_READ,
				NULL,
				OPEN_EXISTING,
				FILE_ATTRIBUTE_NORMAL,
				NULL);
	if(hFile == INVALID_HANDLE_VALUE)
	{
		dwWin32errorCode = GetLastError();
		return dwWin32errorCode;
	}

	//ファイルの終端までまでSEEK
	if(SetFilePointer(hFile,0,NULL,FILE_END) == INVALID_SET_FILE_POINTER)
	{
		dwWin32errorCode = GetLastError();
		CloseHandle(hFile);
		return dwWin32errorCode;
	}
	//ID3タグを作成
	GetId3tagString(szTag);
	strcpy(szDefaultName,getFileName(szFileName));
	strncpy(szTag+3,szDefaultName,30);
	if(WriteFile(hFile,szTag,128,&dwWritten,NULL) == 0)
	{
		dwWin32errorCode = GetLastError();
		CloseHandle(hFile);
		return dwWin32errorCode;
	}
	CloseHandle(hFile);

	Release();
	SetTitle(szDefaultName);
	
	return dwWin32errorCode;
	return 0;
}
*/

/*
 * GlobalCommand.cppからの移植
 */
long check2ByteLength(const char *szTag,long lLimit)
{
/*
	long i=0;
	while(szTag[i])
	{
		bool bIsLb = IsDBCSLeadByte(szTag[i]);
		if(bIsLb)
			i++;
		if(i >= lLimit)
		{
			if(bIsLb)
				return (i-1);
			return i;
		}
		i++;
	}
	return i;
*/
	return lLimit;
}

//欠けている可能性のある２バイト文字対応
void mbsncpy2(unsigned char *dst, unsigned char *src, int c)
{
/*
	for(int i=0; i<c; i++)
	{
		if(IsDBCSLeadByte(src[i]))
		{
			//欠けているリードバイトはコピーしない
			if((i+1) == c)
			{
				dst[i] = '\0';
				break;
			}
			dst[i] = src[i];
			i++;
		}
		dst[i] = src[i];
		if(src[i] == '\0')
		{
			break;
		}
	}
*/
}
