#include <dpb.h>
#include <xfbuf.h>

char *getmem();
long sizmem();

/*
	Copies a diskette to a disk file and back again, for doing
	software distribution, etc. 

	The /S number, if present, is used when putting a file image
	onto a diskette; the data is checksummed as it is written and 
	compared to the /S value, ie. see if the disk image is corrupted
	or incorrect.

	RAWDISK d: file /r/w ddddd/s

	Where:
		d:	Is the drive containing the IBM3740 diskette,
		file	Is the MSDOS file to copy to. Any existing file
			of the same name will be deleted first.
		/r	disk to file
		/w	file to disk
		/s	correct file checksum (when making diskettes)
		/v	verify (read after write)
*/

#define RO 0x01		/* Read only attribute. */
#define HI 0x02		/* Hidden attribute, */
#define SY 0x04		/* System attribute, */
#define VO 0x08		/* Volume ID */
#define DI 0x10		/* directory */
#define AR 0x20		/* Archive, */
#define TG1 0x40	/* Tag #1 */
#define TG2 0x80	/* Tag #2 */

char *buff;
unsigned buffsize;

_stack = 2000;

main(argc,argv)
int argc;
char **argv;
{
char sw[20];
char c,*cp;
unsigned i,sec,n,t;

unsigned count;
unsigned total_secs;		/* data on the diskette installed */
unsigned secs_per_buf;
unsigned sector;
unsigned sec_size;

unsigned chksum;		/* checksum from cmd line */
int diskette;			/* drive to read from, */
int file;			/* handle of created file, */
int cmderr;			/* command line parsing error, */
char verflg,rdflg,wrtflg,onceflg; /* command line switches */
int error_exit;

	printf("Diskette Mass Copier  Copyright Tom Jennings 16 May 90\r\n");

	allmem();
	fastfile(2);				/* get room for files */
	buffsize= sizmem();
	if (buffsize < 1024) {
		printf("**** OOPS: not enough memory (%u) ****\007\n\n",buffsize);
		exit(1);
	}
	buff= getmem(buffsize);

	verflg= onceflg= rdflg= wrtflg= cmderr= chksum= 0; /* assume good command line, */
	diskette= -1;					/* no drive spec yet */
	--argc; ++argv;

	if (argc < 2) {				/* missing something */
		printf("Try:\r\n");
		printf("    rawdisk d: filename/r         Generate image file from drive d: sectors\r\n");
		printf("    rawdisk d: filename/w         Generate a diskette from image file\r\n");
		printf("    rawdisk d: filename/w nnnnn/s As above plus checksum data\r\n");
		printf("    rawdisk d: filename ... /d    As above, but run only once\r\n");
		printf("    rawdisk d: filename ... /v    Verify; read all files after write\r\n");
		exit(1);
	}

	if ((*argv)[1] == ':') {		/* get drive letter */
		diskette= tolower(**argv) - 'a';
		if (_getdpb(diskette + 1,&dpb) == -1) {
			printf("No such drive %c:",diskette + 'A');
			exit(1);
		}
	} else {
		printf("Diskette drive spec must be first!\r\n");
		exit(1);
	}

	--argc; ++argv;
	cpyarg(buff,*argv);		/* get the filename */

	while (argc) {			/* (might be switches on fname) */
		strip_switch(sw,*argv);	/* do all the switches */
		switch (tolower(*sw)) {
			case 'w': wrtflg= 1; break;
			case 'r': rdflg= 1; break;
			case 's': chksum= atoi(*argv); break;
			case 'd': onceflg= 1; break;
			case 'v': verflg= 1; break;
			default: printf("/%c is not a valid switch!\r\n",*sw);
				exit(1);
		}
		--argc; ++argv;
	}
	if (rdflg + wrtflg != 1) {
		printf("You must specify /R or /W\r\n");
		exit(1);
	}

	if (dpb.media == 0xf8) {
		printf("RAWDISK can only be used on removable media!\r\n");
		exit(1);
	}

	sec_size= dpb.sector_size;
	secs_per_buf= buffsize / sec_size;
	total_secs= dpb.secs_clus * dpb.max_cluster + dpb.first_sector - 2;
	sector= 0;
	error_exit= 0;				/* DOS error code */

/* Copy the diskette image to a file; generate and display a checksum. */

	if (rdflg) {
		file= creat(buff,2);
		if (file == -1) {
			printf("Can't create the file \"%s\"\r\n",buff);
			exit(1);
		}

		while (total_secs > 0) {
			count= (total_secs > secs_per_buf ? secs_per_buf : total_secs);
			total_secs -= count;
			printf("\rSectors %d - %d ",sector,sector + count - 1);
			if (_aread(diskette,sector,count,buff)) {
				printf("read error\r\n");
				error_exit= 2;
				break;
			}
			n= i= count * sec_size;			/* generate checksum */
			cp= buff; while (i--) chksum += *cp++;

			sector += count;
			if (write(file,buff,n) != n) {
				printf("\r\nError writing file \"%s\"\r\n",argv[2]);
				error_exit= 3;
				break;
			}
		}
		printf("\r\n\r\n\r\n\r\nImage file checksum: %lu\r\n",0L + chksum);
	}

/* Generate diskette(s) from the image file; loop for each diskette. */

	if (wrtflg) {
		file= open(buff,0);
		if (file == -1) {
			printf("Can't find image file \"%s\"\n",buff);
			exit(2);
		}
		printf("\r\n\r\n");
		n= 0;					/* clear checksum */

		do {
			while (keyhit());		/* clear typeahead */
			printf("Hit a key to start (Control-C to quit): ");
			if (lconin() == 3) {
				error_exit= 1;
				break;
			}
			printf("\r\n");

			lseek(file,0L,0);		/* rewind */
			sec= 0;				/* first sector */

			for (t= total_secs; t > 0;) {
				count= (t > secs_per_buf ? secs_per_buf : t);
				i= count * sec_size;	/* byte count */
				if (read(file,buff,i) != i) {
					printf("Short image file!\r\n\07");
					error_exit= 3;
					break;
				}
				if (chksum) {		/* first time, */
					cp= buff; 	/* check against the checksum */
					while (i--) n += *cp++;
				}
				printf("\rSectors %d - %d ",sec,sec + count - 1);
				if (_awrite(diskette,sec,count,buff)) {
					printf("Write error!\r\n\07");
					chksum= 0;	/* not valid */
					error_exit= 2;
					break;
				}
				t -= count;
				sec += count;
			}

			if (chksum && (n != chksum)) {
				printf("Checksum error! Is: %lu, supposed to be: %lu\r\n\07",0L + n, 0L + chksum);
				error_exit= 2;
				break;
			}
			if (verflg) dir(diskette,"");	/* go check it */
			chksum= 0;			/* do it only once */
			printf("\r\n\r\n");

		} while (!onceflg && !error_exit);
	}
	close(file);
	exit(error_exit);
}

/* Verify a diskette by reading all of the files. */

/* Recursively find each directory, and execute all the weird options. Returns
the number of bytes tallied. */

dir(diskette,s) 
int diskette;
char *s;
{
char path[80];
int f,i;
struct _xfbuf xfbuf;

	sprintf(path,"%c:\\%s*.*",diskette + 'A',s);	/* full search path */

	i= 0;						/* _find flag, file count */
	xfbuf.s_attrib= 0xff;
	while (_find(path,i,&xfbuf)) {
		++i;					/* count a file */
		strcpy(buff,s);				/* build full filename */
		strcat(buff,xfbuf.name);		/* to get stats on it */
		if (xfbuf.f_attrib & (VO | DI)) continue;
		if (*xfbuf.name == '.') continue;

		strip_path(buff,path);			/* make filename */
		strcat(buff,xfbuf.name);		/* for file read */
		printf("Verify: %s ",buff);
		f= open(buff,0);
		if (f == -1) {
			printf("Missing?!\r\n");
			return;
		}
		while (read(f,buff,buffsize));		/* just read data */
		close(f);
		printf("OK\r\n");
	}

	i= 0; xfbuf.s_attrib= DI;			/* directory attribute, */
	while (_find(path,i++,&xfbuf)) {
		if (*xfbuf.name == '.') continue;	/* skip . and .. */
		if (! (xfbuf.f_attrib & DI)) continue;

		strcpy(path,s);				/* build full pathname */
		strcat(path,xfbuf.name);		/* for next iteration */
		strcat(path,"\\");
		dir(diskette,path);			/* do it, tally total */
	}
}

