; RTtoKD 1.0 ©1993 F.Delacroix - FreeWare
; Written on the great Hisoft Devpac3
; Usage: RTtoKD     replaces the filerequester of the reqtools.library
; with the kd_freq.library when it is possible.

	opt	AMIGA,CASE,INCONCE,USER	; Devpac 3 options
;TEST=1	; This is for debugging (don't try to launch from workbench!)
	IFD	TEST	; when in debugging mode,include line numbers
	opt	HCLN	; in source
	ENDC

	include	libraries/reqtools.i	; the other necessary were included
	include	libraries/reqtools_lib.i	; at assembly time using
	include	libraries/kdbase.i	; a pre-assembled(.gs) file
	include	exec/execbase.i
	include	workbench/icon_lib.i
	include	workbench/workbench.i

VERSION=1	; Version of the patch this program deals with
MFL_MATCHWORD='RTKD'	; This is to recognize the FileLists allocated by us

	IFD	TEST
TESTPROGRAM	MACRO	; Macro to launch test program when in debugging
TestPrg	include	TestRT.s	; mode
	ENDM
	ENDC

; flag bits for tag conversion structure
SCRTOFRONT=0	; Bring screen to front
USEPUB=1	; Actually use a public screen
WAITPTR=2	; Set Wait pointer in window
LOCKWIN=3	; Lock window from input (WAITPTR ignored)
ALLOWEMPTY=4	; Accept an empty file name as a positive response

	rsreset	; linked structures for every allocated FileRequest struct
CustomRequest	rs.b	MLN_SIZE	; The ListHeader is in the patch
CR_ReqToolsRequest	rs.l	1	; struct allocated by reqtools
CR_KDRequest	rs.l	1	; struct allocated by kd_freq.library
CR_SIZE	rs.w	0

	rsreset		; Entry in the FileList
MFL_MatchWord	rs.l	1	; for being sure this is really one
MFL_ListPtr	rs.l	1	; of my FileLists (pointer to MFL struct)
MFL_FileList	rs.b	rtFileList_SIZE	; the true rtFileList
MFL_FileName	rs.b	0	; MFL_FileList+rtfl_StrLen+1 bytes long
MFL_SIZEOF	rs.w	0	; (+1 because of 0-ended string)
; Note for quite advanced programmers:
; Some programs (RTPatch) seem to expect the FileName right after the
; rtFileList structure,that's why the extended data is placed before it.
; Note that the 8 bytes of extension are placed before each entry in the
; FileList,thus allowing FileList splittings. However,only the first entry
; is checked during the new rtFreeFileList(). So you are strongly urged
; not to perform FileList mergings (although it is unlikely to happen)
; that would cause gurus (81000009) if the first entries were allocated
; by RTToKD and others by the true reqtools.library. If it is the contrary
; then some memory will never be freed...

	rsreset		; Structure returned by RTTagsToKDstruct routine
CD_Window	rs.l	1	; Window for WaitPointer()/[Un]LockWindow()
CD_WindowLock	rs.l	1	; Pointer returned by rtLockWindow()
CD_Screen	rs.l	1	; Screen to put requester on
CD_PubScrName	rs.l	1	; Public screen name
CD_RTFlags	rs.l	1	; flags found for the reqtools requester
CD_Flags	rs.b	1	; flags (definitions above)
CD_SIZEOF	rs.w	0

	rsreset		; structure that is specific to the patch
patchport	rs.b	MP_SIZE	; dummy message port to locate the patch
version	rs.w	1	; version number (equal to VERSION)
reqtoolsbase	rs.l	1	; base of reqtools.library
kdbase	rs.l	1	; guess what this is
segment	rs.l	1	; SegList to UnLoadSeg() when removing patch
oldallocrequesta	rs.l	1	; these are the original
oldfreerequest	rs.l	1		; reqtools.library functions
oldfreereqbuffer	rs.l	1	; pointers
oldchangereqattr	rs.l	1
oldfilerequesta	rs.l	1
oldfreefilelist	rs.l	1
customreqlist	rs.b	MLH_SIZE	; ListHeader for CustomRequests

DM	MACRO	; this means Dos Message: if this is set at the
	dc.b	1	; end of a message,then the message can be printed
	ENDM	; only when running from CLI. Important
; messages therefore end with a 0 so that when run from workbench,they are
; printed using the EZRequest() function (Reqtools version 37 is then
; required)
WM	MACRO	; this is an important message,to be printed from CLI as
	dc.b	0	; well as from WB
	ENDM

ProgStart
	move.l	a0,a2
	move.l	d0,d2
	move.l	4.w,a6
	move.l	ThisTask(a6),a4	; did we come from workbench ??
	tst.l	pr_CLI(a4)
	bne.s	FromCLI
	lea	pr_MsgPort(a4),a0	; yes,let's get the WBStartup
	jsr	_LVOWaitPort(a6)	; message
	lea	pr_MsgPort(a4),a0
	jsr	_LVOGetMsg(a6)
	move.l	d0,WB.Startup	; and store it
FromCLI
	lea	DOS.Name(pc),a1	; we open the dos.library
	moveq	#33,d0	; with version 33 (release 1.2)
	jsr	_LVOOpenLibrary(a6)
	move.l	d0,DOS.Base
	beq	exit	; if OpenLibrary() failed
	move.l	d0,a6
	jsr	_LVOOutput(a6)	; get the output stream handle
	move.l	d0,Out.Handle
	lea	Bienvenue.MSG(pc),a0	; and print the first message
	bsr	Print

	lea	Port.Name,a1	; are we already in memory ??
	move.l	4.w,a6
	jsr	_LVOFindPort(a6)
	tst.l	d0
	bne	RemovePatch	; remove patch if we are

	tst.l	WB.Startup	; should we get our arguments from CLI
	bne.s	GetWBArgs	; or from workbench ?

GetArgs	lea	NOFILTER.Keyword(pc),a0	; is it a NOFILTER option ?
	bsr	TestArg
	tst.l	d0
	beq.s	FilterFuncToo
	move.l	#TAG_IGNORE,Filter.Forbidden	; yes: disable the FilterFunc
	lea	DisabledFilter.MSG(pc),a0
	bsr	Print
FilterFuncToo		; tag checking (see note down there)
	bsr	SearchNextWord	; search for next word in arguments line
	tst.l	d2	; have we reached the end of the line ?
	bne.s	GetArgs	; no,then test the new word
	bra	ArgGot	; yes, go to the real program

; This routine is called with a DiskObject in a2 and a ToolType name in a1
; it returns TRUE(-1) if the tooltype value was "YES",or FALSE(0) if it
; was something else of inexisiting.
TestToolType
	move.l	do_ToolTypes(a2),a0	; The tooltypes array
	jsr	_LVOFindToolType(a6)	; icon.library will find it
	move.l	d0,a0
	move.l	a0,d0	; is there such a tooltype ?
	beq.s	InexistingTT
	moveq	#3,d1	; 4 chars to test
BuildTTValue
	lsl.l	#8,d0	; shift d0 8 bits to the left and get the next char
	move.b	(a0)+,d0	; at the end of this loop,d0 will contain
	dbra	d1,BuildTTValue	; the first 4 characters of the tt value.
; Note: we CANNOT use a move.l as we are not sure the tooltype value is even
; and as not all of us have 68020/030/040 (/060?)...
	cmp.l	#"YES"<<8,d0	; is it "YES" (+ending 0) ?
	beq.s	TTFound
InexistingTT
	moveq	#0,d0	; no
	rts
TTFound	moveq	#-1,d0	; yes
	rts

GetWBArgs	; we will get our arguments for the workbench
	move.l	WB.Startup(pc),a2	; first, examine the WBStartup
	tst.l	sm_NumArgs(a2)	; received to set the correct directory
	beq.s	NoIconLib
	move.l	sm_ArgList(a2),d0
	beq.s	NoIconLib
	move.l	d0,a2
	move.l	wa_Lock(a2),d1
	move.l	DOS.Base,a6
	jsr	_LVOCurrentDir(a6)
	lea	Icon.Name(pc),a1	; now,let's open the icon.library
	moveq	#33,d0	; version 33 (release 1.2)
	move.l	4.w,a6
	jsr	_LVOOpenLibrary(a6)
	move.l	d0,a6
	move.l	a6,d0
	beq.s	NoIconLib	; failed to open? then abort arg checking
	move.l	wa_Name(a2),a0	; get the DiskObject structure
	jsr	_LVOGetDiskObject(a6)
	move.l	d0,a2
	move.l	a2,d0
	beq.s	NoDiskObject	; no DiskObject structure ??
	lea	NOFILTER.Keyword(pc),a1	; Is this tooltype present ?
	bsr.s	TestToolType
	tst.l	d0
	beq.s	NoNoFilterToolType
	move.l	#TAG_IGNORE,Filter.Forbidden	; disable checking of Filter
NoNoFilterToolType
	move.l	a2,a0
	jsr	_LVOFreeDiskObject(a6)	; free the DiskObject structure
NoDiskObject
	move.l	a6,a1
	move.l	4.w,a6
	jsr	_LVOCloseLibrary(a6)	; and close the icon.library
NoIconLib

ArgGot	lea	ReqTools.Name(pc),a1	; let's open the reqtools.library
	moveq	#38,d0	; version 38 (release 2.0)
	jsr	_LVOOpenLibrary(a6)
	move.l	d0,ReqTools.Base
	bne.s	ReqToolsOpened	; if OpenLibrary() succeeded
	lea	NeedReqTools.MSG(pc),a0	; if it didn't, print the
	bsr	Print	; corresponding error message.
	bra	CloseDOS

TestArg	movem.l	d2/a2,-(sp)	; a2 holds the pointer to the argline
	bsr.s	SkipSpaces	; d2 is its length
TestWord			; a0 is a pointer to the word to compare with
	tst.l	d2	; is it the end of the line?
	beq.s	WordsDiffer	; then, this is not the same word
	move.b	(a2)+,d0	; next character in the argline
	cmp.b	#'a',d0	; is it a lowercase character ??
	blt.s	NoLower
	cmp.b	#'z',d0
	bgt.s	NoLower
	sub.b	#'a'-'A',d0	; yes,make it uppercase
NoLower	cmp.b	(a0)+,d0	; is it the same character as in the keyword?
	bne.s	CharsDiffer
	subq.l	#1,d2	; yes: then test the next character
	bra.s	TestWord
CharsDiffer	; we have found two characters that are different
	tst.b	-1(a0)	; was it the end of the keyword ?
	bne.s	WordsDiffer	; no: then the words are really different
	cmp.b	#' ',-1(a2)	; yes,was it the end of the word in arguments
	bgt.s	WordsDiffer
	moveq	#-1,d0	; yes: then the checking is finished
	movem.l	(sp)+,d2/a2
	rts
WordsDiffer	; the two words are different, tell it to the main routine
	moveq	#0,d0	; by setting FALSE(0) in d0
	movem.l	(sp)+,d2/a2
	rts

SkipSpaces	; this routine simply advances the argline pointer
	tst.l	d2	; to the next non-space character (words are
	beq.s	EndOfLine	; separated by spaces or characters with
	cmp.b	#' ',(a2)+	; a lower ASCII-value)
	bgt.s	SpacesSkipped
	subq.l	#1,d2
	bra.s	SkipSpaces
SpacesSkipped
	subq.l	#1,a2
EndOfLine
	rts

SearchNextWord	; this routine advances the argline pointer (a2) to the
	bsr.s	SkipSpaces	; next word
NextWord.Loop
	tst.l	d2
	beq.s	NoNextWord
	cmp.b	#' ',(a2)+
	ble.s	NextWordFound
	subq.l	#1,d2
	bra.s	NextWord.Loop
NextWordFound
	subq.l	#1,a2
NoNextWord
	rts

ReqToolsOpened
	lea	KD.Name(pc),a1	; let's open the kd_freq.library
	moveq	#3,d0	; version 3
	jsr	_LVOOpenLibrary(a6)
	move.l	d0,KD.Base
	bne.s	KDOpened
	lea	NeedKD.MSG(pc),a0
	bsr	Print
	bra	CloseReqTools

KDOpened	; let's begin the serious things
	jsr	_LVOForbid(a6)	; to prevent bad things with task switching
	move.l	#NewAllocRequest,d0	; pointer to the new function
	move.l	ReqTools.Base,a1	; pointer to library base
	move.w	#_LVOrtAllocRequestA,a0	; offset to correct
	jsr	_LVOSetFunction(a6)	; replace the function
	move.l	d0,OldAllocRequestA	; store the old function pointer

	move.l	#NewFreeRequest,d0	; this is exactly the same for
	move.l	ReqTools.Base,a1	; the other functions
	move.w	#_LVOrtFreeRequest,a0
	jsr	_LVOSetFunction(a6)
	move.l	d0,OldFreeRequest

	move.l	#NewFreeReqBuffer,d0
	move.l	ReqTools.Base,a1
	move.w	#_LVOrtFreeReqBuffer,a0
	jsr	_LVOSetFunction(a6)
	move.l	d0,OldFreeReqBuffer

	move.l	#NewChangeReqAttr,d0
	move.l	ReqTools.Base,a1
	move.w	#_LVOrtChangeReqAttrA,a0
	jsr	_LVOSetFunction(a6)
	move.l	d0,OldChangeReqAttr

	move.l	#NewFileRequest,d0
	move.l	ReqTools.Base,a1
	move.w	#_LVOrtFileRequestA,a0
	jsr	_LVOSetFunction(a6)
	move.l	d0,OldFileRequestA

	move.l	#NewFreeFileList,d0
	move.l	ReqTools.Base,a1
	move.w	#_LVOrtFreeFileList,a0
	jsr	_LVOSetFunction(a6)
	move.l	d0,OldFreeFileList
	jsr	_LVOPermit(a6)	; restore normal task-switching

	lea	PatchPort,a1	; add our dummy port to the system list
	jsr	_LVOAddPort(a6)
	lea	Patched.MSG(pc),a0	; and inform the user the patching
	bsr	Print	; has been done

	IFD	TEST	; if in debugging mode,print the addresses to
	lea	PrintChar.Routine,a2	; the new functions
	lea	Addresses.Data,a1
	lea	Addresses.FMT,a0
	move.l	4.w,a6
	jsr	_LVORawDoFmt(a6)
	ENDC

	clr.l	ProgStart-4	; we cut the segments to leave the patch
	bra.s	CloseDOS	; in memory when we quit

CloseReqTools	; This is called only if kd_freq.library failed to open
	move.l	ReqTools.Base,a1
	move.l	4.w,a6
	jsr	_LVOCloseLibrary(a6)
CloseDOS		; close the dos.library
	move.l	DOS.Base,a1
	move.l	4.w,a6
	jsr	_LVOCloseLibrary(a6)
exit	move.l	WB.Startup,d2	; did we come from workbench ?
	beq.s	ExitToCLI	; when from CLI
	move.l	4.w,a6		; if from workbench, we must reply to the
	jsr	_LVOForbid(a6)	; WBStartup message we received at the
	move.l	d2,a1		; beginning
	jsr	_LVOReplyMsg(a6)
ExitToCLI
	moveq	#0,d0		; return code

	IFND	TEST	; if not in debugging mode, simply quit, else
	rts		; lauch the test program
	ELSE

	TESTPROGRAM	; include test program (can be run alone)
	CNOP	0,4	; for longword alignment

PrintChar.Routine	; Called by RawDoFmt() with D0=CharToPrint
	move.b	d0,Char.Buffer	; NB: This is not meant for being fast
	movem.l	d0-d3/a0-a1/a6,-(sp)	; (this is only for debugging)
	move.l	Out.Handle,d1
	move.l	#Char.Buffer,d2
	moveq	#1,d3
	move.l	DOS.Base,a6
	jsr	_LVOWrite(a6)
	movem.l	(sp)+,d0-d3/a0-a1/a6
	rts
	ENDC

RemovePatch		; remove the resident patch from memory
	move.l	d0,a4	; address of the dummy port we've found
	jsr	_LVOForbid(a6)	; no multitasking for safety
	cmp.w	#VERSION,version(a4)	; is it the good version ?
	beq.s	GoodVersion
	lea	BadVersion.MSG(pc),a0	; no it isn't! Alert the user and
	bsr	Print			; quit
	jsr	_LVOPermit(a6)
	bra	CloseDOS	
GoodVersion
	move.l	customreqlist(a4),a1	; list of CustomRequests structs
	tst.l	LH_HEAD(a1)		; is there one in memory ?
	beq.s	NoMoreOwner
	lea	StillInUse.MSG(pc),a0	; yes: we cannot quit yet
	bsr	Print			; inform the user and quit
	jsr	_LVOPermit(a6)
	bra	CloseDOS
NoMoreOwner
	move.l	a4,a1			; remove the port from system list
	jsr	_LVORemPort(a6)

	move.l	oldallocrequesta(a4),d0	; and reset the old offsets in the
	move.w	#_LVOrtAllocRequestA,a0	; reqtools.library (the MsgPort is
	move.l	reqtoolsbase(a4),a1	; in fact an extended struct (see
	move.l	a1,ReqTools.Base	; at the beginning))
	jsr	_LVOSetFunction(a6)	; note that as we haven't opened
					; the reqtools.library ourselves
	move.l	oldfreerequest(a4),d0	; we have to get it from the
	move.w	#_LVOrtFreeRequest,a0	; extended message struct
	move.l	reqtoolsbase(a4),a1
	jsr	_LVOSetFunction(a6)

	move.l	oldfreereqbuffer(a4),d0
	move.w	#_LVOrtFreeReqBuffer,a0
	move.l	reqtoolsbase(a4),a1
	jsr	_LVOSetFunction(a6)

	move.l	oldchangereqattr(a4),d0
	move.w	#_LVOrtChangeReqAttrA,a0
	move.l	reqtoolsbase(a4),a1
	jsr	_LVOSetFunction(a6)

	move.l	oldfilerequesta(a4),d0
	move.w	#_LVOrtFileRequestA,a0
	move.l	reqtoolsbase(a4),a1
	jsr	_LVOSetFunction(a6)

	move.l	oldfreefilelist(a4),d0
	move.w	#_LVOrtFreeFileList,a0
	move.l	reqtoolsbase(a4),a1
	jsr	_LVOSetFunction(a6)

	jsr	_LVOPermit(a6)	; restore multitasking
	move.l	kdbase(a4),KD.Base	; get the kd_freq opened by the
	lea	Removed.MSG(pc),a0	; other process and signal the user
	bsr.s	Print
	move.l	segment(a4),d1	; seglist used by the patch
	lsr.l	#2,d1		; silly BCPL address: divide by 4
	move.l	DOS.Base,a6
	jsr	_LVOUnLoadSeg(a6)	; free the seglist
	bra	CloseReqTools

Print	movem.l	d0-d3/a0-a4/a6,-(sp)
	moveq	#0,d3	; get message length in d3
	move.b	(a0)+,d3	; (it is in the first byte)
	tst.l	WB.Startup
	bne.s	WBPrint
	move.l	a0,d2
	move.l	Out.Handle,d1	; standard output stream
	beq.s	NoPrint
	move.l	DOS.Base,a6
	jsr	_LVOWrite(a6)
BackFromPrint
	movem.l	(sp)+,d0-d3/a0-a4/a6
NoPrint	rts
WBPrint	tst.b	0(a0,d3)	; is it an important message ?
	bne.s	BackFromPrint
	move.l	a0,a3	; temporary storage of the message
	lea	ReqTools.Name(pc),a1	; open an old version of the
	moveq	#37,d0	; reqtools.library
	move.l	4.w,a6
	jsr	_LVOOpenLibrary(a6)
	move.l	d0,d6
	beq.s	BackFromPrint
	move.l	a3,a1	; message to print
	lea	WBM.Gad(pc),a2	; text for the gadget
	suba.l	a3,a3
	move.l	a3,a4
	lea	WBM.Tags(pc),a0	; tags for the request
	move.l	d6,a6
	jsr	_LVOrtEZRequestA(a6)	; go
	move.l	d6,a1
	move.l	4.w,a6
	jsr	_LVOCloseLibrary(a6)	; close everything and go
	bra.s	BackFromPrint

NOFILTER.Keyword	dc.b	'NOFILTER',0	; this option tells RTtoKD
; to ignore the reqtools RTFI_FilterFunc tag. Otherwise when this tag is
; encountered, the normal reqtools requester is used, causing all ASL
; requests to be handled by reqtools as RTPatch uses a FilterFunc. If you use
; this option,NO filtering will be done (the provided function will never be
; executed), but ASL requests will be passed to kd_freq.library. You could
; also use the FR_Bypass program, but in this case font requester cannot be
; handled by reqtools...

WBM.Title	dc.b	'RTtoKD message:',0
WBM.Gad	dc.b	'I see.',0	; text for the gadget in message requesters
DOS.Name	dc.b	'dos.library',0	; guess what this is
Icon.Name	dc.b	'icon.library',0	; name of the library!
Bienvenue.MSG	dc.b	Bienvenue.LGT
	dc.b	'RTtoKD 1.0 ©1993 F.Delacroix - FreeWare',10
	IFD	TEST
	dc.b	' *** Debugging mode ***',10
	ENDC
Bienvenue.LGT=*-Bienvenue.MSG-1
	DM

DisabledFilter.MSG	dc.b	DisabledFilter.LGT
	dc.b	' FilterFunc tag checking disabled.',10
DisabledFilter.LGT=*-DisabledFilter.MSG-1
	DM

NeedReqTools.MSG	dc.b	NeedReqTools.LGT	; this is used as
	dc.b	'You need the '	; a 'need reqtools' message as well as the
ReqTools.Name		; true reqtools.library name (the 0-byte is not
	dc.b	'reqtools.library',0	printed by DOS)
	dc.b	' V38+ to use this program.',10
NeedReqTools.LGT=*-NeedReqTools.MSG-1
	WM

NeedKD.MSG	dc.b	NeedKD.LGT	; idem
	dc.b	'You need the '
KD.Name	dc.b	'kd_freq.library',0
	dc.b	' V3+ to use this program.',10
NeedKD.LGT=*-NeedKD.MSG-1
	WM

Patched.MSG	dc.b	Patched.LGT
	dc.b	'Succesfully patched:',10
	dc.b	'     rtAllocRequestA(),rtFileRequestA(),rtFreeReqBuffer()',10
	dc.b	'     rtChangeReqAttrA(),rtFileRequestA(),rtFreeFileList()',10
Patched.LGT=*-Patched.MSG-1
	DM

BadVersion.MSG	dc.b	BadVersion.LGT
	dc.b	'Can''t remove patch: bad version number!',10
BadVersion.LGT=*-BadVersion.MSG-1
	WM

StillInUse.MSG	dc.b	StillInUse.LGT
	dc.b	'Can''t remove patch: some program still uses a requester structure.',10
	dc.b	'End it and re-run RTtoKD.',10
StillInUse.LGT=*-StillInUse.MSG-1
	WM

Removed.MSG	dc.b	Removed.LGT
	dc.b	'Patch removed.',10
Removed.LGT=*-Removed.MSG-1
	DM

	IFD	TEST	; RawDoFmt() format string. Hex numbers will be
Addresses.FMT	; inserted in place of the %lx
	dc.b	'AllocRequestA(): %lx',10
	dc.b	'FreeRequest():   %lx',10
	dc.b	'FreeReqBuffer(): %lx',10
	dc.b	'ChangeReqAttrA():%lx',10
	dc.b	'FileRequestA():  %lx',10
	dc.b	'FreeFileList():  %lx',10,0
Char.Buffer	dc.b	0	; temporary storage for the PrintChar rout.
	even
Addresses.Data	; data to be printed in place of %lx: functions addresses
	dc.l	NewAllocRequest,NewFreeRequest,NewFreeReqBuffer
	dc.l	NewChangeReqAttr,NewFileRequest,NewFreeFileList
	ENDC

	even
WBM.Tags	; tag used to print messages in requesters
	dc.l	RT_Window,0	; use workbench screen
	dc.l	RT_IDCMPFlags,IDCMP_MOUSEBUTTONS	; extra flags
	dc.l	RTEZ_ReqTitle,WBM.Title	; title of the requester window
	dc.l	RTEZ_Flags,EZREQF_CENTERTEXT	; center text in window
	dc.l	TAG_END

WB.Startup	dc.l	0	; WBStartup message (0 if we were from CLI)
DOS.Base	ds.l	1	; these are not needed by the patched
Out.Handle	ds.l	1	; routine (otherwise they would be in the
	; next section)

	section	ResidentPatch,CODE	; this will stay resident
; note: you MUSTN'T merge the two hunks of this program, or the guru will
; visit you!

PatchBegin
; from now on, all code is resident,and therefore must be re-entrant as
; it can be executed by several tasks at a time.Needless to say we must
; not modify any 'global' variables. We will always carry the useful values
; in registers,or in freshly allocated memory,so that they can be different
; for each task. This should be bore in mind for ANY program that is to be
; resident like this.

; New AllocRequest() function: allocates memory for a reqtools FileRequest,
; a kd_freq.library FileRequest and a custom request,which is linked with
; the others. If not enough mem,only the reqtools request is allocated (if
; possible)
NewAllocRequest	; req=NewAllocRequestA(type,taglist)(d0,a0)
	cmp.l	#RT_FILEREQ,d0	; is a FileRequest struct needed?
	beq.s	AllocAFileReq
	move.l	OldAllocRequestA,-(sp)	; no! This is none of our business,
	rts	; let reqtools do its normal job
AllocAFileReq
	movem.l	a2-a4/a6,-(sp)
	move.l	OldAllocRequestA,a1	; allocate a reqtools FileRequest
	jsr	(a1)	; with the normal AllocRequestA routine
	move.l	d0,a2
	tst.l	d0
	beq.s	NoKDRequest
	move.l	KD.Base,a6	; allocate a FileRequester with the kd lib
	jsr	_LVOCreateFRequest(a6)
	move.l	d0,a3
	tst.l	d0
	beq.s	NoKDRequest	; cannot allocate: get out
	moveq	#CR_SIZE,d0
	move.l	#MEMF_PUBLIC!MEMF_CLEAR,d1
	move.l	4.w,a6
	jsr	_LVOAllocMem(a6)	; allocate a custom request struct
	tst.l	d0
	bne.s	CustomRequestAllocated	; if allocation succeeded
	move.l	a3,a0
	move.l	KD.Base,a6	; otherwise free the kd request
	jsr	_LVODeleteFRequest(a6)
NoKDRequest
	move.l	a2,d0	; return a pointer to the reqtools FileRequest
	movem.l	(sp)+,a2-a4/a6	; note for all library functions: you should
	rts	; always expect registers d0-d1/a0-a1 to have been modified
	; by the library call, the others are guaranteed to be preserved.
	; so we needn't save the d0-d1/a0-a1 registers (don(t forget we are
	; using another process's stack
CustomRequestAllocated
	move.l	d0,a4	; pointer to cleared CustomRequest structure
	move.l	a2,CR_ReqToolsRequest(a4)	; fill it
	move.l	a3,CR_KDRequest(a4)
	move.l	4.w,a6
	jsr	_LVOForbid(a6)	; no multitasking while we touch the list
	lea	CustomReq.List(pc),a0	; pointer to ListHeader
	move.l	a4,a1	; pointer to node (actually a MinNode struct)
	jsr	_LVOAddTail(a6)	; let exec do it
	jsr	_LVOPermit(a6)	; restore multitasking
	bsr	FillDefaultValues
	bra.s	NoKDRequest

; New FreeRequest function: it will determine whether the given reqtools
; requester structure was allocated by NewAllocRequest() and will free
; all associated structures.If not,it will pass on to the normal function
; note: if the structure is not a FileRequest struct, no CustomRequest
; will be found,and the normal function will be called... so don't worry
NewFreeRequest	; NewFreeRequest(req)(a1)
	movem.l	a3-a4/a6,-(sp)
	move.l	a1,a3	; reqtools requester structure in a3 (for later use)
	bsr.s	FindCustomRequest	; see if we can find a pending
	move.l	a4,d0	; CustomRequest structure...
	beq.s	NoKDToFree	; Could we ?
	bsr.s	RemoveCustomRequest	; yes! remove it from the List
	move.l	CR_KDRequest(a4),a0	; get the kd requester structure
	move.l	KD.Base,a6
	jsr	_LVODeleteFRequest(a6)	; and delete it
	bsr.s	FreeCustomRequest	; then free the structure itself
NoKDToFree
	move.l	a3,a1	; then pass on to the normal reqtools FreeRequest
	movem.l	(sp)+,a3-a4/a6
	move.l	OldFreeRequest,-(sp)
	rts

; This routine searches for a Custom request struct corresponding to the
; given Reqtools request struct. The result is in A4 (0 if none is found)
FindCustomRequest	; (a4) CustomRequest=FindCustomRequest(RTRequest)(a1)
	movem.l	a0-a1/a6/d0-d1,-(sp)	; This is better
	move.l	4.w,a6
	jsr	_LVOForbid(a6)	; no multitasking,we are accessing the list
	lea	CustomReq.List(pc),a0	; pointer to ListHeader
.NotThisOne
	move.l	(a0),d0	; next node in list
	beq.s	.NotFound	; if zero,we have reached the end
	move.l	d0,a0	; pointer to structure
	cmp.l	CR_ReqToolsRequest(a0),a1	; is it the good one?
	bne.s	.NotThisOne
.NotFound
	move.l	d0,a4	; at this point,the struct (or 0) is in D0
	jsr	_LVOPermit(a6)	; multitasking allowed
	movem.l	(sp)+,a0-a1/a6/d0-d1
	rts

; This routine removes the CustomRequest structure from the list
; note: the memory occupied is not freed in this routine (see the next one)
RemoveCustomRequest	; RemoveCustomRequest(CustomRequest)(a4)
	movem.l	d0-d1/a0-a1/a6,-(sp)
	move.l	4.w,a6
	jsr	_LVOForbid(a6)	; as always when accessing public data
	move.l	a4,a1	; the node structure
	lea	CustomReq.List(pc),a0	; ListHeader structure
	jsr	_LVORemove(a6)	; let exec do it
	jsr	_LVOPermit(a6)
	movem.l	(sp)+,d0-d1/a0-a1/a6
	rts

; This one frees the memory used by CustomRequest struct (no comment)
FreeCustomRequest	; FreeCustomRequest(CustomRequest)(a4)
	movem.l	a0-a1/a6/d0-d1,-(sp)
	move.l	4.w,a6
	move.l	a4,a1
	move.l	#CR_SIZE,d0
	jsr	_LVOFreeMem(a6)
	movem.l	(sp)+,a0-a1/a6/d0-d1
	rts

; This is the big piece: the new FileRequestA() routine. Its job is first
; to see if we have a CustomRequest struct for this call, if if is patchable
; (some tags used by reqtools cannot be used), convert the calling proce-
; dure so that kd_freq understands, and then re-convert the result to a
; reqtools format. A tricky part is when using MULTISELECT. See you there.
NewFileRequest	; result=NewFileRequest(filereq,file,title,taglist)(a1/a2/a3,a0)
	movem.l	d1-d7/a0-a6,-(sp)	; yes,we save everything
	move.l	a2,a5	; this is where to store the filename
	bsr	FindCustomRequest	; find a CustomRequest struct
	move.l	a4,d6	; is there one ??
	beq	DefaultCall	; no? Then get back to the normal things
	move.l	CR_KDRequest(a4),a4	; the kd_freq FileRequest struct
	bsr	IsItPatchable	; test if we are able to emulate this call
	tst.l	d0	; -1 if we can,0 else
	beq	DefaultCall
	move.l	a3,kd_fr_reqtitle(a4)	; title of the window

	move.l	rtfi_Dir(a1),a1	; initial directory of the reqtools FR
	move.l	a0,d2	; this is a spare register (don't lose the tags)
	move.l	kd_fr_directory(a4),a0	; directory of the kd requester
CopyInitialDir
	move.b	(a1)+,(a0)+	; copy it
	bne.s	CopyInitialDir
	move.l	d2,a0	; put the taglist back in a0

	move.l	kd_fr_filename(a4),a1	; do the same with the filename
CopyFile.Loop
	move.b	(a2)+,(a1)+
	bne.s	CopyFile.Loop

	bsr	RTTagsToKDStruct	; conversion of the tags
	tst.l	d0	; this is normally a CustomData structure,or 0
	beq	NoMemoryForRequest	; if no memory. The struct holds
	move.l	d0,a3	; important data for the calling procedure
	move.l	ReqTools.Base,a6
	tst.l	CD_Window(a3)	; do we have a window to care of ?
	beq.s	WindowJobDone	; no, then ignore these flags:
	btst	#LOCKWIN,CD_Flags(a3)	; do we have to LockWindow() ?
	beq.s	DontLockWindow
	move.l	CD_Window(a3),a0
	jsr	_LVOrtLockWindow(a6)
	move.l	d0,CD_WindowLock(a3)	; store the WindowLock struct
	bra.s	WindowJobDone	; WAITPTR is then ignored
DontLockWindow
	btst	#WAITPTR,CD_Flags(a3)	; maybe a wait pointer ??
	beq.s	NoWaitPointer
	move.l	CD_Window(a3),a0	; then set it
	jsr	_LVOrtSetWaitPointer(a6)
NoWaitPointer
WindowJobDone
	move.l	rt_IntuitionBase(a6),a6
	btst	#USEPUB,CD_Flags(a3)	; must we use a public screen ?
	beq.s	NoPubToUse
	cmp.w	#36,LIB_VERSION(a6)	; is intuition recent enough ?
	blt.s	NoPubToUse
	move.l	CD_PubScrName(a3),a0	; yes: get its name
	jsr	_LVOLockPubScreen(a6)	; lock it: we get its address and
	move.l	d0,CD_Screen(a3)	; we are sure it remains there
NoPubToUse
	btst	#SCRTOFRONT,CD_Flags(a3)	; shall we bring the screen
	beq.s	DontBringScreenToFront	; to the front ?
	move.l	ib_FirstScreen(a0),d5	; store the former first screen
	move.l	CD_Screen(a3),a0	; the screen we need
	move.l	a0,d0	; is there really one ? or is it the WB screen ?
	beq.s	WorkbenchToFrontInstead
	move.l	ReqTools.Base,a6	; then do it.
	jsr	_LVOrtScreenToFrontSafely(a6)
	bra.s	ScreenBroughtToFront
WorkbenchToFrontInstead
	jsr	_LVOWBenchToFront(a6)	; it is the WB screen we need
ScreenBroughtToFront
DontBringScreenToFront
	move.l	ReqTools.Base,a6
	move.l	d6,a0		; This is our CustomRequest struct
	move.l	CR_ReqToolsRequest(a0),a0
	move.l	CD_RTFlags(a3),d4
	or.l	rtfi_Flags(a0),d4	; get the flags of the reqtools req
	btst	#FREQB_NOBUFFER,d4	; if we mustn't use a buffer to
	beq.s	ActuallyUseBuffer	; keep filenames for next call
	and.l	#~FR_KEEPBUFFERS,kd_fr_flags(a4)	; for the kd req
	bra.s	BufferBusinessDone
ActuallyUseBuffer
	or.l	#FR_KEEPBUFFERS,kd_fr_flags(a4)
BufferBusinessDone
	btst	#FREQB_MULTISELECT,d4	; multiple files to be selected?
	beq.s	SingleSelect
	or.l	#FR_SELECTLIST!FR_SLSHOWNAMES,kd_fr_flags(a4)
	bra.s	MultiSelectSelected
SingleSelect
	and.l	#~(FR_SELECTLIST!FR_SLSHOWNAMES),kd_fr_flags(a4)
MultiSelectSelected
	btst	#FREQB_NOFILES,d4	; only directories ??
	beq.s	SelectFilesToo
	or.l	#FR_DIRSONLY,kd_fr_flags(a4)
	bra.s	FileDirsDone
SelectFilesToo
	and.l	#~FR_DIRSONLY,kd_fr_flags(a4)
FileDirsDone
	btst	#FREQB_SAVE,d4	; do they want the 'Save' features ?
	beq.s	NoSave
	or.l	#FR_INVERTLOOK,kd_fr_flags(a4)
	bra.s	LookChosen
NoSave	and.l	#~FR_INVERTLOOK,kd_fr_flags(a4)
; note: for reqtools,setting the FREQB_SAVE flag disables the double-click
; option. This is translated into the FRB_INVERTLOOK flag, which only pro-
; vides a different look...
LookChosen	; Then let's go !
	move.l	CD_Screen(a3),kd_fr_screen(a4)	; screen to put req on
	move.l	a4,a0	; the kd request structure
	move.l	KD.Base,a6
	jsr	_LVONewFReq(a6)
	exg.l	d0,d5	; Let's clean up before returning...
	move.l	ReqTools.Base,a6
	btst	#SCRTOFRONT,CD_Flags(a3)	; do we have to bring back
	beq.s	NoScreenToBringBackToFront	; a screen ?
	move.l	d0,a0	; we had exchanged the d0 and d5 registers
	jsr	_LVOrtScreenToFrontSafely(a6)
; note: we MUST use a routine like ScreenToFrontSafely() as the screen that
; interests us is not ours,and it may have closed while we were busy...This
; routine first checks if it is still open before poping it to the front
NoScreenToBringBackToFront
	tst.l	CD_Window(a3)	; did we have to take care of a window ?
	beq.s	NoPointerToClear	; nah
	btst	#LOCKWIN,CD_Flags(a3)	; was it locked ??
	beq.s	NoWindowToUnlock	; nah
	move.l	CD_Window(a3),a0	; yea:unlock it
	move.l	CD_WindowLock(a3),a1
	jsr	_LVOrtUnlockWindow(a6)
	bra.s	WindowUnlocked	; in this case,WAITPTR is ignored
NoWindowToUnlock
	btst	#WAITPTR,CD_Flags(a3)	; had we set a wait pointer ?
	beq.s	NoPointerToClear	; nah
	move.l	CD_Window(a3),a0	; yea:we ClearPointer() the
	move.l	rt_IntuitionBase(a6),a6	; window just like reqtools would
	jsr	_LVOClearPointer(a6)	; have done
NoPointerToClear
WindowUnlocked
	btst	#USEPUB,CD_Flags(a3)	; had we locked a public screen?
	beq.s	NoPubScreenToUnlock
	tst.l	CD_Screen(a3)		; was there really a screen locked ?
	beq.s	NoPubScreenToUnlock
	move.l	CD_PubScrName(a3),a0	; the name
	move.l	CD_Screen(a3),a1	; the address
	move.l	ReqTools.Base,a6
	move.l	rt_IntuitionBase(a6),a6
	cmp.w	#36,LIB_VERSION(a6)	; sufficient version ?
	blt.s	NoPubScreenToUnlock
	jsr	_LVOUnlockPubScreen(a6)	; unlock:it is allowed to close now
NoPubScreenToUnlock
	sub.l	#ti_SIZEOF+4,sp	; make room on the stack for 2 tags
	move.l	sp,a0	; pointer to tag array
	move.l	#RTFI_Dir,ti_Tag(a0)	; tag for ChangeReqAttr():RTFI_Dir
	move.l	kd_fr_directory(a4),a1	; to change the directory of the
	move.l	a1,ti_Data(a0)	; reqtools request
ToEndOfDir
	tst.b	(a1)+	; go to the end of the directory name
	bne.s	ToEndOfDir
	subq.l	#1,a1	; the last char (a1 then points to a 0-byte)
	cmp.l	ti_Data(a0),a1	; have we really moved ?
	beq.s	NoSlashToDelete	; no:the dirname was empty
	cmp.b	#'/',-(a1)	; is there a slash to delete at the end?
	bne.s	NoSlashToDelete	; (reqtools doesn't appreciate / at the end)
	clr.b	(a1)	; if there is a slash,clear it
NoSlashToDelete
	clr.l	ti_SIZEOF(a0)	; TAG_DONE
	move.l	d6,a1	; our famous CustomRequest structure
	move.l	CR_ReqToolsRequest(a1),a1	; the reqtools request
	move.l	ReqTools.Base,a6
	move.l	OldChangeReqAttr,a2	; the real ChangeReqAttr() function
	jsr	(a2)
	add.l	#ti_SIZEOF+4,sp	; correct the stack now that we've finished

	move.l	kd_fr_filename(a4),a0	; we will now copy the filename to
	move.l	a5,a1	; the initial variable we had saved into a5
CopyFileName
	move.b	(a0)+,(a1)+
	bne.s	CopyFileName

	move.b	CD_Flags(a3),d3	; We haven't tested ALLOWEMPTY yet
	move.l	a3,a1	; but first,we free the CustomData structure we
	move.l	#CD_SIZEOF,d0	; don't need anymore...
	move.l	4.w,a6
	jsr	_LVOFreeMem(a6)

	move.l	d5,d0	; this is the result from the NewFReq() call
	beq.s	RequesterCancelled	; if 0,then cancel
	move.l	kd_fr_flags(a4),d0	; is there a FileList to return ?
	btst	#FRB_SELECTLIST,d0
	bne.s	ReturnAFileList
; no: a simple filename was requested
	tst.b	(a5)	; is it actually an empty file name ?
	bne.s	NotEmptyFName
	btst	#ALLOWEMPTY,d3	; is it allowed ?
	beq.s	RequesterCancelled
NotEmptyFName
	moveq	#-1,d0	; indicating the call has succeeded
NoFileRequest
	movem.l	(sp)+,d1-d7/a0-a6	; get back all our registers
	rts
NoMemoryForRequest
RequesterCancelled
	moveq	#0,d0	; the requester was canceled: return a 0 (FALSE)
	bra.s	NoFileRequest
ReturnAFileList
	move.l	kd_fr_extras(a4),a0	; the ExtraData structure
	tst.l	kd_ed_SelectList(a0)	; is there a SelectList behind ?
	beq.s	SimpleFile	; Empty select list
	moveq	#0,d2	; if 0,then it is the first entry
	bra.s	FillFileList
; This is when the user selected only one file, we build a one-file
; rtFileList:
SimpleFile
	tst.b	(a5)	; did the user actually select something?
	bne.s	SelectedOneFile
	btst	#ALLOWEMPTY,d3	; this is when the user ok'ed the requester
	beq.s	RequesterCancelled	; but without selecting anything
SelectedOneFile
	move.l	a5,a0	; there was no SelectList but a file name was chosen
	move.l	a0,d0	; the filename was already copied in the FileName var
LonelyFileLength
	tst.b	(a0)+	; calculate its length
	bne.s	LonelyFileLength
	sub.l	d0,a0
	move.l	a0,d3	; d3=FileName length+1
	move.l	d3,d0
	subq.l	#1,d3	; real length
	add.l	#MFL_SIZEOF,d0	; add struct size to it
	move.l	#MEMF_PUBLIC!MEMF_CLEAR,d1
	move.l	4.w,a6
	jsr	_LVOAllocMem(a6)	; allocate memory for FileList
	move.l	d0,a3
	move.l	a3,d0
	beq.s	RequesterCancelled
	move.l	#MFL_MATCHWORD,MFL_MatchWord(a3)	; see note later in
	move.l	a3,MFL_ListPtr(a3)	; building FileLists
	move.l	d3,MFL_FileList+rtfl_StrLen(a3)
	lea	MFL_FileName(a3),a0
	move.l	a0,MFL_FileList+rtfl_Name(a3)
	move.l	a5,a2
CopyLonelyFNameToList
	move.b	(a2)+,(a0)+
	bne.s	CopyLonelyFNameToList
	move.l	a3,d0
	add.l	#MFL_FileList,d0
	bra	NoFileRequest

; here begins a loop to build the rtFileList structure normally returned
; by the reqtools.library...
FillFileList
	move.l	a4,a0	; get the next entry in the list
	move.l	KD.Base,a6
	jsr	_LVONextSelectEntry(a6)
	tst.l	d0	; have we reached the end ?
	beq.s	FileListEnd
	move.l	d0,a2	; a2=FileName
	move.l	a2,a0
; This is another difference between the two libraries: reqtools.library
; only returns the filenames whereas kd_freq.library returns the full path
; to the selected files: this is because you can select files in multiple
; directories. The patch will only transmit the filenames (the directory
; name will be lost) no matter if the files are actually in the directory
; the requester is in. Your program will not know it. I might add an option
; to remove the unwanted files in the future. Let me know if you feel the
; need...
FileNameLen	; go to the end of the path name then search backwards
	tst.b	(a0)+
	bne.s	FileNameLen
	subq.l	#1,a0	; the 0 end of the string
SearchBaseName
	cmp.b	#'/',-(a0)	; is it a slash
	beq.s	BaseNameFound	; if it is,we have found the filename
	cmp.b	#':',(a0)
	beq.s	BaseNameFound
	cmp.l	d0,a0	; no,but have we reached the start of the buffer
	bne.s	SearchBaseName
	bra.s	BaseAlreadyFound	; if we have,abort (empty directory)
BaseNameFound
	addq.l	#1,a0	; dont take the slash in account
BaseAlreadyFound
	move.l	a0,a2	; store the address of filename
	sub.l	d0,a0	; calculate its length
	subq.l	#1,a0
	move.l	a0,d0
	move.l	d0,d3	; d3=Filename length
	add.l	#MFL_SIZEOF+1,d0	; add filename length to struct size
	move.l	#MEMF_PUBLIC!MEMF_CLEAR,d1
	move.l	4.w,a6	; we allocate an extended file list entry
	jsr	_LVOAllocMem(a6)	; (MFL stands for My FileList)
	tst.l	d0	; has the allocation succeeded ?
	beq.s	FileListEnd	; no,it is a critical situation!
	tst.l	d2	; is it the first structure ??
	bne.s	LinkStruct	; No,we must link it with the others
	move.l	d0,d5	; d5=First MFL structure (the one to return)
	moveq	#1,d2	; there is only one first structure
	bra.s	LinkDone
LinkStruct
	move.l	d0,MFL_FileList+rtfl_Next(a3)	; add the struct to the list
	add.l	#MFL_FileList,MFL_FileList+rtfl_Next(a3)
; MFL_FileList+rtfl_Next actually points to the MFL_FileList field in the
; MFL structure,not to the structure itself,this is because of.. see note
; up there.
LinkDone
	move.l	d0,a3	; Last used structure (for linkage)
	move.l	#MFL_MATCHWORD,MFL_MatchWord(a3)	; this is to
	move.l	a3,MFL_ListPtr(a3)	; recognize a structure allocated
; by RTtoKD (this is the same mechanism as the ResidentTags one),again,a
; big note up there will tell you more
	move.l	d3,MFL_FileList+rtfl_StrLen(a3)	; store FileName length
	lea	MFL_FileName(a3),a0	; then the pointer to the filename
	move.l	a0,MFL_FileList+rtfl_Name(a3)
CopyFNameToList	; and the filename itself
	move.b	(a2)+,(a0)+
	bne.s	CopyFNameToList
	bra	FillFileList	; loop the loop

FileListEnd	; finally...
	tst.l	d2
	beq	RequesterCancelled	; if selectlist was empty
	move.l	d5,d0	; pointer to the first MFL structure
	add.l	#MFL_FileList,d0	; but point to the FileList field
	bra	NoFileRequest	; this is the end

DefaultCall
	movem.l	(sp)+,d1-d7/a0-a6	; if the call cannot be patched,
	move.l	OldFileRequestA,-(sp)	; this will pass on to the normal
	rts	; routine

; this routine will return TRUE(-1) if the call can really be patched,and
; FALSE(0) if not (when forbidden tags were encountered).It is called with
; the taglist in a0 and the kd requester in a4
IsItPatchable
	movem.l	a0-a1/d1,-(sp)
	moveq	#-1,d0	; return code: true for now
	move.l	a0,d1	; is there really a taglist ?
	beq.s	CanPatch	; exit if not
NextTag	move.l	(a0),d1	; tag identifier in d1
	lea	ForbiddenTags,a1	; array of forbidden tags
	tst.l	d1	; TAG_END ??
	beq.s	CanPatch	; yes:patching is possible!
	cmp.l	#TAG_IGNORE,d1	; me must ignore this tagitem
	beq.s	ThisTagIsOK
	cmp.l	#TAG_MORE,d1	; ti_Data points to another taglist
	bne.s	NoTagMore
	move.l	ti_Data(a0),a0
	bra.s	NextTag
NoTagMore
	cmp.l	#TAG_SKIP,(a0)	; we must ignore the next tagitem
	bne.s	NextForbiddenTag
	addq.l	#ti_SIZEOF,a0
	bra.s	ThisTagIsOK
NextForbiddenTag
	tst.l	(a1)	; end of the forbidden tags array?
	beq.s	ThisTagIsOK	; yes: this tagitem is allowed
	cmp.l	#TAG_IGNORE,(a1)	; have we to care about this tag ?
	bne.s	CareAboutIt	; yes ! (the TAG_IGNORE is there when the
	addq.l	#4,a1	; user has said 'NOFILTER')
	bra.s	NextForbiddenTag
CareAboutIt
	cmp.l	(a1)+,d1	; is this tag the same as the one in d1?
	beq.s	CantPatch	; yes: patching is not possible
	bra.s	NextForbiddenTag	; was not this tag...
ThisTagIsOK
	addq.l	#ti_SIZEOF,a0	; then verify the next one
	bra.s	NextTag
CantPatch
	moveq	#0,d0	; return value...FALSE
CanPatch
	movem.l	(sp)+,a0-a1/d1
	rts

; Guess what this routine is doing...
FillDefaultValues	; FillDefaultValues(CustomRequest)(a4)
	move.l	a3,-(sp)
	move.l	CR_KDRequest(a4),a3
	clr.l	kd_fr_screen(a3)	; workbench screen
	or.l	#FR_KEEPBUFFERS,kd_fr_flags(a3)	; additionnal default flag
; (some are automatically set by CreateFRequest())
	move.l	(sp)+,a3
	rts

; Another big routine for a big job: translate the tagitems intended for
; reqtools into data understood by kd...
RTTagsToKDStruct	; (d0)CustomData=RTTagsToKDStruct(Tags,KDFrequest)(a0,a4)
	movem.l	a0-a6/d1,-(sp)
	move.l	a0,a2
	move.l	#CD_SIZEOF,d0	; allocate memory for a CustomData structure
	move.l	#MEMF_PUBLIC!MEMF_CLEAR,d1	; remember ? we must be
	move.l	4.w,a6	; re-entrant! The address of this struct will be
	jsr	_LVOAllocMem(a6)	; carried in the registers and
	move.l	d0,a5	; nowhere else.
	tst.l	d0	; No memory ? Then abort.
	beq	NoMemForCustomData	; There wouldn't be enough for fr!
	move.l	kd_fr_extras(a4),a3	; put the ExtraData structure in a3
	move.l	4.w,a6	; ExecBase
	move.l	ThisTask(a6),a0	; current task control structure
	move.l	pr_WindowPtr(a0),a0	; the pr_WindowPtr of the process
	move.l	a0,CD_Window(a5)	; normally points to a window which
	ble.s	WindowPtrInvalid	; is used to determine the screen
	move.l	wd_WScreen(a0),CD_Screen(a5)	; to put the requester on
	bra.s	WindowPtrJobDone	; if 0 or -1 the pointer is invalid
WindowPtrInvalid
	clr.l	CD_Window(a5)
	clr.l	CD_Screen(a5)	; in this case,default the WB screen
WindowPtrJobDone
	bset	#SCRTOFRONT,CD_Flags(a5)	; this is by default
	move.l	a2,d0	; is there really a taglist to scan ??
	beq	EndOfRTTags
GetRTTag
	move.l	(a2),d0	; tag identifier in d0
	beq	EndOfRTTags	; TAG_END ?
	cmp.l	#TAG_IGNORE,d0	; TAG_IGNORE ?
	beq	NextRTTag
	cmp.l	#TAG_MORE,d0	; TAG_MORE=>ti_Data points to another taglist
	bne.s	NoTagMore2
	move.l	ti_Data(a2),a2
	bra.s	GetRTTag
NoTagMore2
	cmp.l	#TAG_SKIP,d0	; TAG_SKIP=ignore the next tagitem
	bne.s	NoTagSkip2
	addq.l	#ti_SIZEOF,a2
	bra	NextRTTag
NoTagSkip2
	cmp.l	#RT_Window,d0	; this tag gives a window ptr that is used
	bne.s	NoWindowTag	; in place of the pr_WindowPtr.
	move.l	ti_Data(a2),a0
	move.l	a0,CD_Window(a5)
	beq	NextRTTag
	move.l	wd_WScreen(a0),CD_Screen(a5)
	bra	NextRTTag
NoWindowTag
	cmp.l	#RT_PubScrName,d0	; this tag says that we will use a
	bne.s	NoPubScrNameTag	; public screen whose name is pointed to by
	move.l	ReqTools.Base,a6	; ti_Data
	move.l	rt_IntuitionBase(a6),a6	; test if intuition version is >36
	cmp.w	#36,LIB_VERSION(a6)	; (if not,there is no public screen)
	blt	NextRTTag
	move.l	ti_Data(a2),CD_PubScrName(a5)	; public screen business is
	bset	#USEPUB,CD_Flags(a5)	; done in the main routine
	bra	NextRTTag
NoPubScrNameTag
	cmp.l	#RT_Screen,d0	; this tag sez that ti_Data holds a pointer
	bne.s	NoScreenTag	; to the screen structure the requester will
	move.l	ti_Data(a2),CD_Screen(a5)	; be on
	bra	NextRTTag
NoScreenTag
	cmp.l	#RT_WaitPointer,d0	; this tag says that the boolean
	bne.s	NoWaitPointerTag	; value in ti_Data determines whether
	tst.l	ti_Data(a2)	; we must put a wait pointer in the window
	beq.s	ClearWaitPointer	; or not
	bset	#WAITPTR,CD_Flags(a5)
	bra	NextRTTag
ClearWaitPointer
	bclr	#WAITPTR,CD_Flags(a5)
	bra	NextRTTag
NoWaitPointerTag
	cmp.l	#RT_LockWindow,d0	; same as before but for locking the
	bne.s	NoLockWindowTag	; window from any input
	tst.l	ti_Data(a2)
	beq.s	ClearLockWindow
	bset	#LOCKWIN,CD_Flags(a5)
	bra.s	NextRTTag
ClearLockWindow
	bclr	#LOCKWIN,CD_Flags(a5)
	bra.s	NextRTTag
NoLockWindowTag
	cmp.l	#RT_ScreenToFront,d0	; will we have to bring the screen
	bne.s	NoScreenToFrontTag	; to the front (ti_Data is a boolean
	tst.l	ti_Data(a2)	; value), defaults to TRUE
	beq.s	ClearScreenToFront
	bset	#SCRTOFRONT,CD_Flags(a5)
	bra.s	NextRTTag
ClearScreenToFront
	bclr	#SCRTOFRONT,CD_Flags(a5)
	bra.s	NextRTTag
NoScreenToFrontTag
	cmp.l	#RTFI_Flags,d0	; these are some flags for the reqtools
	bne.s	NoFlagsTag	; file requester. We just OR them with those
	move.l	CD_RTFlags(a5),d0	; already existing in the field of
	or.l	ti_Data(a2),d0	; our CustomData (this allows mutiple such
	move.l	d0,CD_RTFlags(a5)	; tags)
	bra.s	NextRTTag
NoFlagsTag
	cmp.l	#RTFI_OkText,d0	; this tag provides an alternate text for
	bne.s	NoOkTextTag	; the 'OK' gadget (note:kd allows also
	move.l	ti_Data(a2),kd_ed_oktext(a3)	; permit an alternate text
	or.l	#FR_OKTEXT,kd_fr_flags(a4)	; for cancel,but not reqtools
NoOkTextTag
	cmp.l	#RTFI_AllowEmpty,d0	; should we consider an empty file-
	bne.s	NoAllowEmptyTag	; name as a 'cancel' response ?
	tst.l	ti_Data(a2)
	beq.s	ClearAllowEmpty
	bset	#ALLOWEMPTY,CD_Flags(a5)	; default FALSE
	bra.s	NextRTTag
ClearAllowEmpty
	bclr	#ALLOWEMPTY,CD_Flags(a5)
NoAllowEmptyTag
NextRTTag
	addq.l	#ti_SIZEOF,a2
	bra	GetRTTag
EndOfRTTags
NoMemForCustomData
	move.l	a5,d0	; return the pointer to the CustomData structure
	movem.l	(sp)+,a0-a6/d1
	rts

; The new FreeFileList() function. It will free a FileList structure
; that was returned to the user when calling the filerequester for multi-
; selection. If the FileList was allocated by RTtoKD,the routine will free
; it itself,else it will let reqtools do it
NewFreeFileList	; NewFreeFileList(filelist)(a0)
	movem.l	d2/a6,-(sp)
	move.l	a0,d2	; is there really a fileList ?
	beq.s	NoEntryToFree
	cmp.l	#MFL_MATCHWORD,MFL_MatchWord-MFL_FileList(a0)	; These
	bne.s	ThisIsNotMyFileList	;  checkings are done only for the
	move.l	MFL_ListPtr-MFL_FileList(a0),d0	; first entry in the FileList
	add.l	#MFL_FileList,d0	; FileList merging is thus a BAD
	cmp.l	a0,d0		; IDEA. (see note up there)
	bne.s	ThisIsNotMyFileList
FreeEntries
	sub.l	#MFL_FileList,d2	; pointer to real MFL structure
	move.l	d2,a1
	move.l	MFL_FileList+rtfl_Next(a1),d2	; save pointer to next one
	moveq	#0,d0	; calculate the length of the structure
	move.w	MFL_FileList+rtfl_StrLen+2(a1),d0	; if RTPatch has
	bne.s	NotAReqFileList	; converted it to a req FileList, StrLen is
	move.w	MFL_FileList+rtfl_StrLen(a1),d0	; actually a WORD
NotAReqFileList	; (see notes in doc file)
	add.l	#MFL_SIZEOF+1,d0
	move.l	4.w,a6	; free it
	jsr	_LVOFreeMem(a6)
	tst.l	d2
	bne.s	FreeEntries	; loop to free the next structure
NoEntryToFree
	movem.l	(sp)+,d2/a6
	rts
ThisIsNotMyFileList	; let reqtools free its own structures
	movem.l	(sp)+,d2/a6
	move.l	OldFreeFileList,-(sp)
	rts

; The new FreeReqBuffer() function. One of the simplest ones. It will
; determine if we own a CustomRequest structure and free all associated
; buffers. Note that in all cases, the normal FreeReqBuffer() is called,
; because the reqtools buffer might not be empty.
NewFreeReqBuffer	; NewFreeReqBuffer(req)(a1)
	movem.l	d0-d1/a0-a1/a4/a6,-(sp)
	bsr	FindCustomRequest
	move.l	a4,d0
	beq.s	NormalFreeReqBuffer
	move.l	CR_KDRequest(a4),a0	; free the kd requester buffers
	move.l	KD.Base,a6
	jsr	_LVOFlushFRBuffers(a6)
NormalFreeReqBuffer
	movem.l	(sp)+,d0-d1/a0-a1/a4/a6	; call the normal function
	move.l	OldFreeReqBuffer,-(sp)
	rts

; The new ChangeReqAttr() function. This function will change some of
; the requester data. Note that the AddEntry and RemEntry tags are ignored
; as kd_freq does not allow such operations.If these tags are encountered,
; the only thing that is done is FreeReqBuffer(). Again in this case,the
; normal function is called at the end.
NewChangeReqAttr	; NewChangeReqAttr(req,taglist)(a1,a0)
	movem.l	d0-d1/a0-a4/a6,-(sp)
	bsr	FindCustomRequest
	move.l	a4,d0
	beq	NormalChangeReqAttr	; none of our requesters
	move.l	a0,d0	; is there really a taglist ?
	beq	EndOfChangeTags
GetChangeTag
	move.l	(a0),d0	; again the same damn things about tag linking
	beq	EndOfChangeTags
	cmp.l	#TAG_IGNORE,d0
	beq.s	NextChangeTag
	cmp.l	#TAG_MORE,d0
	bne.s	NoChangeTagMore
	move.l	ti_Data(a0),a0
	bra.s	GetChangeTag
NoChangeTagMore
	cmp.l	#TAG_SKIP,d0
	bne.s	NoChangeTagSkip
	addq.l	#ti_SIZEOF,a0
	bra.s	NextChangeTag
NoChangeTagSkip
	cmp.l	#RTFI_Dir,d0	; should we change the directory the
	bne.s	NoDirTag	; requester is in ?
	movem.l	a0-a1,-(sp)
	move.l	CR_KDRequest(a4),a0	; begin by flushing the kd requester
	move.l	KD.Base,a6	; buffers
	jsr	_LVOFlushFRBuffers(a6)
	movem.l	(sp)+,a0-a1
	move.l	CR_KDRequest(a4),a2	; then copy the new directory name
	move.l	kd_fr_directory(a2),a2	; into the kd requester
	move.l	ti_Data(a0),a3	; (we are not allowed to copy it directly
CopyNewDir	; into the reqtools requester, the real ChangeReqAttrA()
	move.b	(a3)+,(a2)+	; will do it for us later)
	bne.s	CopyNewDir
	bra.s	NextChangeTag

NoDirTag
	cmp.l	#RTFI_MatchPat,d0	; maybe a new pattern to match
	bne.s	NoMatchPatTag	; displayed files on ?
	move.l	CR_KDRequest(a4),a2
	move.l	kd_fr_pattern(a2),a2	; copy it right into the kd
	move.l	ti_Data(a0),a3	; requester (same remarks as the previous
CopyNewPattern	; tag)
	move.b	(a3)+,(a2)+
	bne.s	CopyNewPattern
	bra.s	NextChangeTag

NoMatchPatTag	; if we are to add or remove some entries,just free the
	cmp.l	#RTFI_AddEntry,d0	; buffers of both requester
	beq.s	TouchEntries
	cmp.l	#RTFI_RemoveEntry,d0
	bne.s	NextChangeTag
TouchEntries
	move.l	ReqTools.Base,a6
	jsr	_LVOrtFreeReqBuffer(a6)	; AddEntry and RemEntry don't do
	; anything if the buffer is not initialized,which is the case...

NextChangeTag
	addq.l	#ti_SIZEOF,a0	; get next tag
	bra	GetChangeTag
EndOfChangeTags
NormalChangeReqAttr
	movem.l	(sp)+,d0-d1/a0-a4/a6	; as I said,we call the normal
	move.l	OldChangeReqAttr,-(sp)	; function
	rts

; And this is finally all for the code, now remains a few global data,
; which we mustn't modify in any way (don't forget several tasks can be
; executing us at a time once we are thrown into the system as part of
; reqtools.library. One exception is the ListHeader for CustomRequest
; structures,but accesses to this list are always done in a Forbid()den
; mode...

PatchPort	dc.l	0,0	; This is a dummy MsgPort so that we
	dc.b	NT_MSGPORT,0	; may be removed
	dc.l	Port.Name
	dc.b	PA_IGNORE,0	; PA_IGNORE: the port does not belong to a
	dc.l	0	; task
	dc.l	*+4,0,*-8	; MessageList: should always stay empty
	dc.b	NT_MESSAGE,0	; who would send messages to a dummy MsgPort
	dc.w	VERSION		; Identification version number
ReqTools.Base	dc.l	0	; Base of the reqtools.library
KD.Base	dc.l	0	; base of the kd_freq.library
PatchSegment	dc.l	PatchBegin-4	; pointer to seglist
OldAllocRequestA	dc.l	0	; these are pointers to all the
OldFreeRequest	dc.l	0	; original functions in reqtools.library
OldFreeReqBuffer	dc.l	0
OldChangeReqAttr	dc.l	0
OldFileRequestA	dc.l	0
OldFreeFileList	dc.l	0

CustomReq.List	dc.l	*+4	; listheader structure where all the
	dc.l	0	; CustomRequest structures will be linked
	dc.l	CustomReq.List

ForbiddenTags	; these are the tags we are not able to emulate
	dc.l	RT_IDCMPFlags,RT_ReqHandler
	dc.l	RTFI_VolumeRequest
Filter.Forbidden	; a separate label as the FilterFunc may be enabled
	dc.l	RTFI_FilterFunc,0

Port.Name	; the portname. We can be found using this name
	dc.b	'RTtoKD MsgPort',0
