I64 ClusNumNext(CDrive *drive, I64 c, I64 count=1)
{//Return next count'th cluster in chain.
    Bool unlock;

    DriveCheck(drive);
    if (count <= 0) return c;
    try
    {
        unlock = DriveLock(drive);
        switch (drive->fs_type)
        {
            case FSt_ISO9660:
            case FSt_REDSEA:
                c += count;
                break;

            case FSt_FAT32:
                while (count-- > 0 && 0 < c < 0x0FFFFFF8)
                {
                    DriveFATBlkSet(drive, c);
                    c = drive->cur_fat_blk[c & (BLK_SIZE / 4 - 1)];
                }
                if (!(0 < c < 0x0FFFFFF8))
                    c = INVALID_CLUS;
                break;

            default:
                throw('Drive');
        }
        if (unlock)
            DriveUnlock(drive);
    }
    catch
        if (unlock)
            DriveUnlock(drive);

    return c;
}

I64 Clus2Blk(CDrive *drive, I64 c)
{//Drive cluster num to block num.
    DriveCheck(drive);
    switch (drive->fs_type)
    {
        case FSt_REDSEA:
            return c;

        case FSt_ISO9660:
        case FSt_FAT32:
            return drive->data_area + c * drive->spc;

        default:
            throw('Drive');
    }
}

I64 ClusBlkRead(CDrive *drive, U8 *buf, I64 c, I64 blks)
{//Accepts block count, so padding on last cluster is not read.
    I64  i;
    Bool unlock;

    DriveCheck(drive);
    if (blks <= 0) return c;
    try
    {
        unlock = DriveLock(drive);
        switch (drive->fs_type)
        {
            case FSt_REDSEA:
                BlkRead(drive, buf, c, blks);
                c += blks;
                break;

            case FSt_ISO9660:
                BlkRead(drive, buf, drive->data_area + c * drive->spc, blks);
                c += (blks + drive->spc - 1) / drive->spc;
                break;

            case FSt_FAT32:
                while (blks && 0 < c < 0x0FFFFFF8)
                {
                    i = blks;
                    if (i > drive->spc)
                        i = drive->spc;
                    BlkRead(drive, buf, drive->data_area + c * drive->spc, i);
                    buf += i << BLK_SIZE_BITS;
                    c = ClusNumNext(drive, c, 1);
                    blks -= i;
                }
                if (blks)
                    throw('Drive');
                break;

            default:
                throw('Drive');
        }
        if (unlock)
            DriveUnlock(drive);
    }
    catch
        if (unlock)
            DriveUnlock(drive);

    return c;
}

I64 ClusRead(CDrive *drive, U8 *buf, I64 c, I64 count)
{//Read cluster count from drive to buf.
    return ClusBlkRead(drive, buf, c, count * drive->spc);
}

I64 ClusBlkWrite(CDrive *drive, U8 *buf, I64 c, I64 blks)
{//Accepts block count, so padding on last cluster is not written.
    I64  i;
    Bool unlock;

    DriveCheck(drive);
    if (blks <= 0) return c;
    try
    {
        unlock = DriveLock(drive);
        switch (drive->fs_type)
        {
            case FSt_REDSEA:
                BlkWrite(drive, buf, c, blks);
                c = 0;
                break;

            case FSt_FAT32:
                while (blks)
                {
                    if (!(0 < c < 0x0FFFFFF8))
                        throw('Drive');
                    i = blks;
                    if (i > drive->spc)
                        i = drive->spc;
                    BlkWrite(drive, buf, drive->data_area + c * drive->spc, i);
                    buf += i << BLK_SIZE_BITS;
                    c = ClusNumNext(drive, c);
                    blks -= i;
                }
                break;

            case FSt_ISO9660:
                PrintErr("File System Not Writable\n");
                throw('Drive');

            default:
                throw('Drive');
        }
        if (unlock)
            DriveUnlock(drive);
    }
    catch
        if (unlock)
            DriveUnlock(drive);

    return c;
}

I64 ClusWrite(CDrive *drive, U8 *buf, I64 c, I64 count)
{//Write cluster count from buf to drive.
    return ClusBlkWrite(drive, buf, c, count * drive->spc);
}

I64 ClusAlloc(CDrive *drive, I64 c=0, I64 count=1, Bool contiguous=FALSE)
{//Alloc cluster count into chain.
//c=0 means first cluster in chain
    DriveCheck(drive);
    if (count <= 0) return c;
    switch (drive->fs_type)
    {
        case FSt_REDSEA:
            return RedSeaAllocClus(drive, count);

        case FSt_FAT32:
            if (contiguous)
            {
                if (c) throw('File');
                return FAT32AllocContiguousClus(drive, count);
            }
            else
                return FAT32AllocClus(drive, c, count);


        case FSt_ISO9660:
            PrintErr("File System Not Writable\n");
            throw('Drive');

        default:
            throw('Drive');
    }
}