[comp.sources.amiga] v02i096: virusx - virus exterminator v2.10

page@swan.ulowell.edu (Bob Page) (12/13/88)

Submitted-by: kim@uts.amdahl.com (Kim E. DeVaughn)
Posting-number: Volume 2, Issue 96
Archive-name: dos/virusx210.1

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:    Shell Archiver
#	Run the following text with /bin/sh to create:
#	Announcement
#	Makefile
#	README
#	VirusX.doc
#	_main.c
#	virusx.c
#	vx.asm
# This archive created: Mon Dec 12 16:39:49 1988
cat << \SHAR_EOF > Announcement
This version has mostly WorkBench bug fixes.  It no longer crashes when
run from WorkBench and then quit.  It lets you specify window size on
the command line.  Allows you to bring up a CLI or WShell at any time.

Also recognizes a new variant of the Byte Warrior virus.

SHAR_EOF
cat << \SHAR_EOF > Makefile
MODULES = virusx.o vx.o detach.o
CFLAGS = -n +l -s -DNDEBUG
LINKFLAGS = +cdb

virusx : $(MODULES)
	ln -g -o virusx $(LINKFLAGS) $(MODULES) -lc32 

vx.o : vx.asm
	as -o vx.o vx.asm

detach.o:	_main.c
	cc -n +bl +x3,5 -DDETACH -o $@ _main.c
SHAR_EOF
cat << \SHAR_EOF > README
IMPORTANT difference between this version and previous ones:
DO NOT USE RUNBACK TO RUN VIRUSX.  VirusX has the equivalent
of RunBack built in, just put "VirusX" alone on your command
line (or in your startup sequence).  VirusX WILL work with
RunBack, but will NOT let you pop up a CLI properly from
runback.

For those of you who have been following the VirusX Saga:

The reason for the V2.00/2.01 woes was a bug in the Manx startup
module.  I've redone the startup module and it works, and is also
a fair bit smaller - this is about 1.5K or so smaller than V2.01.

No new viruses.

One new function (Two actually):

Clicking in the VirusX window and hitting P will pop up a new CLI window.
Clicking in the VirusX window and hitting W will open a new WSHell if
you happen to have Wshell.  (If you don't, well, anything in the C
directory named "NewWSH" will get called when you hit W).

Due to the way the startup module works, these new CLI windows do not
have a "Current Directory" - ie, pop up a CLI and type "CD", it
will say "DF0:".  I don't know of any way around this.

Let me know if this version crashes on you at any point - It sure shouldn't!

	...Steve
SHAR_EOF
cat << \SHAR_EOF > VirusX.doc

                  [0;3;31;40m-[0;1;33;40mVirusX[31;40m-[0;31;40m

              [0;1mby Steve Tibbett[0;31;40m


   [0;3;31;40m- The Complete Virus Removal System! -[0;31;40m

VirusX - Fourth in a growing line of "X-Utilities".

REMEMBER: STUFF NEW TO THE LATEST VERSION IS AT THE _END_ OF THIS
FILE!

Version Notes:
--------------
Version Notes are now in the VirusX.C file, as is information on
some of the viruses.


Amiga Viruses have been following us around for some time now, and I
think it's about time we got rid of it for good.

There are a number of CLI-based Virus Checkers out there, which do their
job just fine, but if you're not into using CLI, what do you do? You use
VirusX!

Please, I encourage you to give this program to anybody who might have
the virus.  Including your local dealer - some of the dealers in this
area have the virus all over their disks, which they allow customers to
copy, and they don't do anything about it because they don't know how. 
VirusX makes it extremely simple.

You can put VirusX in your Startup-Sequence.  When run, it will open a
small window so you know it's there (and it will display the occasional
message in it).  Whenever a disk is inserted into any of the 3.5"
drives, that disk is automagically checked for the SCA virus, and also
checked to see if it's boot sector is "Standard".  If the disk has a
nonstandard boot sector, it is either a new form of virus which I don't
know about yet, or it is a commercial program which uses the boot block
for  something constructive (like booting their game).

If VirusX finds a boot block it is suspicious about, it will present the
user with a requester either warning him that the disk has the SCA virus
(or any other current viruse), or telling him that the boot code is
nonstandard.  In either case, he is given the option to either ignore
it, or to Remove it.

If the user selects Remove, after he says he's SURE he wants to
rewrite the disk's boot sector (Remember: Never rewrite the boot
sector of a commercial program unless you KNOW that program doesn't
use it for something else.  If the program gives you the AmigaDOS
window before running, you know it is safe to repair that disk.).  
The boot code written back to the disk by VirusX is the same boot code
that the AmigaDOS INSTALL command (and it's compatible counterpart on
one of the fish disks) uses.  

If you click in the little "VirusX" window, and type a number from 0
to 3, (Corresponding to the drive # you would like to look at), VirusX
will resize it's window to fit in the ASCII text of these two blocks,
and allow you to view it.  When you run across a "Nonstandard Boot
Block", you can now check and see if the boot block is some sort of
new Virus (Assuming that the author of the Virus left a string in it)
as you will see something like "Revenge Virus 1.2G" or whatever string
that identifies the virus.  Note that not all viruses have text strings
in them, so don't use this method alone to determine whether an 
unknown boot block is a virus or not.  

Also, you can check to see which strain of the SCA virus you have
(VirusX will report "an SCA virus", but will not tell you if it is the
"LSD" virus, or the "Zorro/Willow" virus or whatever new ones may
appear).

Generally, if boot code is capable of writing itself back to a different
disk than the one it was loaded from, it is a virus.

Keystrokes:  0, 1, 2, 3, will show you the boot block of whatever drive
you select (0 would be DF0:, say), I will show you the Info window, and
P will get you a new CLI.  C will cause VirusX to  re-check all inserted
disks.  To use the keystrokes, click in the main VirusX window and type
away.

If you run across a strain of the virus, or any other virus that VirusX
doesn't specifically warn of, PLEASE send me a copy of a disk with that
virus on it!  I want to keep VirusX current, and to do so, I need the
viruses.

Of course, there are those of you who are thinking that I am some nut
case trying to spread my own virus hidden under the guise of a virus
checker.  Well, just for you, I've included the C source code.  Please,
if you don't trust me, don't discard a useful utility as untrustworthy
for no reason, CHECK THE SOURCE!  Recompile it if you think I'm trying
to slip a fast one on you.  I just want to see the virus out of all of
our lives.

I want feedback on this!  Send me a letter!  This program is
Copyrighted, but is freely redistributable (It's NOT Shareware).  Do
what you want with it, but  Please don't use it for evil purposes. 
That's what I'm trying to prevent.  (If your conscience is compelling
you to send me something, send me an original game you're bored
with... It won't cost you anything, and it'll keep me busy for a 
few hours (or more...).

My address:

	Steve Tibbett
	2710 Saratoga Pl. #1108
	Gloucester, Ontario
	K1T 1Z2

	My BBS: OMX BBS, 613-731-3419.

	I can be reached on BIX as "s.tibbett" and on People/Link
	as "SteveX".  I'm also on Compuserve, but with their dumb
	numbering system, I can never remember who I am.


---------------------------------------------------------------

BYTE BANDIT VIRUS:

What the Byte Bandit virus does is once it's in
memory, it copies itself to just above the high memory
pointer on the first hunk of RAM it can find (Which means
it's not always in the same place), wedges itself into the
Interrupt Server chain, into the Trackdisk.device's vectors,
and creates itself a Resident structure so it can hang
around after reboot.

It watches EVERY disk inserted, and will write itself to ANY
bootable disk that is inserted!  This one can spread like
wildfire - every disk you insert into your external drive during
a session with this Virus loaded will result in all those disks
being infected.  Ouch.

Also, if you Install a disk while this virus is going, it will  just
copy itself back to the disk - which is why it has to be wiped it from
memory.

When VirusX finds this virus on a disk, it will also display a "Copy
Count" which is the number of disks that have been infected by that
"Branch" on the "Tree" that the virus is on -  If you infect a disk with
your copy, and your copy is number 300, then that copy will be #301.  If
he infects somebody,  that will be #302, but on YOUR copy, two
infectations down the line, there will be another #302... Anyways, the
copy count on MY Byte Bandit virus is #879... 

Note that VirusX will check RAM for this virus as well as the disk. 
This was necessary as you can tell from the description above.

Special thanks must go here to Dave Hewett, who, 2 days after I gave him
a copy of the virus, gave me a printed, commented disassembly of the
virus with meaningful labels and everything I needed to stomp it -
Thanks Dave!

Thanks must also go to Bruce Dawson of CygnusSoft Software, who went to
the trouble of being the First person to send me this Virus.  (As of
yet, he's also the ONLY person - Geez, folks, I need YOUR help to do
this too, eh?)


REVENGE VIRUS:

What this virus does, is everything that the Byte Bandit virus does,
PLUS, after infecting a disk, it will wait one  minute after every
reboot, and change your mouse pointer  into an image of a certain part
of the Male anatomy. 8-)

I think the reason this virus is called the "Revenge" virus is because
it looks specifically for the Byte Bandit and for the SCA Virus.  If
it finds either of these, it Rigs THAT virus so that it will CRASH the
machine unless THIS virus is loaded first.  Note that I might be wrong
about this - that's the way it looks from the disassembly, but I don't
have an SCA virus here to  test it with.   I tried it with the Byte
Bandit, and it didn't seem to do anything like this - but be warned,
in case it pops up later or something.

He stays in RAM via changing the CoolCapture vector to point to his
own code.  He then intercepts the DoIO() call and watches for any
attempts to rewrite or to read the boot block and acts accordingly. 
He also has an interrupt around counting VBlanks until it's time to
bring up his sicko pointer.

To get this virus out of memory is Simple - Hold down the Joystick
button (Plug a joystick into port 2, and hold down the button while
you are rebooting), and the screen will briefly turn RED during the
boot, and it's out of memory. (If you hold down Joystick button AND
mouse button, it will half-remove himself from RAM and turn the screen
Blue)

VirusX will alert you if the virus is present in RAM and will render
it helpless in RAM before telling you about  it.  It will also report
it's presence on disk.  

I'd like to thank Lars Wilkund for being the first (And only so far)
person to send me this virus on disk.  Lasse is part of a Swedish users
group with over 700 members!

BYTE WARRIOR VIRUS:

The Byte Warrior Virus is a lot like the Byte Bandit virus, except
it is not designed to hurt anything - it will start an "Alarm"
sound if it sees another virus (or at least I think it does - 
it hasn't for me), but other than that, it will write itself to
any disk inserted.  There is also a hidden message in it, asking us
to spread it around and not to erase it.  Ya, right.


NORTH STAR VIRUS:

It's a virus itself that alerts you to other ones - 
I think this sort of idea is stupid because it can do just as 
much damage as the rest of them.

One new virus showed up for this version, the "Obelisk Softworks
Crew" virus.  It was sent to me by Jason Allen Smith.  Thanks, 
Jason!

Other changes this version - it's now a bunch smaller (again!) thanks
to a bit of a rewrite in assembler, and some reorganization.


SCA Virus:

This is the original.  It just sits in RAM writing itself to any
disk you boot off of.  You can get it out of memory by either running
VirusX, or holding down the left mouse button while you reboot
the machine (The machine will flash the screen green once it's
truly gone).

-------------------------------------------------------------------

I'd like to thank Lars Wilklund, Jason Allen Smith, Bruce Dawson,
Robb Bowns (sorry, Rob, I can't remember how your last name goes,
I think that's it), and all the others who have sent me disks
whom I cannot remember.  Sorry I don't answer mail as quickly or
as often as I'd like, I have very little time these days.

There are MORE viruses out there!  Please, send them to me!

	...Steve

SHAR_EOF
cat << \SHAR_EOF > _main.c
/* Copyright (C) 1986,1987 by Manx Software Systems, Inc. */

/*
 *	This is common startup code for both the CLI and the WorkBench.
 *	When called from the WorkBench, argc is 0 and argv points to a
 *	WBStartup type of structure.
 */

#include <fcntl.h>
#include <exec/alerts.h>
#include <exec/memory.h>
#include <libraries/dosextens.h>
#include <workbench/startup.h>
#include <functions.h>

extern long _savsp, _stkbase;

extern int errno;
extern int Enable_Abort;

extern int _argc, _arg_len;
extern char **_argv, *_arg_lin;
extern struct WBStartup *WBenchMsg;

extern struct _dev *_devtab;
extern short _numdev;

_main(alen, aptr)
long alen;
char *aptr;
{
	register struct Process *pp, *_FindTask();
	void *_OpenLibrary(), *_GetMsg(), *_AllocMem();
	long _Input(), _Output(), _Open();

#ifdef DETACH
	void do_detach();

	do_detach(&alen, &aptr);
#endif

	if ((_devtab = _AllocMem(_numdev*(long)sizeof(struct _dev),
													MEMF_CLEAR)) == 0) {
		Alert(AG_NoMemory, 0L);
#asm
		move.l	__savsp,sp		;get back original stack pointer
		rts						;and exit
#endasm
	}

	_devtab[0].mode = O_RDONLY;
	_devtab[1].mode = _devtab[2].mode = O_WRONLY;

	_stkbase = _savsp - *((long *)_savsp+1) + 8;
	*(long *)_stkbase = 0x4d414e58L;

	pp = _FindTask(0L);
#ifdef DETACH
	if (alen) {
#else
	if (pp->pr_CLI) {
#endif
		_cli_parse(pp, alen, aptr);
		Enable_Abort = 1;
#ifndef DETACH
		_devtab[0].mode |= O_STDIO;		/* shouldn't close if CLI */
		_devtab[1].mode |= O_STDIO;
#endif
	}

	else {
		_WaitPort(&pp->pr_MsgPort);
		WBenchMsg = _GetMsg(&pp->pr_MsgPort);
		if (WBenchMsg->sm_ArgList)
			_CurrentDir(WBenchMsg->sm_ArgList->wa_Lock);
		/*_wb_parse(pp, WBenchMsg);*/
		_argv = (char **)WBenchMsg;
	}
	_devtab[0].fd = _Input();
	if (_devtab[1].fd = _Output())
		_devtab[2].fd = _Open("*", MODE_OLDFILE);
	main(_argc, _argv);
	exit(0);
}

#ifdef DETACH
extern long _stack, _priority, _BackGroundIO;
extern char *_procname;
BPTR _Backstdout = 0;
extern struct FileLock *_detach_curdir;
extern char *_detach_name;
static long _alen = 0;
static char *_aptr = 0;

static void
do_detach(alen, aptr)
long *alen;
char **aptr;
{
	register struct Process *pp, *_FindTask();
	void *sav, *_OpenLibrary(), *_GetMsg(), *_AllocMem();
	long _Open();
	register unsigned short c;
	register char *cp;
	register struct CommandLineInterface *cli;
	register long l;
	long *lp;
	struct MemList *mm;
	struct FileLock *CurrentDir();

	pp = _FindTask(0L);
	if (pp->pr_CLI) {			/* first time through!! */
		CurrentDir(_detach_curdir = CurrentDir(0L));
		_detach_curdir = DupLock(_detach_curdir);

		cli = (struct CommandLineInterface *) ((long)pp->pr_CLI << 2);
		l = cli->cli_Module;
		if ((sav = _OpenLibrary(DOSNAME, 33L)) == 0) {

			lp = (long *)*((long *)*((long *)*((long *)*((long *)
											_savsp+2)+1)-3)-3)+107;
			if (*lp != cli->cli_Module)
				exit(100);
		}
		else {
			_CloseLibrary(sav);
			lp = 0;
		}
		if (lp)
			*lp = 0;
		if (_stack == 0)
			_stack = cli->cli_DefaultStack * 4;
		if (_BackGroundIO)
			_Backstdout = (BPTR)_Open("*", MODE_OLDFILE);
		_alen = *alen;
		_aptr = _AllocMem(_alen, 0L);
		movmem(*aptr, _aptr, (int)_alen);
		cp = (char *)((long)cli->cli_CommandName << 2);
		_detach_name = AllocMem((long)cp[0]+1, 0L);
		movmem(cp, _detach_name, cp[0]+1);
#asm
		move.l	__savsp,-(sp)
#endasm
		CreateProc(_procname, _priority, l, _stack);
		cli->cli_Module = 0;
#asm
		move.l	(sp)+,sp
		move.l	#0,d0
		rts
#endasm
	}
	else if (_alen && strcmp(pp->pr_Task.tc_Node.ln_Name, _procname) == 0) { /* second time */
		lp = (long *)((long)pp->pr_SegList << 2);
		lp = (long *)(lp[3] << 2);
		sav = lp;
		c = 2;
		while (lp) {
			lp = (long *)(*lp << 2);
			c++;
		}
		mm = _AllocMem((long)sizeof(struct MemList)+
							(c-1)*sizeof(struct MemEntry), 0L);
		lp = sav;
		mm->ml_NumEntries = c;
		c = 0;
		while (lp) {
			mm->ml_me[c].me_Addr = (APTR)lp - 1;
			mm->ml_me[c].me_Length = lp[-1];
			lp = (long *)(*lp << 2);
			c++;
		}
		mm->ml_me[c].me_Addr = (APTR)_aptr;
		mm->ml_me[c++].me_Length = _alen;
		mm->ml_me[c].me_Addr = (APTR)_detach_name;
		mm->ml_me[c++].me_Length = _detach_name[0] + 1;

		AddTail(&((struct Task *)pp)->tc_MemEntry, mm);

		CurrentDir(_detach_curdir);

		pp->pr_COS = _Backstdout;

		*alen = _alen;
		*aptr = _aptr;
	}
}
#endif

SHAR_EOF
cat << \SHAR_EOF > virusx.c
/************************************************************************/
/*									*/
/*									*/
/*                                VirusX      		 		*/
/*									*/
/*			     by Steve Tibbett				*/
/*									*/
/*									*/
/*		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!)					*/
/*									*/
/*	Created with Aztec C V3.6a using 32 bit ints, my makefile is    */
/*      included.  Also included is a new _main.c file, which is my     */
/*      startup code.  It's basically a modified version of the Manx    */
/*      _main.c, in that it doesn't do the CLI parsing, doesn't set     */
/*      stdin or stdout or whatever, but it does detach from the CLI    */
/*      properly (and shouldn't crash when run from WB.                 */
/*									*/
/************************************************************************/
/*									*/
/*  History:								*/
/*  --------								*/
/*	April '88 or so:  V1.0 written and released.			*/
/*     A few days later:  V1.01 released.  V1.0 wrote garbage to the    */
/*                        disk if it was write protected then fixed.    */
/*          27-March-88:  V1.2 released.  V1.2's purpose in life was    */
/*                        to deal with the Byte Bandit virus.           */
/*                        (Actually, it's well after midnight - make    */
/*                        that March 28th. :)                           */
/*          28-March-88:  Oops, V1.2 was 3K or so bigger than it needed */
/*                        to be.  Fix it, release v1.21.                */
/*           15-June-88:  V1.3, V1.2 cleaned up and made smaller.       */
/*            8-July-88:  V1.4.  Revenge virus checking, Viewbooting,   */
/*                        check for SCA in RAM, more cleaning up.       */
/*           24-July-88:  V1.5, only change was the addition of the     */
/*                        Byte Warrior virus.                           */
/*             1-Aug-88:  V1.6 (busy week), Dan Mosedale sent me the    */
/*                        Northstar Virus.  Nuked it.                   */
/*            18-Aug-88:  V1.7 - after 2 weeks off, got the Obelisk     */
/*                        Softworks crew virus.                         */
/*     a few days later:  V1.71, can't remember why.                    */
/*   September Sometime:  Biggest mistake of my life, released V2.0.    */
/*         3 days later:  Bigger mistake:  Released V2.01 - which was   */
/*                        2.0 with another bug added.  Argh.            */
/*             6-Nov-88:  Finally got some time to clean things up,     */
/*                        check out the startup code bugs, clean up the */
/*                        docs and source, and release V2.1.  There     */
/*                        haven't been any new viruses in about a month */
/*                        now, but I hear one or two are on the way.    */
/*									*/
/************************************************************************/
/*									*/
/*  Viruses Dealt With:							*/
/*  -------------------							*/
/*									*/
/* 	SCA		- The SCA is the simplest virus to deal with,   */
/* 			  as it's not actually DOING anything except    */
/*			  hiding in memory, until you reboot.   	*/
/*			  We just look at CoolCapture and fix it to get */
/*			  it out of RAM.				*/
/*									*/
/*	Byte Bandit	- The Byte Bandit virus takes the DoIO() vector */
/*			  and redirects it through itself.  Thus, any   */
/*			  attempt to read or write the boot block (ie,  */
/* 			  AmigaDOS trying to figure out what kind of    */
/* 			  disk it is) results in the BB writing itself  */
/*			  onto that disk.  VirusX couldn't just rewrite */
/*			  the boot block, we have to get him out of RAM */
/*      		  first.  This virus also has an interrupt that */
/*                        crashes the machine every 5 minutes or so     */
/*                        after it's infected a few of your disks.  Ow. */
/*			  It stays in memory not via the Capture        */
/*                        vectors, but by a Resident module.   		*/
/*									*/
/*	Revenge		- Basically, a Byte Bandit clone except it will */
/*			  bring up an obscene pointer a few minutes	*/
/*			  after you reboot.  We treat it much like the  */
/*			  byte bandit.					*/
/*									*/
/*	Byte Warrior 	- Jumps right into 1.2 Kickstart.  Won't work   */
/* 			  under 1.3.  Hangs around via Resident struct, */
/*			  doesn't do any damage.			*/
/*									*/
/*	North Star	- Like SCA, hangs around via CoolCapture,	*/
/*			  killing CoolCapture kills the North Star. 	*/
/*									*/
/*	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>

/********************************************************************/
/*  These are for the Manx detach module, which I've modified but   */
/*  I left these intact.  Stack, Priority, BackgroundIO (always 0!),*/
/*  and the name of your process.				    */
/********************************************************************/
long _stack 		= 8000;
long _priority 		= 0;
long _BackGroundIO 	= 0;
char *_procname 	= "VirusX 2.10";


/********************************************************************/
/* These string constants are used in multiple places, and thus     */
/* save bytes by having only one copy of them.                      */
/********************************************************************/
char TDName[] 		= "trackdisk.device";
char copystring[] 	= "(Copy Count on this disk: %d)";
char ITBodyText[80];
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";
char CaptureStr[] 	= "Capture is pointing at $";
char text[] 		= "DF6: Boot Sectors";
char TITLETEXT[] 	= "VirusX 2.10 by Steve Tibbett";



/********************************************************************/
/* These counters are for the Info window, one for each virus.      */
/********************************************************************/
int 	ObeliskCount;
int 	NorthStarCount;
int 	SCACount;
int 	ByteBanditCount;
int 	ByteWarriorCount;
int 	RevengeCount;


/*******************************************************************/
/*  Miscellaneous variables.					   */
/*******************************************************************/
int 	ChangeCount[4];			/* TD_CHANGECOUNT for 4 drives 	*/
int 	LastSum;			/* Used in the checksumming 	*/
int 	error;				/* sort of a temporary variable */
char 	WindowBig;			/* TRUE if the window is big    */
struct 	Port *diskport;			/* trackdisk's port.            */
struct 	IOStdReq *diskreq;		/* trackdisk's IOStdReq         */
int	DisksChecked, DisksInstalled; 	/* for title bar info           */
int 	VirusBase;			/* ick, whatta name! 		*/
char 	FromCLI;			/* True if run from CLI		*/

struct IntuitionBase 	*IntuitionBase;	/* For Library Bindings		*/
struct GfxBase 		*GfxBase;
struct Window 		*Window;
struct IntuiMessage 	*Message;

int Keepgoing;		/* A flag.  It's false when we want out. 	*/
int x, y, i;		/* Left over from my using Basic 		*/

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,
	NULL,NULL,0,0,0,0,WBENCHSCREEN,	
	};

struct RastPort *RP;

#define BSIZE 40	/* Ha!  I'm not telling what this is! */


/*******************************************************************/ 
/*  diskbuffer is where all disk io goes to.  it's 3*512 rather    */
/*  than 2*512, because I believe one of the viruses watches for   */
/*  reads of 1024 bytes, so I'm just being safe.		   */
/*******************************************************************/ 
UBYTE diskbuffer[3*512];


/************************************************************************/
/* Warning messages.  These messages get modified before being          */
/* displayed (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};




/********************************************************************/
/*  My intuition defines.  There's lots of 'em - theyre self explan.*/
/********************************************************************/

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


/*********************Da Beginnin*************************/

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

FromCLI = TRUE;

switch (argc)
	{
	case 0:
		FromCLI = FALSE;
		break;

	case 3:
		NewWindow.LeftEdge = atoi(argv[1]);
		NewWindow.TopEdge = atoi(argv[2]);
		break;
	};

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, etc. in RAM.  */
CheckMemoryForViruses();

CheckBlock();

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

Quitter:
if (diskport != 0) DeletePort(diskport);
if (diskreq != 0) 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;

		switch (Code)
			{
			case 'p':
			case 'P':
				if (FromCLI == TRUE) Execute("NEWCLI", 0, 0);
				break;

			case 'w':
			case 'W':
				if (FromCLI == TRUE) Execute("NEWWSH", 0, 0);
				break;

			case 'i':
			case 'I':
				if (WindowBig == TRUE) WindowBig = FALSE;
					else WindowBig = TRUE;

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

				WaitForNewSize();
				break;


			case 'c':
			case 'C':
				for (x=0; x<4; x++) ChangeCount[x] = 10000;
				CheckBlock();

			default:
				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[0xc0] == 68)
				if (diskbuffer[0xf1] == 7)
					{
					ByteWarriorCount++;
					ByteWarrior = TRUE;
					};

	if (diskbuffer[0x2b] == '9')
		if (diskbuffer[0x2c] == '.')
			if (diskbuffer[0x2d] == '8')
				if (diskbuffer[0x2e] == '7')
					{
					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++;
					};

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

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


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

	/* 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 */
			error = 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] == (0X444f5300) )
	{
	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, "Cool%s%x", CaptureStr, ExecBase->CoolCapture);
	if (MyRequest(linebuffer, 3) == TRUE) ExecBase->CoolCapture = 0;
	}

if (ExecBase->ColdCapture != 0)
	{
	sprintf(linebuffer, "Cold%s%x", CaptureStr, ExecBase->ColdCapture);
	if (MyRequest(linebuffer, 3) == TRUE) ExecBase->ColdCapture = 0;
	}

if (ExecBase->WarmCapture != 0)
	{
	sprintf(linebuffer, "Warm%s%x", CaptureStr, 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....     */
/* This will write text to the column number in NUM, the text in Name. */
/*  Or something like that. ********************************************/
Out(num, name)
int num;
char *name;
{
Move(RP, 20, num);
Text(RP, name, strlen(name));
}
SHAR_EOF
cat << \SHAR_EOF > vx.asm
;
;
;
;  The ASSEMBLER portion of VirusX starts here.  This is where some of the
;  more often called routines now live - in the smallness of Assembler.
;
;
	xref _geta4
	xref _diskreq
	xref _diskbuffer
	xref _LVODoIO
	xdef _ReadBlock

;
;  ReadBlock:  This will read the boot block.  Yay.
;
_ReadBlock:
	movem.l d1/a0/a1/a4/a5/a6,-(sp)
	jsr _geta4

	move.l _diskreq,a0
	move.w #2,$1c(a0) 		; diskreq->io_Command = CMD_READ;
	lea _diskbuffer,a1
	move.l a1,d0
	move.l d0,$28(a0) 		; diskreq->io_Data = diskbuffer;
	move.l #(3*512),$24(a0) 	; diskreq->io_Length = 3*512;
	move.l #0,$2c(a0)		; diskreq->io_Offset = 0;
	move.l 4,a6

	move.l _diskreq,a1
	jsr _LVODoIO(a6)		; DoIO(diskreq);

	move.l _diskreq,a0
	cmp.b #0,$1f(a0)		; check io_Error
	beq IsOkay			; if (diskreq->io_Error > 0) return(FALSE);	/* disk error, lemme out */

ReturnError:
	move.l #0,d0			; return error
	movem.l (sp)+,d1/a0/a1/a4/a5/a6
	rts

IsOkay:
	move.l #0,$24(a0)		; diskreq->io_Length = 0;
	move.w #9,$1c(a0)		; diskreq->io_Command = TD_MOTOR;

	move.l _diskreq,a1	
	move.l 4,a6
	jsr _LVODoIO(a6)		; DoIO(diskreq);				/* turn off motor */

	move.l _diskreq,a0
	cmp.b #0,$1f(a0)
	bne ReturnError

	move.l #1,d0
	movem.l (sp)+,d1/a0/a1/a4/a5/a6
	rts
SHAR_EOF
#	End of shell archive
exit 0
-- 
Bob Page, U of Lowell CS Dept.  page@swan.ulowell.edu  ulowell!page
Have five nice days.