
**
**	IconSpy v1.2
**
**	$VER: IconSpy.s 1.2 (27.10.94)
**
** 	written by Grzegorz Calkowski
**
** 	(C) Copyright 1994 The Beet Research
**
**
**	Use Asm-One or Trash'm-One to assembly it!
**	(or you will have some work with changing local labels :)
**

		incdir	"asminc:"		; include dir
		include	"exec/exec.i"
		include	"dos/dos.i"
		include	"dos/dosextens.i"
		include	"dos/dostags.i"
		include	"intuition/intuition.i"
		include	"workbench/workbench.i"
		include	"workbench/startup.i"
		include	"graphics/gfxbase.i"

		include	"lvo30/exec_lib.i"
		include	"lvo30/dos_lib.i"
		include	"lvo30/icon_lib.i"
		include	"lvo30/intuition_lib.i"
		include	"lvo30/graphics_lib.i"

		include	"gcmacros.i"

;==============================================================================

		SECTION	DetachStuff,code

rb_StackSize	=	4096
rb_TaskPri	=	0

rb_Start	bra.s	.skip

		dc.b	"$VER: IconSpy 1.2 (27.10.94)",$a,$d,0
		cnop	0,4

;==============================================================================

.skip		move.l	4.w,a6
		cmp	#37,LIB_VERSION(a6)	; check kick version
		blt	rb_KickReq
		cla	a1
		call	FindTask		; get our task
		move.l	d0,a4
		tst.l	pr_CLI(a4)		; start from cli?
		bne.s	rb_FromCLI		; yes, runback
		lea	pr_MsgPort(a4),a0	; get the port
		call	WaitPort
		lea	pr_MsgPort(a4),a0
		call	GetMsg
		move.l	d0,rb_WBMsg		; store the message
		jmp	rb_RunningBack		; go program (workbench)

;==============================================================================

rb_FromCLI	openlib	rb_DOSName
		move.l	d0,rb_DOSBase
		beq.s	rb_Failure		; no dos?
		move.l	d0,a6

		lea	rb_Start(pc),a1
		clr.l	-4(a1)			; clear pointer to next segment
		move.l	#rb_RunningBack-4,d3	; seglist adr
		lsr.l	#2,d3			; create bcpl pointer (adr/4)
		move.l	d3,rb_NewProcTags+4	; put into taglist

		sys	Forbid			; forbid task switching
		move.l	#rb_NewProcTags,d1	; here are what we want
		call	CreateNewProc		; create new process
		tst.l	d0
		beq.s	rb_NoProcess		; successful?
rb_Exit		move.l	4.w,a6
		call	Permit			; permit task switching
rb_CloseDOS	move.l	rb_DOSBase(pc),a1
		call	CloseLibrary

rb_SayGoodBye	moveq	#RETURN_OK,d0		; no errors
		rts
rb_Failure	moveq	#RETURN_FAIL,d0
		rts

rb_NoProcess	move.l	rb_NewProcTags+4(pc),d1
		call	UnLoadSeg
		bra.s	rb_Exit		

;==============================================================================

rb_KickReq	openlib	rb_DOSName
		move.l	d0,rb_DOSBase
		beq.s	rb_Failure		; no dos...
		move.l	d0,a6
		move.l	#rb_OutWindow,d1	; we wanna window
		move.l	#MODE_NEWFILE,d2
		call	Open
		tst.l	d0			; couldn't open?
		beq.s	rb_CloseDOS		; so get lost
		move.l	d0,d7			; store handle in d7
		move.l	d7,d1
		move.l	#rb_KickTxt,d2		; text to display
		move.l	#rb_KeyBuf-rb_KickTxt,d3
		call	Write
		move.l	d7,d1
		move.l	#rb_KeyBuf,d2
		moveq	#1,d3
		call	Read			; read any key
		move.l	d7,d1
		call	Close
		bra.s	rb_CloseDOS		
			
; do not change window's x-dimension - now text fits with topaz 9!

rb_OutWindow	dc.b	"RAW:40/40/350/60/IconSpy v1.2",0
rb_KickTxt	dc.b	10,"Required at least Kickstart V37.",10,10
		dc.b	"Press any key to continue... "
rb_KeyBuf	dc.b	0
		even
		
;==============================================================================

rb_NewProcTags	dc.l	NP_Seglist,0
		dc.l	NP_FreeSeglist,TRUE	; it is *NOT* default as
						; stated in includes!!!!!!!
		dc.l	NP_StackSize,rb_StackSize
		dc.l	NP_Priority,rb_TaskPri
		dc.l	NP_Name,TaskName
		dc.l	TAG_DONE

rb_DOSBase	dc.l	0
rb_DOSName	DOSNAME
rb_IntName	INTNAME
TaskName	dc.b	"IconSpy v1.2 © TBR",0
		even

;==============================================================================

		SECTION	RunningBack,code

rb_RunningBack	bsr	Program			; execute program
		move.l	rb_WBMsg(pc),d7
		beq.s	rb_Done
		move.l	4.w,a6
		call	Forbid			; NOTE! NO Permit needed!
		move.l	d7,a1			; get old message
		call	ReplyMsg		; reply it
rb_Done		moveq	#0,d0
		rts

rb_WBMsg	dc.l	0
		cnop	0,4

;==============================================================================
; 				THE PROGRAM
;==============================================================================

Program		movem.l	d1-d7/a1-a6,-(sp)

		move.l	4.w,_ExecBase

		setexec
		call	Forbid
		lea	PortName(pc),a1
		call	FindPort	       ; check if we patched it already
		call	Permit
		tst.l	d0
		bne	RunAlready

		setexec
		openlib	DOSName				; open libraries
		move.l	d0,_DOSBase
		beq	ExitFAIL
		openlib	IconName
		move.l	d0,_IconBase
		beq	ExitFAIL
		openlib	GfxName
		move.l	d0,_GfxBase
		beq	ExitFAIL

;==============================================================================

		tst.l	rb_WBMsg		; if there is a msg we were
		bne.s	.fromwb			; launched from WB

		setbase	DOS
		move.l	#ArgsTemplate,d1
		move.l	#ArgsArray,d2
		cld	d3
		call	ReadArgs		; parse arguments
		tst.l	d0			; okay?
		bne.s	.argsokay
		call	IoErr			; get error code
		move.l	d0,d1
		move.l	#ErrHead,d2		; head (IconSpy)
		call	PrintFault		; write error msg
		bra	ExitFAIL
.argsokay	move.l	d0,RdArgs
		bra.s	.go

.fromwb		setbase	Icon
		move.l	rb_WBMsg(pc),a2
		move.l	sm_ArgList(a2),a2	; array of WBArgs
		move.l	wa_Lock(a2),d1		; lock of directory program is
		call	CurrentDir,DOS		; make it current
		move.l	wa_Name(a2),a0		; name of file
		call	GetDiskObject		; get the icon
		move.l	d0,DiskObj		; store ptr
		beq.s	.go			; no icon? then go away
		move.l	d0,a0
		move.l	do_ToolTypes(a0),a0	; array of tooltypes
		move.l	a0,a4			; store it for a while
		lea	WINDOW_tt(pc),a1	; WINDOW tooltype
		call	FindToolType
		move.l	d0,ArgsArray		; ptr to window spec
		move.l	a4,a0			; get tooltypes back
		lea	WBENABLE_tt(pc),a1	; WBENABLE tooltype
		call	FindToolType
		move.l	d0,ArgsArray+4		; we'll use it as a boolean

;==============================================================================

.go		tst.l	ArgsArray+4		; WB spying enabled?
		beq.s	.no_wbenable
		st	WBFlag			; set flag
.no_wbenable
		move.l	ArgsArray(pc),d1	; if there was other output
		bne.s	.otheroutput		; specified - use it

		move.l	_GfxBase(pc),a0
		move.l	gb_DefaultFont(a0),a0
		move	tf_XSize(a0),d0
		move	tf_YSize(a0),d1
		mulu	#77,d0
		add	#24,d0
		mulu	#14,d1
		add	#15,d1
		lea	-4(sp),sp
		movem	d0/d1,(sp)	
		move.l	#OutputWin,a0		; use the standard window
		move.l	sp,a1
		lea	Buf,a3
		bsr	PrintF		
		lea	4(sp),sp
		move.l	#Buf,d1
.otheroutput	move.l	#MODE_NEWFILE,d2
		setbase	DOS
		call	Open
		move.l	d0,WinHandle		; store the handle
		beq	ExitFAIL		; okay?
 		move.l	d0,d1
		move.l	#InfoTxt,d2
		move.l	#InfoTxt_len+HeadTxt_len,d3
		call	Write			; write info and header

		setexec
		call	Forbid			; we MUST forbid for a while
		move.l	_IconBase(pc),a1
		lea	_LVOGetDiskObject,a0
		move.l	#GetDiskObj_Patch,d0
		call	SetFunction		; patch _LVOGetDiskObject
		move.l	d0,GetDiskObj_Org+2	; store orginal ptr
		move.l	d0,GetDiskObj_Org2+2
		lea	_LVOFindToolType,a0
		move.l	#FindTT_Patch,d0
		call	SetFunction		; patch _LVOFindToolType
		move.l	d0,FindTT_Org+2		; store orginal ptr
		move.l	d0,FindTT_Org2+2
		call	CacheClearU		; flush caches (mostly for '40)
		call	Permit			; match the forbid

;==============================================================================

		moveq	#MP_SIZE+PortName_len,d0
		move.l	#MEMF_PUBLIC!MEMF_CLEAR,d1
		call	AllocMem		
		move.l	d0,PortAdr
		beq	.waitloop
		move.l	d0,a1

		lea	MP_SIZE(a1),a2
		move.l	a2,LN_NAME(a1)
		move.b	#-1,LN_PRI(a1)
		move.b	#NT_MSGPORT,LN_TYPE(a1)
		move.b	#PA_IGNORE,MP_FLAGS(a1)
		clr.b	MP_SIGBIT(a1)
		clr.l	MP_SIGTASK(a1)

		lea	PortName(pc),a0
.copyname	move.b	(a0)+,(a2)+
		bne.s	.copyname
		call	AddPort		

;==============================================================================

.waitloop	move.l	#SIGBREAKF_CTRL_C!SIGBREAKF_CTRL_D,d0	; C or D break
		call	Wait				; wait for signal
		and.l	#SIGBREAKF_CTRL_C,d0		; if ctrl-c was pressed
		bne.s	.terminated			; then terminate

		st	DisableFlag			; otherwise disable it
 		move.l	WinHandle(pc),d1
		move.l	#DisabledTxt,d2
		move.l	#DisabledTxt_len,d3
		call	Write,DOS			; write "disabled" text

		move.l	#SIGBREAKF_CTRL_C!SIGBREAKF_CTRL_E,d0	; C ot E break
		call	Wait			; wait for signal
		and.l	#SIGBREAKF_CTRL_C,d0	; if ctrl-c was pressed
		bne.s	.terminated		; then terminate

		sf	DisableFlag		; if no, enable now
 		move.l	WinHandle(pc),d1
		move.l	#HeadTxt,d2
		move.l	#HeadTxt_len,d3
		call	Write,DOS		; write header again
		bra.s	.waitloop		; and go back

.terminated	call	Forbid			; necessary here

		move.l	_IconBase(pc),a1	; check for alien patches
		cmp.l	#GetDiskObj_Patch,_LVOGetDiskObject+2(a1)
		bne.s	.alienpatch
		cmp.l	#FindTT_Patch,_LVOFindToolType+2(a1)		
		bne.s	.alienpatch
	
		move.l	GetDiskObj_Org+2(pc),d0
		lea	_LVOGetDiskObject,a0
		call	SetFunction		; restore org vector
		move.l	FindTT_Org+2(pc),d0
		lea	_LVOFindToolType,a0
		call	SetFunction		; as above
		call	CacheClearU		; again flush caches
		call	Permit

		setbase	DOS
 		move.l	WinHandle(pc),d1
		move.l	#TermTxt,d2
		move.l	#TermTxt_len,d3
		call	Write			; write "terminated" text
		moveq	#50,d1
		call	Delay			; wait 1 second
		move.l	WinHandle(pc),d1
		call	Close			; and close the output

		bra	ExitOK			; exit well

.alienpatch	call	Permit
		setbase	DOS
 		move.l	WinHandle(pc),d1
		move.l	#PatchTxt,d2
		move.l	#PatchTxt_len,d3	; tell we're patched!
		call	Write
 		move.l	WinHandle(pc),d1
		move.l	#HeadTxt,d2
		move.l	#HeadTxt_len,d3
		call	Write			; write header again
		setexec
		bra	.waitloop		; go back to the loop

;==============================================================================

ExitOK		bsr.s	CleanUp
		movem.l	(sp)+,d1-d7/a1-a6
		moveq	#RETURN_OK,d0		; all right
		rts

ExitFAIL	bsr.s	CleanUp
		openlib	IntuitionName
		tst.l	d0
		beq.s	.noint
		move.l	d0,a6
		cla	a0
		call	DisplayBeep		; flash (or beep) the screens
		move.l	a6,a1
		setexec
		call	CloseLibrary
.noint		movem.l	(sp)+,d1-d7/a1-a6
		moveq	#RETURN_FAIL,d0		; failure
		rts

;==============================================================================

CleanUp		move.l	RdArgs(pc),d1
		beq.s	.noargs
		setbase	DOS
		call	FreeArgs		; free the cli-args
.noargs		move.l	DiskObj(pc),d0
		beq.s	.nodiskobj
		move.l	d0,a0
		setbase	Icon
		call	FreeDiskObject		; free the icon
.nodiskobj	setexec
		clslib	DOS			; close libraries
		clslib	Icon
		clslib	Gfx

		move.l	PortAdr(pc),d2
		beq.s	.noport
		move.l	d2,a1
		call	RemPort
		move.l	d2,a1
		moveq	#MP_SIZE+PortName_len,d0
		calljmp	FreeMem		
.noport		rts

;==============================================================================

RunAlready	openlib	IntuitionName
		tst.l	d0
		beq.s	.exit
		move.l	d0,a6
		cla	a0
		cla	a2
		cla	a3
		lea	.es(pc),a1
		call	EasyRequestArgs
		move.l	a6,a1
		setexec
		call	CloseLibrary
.exit		bra	ExitOK
		
.es		dc.l	EasyStruct_SIZEOF,0
		dc.l	.title,.text,.gads
.title		dc.b	"IconSpy v1.2",0
.text		dc.b	"IconSpy already running",0
.gads		dc.b	"Nice to know",0


;==============================================================================
;= PATCHES ====================================================================
;==============================================================================


GetDiskObj_Patch
		tst.b	DisableFlag		; check if disabled
		bne	GetDiskObj_Org2		; if yes, skip all of this
		movem.l	d1-a6,-(sp)
		lea	-100(sp),sp		; get some space on stack
		move.l	a0,a5			; store the name
		move.l	a0,d1
		setbase	DOS
		call	FilePart		; get only filepart
		move.l	d0,4(sp)		; store the filename
		bsr	GetProcess		; get process name
		move.l	d0,(sp)			; and store it
		tst.b	WBFlag			; WB trace enabled?
		bne.s	.nowb
		move.l	d0,a0
		lea	WBTxt(pc),a1		; name of WB task
		bsr	StrCmp			; compare names
		tst.l	d0			; was it WB?
		beq.s	.nowb			; no - go ahead
		lea	100(sp),sp		; yes, get outta here
		movem.l	(sp)+,d1-a6
		bra.s	GetDiskObj_Org2		
.nowb		move.l	#GettingTxt,8(sp)	; <getting icon> text ptr
		setbase	Icon
		move.l	a5,a0
GetDiskObj_Org	jsr	$0			; jump to kernel routine
		move.l	d0,16(sp)		; store result
		bne.s	.okay
		move.l	#FailTxt,12(sp)		; tell failed
		bra.s	.gotres
.okay		move.l	#OkayTxt,12(sp)		; tell okay
.gotres		lea	FormatStr(pc),a0	; the format str for RawDoFmt
		lea	(sp),a1			; datastream
		lea	20(sp),a3		; output buf
		bsr	PrintF			; format it!
		setbase	DOS
		move.l	WinHandle(pc),d1
		lea	20(sp),a0		; the text
		move.l	a0,d2
		moveq	#78,d3			; length
		call	Write
		move.l	16(sp),d0		; restore result
		lea	100(sp),sp		; restore stack
		movem.l	(sp)+,d1-a6
		rts
GetDiskObj_Org2	jmp	$0

;==============================================================================

FindTT_Patch	tst.b	DisableFlag		; check if disabled
		bne	FindTT_Org2		; if yes, skip all of this
		movem.l	d1-a6,-(sp)
		lea	-100(sp),sp		; get some space on stack
		move.l	a0,a4			; store ToolType array ptr
		move.l	a1,a5			; store the ToolType
		move.l	a1,8(sp)
		bsr	GetProcess		; get process name
		move.l	d0,(sp)
		tst.b	WBFlag			; stuff here as above :)
		bne.s	.nowb
		move.l	d0,a0
		lea	WBTxt(pc),a1
		bsr	StrCmp
		tst.l	d0
		beq.s	.nowb
		lea	100(sp),sp
		movem.l	(sp)+,d1-a6
		bra.s	FindTT_Org2		
.nowb		move.l	#MinusTxt,4(sp)		; "-" string
		setbase	Icon
		move.l	a4,a0			; restore args
		move.l	a5,a1
FindTT_Org	jsr	$0			; jump to system
		move.l	d0,16(sp)
		bne.s	.okay
		move.l	#NotFoundTxt,12(sp)	; tell not found
		bra.s	.gotres
.okay		move.l	d0,12(sp)		; show what we found
.gotres		lea	FormatStr(pc),a0	; blabla...
		lea	(sp),a1
		lea	20(sp),a3
		bsr	PrintF
		setbase	DOS
		move.l	WinHandle(pc),d1
		lea	20(sp),a0
		move.l	a0,d2
		moveq	#78,d3
		call	Write
		move.l	16(sp),d0		; restore result
		lea	100(sp),sp
		movem.l	(sp)+,d1-a6
		rts
FindTT_Org2	jmp	$0

;==============================================================================

GetProcess	setexec
		cla	a1			; 0 means current task
		call	FindTask
		move.l	d0,a0
		move.l	LN_NAME(a0),d0		; name in d0
		rts


*******************************************************************************
*
*			     SOME ROUTINES HERE
*
*******************************************************************************

*
*   PrintF - formats C-styled text string (ofcoz, using RawDoFmt)
*
*	IN	a0: format string
*		a1: args
*		a3: dest buf	
*	OUT	dest buf - formatted string
*

PrintF		lea	.putchr(pc),a2
		sys	RawDoFmt
		rts
.putchr		move.b	d0,(a3)+
		rts

*******************************************************************************

*
*    StrCpm - compares two null-terminated strings
*
*	IN	a0: string 1
*		a1: string 2
*	OUT	d0: TRUE (non-zero) if equal, FALSE (zero) otherwise
*

StrCmp		move.b	(a0)+,d0
		cmp.b	(a1)+,d0
		bne.s	.diff
		tst.b	d0
		bne.s	StrCmp
		moveq	#TRUE,d0
		rts
.diff		moveq	#FALSE,d0
		rts

*******************************************************************************

;==============================================================================
; Data Area
;==============================================================================

_ExecBase	dc.l	0
_DOSBase	dc.l	0
_IconBase	dc.l	0
_GfxBase	dc.l	0

WinHandle	dc.l	0
RdArgs		dc.l	0
ArgsArray	dc.l	0,0
DiskObj		dc.l	0
PortAdr		dc.l	0

DisableFlag	dc.b	0
WBFlag		dc.b	0

DOSName		DOSNAME
IconName	ICONNAME
IntuitionName	INTNAME
GfxName		GRAFNAME

;==============================================================================

PortName	dc.b	"IconSpy.rendezvous",0
PortName_len	equ	*-PortName

ArgsTemplate	dc.b	"WIN=WINDOW,WBENABLE/S",0
ErrHead		dc.b	"IconSpy",0

WINDOW_tt	dc.b	"WINDOW",0
WBENABLE_tt	dc.b	"WBENABLE",0

OutputWin	dc.b	"CON:0/1/%d/%d/IconSpy v1.2 by Grzegorz Calkowski "
		dc.b	"© 1994 The Beet Research, BEETWARE",0

OutputWin_len	equ	*-OutputWin

WBTxt		dc.b	"Workbench",0
		even

;==============================================================================

InfoTxt		dc.b	" ",10
		dc.b	"Type CTRL-C to exit, CTRL-D/CTRL-E to disable/enable "
		dc.b	"(Like in SnoopDos :)",10,10
InfoTxt_len	=	*-InfoTxt

HeadTxt		dc.b	10,"Process          Icon         ToolType"
		dc.b	"                  Result/Value",10
		dc.b	"-------          ----         --------"
		dc.b	"                  ------------",10
HeadTxt_len	=	*-HeadTxt

PatchTxt	dc.b	10,"Someone else patched the same vectors! Can't exit.",10
PatchTxt_len	=	*-PatchTxt

TermTxt		dc.b	10,"IconSpy terminated.",10
TermTxt_len	=	*-TermTxt

DisabledTxt	dc.b	10,"IconSpy is now disabled (type CTRL-E to enable it).",10
DisabledTxt_len	=	*-DisabledTxt

FormatStr	dc.b	"%-16.16s %-12.12s %-25.25s %-20.20s",10,0
GettingTxt	dc.b	"<getting icon>",0
OkayTxt		dc.b	"Okay",0
FailTxt		dc.b	27,"[33mFail",27,"[31m",0
NotFoundTxt	dc.b	27,"[33mNot found",27,"[31m",0
MinusTxt	dc.b	"-",0


		SECTION	blank,bss

Buf		ds.b	90

