
/********************************************************************
**
**   Rigids v1.0 beta © 1997 by Tadek Knapik. Freeware.
**   Send comments, bug reports etc to 
**   <tadek@malenstwo.iinf.polsl.gliwice.pl>
**
**   This piece of code is probably something I sould be ashame of.
** But it's my first "platform" C program, first disk access, first
** job I had deadline on (let's say so:) and... still I decided to
** show it. Comments appreciated, but please don't hurt me :)
**
********************************************************************/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>

#include <dos/dos.h>
#include <exec/io.h>
#include <exec/memory.h>
#include <devices/hardblocks.h>
#include <devices/scsidisk.h>
#include <devices/trackdisk.h>

#include <proto/dos.h>
#include <proto/exec.h>

/*
#define DEBUG
*/

#define IDNAME_OLDRIGID 0xD2CFCCC4			/* 'ROLD' */
#define BYTESONBLOCK 512
#define DEVNAME 0
#define DEVUNIT 1
#define NOFFSET 2
#define RDBONLY 3
#define DEVWRITE 4
#define RDBFORCE 5
#define FILENAME 6

struct	RigidDiskBlock *memrdb = NULL;
struct	FileInfoBlock *fib = NULL;
struct	RDArgs *rdargs = NULL;			/* for ReadArgs() */
struct	MsgPort *ioport = NULL;
struct	IOExtTD *ioreq = NULL;
long	*buffer = NULL;
long	*memdata = NULL;
long	*iodata = NULL;
long	theunit = NULL;
long	theoffset = NULL;
long	rdbstart = NULL;
long	rdbsize = NULL;
long	rdbshift = NULL;
long	rdbblock = NULL;
long	current = NULL;
int		scsiok = NULL;
int		writeok = NULL;
int		rdbok = NULL;

long BytesPerBlock = BYTESONBLOCK;		/* fixed as for now, but... */

char DevName[80] = "scsi.device";

char argstring[] = "D=DEVICE/K,U=UNIT/K/N,O=OFFSET/K/N,RDB=RDBONLY/S,WRITE/S,FORCE/S,F=FILE/A";
char verstring[] = "$VER: Rigids v1.0b (10.12.97) © 1997 by Tadek Knapik.";

long argarray[] = {0, 0, 0, 0, 0, 0, 0};

void ParseBBB(struct BadBlockBlock *BBB);
void ParsePB(struct PartitionBlock *PB);
void ParseFHB(struct FileSysHeaderBlock *FHB);
void ParseLSB(struct LoadSegBlock *LSB);
long CountChkSum(ULONG *data, ULONG nr);
int ChkRDBSum(struct RigidDiskBlock *RDB);
int ChkBBBSum(struct BadBlockBlock *BBB);
int ChkPBSum(struct PartitionBlock *PB);
int ChkFHBSum(struct FileSysHeaderBlock *FHB);
int ChkLSBSum(struct LoadSegBlock *LSB);

void OpenDev(void);
void CloseAll(void);
int scandev(long block, long *rdbfound);
int SumOK(ULONG *p);

void main(int argc, char **argv)
{

	BPTR thefile;						/* file pointer */

	if(!(rdargs=ReadArgs(argstring, argarray, NULL)))
	{
		printf("   %s\n", &verstring[6]);
		return;
	}

	if (argarray[DEVNAME])
		strcpy(DevName, (STRPTR) argarray[DEVNAME]);

	if (argarray[DEVUNIT])
		theunit = * (ULONG *) argarray[DEVUNIT];

	if ((argarray[NOFFSET]) && (* (ULONG *) argarray[NOFFSET] < RDB_LOCATION_LIMIT))
		theoffset = * (ULONG *) argarray[NOFFSET];

#ifdef DEBUG
	printf("* Device: %s\n", DevName);
	printf("* Unit: %ld\n", theunit);
	printf("* Offset: %ld\n", theoffset);
	printf("* RDBOnly: %s\n", argarray[RDBONLY]? "Yes": "No");
	printf("* Mode: %s\n", argarray[DEVWRITE]? "Write": "Read");
	printf("* File: %s\n", argarray[FILENAME]);
	printf("\n");
#endif

	OpenDev();

#ifdef DEBUG
	printf("* %s unit %ld %s\n", DevName, theunit, scsiok? "opened": "not opened");
#endif

	if(scsiok)
	{
		if((buffer=malloc(BytesPerBlock)))			/* buffer to read blocks into */
		{
			if(argarray[DEVWRITE])			/* write */
			{
				if(thefile=Open((STRPTR) argarray[FILENAME], MODE_OLDFILE))
				{
					if(fib=malloc(sizeof(struct FileInfoBlock)))
					{
						if(ExamineFH(thefile, fib))
						{
							if(memdata=malloc(fib->fib_Size))
							{
								if((fib->fib_Size) && !((fib->fib_Size)%BytesPerBlock))
								{
									if(FRead(thefile, (STRPTR) memdata, (ULONG) fib->fib_Size, (ULONG) 1))
									{
										current = 0;
										memrdb = (struct RigidDiskBlock *) memdata;

										while((current < (fib->fib_Size)/BytesPerBlock) && (current < RDB_LOCATION_LIMIT))
										{
											if(memrdb->rdb_ID==IDNAME_RIGIDDISK)
												break;

											/* to the next "block" */

											memrdb += BytesPerBlock/sizeof(struct RigidDiskBlock);
											++current;
										}

										if(memrdb->rdb_ID==IDNAME_RIGIDDISK)
										{
#ifdef DEBUG
	printf("* RDB found in the file on \"block\" %ld.\n", current);
#endif

/* to know when the user has specified offset 0, and when he/she did not */

											if(!argarray[NOFFSET])
												theoffset = -1;

											if(argarray[RDBONLY] || (theoffset!=-1))
											{
												argarray[RDBONLY] = TRUE;
												iodata = (long *) memrdb;
												rdbsize = (fib->fib_Size)-(current*BytesPerBlock);
											}
											else
											{
												iodata = memdata;
												rdbsize = (fib->fib_Size);
											}

/* data write start is counted from HighRDSKBlock and RDB size, not in-file position */
/* rdbstart and rdbsize are start and size of all data, not RDB structure only! */

											if((rdbstart=(memrdb->rdb_HighRDSKBlock)-(rdbsize/BytesPerBlock)+1) < 0)
												rdbstart = 0;

/* real starting block of RDB, needed for checksum checking */
/* when OFFSET specified, fixed later */

											rdbblock = ((UBYTE *)memrdb-(UBYTE*)memdata)/BytesPerBlock;

/* RDB shifting and checksumming all lists */

											if(theoffset!=-1)
											{

												rdbshift = rdbstart-theoffset;
#ifdef DEBUG
	printf("* Shift value is %ld\n", rdbshift);
#endif

												if(memrdb->rdb_SummedLongs <= 555)
												{
#ifdef DEBUG
	printf("%ld longs to checksum in RDB.\n", memrdb->rdb_SummedLongs);
#endif

													if(memrdb->rdb_BadBlockList != -1)
													{
														ParseBBB((struct BadBlockBlock *) &((UBYTE *)memrdb)[((memrdb->rdb_BadBlockList)-rdbstart)*BytesPerBlock]);
														memrdb->rdb_BadBlockList -= rdbshift;
													}

													if(memrdb->rdb_PartitionList != -1)
													{
														ParsePB((struct PartitionBlock *) &((UBYTE *)memrdb)[((memrdb->rdb_PartitionList)-rdbstart)*BytesPerBlock]);
														memrdb->rdb_PartitionList -= rdbshift;
													}

													if(memrdb->rdb_FileSysHeaderList != -1)
													{
														ParseFHB((struct FileSysHeaderBlock *) &((UBYTE *) memrdb)[((memrdb->rdb_FileSysHeaderList)-rdbstart)*BytesPerBlock]);
														memrdb->rdb_FileSysHeaderList -= rdbshift;
													}

													if(memrdb->rdb_DriveInit != -1)
													{
														memrdb->rdb_DriveInit -= rdbshift;
													}

													memrdb->rdb_HighRDSKBlock -= rdbshift;

													memrdb->rdb_ChkSum = 0;
													memrdb->rdb_ChkSum = CountChkSum((ULONG *) memrdb, (ULONG) memrdb->rdb_SummedLongs);

													rdbstart = theoffset;
													rdbblock = theoffset;

												}
												else
													printf("Can't calculate checksum.\n");

											}

#ifdef DEBUG
	printf("* Checksum is %s.\n", SumOK((ULONG *) memrdb)? "OK": "wrong");
	printf("* Writing RDB at block %ld, size %ld\n", rdbstart, rdbsize);
#endif

											current=0;
											while((scandev(current, &theoffset)) && (theoffset<rdbstart))
											{
#ifdef DEBUG
	printf("* Replacing old 'RDSK' with 'ROLD' at block %ld\n", theoffset);
#endif

												buffer[0] = IDNAME_OLDRIGID;
												ioreq->iotd_Req.io_Command = CMD_WRITE;
												ioreq->iotd_Req.io_Flags = 0;
												ioreq->iotd_Req.io_Data = buffer;
												ioreq->iotd_Req.io_Length = BytesPerBlock;
												ioreq->iotd_Req.io_Offset = theoffset * BytesPerBlock;
												DoIO((struct IORequest *) ioreq);
												current = theoffset+1;
											}

											ioreq->iotd_Req.io_Command = CMD_WRITE;
											ioreq->iotd_Req.io_Flags = 0;
											ioreq->iotd_Req.io_Data = iodata;
											ioreq->iotd_Req.io_Length = rdbsize;
											ioreq->iotd_Req.io_Offset = rdbstart * BytesPerBlock;


											if((ChkRDBSum(memrdb)) || argarray[RDBFORCE])
											{
												if(!rdbok)
													printf("Checksum is corrupt. Forcing write...\n");

												if(!DoIO((struct IORequest *) ioreq))
													printf("%ld bytes%s written at block %ld\n", rdbsize, argarray[RDBONLY]? " (RDB only)": "", rdbstart);
												else
													printf("Device error. File not written.\n");
											}
											else
												printf("Checksum is corrupt. RDB not written (you can use FORCE switch though).\n");

										}
										else
											printf("RDB not found in file %s\n", (STRPTR) argarray[FILENAME]);

									}
									else
										printf("Error reading from file %s\n", (STRPTR) argarray[FILENAME]);

								}
								else
									printf("Error: size of %s is not a multipler of BytesPerBlock.\n", (STRPTR) argarray[FILENAME]);

								free(memdata);

							}
							else
								printf("Not enough memory.\n");

						}
						else
							printf("Can't get file size.\n");

						free(fib);

					}
					else
						printf("Not enough memory.\n");

					Close(thefile);

				}
				else
					printf("Can't open file %s\n", (STRPTR) argarray[FILENAME]);

			}
			else							/* read */
			{

/* first we search for the start of RDB on the disk... and read its length */

				if(!(theoffset && (theoffset < RDB_LOCATION_LIMIT)))
					theoffset = 0;

				if(scandev(theoffset, &rdbstart))			/* search for RDB on the disk */
				{

/* remember the real offset of RDB structure */

					rdbblock = rdbstart;

/* now we can open the file and fill it with the data */

					if(thefile=Open((STRPTR) argarray[FILENAME], MODE_NEWFILE))
					{
						if(!argarray[RDBONLY])
							rdbstart = theoffset;
						rdbsize = ((((struct RigidDiskBlock *)buffer)->rdb_HighRDSKBlock)-rdbstart+1) * BytesPerBlock;

#ifdef DEBUG
	printf("* RDB size is %ld bytes\n", rdbsize);
#endif

						if(memdata=malloc(rdbsize))
						{

							ioreq->iotd_Req.io_Command = CMD_READ;
							ioreq->iotd_Req.io_Flags = 0;
							ioreq->iotd_Req.io_Data = memdata;
							ioreq->iotd_Req.io_Length = rdbsize;
							ioreq->iotd_Req.io_Offset = rdbstart * BytesPerBlock;

							if(!DoIO((struct IORequest *) ioreq))
							{

/* memrdb pointer for checksumming */

								memrdb = (struct RigidDiskBlock *) &((UBYTE *)memdata)[(rdbblock-rdbstart)*BytesPerBlock];

								if(ChkRDBSum(memrdb) || argarray[RDBFORCE])
								{
									if(!rdbok)
										printf("Checksum is corrupt. Forcing read...\n");

									if(writeok=FWrite(thefile, (STRPTR) memdata, (ULONG) rdbsize, (ULONG) 1))
										printf("%ld bytes%s written to %s\n", rdbsize, argarray[RDBONLY]? " (RDB only)": "", (STRPTR) argarray[FILENAME]);
									else
										printf("Write error\n");

								}
								else
									printf("RDB checksum is corrupt. Use FORCE to read it.\n");

							}
							else
								printf("Device error. File not written.\n");

							free(memdata);

						}
						else
							printf("Not enough memory.\n");

						Close(thefile);
						if(!writeok)
							DeleteFile((STRPTR) argarray[FILENAME]);

					}
					else
						printf("Can't open file %s\n", argarray[FILENAME]);
				}
				else
					printf("Can't find RDB on the disk.\n");
			}

		}
		else
			printf("Not enough memory.\n");

	}
	else
		printf("Cant't open %s unit %ld!\n", DevName, theunit);

	CloseAll();
	return;

}


int scandev(long block, long *rdbfound)
{
	while (block < RDB_LOCATION_LIMIT)
	{
		ioreq->iotd_Req.io_Command = CMD_READ;
		ioreq->iotd_Req.io_Flags = 0;
		ioreq->iotd_Req.io_Data = buffer;
		ioreq->iotd_Req.io_Length = BytesPerBlock;
		ioreq->iotd_Req.io_Offset = block * BytesPerBlock;

		if(DoIO((struct IORequest *) ioreq))
		{
#ifdef DEBUG
	printf("* Error number %ld\n", ioreq->iotd_Req.io_Error);
#endif

			printf("Disk error. ");
			return FALSE;
		}
		else if (((struct RigidDiskBlock *)buffer)->rdb_ID==IDNAME_RIGIDDISK)
		{

			*rdbfound = block;

#ifdef DEBUG
	printf("* RDB found on block %ld\n", block);
	printf("* RDB last block is %ld\n", ((struct RigidDiskBlock *)buffer)->rdb_HighRDSKBlock);
#endif

			return TRUE;
		}
		else
			++block;

	}
	return FALSE;

}


/* list parsing... hope I didn't make a mistake here */

void ParseBBB(struct BadBlockBlock *BBB)
{
	if((BBB->bbb_Next)!=-1)
	{
		ParseBBB((struct BadBlockBlock *) &((UBYTE *) memrdb)[((BBB->bbb_Next)-rdbstart)*BytesPerBlock]);
		BBB->bbb_Next -= rdbshift;

		BBB->bbb_ChkSum = 0;
 	 	BBB->bbb_ChkSum = CountChkSum((ULONG *) BBB, (ULONG) BBB->bbb_SummedLongs);
	}
}

void ParsePB(struct PartitionBlock *PB)
{
	if((PB->pb_Next)!=-1)
	{
		ParsePB((struct PartitionBlock *) &((UBYTE *) memrdb)[((PB->pb_Next)-rdbstart)*BytesPerBlock]);
		PB->pb_Next -= rdbshift;

		PB->pb_ChkSum = 0;
	  	PB->pb_ChkSum = CountChkSum((ULONG *) PB, (ULONG) PB->pb_SummedLongs);
	}
}

void ParseFHB(struct FileSysHeaderBlock *FHB)
{
	if((FHB->fhb_Next)!=-1)
	{
		ParseFHB((struct FileSysHeaderBlock *) &((UBYTE *) memrdb)[((FHB->fhb_Next)-rdbstart)*BytesPerBlock]);
		FHB->fhb_Next -= rdbshift;
	}

	if((FHB->fhb_SegListBlocks)!=-1)
	{
		ParseLSB((struct LoadSegBlock *) &((UBYTE *) memrdb)[((FHB->fhb_SegListBlocks)-rdbstart)*BytesPerBlock]);
		FHB->fhb_SegListBlocks -= rdbshift;
	}

	FHB->fhb_ChkSum = 0;
	FHB->fhb_ChkSum = CountChkSum((ULONG *) FHB, (ULONG) FHB->fhb_SummedLongs);

}

void ParseLSB(struct LoadSegBlock *LSB)
{
	if((LSB->lsb_Next)!=-1)
	{
		ParseLSB((struct LoadSegBlock *) &((UBYTE *) memrdb)[((LSB->lsb_Next)-rdbstart)*BytesPerBlock]);
		LSB->lsb_Next -= rdbshift;

		LSB->lsb_ChkSum = 0;
		LSB->lsb_ChkSum = CountChkSum((ULONG *) LSB, (ULONG) LSB->lsb_SummedLongs);
	}
}



/* checksum checking for all RDB structures */

int ChkRDBSum(struct RigidDiskBlock *RDB)
{
	rdbok = TRUE;

	if(!SumOK((ULONG *) RDB))
	{
		rdbok = FALSE;
		return FALSE;
	}

	if((RDB->rdb_BadBlockList) != -1)
		ChkBBBSum((struct BadBlockBlock *) &((UBYTE *)memrdb)[((RDB->rdb_BadBlockList)-rdbblock)*BytesPerBlock]);

	if((RDB->rdb_PartitionList) != -1)
		ChkPBSum((struct PartitionBlock *) &((UBYTE *)memrdb)[((RDB->rdb_PartitionList)-rdbblock)*BytesPerBlock]);

	if((RDB->rdb_FileSysHeaderList) != -1)
		ChkFHBSum((struct FileSysHeaderBlock *) &((UBYTE *)memrdb)[((RDB->rdb_FileSysHeaderList)-rdbblock)*BytesPerBlock]);

	return rdbok;
}

int ChkBBBSum(struct BadBlockBlock *BBB)
{
	if(!rdbok)
		return FALSE;

	if(!SumOK((ULONG *) BBB))
		rdbok = FALSE;

	if((BBB->bbb_Next) != -1)
		ChkBBBSum((struct BadBlockBlock *) &((UBYTE *)memrdb)[((BBB->bbb_Next)-rdbblock)*BytesPerBlock]);

	return rdbok;
}


int ChkPBSum(struct PartitionBlock *PB)
{
	if(!rdbok)
		return FALSE;

	if(!SumOK((ULONG *) PB))
		rdbok = FALSE;

	if((PB->pb_Next) != -1)
		ChkPBSum((struct PartitionBlock *) &((UBYTE *)memrdb)[((PB->pb_Next)-rdbblock)*BytesPerBlock]);

	return rdbok;
}


int ChkFHBSum(struct FileSysHeaderBlock *FHB)
{
	if(!rdbok)
		return FALSE;

	if(!SumOK((ULONG *) FHB))
		rdbok = FALSE;

	if((FHB->fhb_Next) != -1)
		ChkFHBSum((struct FileSysHeaderBlock *) &((UBYTE *)memrdb)[((FHB->fhb_Next)-rdbblock)*BytesPerBlock]);

	if((FHB->fhb_SegListBlocks) != -1)
		ChkLSBSum((struct LoadSegBlock *) &((UBYTE *)memrdb)[((FHB->fhb_SegListBlocks)-rdbblock)*BytesPerBlock]);

	return rdbok;
}

int ChkLSBSum(struct LoadSegBlock *LSB)
{
	if(!rdbok)
		return FALSE;

	if(!SumOK((ULONG *) LSB))
		rdbok = FALSE;

	if((LSB->lsb_Next) != -1)
		ChkLSBSum((struct LoadSegBlock *) &((UBYTE *)memrdb)[((LSB->lsb_Next)-rdbblock)*BytesPerBlock]);

	return rdbok;
}


/* Next two functions had been written looking at David Balazic's
   RDBInformer v0.2. */

long CountChkSum(ULONG *data, ULONG nr)
{
	long i;
	long chksum = 0;

	for(i=0; i<nr; i++)
		chksum -= (*data++);
	return chksum;

}

void CloseAll(void)
{
	if(scsiok)
		CloseDevice((struct IORequest *) ioreq);
	if(ioreq)
		DeleteIORequest(ioreq);
	if(ioport)
		DeleteMsgPort(ioport);
	if (buffer)
		free(buffer);
	if (rdargs)
		FreeArgs(rdargs);
}


/* The source below is took from David Balazic's RDBInformer v0.2 */

void OpenDev(void)
{
if (ioport=CreateMsgPort())
{
#ifdef DEBUG
	printf("* MsgPort created\n");
#endif
 if(ioreq=CreateIORequest(ioport,sizeof(struct IOExtTD)))
#ifdef DEBUG
	printf("* IORequest created\n");
#endif
 {
  if(!OpenDevice(DevName, theunit, (struct IORequest *) ioreq, 0))
  {
   scsiok=TRUE;
   return;
  }
 }
}
scsiok=FALSE;
}

int SumOK(ULONG *p)
{
int i;
LONG chk=0;
ULONG nr=((struct RigidDiskBlock *)p)->rdb_SummedLongs;

if(nr>555) return FALSE;

for(i=0;i<nr;i++)
   chk+=(*p++);
if (chk) return FALSE;
return TRUE;
}

