I64 FSize(CFile *f)
{//Report size of opened file in bytes.
        if (f)
                return f->de.size;
        else
                return 0;
}

CFile *FOpen(U8 *filename, U8 *flags, I64 count=0)
{//Allows flags "r","w","w+". "c" for contiguous.
//(It uses StrOcc() for 'w', 'r', '+', 'c')
        CFile           *f = CAlloc(sizeof(CFile));
        CDirContext     *dirc;
        U8                      *full_name;
        Bool             contiguous = StrOcc(flags, 'c');

        f->clus = INVALID_CLUS;
        f->fblk_num = 0;
        if (count > 0)
                f->max_blk = count - 1;
        else
                f->max_blk = I64_MAX;
        f->file_clus_num = INVALID_CLUS;
        full_name = FileNameAbs(filename);
        f->drive = Letter2Drive(*full_name);
        if (f->drive->fs_type == FSt_REDSEA)
                contiguous = TRUE;
        if (contiguous)
        {
                f->flags |= FF_CONTIGUOUS;
                if (f->drive->fs_type != FSt_REDSEA && !(FileAttr(filename) & RS_ATTR_CONTIGUOUS))
                        throw('File');
        }
        f->clus_buf = CAlloc(f->drive->spc << BLK_SIZE_BITS);
        if (StrOcc(flags, 'w'))
        {
                f->flags = f->flags | FF_WRITE | FF_NEEDS_WRITE;
                if (StrOcc(flags, '+'))
                {
                        if (FileFind(full_name, &f->de, FUF_JUST_FILES))
                        {
                                Free(full_name);
                                if (contiguous)
                                        f->max_blk = (FSize(f) + BLK_SIZE - 1) >> BLK_SIZE_BITS - 1;
                                return f;
                        }
                }
                else
                        Del(full_name,,, FALSE);
                f->de.full_name = full_name;
                f->flags |= FF_NEW_FILE;
                if (dirc = DirContextNew(full_name))
                {
                        StrCopy(f->de.name, dirc->mask);
                        if (count > 0)
                        {//We pre-alloc the whole thing.
                                f->de.clus = ClusAlloc(f->drive,0, (count + f->drive->spc - 1) / f->drive->spc, contiguous);
                                f->de.size = count << BLK_SIZE_BITS;
                                DirNew(dirc->drive, Fs->cur_dir, &f->de, TRUE);
                                f->flags &= ~FF_NEW_FILE;
                        }
                        DirContextDel(dirc);
                        return f;
                }
        }
        else
        {
                if (FileFind(full_name, &f->de, FUF_JUST_FILES))
                {
                        Free(full_name);
                        f->max_blk = (FSize(f) + BLK_SIZE - 1) >> BLK_SIZE_BITS - 1;
                        return f;
                }
        }
        Free(f->clus_buf);
        Free(full_name);
        Free(f);

        return NULL;
}

U0 FClose(CFile *f)
{//Close CFile, updating directory.
        CDirContext *dirc;

        if (f)
        {
                if (f->flags & FF_BUF_DIRTY)
                {
                        ClusWrite(f->drive, f->clus_buf, f->clus, 1);
                        f->flags &= ~FF_BUF_DIRTY;
                }
                if (f->flags & FF_NEEDS_WRITE)
                {
                        if (dirc = DirContextNew(f->de.full_name))
                        {
                                if (!(f->flags & FF_USE_OLD_DATETIME))
                                        f->de.datetime = Now;
                                if (f->flags & FF_NEW_FILE)
                                        DirNew(dirc->drive, Fs->cur_dir, &f->de, TRUE);
                                else
                                        DirNew(dirc->drive, Fs->cur_dir, &f->de, FALSE);
                                DirContextDel(dirc);
                        }
                        else
                                throw('File');
                }
                Free(f->clus_buf);
                Free(f->de.full_name);
                Free(f);
        }
}

I64 FSetClus(CFile *f, I64 c, I64 blk, Bool read)
{
        CDrive  *drive = f->drive;
        I64              i;

        if (f->clus != c)
        {
                if (f->flags & FF_BUF_DIRTY)
                {
                        i = drive->spc;
                        if (f->max_blk != I64_MAX)
                        {
                                i = f->max_blk + 1 - f->file_clus_num * drive->spc;
                                if (i > drive->spc)
                                        i = drive->spc;
                        }
                        ClusBlkWrite(drive, f->clus_buf, f->clus, i);
                        f->flags = f->flags & ~FF_BUF_DIRTY;
                }
                f->clus = c;
                f->file_clus_num = blk / drive->spc;
                if (read)
                {
                        i = drive->spc;
                        if (f->max_blk != I64_MAX)
                        {
                                i = f->max_blk + 1 - f->file_clus_num * drive->spc;
                                if (i > drive->spc)
                                        i = drive->spc;
                        }
                        c = ClusBlkRead(drive, f->clus_buf, c, i);
                }
        }

        return c;
}

Bool FBlkRead(CFile *f, U8 *buf, I64 blk=FFB_NEXT_BLK, I64 count=1)
{//Read [nth,n+count) blks of file.
        CDrive  *drive = f->drive;
        I64              spc   = drive->spc, i, j, c = f->de.clus;

        if (!f || !drive)
                return FALSE;
        if (blk == FFB_NEXT_BLK)
                blk = f->fblk_num;
        if (blk + count - 1 > f->max_blk)
                return FALSE;
        if (count <= 0)
                return TRUE;

        if (f->flags & FF_CONTIGUOUS)
        {
                BlkRead(drive, buf, Clus2Blk(drive, c) + blk, count);
                blk += count;
        }
        else
        {
                i = blk / spc;
                if (0 <= f->file_clus_num <= i)
                {
                        c = f->clus;
                        i -= f->file_clus_num;
                }
                if (i > 0)
                        c = ClusNumNext(drive, c, i);

                if (i = blk % spc)
                {
                        c = FSetClus(f, c, blk, TRUE);
                        if (count < spc - i)
                                j = count;
                        else
                                j = spc - i;
                        MemCopy(buf, f->clus_buf + i << BLK_SIZE_BITS, j << BLK_SIZE_BITS);
                        buf += j << BLK_SIZE_BITS;
                        count -= j;
                        blk += j;
                }
                while (count >= spc)
                {
                        c = FSetClus(f, c, blk, TRUE);
                        MemCopy(buf, f->clus_buf, spc << BLK_SIZE_BITS);
                        buf += spc << BLK_SIZE_BITS;
                        count -= spc;
                        blk += spc;
                }
                if (count > 0)
                {
                        c = FSetClus(f, c, blk, TRUE);
                        MemCopy(buf, f->clus_buf, count << BLK_SIZE_BITS);
                        buf += count<<BLK_SIZE_BITS;
                        blk += count;
                }
        }
        f->fblk_num = blk;

        return TRUE;
}

Bool FBlkWrite(CFile *f, U8 *buf, I64 blk=FFB_NEXT_BLK, I64 count=1)
{//Write [nth,n+count) blks of file.
        CDrive  *drive = f->drive;
        I64              spc   = drive->spc, i, j, c = f->de.clus, c1;

        if (!f || !drive)
                return FALSE;
        if (blk == FFB_NEXT_BLK)
                blk = f->fblk_num;
        if (blk + count - 1 > f->max_blk)
                return FALSE;
        if (!(f->flags & FF_WRITE))
                return FALSE;
        if (count <= 0)
                return TRUE;
        if (f->flags & FF_CONTIGUOUS)
        {
                BlkWrite(drive, buf, Clus2Blk(drive, c) + blk, count);
                blk += count;
        }
        else
        {
                if (!c)
                {
                        c = ClusAlloc(drive, 0, 1, FALSE);
                        f->file_clus_num = 0;
                        f->clus = c;
                        f->de.clus = c;
                        f->flags |= FF_NEEDS_WRITE | FF_NEW_FILE;
                }
                i = blk / spc;
                if (0 <= f->file_clus_num <= i)
                {
                        c = f->clus;
                        i -= f->file_clus_num;
                }
                while (i > 0)
                {
                        c1 = c;
                        c = ClusNumNext(drive, c1, 1);
                        if (c == INVALID_CLUS)
                        {
                                c = ClusAlloc(drive, c1, i, FALSE);
                                if (i > 1)
                                        c = ClusNumNext(drive, c, i - 1);
                                break;
                        }
                        else
                                i--;
                }

                if (i = blk % spc)
                {
                        FSetClus(f, c, blk, TRUE);
                        if (count < spc - i)
                                j = count;
                        else
                                j = spc - i;
                        MemCopy(f->clus_buf + BLK_SIZE * i, buf, j << BLK_SIZE_BITS);
                        f->flags |= FF_BUF_DIRTY;
                        buf += j << BLK_SIZE_BITS;
                        count -= j;
                        blk += j;
                        if (count > 0)
                        {
                                c1 = c;
                                c = ClusNumNext(drive, c1, 1);
                                if (c == INVALID_CLUS)
                                        c = ClusAlloc(drive, c1, 1, FALSE);
                        }
                }
                while (count >= spc)
                {
                        FSetClus(f, c, blk, FALSE);
                        MemCopy(f->clus_buf, buf, spc << BLK_SIZE_BITS);
                        f->flags |= FF_BUF_DIRTY;
                        buf += spc << BLK_SIZE_BITS;
                        count -= spc;
                        blk += spc;
                        if (count > 0)
                        {
                                c1 = c;
                                c = ClusNumNext(drive, c1, 1);
                                if (c == INVALID_CLUS)
                                        c = ClusAlloc(drive, c1, 1, FALSE);
                        }
                }
                if (count > 0)
                {
                        FSetClus(f, c, blk, TRUE);
                        MemCopy(f->clus_buf, buf, count << BLK_SIZE_BITS);
                        f->flags |= FF_BUF_DIRTY;
                        buf += count << BLK_SIZE_BITS;
                        blk += count;
                }
                if (f->de.size < blk << BLK_SIZE_BITS)
                        f->de.size = blk << BLK_SIZE_BITS;
        }
        f->fblk_num = blk;

        return TRUE;
}