#help_index "Info;File/Cmd Line (Typically);Cmd Line (Typically)"
Bool CheckDiskConfirm(Bool *_fix, Bool *_confirm)
{
	if (*_fix && *_confirm)
	{
		"Fix ";
		if (!YorN)
			*_fix = FALSE;
		*_confirm = FALSE;
	}
	return *_fix;
}

I64 RedSeaCheckDiskList(CDrive *drive, CDirEntry *tmpde1, U8 *bits, U8 *bits2, I64 size, I64 bpc)
{
	CDirEntry	*tmpde2;
	I64			 i, j, errs = 0;

	while (tmpde1)
	{
		tmpde2 = tmpde1->next;
		if (tmpde1->attr & RS_ATTR_DIR && tmpde1->sub)
			errs += RedSeaCheckDiskList(drive, tmpde1->sub, bits, bits2, size, bpc);
		j = (tmpde1->size + bpc - 1) / bpc;
		for (i = 0; i < j; i++)
		{
			if (i + tmpde1->clus-drive->data_area > size)
			{
				PrintErr("Invalid Clus:%s Clus:%X\n", tmpde1->full_name, i + tmpde1->clus);
				errs++;
				break;
			}
			if (LBts(bits, i + tmpde1->clus-drive->data_area))
			{
				PrintErr("Dbl Alloc:%s Clus:%X\n", tmpde1->full_name, i + tmpde1->clus);
				errs++;
			}
			if (!LBtr(bits2, i + tmpde1->clus-drive->data_area))
			{
				PrintErr("UnAlloc:%s Clus:%X\n", tmpde1->full_name, i + tmpde1->clus);
				errs++;
			}
		}
		DirEntryDel(tmpde1);
		tmpde1 = tmpde2;
	}

	return errs;
}

I64 RedSeaCheckDisk(U8 drv_let, Bool *_fix, Bool *_confirm)
{
	I64			 i, j, bpc, size, errs = 0;
	CDrive		*drive = Letter2Drive(drv_let), *old_dv = Fs->cur_dv;
	U8			*files_find_mask = MStrPrint("%c:/*", Drive2Letter(drive)),
				*old_dir = StrNew(Fs->cur_dir),
				*bits, *bits2;
	CDirEntry	*ptr,*ptr2;

	Drive(drv_let);
	"Scanning...\n";
	size = (drive->size - (drive->data_area - drive->drv_offset)) / drive->spc;
	bpc = drive->spc << BLK_SIZE_BITS;
	bits = CAlloc((size + 7) >> 3);
	bits2 = CAlloc((size + 7) >> 3 + BLK_SIZE);
	BlkRead(drive, bits2, drive->fat1, ((size + 7) >> 3 + BLK_SIZE - 1) >> BLK_SIZE_BITS);

	//Get Root Dir size
	ptr2 = MAlloc(bpc);
	BlkRead(drive, ptr2, drive->root_clus, 1);
	ptr = ptr2(U8 *) - offset(CDirEntry.start);
	j = (ptr->size + bpc - 1) / bpc;
	Free(ptr2);

	for (i = 0; i < j; i++)
	{
		if (i + drive->root_clus - drive->data_area > size)
		{
			PrintErr("Invalid Clus: RootDir Clus:%X\n", i + drive->root_clus);
			errs++;
			break;
		}
		if (LBts(bits, i + drive->root_clus - drive->data_area))
		{
			PrintErr("Dbl Alloc: RootDir Clus:%X\n", i + drive->root_clus);
			errs++;
		}
		if (!LBtr(bits2, i + drive->root_clus - drive->data_area))
		{
			PrintErr("UnAlloc: RootDir Clus:%X\n", i + drive->root_clus);
			errs++;
		}
	}

	errs += RedSeaCheckDiskList(drive, FilesFind(files_find_mask, FUF_RECURSE), bits, bits2, size, bpc);
	for (i = 1; i < size; i++)
		if (Bt(bits2, i))
		{
			PrintWarn("Shouldn't Alloc Clus:%0X\n", i + drive->data_area);
			errs++;
			if (CheckDiskConfirm(_fix, _confirm))
				RedSeaFreeClus(drive, i + drive->data_area, 1);
		}

	Free(files_find_mask);
	Free(bits);
	Free(bits2);
	Drive(Drive2Letter(old_dv));
	Cd(old_dir);
	Free(old_dir);

	return errs;
}

I64 FAT32CheckDiskList(CDrive *drive, CDirEntry *tmpde1, U8 *bits, U32 *bits2, I64 size, I64 bpc)
{
	CDirEntry	*tmpde2;
	I64			 i, c, errs = 0;

	while (tmpde1)
	{
		tmpde2 = tmpde1->next;
		if (tmpde1->attr & RS_ATTR_DIR && tmpde1->sub)
			errs += FAT32CheckDiskList(drive, tmpde1->sub, bits, bits2, size, bpc);
		i = 0;
		c = tmpde1->clus;
		while (0 < c < 0x0FFFFFF8)
		{
			if (c > size)
			{
				PrintErr("Invalid Clus:%s Clus:%X\n", tmpde1->full_name, c);
				errs++;
				break;
			}
			if (LBts(bits, c))
			{
				PrintErr("Dbl Alloc:%s Clus:%X\n", tmpde1->full_name, c);
				errs++;
			}
			if (!bits2[c])
			{
				PrintErr("UnAlloc:%s Clus:%X\n", tmpde1->full_name, c);
				errs++;
			}
			else
				bits2[c] = 0;
			c = ClusNumNext(drive, c);
			i++;
		}
		if (!(tmpde1->attr & RS_ATTR_DIR))
		{
			i *= bpc;
			if (tmpde1->size>i)
			{
				PrintErr("Alloced File Too Short:%s\n", tmpde1->full_name);
				errs++;
			}
			if (i > tmpde1->size + bpc - 1)
			{
				PrintWarn("Alloced File Too Long:%s\n", tmpde1->full_name);
				errs++;
			}
		}
		DirEntryDel(tmpde1);
		tmpde1 = tmpde2;
	}
	return errs;
}

I64 FAT32CheckDisk(U8 drv_let, Bool *_fix, Bool *_confirm)
{
	I64		 i, bpc, size, c, errs = 0;
	CDrive	*drive = Letter2Drive(drv_let), *old_dv = Fs->cur_dv;
	U8		*files_find_mask = MStrPrint("%c:/*", Drive2Letter(drive)),
			*old_dir = StrNew(Fs->cur_dir),
			*bits;
	U32		*bits2;

	Drive(drv_let);
	"Scanning...\n";
	size = (drive->size - (drive->data_area - drive->drv_offset)) / drive->spc;
	bpc = drive->spc << BLK_SIZE_BITS;
	bits = CAlloc((size + 7) >> 3);
	bits2 = CAlloc(size * 4 + BLK_SIZE);
	BlkRead(drive, bits2, drive->fat1, (size * 4 + BLK_SIZE - 1) >> BLK_SIZE_BITS);

	c = drive->root_clus;
	while (0 < c < 0x0FFFFFF8)
	{
		if (c > size)
		{
			PrintErr("Invalid Clus: RootDir Clus:%X\n", c);
			errs++;
			break;
		}
		if (LBts(bits, c))
		{
			PrintErr("Dbl Alloc: RootDir Clus:%X\n", c);
			errs++;
		}
		if (!bits2[c])
		{
			PrintErr("UnAlloc: RootDir Clus:%X\n", c);
			errs++;
		}
		else
			bits2[c] = 0;
		c = ClusNumNext(drive, c);
	}

	errs += FAT32CheckDiskList(drive, FilesFind(files_find_mask, FUF_RECURSE), bits, bits2, size, bpc);

	bits2[1] = 0; //See $LK,"FAT32Format",A="MN:FAT32Format"$()
	for (i = 1; i < size; i++)
		if (bits2[i])
		{
			PrintWarn("Shouldn't Alloc Clus:%0X\n", i);
			errs++;
			if (CheckDiskConfirm(_fix, _confirm))
				FAT32FreeClus(drive, i);
		}
	Free(files_find_mask);
	Free(bits);
	Free(bits2);
	Drive(Drive2Letter(old_dv));
	Cd(old_dir);
	Free(old_dir);

	return errs;
}

public I64 DiskCheck(U8 drv_let=0, Bool fix=FALSE, Bool confirm=TRUE)
{//Check disk for allocation errors and, optionally, fix.
//You probably want to reformat and reinstall.
	I64		 errs = 0;
	CDrive	*drive = Letter2Drive(drv_let);

	switch (drive->fs_type)
	{
		case FSt_REDSEA:
			errs = RedSeaCheckDisk(drv_let, &fix, &confirm);
			break;

		case FSt_FAT32:
			errs = FAT32CheckDisk(drv_let, &fix, &confirm);
			break;

		default:
			PrintErr("File System Not Supported\n");
	}
	if (errs)
	{
		if (fix)
			"It might be a little better.  ";
		"Copy files to another partition or CD/DVD, "
			"reformat, and copy back.  "
			"Or, copy from a back-up.\n";
	}

	return errs;
}

U0 RedSeaDriveView(U8 drv_let=0)
{
	CDrive	*drive = Letter2Drive(drv_let);
	I64		 lohi, c1, i, x, y,
			 l = (GR_HEIGHT - 3 * FONT_HEIGHT) * (GR_WIDTH - FONT_WIDTH << 1),
			 s = drive->size + drive->drv_offset - drive->data_area;
	U8		*bitmap;
	CDC 	*dc = DCAlias;

	SettingsPush; //See $LK,"SettingsPush",A="MN:SettingsPush"$
	WinMax;
	WinBorder(ON);
	DocCursor;
	DocClear;
	DCFill;
	try
	{
		i = ((s + 7) >> 3 + BLK_SIZE - 1) >> BLK_SIZE_BITS;
		bitmap = MAlloc(i << BLK_SIZE_BITS);
		BlkRead(drive, bitmap, drive->fat1, i);
		i = 0;
		for (y = 0; y < GR_HEIGHT - 3 * FONT_HEIGHT; y++)
		{
			if (KeyScan)
				break;
			for (x = 0; x < GR_WIDTH - FONT_WIDTH << 1; x++)
			{
				lohi = i * s;
				c1 = lohi / l;
				if (Bt(bitmap, c1))
					dc->color = ROP_XOR + BLUE ^ TRANSPARENT;
				else
					dc->color = ROP_XOR + WHITE ^ TRANSPARENT;
				GrPlot(dc, x, y);
				i++;
			}
		}
		Free(bitmap);
	}
	catch
		DriveUnlock(drive);
	CharGet;

	SettingsPop;
	DCFill;
	DCDel(dc);
}
U0 FAT32DriveView(U8 drv_let=0)
{
	CDrive	*drive = Letter2Drive(drv_let);
	I64		 lohi, c1, i, x, y,
			 l = (GR_HEIGHT - 3 * FONT_HEIGHT) * (GR_WIDTH - FONT_WIDTH << 1),
			 s = (drive->size + drive->spc - 1) / drive->spc - (2 + drive->data_area - drive->drv_offset);
	U32		*bitmap;
	CDC		*dc = DCAlias;

	SettingsPush; //See $LK,"SettingsPush",A="MN:SettingsPush"$
	WinMax;
	WinBorder(ON);
	DocCursor;
	DocClear;
	DCFill;
	try
	{
		i = (s * 4 + BLK_SIZE - 1) >> BLK_SIZE_BITS;
		bitmap = MAlloc(i << BLK_SIZE_BITS);
		BlkRead(drive, bitmap, drive->fat1, i);
		i = 0;
		for (y = 0; y < GR_HEIGHT - 3 * FONT_HEIGHT; y++)
		{
			if (KeyScan)
				break;
			for (x = 0; x < GR_WIDTH - FONT_WIDTH << 1; x++)
			{
				lohi = i * s;
				c1 = lohi / l;
				if (bitmap[c1])
					dc->color = ROP_XOR + BLUE ^ TRANSPARENT;
				else
					dc->color = ROP_XOR + WHITE ^ TRANSPARENT;
				GrPlot(dc, x, y);
				i++;
			}
		}
		Free(bitmap);
	}
	catch
		DriveUnlock(drive);
	CharGet;

	SettingsPop;
	DCFill;
	DCDel(dc);
}

public U0 DriveView(U8 drv_let=0)
{//Drive view. Graph the allocation map's fragmentation.
	CDrive *drive = Letter2Drive(drv_let), *old_dv = Fs->cur_dv;

	Drive(drv_let);
	switch (drive->fs_type)
	{
		case FSt_REDSEA:
			RedSeaDriveView(drv_let);
			break;

		case FSt_FAT32:
			FAT32DriveView(drv_let);
			break;

		default:
			PrintErr("File System Not Supported\n");
	}
	Drive(Drive2Letter(old_dv));
}

public U0 DiskView(U8 drv_let=0)
{//Disk view. Pie chart of partition sizes.
	I64		 i, j, attr,
			 h = Fs->pix_width,
			 v = Fs->pix_height,
			 radius;
	CDrive	*drive;
	CBlkDev	*bd = Letter2BlkDev(drv_let);
	CDC		*dc = DCAlias;
	F64		 sect_start, sect_end;

	SettingsPush; //See $LK,"SettingsPush",A="MN:SettingsPush"$
	DocCursor;
	DocClear;
	DCFill;
	if (h < v)
		radius = 0.4 * h;
	else
		radius = 0.4 * v;
	dc->color = BLACK;
	GrCircle(dc, h >> 1, v >> 1, radius);

	j = 1;
	for (i = 0; i < DRIVES_NUM; i++)
	{
		drive = &blkdev.drvs[i];
		if (bd == drive->bd && drive->fs_type)
		{
			sect_start	= -(drive->drv_offset * 2 * ã / (bd->max_blk + 1));
			sect_end	= -((drive->drv_offset + drive->size) * 2 * ã / (bd->max_blk + 1));
			dc->color = BLACK;
			GrLine(dc, h >> 1, v >> 1,
					h >> 1 + radius * Cos(sect_start),
					v >> 1 + radius * Sin(sect_start));

			GrLine(dc, h >> 1, v >> 1,
					h >> 1 + radius * Cos(sect_end),
					v >> 1 + radius * Sin(sect_end));

			attr = DriveTextAttrGet(Drive2Letter(drive));
			dc->color = attr & 15;
			GrPrint(dc, 0, v - FONT_HEIGHT * j, "%C %-8Z", Drive2Letter(drive), drive->fs_type, "ST_DRIVE_TYPES");
			dc->color.c1 = attr >> 4;
			dc->color |= ROPF_DITHER;
			GrFloodFill(dc,
						h >> 1 + (radius - 4) * Cos((sect_start + sect_end) / 2),
						v >> 1 + (radius - 4) * Sin((sect_start + sect_end) / 2), FALSE);
			j++;
		}
	}

	CharGet(,FALSE);
	SettingsPop;
	DCFill;
	DCDel(dc);
}

I64 RedSeaUnusedDriveSpace(U8 drv_let=0)
{
	CDrive	*drive = Letter2Drive(drv_let);
	I64		 res = 0, i, l;
	U8		*bitmap;

	try
	{
		l = drive->size + drive->drv_offset - drive->data_area;
		i = ((l + 7) >> 3 + BLK_SIZE - 1) >> BLK_SIZE_BITS;
		bitmap = MAlloc(i << BLK_SIZE_BITS);
		BlkRead(drive, bitmap, drive->fat1, i);
		for (i = 0; i < l; i++)
			if (!Bt(bitmap, i))
				res++;
		Free(bitmap);
	}
	catch
		DriveUnlock(drive);

	return res * BLK_SIZE * drive->spc;
}
I64 FAT32UnusedDriveSpace(U8 drv_let=0)
{
	CDrive	*drive = Letter2Drive(drv_let);
	I64		 res = 0, i, l;
	U32		*bitmap;

	try
	{
		l = (drive->size + drive->spc - 1) / drive->spc - (2 + drive->data_area - drive->drv_offset);
		i = (l * 4 + BLK_SIZE - 1) >> BLK_SIZE_BITS;
		bitmap = MAlloc(i << BLK_SIZE_BITS);
		BlkRead(drive, bitmap, drive->fat1, i);
		for (i = 0; i < l; i++)
			if (!bitmap[i])
				res++;
		Free(bitmap);
	}
	catch
		DriveUnlock(drive);

	return res * BLK_SIZE * drive->spc;
}
public I64 DriveUnused(U8 drv_let=0)
{//Returns unused size in bytes.
	CDrive	*drive = Letter2Drive(drv_let), *old_dv = Fs->cur_dv;
	U8		*old_dir = StrNew(Fs->cur_dir);
	I64		 res = 0;

	Drive(drv_let);
	switch (drive->fs_type)
	{
		case FSt_REDSEA:
			res = RedSeaUnusedDriveSpace(drv_let);
			break;

		case FSt_FAT32:
			res = FAT32UnusedDriveSpace(drv_let);
			break;

		default:
			PrintErr("File System Not Supported\n");
	}
	Drive(Drive2Letter(old_dv));
	Cd(old_dir);
	Free(old_dir);

	return res;
}
