Bool ISOInit(CDrive *drive, I64 blk)
{
        CBlkDev                 *bd  = drive->bd;
        I64                              spc = bd->blk_size >> BLK_SIZE_BITS, i = blk / spc, drv_offset = 0;
        CISOPriDesc             *iso = MAlloc(bd->blk_size);
        CISODirEntry    *de;
        Bool                     unlock, res = FALSE;
        U8                               buf[8];

        try
        {
                unlock = DriveLock(drive);
                drive->fs_type = FSt_ISO9660;
                drive->spc = spc;
                drive->data_area = drive->root_clus = drive->drv_offset = bd->drv_offset = drive->size = 0;
                while (TRUE)
                {
                        drive->size = MaxI64(drive->size, (i + 1) * spc);
//                      "BlkRead(drive, iso, %d, %d);", i * spc, spc;
                        BlkRead(drive, iso, i * spc, spc);
//                      D(iso);
//                      Sleep(3000);
                        buf[0](U32) = iso->id[0](U32);
                        buf[4](U16) = iso->id[4](U8);
                        switch (ListMatch(buf, "CD001\0CDW02\0BEA01\0BOOT2\0NSR02\0NSR03\0TEA01\0", LMF_EXACT))
                        {
                                case 0:
                                        switch (iso->type)
                                        {
                                                case ISOT_BOOT_RECORD:
                                                        drv_offset += (2 * DVD_BLK_SIZE + DVD_BLK_SIZE) / BLK_SIZE;
                                                        break;

                                                case ISOT_SUPPLEMENTARY_DESC:
                                                        de = &iso->root_dir_record;
                                                        drive->size = iso->vol_space_size.little * bd->blk_size >> BLK_SIZE_BITS;
                                                        if (!StrCompare(iso->publisher_id, "ZealOS RedSea")             ||
                                                                !StrCompare(iso->publisher_id, "ZenithOS RedSea")       ||
                                                                !StrCompare(iso->publisher_id, "TempleOS RedSea"))
                                                        {
                                                                drive->fs_type = FSt_REDSEA;
                                                                bd->drv_offset = drive->drv_offset = 19 << 2 + drv_offset;
                                                                bd->max_blk = drive->size - 1;
                                                                drive->size -= bd->drv_offset;
                                                                RedSeaInit(drive);
                                                        }
                                                        else
                                                                drive->root_clus = de->loc.little;
                                                        res = TRUE;
                                                        goto di_done;

                                                case ISOT_TERMINATOR:
                                                        throw('Drive');
                                        }
                                        break;

                                default: //Its normal for ISO3346 to read NULL blk as terminator
                                        PrintErr("File System Not Supported\n");
                                        throw('Drive');
                        }
                        i++;
                }
di_done:
                Free(iso);
                if (unlock)
                        DriveUnlock(drive);
        }
        catch
        {
                drive->fs_type = FSt_ISO9660;
                drive->spc = spc;
                drive->drv_offset = bd->drv_offset = drive->data_area = drive->root_clus = 0;
                Free(iso);
                if (unlock)
                        DriveUnlock(drive);
        }

        return res;
}

U0 DVDImageRead(U8 dvd_drive_let, U8 *out_name)
{//Read entire CD/DVD image into ISO file.
        CDrive  *drive  = Letter2Drive(dvd_drive_let);
        CBlkDev *bd             = drive->bd;
        U8              *buf    = MAlloc(COPY_BUF_BLKS << BLK_SIZE_BITS), *out_name2 = ExtDefault(out_name, "ISO");
        CFile   *f              = FOpen(out_name2, "w");
        I64              n, spc = bd->blk_size >> BLK_SIZE_BITS, blk = 0, count, retry;

        BlkDevInit(bd);
        if (bd->type != BDT_ATAPI)
                throw('BlkDev');
        if (!out_name)
                out_name = blkdev.default_iso_filename;
        count = CeilU64(drive->size, spc);
        while (count > 0)
        {
                if (count > COPY_BUF_BLKS)
                        n = COPY_BUF_BLKS;
                else
                        n = count;
                if (n > bd->max_reads)
                        n = bd->max_reads;

                retry = 4;
                while (--retry)
                        if (AHCIAtapiBlksRead(bd, buf, blk / spc, n / spc))
//n is 0x800 if max_reads.      Up to 8 additional seconds
                                break;

                if (!retry)
                        AHCIAtapiBlksRead(bd, buf, blk / spc, n / spc);

                FBlkWrite(f, buf, blk, n);
                count -= n;
                blk += n;
        }
        FClose(f);
        Free(buf);
        Free(out_name2);
}

class CDualBuf
{
        U8              *buf0, *buf1;
        I64              in_buf, out_buf, count;
        U8              *filename;
        CBlkDev *dvd_bd;
};

U0 DVDImageWriteTask(CDualBuf *d)
{
        U8              *buf;
        I64              n, blk = 0, count = d->count;
        CFile   *f;

        if (FileAttr(d->filename) & RS_ATTR_CONTIGUOUS)
                f = FOpen(d->filename, "rc");
        else
                f = FOpen(d->filename, "r");
        while (count > 0)
        {
                if (count > COPY_BUF_BLKS)
                        n = COPY_BUF_BLKS;
                else
                        n = count;
                if (n > d->dvd_bd->max_writes)
                        n = d->dvd_bd->max_writes;
                if (d->in_buf & 1)
                        buf = d->buf1;
                else
                        buf = d->buf0;
                while (d->in_buf>d->out_buf + 1)
                        Yield;
                FBlkRead(f, buf, blk, n);
                d->in_buf++;
                count -= n;
                blk += n;
        }
        FClose(f);
}

U0 DVDImageWrite(U8 dvd_drive_let, U8 *in_name=NULL, I64 media_type=MT_DVD)
{//Write CD/DVD ISO file to disk.
        CDualBuf        *d = CAlloc(sizeof(CDualBuf));
        U8                      *buf, *in_name2, *in_name3;
        I64                      i, n, spc, blk = 0, count;
        CDrive          *drive = Letter2Drive(dvd_drive_let);
        CBlkDev         *bd = drive->bd, *bd2;
        CTask           *task;
        CFile           *f;

        if (!in_name)
                in_name = blkdev.default_iso_filename;
        in_name3 = ExtDefault(in_name, "ISO");
        in_name2 = FileNameAbs(in_name3);
        f=FOpen(in_name2, "r");
        if (!f)
        {
                Free(d);
                return;
        }
        count = (FSize(f) + BLK_SIZE - 1) >> BLK_SIZE_BITS;
        FClose(f);
        if (bd->type != BDT_ATAPI)
                throw('BlkDev');
        bd2 = Letter2BlkDev(*in_name2);
        while (bd2->lock_fwding)
                bd2 = bd2->lock_fwding;  //If two blkdevs on same controller, use one lock
        if ((bd2->type == BDT_ATA || bd2->type == BDT_ATAPI) && bd2->base0 == bd->base0)
        {
                PrintErr("Can't burn CD/DVD on same ATA controller as file.\n\n");
                throw('BlkDev');
        }

        bd->flags |= BDF_READ_ONLY_OVERRIDE;
        BlkDevInit(bd);
        spc = bd->blk_size >> BLK_SIZE_BITS;
        if (drive->size < count)
                drive->size = count;

        d->filename = in_name2;
        d->dvd_bd = bd;
        d->buf0 = MAlloc(COPY_BUF_BLKS << BLK_SIZE_BITS);
        d->buf1 = MAlloc(COPY_BUF_BLKS << BLK_SIZE_BITS);
        d->count = count;

        task = Spawn(&DVDImageWriteTask, d, "Write CD/DVD");
        while (d->in_buf <= d->out_buf)
                Yield;

        BlkDevLock(bd);
//      IDEATAPIWaitReady(bd, 0);

        progress1 = 0;
        progress1_max = count;
        StrCopy(progress1_desc, "Writing");
        while (count > 0)
        {
                if (count > COPY_BUF_BLKS)
                        n = COPY_BUF_BLKS;
                else
                        n = count;
                if (n > bd->max_writes)
                        n = bd->max_writes;
                if (d->out_buf & 1)
                        buf = d->buf1;
                else
                        buf = d->buf0;
                while (d->in_buf <= d->out_buf)
                        Yield;
                IDEATAPIWriteBlks(bd, buf, blk / spc, (n + spc - 1) / spc);
                d->out_buf++;
                count -= n;
                blk += n;
                progress1 += n;
        }
        IDEATAPISync(bd);

        progress1 = 0;
        progress1_max = 2;
        StrCopy(progress1_desc, "Closing");
        for (i = 0; i < 2; i++)
        {
                IDEATAPIClose(bd, 0x100, i); //Close tracks
                progress1++;
        }

        IDEATAPISync(bd);

        IDEATAPIClose(bd, 0x200); //close disk
        IDEATAPISync(bd);
        if (media_type == MT_DVD)
        {
                IDEATAPIClose(bd, 0x300);
                IDEATAPISync(bd);
        }

        *progress1_desc = 0;
        progress1 = progress1_max = 0;

        bd->flags &= ~BDF_READ_ONLY_OVERRIDE;
        BlkDevUnlock(bd);
        Free(d->buf0);
        Free(d->buf1);
        Free(in_name2);
        Free(in_name3);
        Free(d);
}