/*
**	patch.library
**
**	Copyright © 1993-1997 by Stefan Fuchs
**		Freely distributable.
*/

#ifndef _PATCH_INCLUDES_H
#include "patch_includes.h"
#endif

/****i* patch.library/FindPatchNotify ***************************************
*
*   NAME
*        FindPatchNotify -- Find the notify structure with the given msgport.
*
*   SYNOPSIS
*        node = FindPatchNotify( msgport )
*
*        struct Node *FindPatchNotify( struct MsgPort *);
*
*   FUNCTION
*        Find the notify structure belonging to the given messageport.
*
*   INPUTS
*        msgport = pointer to a message port
*
*   RESULT
*        node = Pointer to the node with the given messageport or NULL, if
*               not found.
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
******************************************************************************
*
*/

struct Node *FindPatchNotify( struct MsgPort *msgport)
{
struct Node *Pointer;

	for (Pointer = (struct Node *) (PatchBase->PB_NotifyListHeader.lh_Head);
	     Pointer->ln_Succ;
	     Pointer = (struct Node *)Pointer->ln_Succ)
	{
		if((struct MsgPort *)Pointer->ln_Name == msgport) return(Pointer);
	}
	return(NULL);
}


/****i* patch.library/SendNotify ***************************************
*
*   NAME
*        SendNotify -- Send messages to all notify messageports
*
*   SYNOPSIS
*        SendNotify( code, patch )
*
*        SendNotify( ULONG, struct Patch *);
*
*   FUNCTION
*        Send PatchNotify messages to all notify messageports.
*        The memory for the messages and a replyport will be allocated.
*        This functions returns only, if all messages have been send back.
*
*   INPUTS
*        code = PATCOD_xxx code indicating type of change (see patch.h)
*        patch = optional pointer to patch structure
*
*   RESULT
*
*   NOTES
*        If an error occurs (out of memory), no messages might be send.
*
*   BUGS
*
*   SEE ALSO
*        AddPatchNotifyA(), patch.h
*
******************************************************************************
*
*/

void SendNotify( ULONG code, struct Patch *patch)
{
struct Node *pointer;
struct PatchNotifyMessage *msg;
struct MsgPort *port;

	if( msg = BAllocmem(sizeof(struct PatchNotifyMessage), MEMF_CLEAR| MEMF_PUBLIC))
	{
		if( port = CreateMsgPort())
		{
			msg->pnm_ExecMessage.mn_ReplyPort = port;
			msg->pnm_ExecMessage.mn_Length = sizeof(struct PatchNotifyMessage);
			msg->pnm_Class = PATCHNOTIFY_CLASS;
			msg->pnm_Code = code;
			msg->pnm_Object = patch;

			for (pointer = (struct Node *)PatchBase->PB_NotifyListHeader.lh_Head;
			     pointer->ln_Succ;
			     pointer = (struct Node *)pointer->ln_Succ)

			{
				PutMsg( (struct MsgPort *)(pointer->ln_Name), (struct Message *)msg);
				WaitPort( port);
				GetMsg( port);
			}
			DeleteMsgPort( port);
		}
		AFreemem( msg);
	}
}
/****i* patch.library/GetMasterPatch ***************************************
*
*   NAME
*        GetMasterPatch -- Return the MasterPatch structure a Patch structure
*                          belongs to
*
*   SYNOPSIS
*        master = GetMasterPatch( patch )
*
*        struct MasterPatch *GetMasterPatch( struct Patch *);
*
*   FUNCTION
*        Return a pointer to the MasterPatch structure of a Patch structure.
*
*   INPUTS
*        patch = pointer to a patch structure
*
*   RESULT
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
******************************************************************************
*
*/

struct MasterPatch *GetMasterPatch(struct Patch *patch)
{
	return( (struct MasterPatch *)(((UBYTE *)GetListNodeHeader( (struct Node *)patch)) -offsetof(struct MasterPatch, MPS_PatchHeader)));
}


/****i* patch.library/DisconnectStartType ***************************************
*
*   NAME
*        DisconnectStartType -- Return a library's jumptable to its original state
*
*   SYNOPSIS
*        error = DisconnectStartType( master )
*
*        ULONG error DisconnectStartType( struct MasterPatch *);
*
*   FUNCTION
*        Return a library's jumptable to its original state and therefore
*        no patch.library patchcodes will be called for this function.
*        This function can fail, if a non-patch.library patch has been
*        installed after our own one.
*        This function will change the jump-table with Setfunction or
*        an own replacement routine, depending on wether the patch
*        was installed with SetFunction or not.
*
*   INPUTS
*        master = pointer to a MasterPatch structure
*
*   RESULT
*        error = errorcode as defined in patch.h
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*        patch.h, ConnectStartType()
*
******************************************************************************
*
*/


#define INSTR_JMP   0x4ef9

ULONG DisconnectStartType( struct MasterPatch *master)
{
ULONG Result = PATERR_Ok;
struct Patch *starttype;
UBYTE *memptr;

	if( master->MPS_Flags & MPSF_Attached)
	{
		starttype = (struct Patch *)FindType( (struct List *)&(master->MPS_PatchHeader), PS_TYPE_START);
		if( master->MPS_Flags & MPSF_UseSetFunction)
		{
			Disable();
			memptr = SetFunction( master->MPS_PatchedLibraryBase, master->MPS_PatchedLVO, master->MPS_SetFunctionResult);
			if( memptr != &(starttype->PS_PatchCode->PC_PatchCodeStart))
			{
				SetFunction( master->MPS_PatchedLibraryBase, master->MPS_PatchedLVO, (ULONG (*)())memptr);
				Result = PATERR_PatchInstalled;
			}
			Enable();
		}
		else
		{
			memptr = (UBYTE *)master->MPS_PatchedLibraryBase;
			memptr += master->MPS_PatchedLVO;
			Disable();
			if( *((UWORD *)memptr) == INSTR_JMP)
			{
				memptr += 2;
				if( ((ULONG *)memptr)[0] == (ULONG)(&(starttype->PS_PatchCode->PC_PatchCodeStart)))
				{
					*((ULONG *)memptr) = master->MPS_OldEntry02;
					memptr -= 2;
					*((UWORD *)memptr) = master->MPS_OldEntry01;
					CacheClearU();
					master->MPS_PatchedLibraryBase->lib_Flags |= LIBF_CHANGED;
					SumLibrary(master->MPS_PatchedLibraryBase);
				}
				else Result = PATERR_PatchInstalled;
			}
			else Result = PATERR_PatchInstalled;
			Enable();
		}
		if( Result == PATERR_Ok)
			master->MPS_Flags &= ~MPSF_Attached;
	}
	return(Result);
}


/****i* patch.library/ConnectStartType ***************************************
*
*   NAME
*        ConnectStartType -- Change a library's jump-table, to point to
*                            the StartType patchcode
*
*   SYNOPSIS
*        error = ConnectStartType( master )
*
*        ULONG error ConnectStartType( struct MasterPatch *);
*
*   FUNCTION
*        Change a library's jump-table, to point to the StartType patchcode.
*
*        This function can fail, if the jump-table entry is not in the standard
*        format.
*        This function will change the jump-table with Setfunction or
*        an own replacement routine, depending on wether PatchSetFunc is
*        active or not.
*
*   INPUTS
*        master = pointer to a MasterPatch structure
*
*   RESULT
*        error = errorcode as defined in patch.h
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*        patch.h, DisconnectStartType()
*
******************************************************************************
*
*/


ULONG ConnectStartType( struct MasterPatch *master)
{
ULONG Result = PATERR_Ok;
struct Patch *starttype;
UBYTE *memptr;

	if( (master->MPS_Flags & MPSF_Attached) == NULL)
	{
		if( master->MPS_PatchedLibraryBase)
		{
			Result = FillOrigType(master);
			if( Result == PATERR_Ok)
			{
				if(starttype = (struct Patch *)FindType( (struct List *)&(master->MPS_PatchHeader), PS_TYPE_START))
				{
					master->MPS_Flags |= MPSF_Attached;
					if( master->MPS_Flags & MPSF_UseSetFunction)
					{
						master->MPS_SetFunctionResult = SetFunction( master->MPS_PatchedLibraryBase, master->MPS_PatchedLVO, (ULONG (*)())&(starttype->PS_PatchCode->PC_PatchCodeStart));
					}
					else
					{
						memptr = (UBYTE *)master->MPS_PatchedLibraryBase;
						memptr += master->MPS_PatchedLVO;
						Disable();
						*((UWORD *)memptr) = INSTR_JMP;
						memptr += 2;
						*((ULONG *)memptr) = (ULONG)&(starttype->PS_PatchCode->PC_PatchCodeStart);
						CacheClearU();
						master->MPS_PatchedLibraryBase->lib_Flags |= LIBF_CHANGED;
						SumLibrary(master->MPS_PatchedLibraryBase);
						Enable();
					}
				}
			}
		}
	}
	return( Result);
}


/****i* patch.library/FillOrigType ***************************************
*
*   NAME
*        FillOrigType -- Set certain parameters for OrigType patch structures
*
*   SYNOPSIS
*        error = FillOrigType( master )
*
*        ULONG error FillOrigType( struct MasterPatch *);
*
*   FUNCTION
*        Read the from the library's jump-table and check, if it is in a format
*        patch.library understands.
*        Check, if PatchSetFunc is running and set the appropriate flags.
*        This function can fail, if the jump-table entry is not in the standard
*        format.
*
*   INPUTS
*        master = pointer to a MasterPatch structure
*
*   RESULT
*        error = errorcode as defined in patch.h
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*        patch.h, ConnectStartType()
*
******************************************************************************
*
*/
ULONG FillOrigType( struct MasterPatch *master)
{
struct TagItem taglist[2] =
	{ {PATT_PatchName, (ULONG) ("PatchSetFunc")},
	  TAG_DONE
	};
struct Patch *origtype;
UBYTE *memptr;

	memptr = (UBYTE *)master->MPS_PatchedLibraryBase;
	memptr += master->MPS_PatchedLVO;

	Disable();
	master->MPS_OldEntry01 = *((UWORD *)memptr);
	memptr += 2;
	master->MPS_OldEntry02 = *((ULONG *)memptr);
	Enable();

	origtype = (struct Patch *)FindType( (struct List *)&(master->MPS_PatchHeader), PS_TYPE_ORIG);

	if( master->MPS_OldEntry01 != INSTR_JMP)
		return(PATERR_FuncNotStd);

	memptr = origtype->PS_Jsr;
	memptr += 2;
	*((ULONG *)memptr) = master->MPS_OldEntry02;

	if( PatchBase->PB_Flags & PBF_PatchSetFuncActive)
		return(PATERR_Ok);

	if( FindPatchTagsA( taglist))
		return(PATERR_Ok);

	master->MPS_Flags |= MPSF_UseSetFunction;
	return(PATERR_Ok);
}

/****i* patch.library/TestUsage ***************************************
*
*   NAME
*        TestUsage -- Test, if a patchcode is in use by any task
*
*   SYNOPSIS
*        error = TestUsage( patch )
*
*        ULONG error TestUsage( struct Patch *);
*
*   FUNCTION
*        Test, if a patchcode is in use by any task.
*        If the global CheckPC flag is turned off, only test the usage counters,
*        otherwise also check all tasks and compare their program-counters
*        against specific memory locations not covered by the usage-counters.
*        Skip these tests for certain OS-functions.
*
*   INPUTS
*        patch = pointer to a Patch structure or NULL for no action.
*
*   RESULT
*        error = errorcode as defined in patch.h
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
******************************************************************************
*
*/

#define LVORemTask -288
#define LVOExit -144


ULONG TestUsage( struct Patch *patch)
{
struct MasterPatch *master;
struct Task *task;

	if( patch)
	{
		master = GetMasterPatch( patch);

/* exec/Remtask(0) does not return from rts, so ignore invalid UsageCounter */
		if( (master->MPS_PatchedLVO == LVORemTask) && strcmp( "exec.library", master->MPS_Node.ln_Name) == NULL)
			return( PATERR_Ok);

/* dos/Exit(0) does not return from rts, so ignore invalid UsageCounter */
		if( (master->MPS_PatchedLVO == LVOExit) && strcmp( "dos.library", master->MPS_Node.ln_Name) == NULL)
			return( PATERR_Ok);
		if( patch->PS_PatchCode->PC_Usage)
		{
			return(PATERR_PatchInUse);
		}
		if( PatchBase->PB_Flags & PBF_CheckPC == NULL)
			return(PATERR_Ok);

		Disable();
		for (task = (struct Task *) (SysBase->TaskReady.lh_Head);
		     task->tc_Node.ln_Succ;
		     task = (struct Task *)task->tc_Node.ln_Succ)
		{
			if(TestPCUsage( patch, GetPC( task->tc_SPReg, SysBase)))
			{
				Enable();
				return(PATERR_PatchInUse);
			}
		}

		for (task = (struct Task *) (SysBase->TaskWait.lh_Head);
		     task->tc_Node.ln_Succ;
		     task = (struct Task *)task->tc_Node.ln_Succ)
		{
			if(TestPCUsage( patch, GetPC( task->tc_SPReg, SysBase)))
			{
				Enable();
				return(PATERR_PatchInUse);
			}
		}
		Enable();
	}
	return(PATERR_Ok);
}


/****i* patch.library/TestPCUsage ***************************************
*
*   NAME
*        TestPCUsage -- Compare certain memory adresses against a PC
*
*   SYNOPSIS
*        error = TestPCUsage( patch, PC)
*
*        ULONG error TestPCUsage( struct Patch *, APTR);
*
*   FUNCTION
*        Compare certain memory adresses against a programm-counter.
*
*   INPUTS
*        patch = pointer to a Patch structure.
*        PC = pointer to the next instruction, to be executed.
*
*   RESULT
*        error = 0: Code is not in use
*                1: Code is in use
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
******************************************************************************
*
*/

ULONG TestPCUsage( struct Patch *patch, APTR pc)
{
	if(patch->PS_TestUsage1 == pc)
		return 1;
	if(patch->PS_TestUsage2 == pc)
		return 1;
	if(patch->PS_TestUsage3 == pc)
		return 1;
	return 0;
}


/****i* patch.library/ReCalcStackSize ***************************************
*
*   NAME
*        ReCalcStackSize -- Calculate the highest required stacksize
*
*   SYNOPSIS
*        ReCalcStackSize( master)
*
*        ReCalcStackSize( struct  MasterPatch *);
*
*   FUNCTION
*        Calculate the highest required stacksize for all patches and
*        place it, into the StartTypes stack fieled
*        If the stacksize has changed, allocate memory for stack caching.
*
*   INPUTS
*        master = pointer to a MasterPatch structure.
*
*   RESULT
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
******************************************************************************
*
*/

void ReCalcStackSize( struct MasterPatch *master)
{
ULONG StackSize = 0;
struct Patch *patch;

	for( patch = (struct Patch *)master->MPS_PatchHeader.mlh_Head;
	     patch->PS_Node.ln_Succ;
   	     patch= (struct Patch *)patch->PS_Node.ln_Succ)
	{
		if(( patch->PS_Node.ln_Type == PS_TYPE_USER) || ( patch->PS_Node.ln_Type == PS_TYPE_SYSTEM) || ( patch->PS_Node.ln_Type == PS_TYPE_ORIG))
		{
			if( patch->PS_StackSize > StackSize)
				StackSize = patch->PS_StackSize;
		}
	}

	patch= (struct Patch *)FindType( (struct List *)(&(master->MPS_PatchHeader)), PS_TYPE_START);
	if(patch->PS_PatchCode->PC_StackSize != StackSize)
	{
		patch->PS_PatchCode->PC_StackSize = StackSize;
		PreAllocatedStacksAlloc( master, StackSize );
	}
}

/****i* patch.library/PreAllocatedStacksAlloc ***************************************
*
*   NAME
*        PreAllocatedStacksAlloc -- Allocate memory for stack extension
*
*   SYNOPSIS
*        PreAllocatedStacksAlloc( master, stacksize)
*
*        PreAllocatedStacksAlloc( struct  MasterPatch *, ULONG);
*
*   FUNCTION
*        Allocate memory for stack extension of the given stacksize and
*        fill the slots in the masterpatch structure.
*        A stacksize, of null frees all slots.
*
*   INPUTS
*        master = pointer to a MasterPatch structure.
*	stacksize = number of bytes to allocate
*
*   RESULT
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
******************************************************************************
*
*/

void PreAllocatedStacksAlloc( struct MasterPatch *master, ULONG stacksize)
{
ULONG stacks, i;

	PreAllocatedStacksFree( master);

	if(stacksize)
	{
		stacks = MIN(STACKSLOTS,PatchBase->PB_StackExtensions);
		if( stacks)
		{
			for( i = 0; i < stacks; i++)
			{
				master->MPS_StackSlots[i] = AllocStack(stacksize);
			}
		}
	}
}
/****i* patch.library/PreAllocateStacksFree ***************************************
*
*   NAME
*        PreAllocatedStacksFree -- Free memory for stack extension
*
*   SYNOPSIS
*        PreAllocatedStacksFree( master)
*
*        PreAllocatedStacksFree( struct  MasterPatch *);
*
*   FUNCTION
*        Free memory for stack extension
*        
*
*   INPUTS
*        master = pointer to a MasterPatch structure.
*
*   RESULT
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
******************************************************************************
*
*/

void PreAllocatedStacksFree( struct MasterPatch *master)
{
ULONG i;
struct PatchStack *patchstack;

	for( i = 0; i < STACKSLOTS; i++)
	{
		patchstack = master->MPS_StackSlots[i];
		master->MPS_StackSlots[i] = 0;
		FreeStack( patchstack );
	}
}

/****i* patch.library/AllocStack ***************************************
*
*   NAME
*        AllocStack -- Allocate and initialize a new stack
*
*   SYNOPSIS
*        patchstack = AllocStack( stacksize )
*
*        APTR AllocStack( ULONG);
*
*   FUNCTION
*        Allocate memory for stack extension
*        
*
*   INPUTS
*        stacksize = number of bytes, the new stack should hold.
*
*   RESULT
*	patchstack = pointer to a PatchStack structure or NULL on failure.
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
******************************************************************************
*
*/

struct PatchStack *AllocStack( ULONG stacksize )
{
APTR stack;
struct PatchStack *patchstack = NULL;

	stack = AllocVec(stacksize + sizeof(struct PatchStack) + 8, MEMF_PUBLIC);
	if(stack )
	{
		patchstack = (struct PatchStack *)((UBYTE *)stack + stacksize);
		patchstack->PST_Reserved2 = 0xCCDDEEFF;
		patchstack->PST_Reserved1 = 0x8899AABB;

		patchstack->PST_StackSwap.stk_Upper = (ULONG) patchstack;
		patchstack->PST_StackSwap.stk_Lower = stack;

		patchstack->PST_Usage = 0L;
		patchstack->PST_Stacksize = stacksize;
		patchstack->PST_Magic2 = &patchstack->PST_Magic1;
		patchstack->PST_Magic1 = 0x50415443;   /* 'PATC' */
	}
	return( patchstack);
}
/****i* patch.library/FreeStack ***************************************
*
*   NAME
*        FreeStack -- Free memory for stack 
*
*   SYNOPSIS
*        FreeStack( patchstack)
*
*        FreeStack( APTR );
*
*   FUNCTION
*        Free memory for stack extension
*        
*
*   INPUTS
*        patchstack = pointer to a PatchStack structure or NULL for no action.
*
*   RESULT
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
******************************************************************************
*
*/

void FreeStack( struct PatchStack *patchstack )
{
	if( patchstack)
	{
		patchstack->PST_Magic1 = 0;
		patchstack->PST_Magic2 = NULL;
		patchstack = (struct PatchStack *)((UBYTE *)patchstack - patchstack->PST_Stacksize);
		FreeVec(patchstack);
	}
}
/****i* patch.library/AllocStackCached ***************************************
*
*   NAME
*        AllocStackCached -- Allocate and initialize a new stack
*
*   SYNOPSIS
*        patchstack = AllocStackCached( patch, stacksize )
*
*        APTR AllocStackCached( struct Patch *, ULONG );
*        D0                     A0              D0
*
*
*   FUNCTION
*        Allocate memory for stack extension
*        
*
*   INPUTS
*        patch = patch structure to allocate stack for a stack.
*        stacksize = number of bytes, the new stack should hold.
*
*   RESULT
*	patchstack = pointer to PatchStack structure or NULL on failure.
*
*   NOTES
*        This function must be optimized for speed and stack-usage,
*        as it may be called from patch routines.
*
*   BUGS
*
*   SEE ALSO
*
******************************************************************************
*
*/

struct PatchStack * LIBFUNC AllocStackCached( REGA0 struct Patch *patch GNUC_REGA0, REGD0 ULONG stacksize GNUC_REGD0)
{
ULONG i, stacks;
struct MasterPatch *master;

	stacks = MIN(STACKSLOTS,PatchBase->PB_StackExtensions);
	if( stacks)
	{
		master = GetMasterPatch( patch);
		for( i = 0; i < stacks; i++)
		{
			if(master->MPS_StackSlots[i])
			{
				if(master->MPS_StackSlots[i]->PST_Usage == NULL)
				{
					Disable();
					master->MPS_StackSlots[i]->PST_Usage = 1L; /* atomic */
					Enable();
					return(master->MPS_StackSlots[i]);
				}
			}
		}
	}
	if(PatchBase->PB_Flags & PBF_NoStackAllocmem)
	{
		return(NULL);
	}

	return( AllocStack(stacksize));
}
/****i* patch.library/FreeStackCached ***************************************
*
*   NAME
*        FreeStackCached -- Free memory for stack 
*
*   SYNOPSIS
*        FreeStackCached( patchstack)
*
*        FreeStackCached( APTR );
*                         a1
*
*   FUNCTION
*        Free memory for stack extension
*        
*
*   INPUTS
*        patchstack = pointer to PatchStack structure or NULL for no action.
*
*   RESULT
*
*   NOTES
*        This function must be optimized for speed and stack-usage,
*        as it may be called from patch routines.
*
*   BUGS
*
*   SEE ALSO
*
******************************************************************************
*
*/

void LIBFUNC FreeStackCached( REGA1 struct PatchStack *patchstack GNUC_REGA1)
{
	if(patchstack->PST_Usage)
	{
		Disable();
		patchstack->PST_Usage = 0L;	/* atomic */
		Enable();
	}
	else
	{
		FreeStack( patchstack);
	}
}

/****i* patch.library/PatchMatch ***************************************
*
*   NAME
*        PatchMatch -- Check, if a task is allowed to execute a patch
*
*   SYNOPSIS
*        success = PatchMatch( tasklistheader)
*
*        ULONG PatchMatch( struct TLHeader *);
*        D0                A0
*
*   FUNCTION
*        Check, if a task is allowed to execute a patch
*
*   INPUTS
*        tasklistheader = pointer to TLHeader structure.
*
*   RESULT
*        success = 
*
*
*   NOTES
*        This function must be optimized for speed and stack-usage,
*        as it may be called from patch routines.
*
*   BUGS
*
*   SEE ALSO
*
******************************************************************************
*
*/

ULONG LIBFUNC PatchMatch( REGA0 struct TLHeader *list GNUC_REGA0)
{
struct Task *task;
struct TLNode *pointer;

	task = SysBase->ThisTask;

	for (pointer = (struct TLNode *) list->TL_List.lh_Head;
	     pointer->TN_Node.ln_Succ;
	     pointer = (struct TLNode *)pointer->TN_Node.ln_Succ)
	{

		switch( pointer->TN_Node.ln_Type)
		{
			case TLI_TYPE_TASKID:
				if(task == (struct Task *)pointer->TN_Node.ln_Name)
				{
					return(list->TL_Result1);
				}
				break;

			case TLI_TYPE_TASKNAME:
				if( strcmp(task->tc_Node.ln_Name, pointer->TN_Node.ln_Name) == 0)
				{
					return(list->TL_Result1);
				}
				break;

			case TLI_TYPE_TASKPATTERN:
				if( MatchPatternNoCase( pointer->TN_Pattern, task->tc_Node.ln_Name))
				{
					return(list->TL_Result1);
				}
				break;
		}
	}

	return(list->TL_Result2);
}


/****i* patch.library/SAVEObtainSemaphoreShared ***************************************
*
*   NAME
*        SAVEObtainSemaphoreShared -- Safely get shared access to the global semaphore
*
*   SYNOPSIS
*        error = SAVEObtainSemaphoreShared( )
*
*        ULONG error SAVEObtainSemaphoreShared( void);
*
*   FUNCTION
*        Get shared access to the global patch.library semaphore without
*        breaking forbid() or disable() unless the global BreakForbid flag
*        has been set.
*
*   INPUTS
*
*   RESULT
*        error = errorcode as defined in patch.h
*
*   NOTES
*        On V38 or less this function returns an exclusive semaphore.
*
*   BUGS
*
*   SEE ALSO
*        SAVEObtainSemaphore(), patch.h
*
******************************************************************************
*
*/

ULONG SAVEObtainSemaphoreShared( void)
{
	if(SysBase->LibNode.lib_Version < 39L)
	{
		return(SAVEObtainSemaphore());
	}

	if( (SysBase->IDNestCnt == -1 && SysBase->TDNestCnt == -1) ||
            (PatchBase->PB_Flags & PBF_BreakForbid))
		ObtainSemaphoreShared(&(PatchBase->PB_Semaphore));
	else
	{
		if( AttemptSemaphoreShared(&(PatchBase->PB_Semaphore)) == FALSE)
			return( PATERR_NoSemaphore);
	}
	return(PATERR_Ok);
}

/****i* patch.library/SAVEObtainSemaphore ***************************************
*
*   NAME
*        SAVEObtainSemaphore -- Safely get access to the global semaphore
*
*   SYNOPSIS
*        error = SAVEObtainSemaphore( )
*
*        ULONG error SAVEObtainSemaphore( void);
*
*   FUNCTION
*        Get access to the global patch.library semaphore without
*        breaking forbid() or disable() unless the global BreakForbid flag
*        has been set.
*
*   INPUTS
*
*   RESULT
*        error = errorcode as defined in patch.h
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*        SAVEObtainSemaphoreShared(), patch.h
*
******************************************************************************
*
*/

ULONG SAVEObtainSemaphore( void)
{
	if( (SysBase->IDNestCnt == -1 && SysBase->TDNestCnt == -1) || (PatchBase->PB_Flags & PBF_BreakForbid))
		ObtainSemaphore(&(PatchBase->PB_Semaphore));
	else
	{
		if( AttemptSemaphore(&(PatchBase->PB_Semaphore)) == FALSE)
			return( PATERR_NoSemaphore);
	}
	return(PATERR_Ok);
}

/****i* patch.library/TestPatchHandle ***************************************
*
*   NAME
*        TestPatchHandle -- Test, if a pointer to a patch structure is valid.
*
*   SYNOPSIS
*        error = TestPatchHandle( patch)
*
*        ULONG error TestPatchHandle( struct Patch *);
*
*   FUNCTION
*        Test, if a pointer to a patch structure is still valid.
*
*   INPUTS
*        patch = pointer to a (hopefully) valid patch structure or NULL
*                for no action.
*
*   RESULT
*        error = errorcode as defined in patch.h
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*        patch.h
*
******************************************************************************
*
*/
ULONG TestPatchHandle( struct Patch *patch)
{
struct MasterPatch *master;
struct Node *pointer;
	if( patch)
	{
		for( master = (struct MasterPatch *)(&(PatchBase->PB_MasterPatchHeader))->lh_Head;
		     master->MPS_Node.ln_Succ;
		     master = (struct MasterPatch *)master->MPS_Node.ln_Succ)
		{
			for( pointer = (struct Node *)(&(master->MPS_PatchHeader))->mlh_Head;
			     pointer->ln_Succ;
			     pointer = (struct Node *)pointer->ln_Succ)
			{
				if( pointer == (struct Node *)patch) return(PATERR_Ok);
			}
		}
	}
	return( PATERR_InvalidHandle);
}

/****i* patch.library/BAllocmem ***************************************
*
*   NAME
*        BAllocmem -- Allocate memory from the memory pool remembering
*                     its size.
*
*   SYNOPSIS
*        memory = BAllocmem( size, attributes )
*
*        APTR BAllocmem( ULONG, ULONG );
*
*   FUNCTION
*        Allocate memory from the global or (if it exists) from a local
*        memory pool and remember its size.
*        Deallocate this memory with AFreemem().
*
*   INPUTS
*        size = number of bytes to allocate
*        attributes = memory attributes (only required, if no local
*                     memory pool is available)
*
*   RESULT
*        memory = pointer to newly allocated memory block or NULL on failure
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*        Allocmem(), AllocVec(), AFreemem()
*
******************************************************************************
*
*/


APTR BAllocmem( ULONG size, ULONG attr)
{
	return( AAllocmem( size, attr, PatchBase->PB_MemoryPool));
}

