U0 CDate2Dos(U16 *t, U16 *d, CDate cdt)
{
	CDateStruct ds;

	Date2Struct(&ds, cdt);
	*d = ds.day_of_mon + (ds.mon + (ds.year - 1980) << 4) << 5;
	*t = ds.sec >> 1 + (ds.min + ds.hour << 6) << 5;
}

CDate Dos2CDate(U16 t, U16 d)
{
	CDateStruct ds;

	MemSet(&ds, 0, sizeof(CDateStruct));
	ds.day_of_mon	= d & 0x1F;
	d = d >> 5;
	ds.mon			= d & 0xF;
	ds.year			= d >> 4 + 1980;
	ds.sec			= (t & 0x1F) * 2;
	t = t >> 5;
	ds.min			= t & 0x3F;
	ds.hour			= t >> 6;

	return Struct2Date(&ds);
}

U0 FAT32Init(CDrive *drive)
{
	CFAT32Boot br32;
	Bool unlock;

	try
	{
		unlock = DriveLock(drive);
		drive->fs_type = FSt_FAT32;
		BlkRead(drive, &br32, drive->drv_offset, 1);
		drive->file_system_info_sect	= drive->drv_offset + br32.file_system_info_sect;
		drive->fat1						= drive->drv_offset + br32.reserved_sects;
		drive->fat2						= drive->fat1 + br32.sects_per_fat;
		drive->data_area				= drive->fat2 + br32.sects_per_fat - 2 * br32.sects_per_clus; //Starts at Clus 2
		drive->spc						= br32.sects_per_clus;
		drive->root_clus				= br32.root_clus;
		DriveFATBlkAlloc(drive);
		Free(drive->fis);
		drive->fis						= ZMAlloc(BLK_SIZE);
		BlkRead(drive, drive->fis, drive->file_system_info_sect, 1);
		if (unlock)
			DriveUnlock(drive);
	}
	catch
		if (unlock)
			DriveUnlock(drive);
}

U0 FAT32Format(U8 drv_let, Bool quick=TRUE)
{
	CFAT32Boot			*br    = CAlloc(BLK_SIZE);
	CFAT32FileInfoSect	*fis   = CAlloc(BLK_SIZE);
	CDrive				*drive = Letter2Drive(drv_let);
	I64					 i, l;

	try
	{
		DriveLock(drive);
		DriveTypeSet(drv_let, FSt_FAT32);
		drive->fs_type = FSt_FAT32;
		br->jump_and_nop[0]		= OC_JMP_REL8;
		br->jump_and_nop[1]		= offset(CFAT32Boot.code) - 2;
		br->jump_and_nop[2]		= OC_NOP;
		br->oem_name[0](I64)	= 'MSWIN4.1';
		br->bytes_per_sect		= BLK_SIZE;

		if (drive->size <= 500000)
			br->sects_per_clus = 1;
		else if (drive->size <= 2000000)
			br->sects_per_clus = 2;
		else if (drive->size <= 6000000)
			br->sects_per_clus = 4;
		else if (drive->size <= 12000000)
			br->sects_per_clus = 8;
		else if (drive->size <= 33000000)
			br->sects_per_clus = 16;
		else if (drive->size <= 67000000)
			br->sects_per_clus = 32;
		else
			br->sects_per_clus = 64;

		br->reserved_sects			= 32;
		br->copies_of_fat			= 2;
		br->media_desc				= 0xF8;
		br->sects					= drive->size;
		l = (br->sects / br->sects_per_clus) >> (BLK_SIZE_BITS - 2) + 1;
		br->sects_per_fat			= l;
		br->root_clus				= 2;
		br->file_system_info_sect	= 1;
		br->log_drive_num			= 0x80;
		br->ext_signature			= 0x29;
		br->serial_num				= RandU32;
		MemCopy(br->vol_name, "NO NAME    ", 11);
		br->fat_name[0](I64)		= 'FAT32   ';
		br->signature				= 0xAA55;

		fis->signature1				= 'RRaA';
		fis->signature2				= 'rrAa';
		fis->free_clus				= -1;
		fis->most_recently_alloced	= 0;
		fis->signature3				= 0xAA550000;

		if (quick)
			i = br->reserved_sects + 2 * l + 4 * br->sects_per_clus;
		else
			i = drive->size;
		BlkWriteZero(drive, drive->drv_offset, i);

		BlkWrite(drive, fis, drive->drv_offset + br->file_system_info_sect, 1);
		BlkWrite(drive, br, drive->drv_offset, 1);
		FAT32Init(drive);
		ClusAlloc(drive, 0, 1, FALSE); //Alloc #1
		br->root_clus = ClusAlloc(drive, 0, 1, FALSE);
		BlkWrite(drive, br, drive->drv_offset, 1);
		FAT32Init(drive);
		DriveUnlock(drive);
	}
	catch
		DriveUnlock(drive);
	Free(br);
	Free(fis);
}

Bool FATNameTo(U8 *dst, U8 *src)
{
	I64 i;

	MemSet(dst, CH_SPACE, 11);
	if (!FileNameCheck(src))
		return FALSE;
	if (!StrCompare(src, ".."))
	{
		*dst   = '.';
		dst[1] = '.';
		return TRUE;
	}
	else if (!StrCompare(src, "."))
	{
		*dst = '.';
		return TRUE;
	}
	i = 0;
	while (i < 8 && *src && *src != '.')
		dst[i++] = ToUpper(*src++);
	i = 8;
	if (*src == '.')
		src++;
	while (*src)
		if (*src != '.')
			dst[i++] = ToUpper(*src++);
		else
			src++;

	return TRUE;
}

I64 FATNameXSum(U8 *src)
{
	I64 i, res = 0;

	for (i = 0; i < 11; i++)
		if (res & 1)
			res.u8[0] = 0x80 + res >> 1 + *src++;
		else
			res.u8[0] = res >> 1 + *src++;

	return res;
}

Bool FATFromName(U8 *dst, U8 *src)
{
	I64 i, j, k = 0;

	for (j = 7; j >= 0 && src[j] == CH_SPACE; j--);

	for(i = 0; i <= j; i++)
		dst[k++] = src[i];

	for (j = 10; j >= 8 && src[j] == CH_SPACE; j--);

	if (*src != '.' && j != 7)
		dst[k++] = '.';
	for(i = 8; i <= j; i++)
		dst[k++] = src[i];
	dst[k++] = 0;

	return FileNameCheck(dst);
}

U8 fat_long_name_map[13] =
{
	offset(CFAT32DirEntryLong.name1), 
	offset(CFAT32DirEntryLong.name1) + 2, 
	offset(CFAT32DirEntryLong.name1) + 4,
	offset(CFAT32DirEntryLong.name1) + 6,
	offset(CFAT32DirEntryLong.name1) + 8,
	offset(CFAT32DirEntryLong.name2),
	offset(CFAT32DirEntryLong.name2) + 2,
	offset(CFAT32DirEntryLong.name2) + 4,
	offset(CFAT32DirEntryLong.name2) + 6,
	offset(CFAT32DirEntryLong.name2) + 8,
	offset(CFAT32DirEntryLong.name2) + 10,
	offset(CFAT32DirEntryLong.name3),
	offset(CFAT32DirEntryLong.name3) + 2
};

Bool DirLongNameFill(CDirEntry *tmpde, CFAT32DirEntryLong *de, I64 *xsum)
{
	I64 i;
	U8 *ptr = de;

	if (de->ord & 0x40)
	{
		MemSet(tmpde, 0, sizeof(CDirEntry));
		*xsum = de->xsum;
	}
	else if (de->type || de->zero || de->xsum != *xsum)
	{
		MemSet(tmpde, 0, sizeof(CDirEntry));
		*xsum = 0;
		return FALSE;
	}
	switch (de->ord & 0x3F)
	{
		case 1:
			for (i = 0; i < 13; i++)
				if (!(tmpde->name[i] = ptr[fat_long_name_map[i]]))
					return TRUE;
			break;

		case 2:
			for (i = 0; i < 12; i++)
				if (!(tmpde->name[i + 13] = ptr[fat_long_name_map[i]]))
					return TRUE;
			break;
	}

	return TRUE;
}

Bool FAT32CDirFill(CDirEntry *tmpde, CFAT32DirEntry *de, CDate _local_time_offset)
{
	Bool res;

	if (*tmpde->name)
		res = TRUE;
	else
		res = FATFromName(tmpde->name, de->name);
	tmpde->clus		= de->clus_lo + de->clus_hi << 16;
	tmpde->size		= de->size;
	tmpde->attr		= de->attr;
	tmpde->datetime	= Dos2CDate(de->WrtTime, de->WrtDate) - _local_time_offset;

	return res;
}

Bool FAT32DirFill(CFAT32DirEntry *de, CDirEntry *tmpde, I64 *_de_count, CDate _local_time_offset)
{//Fill up to 3 entries and store count of entries.
	I64					 de_count = 0, i, l, xsum, ord;
	U8					*ptr, dname[16];
	CFAT32DirEntryLong	*ld = de;
	Bool				 res;

	MemSet(de, 0, sizeof(CFAT32DirEntry));
	res = FATNameTo(de->name, tmpde->name);
	FATFromName(dname, de->name);
	if (StrCompare(dname, tmpde->name))
	{
		ord = 0x41;
		xsum = FATNameXSum(de->name);
		if ((l = StrLen(tmpde->name)) > 13)
		{
			ptr = &ld[de_count];
			MemSet(ptr, 0, sizeof(CFAT32DirEntryLong));
			ld[de_count].attr = RS_ATTR_LONG_NAME;
			ld[de_count].xsum = xsum;
			ld[de_count].ord  = 0x42;
			for (i = 13; i < l; i++)
				ptr[fat_long_name_map[i - 13]] = tmpde->name[i];
			i++;
			for (; i < 26; i++)
				ptr[fat_long_name_map[i - 13]](U16) = 0xFFFF;
			ord = 1;
			l = 13;
			de_count++;
		}
		ptr = &de[de_count];
		MemSet(ptr, 0, sizeof(CFAT32DirEntryLong));
		ld[de_count].attr = RS_ATTR_LONG_NAME;
		ld[de_count].xsum = xsum;
		ld[de_count].ord  = ord;
		for (i = 0; i < l; i++)
			ptr[fat_long_name_map[i]] = tmpde->name[i];
		i++;
		for (; i < 13; i++)
			ptr[fat_long_name_map[i]](U16) = 0xFFFF;
		de_count++;
		MemSet(&de[de_count], 0, sizeof(CFAT32DirEntry));
		res = FATNameTo(de[de_count].name, tmpde->name);
	}
	de[de_count].clus_lo = tmpde->clus.u16[0];
	de[de_count].clus_hi = tmpde->clus.u16[1];
	if (!(tmpde->attr & RS_ATTR_DIR))
		de[de_count].size = tmpde->size;
	de[de_count].attr = tmpde->attr;
	if (!tmpde->datetime)
		tmpde->datetime = Now;
	CDate2Dos(&de[de_count].WrtTime, &de[de_count].WrtDate, tmpde->datetime + _local_time_offset);
	if (_de_count)
		*_de_count = de_count + 1;

	return res;
}

Bool FAT32FileFind(CDrive *drive, I64 cur_dir_clus, U8 *name, CDirEntry *_res, I64 fuf_flags=0)
{//$LK,"FUF_JUST_DIRS",A="MN:FUF_JUST_DIRS"$, $LK,"FUF_JUST_FILES",A="MN:FUF_JUST_FILES"$
	Bool			 res = FALSE, unlock;
	CFAT32DirEntry	*buf;
	I64				 xsum = 0, attr, cur_dir_entry, entries_per_clus;
	U8				 dname[CDIR_FILENAME_LEN], ch;
	CDirEntry		 long_name;

	if (fuf_flags & ~FUG_FILE_FIND)
		throw('FUF');
	MemSet(_res, 0, sizeof(CDirEntry));
	MemSet(&long_name, 0, sizeof(CDirEntry));
	DriveCheck(drive);
	if (drive->fs_type != FSt_FAT32)
		PrintErr("Not FAT32 Drive\n");
	else if (!CFileNameTo(dname, name))
		PrintErr("Invalid FileName: \"%s\".\n", name);
	else
		try
		{
			unlock = DriveLock(drive);
			buf = MAlloc(BLK_SIZE * drive->spc);
			entries_per_clus = drive->spc << FAT32_ENTRIES_BITS;
			ClusRead(drive, buf, cur_dir_clus, 1);
			cur_dir_entry=0;
			while (ch = *buf[cur_dir_entry].name)
			{
				attr = buf[cur_dir_entry].attr;
				if (ch != 0xE5)
				{
					if (attr & RS_ATTR_LONG_NAME_MASK == RS_ATTR_LONG_NAME)
						DirLongNameFill(&long_name, &buf[cur_dir_entry], &xsum);
					else
					{
						if (!(attr & RS_ATTR_VOL_ID))
						{
							if (xsum == FATNameXSum(buf[cur_dir_entry].name))
								MemCopy(_res, &long_name, sizeof(CDirEntry));
							else
								MemSet(_res, 0, sizeof(CDirEntry));
							if (!(fuf_flags & FUF_JUST_DIRS &&
								!(attr & RS_ATTR_DIR)) &&
								!(fuf_flags & FUF_JUST_FILES && attr & RS_ATTR_DIR) &&
								FAT32CDirFill(_res, &buf[cur_dir_entry], drive->fat32_local_time_offset) &&
								!StrCompare(dname, _res->name))
							{
								res = TRUE;
								goto fff_done;
							}
						}
						MemSet(&long_name, 0, sizeof(CDirEntry));
					}
				}
				else
					MemSet(&long_name, 0, sizeof(CDirEntry));
				if (++cur_dir_entry == entries_per_clus)
				{
					cur_dir_clus = ClusNumNext(drive, cur_dir_clus);
					if (!(0 < cur_dir_clus < 0x0FFFFFF8))
						break;
					else
					{
						ClusRead(drive, buf, cur_dir_clus, 1);
						cur_dir_entry = 0;
					}
				}
			}
			MemSet(_res, 0, sizeof(CDirEntry));
fff_done:
			Free(buf);
			if (unlock)
				DriveUnlock(drive);
		}
		catch
			if (unlock)
				DriveUnlock(drive);

	return res;
}

U8 *FAT32FileRead(CDrive *drive, U8 *cur_dir, U8 *filename, I64 *_size, I64 *_attr)
{
	U8			*buf = NULL;
	CDirEntry	 de;
	I64			 c, blk_count, cur_dir_clus;

	DriveCheck(drive);
	*_size = 0;
	*_attr = 0;
	if (drive->fs_type != FSt_FAT32)
		PrintErr("Not FAT32 Drive\n");
	else
		try
		{
			DriveLock(drive);
			cur_dir_clus = Name2DirClus(drive, cur_dir);
			if (FAT32FileFind(drive, cur_dir_clus, filename, &de, FUF_JUST_FILES))
			{
				blk_count = (de.size + BLK_SIZE - 1) >> BLK_SIZE_BITS;
				buf = MAlloc(blk_count << BLK_SIZE_BITS + 1);
				c = de.clus;
				if (!(0 < c < 0x0FFFFFF8))
					c = 0x0FFFFFFF;
				else
					c = ClusBlkRead(drive, buf, c, blk_count);
				buf[de.size] = 0; //Terminate
				*_size = de.size;
				*_attr = FileAttr(de.name, de.attr);
			}
			DriveUnlock(drive);
		}
		catch
			DriveUnlock(drive);
	return buf;
}

Bool FAT32Cd(U8 *name, I64 cur_dir_clus)
{
	CDirEntry de;

	if (Fs->cur_dv->fs_type != FSt_FAT32)
		PrintErr("Not FAT32 Drive\n");
	else if (FAT32FileFind(Fs->cur_dv, cur_dir_clus, name, &de, FUF_JUST_DIRS))
		return TRUE;
	else
		PrintErr("File not found: \"%s\".\n", name);

	return FALSE;
}

U0 FAT32FreeClus(CDrive *drive, I64 c)
{
	I64  next, saved_c = c;
	Bool unlock, unlock_break;

	DriveCheck(drive);
	if (!(0 < c < 0x0FFFFFF8))
		return;
	if (drive->fs_type != FSt_FAT32)
		PrintErr("Not FAT32 Drive\n");
	else
		try
		{
			unlock_break = BreakLock;
			unlock = DriveLock(drive);
			DriveFATBlkClean(drive);
			do
			{
				DriveFATBlkSet(drive, c, 0);
				next = drive->cur_fat_blk[c & (BLK_SIZE / 4 - 1)];
				drive->cur_fat_blk[c & (BLK_SIZE / 4 - 1)] = 0;
				LBts(&drive->fat_blk_dirty, 0);
				c = next;
			}
			while (0 < c < 0x0FFFFFF8);

			DriveFATBlkClean(drive, 0);

			c = saved_c;
			do
			{
				DriveFATBlkSet(drive, c, 1);
				next = drive->cur_fat_blk[c & (BLK_SIZE / 4 - 1)];
				drive->cur_fat_blk[c & (BLK_SIZE / 4 - 1)] = 0;
				LBts(&drive->fat_blk_dirty, 0);
				c = next;
			}
			while (0 < c < 0x0FFFFFF8);

			DriveFATBlkClean(drive, 1);
			if (unlock)
				DriveUnlock(drive);
			if (unlock_break)
				BreakUnlock;
		}
		catch
		{
			if (unlock)
				DriveUnlock(drive);
			if (unlock_break)
				BreakUnlock;
		}
}

I64 FAT32AllocClus(CDrive *drive, I64 c, I64 count)
{
	Bool wrap_around = FALSE, unlock, unlock_break;
	I64  first = INVALID_CLUS, j, l;

	if (count <= 0)
		return 0x0FFFFFFF;

	try
	{
		unlock_break = BreakLock;
		unlock = DriveLock(drive);
		l = (drive->size + drive->drv_offset - drive->data_area) / drive->spc - 1;
		j = drive->fis->most_recently_alloced;
		while (count-- > 0)
		{
			while (TRUE)
			{
				j++;
				if (j < 1)
					j = 1;
				if (j >= l)
				{
					if (wrap_around)
						throw('Drive');
					j = 1;
					wrap_around = TRUE;
				}
				DriveFATBlkSet(drive, j);
				if (!drive->cur_fat_blk[j & (BLK_SIZE / 4 - 1)])
					break;
			}
			if (!(0 < first < 0x0FFFFFF8))
				first = j;
			if (0 < c < l)
			{
				DriveFATBlkSet(drive, c);
				drive->cur_fat_blk[c & (BLK_SIZE / 4 - 1)] = j;
				LBts(&drive->fat_blk_dirty, 0);
			}
			c = j;
		}

		if (0 < c < l)
		{
			DriveFATBlkSet(drive, c);
			drive->cur_fat_blk[c & (BLK_SIZE / 4 - 1)] = 0x0FFFFFFF;
			LBts(&drive->fat_blk_dirty, 0);
		}
		DriveFATBlkClean(drive);

		drive->fis->most_recently_alloced = j;
		drive->fis->free_clus = -1;
		BlkWrite(drive, drive->fis, drive->file_system_info_sect, 1);
	}
	catch
	{
		if (unlock)
			DriveUnlock(drive);
		if (unlock_break)
			BreakUnlock;
	}
	if (unlock)
		DriveUnlock(drive);
	if (unlock_break)
		BreakUnlock;
	return first;
}

I64 FAT32AllocContiguousClus(CDrive *drive, I64 count)
{
	I64  i, first = 1;
	Bool cont, unlock, unlock_break;

	if (count <= 0)
		return 0x0FFFFFFF;
	try
	{
		unlock_break = BreakLock;
		unlock = DriveLock(drive);
		while (TRUE)
		{
			first++;
			i = 0;
			cont = TRUE;
			while (cont && i < count)
			{
				if ((first + i + 1) * drive->spc + drive->data_area > drive->size + drive->drv_offset)
					throw('Drive');
				DriveFATBlkSet(drive, first + i);
				if (drive->cur_fat_blk[(first + i) & (BLK_SIZE / 4 - 1)])
					cont = FALSE;
				else
					i++;
			}
			if (!cont)
				first = first + i;
			else
			{
				DriveFATBlkClean(drive);

				for (i = 0; i < count; i++)
				{
					DriveFATBlkSet(drive, first + i, 0);
					if (i + 1 == count)
						drive->cur_fat_blk[(first + i) & (BLK_SIZE / 4 - 1)] = 0x0FFFFFFF;
					else
						drive->cur_fat_blk[(first + i) & (BLK_SIZE / 4 - 1)] = first + i + 1;
					LBts(&drive->fat_blk_dirty, 0);
				}
				DriveFATBlkClean(drive, 0);

				for (i = 0; i < count; i++)
				{
					DriveFATBlkSet(drive, first + i, 1);
					if (i + 1 == count)
						drive->cur_fat_blk[(first + i) & (BLK_SIZE / 4 - 1)] = 0x0FFFFFFF;
					else
						drive->cur_fat_blk[(first + i) & (BLK_SIZE / 4 - 1)] = first + i + 1;
					LBts(&drive->fat_blk_dirty, 0);
				}
				DriveFATBlkClean(drive, 1);
				break;
			}
		}
	}
	catch
	{
		if (unlock)
			DriveUnlock(drive);
		if (unlock_break)
			BreakUnlock;
	}
	if (unlock)
		DriveUnlock(drive);
	if (unlock_break)
		BreakUnlock;

	return first;
}

Bool FAT32DirNew(CDrive *drive, U8 *cur_dir, CDirEntry *tmpde, Bool free_old_chain)
{
//See $LK,"::/Doc/CutCorners.DD"$.
	CFAT32DirEntry	*buf, *last_buf, *tmp_buf, de[3];
	I64				 i, attr, avail_count, de_count, c, cur_dir_entry,
					 entries_per_clus, cur_dir_clus, xsum = 0, last_dir_clus = INVALID_CLUS;
	U8				 ch;
	Bool			 written = FALSE, unlock, unlock_break;
	CDirEntry		 long_name;

	FAT32DirFill(&de, tmpde, &de_count, drive->fat32_local_time_offset);
	MemSet(&long_name, 0, sizeof(CDirEntry));
	try
	{
		unlock_break = BreakLock;
		unlock = DriveLock(drive);
		cur_dir_clus = Name2DirClus(drive, cur_dir);
		buf = MAlloc(BLK_SIZE * drive->spc);
		last_buf = CAlloc(BLK_SIZE * drive->spc);
		entries_per_clus = drive->spc << FAT32_ENTRIES_BITS;
		ClusRead(drive, buf, cur_dir_clus, 1);
		cur_dir_entry = 0;
		while (ch = *buf[cur_dir_entry].name)
		{
			attr = buf[cur_dir_entry].attr;
			if (ch != 0xE5 && attr & RS_ATTR_LONG_NAME_MASK == RS_ATTR_LONG_NAME)
				DirLongNameFill(&long_name, &buf[cur_dir_entry], &xsum);
			else
			{
				avail_count = FAT32_ENTRIES_PER_BLK - cur_dir_entry & (FAT32_ENTRIES_PER_BLK - 1);
				for (i = 0; i < avail_count; i++)
					if (*buf[cur_dir_entry + i].name != 0xE5)
					{
						if (*buf[cur_dir_entry + i].name)
							avail_count = i;
						break;
					}
				if (ch == 0xE5 && !written && avail_count >= de_count)
				{
					MemCopy(&buf[cur_dir_entry], &de, de_count * sizeof(CFAT32DirEntry));
					BlkWrite(drive, &buf[cur_dir_entry & -FAT32_ENTRIES_PER_BLK],
							 drive->data_area + cur_dir_clus * drive->spc + cur_dir_entry >> FAT32_ENTRIES_BITS, 1);
					cur_dir_entry += de_count - 1; //gets inc'ed
					written = TRUE;
				}
				else if (ch != 0xE5 && !(attr & RS_ATTR_VOL_ID))
				{
					if (xsum != FATNameXSum(buf[cur_dir_entry].name))
						MemSet(&long_name, 0, sizeof(CDirEntry));
					if (!*long_name.name)
						FATFromName(long_name.name, buf[cur_dir_entry].name);
//Del old entry with same name
					if (!StrCompare(long_name.name, tmpde->name))
					{
						if (free_old_chain)
							FAT32FreeClus(drive, buf[cur_dir_entry].clus_lo + buf[cur_dir_entry].clus_hi << 16);
						if (!written)
						{
							MemCopy(&buf[cur_dir_entry], &de[de_count - 1], sizeof(CFAT32DirEntry));
							BlkWrite(drive, &buf[cur_dir_entry & -FAT32_ENTRIES_PER_BLK], 
									 drive->data_area + cur_dir_clus * drive->spc + cur_dir_entry >> FAT32_ENTRIES_BITS, 1);
							written = TRUE;
						}
						else
						{
							*buf[cur_dir_entry].name = 0xE5;
							i = 1;
							while (i <= cur_dir_entry &&
									buf[cur_dir_entry - i].attr & RS_ATTR_LONG_NAME_MASK == RS_ATTR_LONG_NAME)
								*buf[cur_dir_entry-i++].name = 0xE5;
							i--;
							BlkWrite(drive, &buf[(cur_dir_entry - i) & -FAT32_ENTRIES_PER_BLK], 
									 drive->data_area + cur_dir_clus * drive->spc + (cur_dir_entry - i) >> FAT32_ENTRIES_BITS,
									 (i + FAT32_ENTRIES_PER_BLK) >> FAT32_ENTRIES_BITS);
							if (i == cur_dir_entry && 0 < last_dir_clus < 0x0FFFFFF8)
							{
								i = 1;
								while (i <= entries_per_clus &&
									   last_buf[entries_per_clus - i].attr & RS_ATTR_LONG_NAME_MASK == RS_ATTR_LONG_NAME)
									*last_buf[entries_per_clus - i++].name = 0xE5;
								if (--i > 0)
									BlkWrite(drive, &buf[(entries_per_clus - i) & -FAT32_ENTRIES_PER_BLK], 
											 drive->data_area +
											 last_dir_clus * drive->spc + (entries_per_clus - i) >> FAT32_ENTRIES_BITS,
											 (i + FAT32_ENTRIES_PER_BLK - 1) >> FAT32_ENTRIES_BITS);
							}
						}
						break;
					}
				}
				MemSet(&long_name, 0, sizeof(CDirEntry));
			}
			if (++cur_dir_entry == entries_per_clus)
			{
				last_dir_clus = cur_dir_clus;
				tmp_buf		= buf;
				buf			= last_buf;
				last_buf	= tmp_buf;
				c=ClusNumNext(drive, cur_dir_clus);
				if (!(0 < c < 0x0FFFFFF8))
				{
					c = ClusAlloc(drive, cur_dir_clus, 1, FALSE);
					MemSet(buf, 0, BLK_SIZE * drive->spc);
					ClusWrite(drive, buf, c, 1);
				}
				else
					ClusRead(drive, buf, c, 1);
				cur_dir_clus  = c;
				cur_dir_entry = 0;
			}
		}
		if (!written)
		{
			avail_count = FAT32_ENTRIES_PER_BLK - cur_dir_entry & (FAT32_ENTRIES_PER_BLK - 1);
			if (avail_count < de_count)
			{
				for (i = 0; i < avail_count; i++)
					*buf[cur_dir_entry + i].name = 0xE5;
				BlkWrite(drive, &buf[cur_dir_entry & -FAT32_ENTRIES_PER_BLK], 
						 drive->data_area + cur_dir_clus * drive->spc + cur_dir_entry >> FAT32_ENTRIES_BITS, 1);
				cur_dir_entry += avail_count;
				if (cur_dir_entry == entries_per_clus)
				{
					last_dir_clus	= cur_dir_clus;
					tmp_buf			= buf;
					buf				= last_buf;
					last_buf		= tmp_buf;
					cur_dir_clus	= ClusAlloc(drive, cur_dir_clus, 1);
					cur_dir_entry	= 0;
					MemSet(buf, 0, BLK_SIZE * drive->spc);
					ClusWrite(drive, buf, cur_dir_clus, 1);
				}
			}
			MemCopy(&buf[cur_dir_entry], &de, de_count * sizeof(CFAT32DirEntry));
			BlkWrite(drive, &buf[cur_dir_entry & -FAT32_ENTRIES_PER_BLK], 
					 drive->data_area + cur_dir_clus * drive->spc + cur_dir_entry >> FAT32_ENTRIES_BITS, 1);
			cur_dir_entry += de_count;
			if (cur_dir_entry == entries_per_clus)
			{
				cur_dir_clus = ClusAlloc(drive, cur_dir_clus, 1);
				MemSet(buf, 0, BLK_SIZE * drive->spc);
				ClusWrite(drive, buf, cur_dir_clus, 1);
			}
			else
			{
				MemSet(&buf[cur_dir_entry], 0, sizeof(CFAT32DirEntry));
				BlkWrite(drive, &buf[cur_dir_entry & -FAT32_ENTRIES_PER_BLK], 
						 drive->data_area + cur_dir_clus * drive->spc + cur_dir_entry >> FAT32_ENTRIES_BITS, 1);
			}
		}
		Free(last_buf);
		Free(buf);
		if (unlock)
			DriveUnlock(drive);
		if (unlock_break)
			BreakUnlock;
	}
	catch
	{
		if (unlock)
			DriveUnlock(drive);
		if (unlock_break)
			BreakUnlock;
	}
	return FALSE;
}

I64 FAT32FilesDel(CDrive *drive, U8 *cur_dir, U8 *files_find_mask, I64 fuf_flags, Bool del_dir, Bool print_message)
{
	CFAT32DirEntry	*buf, *last_buf, *tmp_buf;
	I64				 i, res = 0, attr, xsum = 0, last_dir_clus = INVALID_CLUS, cur_dir_entry, entries_per_clus, cur_dir_clus;
	U8				 ch;
	Bool			 unlock_break;
	CDirEntry		 long_name;

	MemSet(&long_name, 0, sizeof(CDirEntry));
	try
	{
		unlock_break = BreakLock;
		DriveLock(drive);
		cur_dir_clus = Name2DirClus(drive, cur_dir);
		buf = MAlloc(BLK_SIZE * drive->spc);
		last_buf = CAlloc(BLK_SIZE * drive->spc);
		entries_per_clus = drive->spc << FAT32_ENTRIES_BITS;
		ClusRead(drive, buf, cur_dir_clus, 1);
		cur_dir_entry = 0;
		while (ch = *buf[cur_dir_entry].name)
		{
			attr = buf[cur_dir_entry].attr;
			if (ch != 0xE5 && ch != '.')
			{
				if (attr & RS_ATTR_LONG_NAME_MASK == RS_ATTR_LONG_NAME)
					DirLongNameFill(&long_name, &buf[cur_dir_entry], &xsum);
				else
				{
					if (!(attr & RS_ATTR_VOL_ID) && (del_dir || !(attr & RS_ATTR_DIR)))
					{
						if (xsum != FATNameXSum(buf[cur_dir_entry].name))
							MemSet(&long_name, 0, sizeof(CDirEntry));
						if (!*long_name.name)
							FATFromName(long_name.name, buf[cur_dir_entry].name);
						if (FilesFindMatch(long_name.name, files_find_mask, fuf_flags))
						{
							if (!(attr & RS_ATTR_DIR))
								res++;
							if (print_message)
								"Del %s\n", long_name.name;
							*buf[cur_dir_entry].name = 0xE5;
							i = 1;
							while (i <= cur_dir_entry &&
								   buf[cur_dir_entry - i].attr & RS_ATTR_LONG_NAME_MASK == RS_ATTR_LONG_NAME)
								*buf[cur_dir_entry - i++].name = 0xE5;
							i--;
							BlkWrite(drive, &buf[(cur_dir_entry - i) & -FAT32_ENTRIES_PER_BLK], 
									 drive->data_area + cur_dir_clus * drive->spc + (cur_dir_entry - i) >> FAT32_ENTRIES_BITS, 
									 (i + FAT32_ENTRIES_PER_BLK) >> FAT32_ENTRIES_BITS);
							if (i == cur_dir_entry && last_dir_clus != INVALID_CLUS)
							{
								i = 1;
								while (i <= entries_per_clus &&
									   last_buf[entries_per_clus - i].attr & RS_ATTR_LONG_NAME_MASK == RS_ATTR_LONG_NAME)
									*last_buf[entries_per_clus - i++].name = 0xE5;
								if (--i > 0)
									BlkWrite(drive, &buf[(entries_per_clus - i) & -FAT32_ENTRIES_PER_BLK], 
											 drive->data_area +
											 last_dir_clus * drive->spc + (entries_per_clus - i) >> FAT32_ENTRIES_BITS, 
											 (i + FAT32_ENTRIES_PER_BLK - 1) >> FAT32_ENTRIES_BITS);
							}
							FAT32FreeClus(drive, buf[cur_dir_entry].clus_lo + buf[cur_dir_entry].clus_hi << 16);
						}
					}
					MemSet(&long_name, 0, sizeof(CDirEntry));
				}
			}
			else
				MemSet(&long_name, 0, sizeof(CDirEntry));
			if (++cur_dir_entry == entries_per_clus)
			{
				last_dir_clus = cur_dir_clus;
				cur_dir_clus = ClusNumNext(drive, cur_dir_clus, 1);
				tmp_buf	 = buf;
				buf		 = last_buf;
				last_buf = tmp_buf;
				ClusRead(drive, buf, cur_dir_clus, 1);
				cur_dir_entry = 0;
			}
		}
		Free(buf);
		Free(last_buf);
		DriveUnlock(drive);
		if (unlock_break)
			BreakUnlock;
	}
	catch
	{
		DriveUnlock(drive);
		if (unlock_break)
			BreakUnlock;
	}

	return res;
}

I64 FAT32FileWrite(CDrive *drive, U8 *cur_dir, U8 *name, U8 *buf, I64 size, CDate cdt, I64 attr)
{
	CDirEntry	de;
	I64			c = 0, blk_count;
	Bool		contiguous;

	MemSet(&de, 0, sizeof(CDirEntry));
	if (size < 0)
		size = 0;
	if (drive->fs_type != FSt_FAT32)
		PrintErr("Not FAT32 Drive\n");
	else if (!CFileNameTo(de.name, name))
		PrintErr("Invalid FileName: \"%s\".\n", name);
	else
	{
		FAT32FilesDel(drive, cur_dir, de.name, 0, FALSE, FALSE);
		if (attr & RS_ATTR_CONTIGUOUS)
			contiguous = TRUE;
		else
			contiguous = FALSE;
		de.size = size;
		if (blk_count = (size + BLK_SIZE - 1) >> BLK_SIZE_BITS)
			c = ClusAlloc(drive, 0, (blk_count + drive->spc - 1) / drive->spc, contiguous);
		else
			c = 0x0FFFFFFF;
		de.clus		= c;
		de.attr		= attr;
		de.datetime	= cdt;
		if (blk_count)
			ClusBlkWrite(drive, buf, c, blk_count);
		FAT32DirNew(drive, cur_dir, &de, TRUE);
	}
	return c;
}

CDirEntry *FAT32FilesFind(U8 *files_find_mask, I64 fuf_flags, CDirEntry *parent=NULL, I64 *_dir_size=NULL)
{
	CDrive			*drive = Fs->cur_dv;
	CFAT32DirEntry	*buf;
	I64				 attr, xsum = 0, dir_size = 0, sub_dir_size, cur_dir_clus, cur_dir_entry, entries_per_clus;
	U8				 ch;
	CDirEntry		*res = NULL, *tmpde, long_name;

	if (fuf_flags & ~FUG_FILES_FIND)
		throw('FUF');
	try
	{
		MemSet(&long_name, 0, sizeof(CDirEntry));
		DriveLock(drive);
		cur_dir_clus = Name2DirClus(drive, Fs->cur_dir);
		buf = MAlloc(BLK_SIZE * drive->spc);
		entries_per_clus = drive->spc << FAT32_ENTRIES_BITS;
		ClusRead(drive, buf, cur_dir_clus, 1);
		dir_size += drive->spc * BLK_SIZE;
		cur_dir_entry = 0;
		while (ch = *buf[cur_dir_entry].name)
		{
			attr = buf[cur_dir_entry].attr;
			if (ch != 0xE5)
			{
				if (attr & RS_ATTR_LONG_NAME_MASK == RS_ATTR_LONG_NAME)
					DirLongNameFill(&long_name, &buf[cur_dir_entry], &xsum);
				else
				{
					if (!(attr & RS_ATTR_VOL_ID))
					{
						tmpde = MAlloc(sizeof(CDirEntry));
						if (xsum == FATNameXSum(buf[cur_dir_entry].name))
							MemCopy(tmpde, &long_name, sizeof(CDirEntry));
						else
							MemSet(tmpde, 0, sizeof(CDirEntry));
						if (FAT32CDirFill(tmpde, &buf[cur_dir_entry], drive->fat32_local_time_offset))
						{
							tmpde->parent = parent;
							if (Bt(&fuf_flags, FUf_RECURSE) && attr & RS_ATTR_DIR && *tmpde->name != '.')
							{
								tmpde->next = res;
								res = tmpde;
								tmpde->full_name = DirNameAbs(tmpde->name);
								DriveUnlock(drive);
								if (Cd(tmpde->name))
								{
									tmpde->sub=FAT32FilesFind(files_find_mask, fuf_flags, tmpde, &sub_dir_size);
									tmpde->size = sub_dir_size;
									Cd("..");
								}
								DriveLock(drive);
							}
							else
							{
								tmpde->full_name = FileNameAbs(tmpde->name);
								if ((attr & RS_ATTR_DIR ||
									!Bt(&fuf_flags,  FUf_JUST_DIRS)) &&
									!(Bt(&fuf_flags, FUf_RECURSE) && *tmpde->name == '.' && attr & RS_ATTR_DIR) &&
									FilesFindMatch(tmpde->full_name, files_find_mask, fuf_flags))
								{
									tmpde->next = res;
									res = tmpde;
								}
								else
									DirEntryDel(tmpde);
							}
						}
						else
							DirEntryDel(tmpde);
					}
					MemSet(&long_name, 0, sizeof(CDirEntry));
				}
			}
			else
				MemSet(&long_name, 0, sizeof(CDirEntry));
			if (++cur_dir_entry == entries_per_clus)
			{
				cur_dir_clus = ClusNumNext(drive, cur_dir_clus);
				if (cur_dir_clus == INVALID_CLUS)
					break;
				else
				{
					ClusRead(drive, buf, cur_dir_clus, 1);
					dir_size += drive->spc * BLK_SIZE;
					cur_dir_entry = 0;
				}
			}
		}
		Free(buf);
		DriveUnlock(drive);
	}
	catch
		DriveUnlock(drive);
	if (_dir_size)
		*_dir_size = dir_size;

	return res;
}

Bool FAT32MkDir(CDrive *drive, U8 *cur_dir, U8 *name, I64 entry_count)
{
	I64				 c, cur_dir_clus = Name2DirClus(drive, cur_dir), 
					 //Rough estimate of size
					 size = CeilU64((entry_count + 2) << FAT32_ENTRIES_BITS,
					 drive->spc << BLK_SIZE_BITS);
	U8				*buf = CAlloc(size);
	CDirEntry		 d_native;
	CFAT32DirEntry	*dFAT = buf;
	Bool			 unlock_break;

	try
	{
		unlock_break = BreakLock;
		c = FileWrite(name, buf, size, 0, RS_ATTR_DIR);
		MemSet(&d_native, 0, sizeof(CDirEntry));
		d_native.attr		= RS_ATTR_DIR;
		*d_native.name		= '.';
		d_native.name[1]	= 0;
		d_native.clus		= c;
		d_native.size		= 0;
		d_native.datetime	= Now;
		FAT32DirFill(dFAT, &d_native, NULL, drive->fat32_local_time_offset);
		dFAT++;

		MemSet(&d_native, 0, sizeof(CDirEntry));
		d_native.attr		= RS_ATTR_DIR;
		*d_native.name		= '.';
		d_native.name[1]	= '.';
		d_native.name[2]	= 0;
		d_native.clus		= cur_dir_clus;
		d_native.size		= 0;
		d_native.datetime	= Now;
		FAT32DirFill(dFAT, &d_native, NULL, drive->fat32_local_time_offset);
		ClusWrite(drive, buf, c, 1);
		Free(buf);
		if (unlock_break)
			BreakUnlock;
	}
	catch
		if (unlock_break)
			BreakUnlock;

	return TRUE;
}
