
 
/* VirusX - by Steve Tibbett

	The complete Virus Elimination System!

		Please - if you find a new virus, Send me a copy!
		(And warn me it's on the disk!).  I want to keep
		this program current.  (Feel free to put something
		neat on the disk also!)


History:
--------

 A few weeks ago: V1.0 written.
A few days later: V1.01 released - V1.0 wrote garbage when disk was Write Prot. 
       27-Mar-88: V1.2 - Added Byte Bandit RAM detection, and disk detection.  
		  (Actually, it's well after Midnight - make that 28-Mar-88)
Just after
uploading V1.2
     to Amic BBS: Realized I put the wrong version in the ARC file - Had to
                  rename V1.2 to V1.21...

   June 15, 1988: Created V1.3 which was just V1.21 cleaned up, made a bunch smaller.

    July 8, 1988: Recieved Revenge virus Yesterday, disassembled it,
                  and added it to VirusX 1.4.  Added ViewBoot and SCA check also.

   July 24, 1988: Recieved the Byte Warrior virus, added it.  V1.5

    Aug. 1, 1988: These things are showing up Quick!  Northstar Virus
                  sent to me by Dan Mosedale.  Thanks, Dan!  V1.6

   Aug. 18, 1988: Whew, after a 2 week break, got the Obelisk
		  Softworks Crew Virus, dealt with it within an hour.
	  	  (this is becoming a habit 8-)  1.7


  Viruses Dealt With:

	Byte Bandit	- RAM and Disk checked, TD vector patched,
			  resident module destroyed, Interrupt
			  patched.
	
	SCA		- Disk checked, CoolCapture checked, fixed
			  if it points to a known SCA virus.

	Revenge		- DoIO patched, Interrupt patched, Disk
			  checked.

	Byte Warrior - 

	North Star	- Hangs around via Coolcapture, I just killed it.

	Obelisk Softworks Crew - Hangs around via CoolCapture, also
				 watches reads of DoIO() (but doesn't
				 infect EVERY disk - onlyt ones you boot
				 off of)

*/

#include <stdio.h>
#include <exec/types.h>
#include <intuition/intuition.h>
#include <devices/bootblock.h>
#include <devices/trackdisk.h>
#include <exec/execbase.h>
#include <libraries/dos.h>

/*  This stuff is for the detach module Manx provides, that's how
    VirusX can be executed in the Startup Sequence and have the 
    initial CLI window close without any problems. 

	 (but not until I get a version of 1.3 that works with it)

	*/

long _stack = 16000;
long _priority = 0;
long _BackGroundIO = 0;
char *_procname = "VirusX";


/*

	Created with Aztec C V3.6a using 32 bit ints.

	(My makefile is included in the archive)

	No longer uses Precompiled Headers, so it should compile OK.

*/

char WindowBig;
struct 		Port 		*diskport;	/* disk's port.*/
struct 		IOStdReq 	*diskreq;	/* disk's IOStdReq */
int		DisksChecked, 
		DisksInstalled, 
		VirusesFound;			/* for title bar info */
char 		titlebuffer[75];		/* Where tietie bar is kept */
int VirusBase;	/* ick, whatta name! */

/* Amount of boot code we've got bwlow (approx):  */
#define BSIZE 40

char TDName[] = "trackdisk.device";	/* Saves space */
char copystring[] = "(Copy Count on this disk: %d)"; /* Again, saves space */
char ITBodyText[80]; /* this saves mega space! */
char VN_OBELISK[] = "Obelisk";
char VN_NORTHSTAR[] = "North Star";
char VN_SCA[] = "SCA";
char VN_BYTEBANDIT[] = "Byte Bandit";
char VN_BYTEWARRIOR[] = "Byte Warrior";
char VN_REVENGE[] = "Revenge";

int ObeliskCount;
int NorthStarCount;
int SCACount;
int ByteBanditCount;
int ByteWarriorCount;
int RevengeCount;

int ChangeCount[4];		/* TD_CHANGECOUNT for all 4 drives */
int LastSum;			/* Used in the checksumming */
int CheckDrives;		/* Boolean, from the command line */
int error;			/* sort of a temporary variable sort of */

unsigned char diskbuffer[3*512];/* Everything ends up in here. 
				   I suppose I should have AllocMem'ed this,
				   but it's SO easy this way 

			Note that this is 3*512, not 2*512.  The reason
			for this is that I have heard that the SCA will
			attempt to rewrite itself whenever it sees a Read
			request of 1024 bytes... So I read 3 blocks.
			*/

/* Warning messages.  These messages get modified before being displayed
wack   (Unless you DO have a DF9:) */

char TEXTPTR[] = "Danger:  The disk in DF9: is";
char NBCTEXT[] = "Danger:  The disk in DF9: has";  /* What a waste, eh? */
char CopyText[40];

/* This is a byte by byte copy of working boot block code.  Check it
   out if you like.  This is what gets written back to the disk when you
   ask VirusX to fix a disk. */

unsigned char bootblock[] = { 'D', 'O', 'S', 0, 
0xc0, 0x20, 0x0f, 0x19, 0x00, 0x00, 0x03, 0x70, 0x43, 0xfa, 0x00, 0x18, 
0x4e, 0xae, 0xff, 0xa0, 0x4a, 0x80, 0x67, 0x0a, 0x20, 0x40, 0x20, 0x68, 
0x00, 0x16, 0x70, 0x00, 0x4e, 0x75, 0x70, 0xff, 0x60, 0xfa, 0x64, 0x6f,
0x73, 0x2e, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x00, 0x00, 0x00,
0x00, 0x00};

struct TextAttr TxtAt_Plain = {	"topaz.font", 8, FS_NORMAL, FPF_ROMFONT};


/***  Non SCA warning requester IntuiText's ***/
struct IntuiText Body2 = { 0, 1,  JAM2, 20,18, &TxtAt_Plain,
	"Nonstandard Boot Code!", NULL };
struct IntuiText Body1 = {
   0, 1,        JAM2,          20, 8, &TxtAt_Plain,      (UBYTE *)NBCTEXT, &Body2 };



/*  This one says "The disk in DFx: is"  */
struct IntuiText GenericFirstBody = {
   0, 1,        JAM2,          20, 8, &TxtAt_Plain,      (UBYTE *)TEXTPTR, 0 };



/***** Generic IntuiTexts used as of V1.7 ******/

struct IntuiText GenericDiskBody = {
   0, 1,     JAM2,          20,18,        &TxtAt_Plain,ITBodyText, &GenericFirstBody };

struct IntuiText SCAPos = {
   0, 1,     JAM2,           7,3,        &TxtAt_Plain, "Remove it", NULL };
struct IntuiText Repair = {
   0, 1,     JAM2,           7,3,        &TxtAt_Plain, "Repair it", NULL };
struct IntuiText SCANeg = {
   0, 1,    JAM2,           7,3,        &TxtAt_Plain, "Ignore it", NULL };



/*  Special cases (display copy count  */

/***** BBANDIT Requester IntuiText's ******/
struct IntuiText BBDiskbody2 = {
   0, 1,     JAM2,          20,30,        &TxtAt_Plain, CopyText, &GenericFirstBody };
struct IntuiText BBDiskbody = {
   0, 1,     JAM2,          20,18,        &TxtAt_Plain, ITBodyText, &BBDiskbody2};

/***** Revenge on Disk Requester IntuiText's ******/
struct IntuiText RevDiskbody3 = {
   0, 1,     JAM2,          20,30,        &TxtAt_Plain, CopyText, &GenericFirstBody };
struct IntuiText RevDiskbody = {
   0, 1,     JAM2,          20,18,        &TxtAt_Plain,(UBYTE *) "infected with the -Revenge- VIRUS!", &RevDiskbody3};


/***** Generic Notice - Removed from Memory ****/
struct IntuiText GRB3 = {
   0, 1,     JAM2,          20,18,        &TxtAt_Plain,(UBYTE *) "in memory, and is now disabled.  See the", NULL};
struct IntuiText GRB2 = {
   0, 1,     JAM2,          20,28,        &TxtAt_Plain,(UBYTE *) "documentation for more information!", &GRB3 };
struct IntuiText GenericRAMBody = {
   0, 1,     JAM2,          20,8,        &TxtAt_Plain, ITBodyText, &GRB2 };
struct IntuiText BBMPos = {
   0, 1,     JAM2,           7,3,        &TxtAt_Plain, " Thanks! ", NULL };

struct IntuiText Mem3 = {
   0, 1,     JAM2,          20,28,        &TxtAt_Plain,(UBYTE *) "some other utility is the cause of it.", NULL};
struct IntuiText Mem2 = {
   0, 1,     JAM2,          20,18,        &TxtAt_Plain,(UBYTE *) "This could mean a new Virus is in RAM, or", &Mem3};
struct IntuiText Mem1 = {
   0, 1,     JAM2,          20,8,        &TxtAt_Plain, 0 , &Mem2};



/***** Write Protect Error Requester IntuiText's ******/
struct IntuiText ERRBody2 = {
   0, 1,     JAM2,          20,18,        &TxtAt_Plain,(UBYTE *) "Write Protected.", NULL };
struct IntuiText ERRBody = {
   0, 1,        JAM2,          20, 8, &TxtAt_Plain,      (UBYTE *)"DISK ERROR:  Disk is", &ERRBody2 };
struct IntuiText ERRPos = {
   0, 1,     JAM2,           7,3,        &TxtAt_Plain, "Retry", NULL };
struct IntuiText ERRNeg = {
   0, 1,    JAM2,           7,3,        &TxtAt_Plain, "Cancel", NULL };


/***** Rewrite block?  Really? ******/
struct IntuiText REWBody3 = {
   0, 1,     JAM2,          20,28,        &TxtAt_Plain,(UBYTE *) "boot sectors?", NULL };
 struct IntuiText REWBody2 = {
   0, 1,     JAM2,          20,18,        &TxtAt_Plain,(UBYTE *) "rewrite that disk's boot", &REWBody3};
struct IntuiText REWBody = {
   0, 1,        JAM2,          20, 8, &TxtAt_Plain,      (UBYTE *)"Are you sure you want to", &REWBody2 };
struct IntuiText REWPos = {
   0, 1,     JAM2,           7,3,        &TxtAt_Plain, "Yes", NULL };
struct IntuiText REWNeg = {
   0, 1,    JAM2,           7,3,        &TxtAt_Plain, "No!", NULL };

struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct Window *Window;
struct IntuiMessage *Message;

int Keepgoing;		/* a boolean flag.  it's false when we want out. */
int x, y, i;		/* left over from my using Basic */

/*** The Newwindow Structure. ***/

char TITLETEXT[] = "VirusX 2.00 by Steve Tibbett";
char text[] = "DF6: Boot Sectors"; /* for display thing */

struct NewWindow NewWindow = {
	128,0,	309,10,	 0,1,		
	DISKINSERTED | CLOSEWINDOW | VANILLAKEY | NEWSIZE | MOUSEBUTTONS,	/* IDCMP Flagz */
	WINDOWDRAG | WINDOWDEPTH | RMBTRAP | WINDOWCLOSE | NOCAREREFRESH, /* Windo Flagz */	
	NULL, NULL,TITLETEXT,	/* My name.  Vous Touchez mon Name, Vous dies. */
	NULL,NULL,0,0,0,0,WBENCHSCREEN,	
	};

struct RastPort *RP;

/*********************Da Beginnin*************************/
main()
{

WindowBig = FALSE;

/* Come on, folks, is intuition ever NOT going to be available???? */
IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",0);

/* Same with GfxBase.  If GfxBase is gone, we DESERVE to crash. */
GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",0);

/*  We use the same port/request through the whole program.  Works OK. */
diskport = CreatePort(0,0);
diskreq = CreateStdIO(diskport);

Window = OpenWindow(&NewWindow);
if (Window == NULL) 
	goto Quitter;	/* No memory to open little window! */

RP = Window->RPort;

/* This does some setup stuff, I guess, eh? */
SetUp();

/*  Check for Byte Bandit, SCA, Revenge and ByteWarrior in RAM.  */
CheckMemoryForViruses();

CheckBlock();

DoLittle();	/* The main loop.  Do Little.  Ya. */

Quitter:
DeletePort(diskport);
DeleteStdIO(diskreq);

CloseLibrary(GfxBase);
CloseLibrary(IntuitionBase);
}			

/*********************/
DoLittle()
{
register int Code;	/* for storing our IntuiMessage stuff */
register int Class;
register int KG2;	/* KeepGoing 2.  Another booleean. */

KG2 = TRUE;

SetAPen(RP, 1);
SetBPen(RP, 0);
SetDrMd(RP, JAM2);

while (KG2 == TRUE)
	{
	if (WindowBig == TRUE) DoStats();

	Goober:

	WaitPort(Window->UserPort);
	Message = GetMsg(Window->UserPort);

	Class = Message->Class;
	Code = Message->Code;
	ReplyMsg(Message);
	
	if (Class == CLOSEWINDOW) 
		{
		KG2 = FALSE;
		continue;
		};

	if (Class == MOUSEBUTTONS)
		if (Code == MENUDOWN)
			{ Class = VANILLAKEY; Code = 'i'; }
			else goto Goober;

	if (Class == VANILLAKEY)
		{
		int flag;

		if (Code == 'i')
			{
			if (WindowBig == TRUE) WindowBig = FALSE;
				else WindowBig = TRUE;

			if (WindowBig == TRUE) 
				{
				SizeWindow(Window, 0, 100);
				} else SizeWindow(Window, 0, -100);

			WaitForNewSize();
			};

		if (Code == 'c')
			{
			for (x=0; x<4; x++) ChangeCount[x] = 10000;
			CheckBlock();
			}
			else
			{
			flag = ShowAscii(Code);
			if (flag == 1) CheckBlock();
			};
		};

	if (Class == DISKINSERTED) CheckBlock();
	};

CloseWindow(Window);
}


/************************************************/
/* Opens trackdisk, finds out who's out there,  */
/* and sets Changecount up accordioningly.      */
/************************************************/
SetUp()
{
for (x = 0; x < 4; x++)	/* go thru all 4 possible drives */
	{
	ChangeCount[x] = 1000;
	error = OpenDevice(TDName,x,diskreq,0);
	if (error > 0) 
		{
		ChangeCount[x] = -1;
		} else CloseDevice(diskreq);
	};

}

/*********************************************************/
/* This routine returns which drive changed disks lately */
/*********************************************************/
WhoChanged()
{
int RetVal;	/* The value we'll return */

RetVal = -1;	/* return -1 if all else fails */
for (x = 0; x < 4; x++)
	{
	if (ChangeCount[x] == -1) continue;	/* no drive here */
	error = OpenDevice(TDName,x,diskreq,0);
	if (error > 0) continue;	/* no drive here */
	
	diskreq->io_Command = TD_CHANGESTATE;
	DoIO(diskreq);
	if (diskreq->io_Actual != 0) 
		{
		CloseDevice(diskreq);
		continue;
		};

	diskreq->io_Command = TD_CHANGENUM;
	DoIO(diskreq);
	if (diskreq->io_Actual != ChangeCount[x]) 
		{
		RetVal = x;
		ChangeCount[x] = diskreq->io_Actual;
		CloseDevice(diskreq);
		goto Out;
		};

	CloseDevice(diskreq);
	};

Out:;
return(RetVal);
}

/****************************************************************/
/*  Figures out which drive changed disks (using WhoChanged(),  */
/*  And checks it.  Calling this after every DISKINSERTED is OK.*/
/****************************************************************/

CheckBlock()
{
/*  How many register vars can I use, anyway? */
register int Sum, Bootable, Virus;
register int a, Unit;
int SCA, ByteWarrior, Revenge, BBandit;
int NorthStar, Obelisk;
register unsigned int *iptr;
unsigned int *ptr;

while ((Unit = WhoChanged()) != -1)
	{
	DisksChecked++;

	SCA = FALSE;
	BBandit = FALSE;
	Revenge = FALSE;
	ByteWarrior = FALSE;
	NorthStar = FALSE;
	Obelisk = FALSE;

	/* Unit # to open is returned by "WhoChanged()" up above. */
	if (Unit == -1) return;
	error = OpenDevice(TDName,Unit,diskreq,0);
	if (error > 0) return;

	error = ReadBlock();
	CloseDevice(diskreq);
	if (error == FALSE) return;

	ptr = diskbuffer;
	iptr = diskbuffer;

	if (iptr[0] != ID_DOS_DISK) return;	/* No DOS/0 */

	Sum = 0;
	for (a=0; a<256 /*1024/4 cuz we're dealing with ptr math now*/  ; a++)
		{
		LastSum = Sum;
		Sum = Sum + ptr[a];
		if (LastSum > Sum) Sum++;	/* took me a while to figger this out */
		}

	if (Sum != 0) 
		{
		return;	/* if it's not bootable, we DONT want it! */
		};

	ptr = &diskbuffer[4];

	if (diskbuffer[0x34] == 100)
		if (diskbuffer[0xc4] == 48)
			if (diskbuffer[0xb] == 89)
				if (diskbuffer[0xf1] == 7)
					{
					VirusesFound++;
					ByteWarriorCount++;
					ByteWarrior = TRUE;
					};

	if (diskbuffer[0x2b] == '9')
		if (diskbuffer[0x2c] == '.')
			if (diskbuffer[0x2d] == '8')
				if (diskbuffer[0x2e] == '7')
					{
					VirusesFound++;
					ByteBanditCount++;
					BBandit = TRUE;	/* 9.87 is part of BBandit Virus */
					};

	/* check specifically for SCA virus */
	if (diskbuffer[8] == 'C')
		if (diskbuffer[9] == 'H')
			if (diskbuffer[10] == 'W')
					{
					SCA = TRUE;	/* CHW is part of SCA virus */
					SCACount++;
					VirusesFound++;
					};

	if (diskbuffer[0xe] == 'I')
		if (diskbuffer[0xf] == 'D')
			if (diskbuffer[0x10] == '9')
				if (diskbuffer[0x1a6] == 'f')
					{
					Revenge= TRUE;
					RevengeCount++;
					VirusesFound++;
					};

	if (diskbuffer[0x12] == 78)
		if (diskbuffer[0x13c] == 68)		
			if (diskbuffer[0x18] = 83)
				if (diskbuffer[0x19] == 116)
					{
					NorthStar = TRUE;
					NorthStarCount++;
					VirusesFound++;
					};


	if (diskbuffer[0x38] == 71)
		if (diskbuffer[0xbc] == 83)
			if (diskbuffer[0x1fb] == 100)
				if (diskbuffer[0x2d] == 80)
					{
					Obelisk = TRUE;
					ObeliskCount++;
					VirusesFound++;
					};

	/* compare boot block with real boot block.  If it's not, notify God. */
	Virus = FALSE;

	for (x = 0; x < 39; x++) /* num of bytes in bootblock */
		{
		if (diskbuffer[8+x] != bootblock[8+x])
			{	
			Virus = TRUE;
			};
		};

	/* Oh no, a Virus! */
	if (Virus == TRUE) 
		{
		NBCTEXT[23] = '0'+Unit; /* change DF9: to real drive in text */
		TEXTPTR[23] = '0'+Unit;
		error = FALSE;

		if (SCA == TRUE)
			{
			/* OH NOOOOO, an SCA virus.  Wimpo virus, compared to BBandit 
			   but it's a lot nicer code to read. */
			error = MyRequest(VN_SCA, 1);
			}
		else if (BBandit == TRUE)
			{
			/* The Byte Bandit Virus.  Tricky bugger, he WAS. Cheats, tho. */
			sprintf(CopyText, copystring, (diskbuffer[74]*256)+diskbuffer[75]);
			BuildITBodyText(VN_BYTEBANDIT, 1);
			error = AutoRequest(Window, &BBDiskbody, &SCAPos, &SCANeg, 0, 0, 380, 80);
			}
		else if (Revenge == TRUE)
			{
			/* Revenge virus.  X rated bugger, lot like Byte Bandit. */
			sprintf(CopyText, copystring, (diskbuffer[0x3f6]*256)+diskbuffer[0x3f7]);
			BuildITBodyText(VN_REVENGE, 1);
			error = AutoRequest(Window, &RevDiskbody, &SCAPos, &SCANeg, 0, 0, 380, 80);
			}
		else if (ByteWarrior == TRUE)
			{
			/* Byte Warrior.  Very 'friendly' virus.  Ez to get rid of.  */
			error = MyRequest(VN_BYTEWARRIOR, 1);
			}
		else if (NorthStar == TRUE)
			{
			/* NorthStar.  Nice virus - alerts you to others, ez to get rid of */
			MyRequest(VN_NORTHSTAR, 1);
			}
		else if (Obelisk == TRUE)
			{
			/* At least these guys are getting creative with their Graphics! */
			error = MyRequest(VN_OBELISK, 1);
			}
		else 
			{
			/* Probably just a custom boot block (or a new virus...) */
			error = AutoRequest(Window, &Body1, &SCAPos, &SCANeg, 0, 0, 320, 70);
			}
		if (error == TRUE) DoInstall(Unit); /* user wants it neutered. */
		};
	};  /* End of While Whochanged */
}

/********************************************/
/* This is where the boot code gets changed */
/********************************************/
DoInstall(un)
int un;	/* unit to write to */
{
register int x;
register int Sum;
register int err, a;

/* Rewrite disk?  Really?  */
error = AutoRequest(Window, &REWBody, &REWPos, &REWNeg, 0, 0, 320, 75);
if (error != TRUE) return;	/* user changed his brain. */

DisksInstalled++;

error = OpenDevice(TDName, un,diskreq,0);
if (error > 0) return;

trygain:

diskreq->io_Command = TD_PROTSTATUS;
DoIO(diskreq);		/* check if disk is write protected */

if (diskreq->io_Actual != 0)
	{
	error = AutoRequest(Window, &ERRBody, &ERRPos, &ERRNeg, 0, 0, 280, 75);
	if (error == TRUE) /* error is true or false, depending on user */
		{
		goto trygain;
		};
	CloseDevice(diskreq);
	return;	/* unrecoverable write protect error!!!!!!!!! */
	};


for (x = 0; x < 1024; x++)
	diskbuffer[x] = 0;	/* clear diskbuffer to zero.  clean. */

CopyMem(bootblock, diskbuffer, 50); /* Copy it over */

/* Write it ! */

	{
	error = 0;
	diskreq->io_Length = 1024; /* here we go! */
	diskreq->io_Data = &diskbuffer[0];  
	diskreq->io_Command = CMD_WRITE;
	diskreq->io_Offset = 0L;
	DoIO(diskreq);
	error = diskreq->io_Error;
	};

if (error < 19)
	{
	diskreq->io_Command = CMD_UPDATE;	/* flush buffer to disk */
	DoIO(diskreq);
	error = diskreq->io_Error;
	};

if (error < 19) 
	{
	diskreq->io_Length = 0;
	diskreq->io_Command = ETD_MOTOR;
	DoIO(diskreq);				/* turn off motor */
	error = diskreq->io_Error;
	};

CloseDevice(diskreq);

if (error > 19) 
	{
	SetWindowTitles(Window, "Error, Nothing Done.", -1);
	} 
    else
	{
	SetWindowTitles(Window, "Disk Healed.", -1);
	};

Delay(150);
SetWindowTitles(Window, TITLETEXT, -1);
}


/************************/

CheckMemoryForViruses()
{
int Temp;
struct ExecBase *ExecBase;
unsigned int *LongMemPointer;	/* Used for reading FROM memory */
unsigned short *loc;
unsigned int *ptr;
char linebuffer[80];

ExecBase = OpenLibrary("exec.library", 0);


/**************- Check for Byte Bandit (look at TD Vector) *************/
/*  (Byte Bandit isn't at a fixed location.  Depends on your RAM.      */

/* LongMemPointer = &trackdisk.device */
LongMemPointer = FindName(&ExecBase->DeviceList, TDName);
Temp = LongMemPointer;
Temp = Temp - 0x1c;
LongMemPointer = Temp; 
VirusBase = (*LongMemPointer) - 0x1b8;
LongMemPointer = VirusBase;

if (*LongMemPointer == ID_DOS_DISK)	/* klugo */
	{
	/* Ok, so we don't really remove it from memory, but we DO render
           it harmless. */
#asm
	xdef _VirusBase
	xref _geta4
	xref _LVODisable
	xref _LVOEnable

	jsr _geta4

	move.l 4,a6
	jsr _LVODisable(a6)

	move.l _VirusBase,a0

	move.w #$4e71,d0

	move.w d0,$aa(a0)	; noops
	move.w d0,$ac(a0)	
	move.w d0,$ae(a0)
	move.w d0,$b0(a0)

	move.w #$6000,$1c2(a0)	; change bxx to bra

	move.w #$6000,$2d2(a0)

	move.w #$4e75,$388(a0)
	move.w #0,$3ea(a0)
	move.w #0,(a0)

	jsr _LVOEnable(a6)

#endasm
	MyRequest(VN_BYTEBANDIT, 2);
	};

/****************- Look for Revenge Virus (at $7e000) ****************/
if (ExecBase->CoolCapture == 516192)
	{
	/* Fix the CoolCapture vector */
	ExecBase->CoolCapture = 0;

#asm
	xref _LVODisable
	xref _LVOEnable

	move.l 4,a6
	jsr _LVODisable(a6)
	
	; Get rid of his DOS\0 in memory 
	lea $7e000,a0
	move.l #0,(a0)

	; Patch his DoIO() wedge 
	move.w #$4ef9,$1e0(a0)
	move.w #$7,$1e2(a0)
	move.w #$e066,$1e4(a0)

	; Patch his Interrupt wedge
	move.w #$4ef9,$2da(a0)
	move.w #$7,$2dc(a0)
	move.w #$e06c,$2de(a0)

	jsr _LVOEnable(a6)
#endasm

	MyRequest(VN_REVENGE, 2);
	};

/******************** See if SCA is in RAM ***************************/
if (ExecBase->CoolCapture == 0x7ec3e)
	{
	ExecBase->CoolCapture = 0;
	MyRequest(VN_SCA, 2);
	};
	
/***************** Check for Obelisk  *******************************/
if (ExecBase->CoolCapture == 0x7e86c)
	{
	ExecBase->CoolCapture = 0;
	ptr = 0x7e88a;
	Forbid();
	ptr[0] = 10;
	Permit();
	MyRequest(VN_OBELISK, 2);
	};

/******************** How about North Star? **************************/
if (ExecBase->CoolCapture == 0x7ec0e)
	{
	ExecBase->CoolCapture = 0;
	MyRequest(VN_NORTHSTAR, 2);
	};

/********************* Check for Byte Warrior ************************/
ptr = 0x7f800;
if (ptr[0] == ('D'<<24) + ('O'<<16) + ('S'<<8))
	{
	ptr = 0x7f954;
	if (ptr[0] == 0x4afc)
		{
		ptr[0] = 0;		/* Kill resident matchtag */
		
#asm
	xref	_LVOForbid
	xref	_LVOPermit
	xref	_Window
	xref 	_BBMPos
	xref	_LVOAutoRequest

		move.l 4,a6
		jsr _LVOForbid(a6)

		lea $7f972,a0
		move.w #$4ef9,(a0)
		move.w #$fc,2(a0)
		move.w #$6dc,4(a0)
		; jump right into the 1.2 ROMs. Ow.
		
		lea $7f800,a0
		move.l #0,(a0)
		jsr _LVOPermit(a6)

#endasm
		MyRequest(VN_BYTEWARRIOR, 2);
		};
	};

if (ExecBase->CoolCapture != 0)
	{
	sprintf(linebuffer, "CoolCapture is pointing at $%x", ExecBase->CoolCapture);
	if (MyRequest(linebuffer, 3) == TRUE) ExecBase->CoolCapture = 0;
	}

if (ExecBase->ColdCapture != 0)
	{
	sprintf(linebuffer, "ColdCapture is pointing at $%x", ExecBase->ColdCapture);
	if (MyRequest(linebuffer, 3) == TRUE) ExecBase->ColdCapture = 0;
	}

if (ExecBase->WarmCapture != 0)
	{
	sprintf(linebuffer, "WarmCapture is pointing at $%x", ExecBase->WarmCapture);
	if (MyRequest(linebuffer, 3) == TRUE) ExecBase->WarmCapture = 0;
	}


CloseLibrary(ExecBase);
}


/**************************************************************/
/*  This is the routine that displauys a block as ASCII text. */
/**************************************************************/
ShowAscii(key)
int key;
{
char linebuffer[80];
int drive;
int x,y;
int deltax, deltay;
int a,b;
int FLAG=0;

struct RastPort *RP;
RP = Window->RPort;

drive = key - '0';

if ((drive < 0) || (drive > 3) || (ChangeCount[drive] == -1)) return;

error = OpenDevice(TDName,drive,diskreq,0);
if (error > 0) return;

error = ReadBlock();

CloseDevice(diskreq);

if (error == FALSE) return;

/* save the amount we moved the window */
deltax = Window->LeftEdge;
deltay = Window->TopEdge;

MoveWindow(Window, -deltax, -deltay);

if (WindowBig == FALSE) SizeWindow(Window, 278, 160);
	else SizeWindow(Window, 278, 60);

WaitForNewSize();

SetAPen(RP, 3);
Move(RP, 14+(12*8), 165);
Text(RP, "Block 0", 7);
Move(RP, 324+(12*8), 165);
Text(RP, "Block 1", 7);
SetAPen(RP, 1);

text[2] = key;
SetWindowTitles(Window, text, -1);

x=0; y=0;

SetAPen(RP, 1);
SetDrMd(RP, JAM2);

for (a=0; a<512; a=a+32)
	{
	Move(RP, 10+(x*8), 20+(y*9));
	Text(RP, &diskbuffer[a], 32);

	Move(RP, 320+(x*8), 20+(y*9));
	Text(RP, &diskbuffer[a+512], 32);
	y++;
	};

Wait(1<<Window->UserPort->mp_SigBit);
Message = GetMsg(Window->UserPort);

/*  If a disk was inserted, we want CheckBlock() to happen later on some time */
if (Message->Class == DISKINSERTED) FLAG=1;
ReplyMsg(Message);

returner:
if (WindowBig == FALSE)
	SizeWindow(Window, -278, -160);
	else SizeWindow(Window, -278, -60);

SetWindowTitles(Window, TITLETEXT, -1);
WaitForNewSize();

/* deltas plus current position, in case dude moved the window */
MoveWindow(Window, deltax+(-Window->LeftEdge), deltay+(-Window->TopEdge));
Delay(2);
return(FLAG);
}


/*  
 When you do a SizeWindow() command, you have to wait for a NEWSIZE
 IntuiMessage before drawing in it.  That's all this routine does.
 */
WaitForNewSize()
{
while (TRUE)
	{
	WaitPort(Window->UserPort);
	Message = GetMsg(Window->UserPort);
	if (Message->Class != NEWSIZE)
		{
		ReplyMsg(Message);
		continue;
		};
	ReplyMsg(Message);
	break;
	};
}

/*
	type:

		1 == "is infected with the xxxx VIRUS!"
		2 == "somethin about virus in ram"
		3 == virus in cold/cool/warm capture 
*/

BuildITBodyText(text, type)
char *text;
int type;
{
switch (type)
	{
	case 1:
		strcpy(ITBodyText, "infected with the `");
		strcat(ITBodyText, text);
		strcat(ITBodyText, "' VIRUS!");
		break;
	case 2:
		strcpy(ITBodyText, "NOTICE:  The `");
		strcat(ITBodyText, text);
		strcat(ITBodyText, "' VIRUS was found");
		break;
	};
}

/***** an attempt to save space *********/
MyRequest(string, type)
char *string;
int type;
{
if (type != 3)
	{
	BuildITBodyText(string, type);
	} else
	{
	Mem1.IText = string;
	};

if (type == 1) return(AutoRequest(Window, &GenericDiskBody, &SCAPos, &SCANeg, 0, 0, 380, 80));
if (type == 2) return(AutoRequest(Window, &GenericRAMBody, &BBMPos, &BBMPos, 0, 0, 395, 78));
if (type == 3) return(AutoRequest(Window, &Mem1, &Repair, &SCANeg, 0, 0, 395, 78));
}


/********* DoStats() **********/
DoStats()
{
char linebuffer[80];
char numbuf[30];

SetAPen(RP, 0);
RectFill(RP, 2, 11, 303, 107);
SetAPen(RP, 1);

sprintf(linebuffer, "  Disks Checked: %d", DisksChecked);
Out(20, linebuffer);

sprintf(linebuffer, "Disks Installed: %d", DisksInstalled);
Out(29, linebuffer);

Out(43, "    Viruses Found:");

PV(52, VN_SCA, SCACount);
PV(61, VN_BYTEBANDIT, ByteBanditCount);
PV(70, VN_NORTHSTAR, NorthStarCount);
PV(79, VN_BYTEWARRIOR, ByteWarriorCount);
PV(88, VN_REVENGE, RevengeCount);
PV(97, VN_OBELISK, ObeliskCount);
}

/****************** Ok, we're REALLY being chintzy here. */
PV(num, name, howmany)
int num;
char *name;
int howmany;
{
register int x;
char linebuffer[80];

sprintf(linebuffer, "%-12s: %d", name, howmany);
Out(num, linebuffer);
}

/********************* Cheap?  Me?  Nooooo....     */
Out(num, name)
int num;
char *name;
{
Move(RP, 20, num);
Text(RP, name, strlen(name));
}
