/*
    InstallWedge, RemoveWedge and GetJumpTable were taken from
    UnixDirsII (by Martin W. Scott, mws@castle.ed.ac.uk)
    They are based on a method used in ISpy, a CBM-provided AmigaMail example.
*/

#include <dos.h>
#include <signal.h>
#include <exec/types.h>
#include <exec/execbase.h>
#include <exec/memory.h>
#include <exec/semaphores.h>
#include <string.h>
#include <proto/exec.h>

/* The name this JumpTable/Semaphore will get. */
UBYTE *JTName = "AVWM-WindowMonitor-JT";

#include "wbwm.h"
#include "jumptable.h"

extern struct LVOTable LVOArray[NUMBEROFFUNCTIONS];

BOOL
InstallWedge (VOID)
{
  struct JumpTable *jumptable;
  ULONG *addressptr;
  UCOUNT i, j;

  Forbid ();

  /* Get pointer to JumpTable. Create it if necessary */
  if (jumptable = GetJumpTable (JTName))
  {
    /* Try to get exclusive lock on semaphore, in case it already existed. */
    if (AttemptSemaphore ((struct SignalSemaphore *) jumptable))
    {
      /* Make sure nobody else has function addresses in the jumptable */
      if (jumptable->jt_Owner == NULL)
      {
	jumptable->jt_Owner = FindTask (0);
	/* Don't want to disable any longer than necessary */
	Disable ();

	for (i = 2, j = 0; i < NUMBEROFFUNCTIONS * 6; i += 6, j++)
	{
	  /* Replace addresses in the jumptable with my own. */
	  addressptr = (ULONG *) ((UBYTE *) jumptable->jt_Function + i);
	  (*((ULONG *) LVOArray[j].lt_oldFunction)) = (ULONG) * addressptr;
	  *addressptr = (ULONG) LVOArray[j].lt_newFunction;
	}
	Enable ();
      } 
      else
	printf ("Already running.\n");
      ReleaseSemaphore ((struct SignalSemaphore *) jumptable);
    }
    else
      printf ("Can't lock table.\n");
  }
  else
    printf ("Can't create jumptable\n");
  Permit ();

  return ((BOOL) jumptable);
}

BOOL
RemoveWedge (VOID)
{
  struct JumpTable *jumptable;
  ULONG *addressptr;
  UCOUNT i, j;

  Forbid ();

  if (jumptable = GetJumpTable (JTName))
  {
    /* Check if this task owns this jumptable */
    if (jumptable->jt_Owner == FindTask (0))
    {
      /* Get the semaphore exclusively.
       * Depending on what got SetFunction ()'ed this could take some time.
       * Also note that shared locks are used to indicate the code is
       * being executed and that shared locks can jump ahead of queue'ed
       * exclusive locks, adding to the waittime.
       */
      ObtainSemaphore ((struct SignalSemaphore *) jumptable);

      Disable ();

      /* Restore old pointers in jumptable */

      for (i = 2, j = 0; i < NUMBEROFFUNCTIONS * 6; i += 6, j++)
      {
        addressptr = (ULONG *) ((UBYTE *) jumptable->jt_Function + i);
        *addressptr = (*((ULONG *) LVOArray[j].lt_oldFunction));
      }
      Enable ();

      jumptable->jt_Owner = NULL;
      ReleaseSemaphore ((struct SignalSemaphore *) jumptable);
    }
  }
  Permit ();

  return (TRUE);
}

struct JumpTable *
GetJumpTable (UBYTE * name)
{
  struct JumpTable *jumptable;
  ULONG *addressptr;
  UWORD *jmpinstr;
  UBYTE *jtname;
  UCOUNT i, j;

  /* Not really necessary to forbid again, just to indicate that I don't
   * want another task to create the semaphore while I'm trying to do the
   * same. Here GetJumpTable () is only called from InstallWedge (), so it
   * will just bump the forbid count.
   */
  Forbid ();

  if (!(jumptable = (struct JumpTable *) FindSemaphore (name)))
  {
    if (jumptable = AllocMem (sizeof (struct JumpTable), MEMF_PUBLIC | MEMF_CLEAR))
    {
      if (jtname = AllocMem (strlen (name) + 1, MEMF_PUBLIC | MEMF_CLEAR))
      {
        for (i = 0, j = 0; i < NUMBEROFFUNCTIONS * 6; i += 6, j++)
        {
          jmpinstr = (UWORD *) ((UBYTE *) jumptable->jt_Function + i);
          *jmpinstr = 0x4EF9;

          addressptr = (ULONG *) ((UBYTE *) jumptable->jt_Function + i + 2);
          *addressptr = (ULONG) SetFunction (
             (struct Library *) (*((ULONG *) LVOArray[j].lt_LibBase)),
             LVOArray[j].lt_LVO,
             (VOID *) ((UBYTE *) jumptable->jt_Function + i)
          );
        }
        jumptable->jt_Semaphore.ss_Link.ln_Pri = 0;

        strcpy (jtname, name);
        jumptable->jt_Semaphore.ss_Link.ln_Name = jtname;
        AddSemaphore ((struct SignalSemaphore *) jumptable);
      }
      else {
        FreeMem (jumptable, sizeof (struct JumpTable));
        jumptable = NULL;
      }
    }
  }
  Permit ();

  /* If succeeded, you now have a jumptable which entries point to the original
   * library functions. If another task SetFunction ()'ed one or more of those
   * already, that task can never go away anymore.
   */
  return (jumptable);
}
