/*
**		FO v1.0 by FBJ
**      
**      (c)Copyright 1991, Campagne Fabien, All Rights Reserved
**
**      Campagne Fabien
**      805, Rue des Gentianes
**      39000 Lons Le saunier
**      FRANCE
**
**	un optimiseur de disk (rapide).
**	FO doit être linké avec TD.o											
**  pour compiler: cc FO "objects:TD.o -gs"									
**      
**                                                                          */

#include <exec/types.h>
#include <exec/memory.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <stdio.h>
#include <string.h>
#include "fbj/Blocks.h"
#include "fbj/ShortBlocks.h"
#include "fbj/TDprotos.h"


/*#define DEBUG 1
*/
#define ON 1L
#define OFF 0L
#define R CMD_READ
#define W CMD_WRITE
#define used 0
#define var Variables
#define BBM VarAdr->BuffBitMap
/*#define BootB VarAdr->BuffTrack*/
#define BT VarAdr->BuffTrack
#define sourceDrive VarAdr->sourcedrive
#define destDrive VarAdr->destdrive
#define dsklist VarAdr->disklist
#define CLI VarAdr->Cli
#define err VarAdr->Err
#define GFree VarAdr->GestFree
#define DFree VarAdr->DataFree
#define FLnog VarAdr->FLnoG
#define drivebusy VarAdr->DriveBusy
#define FORMAT VarAdr->Format
#define CHECK VarAdr->Check

extern struct DosLibrary *DOSBase;

struct Variables{
	LONG	*BuffBitMap;	/* adr du buffer contenant le bloc BitMap */
	WORD	BitMap;		/* n° du Block BitMap */
	LONG	*BuffTrack; /* Buffer de track pour le formattage de la destination */
	LONG	*Empty;		/* pointeur sur zone contenant datas pour blocs vides */
	BYTE	sourcedrive;	/* */
	BYTE	destdrive;	/* */
	LONG	TotalBlocks; /* */
	LONG	(*TableMem)[]; /* */
	WORD	EndTMem;	/* indice sur le dernier LONG occupé de TableMem */
	WORD	GestFree;	/* dernier bloc de Gestion libre */
	WORD	DataFree;	/* dernier bloc de Data libre */
	LONG	*inCHIP;	/* Buffer 512L en CHIP pour TD */
	struct  DiskList *disklist;	/* */
	BYTE	Cli;	/* if non set, optimize for WB use */
	BYTE	FLnoG;	/* if set, File List will no longuer be considered as Gestion B */
	BYTE	Format; /* if set, the whole disk is formatted */
	BYTE	Check;	/* if set, The Dos Structure of Source is Checked */
	WORD	Err;	/* compteur d' erreur */
	BYTE	DriveBusy[5];
};
struct var *VarAdr;

void RendMem(void);
void FreeBusy(void);

main(ac,av)
BYTE *av[];
WORD ac;
{
WORD TotalBlocks,i,j=0,Perr=0;
LONG k;
BYTE argn;
	puts("FO v1.0 Fast Optimiser by FBJ.");
	VarAdr=(struct var *)AllocMem(sizeof(struct var),MEMF_CLEAR|MEMF_PUBLIC);
	atexit(RendMem);

	WBench(1);				/* ( Par défault ) */
	forFDir(0);
	NoFormat(0);
	Check(0);

	if (ac<3 )
	{
		if (*av[1]=='?') {help(); exit(0);}
		else puts("? for help");exit(0);
	}
	if (strnicmp(av[1],"df",2)) {puts("1st arg: source drive"); exit(0);}
	if (strnicmp(av[2],"df",2)) {puts("2nd arg: dest drive"); exit(0);}
	sourceDrive=*(av[1]+2)-48;
	destDrive=  *(av[2]+2)-48;
	if (sourceDrive<0 || sourceDrive>3) {puts("1st arg: source drive"); exit(0);}
	if (destDrive<0 || destDrive>3) {puts("2nd arg: dest drive"); exit(0);}

	argn=3;
	while (ac>=argn && *av[argn]=='-')
	{
	switch (*(av[argn]+1)) {
		case 'w' : WBench(1); break;
		case 'W' : WBench(1); break;
		case 'c' : ComLineInt(1); break;
		case 'n' : if ( !strnicmp(av[argn]+1,"nfo",3) ) NoFormat(1); break;
		case 'N' : if ( !strnicmp(av[argn]+1,"nfo",3) ) NoFormat(1); break;
		case 'F' : if ( !strnicmp(av[argn]+1,"FDir",4) ) forFDir(1); break;
		case 'C' : Check(1); break;
		}
	argn++;
	}
	
	puts("Now insert SOURCE disk and RETURN");
	while ( (getchar()) != 10) ;

	DiskBUSY(av[2],1);
	atexit(FreeBusy);
	strncpy(drivebusy,av[2],5);

	if (!OpenTD(sourceDrive)) {puts("Source drive Not Available"); exit(0);}
	if (!OpenTD(destDrive)) {puts("Dest drive Not Available"); exit(0);}
	atexit(CloseTD);
	VarAdr->inCHIP=(LONG *)AllocMem(512L,MEMF_CHIP|MEMF_CLEAR|MEMF_PUBLIC);
	VarAdr->BuffBitMap=(LONG *)AllocMem(512L,MEMF_CHIP|MEMF_CLEAR|MEMF_PUBLIC);
	Read_WriteB(R,sourceDrive,BBM,880L); /* charge le Root */
	/* on trouve BitMap */
	VarAdr->BitMap=(WORD)((struct RootB *)(VarAdr->BuffBitMap))->BitMapTable[0]; 
#ifdef DEBUG
	printf("n ° du bloc BitMap :%d\n",VarAdr->BitMap);
#endif
	/* on le charge */
	Read_WriteB(R,sourceDrive,BBM,VarAdr->BitMap);
	/* on réserve le buffer de track et on charge le bootBlock oû il faut */
	BT=(LONG *)AllocMem(11*512L,MEMF_CHIP|MEMF_CLEAR|MEMF_PUBLIC);
	Read_WriteB(R,sourceDrive,BT,0L);
	Read_WriteB(R,sourceDrive,BT+128L,1L);
#ifdef DEBUG
	printf("Adr du Buff de Track : %p\n",BT);
#endif
	VarAdr->Empty=(LONG *)AllocMem(512L,MEMF_CLEAR|MEMF_PUBLIC);
	if (VarAdr->Empty==0) Perr++;
	VarAdr->TotalBlocks=HowBlockUsed(BBM);
#ifdef DEBUG
	printf("Total des Blocks Alloués : %d\n",VarAdr->TotalBlocks);
#endif
	VarAdr->TableMem=(APTR)AllocMem(VarAdr->TotalBlocks*4+4,MEMF_CLEAR|MEMF_PUBLIC);
#ifdef DEBUG
	printf("Adr de TableMem : %p\n",&VarAdr->TableMem);
#endif
	j=0;
	for (i=0;i!=VarAdr->TotalBlocks;i++)
	{
	k=AllocMem(512L,MEMF_CLEAR|MEMF_PUBLIC);
	if (k==0) {Perr=103; break;}
	(*VarAdr->TableMem)[j++]=k;
	}
	(*VarAdr->TableMem)[j]=0L;
	VarAdr->EndTMem=j-1;
	
	if (Perr)
	{
		puts("Désolé, pas assez de mémoire pour optimisation rapide");
		exit(0);
	}
	/*	On initialise la strucure DiskList */
	dsklist=(struct SinitB*)AllocMem(sizeof(struct SinitB),MEMF_CLEAR|MEMF_PUBLIC);
	dsklist->dl_Bloc=-9;
	((struct SinitB*)dsklist)->dl_Type=0;

	/*	Avec un bloc initial qui ne sert à rien mais est bien pratique */

	LoadBinMem();
	MtrOff(sourceDrive);
	if (CHECK)
	{
		puts("Checking Dos Structure of disk");
		CheckDosStruct();
		if (!err) puts("No Error in Dos Structure");
		else {printf("%d ERRORS on disk !! FO Stopped\n",err); exit(0);}
	}
	MtrOff(sourceDrive);
	puts("Now I optimize ...");
	Optimize();
	BitMap();
	ChangeBlocks();
	puts("Writing result to destination disk");
	MemToDisk();
	puts("Fast Optimization done.");

	exit(0);
}

void RendMem()
{
LONG	M=1;
WORD	i=0;
	if (VarAdr->BuffBitMap)
	{
		FreeMem(BBM,512L);
		BBM=0L;
	}
	if (VarAdr->BuffTrack)
	{
		FreeMem(BT,11*512L);
		BT=0L;
	}
	if (VarAdr->Empty)
	{
		FreeMem(VarAdr->Empty,512L);
		VarAdr->Empty=0;
	}
	
	i=0;
	while (M)
	{
	M=(*VarAdr->TableMem)[i++];
	if (M) FreeMem(M,512L); else break;
	}
	if (VarAdr->TableMem) FreeMem(VarAdr->TableMem,VarAdr->TotalBlocks*4+4);
	if (dsklist) FreeDiskList();
	if (VarAdr->inCHIP) {FreeMem(VarAdr->inCHIP,512L); VarAdr->inCHIP=0;}
	if (VarAdr) {FreeMem(VarAdr,sizeof(struct var)); VarAdr=0;}

}

void FreeBusy()
{
	DiskBUSY(drivebusy,0);
}

/*	isBlocUsed renvoie 1 si Block libre ou 0 si block occuppé 
	il se réfère au BitMap	*/

isBlocUsed(Bloc)
WORD	Bloc;
{
LONG	A,B;
	if (Bloc==0 || Bloc==1) return(1); /* Faux car blocks used mais pratique ! */
	A=*(BBM+1+(Bloc-2)/32);
	B=(A>>(((Bloc-2)%32)) & 0x1L);
	return(B);	 
}

HowBlockUsed(AdrBM)
LONG *AdrBM;
{
WORD i,TotalBlocks=0;

	for (i=2;i<=1760;i++)
	{
	TotalBlocks+=(WORD)!(isBlocUsed(i));
	}
	return(TotalBlocks);
}

LoadBinMem()
{
LONG	n,m=0;
APTR	buff1,buff2,inCHIP;
struct DiskList *dlist;

	inCHIP=VarAdr->inCHIP;
	puts("Reading Source and preparing for Fast Optimising");
	for (n=2;n<1760;n++)
	{
		if (isBlocUsed(n)==used) 
		{
			buff1=(APTR)(Buffer(m++));
			if (TypeOfMem(buff1)&MEMF_CHIP) buff2=buff1;
			else buff2=inCHIP;
			if (Read_WriteB(R,(LONG)sourceDrive,buff2,n)) 
			{
				puts("Read Error, can' t optimize");
				exit(0);
			}
			if (buff2==inCHIP) CopyMemQuick(buff2,buff1,512L);
#ifdef DEBUG
	if (!Buffer(m-1))
	{
		puts("Problème avec LoadBinMem !");
		exit(0);
	}
#endif
		Organising(buff1,dlist,n);
		}		
		if ((n%22)==0)
		{
			printf("CYL %d",n/22);
			putchar(13);
			fflush(stdout);
		}
		
	}
	
}

/*	Cherche et renvoie l' adresse oû ranger le bloc */
Buffer(m)
LONG m;
{
	return((*VarAdr->TableMem)[m]);
}

Organising(buff1,dlist,n)
LONG	*buff1;
LONG	n;
struct DiskList *dlist;

{
	switch (*buff1)
	{
		case 2L :
		{
			switch (*(buff1+127))
			{

				case 1L: /* RootBlock */
				{
				/*	printf("Type : RootB , bloc #%d\n",n);*/
					dlist=(struct DiskList *)Preparing(sizeof(struct SRootB),dsklist);
					dlist->dl_NextB=0;
					dlist->dl_Bloc=n;
					dlist->dl_Bloc2=n;
					dlist->dl_Data=0;
					dlist->dl_Type=3;
					dlist->dl_types.dl_type3.reserved1=0;
					dlist->dl_AdrB=buff1;
					break;
				}

				case -3L: /* File Header Block */
				{
				/*	printf("Type : File HeaderB , bloc #%d\n",n);*/
					dlist=(struct DiskList *)Preparing(sizeof(struct SFileHderB),dsklist);
					dlist->dl_NextB=0;
					dlist->dl_Bloc=n;
					dlist->dl_Bloc2=0;
					dlist->dl_Type=-1;
					dlist->dl_types.dl_type1.dl_Parent=((struct FileHeaderB*)buff1)->ParentDir;
					dlist->dl_types.dl_type1.dl_NextHash=((struct FileHeaderB*)buff1)->NextHash;
					dlist->dl_types.dl_type1.dl_Extension=((struct FileHeaderB*)buff1)->Extension;
					dlist->dl_AdrB=buff1;
					dlist->dl_Data=isinfo(buff1);
					break;
				}

				case 2L: /* User Directory Block */
				{
				/*	printf("Type : User DirB , bloc #%d\n",n);*/
					dlist=(struct DiskList *)Preparing(sizeof(struct SUserDirB),dsklist);
					dlist->dl_NextB=0;
					dlist->dl_Bloc=n;
					dlist->dl_Bloc2=0;
					dlist->dl_Type=4;
					dlist->dl_types.dl_type4.dl_Parent=((struct UserDirB*)buff1)->ParentDir;
					dlist->dl_types.dl_type4.dl_NextHash=((struct UserDirB*)buff1)->NextHash;
					dlist->dl_AdrB=buff1;
					dlist->dl_Data=isinfo(buff1);
					break;
				}

				default :
				{
					printf("Type : UnknownB1 , bloc #%d\n",n); 
					dlist=(struct DiskList *)Preparing(sizeof(struct SUnknownB),dsklist);
					dlist->dl_NextB=0;
					dlist->dl_Bloc=n;
					dlist->dl_Bloc2=n;
					dlist->dl_Data=0;
					dlist->dl_Type=-2;
					dlist->dl_AdrB=buff1; 
					break;
				}
			}
			break;
		}

		case 16L: if (*(buff1+127)==-3) /* FileList Block */
		{
		/*	printf("Type : File ListB , bloc #%d\n",n);*/
			dlist=(struct DiskList *)Preparing(sizeof(struct SFileListB),dsklist);
			dlist->dl_NextB=0;
			dlist->dl_Bloc=n;
			dlist->dl_Bloc2=0;
			dlist->dl_Data=0;
			dlist->dl_Type=13;
			dlist->dl_types.dl_type13.dl_FileHeader=((struct FileListB*)buff1)->ParentFH;
			dlist->dl_types.dl_type13.dl_Extension=((struct FileListB*)buff1)->Extension;
			dlist->dl_AdrB=buff1;
			break;
		}

		case 8L: /* Data Block */
		{
/*		printf("Type : DataB , bloc #%d\n",n);*/
		dlist=(struct DiskList *)Preparing(sizeof(struct SDataB),dsklist);
		dlist->dl_NextB=0;
		dlist->dl_Bloc=n;
		dlist->dl_Bloc2=0;
		dlist->dl_Data=0;
		dlist->dl_Type=8;
		dlist->dl_types.dl_type8.dl_FileHeader=((struct DataB*)buff1)->HeaderKey;
		dlist->dl_types.dl_type8.dl_NextData=((struct DataB*)buff1)->NextDataBlock;
		dlist->dl_AdrB=buff1;
		break;
		}
	
		default :
		{
/*			printf("Type : UnknownB2 , bloc #%d\n",n); *//* Par ex le B BitMap */
			if (n==VarAdr->BitMap) break;
			dlist=(struct DiskList *)Preparing(sizeof(struct SUnknownB),dsklist);
			dlist->dl_NextB=0;
			dlist->dl_Bloc=n;
			dlist->dl_Bloc2=n;
			dlist->dl_Data=0;
			dlist->dl_Type=-2;
			dlist->dl_AdrB=buff1;
			break;
		}
	}
}

Preparing(structsize,adr)
LONG structsize;
struct DiskList *adr;

{
static struct DiskList *Sadr=0;

	if (Sadr==0) Sadr=adr; else adr=Sadr;
	if (adr->dl_NextB==0)
	{
		adr->dl_NextB=(struct DiskList *)AllocMem(structsize,MEMF_PUBLIC);
		if (adr->dl_NextB==0)
		{
			puts("Pas de mem pour Preparing()");
			exit(0);
		}
		Sadr=adr->dl_NextB;
		return(adr->dl_NextB);
	}
	else return(Preparing(structsize,adr->dl_NextB));
}

FreeDiskList()
{
struct DiskList *dlist;
LONG	length=0;
APTR	tmp;

	dlist=dsklist;
	do
	{
		length=Length(dlist);
		tmp=dlist->dl_NextB;
		if (dlist && length) FreeMem(dlist,length); 
		dlist=tmp;
	}
	while (dlist->dl_NextB!=0);
	FreeMem(dlist,Length(dlist));	
	dsklist=0;

}

Length(dlist)
struct DiskList *dlist;
{
LONG length;
	switch (dlist->dl_Type)
	{
		case -2L: length=sizeof(struct SUnknownB); break;
		case 13L: length=sizeof(struct SFileListB); break;
		case  8L: length=sizeof(struct SDataB); break;
		case  4L: length=sizeof(struct SUserDirB); break;
		case  3L: length=sizeof(struct SRootB); break;
		case -1L: length=sizeof(struct SFileHderB); break;
		case  0L: length=sizeof(struct SinitB); break;
		default : length=0; puts("err in FreeDiskList"); break;
	}
	return(length);	
}

CheckDosStruct()
{
struct DiskList *dlist;
WORD n;
struct RootB *adr;

	dlist=(APTR)whereB(880);
	if (dlist==0) NotSet(880);
	else


	{	adr=(struct RootB *)dlist->dl_AdrB;
		for (n=0;n<72;n++) /* 72 compris puisque n++ */
		{
			explore(adr->HashTable[n],880);
		}
	}
}

/*	explore: suit une chaine de Hash à partir du bloc header en paramètre */

explore(bloc,parent)
WORD bloc;
WORD parent;
{
struct DiskList *dlist; 
WORD n,nextbloc;
struct UserDirB *adr;

	if (bloc==0) return(0);
	/*printf("dans explore bloc: %d, parent: %d\n",bloc,parent);*/
	dlist=VarAdr->disklist;
	while (dlist->dl_Bloc!=bloc && dlist!=0)		dlist=dlist->dl_NextB;

	if (dlist==0) NotSet(bloc,parent);
	else

	if (dlist->dl_Type!=-1 && dlist->dl_Type!=4)
	{
		printf ("ERR : Block #%d SHOULD BE Header or UserDir block\n",bloc);
		err++;
	}

	if (dlist->dl_types.dl_type4.dl_Parent!=parent)
	{
		printf("ERR : Link Error between block #%d and #%d\n",bloc,parent);
		err++;
	}

	if (dlist->dl_Type==-1) CheckHeader(dlist);
	if (dlist->dl_Type==4) 
	{
		adr=(struct UserDirB *)dlist->dl_AdrB;
		for (n=0;n<72;n++)
		{
			explore(adr->HashTable[n],bloc);	
		}
	}

	if (nextbloc=dlist->dl_types.dl_type1.dl_NextHash==0) return(0);
	else explore(nextbloc,parent);
}

CheckHeader(dlist)
struct DiskList *dlist;
{
WORD bloc,n;
struct FileHeaderB *adr;
BYTE errfile=0;

	bloc=dlist->dl_Bloc;
	/*printf("dans CheckHeader bloc: %d\n",bloc);*/
	adr=dlist->dl_AdrB;

	for (n=0;n<72;n++)
	{
		errfile+=CheckLink(adr->DataTable[n],bloc);
	}
	if (adr->Extension) CheckFList(adr->Extension,bloc);


	if (errfile) badFile(adr->FileName);
	return(0);
}
CheckFList(bloc,fhb)
WORD bloc,fhb;
{
struct SFileListB *dlistFL;
struct FileListB  *adr;
BYTE   errfile=0;
WORD   n;

	dlistFL=(APTR)whereB(bloc);
/*	printf("dans CheckFList bloc: %d, pour FHB: %d\n",bloc,fhb);*/
	if (dlistFL==0) NotSet(bloc,fhb);
	else{
	if (dlistFL->dl_Type!=13)
	{
		printf ("ERR : Block #%d SHOULD BE FileList block\n",bloc);
		err++;
		errfile++;
	}
	else
	{
		adr=(struct FileListB *)dlistFL->dl_AdrB;
		if (adr->ParentFH!=fhb)
		{
			printf("ERR : Link Error between block #%d and #%d\n",bloc,fhb);
			err++;
		}
		for (n=0;n<72;n++)
		{
			errfile+=CheckLink(adr->DataTable[n],fhb);
		}
	}
	if (adr->Extension) errfile+=CheckFList(adr->Extension,fhb);
	}
	return(errfile);
}

CheckLink(dataB,fhB)
WORD dataB,fhB;
{
struct SDataB *dlist;
BYTE errdata=0;

	if (dataB==0) return(0);
/*	printf("dans CheckLink data : %d, fh : %d\n",dataB,fhB);*/
	dlist=(struct SDataB *)VarAdr->disklist;
	while (dlist->dl_Bloc!=dataB && dlist!=0)		dlist=dlist->dl_NextB;

	if (dlist==0) NotSet(dataB,fhB);
	else

	if (dlist->dl_Type!=8)
	{
		printf ("ERR : Block #%d SHOULD BE data block (pointed by FHB #%d)\n",dataB,fhB);
		err++;
		errdata=1;
	}
	else
	if (dlist->dl_type8.dl_FileHeader!=fhB)
	{
		printf("ERR : Link Error between block #%d and #%d\n",fhB,dataB);
		err++;
		errdata=1;
	}
	return(errdata);
}

badFile(adrName)
BYTE *adrName;
{
BYTE length;
BYTE buff[30];

	length=*adrName;
	adrName++;
	strncpy(buff,adrName,length);
	buff[length]=0;
	printf("File %s contains errors\n",buff);
}

NotSet(bloc,where)
WORD bloc,where;
{
	printf("Block #%d Not Found ! (not unset in BitMap), in %p\n",bloc,where);
	err++;
}

whereB(bloc)
WORD bloc;
{
struct DiskList *dlist;

	dlist=VarAdr->disklist;
	while (dlist->dl_Bloc!=bloc && dlist!=0) dlist=dlist->dl_NextB;
	return(dlist);
}
isinfo(buff)
BYTE *buff;
{
BYTE name[30],length,n;


	length=buff[108*4];
	strncpy(name,buff+108*4+1,length);
	name[length]=0;
	for (n=0; n<length && name[n] != '.';++n);
	if (stricmp(name+n,".info")==0)
	return(1);
	else return(0);
}

Optimize()
{
WORD UDB[100];
WORD i=1;

	UDB[0]=880;
	UDB[1]=0;
	/*printf("UDB[0] : %d\n",UDB[0]);*/
	while (UDB[0])
	{if (!arrange(UDB,&i)) break;} 

}

arrange(UDB,i)
WORD UDB[],*i;
{
static WORD gfree=882,dfree=2;
struct SUserDirB *udbf,*udbp,*fhB;
struct UserDirB  *adrf;
struct UserDirB  *adrp;
/*struct DiskList	 *hdb;*/
struct FileHeaderB *adrh;
WORD   UDBP,n,bloc,parent;

/*	printf("in arrange() UDB[0] : %d, i : %d\n",UDB[0],*i);*/
	udbf=(APTR)whereB(UDB[0]);
	if (udbf==0) {NotSet(UDB[0],0); return(0);}

	for (n=0;n<*i;n++) UDB[n]=UDB[n+1];	/* On s' occuppe de UDB[0] donc on le retire de la liste */
	UDB[*i]=0;
	(*i)--;

	UDBP=udbf->dl_type4.dl_Parent;

	if (UDBP)	/* null si Root */
	{
		udbp=(APTR)whereB(UDBP);
		if (udbp==0) {NotSet(UDBP,1); return(0);}
	}

	adrf=udbf->dl_AdrB;
	/*printf("adrf : %p\n",adrf);*/

	if (UDBP)	/* Cas du User Dir B */
	{
		adrp=udbp->dl_AdrB;
		parent=adrf->ParentDir=udbp->dl_Bloc2;	/* Les Parents sont tjs déplacés avt */
	}

	for (n=71;n!=-1;n--)
	{
	
		bloc=adrf->HashTable[n];
		if (bloc)
		{
			adrf->HashTable[n]=gfree;
			do
			{
				fhB=(APTR)whereB(bloc);
				if (fhB==0) {NotSet(bloc,2); break;}

				if (fhB->dl_Type==-1)
				{
					gfree=MoveHderB(bloc,gfree,&dfree,UDB,i);
				}
				else
				if (fhB->dl_Type==4)
				{
					gfree=MoveUserDirB(bloc,gfree,parent);
					UDB[(*i)++]=bloc;
					UDB[*i]=0;
				}

				adrh=(struct FileHeaderB *)fhB->dl_AdrB;
				bloc=adrh->NextHash;
				adrh->NextHash= (bloc ? gfree : 0);
				/*printf("adrh: %p,bloc: %d\n",adrh,bloc);*/
			}
			while (bloc);
		}
	}
	/*printf("gfree: %d dfree: %d\n",gfree,dfree);*/
	GFree=gfree;
	DFree=dfree;
	return(1);
}




Inc(datafree)
WORD *datafree;
{
#ifdef DEBUG
/*	printf("in Inc() datafree: %d\n",*datafree);*/
#endif
	if (*datafree<879) (*datafree)++; /* Ainsi 879 est donné mais pas 880 */
	else
	{
		if (*datafree==879) *datafree=1759;
		else (*datafree)--;
	}
	return(*datafree);
}

Nextdfree(datafree)
WORD *datafree;
{
	if (*datafree<879) return(*datafree+1); 
	else
	{
		if ((*datafree+1)==880) return(1759);
		else return(*datafree-1);
	}
}

/*	MoveHderB déplace le Header spécifié du bloc lB au bloc nB.
	nB: nouveau n° de bloc.
	oB: ancien  n° de bloc.
*/

MoveHderB(oB,nB,datafree,UDB,i)
WORD nB,oB,*datafree;
WORD UDB[],*i;
{
struct SFileHderB *dlistB,*fhB;
struct FileHeaderB *adr;
struct DiskList	 *hdb;
struct FileHeaderB *adrh;
WORD   n,ret,ext,bloc,*nflB;
WORD   gfree;
LONG   AdrD;
BYTE   first;
#ifdef DEBUG
	printf("in MoveHderB() oB: %d, nB: %d,datafree: %d\n",oB,nB,*datafree);
#endif
	dlistB=(APTR)whereB(oB);
	if (dlistB==0) NotSet(oB,4);
	else
	{
		if (dlistB->dl_Type!=-1) return(0);
		adr=(struct FileHeaderB *)dlistB->dl_AdrB;
			/* On change le HeaderKey du bloc */
		adr->HeaderKey=nB;
		dlistB->dl_Bloc2=nB;

			/* On change tous les DatasB en conséquence */
		gfree=nB+1;
		first=1;

		for (n=71;n!=-1;n--)
		{	
			if (adr->DataTable[n])
			{
				if ( (dlistB->dl_Data==1) && (CLI==0) ) /* bloc de data ds gestion */
				{
					AdrD=ModifyDataHder(adr->DataTable[n],nB,gfree,adr);
					adr->DataTable[n]=gfree;
					if (first) {adr->FirstDataB=gfree; first=0;}

					gfree++;
					if (n) ModifyDataNext(AdrD,gfree);
					else ModifyDataNext(AdrD,gfree+1);
				}
				else
				{
					AdrD=ModifyDataHder(adr->DataTable[n],nB,*datafree,adr);
					adr->DataTable[n]=*datafree;
					if (first) {adr->FirstDataB=*datafree; first=0;}

					Inc(datafree);
					if (n) ModifyDataNext(AdrD,*datafree);
					else
					{
						if (FLnog) ModifyDataNext(AdrD,Nextdfree(datafree));
						else ModifyDataNext(AdrD,*datafree);
					}
				}

			}
		}

		if (adr->Extension) 
		{
			if (FLnog) 
			{
				nflB=datafree;
				ext=*datafree;
			}	
			else
			{
				nflB=&gfree;
				ext=gfree;
			}
			if (CLI)	/* pas de .info dans les blocs de gestion */
			{
				ModifyFList(adr->Extension,nB,nflB,datafree,0);
			}
			else
			if (dlistB->dl_Data==1) /* si .info alors ds les blocs de gestion */
			{
				ModifyFList(adr->Extension,nB,nflB,&gfree,1);
			}
			else
			{
				ModifyFList(adr->Extension,nB,nflB,datafree,0);
			}
			adr->Extension=ext;
		}


	}
	return(gfree);
}

ModifyDataHder(bloc,nfhB,ndataB,adrfh)
WORD bloc,nfhB,ndataB;
LONG adrfh;
{
struct SDataB *dlistD;
struct DataB  *adr;

#ifdef DEBUG
	printf("in MofifyDataHder() bloc: %d, nfhB : %d, ndataB: %d\n",bloc,nfhB,ndataB);
#endif
	dlistD=(APTR)whereB(bloc);
	if (dlistD==0) {NotSet(bloc,adrfh); exit(0);}
	else
	dlistD->dl_Bloc2=ndataB;
	dlistD->dl_type8.dl_FileHeader=nfhB;
	adr=dlistD->dl_AdrB;
	adr->HeaderKey=nfhB;
	return(adr);
}

ModifyDataNext(adr,ndataNext)
WORD ndataNext;
struct DataB *adr;
{
	if (adr->NextDataBlock)	adr->NextDataBlock=ndataNext;

}

/* Renvoie n° du prochain bloc libre */

ModifyFList(bloc,nfhB,nflB,datafree,data)
WORD bloc,nfhB,*nflB,*datafree,data;
{
struct SFileListB *dlistFL;
struct FileListB  *adr;
LONG   *AdrD;
WORD   n,ret=0,ext;
BYTE   first;
#ifdef DEBUG
	printf("in MofifyFList() bloc: %d, nfhB : %d, nflB: %d,datafree: %d\n",bloc,nfhB,*nflB,(*datafree)+1);
#endif
	dlistFL=(APTR)whereB(bloc);
	if (dlistFL==0) NotSet(bloc,5);
	else
	{
		/*printf(" Type du bloc: %d\n",dlistFL->dl_Type);*/
		dlistFL->dl_type13.dl_FileHeader=nfhB;
		dlistFL->dl_Bloc2=*nflB;
		adr=(struct FileListB *)dlistFL->dl_AdrB;
		adr->ParentFH=nfhB;
		adr->HeaderKey=*nflB;
		if (FLnog) Inc(nflB); else (*nflB)++;
		
		first=1;
		for (n=71;n!=-1;n--)
		{
			if(adr->DataTable[n])
			{
				AdrD=(APTR)ModifyDataHder(adr->DataTable[n],nfhB,*datafree,bloc);
				adr->DataTable[n]=*datafree;
				if (first) {adr->FirstDataB=*datafree; first=0;}

				if (data==0) Inc(datafree); else (*datafree)++;

				if (n)
				{
					if (data==0) ModifyDataNext(AdrD,*datafree);
					else ModifyDataNext(AdrD,*datafree+1);
				}
				else
				{
					if (FLnog) ModifyDataNext(AdrD,Nextdfree(datafree));
					else ModifyDataNext(AdrD,*datafree);
				}
			}
		}

		ext=*nflB;
		if (adr->Extension) 
		{
			ret+=ModifyFList(adr->Extension,nfhB,nflB,datafree,data);
			adr->Extension=ext;
			return(++ret);
		}

		return(ext);
	}
}

MoveUserDirB(oB,nB,parent)
WORD oB,nB,parent;
{
struct SUserDirB *dlist;
struct UserDirB *adr;
WORD n;

#ifdef DEBUG
	printf("in MoveUserDir() oB: %d, nB: %d, parent: %d\n",oB,nB,parent);
#endif
	dlist=(APTR)whereB(oB);
	if (dlist==0) NotSet(oB,6);
	else
	{	
		if (dlist->dl_Type==4)
		{

			adr=dlist->dl_AdrB;
			dlist->dl_Bloc2=nB;
			adr->HeaderKey=nB;
			for (n=71;n!=-1;n--)
			{
				if (adr->HashTable[n])
				{
					MoveHashFils(adr->HashTable[n],nB);
				}
			}
		return(nB+1);
		} else puts("err in MoveUserDirB");
	}
}

MoveHashFils(bloc,parent)
WORD bloc,parent;
{
struct DiskList *dlist;
struct  FileHeaderB *adr;

#ifdef DEBUG
	printf("in MoveHashFils() bloc: %d, parent: %d\n",bloc,parent);
#endif
	dlist=(APTR)whereB(bloc);
	if (dlist==0) {NotSet(bloc,7); return(0);}

	if (dlist->dl_Type==-1) adr=dlist->dl_AdrB;
	if (dlist->dl_Type==4) adr=dlist->dl_AdrB;

	dlist->dl_types.dl_type4.dl_Parent=parent;
	adr->ParentDir=parent;
}

ChangeBlocks()
{
struct DiskList *dlist;

	dlist=(struct DiskList *)VarAdr->disklist;

	while (dlist) 
	{
		dlist->dl_Bloc=dlist->dl_Bloc2;
		dlist=dlist->dl_NextB;
	}

}

MemToDisk()
{
WORD n,bitmap;
LONG offset,*adrB,*empty,ddrive,A;
BYTE *BuffT,write,length;
WORD c,Form;
struct DiskList *dlist=0;
struct RootNode *RN;
struct RootB *adr;

	if (sourceDrive==destDrive) {puts("Now insert dest disk and RETURN");
	while ( (c=getchar()) != 10) ;}

	MtrOn(destDrive);
	MtrOff(destDrive);
	if (isInserted(destDrive)) {puts("YOU MUST insert DESTINATION disk and RETURN");
	while ( (c=getchar()) != 10) ;
	while (isInserted(destDrive)) fflush(stdout); }

	if (isProtected(destDrive)) {puts("UnProtect DESTINATION disk and press RETURN");
	while ( (c=getchar()) != 10) ;
	while (isProtected(destDrive)) fflush(stdout);}
	

	offset=2*512L;	/* Le boot Block est déja dans le Buffer de Track */
	BuffT=BT;
	/*	On met à jour la date de dernière modif de la disquette */
	dlist=(APTR)whereB(880);
	if (dlist==0) NotSet(880,8);
	adr=dlist->dl_AdrB;
	RN=(struct RootNode *)(DOSBase->dl_Root);
	adr->Mday=RN->rn_Time.ds_Days;
	adr->Mmin=RN->rn_Time.ds_Minute;
	adr->Mtick=RN->rn_Time.ds_Tick;

	length=adr->DiskName[0];
	adr->DiskName[0]=length+1;
	adr->DiskName[length+1]='.';


	bitmap=VarAdr->BitMap;
	empty=VarAdr->Empty;
	ddrive=destDrive;
	A=('F'<<24|'B'<<16|'J'<<8|'.');

	for (n=0;n<128;n++)
	{
		empty[n]=A;
	}

	/*printf("buffer a l' adresse %p\n",empty);*/
	putchar(13);

	Form=FORMAT;
	/*printf("Form : %d, VarAdr->Format: %ld\n",Form,VarAdr->Format);*/
	write=Form;
	for (n=2;n<1761;n++)
	{
		if (offset==11*512) 
		{
			/*printf("(n-12): %d\n",(n-11));	*/
			if (write)	CMDonD(ddrive,TD_FORMAT,0,11*512L,BuffT,(n-11)*512L);
			offset=0L;
			if (!Form) write=0;
		}
		if (( (n-22) % 22 )==0)
		{
			printf("CYL n°: %d",n/22);
			putchar(13);
			fflush(stdout);
		}
		if (n==881) {adrB=BBM; write=1;}
		else 
		{
			dlist=(APTR)whereB(n);
			if (dlist==0) adrB=empty;
			else 
			{
				adrB=dlist->dl_AdrB;
				BuildCheckSum(adrB);
				write=1;
			}
		}	
		CopyMemQuick(adrB,BuffT+offset,512L);
		offset+=512L;
	}
} 

BuildCheckSum(Buffer)
ULONG *Buffer;
{
ULONG B;
WORD	n;
	
	Buffer[5]=0;
	n=0;
	B=0;
	while (n<=127)
	{
		B+=Buffer[n]; 
		n++;
	}
	Buffer[5]=-B;
}

BitMap()
{
struct DiskList *dlist;
struct RootB *adr;
WORD 	n;
LONG	*buffbm,SBM=0;

	buffbm=BBM;
	dlist=(APTR)whereB(880);
	if (dlist==0) NotSet(880,9);

	adr=dlist->dl_AdrB;
	adr->BitMapTable[0]=881L;
	adr->BMvalid=-1;

	/*printf("GFree: %d DFree: %d\n",GFree,DFree);*/

	for (n=0;n!=56;n++) buffbm[n]=-1L;	/* on nettoye le BitMap */
	for (n=56;n!=128;n++) buffbm[n]=0L;
	if (DFree<880)
	{
		for (n=0;n<DFree;n++) SetBinBM(n,buffbm);
	}
	else
	{
		for (n=0;n!=880;n++) SetBinBM(n,buffbm);
		for (n=DFree+1;n!=1761;n++) SetBinBM(n,buffbm);
		
	}

	for (n=880;n!=GFree;n++) SetBinBM(n,buffbm);

	buffbm[0]=0L;
	for (n=0;n!=128;n++)
	{
		SBM+=buffbm[n];
	}
	buffbm[0]=-SBM;

}

SetBinBM(bloc,adrbm)
WORD bloc;
LONG *adrbm;
{
	adrbm++;
	adrbm[(bloc-2)/32] &= ~(1<< ( (bloc-2)%32 ));
}

/* Les options. */

WBench(flag)
BYTE flag;
{
	CLI=!flag;
/*	printf("in WBench, CLI set to %d\n",!flag);*/
}

ComLineInt(flag)
BYTE flag;
{
	CLI=flag;
/*	printf("in ComLineInt, CLI set to %d\n",flag);*/
}

NoFormat(flag)
BYTE flag;
{
	VarAdr->Format=!flag;
/*	printf("in NoFormat, Format set to %d\n",VarAdr->Format);*/
}

forFDir(flag)
BYTE flag;
{
	FLnog=flag;
/*	 printf("in forFDir, FLnog set to %d\n",flag);*/
}

Check(flag)
BYTE flag;
{
	CHECK=flag;
}

help()
{
	puts("FO is FreeWare but contributions are welcomed: ");
	puts("");
	puts("Send any donations to CAMPAGNE Fabien");
	puts("                      805,  Rue des Gentianes");
	puts("                      39000 Lons Le Saunier");
	puts("                      France");
	puts("");
	puts("Now how to use FO:");
	puts("          1st argument must be SOURCE drive");
	puts("          2nd argument must be DESTINATION drive");
	puts("                        (They could be the same)");
	puts("Options:");
	puts("          -w : Worbench optimization ( all .info files (ex disk.info) are");
	puts("                near gestion-blocks)");
	puts("          -c : CLI optimization ( .info files are in the area of data-blocks)");
	puts("          -nFo or -nFormat if you use disk already formatted on destination.");
	puts("          -C : Check of the Dos Structure of Source disk before optimization.");
	puts("          -FDir : to use in conjunction with FDir (the Fast Dir by mine)");
	puts("                  (File List blocks are moved in data-area)");
	puts("");
	puts("                                 Enjoy FO.           FBJ.");
}








