/*

Directory Opus 4
Original GPL release version 4.12
Copyright 1993-2000 Jonathan Potter

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

All users of Directory Opus 4 (including versions distributed
under the GPL) are entitled to upgrade to the latest version of
Directory Opus version 5 at a reduced price. Please see
http://www.gpsoft.com.au for more information.

The release of Directory Opus 4 under the GPL in NO WAY affects
the existing commercial status of Directory Opus 5.

*/

#include "diskop.h"

#define FORMATFLAG_FFS             1
#define FORMATFLAG_INTERNATIONAL   2
#define FORMATFLAG_CACHING         4
#define FORMATFLAG_TRASHCAN        8
#define FORMATFLAG_VERIFY         16

enum {
	FORMAT_NAME,
	FORMAT_FFS,
	FORMAT_INTERNATIONAL,
	FORMAT_CACHING,
	FORMAT_TRASHCAN,
	FORMAT_VERIFY,
	FORMAT_FORMAT,
	FORMAT_QUICKFORMAT,
	FORMAT_CANCEL};

struct TagItem
	format_name_gadget[]={
		{RO_Type,OBJECT_GADGET},
		{RO_GadgetType,GADGET_STRING},
		{RO_GadgetID,FORMAT_NAME},
		{RO_Left,19},
		{RO_Width,21},
		{RO_Height,1},
		{RO_TextNum,STR_FORMAT_NAME},
		{RO_TextPos,TEXTPOS_LEFT},
		{RO_StringLen,31},
		{TAG_END,0}},
	format_ffs_gadget[]={
		{RO_Type,OBJECT_GADGET},
		{RO_GadgetType,GADGET_CHECK},
		{RO_GadgetID,FORMAT_FFS},
		{RO_Left,14},
		{RO_Top,2},
		{RO_TextNum,STR_FORMAT_FFS},
		{RO_TextPos,TEXTPOS_RIGHT},
		{TAG_END,0}},
	format_international_gadget[]={
		{RO_Type,OBJECT_GADGET},
		{RO_GadgetType,GADGET_CHECK},
		{RO_GadgetID,FORMAT_INTERNATIONAL},
		{RO_Left,14},
		{RO_Top,3},
		{RO_TopFine,5},
		{RO_TextNum,STR_FORMAT_INTERNATIONAL},
		{RO_TextPos,TEXTPOS_RIGHT},
		{TAG_END,0}},
	format_caching_gadget[]={
		{RO_Type,OBJECT_GADGET},
		{RO_GadgetType,GADGET_CHECK},
		{RO_GadgetID,FORMAT_CACHING},
		{RO_Left,14},
		{RO_Top,4},
		{RO_TopFine,10},
		{RO_TextNum,STR_FORMAT_CACHING},
		{RO_TextPos,TEXTPOS_RIGHT},
		{TAG_END,0}},
	format_trashcan_gadget[]={
		{RO_Type,OBJECT_GADGET},
		{RO_GadgetType,GADGET_CHECK},
		{RO_GadgetID,FORMAT_TRASHCAN},
		{RO_Left,14},
		{RO_Top,5},
		{RO_TopFine,15},
		{RO_TextNum,STR_FORMAT_TRASHCAN},
		{RO_TextPos,TEXTPOS_RIGHT},
		{TAG_END,0}},
	format_verify_gadget[]={
		{RO_Type,OBJECT_GADGET},
		{RO_GadgetType,GADGET_CHECK},
		{RO_GadgetID,FORMAT_VERIFY},
		{RO_Left,14},
		{RO_Top,6},
		{RO_TopFine,20},
		{RO_TextNum,STR_FORMAT_VERIFY},
		{RO_TextPos,TEXTPOS_RIGHT},
		{RO_BoolOn,TRUE},
		{TAG_END,0}},
	format_format_gadget[]={
		{RO_Type,OBJECT_GADGET},
		{RO_GadgetType,GADGET_BOOLEAN},
		{RO_GadgetID,FORMAT_FORMAT},
		{RO_Top,8},
		{RO_TopFine,37},
		{RO_Width,13},
		{RO_Height,1},
		{RO_HeightFine,4},
		{RO_TextNum,STR_FORMAT_FORMAT},
		{RO_TextPos,TEXTPOS_CENTER},
		{RO_HighRecess,TRUE},
		{TAG_END,0}},
	format_quickformat_gadget[]={
		{RO_Type,OBJECT_GADGET},
		{RO_GadgetType,GADGET_BOOLEAN},
		{RO_GadgetID,FORMAT_QUICKFORMAT},
		{RO_Left,14},
		{RO_Top,8},
		{RO_TopFine,37},
		{RO_Width,13},
		{RO_Height,1},
		{RO_HeightFine,4},
		{RO_TextNum,STR_FORMAT_QUICKFORMAT},
		{RO_TextPos,TEXTPOS_CENTER},
		{RO_HighRecess,TRUE},
		{TAG_END,0}},
	format_cancel_gadget[]={
		{RO_Type,OBJECT_GADGET},
		{RO_GadgetType,GADGET_BOOLEAN},
		{RO_GadgetID,FORMAT_CANCEL},
		{RO_Left,28},
		{RO_Top,8},
		{RO_TopFine,37},
		{RO_Width,13},
		{RO_Height,1},
		{RO_HeightFine,4},
		{RO_TextNum,STR_FORMAT_EXIT},
		{RO_TextPos,TEXTPOS_CENTER},
		{RO_HighRecess,TRUE},
		{TAG_END,0}},

	format_device_list[]={
		{RO_Type,OBJECT_LISTVIEW},
		{RO_ListViewID,0},
		{RO_LeftFine,2},
		{RO_Width,10},
		{RO_Height,6},
		{RO_HeightFine,30},
		{RO_HighRecess,TRUE},
		{TAG_END,0}},

	format_info_box[]={
		{RO_Type,OBJECT_BORDER},
		{RO_BorderType,BORDER_RECESSED},
		{RO_LeftFine,2},
		{RO_Top,7},
		{RO_TopFine,28},
		{RO_Width,41},
		{RO_WidthFine,-4},
		{RO_Height,1},
		{RO_HeightFine,4},
		{TAG_END,0}},
		
	*format_gadgets[]={
		format_name_gadget,
		format_ffs_gadget,
		format_international_gadget,
		format_caching_gadget,
		format_trashcan_gadget,
		format_verify_gadget,
		format_format_gadget,
		format_quickformat_gadget,
		format_cancel_gadget,
		NULL};

void diskop_format(vis,portname,argc,argv)
struct VisInfo *vis;
char *portname;
int argc;
char *argv[];
{
	struct RequesterBase formatreq;
	struct Window *window;
	struct IntuiMessage *msg;
	Object_Border *infobox;
	struct DOpusListView *devicelist,*view;
	struct Gadget *gadlist,*gad,*gad_international=NULL,*gad_caching=NULL;
	ULONG class,mask,flags;
	USHORT code,gadgetid;
	APTR iaddress;
	struct StringExtend stringex;
	char *diskname;
	int a,b,gadcount=0,ignorechange=0;
	int start=-1,quick=0,startflags=FORMATFLAG_TRASHCAN;

	formatreq.rb_width=41;
	formatreq.rb_height=9;
	formatreq.rb_widthfine=16;
	formatreq.rb_heightfine=54;
	formatreq.rb_leftoffset=8;
	formatreq.rb_topoffset=8;
	formatreq.rb_flags=0;

	fill_out_req(&formatreq,vis);

	formatreq.rb_privateflags=0;
	formatreq.rb_screenname=NULL;

	if (formatreq.rb_screen && !(vis->vi_flags&VISF_BORDERS)) {
		formatreq.rb_flags|=RBF_STRINGS;
		formatreq.rb_title=NULL;
	}
	else {
		formatreq.rb_flags|=RBF_BORDERS|RBF_CLOSEGAD|RBF_STRINGS;
		formatreq.rb_title=string_table[STR_FORMAT_FORMAT];
	}

	formatreq.rb_extend=&stringex;
	formatreq.rb_idcmpflags=0;
	formatreq.rb_string_table=string_table;

	for (a=0;a<2;a++) {
		stringex.Pens[a]=vis->vi_stringcol[a];
		stringex.ActivePens[a]=vis->vi_activestringcol[a];
	}
	stringex.InitialModes=0;
	stringex.EditHook=NULL;
	stringex.WorkBuffer=NULL;
	for (a=0;a<4;a++) stringex.Reserved[a]=0;

	if (SysBase->LibNode.lib_Version<39) mask=12;
	else mask=0;

	if (!(window=OpenRequester(&formatreq)) ||
		!(gadlist=addreqgadgets(&formatreq,format_gadgets,mask,&gadcount)) ||
		!(infobox=(Object_Border *)
			AddRequesterObject(&formatreq,format_info_box)) ||
		!(devicelist=(struct DOpusListView *)
			AddRequesterObject(&formatreq,format_device_list)) ||
		!(devicelist->items=get_device_list(&formatreq.rb_memory,NULL))) {
		CloseRequester(&formatreq);
		return;
	}

	fix_listview(&formatreq,devicelist);
	select_device(devicelist,NULL);

	diskname=((struct StringInfo *)gadlist->SpecialInfo)->Buffer;
	strcpy(diskname,string_table[STR_FORMAT_EMPTY]);

	get_env("format",gadlist,gadcount,devicelist);

	flags=0;
	gad=gadlist;
	for (a=0;a<gadcount;a++) {
		if (gad->Activation&GACT_TOGGLESELECT)
			if (gad->Flags&GFLG_SELECTED) flags|=1<<(gad->GadgetID-1);
		gad->Flags&=~GFLG_DISABLED;
		gad=gad->NextGadget;
	}

	if (SysBase->LibNode.lib_Version>38) {
		gad_international=gadlist->NextGadget->NextGadget;
		gad_caching=gad_international->NextGadget;
	}

	for (a=0;a<argc;a++) {
		if (LStrCmpI(argv[a],"quick")==0) quick=1;
		else if (LStrCmpI(argv[a],"ffs")==0) startflags|=FORMATFLAG_FFS;
		else if (LStrCmpI(argv[a],"cache")==0) startflags|=FORMATFLAG_CACHING;
		else if (LStrnCmpI(argv[a],"inter",5)==0) startflags|=FORMATFLAG_INTERNATIONAL;
		else if (LStrCmpI(argv[a],"noicons")==0) startflags&=~FORMATFLAG_TRASHCAN;
		else if (LStrCmpI(argv[a],"verify")==0) startflags|=FORMATFLAG_VERIFY;
		else {
			if (start==-1) {
				for (b=0;devicelist->items[b];b++) {
					if (LStrCmpI(argv[a],devicelist->items[b])==0) {
						start=b;
						devicelist->itemselected=b;
						break;
					}
				}
			}
			else LStrnCpy(diskname,argv[a],30);
		}
	}

	if (start>-1) {
		gad=gadlist;
		for (a=0;a<gadcount;a++) {
			if (startflags&(1<<(gad->GadgetID-1))) gad->Flags|=GFLG_SELECTED;
			else gad->Flags&=~GFLG_SELECTED;
			gad=gad->NextGadget;
		}
	}

	RefreshRequesterObject(&formatreq,NULL);
	RefreshGList(gadlist,window,NULL,gadcount);

	if (flags&FORMATFLAG_CACHING && gad_international) {
		flags|=FORMATFLAG_INTERNATIONAL;
		gad_international->Flags|=GFLG_SELECTED;
		RefreshGList(gad_international,window,NULL,1);
		DisableGadget(gad_international,window->RPort,0,0);
	}

	if (!(AddListView(devicelist,1))) {
		CloseRequester(&formatreq);
		return;
	}
	show_sel_item(devicelist);
	show_device_info(&formatreq,infobox,devicelist->items[devicelist->itemselected]);

	if (start>-1) {
		if (do_format(&formatreq,
			infobox,
			devicelist->items[start],
			diskname,
			startflags,
			quick))
			dopus_message(DOPUSMSG_UPDATEDRIVE,(APTR)devicelist->items[start],portname);
		RemoveListView(devicelist,1);
		CloseRequester(&formatreq);
		return;
	}

	FOREVER {
		while (msg=(struct IntuiMessage *)GetMsg(window->UserPort)) {
			if ((view=(struct DOpusListView *)ListViewIDCMP(devicelist,msg))==
				(struct DOpusListView *)-1) {
				class=msg->Class; code=msg->Code;
				iaddress=msg->IAddress;
				ReplyMsg((struct Message *)msg);

				switch (class) {
					case IDCMP_DISKINSERTED:
					case IDCMP_DISKREMOVED:
						if (ignorechange) ignorechange=0;
						else {
							show_device_info(&formatreq,
								infobox,
								devicelist->items[devicelist->itemselected]);
						}
						break;

					case IDCMP_VANILLAKEY:	
						a=0;
						code=toupper(code);
						switch (code) {
							case 'N':
								ActivateStrGad(gadlist,window);
								break;
							case 'F':
								a=1;
								flags^=FORMATFLAG_FFS;
								break;
							case 'I':
								if (SysBase->LibNode.lib_Version>38) {
									a=2;
									flags^=FORMATFLAG_INTERNATIONAL;
								}
								break;
							case 'D':
								if (SysBase->LibNode.lib_Version>38) {
									a=3;
									flags^=FORMATFLAG_CACHING;
								}
								break;
							case 'P':
								if (SysBase->LibNode.lib_Version>38) a=4;
								else a=2;
								flags^=FORMATFLAG_TRASHCAN;
								break;
							case 'V':
								if (SysBase->LibNode.lib_Version>38) a=5;
								else a=3;
								flags^=FORMATFLAG_VERIFY;
								break;

							case 0x1b:
								set_env("format",gadlist,gadcount,devicelist);
								RemoveListView(devicelist,1);
								CloseRequester(&formatreq);
								return;
						}
						if (a) {
							gad=gadlist;
							while (a-- && gad->NextGadget) gad=gad->NextGadget;
							gad->Flags^=GFLG_SELECTED;
							RefreshGList(gad,window,NULL,1);
							if (gad==gad_international) {
								gad_caching->Flags&=~GFLG_SELECTED;
								RefreshGList(gad_caching,window,NULL,1);
								flags&=~FORMATFLAG_CACHING;
							}
							else if (gad==gad_caching) {
								gad_international->Flags&=~GFLG_SELECTED;
								RefreshGList(gad_international,window,NULL,1);
								flags&=~FORMATFLAG_INTERNATIONAL;
							}
						}
						break;

					case IDCMP_GADGETDOWN:
					case IDCMP_GADGETUP:
						gadgetid=((struct Gadget *)iaddress)->GadgetID;
					case IDCMP_CLOSEWINDOW:
						if (class==IDCMP_CLOSEWINDOW) gadgetid=FORMAT_CANCEL;

						switch (gadgetid) {
							case FORMAT_FFS:
								flags^=FORMATFLAG_FFS;
								break;

							case FORMAT_INTERNATIONAL:
								flags^=FORMATFLAG_INTERNATIONAL;
								flags&=~FORMATFLAG_CACHING;
								gad_caching->Flags&=~GFLG_SELECTED;
								RefreshGList(gad_caching,window,NULL,1);
								break;

							case FORMAT_CACHING:
								flags^=FORMATFLAG_CACHING;
								if (flags&FORMATFLAG_CACHING) {
									flags|=FORMATFLAG_INTERNATIONAL;
									gad_international->Flags|=GFLG_SELECTED;
									RefreshGList(gad_international,window,NULL,1);
									DisableGadget(gad_international,window->RPort,0,0);
								}
								else EnableGadget(gad_international,window->RPort,0,0);
								break;

							case FORMAT_TRASHCAN:
								flags^=FORMATFLAG_TRASHCAN;
								break;

							case FORMAT_VERIFY:
								flags^=FORMATFLAG_VERIFY;
								break;

							case FORMAT_CANCEL:
								set_env("format",gadlist,gadcount,devicelist);
								RemoveListView(devicelist,1);
								CloseRequester(&formatreq);
								return;

							case FORMAT_FORMAT:
							case FORMAT_QUICKFORMAT:
								if (do_format(&formatreq,
									infobox,
									devicelist->items[devicelist->itemselected],
									diskname,
									flags,
									(gadgetid==FORMAT_QUICKFORMAT))) {
									ignorechange=1;
									dopus_message(DOPUSMSG_UPDATEDRIVE,
										(APTR)devicelist->items[devicelist->itemselected],
										portname);
								}
								break;
						}
						break;
				}
			}
			else if (view) show_device_info(&formatreq,infobox,view->items[view->itemselected]);
		}
		Wait(1<<window->UserPort->mp_SigBit);
	}
}

void show_device_info(reqbase,border,name)
struct RequesterBase *reqbase;
Object_Border *border;
char *name;
{
	struct DeviceNode *devnode;
	struct DosEnvec *dosenvec;
	int tracks,tracksize,size;
	char infobuf[60],sizebuf[20];

	border_text(reqbase,border,NULL);

	if (!name || !(devnode=find_device(name))) return;

	dosenvec=(struct DosEnvec *)
		BADDR(((struct FileSysStartupMsg *)BADDR(devnode->dn_Startup))->fssm_Environ);

	tracks=dosenvec->de_HighCyl-dosenvec->de_LowCyl+1;
	tracksize=(dosenvec->de_BlocksPerTrack*dosenvec->de_Surfaces)*(dosenvec->de_SizeBlock*4);
	size=tracks*tracksize;

	getsizestring(sizebuf,size);
	lsprintf(infobuf,string_table[STR_FORMAT_INFODISPLAY],tracks,tracksize,sizebuf);

	border_text(reqbase,border,infobuf);
}

void getsizestring(buf,a)
char *buf;
ULONG a;
{
	a/=1024;
	if (a>1073741824) lsprintf(buf,"HUGE");
	else if (a>1048576) {
		getfloatstr((double)((double)a/1048576),buf);
		LStrCat(buf,"G");
	}
	else if (a>1024) {
		getfloatstr((double)((double)a/1024),buf);
		LStrCat(buf,"M");
	}
	else lsprintf(buf,"%ldK",a);
}

void getfloatstr(f,buf)
double f;
char *buf;
{
	int a,b,c,d;
	char buf1[20];

	a=(int)f; f-=a;
	b=(int)(f*100);
	c=(b/10)*10; d=b-c;
	if (d>4) c+=10;
	if (c==100) {
		c=0; ++a;
	}
	lsprintf(buf1,"%ld",c); buf1[1]=0;
	lsprintf(buf,"%ld.%s",a,buf1);
}

do_format(reqbase,border,device,name,flags,quick)
struct RequesterBase *reqbase;
Object_Border *border;
char *device,*name;
ULONG flags;
char quick;
{
	ULONG dostype;
	struct DeviceHandle handle;
	int suc=1,txt=STR_FAILED_ERROR;
	struct Requester busyreq;

	if (flags&FORMATFLAG_INTERNATIONAL) {
		if (flags&FORMATFLAG_FFS) dostype=ID_INTER_FFS_DISK;
		else dostype=ID_INTER_DOS_DISK;
	}
	else if (flags&FORMATFLAG_CACHING) {
		if (flags&FORMATFLAG_FFS) dostype=ID_FASTDIR_FFS_DISK;
		else dostype=ID_FASTDIR_DOS_DISK;
	}
	else if (flags&FORMATFLAG_FFS) dostype=ID_FFS_DISK;
	else dostype=ID_DOS_DISK;

	if (!(open_device(device,&handle))) txt=STR_NODEVICE_ERROR;
	else {
		InitRequester(&busyreq);
		busyreq.Flags=NOISYREQ;
		Request(&busyreq,reqbase->rb_window);
		SetBusyPointer(reqbase->rb_window);

		border_text(reqbase,border,string_table[STR_CHECKING_DESTINATION]);

		if (check_disk(reqbase,handle.device_req,device,1)) {
			if (check_blank_disk(reqbase,device,string_table[STR_FORMAT_FORMAT])) {

				suc=0;

				inhibit_drive(device,DOSTRUE);
				drive_motor(handle.device_req,1);

				if (!quick) {
					suc=do_raw_format(reqbase,border,
						handle.device_req,
						(handle.dosenvec->de_SizeBlock<<2)*
							handle.dosenvec->de_Surfaces*handle.dosenvec->de_BlocksPerTrack,
						handle.dosenvec->de_LowCyl,
						handle.dosenvec->de_HighCyl-handle.dosenvec->de_LowCyl+1,
						handle.dosenvec->de_BufMemType,
						flags);
				}

				if (!suc) {
					border_text(reqbase,border,string_table[STR_FORMAT_INITIALISING]);

					if (DOSBase->dl_lib.lib_Version>36) {
						if (Format(device,name,dostype)) suc=0;
						else suc=ERROR_FAILED;
					}
					else {
						suc=do_initialise(handle.device_req,name,dostype,
							handle.dosenvec->de_LowCyl*
								handle.dosenvec->de_BlocksPerTrack*
								handle.dosenvec->de_Surfaces,
							(handle.dosenvec->de_HighCyl-handle.dosenvec->de_LowCyl+1)*
								handle.dosenvec->de_BlocksPerTrack*handle.dosenvec->de_Surfaces,
							handle.dosenvec->de_Reserved,
							handle.dosenvec->de_BufMemType,
							flags);
					}
				}

				drive_motor(handle.device_req,0);
				inhibit_drive(device,FALSE);

				switch (suc) {
					case 0:
						if (flags&FORMATFLAG_TRASHCAN) {
							if (SysBase->LibNode.lib_Version<36) Delay(150);
							write_trashcan(reqbase,border,device);
						}
						txt=STR_SUCCESS;
						break;
					case ERROR_FAILED: txt=STR_FAILED_ERROR; break;
					case ERROR_MEMORY: txt=STR_MEMORY_ERROR; break;
					case ERROR_BITMAP: txt=STR_BITMAP_ERROR; break;
					case ERROR_ABORTED: txt=STR_ABORTED; break;
					case ERROR_VERIFY: txt=-1; break;
					default: txt=STR_DEVICE_ERROR; break;
				}
			}
			else txt=STR_ABORTED;
		}
		else txt=STR_ABORTED;

		EndRequest(&busyreq,reqbase->rb_window);
		ClearPointer(reqbase->rb_window);
	}

	close_device(&handle);
	if (txt>-1) border_text(reqbase,border,string_table[txt]);
	return((txt==STR_SUCCESS));
}

do_initialise(device_req,name,dostype,firstblock,numblocks,reserved,memtype,flags)
struct IOExtTD *device_req;
char *name;
ULONG dostype;
ULONG firstblock,numblocks,reserved;
ULONG memtype;
ULONG flags;
{
	ULONG *bitmap,bitmapsize;
	char *buffer;
	struct BitmapBlock *bitmapblock;
	struct BitmapExtension *bitmapextension;
	struct RootDirectory *rootdirectory;
	struct DOpusRemember *key=NULL;
	LONG error,a;
	LONG bitmapcount,bitmapblocks;
	LONG count,root;
	LONG extensioncount,extensionblocks=0;

	bitmapsize=(numblocks-reserved+31)/32;

	if (!(buffer=LAllocRemember(&key,TD_SECTOR,memtype|MEMF_PUBLIC|MEMF_CLEAR)))
		return(ERROR_MEMORY);

	*(ULONG *)buffer=dostype;

	for (a=0;a<2;a++) {
		if (error=do_writeblock(device_req,buffer,firstblock+a)) {
			LFreeRemember(&key);
			return(error);
		}
		*(ULONG *)buffer=0;
	}

	if (!(bitmap=(ULONG *)LAllocRemember(&key,sizeof(ULONG)*bitmapsize,MEMF_PUBLIC))) {
		LFreeRemember(&key);
		return(ERROR_MEMORY);
	}

	for (a=0;a<bitmapsize;a++) bitmap[a]=~0;

	count=bitmapblocks=(bitmapsize+126)/127;

	if (count>25) {
		if (!(flags&FORMATFLAG_FFS)) {
			LFreeRemember(&key);
			return(ERROR_BITMAP);
		}
		count-=25;

		do {
			++extensionblocks;

			if (count>127) count-=127;
			else count=0;
		}
		while (count);
	}

	root=numblocks>>1;
	rootdirectory=(struct RootDirectory *)buffer;
	for (a=0;a<TD_SECTOR;a++) buffer[a]=0;

	rootdirectory->PrimaryType=2;
	rootdirectory->HashTableSize=128-56;
	rootdirectory->BitmapFlag=DOSTRUE;
	DateStamp(&rootdirectory->LastRootChange);

	if ((a=strlen(name))>31) a=31;

	rootdirectory->DiskName[0]=a;
	CopyMem(name,&rootdirectory->DiskName[1],a);

	DateStamp(&rootdirectory->LastDiskChange);
	DateStamp(&rootdirectory->CreationDate);

	rootdirectory->SecondaryType=1;

	for (a=0;a<bitmapblocks;a++) {
		if (a==25) break;
		rootdirectory->BitmapPointers[a]=root+firstblock+a+1;
	}

	if (bitmapblocks>25) rootdirectory->BitmapExtension=root+firstblock+26;
	rootdirectory->Checksum=-do_checksum((ULONG *)rootdirectory);

	if (error=do_writeblock(device_req,rootdirectory,root+firstblock)) {
		LFreeRemember(&key);
		return(error);
	}

	for (a=root-reserved;a<root-reserved+bitmapblocks+extensionblocks+1;a++)
		bitmap[a/32]&=BitTable[a%32];

	bitmapblock=(struct BitmapBlock *)buffer;

	for (a=0;a<bitmapblocks;a++) {
		CopyMem(&bitmap[a*127],&bitmapblock->Bitmap[0],sizeof(ULONG)*127);
		bitmapblock->Checksum=0;
		bitmapblock->Checksum=-do_checksum((ULONG *)bitmapblock);
		if (error=do_writeblock(device_req,bitmapblock,root+firstblock+a+1)) {
			LFreeRemember(&key);
			return(error);
		}
	}

	if (extensionblocks) {
		bitmapextension=(struct BitmapExtension *)buffer;

		bitmapcount=root+firstblock+1;
		extensioncount=bitmapcount+bitmapblocks;
		bitmapblocks-=25;

		while (bitmapblocks) {
			for (a=0;a<TD_SECTOR;a++) buffer[a]=0;
			for (a=0;a<(bitmapblocks<127?bitmapblocks:127);a++)
				bitmapextension->BitmapPointers[a]=bitmapcount++;
			if (bitmapblocks>127) bitmapblocks-=127;
			else bitmapblocks=0;

			if (bitmapblocks) bitmapextension->BitmapExtension=extensioncount+1;
			if (error=do_writeblock(device_req,bitmapextension,extensioncount++)) {
				LFreeRemember(&key);
				return(error);
			}
		}
	}

	LFreeRemember(&key);
	return(0);
}

do_raw_format(reqbase,border,device_req,tracksize,lowtrack,numtracks,memtype,flags)
struct RequesterBase *reqbase;
Object_Border *border;
struct IOExtTD *device_req;
ULONG tracksize,lowtrack,numtracks;
ULONG memtype;
ULONG flags;
{
	ULONG *trackbuffer,*verifybuffer;
	struct DOpusRemember *key=NULL;
	ULONG offset;
	int track,a,cmpsize,ret;
	char infobuf[80];

	if (!(trackbuffer=(ULONG *)LAllocRemember(&key,tracksize,memtype|MEMF_CLEAR)))
		return(ERROR_MEMORY);
	if (flags&FORMATFLAG_VERIFY) {
		verifybuffer=(ULONG *)LAllocRemember(&key,tracksize,memtype);
		cmpsize=tracksize>>2;
	}
	else verifybuffer=NULL;

	offset=lowtrack*tracksize;

	for (track=0;track<numtracks;track++) {
		lsprintf(infobuf,string_table[STR_FORMAT_FORMATTING],track,numtracks-track-1);
		border_text(reqbase,border,infobuf);

		FOREVER {
			if (check_abort(reqbase->rb_window)) {
				LFreeRemember(&key);
				return(ERROR_ABORTED);
			}

			device_req->iotd_Req.io_Command=TD_FORMAT;
			device_req->iotd_Req.io_Data=(APTR)trackbuffer;
			device_req->iotd_Req.io_Offset=offset;
			device_req->iotd_Req.io_Length=tracksize;

			if (!(DoIO((struct IORequest *)device_req))) {
				if (!verifybuffer) break;
				device_req->iotd_Req.io_Command=CMD_UPDATE;
				if (!(DoIO((struct IORequest *)device_req))) break;
			}

			lsprintf(infobuf,string_table[STR_FORMAT_FORMATERROR],track);
			border_text(reqbase,border,infobuf);
			if (!(check_error(reqbase,infobuf,STR_RETRY))) {
				LFreeRemember(&key);
				return(ERROR_FAILED);
			}
		}

		if (verifybuffer) {
			lsprintf(infobuf,string_table[STR_FORMAT_VERIFYING],track,numtracks-track-1);
			border_text(reqbase,border,infobuf);

			FOREVER {
				if (check_abort(reqbase->rb_window)) {
					LFreeRemember(&key);
					return(ERROR_ABORTED);
				}

				device_req->iotd_Req.io_Command=CMD_READ;
				device_req->iotd_Req.io_Data=(APTR)verifybuffer;
				device_req->iotd_Req.io_Offset=offset;
				device_req->iotd_Req.io_Length=tracksize;

				if (!(DoIO((struct IORequest *)device_req))) {
					for (a=0;a<cmpsize;a++) {
						if (verifybuffer[a]!=0) break;
					}
					if (a==cmpsize) break;
					lsprintf(infobuf,string_table[STR_FORMAT_VERIFYERROR],track);
					ret=ERROR_VERIFY;
				}
				else {
					lsprintf(infobuf,string_table[STR_FORMAT_FORMATERROR],track);
					ret=ERROR_FAILED;
				}

				border_text(reqbase,border,infobuf);
				if (!(check_error(reqbase,infobuf,STR_RETRY))) {
					LFreeRemember(&key);
					return(ret);
				}
			}
		}

		offset+=tracksize;
	}
	LFreeRemember(&key);
	return(0);
}

void write_trashcan(reqbase,border,device)
struct RequesterBase *reqbase;
Object_Border *border;
char *device;
{
	struct DiskObject *trashcan;
	BPTR lock;
	char name[80];

	if (!IconBase) return;

	border_text(reqbase,border,string_table[STR_FORMAT_MAKINGTRASHCAN]);

	lsprintf(name,"%sTrashcan",device);
	if (!(lock=CreateDir(name))) return;
	UnLock(lock);

	if (IconBase->lib_Version>36 &&
		(trashcan=GetDefDiskObject(WBGARBAGE))) {
		PutDiskObject(name,trashcan);
		FreeDiskObject(trashcan);
	}
	else PutDiskObject(name,&trashcanicon_icon);
}
