#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, "ZenithOS 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], "ZenithOS");
		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 $LK,"::/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 $LK,"BDT_ISO_FILE_WRITE",A="MN: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;
}
