/*

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};

/* prototypes */
void show_device_info(struct RequesterBase *reqbase,Object_Border *border,char *name);
void getsizestring(char *buf,long long a);
void getfloatstr(double f,char *buf);
void write_trashcan(struct RequesterBase *reqbase,Object_Border *border,char *device);
/* end of prototypes */

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;
    long long 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=((long long)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;
long long 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",(long)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);
}
