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)
{//FUF_JUST_DIRS, 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 ::/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;
}