#help_index "File/CD DVD"

U0 FillU16Palindrome(CPalindromeU16 *dst, U16 w)
{
    dst->big    = EndianU16(w);
    dst->little = w;
}

U0 FillU32Palindrome(CPalindromeU32 *dst, I64 d)
{
    dst->big    = EndianU32(d);
    dst->little = d;
}

class CElTorito
{
    U16 w[16];
    U8  bootable;   // 88=bootable 00=not bootable
    U8  media_type; // 0=no emulation 4=hard disk
    U16 load_seg;   // 0000->07C0
    U8  sys_type;
    U8  zero;
    U16 sect_count;
    U32 load_rba;   // start address of virtual disk
    U8  zero2[20];
};

U0 RedSeaISO9660Stage1(U8 *iso_filename, U8 *stage2_filename)
{
    CDirEntry    de;
    CFile       *out_file = NULL;
    U8          *stage1_buf = CAlloc(DVD_BOOT_LOADER_SIZE);

    if (FileFind(stage2_filename, &de) && (out_file = FOpen(iso_filename, "wc+")))
    {
        MemCopy(stage1_buf, BDVD_START, BDVD_END - BDVD_START);
        *(BDVD_BLK_LO     - BDVD_START + stage1_buf)(U32 *) = de.clus >> 2;
        *(BDVD_BLK_COUNT  - BDVD_START + stage1_buf)(U16 *) = (de.size + DVD_BLK_SIZE - 1) >> (BLK_SIZE_BITS + 2);
        *(BDVD_SHIFT_BLKS - BDVD_START + stage1_buf)(U16 *) = de.clus & 3;

        if (de.clus & 3)
            *(BDVD_BLK_COUNT - BDVD_START + stage1_buf)(U16 *) += 1;

        FBlkWrite(out_file, stage1_buf, 20 << 2 + 1 << 2, DVD_BOOT_LOADER_SIZE / BLK_SIZE);
        FClose(out_file);
    }
    Free(stage1_buf);
}

U0 RedSeaISO9660(U8 *iso_filename, U8 drv_let)
{
    CDrive      *drive = Letter2Drive(drv_let);
    CISOPriDesc *iso_pri  = CAlloc(DVD_BLK_SIZE), 
                *iso_boot = CAlloc(DVD_BLK_SIZE), 
                *iso_sup  = CAlloc(DVD_BLK_SIZE), 
                *iso_term = CAlloc(DVD_BLK_SIZE);
    I64          iso_size = 0, i, j;
    U32         *d;
    CElTorito   *et = CAlloc(DVD_BLK_SIZE);
    U8          *zero_buf = CAlloc(DVD_BLK_SIZE), *st;
    CFile       *out_file = NULL;

    if (out_file = FOpen(iso_filename, "wc+"))
    {
        iso_size = FSize(out_file) / DVD_BLK_SIZE;

        for (i = 0; i < drive->bd->drv_offset; i += 4)
            FBlkWrite(out_file, zero_buf, i, 4);

        iso_pri->type = ISOT_PRI_VOL_DESC;
        StrCopy(iso_pri->id, "CD001");
        iso_pri->version = 1;
        FillU16Palindrome(&iso_pri->vol_set_size,       1);
        FillU16Palindrome(&iso_pri->vol_seq_num,        1);
        FillU16Palindrome(&iso_pri->log_block_size,     DVD_BLK_SIZE);
        FillU32Palindrome(&iso_pri->vol_space_size,     iso_size);
        FillU32Palindrome(&iso_pri->root_dir_record,    drive->root_clus);
        iso_pri->file_structure_version = 1;
        StrCopy(iso_pri->publisher_id, "ZealOS RedSea");

        st = MStrPrint("ZealOS V%0.2f %D %T", sys_os_version, Now, Now);
        StrCopy(iso_pri->preparer_id, st);
        Free(st);

        MemCopy(iso_sup, iso_pri, DVD_BLK_SIZE);
        iso_sup->type = ISOT_SUPPLEMENTARY_DESC;

        iso_boot->type = ISOT_BOOT_RECORD;
        StrCopy(iso_boot->id, "CD001");
        iso_boot->version = 1;
        StrCopy(iso_boot(U8 *) + 7, "EL TORITO SPECIFICATION");

        FBlkWrite(out_file, iso_pri, 16 << 2, 4);
        iso_term->type = ISOT_TERMINATOR;
        StrCopy(iso_term->id, "CD001");
        iso_term->version = 1;

        d = iso_boot(U8 *) + 0x47;
        *d = 20 << 2 >> 2;
        FBlkWrite(out_file, iso_boot, 17 << 2, 4);

        FBlkWrite(out_file, iso_sup,  18 << 2, 4);
        FBlkWrite(out_file, iso_term, 19 << 2, 4);

        et->w[0] = 1;
        StrCopy(&et->w[2], "ZealOS");
        et->w[15] = 0xAA55;

        j = 0;
        for (i = 0; i < 16; i++) //Checksum
            j += et->w[i];

        et->w[14]       = -j;
        et->bootable    = 0x88;
        et->media_type  = 0;//0=no emu 2=1.44meg 4=hard drive
        et->sect_count  = 4;  //5 seems like the limit,  4 is safer
        et->load_rba    = 20 << 2 >> 2 + 1;
        FBlkWrite(out_file, et, 20 << 2, 4);
        FClose(out_file);
    }
    Free(zero_buf);
    Free(et);
    Free(iso_pri);
    Free(iso_boot);
    Free(iso_sup);
    Free(iso_term);
}

I64 RedSeaISOPass1(CDirEntry *tmpde)
{
    I64 dir_entry_count = 3 + LinkedListCount(tmpde), res = 0;

    while (tmpde)
    {
        if (tmpde->attr & RS_ATTR_DIR)
        {
            if (tmpde->sub)
                res += RedSeaISOPass1(tmpde->sub);
            else
                res += BLK_SIZE; //Empty dir
        }
        else
            res += CeilU64(tmpde->size, BLK_SIZE);

        tmpde = tmpde->next;
    }
    res += CeilU64(dir_entry_count << 6, BLK_SIZE); //Size in bytes

#assert CDIR_SIZE == 64

    return res;
}

public I64 RedSeaISO(U8 *_iso_filename=NULL, U8 *_src_dir, U8 *_stage2_filename=NULL)
{//See ::/Misc/DoDistro.ZC. Must be ISO.C
    I64          i, res, root_count, root_dir_blks, bitmap_blks, bitmap_blks1;
    CDirEntry   *tmpde;
    U8           buf[STR_LEN], *iso_filename, *src_dir, *stage2_filename;
    CDrive      *drive = DriveMakeFreeSlot(DriveNextFreeLet('Q')); //First BDT_ISO_FILE_WRITE
    CBlkDev     *bd = BlkDevNextFreeSlot(drive->drv_let, BDT_ISO_FILE_WRITE);

    if (!IsDir(_src_dir))
        PrintErr("'%s' is not a dir.\n", _src_dir);
    else
    {
        if (!_iso_filename)
            _iso_filename = blkdev.default_iso_c_filename;

        iso_filename = ExtChange(_iso_filename, "ISO.C");
        src_dir = DirNameAbs(_src_dir);

        if (_stage2_filename)
        {
            stage2_filename = FileNameAbs(_stage2_filename);
            *stage2_filename = drive->drv_let;

            i = StrLen(src_dir);
            if (i != 3) //If not root
                i++;        //Skip slash

            StrCopy(stage2_filename + 3, stage2_filename + i);
        }
        else
            stage2_filename = NULL;

        tmpde = FilesFind(src_dir, FUF_RECURSE);
        root_count = LinkedListCount(tmpde) + 3;
        root_dir_blks = CeilU64(root_count << 6, BLK_SIZE) >> BLK_SIZE_BITS;

        if (res = RedSeaISOPass1(tmpde) >> BLK_SIZE_BITS)
        {
            bd->drv_offset = 19 << 2 + (DVD_BLK_SIZE * 2 + DVD_BOOT_LOADER_SIZE) / BLK_SIZE;
            bitmap_blks = 1;
            do
            {
                bitmap_blks1 = bitmap_blks;
                bitmap_blks  = (res + bitmap_blks + BLK_SIZE << 3 - 1) / BLK_SIZE << 3;
            }
            while (bitmap_blks != bitmap_blks1);

            bd->max_blk             = CeilI64(bd->drv_offset + 1 + bitmap_blks + res, 4);
            bd->max_blk--; //Inclusive.
            bd->file_disk_name      = SysStrNew(iso_filename);
            bd->init_root_dir_blks  = root_dir_blks;
            BlkDevAdd(bd,, TRUE, TRUE);
            StrPrint(buf, "%C:/", drive->drv_let);
            CopyTree(src_dir, buf, TRUE);
            RedSeaISO9660Stage1(iso_filename, stage2_filename);
            DriveDel(drive);
            BlkDevDel(bd);
        }
        Free(stage2_filename);
        Free(src_dir);
        Free(iso_filename);
    }
    return res;
}