#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 addr 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);
        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");

                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.CC. 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              = ZStrNew(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;
}