/****************************************************************************
 ■ HoneyBox
   2010(C) Mr.Honey
****************************************************************************/

#include "mp3.h"
#include "VS1011e.h"
#include "romdat.h"
#include "ff.h"
#include "resource.h"

#define ENABLE_DISP

//=================================================================

typedef struct _params_t {
	rect_t R;
	U8 T;
	U16 C;
} params_t;
typedef struct _pinfo_t {
	DIR* pDir;
	U8 nPos;
	U8 nMax;
	U8 nL;
	const params_t Params;
} pinfo_t;

static UINT Transfer(const BYTE* buf, UINT cb);
static void SetVol(void);
static void SoftReset(void);
static void FastOut(U8 data);
static void Write(U8 addr, U16 data);
static U16 Read(U8 addr);

static U8 FirstOpen(void);
static U8 Seek(U8 flag, U8 skip);
static U8 SeekDir(U8 flag, const char* start);
static U8 IsValidDir(const FILINFO* pInfo);
static U8 SetDir(void);
static U8 SetPos(DIR* pDir, const char* fname);
static U8 SeekFile(U8 flag);
static U8 IsValidFile(const FILINFO* pInfo);
static U8 SetInfo(pinfo_t* pInfo, U8 flag);
static void Close(void);

static void LoadFn(U8* buf, U8 addr, U8 size);
static void SaveFn(U8 addr, U8 size, const U8* buf);
static void CopyFn(char* buf, const char* src);
static U8 IsEqFn(const char* str1, const char* str2);

static void UpdateStat(U8 flags);
static void DrawPos(pinfo_t* pInfo);
static U8 UpdateTime(void);

#define WaitDreq()	while(!MP3_DREQ);

//=================================================================

#define OST_CLOSE		0x00
#define OST_ERROR		0x01
#define OST_NOTFOUND	0x02
#define OST_OPEN		0x03

#define PST_STOP		0x00
#define PST_PLAY		0x01
#define PST_STOPPING	0x02
#define PSM_CHANGE		0x80
#define PSM_NEXT		0x01
#define PSM_SKIP		0x10

#define UPD_OST			0x01
#define UPD_PST			0x02

//=================================================================

#pragma udata overlay share=0x500
static FATFS _s_FS_;
static DIR s_Root;
static FILINFO s_DirInfo;
static DIR s_Dir;
static FILINFO s_FileInfo;
static FIL s_File;
static char s_sWork[EEP_FNAME_LEN];

#pragma udata
static U8 s_nOpen;
static U8 s_nState;
static U8 s_nVol;
static U8 s_nNameR;
static char s_Name[EEP_FNAME_LEN];
static U16 s_nNums;
static U8 s_nIcon;
static U16 s_nTime;
static U8 s_nMin;
static U8 s_nSec;
static U8 s_nPat;

#pragma idata
static pinfo_t s_DirPos = {
	0, 0, 0, 0, {
		{ MP3_DIR_X, MP3_DIR_Y, MP3_DIR_W, MP3_DIR_H },
		MP3_DIR_T, MP3_DIR_C }
};
static pinfo_t s_FilPos = {
	0, 0, 0, 0, {
		{ MP3_POS_X, MP3_POS_Y, MP3_POS_W, MP3_POS_H },
		MP3_POS_T, MP3_POS_C }
};

#ifdef ENABLE_VS1011E_TEST
#define SM_TESTS	0x0020
#else
#define SM_TESTS	0x0000
#endif

//=================================================================

// ●初期化
extern void InitMP3(void)
{
	s_DirPos.pDir = &s_Root;
	s_FilPos.pDir = &s_Dir;
}

// ●ON/OFF
extern void MP3_Enable(U8 flag)
{
	if (flag)
	{
		s_nVol = 0;

		DrawBase(MP3_TITLE, MP3_AREA);
		s_nNameR = 0;
		*s_Name = '\0';
		s_nNums = 0;
		s_nIcon = 0;
		s_nTime = 0xFFFF;
		s_nMin = 0;
		s_nSec = 0;
		s_nPat = 0;

		MP3_XRESET = 1;
		DelayMs(5); // 念のため

		// SM_SDINEW,SM_LAYER12
		WaitDreq();
		Write(SCI_MODE, 0x0802 | SM_TESTS);

		// SCI_CLOCKF(12.288MHz->24.576MHz)
		WaitDreq();
		Write(SCI_CLOCKF, 12288 / 2 + 0x8000);

		// Analog power down
		WaitDreq();
		Write(SCI_VOL, 0xFFFF);

		// ラスト選曲
		s_nOpen = FirstOpen();
		s_nState = PST_STOP;
		UpdateStat(UPD_OST);
	}
	else
	{
		MP3_XRESET = 0;
		Close();
	}
}

// ●音量設定
extern void MP3_Volume(U8 vol)
{
	s_nVol = vol;
	if (s_nState == PST_PLAY)
		SetVol();
}

// ●再生開始
extern void MP3_Play(void)
{
	if (s_nOpen == OST_OPEN && s_nState == PST_STOP)
	{
		SetVol();

		// アンプミュート解除
		DelayMs(200);
		MUTE = 0;

		s_nState = PST_PLAY;
		UpdateStat(UPD_PST);
	}
}

// ●再生停止
extern void MP3_Stop(void)
{
	if (s_nState != PST_STOP)
	{
		s_nState = PST_STOPPING;
		while (s_nState != PST_STOP)
			MP3_Pump();
	}
}

// ●選曲
extern void MP3_Seek(U8 flag, U8 skip)
{
	if (s_nState == PST_STOP)
	{
		// 停止中はカレントを移動させるのみ
		if (s_nOpen == OST_OPEN)
		{
			s_nOpen = Seek(flag, skip);
			UpdateStat(UPD_OST);
		}
	}
	else if (s_nState == PST_PLAY)
	{
		// 再生シーケンスと同期して実行
		s_nState = PSM_CHANGE;
		if (flag) s_nState |= PSM_NEXT;
		if (skip) s_nState |= PSM_SKIP;
	}
}

// ●ステートドリブン
extern U8 MP3_Pump(void)
{
	if (s_nState == PST_PLAY)
	{
		if (MP3_DREQ)
		{
			// データ転送
			UINT cb;
			if (f_forward(&s_File, Transfer, 2048, &cb) != FR_OK)
			{
				// 異常->停止
				s_nState = PST_STOPPING;
			}
			else if (s_File.fptr >= s_File.fsize)
			{
				// 終曲->次のファイル
				s_nState = PSM_CHANGE | PSM_NEXT;
			}
			UpdateTime();
		}
		return 0x00;
	}
	else if (s_nState == PST_STOPPING)
	{
		// アンプミュート
		MUTE = 1;
		DelayMs(50);

		// Analog power down
		WaitDreq();
		Write(SCI_VOL, 0xFFFF);

		// 再生停止
		SoftReset();

		f_lseek(&s_File, 0);
		s_nState = PST_STOP;

		UpdateStat(UPD_PST);
	}
	else if (s_nState & PSM_CHANGE)
	{
		// 選曲
		SoftReset();

		s_nOpen = Seek((s_nState & PSM_NEXT) != 0, (s_nState & PSM_SKIP) != 0);
		s_nState = (s_nOpen == OST_OPEN)? PST_PLAY : PST_STOP;
		UpdateStat(UPD_OST);
	}

	return 0x03;
}

// ●再生中かどうかを調べる
extern U8 MP3_IsPlaying(void)
{
	return (s_nState != PST_STOP);
}

// ●正弦波テスト
#ifdef ENABLE_VS1011E_TEST
extern void MP3_Test(U8 flag)
{
	WaitDreq();

	DisableINT();
	MP3_SCLK = 0;
	MP3_XDCS = 0;

	if (flag)
	{
		// 開始
		FastOut(0x53);
		FastOut(0xEF);
		FastOut(0x6E);
		FastOut(0x68); // Fs=22.050kHz
		FastOut(0x00);
		FastOut(0x00);
		FastOut(0x00);
		FastOut(0x00);
	}
	else
	{
		FastOut(0x45);
		FastOut(0x78);
		FastOut(0x69);
		FastOut(0x74);
		FastOut(0x00);
		FastOut(0x00);
		FastOut(0x00);
		FastOut(0x00);
	}

	MP3_XDCS = 1;
	MP3_SCLK = 1;
	MP3_SI = 0;
	EnableINT();
}
#endif

//=================================================================

// ●転送コールバック
static UINT Transfer(const BYTE* buf, UINT btf)
{
	if (btf > 0)
	{
		UINT total = 0;
		do {
			UINT rem = btf - total;
			U8 cb = (rem >= 32)? 32 : rem;
			U8 j = cb;

			WaitDreq();

			DisableINT();
			MP3_SCLK = 0;
			MP3_XDCS = 0;

			do FastOut(*buf++);
			while (--j > 0);

			MP3_XDCS = 1;
			MP3_SCLK = 1;
			MP3_SI = 0;
			EnableINT();

			total += cb;
		}
		while (total < btf);
		return total;
	}

	return MP3_DREQ;
}

// ●ボリューム設定
static void SetVol(void)
{
	if (s_nVol > 0)
	{
		#define MP3VOL 0x58
		U16 vol = MP3VOL - 
			(((U16)s_nVol * MP3VOL + MAX_VOLUME - 1) / MAX_VOLUME);

		WaitDreq();
		Write(SCI_VOL, MAKEWORD(vol, vol)); // L = R
	}
	else
	{
		// ミュートと等価
		WaitDreq();
		Write(SCI_VOL, 0xFEFE);
	}
}

// ●デコーダのリセット
static void SoftReset(void)
{
	// 途切れ防止
	U16 i = 64; // 64x32=2048 byte;
	do {
		U8 j = 32;
		WaitDreq();

		DisableINT();
		MP3_SCLK = 0;
		MP3_XDCS = 0;

		do FastOut(0); // 0 Padding
		while (--j > 0);

		MP3_XDCS = 1;
		MP3_SCLK = 1;
		EnableINT();
	}
	while (--i > 0);

	// S/W Reset
	WaitDreq();
	Write(SCI_MODE, 0x0806 | SM_TESTS);
	DelayMs(1); // need 2uS
}

// ●送信
static void Write(U8 addr, U16 data)
{
	U16 bits, mask;

	DisableINT();
	MP3_SCLK = 0;
	MP3_XCS = 0;

	// Address (0x02,addr)
	bits = 0x0200 | addr;

	mask = 0x8000; // 8+8Bits
	do {
		MP3_SI = (bits & mask)? 1 : 0;
		MP3_SCLK = 1;
		MP3_SCLK = 0;
	}
	while (mask >>= 1);

	// Data
	mask = 0x8000; // 16Bits
	do {
		MP3_SI = (data & mask)? 1 : 0;
		MP3_SCLK = 1;
		MP3_SCLK = 0;
	}
	while (mask >>= 1);

	MP3_XCS = 1;
	MP3_SCLK = 1;
	MP3_SI = 0;
	EnableINT();
}

// ●受信
static U16 Read(U8 addr)
{
	U16 bits, mask, data = 0;

	DisableINT();
	DIR_MP3_SO = 1;
	MP3_SCLK = 0;
	MP3_XCS = 0;

	// Address (0x03,addr)
	bits = 0x0300 | addr;

	mask = 0x8000; // 8+8Bits
	do {
		MP3_SI = (bits & mask)? 1 : 0;
		MP3_SCLK = 1;
		MP3_SCLK = 0;
	}
	while (mask >>= 1);

	// Data
	mask = 0x8000; // 16Bits
	do {
		MP3_SCLK = 1;
		data = (data << 1) | MP3_SO;
		MP3_SCLK = 0;
	}
	while (mask >>= 1);

	MP3_XCS = 1;
	MP3_SCLK = 1;
	MP3_SI = 0;
	DIR_MP3_SO = 0;
	EnableINT();

	return data;
}

// ●出力
static void FastOut(U8 data)
{
	U8 mask = 0x80;
	do {
		MP3_SI = (data & mask)? 1 : 0;
		MP3_SCLK = 1;
		MP3_SCLK = 0;
	}
	while (mask >>= 1);
}

//=================================================================

// ●初期オープン
static U8 FirstOpen(void)
{
	char fname[EEP_FNAME_LEN];
	U8 nRes;

	// ルートディレクトリを開く
	if (f_chdir("/") != FR_OK)
		return OST_ERROR;
	if (f_opendir(&s_Root, "/") != FR_OK)
		return OST_ERROR;

	// ラストディレクトリを開く
	LoadFn(fname, (U8)LAST_DIR, sizeof(fname));
	if (*fname != '\0')
	{
		for(;;)
		{
			if (f_readdir(&s_Root, &s_DirInfo) != FR_OK)
				return OST_ERROR;
			if (*s_DirInfo.fname == '\0')
				break;
			if (IsEqFn(s_DirInfo.fname, fname))
			{
				if (!IsValidDir(&s_DirInfo))
					*s_DirInfo.fname = '\0';
				break;
			}
		}
		if (*s_DirInfo.fname != '\0')
		{
			if (!SetDir())
				return OST_ERROR;
			
			// ラストファイルを開く
			LoadFn(fname, (U8)LAST_FILE, sizeof(fname));
			if (*fname != '\0')
			{
				for(;;)
				{
					if (f_readdir(&s_Dir, &s_FileInfo) != FR_OK)
						return OST_ERROR;
					if (*s_FileInfo.fname == '\0')
						break;
					if (IsEqFn(s_FileInfo.fname, fname))
					{
						if (!IsValidFile(&s_FileInfo))
							*s_FileInfo.fname = '\0';
						break;
					}
				}
				if (*s_FileInfo.fname != '\0')
				{
					// ファイルが見つかった
					if (!SetInfo(&s_DirPos, 1))
						return OST_ERROR;
					if (!SetInfo(&s_FilPos, 1))
						return OST_ERROR;

					if (f_open(&s_File, fname, FA_READ) == FR_OK)
						return OST_OPEN;
				}

				// ラスト情報は有効でなくなった
				SaveFn((U8)LAST_DIR, sizeof(LAST_DIR), "");
				SaveFn((U8)LAST_FILE, sizeof(LAST_FILE), "");
			}
		}
	}

	// 先頭からシークして有効なエントリを探す
	if (f_readdir(&s_Root, 0) != FR_OK)
		return OST_ERROR;
	do {
		nRes = SeekDir(1, 0);
		if (nRes != OST_OPEN)
			return nRes;

		nRes = SeekFile(1);
		if (nRes == OST_ERROR)
			return OST_ERROR;
	}
	while (nRes != OST_OPEN);

	// エントリが見つかった
	if (!SetInfo(&s_DirPos, 1))
		return OST_ERROR;
	if (!SetInfo(&s_FilPos, 1))
		return OST_ERROR;

	return nRes;
}

// ●シーク
static U8 Seek(U8 flag, U8 skip)
{
	char fname[EEP_FNAME_LEN];
	U8 nRes1, nRes2;

	// ファイルシーク
	if (!skip)
	{
		if (!flag)
		{
			// 再生が開始されて10秒以上経過した場合は頭出し操作とする
			if (Read(SCI_DECODE_TIME) >= 10)
			{
				f_lseek(&s_File, 0);
				return OST_OPEN;
			}
		}
		nRes1 = SeekFile(flag);
		if (nRes1 != OST_NOTFOUND)
		{
			// 成功しない限りクローズ状態にしておく
			if (nRes1 != OST_OPEN && s_nOpen == OST_OPEN)
			{
				f_close(&s_File);
				s_nOpen = OST_CLOSE;
			}
			if (nRes1 == OST_OPEN)
			{
				// 位置情報の取得
				if (!SetInfo(&s_FilPos, 0))
					return OST_ERROR;
			}
			return nRes1;
		}
	}
	// クローズしておく
	if (s_nOpen == OST_OPEN)
	{
		f_close(&s_File);
		s_nOpen = OST_CLOSE;
	}

	// ディレクトリシーク
	CopyFn(fname, s_DirInfo.fname);
	CopyFn(s_sWork, fname);
	do {
		// 次のディレクトリを見つける
		nRes1 = SeekDir(flag, fname);
		if (nRes1 == OST_ERROR)
			return OST_ERROR;

		// 次のファイルを見つける
		nRes2 = SeekFile((skip)? 1 : flag);
		if (nRes2 == OST_ERROR)
			return OST_ERROR;
	}
	while (nRes1 != OST_NOTFOUND && nRes2 != OST_OPEN);

	// 予期せぬエラーの検出
	// 一巡して現在に戻るはずなので通常あり得ない
	if (nRes2 != OST_OPEN)
		return OST_ERROR;

	// 位置情報の取得
	if (!SetInfo(&s_DirPos, 1))
		return OST_ERROR;
	if (!SetInfo(&s_FilPos, 1))
		return OST_ERROR;

	return OST_OPEN;
}

// ●ディレクトリラウンドシーク
static U8 SeekDir(U8 flag, const char* fname)
{
	if (flag)
	{
		// フォワード
		for(;;)
		{
			if (f_readdir(&s_Root, &s_DirInfo) != FR_OK)
				return OST_ERROR;
			if (*s_DirInfo.fname == '\0')
			{
				if (f_readdir(&s_Root, 0) != FR_OK)
					return OST_ERROR;
				if (fname == 0)
					return OST_NOTFOUND;
				for(;;)
				{
					if (f_readdir(&s_Root, &s_DirInfo) != FR_OK)
						return OST_ERROR;
					if (*s_DirInfo.fname == '\0')
						return OST_ERROR; // 通常あり得ない
					if (IsValidDir(&s_DirInfo))
						break;
				}
				if (IsEqFn(s_DirInfo.fname, fname)) {
					return (SetDir())? OST_NOTFOUND : OST_ERROR;
				}
				break;
			}
			if (IsValidDir(&s_DirInfo)) {
				break;
			}
		}
		if (!SetDir()) {
			return OST_ERROR;
		}
	}
	else
	{
		// リバース
		FILINFO dinfo;
		dinfo.fattrib = 0;
		for(;;)
		{
			if (f_readdir(&s_Root, 0) != FR_OK)
				return OST_ERROR;
			for(;;)
			{
				if (f_readdir(&s_Root, &s_DirInfo) != FR_OK)
					return OST_ERROR;
				if (*s_DirInfo.fname == '\0')
				{
					if (*s_sWork != '\0')
						return OST_ERROR; // 通常あり得ない
					break;
				}
				if (IsValidDir(&s_DirInfo))
				{
					if (*s_sWork != '\0')
					{
						if (IsEqFn(s_DirInfo.fname, s_sWork))
							break;
					}
					dinfo = s_DirInfo;
				}
			}
			if (dinfo.fattrib != 0)
			{
				s_DirInfo = dinfo;
				if (!SetPos(&s_Root, s_DirInfo.fname))
					return OST_ERROR;

				if (*s_sWork == '\0')
				{
					if (IsEqFn(s_DirInfo.fname, fname))
						return (SetDir())? OST_NOTFOUND : OST_ERROR;
				}
				if (!SetDir()) {
					return OST_ERROR;
				}

				CopyFn(s_sWork, s_DirInfo.fname);
				break;
			}
			else
			{
				dinfo = s_DirInfo; // 無ければ一巡させる
				*s_sWork = '\0';
			}
		}
	}

	return OST_OPEN;
}

// ●有効なディレクトリかどうかを調べる
static U8 IsValidDir(const FILINFO* pInfo)
{
	if ((pInfo->fattrib & (AM_HID | AM_SYS | AM_VOL | AM_DIR)) != AM_DIR)
		return 0;
	if (pInfo->fname[0] == '.')
		return 0;

	return 1;
}

// ●ディレクトリをセットする
static U8 SetDir(void)
{
	if (f_chdir("/") != FR_OK)
		return 0;
	if (f_opendir(&s_Dir, s_DirInfo.fname) != FR_OK)
		return 0;
	if (f_chdir(s_DirInfo.fname) != FR_OK)
		return 0;

	return 1;
}

// ●ディレクトリ位置を設定する
static U8 SetPos(DIR* pDir, const char* fname)
{
	if (f_readdir(pDir, 0) == FR_OK)
	{
		FILINFO Info;
		for(;;)
		{
			if (f_readdir(pDir, &Info) != FR_OK)
				break;
			if (*Info.fname == '\0')
				break;
			if (IsEqFn(Info.fname, fname))
				return 1;
		}
	}
	return 0;
}

// ●ファイルシーク
static U8 SeekFile(U8 flag)
{
	FILINFO finfo;
	char fname[EEP_FNAME_LEN];

	// リバースシークの為の準備
	if (!flag)
	{
		finfo.fsize = 0;

		if (s_nOpen == OST_OPEN)
			CopyFn(fname, s_FileInfo.fname);
		else
			fname[0] = '\0';

		if (f_readdir(&s_Dir, 0) != FR_OK)
			return OST_ERROR;
	}

	// クローズしておく
	if (s_nOpen == OST_OPEN)
	{
		f_close(&s_File);
		s_nOpen = OST_CLOSE;
	}
	for(;;)
	{
		if (f_readdir(&s_Dir, &s_FileInfo) != FR_OK)
			return OST_ERROR;
		
		if (*s_FileInfo.fname == '\0')
		{
			if (flag)
				return OST_NOTFOUND;
			break;
		}
		if (IsValidFile(&s_FileInfo))
		{
			if (fname[0] != '\0')
			{
				if (IsEqFn(s_FileInfo.fname, fname))
					break;
			}
			if (f_open(&s_File, s_FileInfo.fname, FA_READ) == FR_OK)
			{
				if (flag)
					return OST_OPEN;

				finfo = s_FileInfo;
			}
			// オープンできないファイルはエラーせず次を探す
		}
	}

	// ココはリバースシーク時のみ通る
	if (finfo.fsize != 0)
	{
		s_FileInfo = finfo;
		if (!SetPos(&s_Dir, s_FileInfo.fname))
			return OST_ERROR;

		if (f_open(&s_File, s_FileInfo.fname, FA_READ) == FR_OK)
			return OST_OPEN;

		// オープンできたはずなのに出来なかった場合
		return OST_ERROR;
	}

	return OST_NOTFOUND;
}

// ●有効なファイルかどうかを調べる
static U8 IsValidFile(const FILINFO* pInfo)
{
	if ((pInfo->fattrib & (AM_HID | AM_SYS | AM_VOL | AM_DIR)) != 0)
		return 0;
	if (pInfo->fsize < 8)
		return 0;

	return 1;
}

// ●位置情報を取得設定する
static U8 SetInfo(pinfo_t* pInfo, U8 flag)
{
	FILINFO info;
	U8 fRoot = (pInfo->pDir == &s_Root);
	const char* pCur = (fRoot)? s_DirInfo.fname : s_FileInfo.fname;
	U8 nPos = 0;

	if (f_readdir(pInfo->pDir, 0) != FR_OK)
		return 0;

	for(;;)
	{
		if (f_readdir(pInfo->pDir, &info) != FR_OK)
			return 0;
		if (*info.fname == '\0')
			break;

		if (fRoot) {
			if (!IsValidDir(&info))
				continue;
		}
		else {
			if (!IsValidFile(&info))
				continue;
		}
		if (IsEqFn(info.fname, pCur))
		{
			pInfo->nPos = nPos;
			if (!flag)
				break;
		}
		nPos++;
	}

	if (flag)
	{
		pInfo->nMax = nPos;
		return SetPos(pInfo->pDir, pCur);
	}

	return 1;
}

// ●ファイルをクローズする
static void Close(void)
{
	if (s_nOpen == OST_OPEN)
	{
		f_close(&s_File);

		// ラスト保存
		SaveFn((U8)LAST_DIR, EEP_FNAME_LEN, s_DirInfo.fname);
		SaveFn((U8)LAST_FILE, EEP_FNAME_LEN, s_FileInfo.fname);
	}
	s_nOpen = OST_CLOSE;
}

//=================================================================

// ●ファイル名ロード
static void LoadFn(U8* buf, U8 addr, U8 max)
{
	U8 *end = buf + max;
	do *buf = Read_b_eep(addr++);
	while (*buf != 0 && ++buf < end);
}

// ●ファイル名セーブ
static void SaveFn(U8 addr, U8 max, const U8* buf)
{
	U8 *end = buf + max - 1;
	while (*buf != 0 && buf < end)
	{
		Write_b_eep(addr++, *buf++);
		Busy_eep();
	}
	Write_b_eep(addr, 0);
	Busy_eep();
}

// ●ファイル名コピー
static void CopyFn(char* buf, const char* src)
{
	while (*src) {
		*buf++ = *src++;
	}
	*buf = '\0';
}

// ●ファイル名比較
static U8 IsEqFn(const char* str1, const char* str2)
{
	for (; *str1 != '\0' && *str2 != '\0'; str1++, str2++)
	{
		// 大小文字区別有り
		if (*str1 != *str2)
			return 0;
	}
	return (*str1 == *str2);
}

//=================================================================

// ●表示更新
static void UpdateStat(U8 flags)
{
#ifdef ENABLE_DISP

	static rect_t NameR = {
		MP3_NAME_X, MP3_NAME_Y, 0, MP3_NAME_S
	};
	static text_t NameI = {
		MP3_NAME_X, MP3_NAME_Y, 0, MP3_NAME_S, 0, 0, 0, 0
	};

	if (flags & UPD_OST)
	{
		U16 nNums = 0;

		// タイトル領域
		NameI.rams = 0;
		NameI.roms = 0;

		if (s_nOpen == OST_OPEN)
		{
			NameI.c = MP3_NAME_C1;
			NameI.rams = s_DirInfo.fname;
		}
		else if (s_nOpen == OST_NOTFOUND) {
			NameI.c = MP3_NAME_C2;
			NameI.roms = "Not Found.";
		}
		else {
			NameI.c = MP3_NAME_C3;
			NameI.roms = "Error.";
		}

		if (NameI.rams == 0 || !IsEqFn(s_Name, NameI.rams))
		{
			if (s_nNameR > MP3_NAME_X)
			{
				NameR.w = s_nNameR - MP3_NAME_X;
				LCD_Fill(&NameR, COL_SCREEN);
			}
			s_nNameR = LCD_Text(&NameI);
			if (NameI.rams != 0)
				CopyFn(s_Name, NameI.rams);
			else
				*s_Name = '\0';
		}

		// 内訳領域
		if (s_nOpen == OST_OPEN)
			nNums = MAKEWORD(s_DirPos.nPos, s_DirPos.nMax);
		if (s_nNums != nNums)
		{
			if (s_nNums != 0)
			{
				const static rect_t NumsR = {
					MP3_NUMS_X, MP3_NUMS_Y, MP3_NUMS_W, MP3_NUMS_S
				};
				LCD_Fill(&NumsR, COL_SCREEN);
			}
			if (nNums != 0)
			{
				static text_t NumsI = {
					0, MP3_NUMS_Y, MP3_NUMS_C, MP3_NUMS_S,
					0, 0, 0, TXT_2DIG
				};
				NumsI.x = MP3_NUMS_X;
				NumsI.num = s_DirPos.nPos + 1;
				NumsI.x = LCD_Text(&NumsI);
				NumsI.roms = "/";
				NumsI.x = LCD_Text(&NumsI);
				NumsI.roms = 0;
				NumsI.num = s_DirPos.nMax;
				LCD_Text(&NumsI);
			}
			s_nNums = nNums;
		}

		// ゲージ領域(SDは使用しないので常に実行)
		DrawPos(&s_DirPos);
		DrawPos(&s_FilPos);
	}

	if (flags & (UPD_OST | UPD_PST))
	{
		// アイコン領域
		U8 nIcon = 0;
		if (s_nOpen == OST_OPEN)
			nIcon = (s_nState == PST_PLAY)? PLAY_IMG : STOP_IMG;
		if (s_nIcon != nIcon)
		{
			static const rect_t IconR = {
				PLAY_IX, PLAY_IY, PLAY_IW, PLAY_IH
			};
			if (nIcon != 0) {
				LCD_Blit(&IconR, nIcon);
			}
			else {
				LCD_Fill(&IconR, COL_SCREEN);
			}
			s_nIcon = nIcon;
		}

		// 時間領域
		if (s_nTime != 0 || s_nOpen != OST_OPEN)
		{
			if (s_nOpen == OST_OPEN)
			{
				static text_t TimeI = {
					0, MP3_TIME_Y, MP3_TIME_C, MP3_TIME_S,
					0, 0, 0, TXT_2DIG
				};
				TimeI.x = MP3_TIME_X1;
				LCD_Text(&TimeI);
				TimeI.x = MP3_TIME_X2;
				LCD_Text(&TimeI);
				s_nTime = 0;
			}
			else
			{
				static rect_t TiimeR = {
					0, MP3_TIME_Y, MP3_TIME_W, MP3_TIME_S
				};
				TiimeR.x = MP3_TIME_X1;
				LCD_Fill(&TiimeR, COL_SCREEN);
				TiimeR.x = MP3_TIME_X2;
				LCD_Fill(&TiimeR, COL_SCREEN);
				s_nTime = 0xFFFF;
			}
			s_nMin = s_nSec = s_nPat = 0;
		}
	}

#endif
}

// ●ゲージ描画
static void DrawPos(pinfo_t* pInfo)
{
	rect_t rc = pInfo->Params.R;

	if (pInfo->nL > 0)
	{
		rc.x = pInfo->nL;
		LCD_Fill(&rc, COL_SCREEN);
	}

	rc.x = pInfo->Params.R.x;
	if (pInfo->nMax > 1)
	{
		U8 max = pInfo->nMax - 1;
		rc.x += ((U16)pInfo->nPos * pInfo->Params.T + max - 1) / max;
	}

	LCD_Fill(&rc, pInfo->Params.C);
	pInfo->nL = rc.x;
}

// ●表示更新
static U8 UpdateTime(void)
{
#ifdef ENABLE_DISP

	static text_t TimeI = {
		0, MP3_TIME_Y, MP3_TIME_C, MP3_TIME_S,
		0, 0, 0, 0
	};

	U8 nMin, nSec;
	s_nTime = Read(SCI_DECODE_TIME);
	nMin = s_nTime / 60;
	nSec = s_nTime % 60;

	// 1桁づつ更新しないと途切れる
	if (s_nMin != nMin)
	{
		if (s_nPat == 0)
		{
			TimeI.x = MP3_TIME_X1;
			TimeI.num = nMin / 10;
			s_nPat = LCD_Text(&TimeI);
		}
		else
		{
			TimeI.x = s_nPat;
			TimeI.num = nMin % 10;
			LCD_Text(&TimeI);
			s_nPat = 0;
			s_nMin = nMin;
		}
		return 0x00;
	}
	else if (s_nSec != nSec)
	{
		if (s_nPat == 0)
		{
			TimeI.x = MP3_TIME_X2;
			TimeI.num = nSec / 10;
			s_nPat = LCD_Text(&TimeI);
		}
		else
		{
			TimeI.x = s_nPat;
			TimeI.num = nSec % 10;
			LCD_Text(&TimeI);
			s_nPat = 0;
			s_nSec = nSec;
		}
		return 0x00;
	}

#endif
	return 0x02;
}
