//----------------------------------------------------------------------------
// FAT32ファイル検索関数
//	ロングファイル名対応版
//----------------------------------------------------------------------------

#include "defines.h"
#include "acdata.h"	// ctos80
#include "astring.h"	// SplitDir
#include "acmp.h"	// fcpwildDOS
#include "ascsi.h"
#include "FAT32.h"
#include "u2stbl.h"	// Unicode→S-JIS変換
#include <string.h>	// strcpy
#include <error.h>

#define	DEBUG	0
#if DEBUG
#include <stdio.h>
#endif

// #define _VFAT_があったら_VFAT対応になる

#ifdef _VFAT_
#define	VFAT1NAME	13	// VFATの１エントリ内のファイル名格納文字数
#endif

//----------------------------------------------------------------------------

#if DEBUG >=2
void PrintAtrb(unchar atr)
// ファイル属性表示
{
	printf((atr&FA_ARCH)?  "A":"-");
	printf((atr&FA_DIREC)? "D":"-");
	printf((atr&FA_VOL)?   "V":"-");
	printf((atr&FA_SYS)?   "S":"-");
	printf((atr&FA_HIDE)?  "H":"-");
	printf((atr&FA_RONLY)? "R":"-");
	printf(" ");
}

void PrintFDate(unshort dt)
// ファイル日付表示
{
	if (dt==0) { // 特殊表示
		printf("0000/00/00 ");
	} else {
		printf("%04d/%02d/%02d "
			,((dt&0b1111111000000000)>>9)+1980
			, (dt&0b0000000111110000)>>5
			, (dt&0b0000000000001111)>>0
		);
	}
}

void PrintFTime(unshort tm)
// ファイル時刻表示
{
	printf("%02d:%02d:%02d "
		,(tm&0b1111100000000000)>>11
		,(tm&0b0000011111100000)>>5
		,(tm&0b0000000000011111)>>0
	);
}
#endif

int
#ifdef _VFAT_
_VFILES32
#else
_FILES32
#endif
	(FILE32 *fp,unchar *fname,int atr,int fstart)
// fstart : 0=継続(NFILES) , 1=新規ファイル(FILES) , 2=新規ディレクトリ
// fnameにワイルドカード指定可能；比較方法はDOSと全く同じ
// ret : >=0:見つかった , <0:見つからなかった/エラーが発生した
// ret<0になった時には絶対に継続させないこと
// ワイルドカードを許すのはファイル名部分のみ
{
int ret=-1; // 見つからなかったの印
unchar *b,at;
unchar dir[MAXLONGFILE]; // １ディレクトリ名
#ifdef _VFAT_
int vfat=0,fend,len;
unchar sum;
UniCode unicodes[MAXLONGFILE];
unchar fnameL[MAXLONGFILE];
#endif
	#if DEBUG >=2
	#ifdef _VFAT_
	printf("_VFILES32:atr=%02x\n",atr);
	#else
	printf("_FILES32:atr=%02x\n",atr);
	#endif
	#endif
	switch (fstart) {
	    case 1: // 新規検索時
		#ifdef _VFAT_
		// 指定ファイル名を大文字化しておく
		strcpy(fnameL,fname);
		jstrupr(fnameL);
		fname=fnameL;
		#endif
	    	
		// path->SCSI-ID+LUN
		fname=Path2ScsiID(fname,&fp->scsiid);
		if (fname==NULL) {
			fp->error=ENOTDIR;
			return(-4); // ID指定がおかしい
		}
		
		// ドライブ情報取得
		if (GetDriveInf32(fp)) return(-2); // ドライブ情報が得られない／サポート外ドライブ
		
		// 初期ディレクトリへFATを移動
		fname=SplitDir(fname,dir); // １ディレクトリ名を取得
		while (dir[0]!=0) {
			#if DEBUG
			printf("dir=%s\n",dir);
			#endif
			#ifdef _VFAT_
			ret=_VFILES32(fp,dir,FA_DIREC,2);
			#else
			ret=_FILES32(fp,dir,FA_DIREC,2);
			#endif
			if (ret<0) {
				fp->error=ENOTDIR;
				return(-1); // ディレクトリが見つからない
			}
			fp->fat=fp->filefat;
			fname=SplitDir(fname,dir); // 次の１ディレクトリ名を取得
		} // ルートディレクトリ指定の時は何もしない
		// fname=ファイル名
	    case 2: // ディレクトリサーチ（内部処理用；非公開）
	    	strcpy(fp->nextfname,fname);
	    	fp->nextatr=atr;
		#ifdef _VFAT_
		len=strlen(fname);
		#endif
		#if DEBUG >= 2
		printf("fname=%s\n",fname);
		#endif
	    	break;
	    case 0: // 継続時
	    	b=fp->next;
	    	fname=fp->nextfname;
		#ifdef _VFAT_
		len=strlen(fname);
		#endif
	    	atr=fp->nextatr;
	    	#if DEBUG >=2
	    	printf("途中fname=%s\n",fname);
	    	#endif
	    	goto Keizoku;
	}
	
	// 検索
	do {
	    // １クラスタ分読み出し
	    if (CluRead(fp)) return(-3); // 読み出せない
	    b=fp->buff;
	    #if DEBUG >= 3
	    {
	    FILE *fp16;
	    fp16=fopen("DIR.DAT","wb");
	    if (fp16!=NULL) {
		    fwrite(b,CLU1MAX,1,fp16);
		    fclose(fp16);
	    }
	    }
	    #endif
	    do {
		// ディレクトリは１エントリ３２バイト
		// ディレクトリエントリ構造
		// [00〜07] : ファイル名
		// [08〜0A] : 拡張子
		// [0b]     : 属性
		// [0c]     : 未使用(NTの小文字フラグ)
		// [0d]     : 作成時刻10ms
		// [0E〜0F] : 作成時刻
		// [10〜11] : 作成日付
		// [12〜13] : アクセス日付
		// [14〜15] : クラスタ上位
		// [16〜17] : 更新時刻
		// [18〜19] : 更新日付
		// [1A〜1B] : クラスタ下位
		// [1C〜1F] : ファイルサイズ
		// 属性
		// bit  5=A : 通常ファイル
		//	4=D : ディレクトリ
		//	3=V : ボリューム名
		//	2=S : システムファイル
		//	1=H : 隠しファイル
		//	0=R : 読みだし専用ファイル
		if (*b==0) {
			// Windows95OSR2での実験では、ファイルエントリは
			// １クラスタ未満で終了することがある。
			//（確認したのは８Ｋクラスタで４Ｋで終わっていた。）
			// そのためエントリ終了を[0]=0だけでなくFATリンクの
			// ENDofFATsとあわせるようにした。
			ret=(-1); // End of Directory : ファイルがない
			break;
		}
		if (*b==0xe5) { // 削除ファイルなので飛ばす
			#ifdef _VFAT_
			vfat=0; // vfat途中に有ったら、そのvfatエントリ並び全体を無効にする
			#endif
			continue;
		}
		
		if (*b==0x05) *b=0xe5; // 0xe5で始まるファイル名は0x05に置き換えられている
		at=b[0x0b];
		if (at==0x0f) { // VFATエントリの可能性有り:VFATはVSHRが立つ
		    #ifdef _VFAT_
		    if (b[0x1a]==0 && b[0x1b]==0) { // さらにFAT=0なのでVFATである
			if (b[0x00]>=0x40) { // 最後のエントリ
				fend=1; // 最後
				fp->VFATTop=fp->oldfat; // VFAT先頭FAT(fp.fatは次に読むfatである)
				fp->OfstVFATTop=(unshort)(b-fp->buff); // VFAT先頭オフセット
				vfat=(int)(b[0x00]-0x40); // 番号
				sum=b[0x0d]; // Check Sum
				#if DEBUG >=2
				printf("先頭VFAT:%2d/SUM=$%02x\n",vfat,sum);
				#endif
			} else {
			    if (vfat>0) {
				if (b[0x00]!=vfat || b[0x0d]!=sum) vfat=0;
				// 並びがおかしい/CheckSumがおかしいのでこのエントリは全部無効
				#if DEBUG >=2
				printf("途中VFAT:%2d/SUM=$%02x\n",vfat,sum);
				#endif
				fend=0; // 途中
			    } // 一端おかしくなったら、次の最後のエントリが来るまで処理しない
			}
			if (vfat>0) {
				// ファイル名取得
				vfat--; // エントリ番号は最後→1へと逆順である
				VGetLongname(b,&unicodes[vfat*VFAT1NAME],fend);
				if (vfat==0) vfat=-1; // 消えるとまずいので
			}
		    } else vfat=0;
		    #endif
		    continue;
		}
		if (at==0) at|=FA_ARCH; // MS-DOSではFA_ARCHが立たない通常ファイルが存在するので対策
		if (!(at&atr)) { // 属性が全く合わない
			#if DEBUG >=2
			printf("属性:%02x<->指定:%02x\n",at,atr);
			#endif
			continue;
		}
		
		#ifdef _VFAT_
		// CheckSumの判定
		#if DEBUG >=2
		if (vfat!=0) printf("\nVFAT:SUM:$%02x<->Calc:$%02x\t",sum,VCalcFnameSum(b));
		#endif
		if (vfat!=0 && sum==VCalcFnameSum(b)) { // 一致
			unchar Lfname[MAXLONGFILE];
			Uni2SJisSF(fp->lname,unicodes);
			#if DEBUG
			printf("%s\n",fp->lname);
			#endif
			// ロングファイル名比較
			// ロングファイル名内では'.'は単なる文字なので、そのまま全体を比較する
			// これに付いては、Windowsとは比較結果が違うかも知れない
			strcpy(Lfname,fp->lname);
			jstrupr(Lfname); // 英大文字小文字は区別しない
			if (jcpwild(fname,Lfname)) goto LONGOK; // ロングファイル名一致
			// ロングファイル名が一致しない時はショートファイル名で比較
		} else { // 不一致の時はこのエントリは無効
			fp->lname[0]=0; // なし
			#if 0
			// ロングファイル名にはショートファイル名そのものを入れておく
			strcpy(fp->lname,fp->name);
			#endif
		}
		// ロングファイル名がない時は従来と同じ比較
		vfat=0;
		// 指定ファイル名がロングファイル名でしか表現出来ない時は
		// ショートファイル名による比較はしない（しようとするとfcpwildDOS()が暴走する）
		if (len>=MAXFILEEXT) continue;
		#endif
		
		#if DEBUG
		{int i;
		printf("<%02x←%02x><cmp:",at,atr);
		for (i=0;i<8+3;i++) printf("%c",b[i]);
		printf("←%s>\n",fname);
		}
		#endif
		if (!fcpwildDOS(fname,b)) continue; // 名前が一致しない
	#ifdef _VFAT_
	    LONGOK:;
	#endif
		// 見つかった
		{unchar *nm0,*nm,c;
		int i;
		nm0=fp->name0;
		nm=fp->name;
		for (i=0;i<8;i++) {
			c=b[i];
			*nm0++=c;
			if (c!=' ') *nm++=c; // 普通のファイル名形式に変換
		}
		*nm++='.'; // 以下拡張子
		for (i=0;i<3;i++) {
			c=b[8+i];
			*nm0++=c;
			if (c!=' ') *nm++=c; // 普通のファイル名形式に変換
		}
		if (*(nm-1)=='.') nm--; // 最後が.で終わる時はそれを消す
		*nm0=*nm=0; // EOS
		}
		#if DEBUG
		printf("%s\t",fp->name);
		#endif
		
		fp->atr    =b[0x0b];
		fp->m10ms  =b[0x0d];
		fp->mtime  =ctos80(&b[0x0e]);
		fp->mdate  =ctos80(&b[0x10]);
		fp->adate  =ctos80(&b[0x12]);
		fp->time   =ctos80(&b[0x16]);
		fp->date   =ctos80(&b[0x18]);
		fp->filelen=ctol80(&b[0x1c]);
		fp->filefat=fp->topfat=(ctos80(&b[0x14])<<16)|ctos80(&b[0x1a]);
		#if DEBUG >=2
		PrintAtrb(fp->atr);
		PrintFDate(fp->date);
		PrintFTime(fp->time);
		PrintFDate(fp->adate);
		printf("%ld ",fp->filelen);
		printf("/ファイルFAT:%08x\n",fp->filefat);
		#endif
		fp->next =b; // 次回検索用
		fp->error=0;
		return(0);
		/***************************************/
		Keizoku:;
	    } while (b+=32,b<&fp->buff[CLU1MAX]); // 1ファイルエントリ32バイト
	    // この１クラスタ分（未満）内にはない
	    // fp->fatはすでに次のFAT番号を示している
	    #if DEBUG >=2
	    printf("NextFat=$%x\n",fp->fat);
	    #endif
	} while(fp->fat!=ENDofFATs); // FATリンク最後まで
	// ファイルがない
	#if DEBUG >=2
	printf("ファイルがない:end of fat\n");
	#endif
	fp->error=ENFILE;
//    FILES_END:;
	return(ret);
}

//----------------------------------------------------------------------------

#if 0

void main(int argc,unchar *argv[])
{
FILE32 fp;
int ret;
unchar *path;
	ret=getfname(argc,argv,1,NULL);
	if (ret==0) {
		printf("パス名が必要です\n");
		exit(1);
	}
	path=argv[ret];
	{
	unchar drv[MAXDRIVE+1]; // LUNが入ると＋１桁必要だから
	unchar dir[MAXDIR];
	unchar fname[MAXFILE];
	unchar ext[MAXEXT];
	strsfn32(path,drv,dir,fname,ext);
	printf("drv=%s,dir=%s,name=%s,ext=%s\n",drv,dir,fname,ext);
	}
	#ifdef _VFAT_
	ret=_VFILES32(&fp,path,FA_ARCH|FA_DIREC,1);
	#else
	ret=_FILES32(&fp,path,FA_ARCH|FA_DIREC,1);
	#endif
	printf("最初ret=%d\n",ret);
	while (ret>=0) {
		#ifdef _VFAT_
		printf("\tS:%s\tL:%s\n",fp.name,fp.lname);
		#else
		printf("\t%s\n",fp.name);
		#endif
		#ifdef _VFAT_
		ret=_VFILES32(&fp,"",0,0);
		#else
		ret=_FILES32(&fp,"",0,0);
		#endif
	}
	printf("最後ret=%d\n",ret);
	exit(0);
}
#endif

//----------------------------------------------------------------------------
