/*
** PatchUtil.c von Patrick Ohly
** unterstützende Funktionen für Patches
** © 1994 Patrick Ohly
*/

#include <dos/dosextens.h>

#ifdef _DCC
#include <clib/exec_protos.h>
#else
#include <proto/exec.h>
#endif

#include <exec/memory.h>
#include <exec/execbase.h>
#include "PatchUtil.h"

extern struct ExecBase *SysBase;

/* Werte des Assembler-Befehls JMP */
#define JMP_CMD 0x4EF9

/* Aufbau der Assembler-Sprungbefehle: */
#define JMP 0
#define JMP_ADDR 1 /* ULONG */
/* nur bei alter dos.library verwendet:
** erst D0 auf Funktions-abhängigen Wert setzen,
** dann DOS-dispatcher anspringen
*/
#define MOVEQ 3
#define JMP_DOS 4
#define JMP_DOS_ADDR 5 /* ULONG */
#define BUFFER_SIZE 7

/*
** SafeSetFunction():
** Funktion: änderte einen Library-Vektor auf neue Funktion um
**
** Argumente: library_base - zu patchende Library
**            lvo          - Offset der zu patchenden Funktion
**            new_function - die eigene Funktion
**            handle       - Zeiger auf Langwort
** Ergebnis:  old_function - Zeiger auf alte Funktion oder NULL für Fehler
**            handle       - wird für RemoveFunction() auf Zeiger
**                           des Sprungbefehls gesetzt
*/

APTR SafeSetFunction(
   struct Library *library_base,
   LONG offset,
   APTR new_function,
   APTR *handle)
{
	UWORD *asm_buffer;
	APTR result = NULL;

	if(asm_buffer = AllocMem(2 * BUFFER_SIZE, MEMF_CLEAR|MEMF_PUBLIC))
	{
		ULONG current_function;
		UWORD *vector;
		BOOL standard_lvo;

		*handle = asm_buffer;

		Disable();

		vector = (UWORD *)(((ULONG)library_base) + offset);

		/* Standard- und DOS-Format unterstützen  */
		standard_lvo = (vector[0] == JMP_CMD);

		/* JMP- bzw. BRA-Zieladresse ermitteln */
		if(standard_lvo)
			current_function = *((ULONG *)&vector[1]);
		else
			current_function = (ULONG)vector +
			                   vector[2] + 4;

		/* eigene Funktion in asm_buffer eintragen */
		asm_buffer[JMP]                   = JMP_CMD;
		*((APTR *)&asm_buffer[JMP_ADDR]) = new_function;
		/* alten Inhalt retten */
		if(standard_lvo)
			result = (APTR)current_function;
		else
		{
			/* alten DOS-Aufruf nachstellen */
			/* moveq #?,d0 */
			asm_buffer[MOVEQ]   = vector[0];
			asm_buffer[JMP_DOS] = JMP_CMD;
			*((ULONG *)&asm_buffer[JMP_DOS_ADDR]) =
			                      current_function;
			result = &asm_buffer[MOVEQ];
		}

		/* neuen Zeiger in LVO eintragen */
		vector[0] = JMP_CMD;
		*((ULONG *)&vector[1]) = (ULONG)asm_buffer;

		if(SysBase->LibNode.lib_Version >= 36)
			CacheClearU();

		/* Library neu berechnen */
		library_base->lib_Flags |= LIBF_CHANGED;
		SumLibrary(library_base);

		Enable();
	}

	return(result);
}

/*
** RemoveFunction()
** Funktion: macht SafeSetFunction() rückgängig
**
** Argumente: old_function - der Funktionswert, der zurückgeschrieben wird
**            handle       - der von SafeSetFunction() gesetzte Wert
*/

VOID RemoveFunction(APTR old_function, APTR handle)
{
	Disable();

	/* alten Wert zurückschreiben */
	*((APTR *)&(((UWORD *)handle)[JMP_ADDR])) = old_function;

	if(SysBase->LibNode.lib_Version >= 36)
		CacheClearU();

	Enable();
}

/*
** IsProcess:
** Funktion: testet auf Process
** Ergebnis: TRUE zurück, wenn von einem Prozeß aufgerufen,
**           ansonsten FALSE
*/
BOOL IsProcess(VOID)
{
   struct Task *my_task;

   my_task = FindTask(NULL);
   return((BOOL)
         (my_task->tc_Node.ln_Type == NT_PROCESS));
}
