/*

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"

enum {
	INSTALL_FFS,
	INSTALL_INSTALL,
	INSTALL_NOBOOT,
	INSTALL_CANCEL};

struct TagItem
	install_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}},

	install_ffs_gadget[]={
		{RO_Type,OBJECT_GADGET},
		{RO_GadgetType,GADGET_CHECK},
		{RO_GadgetID,INSTALL_FFS},
		{RO_Left,14},
		{RO_TextNum,STR_FORMAT_FFS},
		{RO_TextPos,TEXTPOS_RIGHT},
		{RO_ChkCenter,TRUE},
		{TAG_END,0}},

	install_install_gadget[]={
		{RO_Type,OBJECT_GADGET},
		{RO_GadgetType,GADGET_BOOLEAN},
		{RO_GadgetID,INSTALL_INSTALL},
		{RO_Top,8},
		{RO_TopFine,37},
		{RO_Width,13},
		{RO_Height,1},
		{RO_HeightFine,4},
		{RO_TextNum,STR_INSTALL_INSTALL},
		{RO_TextPos,TEXTPOS_CENTER},
		{RO_HighRecess,TRUE},
		{TAG_END,0}},
	install_noboot_gadget[]={
		{RO_Type,OBJECT_GADGET},
		{RO_GadgetType,GADGET_BOOLEAN},
		{RO_GadgetID,INSTALL_NOBOOT},
		{RO_Left,14},
		{RO_Top,8},
		{RO_TopFine,37},
		{RO_Width,13},
		{RO_Height,1},
		{RO_HeightFine,4},
		{RO_TextNum,STR_INSTALL_NOBOOT},
		{RO_TextPos,TEXTPOS_CENTER},
		{RO_HighRecess,TRUE},
		{TAG_END,0}},
	install_cancel_gadget[]={
		{RO_Type,OBJECT_GADGET},
		{RO_GadgetType,GADGET_BOOLEAN},
		{RO_GadgetID,INSTALL_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}},

	install_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}},

	*install_gadgets[]={
		install_ffs_gadget,
		install_install_gadget,
		install_noboot_gadget,
		install_cancel_gadget,
		NULL};

void diskop_install(vis,argc,argv)
struct VisInfo *vis;
int argc;
char *argv[];
{
	struct RequesterBase installreq;
	struct Window *window;
	struct IntuiMessage *msg;
	Object_Border *infobox;
	struct DOpusListView *devicelist,*view;
	struct Gadget *gadlist;
	ULONG class,mask;
	USHORT code,gadgetid;
	APTR iaddress;
	int gadcount=0,ignorechange=0,a,b;
	int start=-1,startffs=0,startnoboot=0;

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

	fill_out_req(&installreq,vis);

	installreq.rb_privateflags=0;
	installreq.rb_screenname=NULL;

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

	installreq.rb_extend=NULL;
	installreq.rb_idcmpflags=0;
	installreq.rb_string_table=string_table;

	if (SysBase->LibNode.lib_Version<36) mask=1;
	else mask=0;

	if (!(window=OpenRequester(&installreq)) ||
		!(gadlist=addreqgadgets(&installreq,install_gadgets,mask,&gadcount)) ||
		!(infobox=(Object_Border *)
			AddRequesterObject(&installreq,install_info_box)) ||
		!(devicelist=(struct DOpusListView *)
			AddRequesterObject(&installreq,install_device_list)) ||
		!(devicelist->items=get_device_list(&installreq.rb_memory,"DF0:"))) {
		CloseRequester(&installreq);
		return;
	}

	fix_listview(&installreq,devicelist);
	get_env("install",gadlist,gadcount,devicelist);

	for (a=0;a<argc;a++) {
		if (LStrCmpI(argv[a],"ffs")==0 && !mask) startffs=1;
		else if (LStrCmpI(argv[a],"noboot")==0) startnoboot=1;
		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;
					}
				}
			}
		}
	}

	if (!(AddListView(devicelist,1))) {
		CloseRequester(&installreq);
		return;
	}
	show_sel_item(devicelist);

	if (start>-1) {
		if (startffs) gadlist->Flags|=GFLG_SELECTED;
		else gadlist->Flags&=~GFLG_SELECTED;
	}

	RefreshRequesterObject(&installreq,NULL);
	RefreshGList(gadlist,window,NULL,gadcount);
	show_install_info(&installreq,infobox,devicelist->items[devicelist->itemselected]);

	if (start>-1) {
		do_install(&installreq,infobox,
			devicelist->items[devicelist->itemselected],
			startnoboot,startffs);
		RemoveListView(devicelist,1);
		CloseRequester(&installreq);
		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_install_info(&installreq,
								infobox,
								devicelist->items[devicelist->itemselected]);
						}
						break;

					case IDCMP_VANILLAKEY:	
						code=toupper(code);
						switch (code) {
							case 'F':
								if (!mask) {
									gadlist->Flags^=GFLG_SELECTED;
									RefreshGList(gadlist,window,NULL,1);
								}
								break;
							case 0x1b:
								set_env("install",gadlist,gadcount,devicelist);
								RemoveListView(devicelist,1);
								CloseRequester(&installreq);
								return;
						}
						break;

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

						switch (gadgetid) {
							case INSTALL_CANCEL:
								set_env("install",gadlist,gadcount,devicelist);
								RemoveListView(devicelist,1);
								CloseRequester(&installreq);
								return;
							case INSTALL_INSTALL:
							case INSTALL_NOBOOT:
								if (do_install(&installreq,infobox,
									devicelist->items[devicelist->itemselected],
									(gadgetid==INSTALL_NOBOOT),(!mask && gadlist->Flags&GFLG_SELECTED)))
									ignorechange=1;
								break;
						}
						break;
				}
			}
			else if (view) {
				show_install_info(&installreq,
					infobox,view->items[view->itemselected]);
			}
		}
		Wait(1<<window->UserPort->mp_SigBit);
	}
}

void show_install_info(reqbase,border,name)
struct RequesterBase *reqbase;
Object_Border *border;
char *name;
{
	struct DeviceHandle handle;
	ULONG *buffer,tracksize,dostype;
	char infobuf[80];
	int a,sum,lastsum,ffs;

	border_text(reqbase,border,NULL);
	strcpy(infobuf,string_table[STR_INSTALL_NOINFO]);

	if (open_device(name,&handle)) {
		handle.device_req->iotd_Req.io_Command=TD_CHANGESTATE;
		DoIO((struct IORequest *)handle.device_req);
		if (handle.device_req->iotd_Req.io_Actual)
			lsprintf(infobuf,string_table[STR_NODISKPRESENT],name);
		else {
			tracksize=(handle.dosenvec->de_SizeBlock<<2)*
				handle.dosenvec->de_Surfaces*handle.dosenvec->de_BlocksPerTrack,
			drive_motor(handle.device_req,1);
			if (buffer=(ULONG *)AllocMem(TD_SECTOR*2,handle.dosenvec->de_BufMemType)) {
				handle.device_req->iotd_Req.io_Command=CMD_READ;
				handle.device_req->iotd_Req.io_Data=(APTR)buffer;
				handle.device_req->iotd_Req.io_Offset=handle.dosenvec->de_LowCyl*tracksize;
				handle.device_req->iotd_Req.io_Length=TD_SECTOR*2;
				if (!(DoIO((struct IORequest *)handle.device_req))) {
					dostype=buffer[0];
					for (a=0,sum=0;a<TD_SECTOR>>1;a++) {
						lastsum=sum;
						sum+=buffer[a];
						if (lastsum>sum) ++sum;
					}
					if (sum!=0) strcpy(infobuf,string_table[STR_INSTALL_NOTBOOTABLE]);
					else {
						if (dostype&1) ffs=1;
						else ffs=0;

						if (install_compare_block(buffer,bootblock_13,13)) {
							lsprintf(infobuf,string_table[STR_INSTALL_NORMAL],
								"1.3",(ffs)?"FFS":"OFS");
						}
						else if (install_compare_block(buffer,bootblock_20,24)) {
							lsprintf(infobuf,string_table[STR_INSTALL_NORMAL],
								"2.0+",(ffs)?"FFS":"OFS");
							if ((dostype&0xfffffffe)==ID_INTER_DOS_DISK)
								strcat(infobuf," (Inter)");
							else if ((dostype&0xfffffffe)==ID_FASTDIR_DOS_DISK)
								strcat(infobuf," (Cache)");
						}
						else strcpy(infobuf,string_table[STR_INSTALL_NONSTANDARD]);
					}
				}
				FreeMem(buffer,TD_SECTOR*2);
			}
		}
		drive_motor(handle.device_req,0);
	}
	close_device(&handle);
	border_text(reqbase,border,infobuf);
}

install_compare_block(block,testblock,size)
ULONG *block,*testblock,size;
{
	int a;

	for (a=3;a<size;a++)
		if (block[a]!=testblock[a])
			return(0);
	return(1);
}

do_install(reqbase,border,device,noboot,forceffs)
struct RequesterBase *reqbase;
Object_Border *border;
char *device;
int noboot,forceffs;
{
	struct DeviceHandle handle;
	ULONG dostype;
	ULONG *buffer,tracksize;
	BPTR lock;
	struct InfoData __aligned data;
	int a,err=STR_SUCCESS,sum,lastsum;
	struct Requester busyreq;

	InitRequester(&busyreq);
	busyreq.Flags=NOISYREQ;
	Request(&busyreq,reqbase->rb_window);
	SetBusyPointer(reqbase->rb_window);

	if (open_device(device,&handle)) {
		border_text(reqbase,border,string_table[STR_CHECKING_DESTINATION]);
		drive_motor(handle.device_req,1);
		tracksize=(handle.dosenvec->de_SizeBlock<<2)*
			handle.dosenvec->de_Surfaces*handle.dosenvec->de_BlocksPerTrack;

		if (check_disk(reqbase,handle.device_req,device,1)) {
			if (lock=Lock(device,ACCESS_READ)) {
				Info(lock,&data);
				UnLock(lock);

				dostype=data.id_DiskType;

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

				if ((dostype&~(0xff))==0x444f5300) {
					if (forceffs) dostype|=1;
					if (buffer=(ULONG *)AllocMem(TD_SECTOR*2,handle.dosenvec->de_BufMemType|MEMF_CLEAR)) {

						if (noboot) buffer[0]=dostype;
						else {
							if (SysBase->LibNode.lib_Version<36)
								CopyMem((char *)bootblock_13,(char *)buffer,sizeof(bootblock_13));
							else CopyMem((char *)bootblock_20,(char *)buffer,sizeof(bootblock_20));

							buffer[0]=dostype;

							for (a=0,sum=0;a<TD_SECTOR>>1;a++) {
								lastsum=sum;
								sum+=buffer[a];
								if (lastsum>sum) ++sum;
							}

							buffer[1]=~sum;
						}

						inhibit_drive(device,DOSTRUE);

						FOREVER {
							handle.device_req->iotd_Req.io_Command=CMD_WRITE;
							handle.device_req->iotd_Req.io_Data=(APTR)buffer;
							handle.device_req->iotd_Req.io_Offset=handle.dosenvec->de_LowCyl*tracksize;
							handle.device_req->iotd_Req.io_Length=TD_SECTOR*2;

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

							border_text(reqbase,border,string_table[STR_INSTALL_ERRORWRITING]);
							if (!(check_error(reqbase,string_table[STR_INSTALL_ERRORWRITING],STR_RETRY))) {
								err=-1;
								break;
							}
						}

						inhibit_drive(device,FALSE);
						FreeMem(buffer,TD_SECTOR*2);
					}
					else err=STR_MEMORY_ERROR;
				}
				else err=STR_INVALID_DISK;
			}
			else err=STR_INVALID_DISK;
		}
		else err=STR_ABORTED;

		drive_motor(handle.device_req,0);
	}
	else err=STR_NODEVICE_ERROR;

	if (err>-1) border_text(reqbase,border,string_table[err]);

	close_device(&handle);

	EndRequest(&busyreq,reqbase->rb_window);
	ClearPointer(reqbase->rb_window);
	return((err==STR_SUCCESS));
}
