/*
** PNG 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  44
#define REVISION 6
#define LIBNAME  "png.datatype\0png 44.6 (9.9.2001) by Gunther Nikl\r\n"

#if !defined(__HAVE_68881__)
#define HAVEFPU(x) (0)
#else
#define HAVEFPU(x) ((x|(AFF_68020|AFF_68881)) != x)
#endif

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

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

LONG
ReturnError(VOID)
{
  return -1;
}

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

struct ExecBase *SysBase = NULL;

#if defined(__GNUC__) && !defined(__HAVE_68881__)
  struct Library *UtilityBase;
  ALIAS(__UtilityBase,UtilityBase)
  struct Library *MathIeeeSingBasBase;
  ALIAS(__MathIeeeSingBasBase,MathIeeeSingBasBase)
  struct Library *MathIeeeDoubBasBase;
  ALIAS(__MathIeeeDoubBasBase,MathIeeeDoubBasBase)
  struct Library *MathIeeeDoubTransBase;
  ALIAS(__MathIeeeDoubTransBase,MathIeeeDoubTransBase)
  #define __UserLibInit() \
    (UtilityBase=cb->UtilityBase,MathIeeeSingBasBase=OpenLibrary("mathieeesingbas.library",37)) == NULL || \
    (MathIeeeDoubBasBase=OpenLibrary("mathieeedoubbas.library",37)) == NULL || \
    (MathIeeeDoubTransBase=OpenLibrary("mathieeedoubtrans.library",37)) == NULL
  #define __UserLibTerm() \
    CloseLibrary(MathIeeeDoubTransBase); MathIeeeDoubTransBase = NULL; \
    CloseLibrary(MathIeeeDoubBasBase); MathIeeeDoubBasBase = NULL; \
    CloseLibrary(MathIeeeSingBasBase); MathIeeeSingBasBase = NULL; \
    UtilityBase = NULL
#elif defined(__SASC) && defined(_FFP)
  struct Library *UtilityBase,*MathBase,*MathTransBase;
  #define __UserLibInit() \
    (UtilityBase=cb->UtilityBase,MathBase=OpenLibrary("mathffp.library",37)) == NULL || \
    (MathTransBase=OpenLibrary("mathtrans.library",37)) == NULL
  #define __UserLibTerm() \
    CloseLibrary(MathTransBase); MathTransBase = NULL; \
    CloseLibrary(MathBase); MathBase = NULL; \
    UtilityBase = NULL;
#else
  #define __UserLibInit() 0
  #define __UserLibTerm()
#endif

INLINE APTR EB(APTR eb) { return (SysBase=eb); }

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

  /* 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;

    __UserLibTerm();

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

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

    CloseLibrary(cb->UtilityBase);
    cb->UtilityBase = 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 && (Cleanup(cb),!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))
{
  BPTR Result = 0;

  // Check and decrement usage count

  if (!cb->LibNode.lib_OpenCnt || !--cb->LibNode.lib_OpenCnt)
    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;

  // Increment the user count and prevent delayed expunge

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

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

  ObtainSemaphore(&cb->LockSemaphore);

  // Already initialized?

  if (!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->UtilityBase=OpenLibrary("utility.library",39)) == NULL ||
       (cb->DataTypesBase=OpenLibrary("datatypes.library",39)) == NULL ||
       (cb->SuperClassBase=OpenLibrary("datatypes/picture.datatype",39)) == NULL ||
       __UserLibInit() ||
       (cb->PictureClass=CreateClass(cb,IntBase)) == NULL))
  {
    Cleanup(cb); Result = NULL; --cb->LibNode.lib_OpenCnt;
  }

  // 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) = EB(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);

  // Support for a hack...

  cb->FBlit = FindPort("FBlit");

  // sanity check(s)

  if (39>SysBase->LibNode.lib_Version || HAVEFPU(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
};
