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                      = SysMAlloc(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;
}