
/********************************************************************
**
**   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

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 (14.12.97) © 1997 by Tadek Knapik.";

enum {DEVNAME = 0, DEVUNIT, NOFFSET, RDBONLY, DEVWRITE, RDBFORCE, FILENAME};
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])
		theoffset = * (ULONG *) argarray[NOFFSET];

	if ((theoffset >= 0) && (theoffset < RDB_LOCATION_LIMIT))
	{

#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("* Force: %s\n", argarray[RDBFORCE]? "Yes": "No");
	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

												if((ChkRDBSum(memrdb)) || argarray[RDBFORCE])
												{

													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(!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(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();
	}
	else
		printf("Invalid offset value (must be from 0 to %ld).\n", RDB_LOCATION_LIMIT-1);

}


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

