#define	FA_NORMAL	0x00		/* Normal file,	no attributes */
#define	FA_RDONLY	0x01		/* Read	only attribute */
#define	FA_HIDDEN	0x02		/* Hidden file */
#define	FA_SYSTEM	0x04		/* System file */
#define	FA_LABEL	0x08		/* Volume label	*/
#define	FA_DIREC	0x10		/* Directory */
#define	FA_ARCH		0x20		/* Archive */

enum { F_READ, F_WRITE, F_READWRITE };

#ifdef _CHECKLONGNAMESUPPORT_
	#ifndef _USELONGNAME_
		#define _USELONGNAME_ TRUE
	#endif
	
	:byte longnameApi = 0;

	#define	MAXPATH		261 + 256

	#define FFBLK _L_FFBLK
	#define setcurrentpath chdir
	#define chmod setfattr
	#define unlink deletefile

	:void initLongnameApi()
	{
		longnameApi = checkLongnameSupport();		
	}

	:byte isLongnameSupport()
	{
		return longnameApi;
	}

	:fastcall truename(SI,DI)
	{
		IF(longnameApi) @_l_truename();
		ELSE @_d_truename();
	}

	:int fastcall fopen(DX,AL)
	{
		IF(longnameApi) @_l_fopen(DX,AL);
		ELSE @_d_fopen(DX,AL);
	}

	:int fastcall fcreate(DX,CX)
	{
		IF(longnameApi) @_l_fcreate(DX,CX);
		ELSE @_d_fcreate(DX,CX);
	}

	:fastcall chdir(DX)
	{
		IF(longnameApi) @_l_chdir();
		ELSE @_d_chdir();
	}

	:fastcall mkdir(DX)
	{
		IF(longnameApi) @_l_mkdir();
		ELSE @_d_mkdir();
	}

	:fastcall rmdir(DX)
	{
		IF(longnameApi) @_l_rmdir();
		ELSE @_d_rmdir();
	}

	:fastcall rename(DX,DI)
	{
		IF(longnameApi) @_l_rename();
		ELSE @_d_rename();
	}

	:fastcall getfattr(DX)
	{
		IF(longnameApi) @_l_getfattr();
		ELSE @_d_getfattr();
	}

	:fastcall setfattr(DX,CX)
	{
		IF(longnameApi) @_l_setfattr();
		ELSE @_d_setfattr();
	}

	:fastcall deletefile(DX)
	{
		IF(longnameApi) @_l_deletefile();
		ELSE @_d_deletefile();
	}

	:fastcall getcurrentpath(SI,DL)
	{
		IF(longnameApi) @_l_getcurrentpath();
		ELSE @_d_getcurrentpath();
	}

	:int fastcall findfirst(DX,DI,CX)
	{
		IF(longnameApi) @_l_findfirst(DX,DI,CX);
		ELSE @_d_findfirstfix(DX,DI,CX);
	}

	:int fastcall findnext(DX)
	{
		IF(longnameApi) @_l_findnext(DX);
		ELSE @_d_findnextfix(DX);
	}

	:fastcall findclose(BX)
	{
		IF(longnameApi) @_l_findclose();
	}

	:fastcall fileexist(DX)
	{
		FFBLK ffblk;

		findfirst(DX,#ffblk,FA_RDONLY + FA_HIDDEN + FA_SYSTEM + FA_DIREC);
		IF(AX == 0) return TRUE;
		ELSE return FALSE;
	}

#else
	#ifdef _USELONGNAME_
		#define	MAXPATH		261 + 256
		#define fileexist _l_fileexist
		#define truename _l_truename
		#define fopen _l_fopen
		#define fcreate _l_fcreate
		#define setcurrentpath _l_chdir
		#define chdir _l_chdir
		#define mkdir _l_mkdir
		#define rmdir _l_rmdir
		#define rename _l_rename
		#define getfattr _l_getfattr
		#define setfattr _l_setfattr
		#define chmod _l_setfattr
		#define unlink _l_deletefile
		#define deletefile _l_deletefile
		#define getcurrentpath _l_getcurrentpath
		#define FFBLK _L_FFBLK
		#define findfirst _l_findfirst
		#define findnext _l_findnext
		#define findclose _l_findclose
	#else
		#define	MAXPATH		261
		#define fileexist _d_fileexist
		#define truename _d_truename
		#define fopen _d_fopen
		#define fcreate _d_fcreate
		#define setcurrentpath _d_chdir
		#define chdir _d_chdir
		#define mkdir _d_mkdir
		#define rmdir _d_rmdir
		#define rename _d_rename
		#define getfattr _d_getfattr
		#define setfattr _d_setfattr
		#define chmod _d_setfattr
		#define unlink _d_deletefile
		#define deletefile _d_deletefile
		#define getcurrentpath _d_getcurrentpath
		#define FFBLK _D_FFBLK
		#define findfirst _d_findfirst
		#define findnext _d_findnext
		#define findclose _d_findclose
	#endif
#endif

:int checkLongnameSupport()
{
	AX = 713Bh;
	DX = ".";
	$int 21h;
	IF(AX == 7100h) return FALSE;
	ELSE return TRUE;
}

:fastcall fclose(BX)
{
	AH = 3Eh;
	$int 21h
	IF(!CARRYFLAG) AX = 0;
}

:fastcall fread(BX,DX,CX)
{
	AH = 3Fh;
	$int 21h
	IF(CARRYFLAG) AX = 0;
}

:fastcall fwrite(BX,DX,CX)
{
	AH = 40h;
	$int 21h
	IF(CARRYFLAG) AX = 0;
}

:fastcall setdta(DX)
{
	AH = 1Ah;
	$int 21h
}

:fastcall getdta()
{
	AH = 2Fh;
	$int 21h
}

:fastcall getcurrentfullpath(DX)
{
	@truename(".",DX);
}

:fastcall getcurrentdrive()
{
	AH = 19h;
	$int 21h
}

:fastcall setcurrentdrive(DL)
{
	AH = 0Eh;
	$int 21h
}

:fastcall getftime(BX)
{
	AX = 5700h;
	$int 21h
}

:fastcall setftime(BX,CX,DX)
{
	AX = 5701h;
	$int 21h
}

:word fastcall getfilesize(DX) // return from DX,AX
{
	IF(@fopen(DX,0) == 0)
	{
		DX = 0;
		AX = 0;
		return;
	}
	BX >< AX; 
	AX = 4202h;
	DX = 0;
	CX = 0;
	$int 21h
	$push ax
	AH = 3Eh;
	$int 21h
	$pop ax	
}

:dword fastcall getfilesize32(DX)
{
	IF(@fopen(DX,0) == 0) return 0;
	BX >< AX; 
	AX = 4202h;
	DX = 0;
	CX = 0;
	$int 21h
	$push dx,ax
	AH = 3Eh;
	$int 21h
	$pop eax
	return EAX;
}

:dword fastcall getdfreespace(BL)
{
	char extendedFreeSpaceBuffer[30h];
	char driveBuffer[4];

	SSDWORD[#driveBuffer] = '\\:C';
	driveBuffer[0] = BL;

	AX = 7303h;
	DX = #driveBuffer;
	DI = #extendedFreeSpaceBuffer;
	CX = sizeof(extendedFreeSpaceBuffer);
	$int 21h

	IF(!CARRYFLAG) || (AL != 0) // Under DOS 7.x 
	{
		SI = #extendedFreeSpaceBuffer;
		RETURN DSDWORD[SI + 4] * DSDWORD[SI + 8] * DSDWORD[SI + 0Ch];
	}

	AH = 36h;
	DL = BL - 'A' + 1;
	$int 21h
	IF(AX != -1) 
	{
		$mul cx
		$mul bx
	}
	ELSE AX = 0;
	
	EDX <<= 10h;
 	return AX + EDX;
}

// Normal DOS API

:int fastcall _d_findfirstfile(DX,CX)
{
	AH = 4Eh;
	$int 21h    
	IF(!CARRYFLAG) AX = 0;
}

:int fastcall _d_findnextfile()
{
	AH = 4Fh;
	$int 21h    
	IF(!CARRYFLAG) AX = 0;
}

:int fastcall _d_fileexist(DX)
{
	IF(@_d_findfirstfile(DX,FA_RDONLY + FA_HIDDEN + FA_SYSTEM + FA_DIREC) == 0) return TRUE;
	ELSE return FALSE;	
}

:int fastcall _d_fopen(DX,AL)
{
	AH = 3Dh;
	$int 21h   
	IF(CARRYFLAG) AX = 0;
}

:int fastcall _d_fcreate(DX,CX)
{
	AH = 3Ch;
	$int 21h
	IF(CARRYFLAG) AX = 0;
}

:fastcall _d_mkdir(DX)
{
	AH = 39h;
	$int 21h
}

:fastcall _d_rmdir(DX)
{
	AH = 3Ah;
	$int 21h
}

:fastcall _d_chdir(DX)
{
	AH = 3Bh;
	$int 21h
}

:fastcall _d_truename(SI,DI)
{
	AH = 60h;
	$int 21h
}

:fastcall _d_rename(DX,DI)
{
	AH = 56h;
	$int 21h
}

:fastcall _d_getfattr(DX)
{
	AX = 4300h;
	$int 21h
}

:fastcall _d_setfattr(DX,CX)
{
	AX = 4301h;
	$int 21h
}

:fastcall _d_deletefile(DX)
{
	AH = 41h;
	$int 21h
}

:fastcall _d_getcurrentpath(SI,DL)
{
	AH = 47h;
	$int 21h
}

struct _D_FFBLK
{
	byte	ff_reserved[21]; /* reserved by DOS */
	byte	ff_attrib;       /* attribute found */
	word	ff_ftime;        /* file time */
	word	ff_fdate;        /* file date */
	dword	ff_fsize;        /* file size */
	byte	ff_name[13];     /* found file name */
};

:int fastcall _d_findfirst(DX,SI,CX)
{
	@getdta();  // return in ES:BX
	$push dx
	@setdta(SI);
	$pop dx
	@_d_findfirstfile(DX,CX);
	$push ax
	@setdta(BX);
	$pop ax
}

:int fastcall _d_findnext(DX)
{
	@getdta();  // return in ES:BX
	@setdta(DX);
	@_d_findnextfile();
	$push ax
	@setdta(BX);
	$pop ax
}

:void fastcall _d_findclose(BX)
{
	// nothing to do
}

// LFN DOS API

:fastcall _l_mkdir(DX)
{
	AX = 7139h;
	$int 21h
}

:fastcall _l_rmdir(DX)
{
	AX = 713Ah;
	$int 21h
}

:fastcall _l_chdir(DX)
{
	AX = 713Bh;
	$int 21h
}

:fastcall _l_deletefile(DX)
{
	AX = 7141h;
	SI = 0;
	$int 21h
}

:fastcall _l_getfattr(DX)
{
	AX = 7143h;
	BL = 0;
	$int 21h
}

:fastcall _l_setfattr(DX,CX)
{
	AX = 7143h;
	BL = 1;
	$int 21h
}

:fastcall _l_getcurrentpath(SI,DL)
{
	AX = 7147h;
	$int 21h
}

:int fastcall _l_findfirstfile(DX,CX,DI)
{
	AX = 714Eh;
	SI = 0;
	$int 21h
}

:fastcall _l_findnextfile(BX,DI)
{
	AX = 714Fh;
	SI = 0;
	$int 21h
}

:fastcall _l_findclosefile(BX)
{
	AX = 71A1h;
	$int 21h
}

:fastcall _l_truename(SI,DI)
{
	AX = 7160h;
	CX = 0;
	$int 21h
}

:fastcall _l_rename(DX,DI)
{
	AX = 7156h;
	$int 21h
}

:int fastcall _l_fopen(SI,BL)
{
	AX = 716Ch;
	BH = 0; // read and write
	CX = 0;
	DX = 1; // open, fail if file does not exist
	DI = 0;
	$int 21h
	IF(CARRYFLAG) AX = 0;
}

:int fastcall _l_fcreate(SI,CX)
{
	AX = 716Ch;
	BX = 2; // read and write
	DX = 12h; //create new or truncate
	DI = 0;
	$int 21h
	IF(CARRYFLAG) AX = 0;
}

struct _L_FFBLK
{
	byte	ff_attrib,reserved1[3];
	dword	ff_create_time[2];
	dword	ff_last_access_time[2];
	word	ff_ftime;
	word	ff_fdate;
	dword	ff_last_modify_high;
	dword	ff_high_fsize;
	dword	ff_fsize;
	byte	reserved2[8];
	byte	ff_name[260];
	byte	ff_short_name[14];
	word	ff_handle;
};

:int fastcall _l_findfirst(DX,DI,CX)
{
	AX = 714Eh;
	SI = 1;
	$int 21h
	DI._L_FFBLK.ff_handle = AX;
	IF(!CARRYFLAG) AX = 0;
}

:int fastcall _l_findnext(DI)
{
	BX = DI._L_FFBLK.ff_handle;
	IF(BX == 0) 
	{
		AX = -1;
		$ret
	}
	AX = 714Fh;
	SI = 1;
	$int 21h
	IF(!CARRYFLAG) AX = 0;
}

:int fastcall _l_findclose(BX)
{
	BX = BX._L_FFBLK.ff_handle;
	IF(BX != 0)
	{
		@_l_findclosefile(BX);
	}
}

:int fastcall _l_fileexist(DX)
{
	FFBLK ffblk;

	_l_findfirst(DX,#ffblk,FA_RDONLY + FA_HIDDEN + FA_SYSTEM + FA_DIREC);
	$push ax
	_l_findclose(#ffblk);
	$pop ax
	IF(AX == 0) return TRUE;
	ELSE return FALSE;
}

:void fastcall _restoredta(DX)
{
	DI = DX;
	SI = #DI._L_FFBLK.ff_name + 15;
	CX = 80h;
	$rep $movsb
}

:void fastcall _fixfindstruct(BX)
{
	SI = BX;
	DI = #BX._L_FFBLK.ff_name + 15;
	CX = 80h;
	$rep $movsb

	DI = #BX._L_FFBLK.ff_name;
	SI = #BX._D_FFBLK.ff_name;
	CX = 13;
	$rep $movsb

	BX._L_FFBLK.ff_fsize = BX._D_FFBLK.ff_fsize;
	BX._L_FFBLK.ff_attrib = BX._D_FFBLK.ff_attrib;
	BX._L_FFBLK.ff_ftime = BX._D_FFBLK.ff_ftime;
	BX._L_FFBLK.ff_fdate = BX._D_FFBLK.ff_fdate;
}

:int fastcall _d_findfirstfix(DX,SI,CX)
{
	@getdta();  // return in ES:BX
	$push dx
	@setdta(SI);
	$pop dx
	@_d_findfirstfile(DX,CX);
	$push ax
	@setdta(BX);
	
	_fixfindstruct(SI);

	$pop ax	
}

:int fastcall _d_findnextfix(DX)
{
	@_restoredta(DX);
	@getdta();  // return in ES:BX
	@setdta(DX);
	@_d_findnextfile();
	$push ax
	$push dx
	@setdta(BX);
	$pop dx
	_fixfindstruct(DX);
	$pop ax
}