/*
** BMP Datatype
**
** written by Gunther Nikl (gnikl@informatik.uni-rostock.de)
** 
** loosly based on libinit.c of AIFF datatype 1.16 from Olaf "Olsen" Barthel
*/

#include "classbase.h"

/****************************************************************************/

#define VERSION  40
#define REVISION 12
#define LIBNAME  "bmp.datatype\0bmp 40.12 (10.4.99) by Gunther Nikl\r\n"

PLAIN( struct Library *UtilityBase; )
PLAIN( ALIAS(__UtilityBase,UtilityBase) )

/****************************************************************************/

  // First executable routine of this library; must return an error
  // to the unsuspecting caller

LONG
ReturnError(VOID)
{
  return -1;
}

/****************************************************************************/

  /* Cleanup(struct ClassBase *cb):
   *
   *  Frees all resources allocated by LibOpen()
   */

STATIC VOID
Cleanup(struct ClassBase *cb)
{
  LREG(a6,APTR IntuitionBase) = cb->IntuitionBase;
  struct IClass *cl;

  // Note that if FreeClass() doesn't succeed the
  // best the code can do is leave the library in
  // memory thus not crashing the machine due to
  // this kind of error.

  if ((cl=cb->PictureClass) && !FreeClass(cl)) {
    AddClass(cl);
  }
  else {

    LREG(a6,APTR SysBase) = cb->SysBase;

    cb->PictureClass = NULL;

    PLAIN( CloseLibrary(UtilityBase);
    UtilityBase = NULL; )

    CloseLibrary(cb->SuperClassBase);
    cb->SuperClassBase = NULL;

    CloseLibrary(cb->DataTypesBase);
    cb->DataTypesBase = NULL;

    CloseLibrary(cb->GfxBase);
    cb->GfxBase = NULL;

    CloseLibrary(cb->IntuitionBase);
    cb->IntuitionBase = NULL;

    CloseLibrary(cb->DOSBase);
    cb->DOSBase = NULL;
  }
}

  /* GetClassEngine(REG(a6,struct ClassBase *cb)):
   *
   *  Get access to the class this library implements
   */

ASM(APTR)
GetClassEngine(REG(a6,struct ClassBase *cb))
{
  return cb->PictureClass;
}

  /* LibReserved(VOID):
   *
   *  The mandatory reserved library function
   */

LONG
LibReserved(VOID)
{
  return 0;
}

  /* LibExpunge(REG(a6,struct ClassBase *cb)):
   *
   *  Expunge the library, remove it from memory
   */

ASM(BPTR)
LibExpunge(REG(a6,struct ClassBase *cb))
{
  BPTR Result = 0;

  // Expunge it later

  cb->LibNode.lib_Flags |= LIBF_DELEXP;

  // Can we get away with this?

  if (!cb->LibNode.lib_OpenCnt && !cb->PictureClass) {

    LREG(a6,APTR SysBase) = cb->SysBase;

    // Remove the library from the public list

    Remove((struct Node *)cb);

    // Return the seglist, so it can be unloaded

    Result = cb->SegList;

    // Free the vector table and the library data

    FreeMem((UBYTE *)cb-cb->LibNode.lib_NegSize,cb->LibNode.lib_NegSize+cb->LibNode.lib_PosSize);
  }

  // Return the segment pointer, if any

  return Result;
}

  /* LibClose(REG(a6,struct ClassBase *cb)):
   *
   *  Close the library, as called by CloseLibrary()
   */

ASM(BPTR)
LibClose(REG(a6,struct ClassBase *cb))
{
  LREG(a6,APTR SysBase) = cb->SysBase;
  BPTR Result;

  // We are going to modify shared data,
  // so watch out

  ObtainSemaphore(&cb->LockSemaphore);

  // Decrement usage count and check how
  // many customers we still have

  if (!cb->LibNode.lib_OpenCnt || !--cb->LibNode.lib_OpenCnt)
    Cleanup(cb);

  // Release the lock

  ReleaseSemaphore(&cb->LockSemaphore);

  // Can we remove ourselves?

  Result = 0;

  if (cb->LibNode.lib_Flags & LIBF_DELEXP)
    Result = LibExpunge(cb);

  return Result;
}

INLINE struct IClass *CreateClass(APTR cb,APTR IntuitionBase)
{
  struct IClass *cl = MakeClass(LIBNAME,"datatypes/picture.datatype"+10,NULL,0,0);
  ASM(VOID) Dispatch();

  if (cl) {
    cl->cl_Dispatcher.h_Entry = (HOOKFUNC)Dispatch; cl->cl_UserData = (ULONG)cb; AddClass(cl);
  }

  return cl;
}

  /* LibOpen(REG(a6,struct ClassBase *cb)):
   *
   *  Open the library, as called via OpenLibrary()
   */

ASM(APTR)
LibOpen(REG(a6,struct ClassBase *cb))
{
  APTR IntBase, SysBase = cb->SysBase, Result = cb;
  UWORD OpenCount;

  // We are going to modify data while in multitasking,
  // so watch out

  ObtainSemaphore(&cb->LockSemaphore);

  // Is this the first initialization?

  if ((OpenCount=cb->LibNode.lib_OpenCnt) == 0 && !cb->PictureClass &&
      ((cb->DOSBase=OpenLibrary("dos.library",39)) == NULL ||
       (cb->IntuitionBase=IntBase=OpenLibrary("intuition.library",39)) == NULL ||
       (cb->GfxBase=OpenLibrary("graphics.library",39)) == NULL ||
       (cb->DataTypesBase=OpenLibrary("datatypes.library",39)) == NULL ||
       (cb->SuperClassBase=OpenLibrary("datatypes/picture.datatype",39)) == NULL ||
       PLAIN( (UtilityBase=OpenLibrary("utility.library",39)) == NULL || )
       (cb->PictureClass=CreateClass(cb,IntBase)) == NULL))
  {
    Cleanup(cb); Result = NULL;
  }
  else
    ++OpenCount;

  // Prevent delayed expunge and adjust OpenCount

  cb->LibNode.lib_Flags &= ~LIBF_DELEXP;
  cb->LibNode.lib_OpenCnt = OpenCount;

  // Release the lock

  ReleaseSemaphore(&cb->LockSemaphore);

  // Return the library base, if any

  return Result;
}

  /* LibInit():
   *
   *  Initialize the library
   */

ASM(APTR)
LibInit(REG(a0,BPTR SegList),REG(d0,struct ClassBase *cb),REG(a6,APTR sysBase))
{
  LREG(a6,struct ExecBase *SysBase) = sysBase;

  // Set up the header data; everything that doesn't get
  // set up here will have been set up by InitResident()

  cb->LibNode.lib_Revision = REVISION;

  // Remember the segment pointer

  cb->SegList = SegList;

  // Remember the exec library base pointer

  cb->SysBase = &SysBase->LibNode;

  // Initialize the shared data access semaphore

  InitSemaphore(&cb->LockSemaphore);

  // sanity check(s)

  if (39>SysBase->LibNode.lib_Version || REQUIRES_68020(SysBase->AttnFlags)) {
    FreeMem((UBYTE *)cb-cb->LibNode.lib_NegSize,cb->LibNode.lib_NegSize+cb->LibNode.lib_PosSize);
    cb = NULL;
  }

  return cb;
}

/****************************************************************************/

  // This is the table of functions that make up the library. The first
  // four are mandatory, everything following it are user callable
  // routines. The table is terminated by the value -1.

static const APTR LibVectors[] = {
  (APTR)LibOpen,
  (APTR)LibClose,
  (APTR)LibExpunge,
  (APTR)LibReserved,
  (APTR)GetClassEngine,
  (APTR)-1
};

  // The following data structures and data are responsible for
  // setting up the Library base data structure and the library
  // function vector.

static const ULONG LibInitTable[] = {
  (ULONG)sizeof(struct ClassBase), // Size of the base data structure
  (ULONG)LibVectors,               // Points to the function vector
  (ULONG)NULL,                     // Library base data structure setup table
  (ULONG)LibInit                   // The address of the routine to do the setup
};

/****************************************************************************/

  // The library loader looks for this marker in the memory
  // the library code and data will occupy. It is responsible
  // setting up the Library base data structure.

static const struct Resident RomTag = {
  RTC_MATCHWORD,                // Marker value
  (struct Resident *)&RomTag,   // This points back to itself
  (struct Resident *)&RomTag+1, // This points behind this marker
  RTF_AUTOINIT,                 // The Library should be set up according to the given table
  VERSION,                      // The version of this Library
  NT_LIBRARY,                   // This defines this module as a Library
  0,                            // Initialization priority of this Library; unused
  LIBNAME,                      // Points to the name of the Library
  LIBNAME+13,                   // The identification string of this Library
  (APTR)LibInitTable            // This table is for initializing the Library
};
