/* 
   Carthasis Library

   Author:	Jörg van de Loo
			Hövel 15
			47559 Kranenburg
			FRG

   using MAXON's C++ 4 in C mode.

   USE LARGE DATA MODEL TO COMPILE THIS FILE
   AND LINK WITHOUT STARTUP-CODE.

   NOTE: A library has to be reentrant, which means that it's not allowed to use
   global variables that can be modified at the same time by different
   callers. Thus you have to make them local, e.g. a good thing is to use
   local stack variables and of course AllocMem() allocated structures or
   equivalent allocated structures within the routine which is executed!
*/

#include	<exec/nodes.h>
#include	<exec/memory.h>
#include	<exec/lists.h>
#include	<exec/libraries.h>
#include	<exec/resident.h>
#include	<exec/execbase.h>

#include	<libraries/dos.h>
#include	<dos/dosextens.h>

#include	<graphics/gfxbase.h>

#include	<intuition/intuitionbase.h>

#include	<utility/tagitem.h>

#include	<clib/exec_protos.h>
#include	<pragma/exec_lib.h>

#include	<clib/dos_protos.h>
#include	<pragma/dos_lib.h>

#include	<clib/graphics_protos.h>
#include	<pragma/graphics_lib.h>

#include	<clib/wb_protos.h>
#include	<pragma/wb_lib.h>

#include	<clib/icon_protos.h>
#include	<pragma/icon_lib.h>


#ifdef __GNUC__
#define ASM
#define REG(reg,arg) arg __asm(#reg)
#else
#if !defined (__MAXON__)
#define ASM __asm
#endif
#define REG(reg,arg) register __##reg arg
#endif

#ifdef __MAXON__
extern "C" void GetBaseReg(void);
#define __saveds
#define ASM
#endif

#if defined(__MAXON__) || defined(__STORM__)
#define __inline
#define __stdargs
#endif

/* ###########################################################

   P R O T O T Y P E S
*/

struct SharedLib *LIB_Open(REG(a6, struct SharedLib *base));
int LIB_Close( REG(a6, struct SharedLib *base));
BPTR LIB_Expunge( REG(a6, struct SharedLib *base));
int LIB_Reserved( REG(a6, struct SharedLib *base));


int User_Startup( REG(a6, struct SharedLib *base));
void User_Cleanup( REG(a6, struct SharedLib *base));

// Up from here thre library functions (prototypes)

void ModifyCharacter( struct FontDescription *fd, REG(a6, struct SharedLib *base));
void ModifyCharacterA( REG(a0, struct FontDescription *fd), REG(a6, struct SharedLib *base));
struct TextFont *CreateCatharsisFont( struct TextFont *font, REG(a6, struct SharedLib *base));
struct TextFont *CreateCatharsisFontA( REG(a0, struct TextFont *font), REG(a6, struct SharedLib *base));
struct TextFont *OpenCatharsisFont( struct TextFont *font, REG(a6, struct SharedLib *base));
struct TextFont *OpenCatharsisFontA( REG(a0, struct TextFont *font), REG(a6, struct SharedLib *base));
void CloseCatharsisFont( struct TextFont *font, REG(a6, struct SharedLib *base));
void CloseCatharsisFontA( REG(a0, struct TextFont *font), REG(a6, struct SharedLib *base));
int ComputeCatharsisValue( int bg, int fg, int percent, int normal, REG(a6, struct SharedLib *base));
int ComputeCatharsisValueA( REG(d0, int bg), REG(d1, int fg), REG(d2, int percent), REG(d3, int normal), REG(a6, struct SharedLib *base));
ULONG CompressColorValue( ULONG r, ULONG g, ULONG b, REG(a6, struct SharedLib *base));
ULONG CompressColorValueA( REG(d0, ULONG r), REG(d1, ULONG g), REG(d2, ULONG b), REG(a6, struct SharedLib *base));
ULONG CompatibleColorValue( ULONG v, REG(a6, struct SharedLib *base));
ULONG CompatibleColorValueA( REG(d0, ULONG v), REG(a6, struct SharedLib *base));
ULONG CatharsisColorValue( ULONG val1, ULONG val2, ULONG * red, ULONG * green, ULONG * blue, REG(a6, struct SharedLib *base));
ULONG CatharsisColorValueA( REG(d0, ULONG val1), REG(d1, ULONG val2), REG(a0, ULONG * red), REG(a1, ULONG * green), REG(a2, ULONG * blue), REG(a6, struct SharedLib *base));
void CatharsisText( struct RastPort *rp, unsigned char *str, unsigned int len, unsigned int pen, struct TextFont *font, REG(a6, struct SharedLib *base));
void CatharsisTextA( REG(a1, struct RastPort *rp), REG(a0, unsigned char *str), REG(d0, unsigned int len), REG(d1, unsigned int pen), REG(a2, struct TextFont *font), REG(a6, struct SharedLib *base));



/* ########################################################### */

struct ExecBase *SysBase;					// Global, in data area of library so that the compiler has
struct GfxBase *GfxBase;					// access to it
struct IntuitionBase *IntuitionBase;

UBYTE GrafName[]		 = "graphics.library";
UBYTE IntuitionName[] = "intuition.library";

#define LIB_Version		1					// Change these four for your own library...
#define LIB_Revision	0					// and also in 'LIB_Version.s' !!!

UBYTE LIB_Name[] = "catharsis.library";		// Name of this library
UBYTE LIB_IdString[] = "catharsis 1.0p (04-Jul-01)";	// More datails of library (string)

/* ########################################################### */

/* The library base structure */

struct SharedLib
{
	struct Node sl_LibNode;
	UBYTE sl_Flags;							// LIB Flags
	UBYTE sl_Pad0;							// Dummy
	UWORD sl_NegSize;						// LIB Negative size (function table)
	UWORD sl_PosSize;						// LIB Positive size (it's this table)
	UWORD sl_Version;						// LIB Version
	UWORD sl_Revision;						// LIB Revision
	char *sl_IdString;						// LIB Detail string
	ULONG sl_Sum;							// LIB Checksum
	UWORD sl_OpenCnt;						// LIB Open count
	UWORD sl_PatchCnt;						// - Internally used
	BPTR sl_SegList;						// LIB Segment pointer
	void *sl_OldFuncPtr;
	struct ExecBase *sl_SysBase;			// Pointers to SysBase
	struct GfxBase *sl_GfxBase;
	struct IntuitionBase *sl_IntuitionBase;
	struct TextFont *sl_TextFont[256];		// Remembered TextFont pointers
	struct TextFont *sl_CatharsisFont[256];	// Created TextFont pointers
};											// Size of table (positive size)

struct SharedLib *ThisLib;

/* ########################################################### */

/* M U S T   B E   T H E   V E R Y   F I R S T   E N T R Y

   Now, it's in LIB_Resident.s ....

   int _INIT_0_lib_start_not_allowed()  // If we are called from CLI
   {
	   return -1;
   }									// ...exit quick */

/* ########################################################### */


struct Library *LIB_InitRoutine(REG(d0, struct SharedLib *obj), REG(a0, BPTR seglist), REG(a6, struct ExecBase *AbsSysBase))
{

	if (!(obj->sl_SysBase == AbsSysBase))	// Library already set up?
	{
		ThisLib = obj;								// Pointer to own library, global
		obj->sl_SegList = seglist;					// Remember segment(s) of library
		obj->sl_SysBase = SysBase = AbsSysBase;		// Setup SysBase
		obj->sl_LibNode.ln_Type = NT_LIBRARY;		// Initialize the library...
		obj->sl_LibNode.ln_Name = LIB_Name;
		obj->sl_Flags = LIBF_SUMUSED | LIBF_CHANGED;
		obj->sl_Version = LIB_Version;
		obj->sl_Revision = LIB_Revision;
		obj->sl_IdString = LIB_IdString;

		// Call user's startup-routine
		if ( !User_Startup( obj))
		{
			return 0;								// User init failed, thus creating the library failed
		}
	}
	return (struct Library *) obj;					// Success!

}

/* ########################################################### */

/* Add here the names of your functions in the right order! */

void *LIB_FunctionTable[] =
{
	LIB_Open,
	LIB_Close,
	LIB_Expunge,
	LIB_Reserved,

// - Public functions up from here
	ModifyCharacter,
	ModifyCharacterA,
	CreateCatharsisFont,
	CreateCatharsisFontA,
	OpenCatharsisFont,
	OpenCatharsisFontA,
	CloseCatharsisFont,
	CloseCatharsisFontA,
	ComputeCatharsisValue,
	ComputeCatharsisValueA,
	CompressColorValue,
	CompressColorValueA,
	CompatibleColorValue,
	CompatibleColorValueA,
	CatharsisColorValue,
	CatharsisColorValueA,
	CatharsisText,
	CatharsisTextA,

// - end mark of function table
	(void *) -1
};

/* ########################################################### */

/* The APTR is needed because Commodore made a stupid APTR assign instead
   of a useable structure, which could look like this:

   struct LibraryInitTable
   {
   unsigned long      LIB_POS_SIZE;
   void             **LIB_FUNC_TABLE;
   void              *LIB_DATA_TABLE;
   void             (*LIB_INIT_FUNCTION)( struct SharedLib *, BPTR, struct ExecBase *);
   };
*/

const APTR LIB_InitTable[] =
{
	(APTR) sizeof (struct SharedLib),
	LIB_FunctionTable,
	0,										// We have to do it in main, since the INIT macros don't deal with MCPP!
	 LIB_InitRoutine
};


/* This has been moved to LIB_Resident.s ....

   const struct Resident LIB_Resident =
   {
   RTC_MATCHWORD,				Code of resident (for exec's MakeLibrary())
   (APTR) &LIB_Resident,		Point back to address of begin
   (APTR) ((char *) &LIB_Resident + sizeof( struct Resident)),  End of this structure
   RTF_AUTOINIT,				Flag for auto call
   LIB_Version,					...
   NT_LIBRARY,					Type = library
   0,							Priority, always zero
   LIB_Name,					Name of this library
   LIB_IdString,				More datails of library (string)
   (APTR) LIB_InitTable,		Pointer to init-table
   };
*/

extern const struct Resident LIB_Resident;


/* ########################################################### */

struct SharedLib *LIB_Open( REG(a6, struct SharedLib *obj))
{
	obj->sl_OpenCnt ++;					// Another caller

	obj->sl_Flags &= ~LIBF_DELEXP;		// Prevent from being flushed from memory

	return obj;
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

int LIB_Close( REG( a6, struct SharedLib *obj))
{
	obj->sl_OpenCnt --;

	if (obj->sl_OpenCnt > 0)
		return 0;

	if (obj->sl_Flags & LIBF_DELEXP)
		return LIB_Expunge(obj);
	else
		return 0;
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

BPTR LIB_Expunge( REG(a6, struct SharedLib * obj))
{
	BPTR seglist = 0;
	long libsize;

	if (obj->sl_PatchCnt)
	{
		obj->sl_Flags &= ~LIBF_DELEXP;		// Prevent from being flushed from memory

		return 0;
	}

	if (obj->sl_OpenCnt <= 0)
	{
		// Invoke user's cleanup-routine
		User_Cleanup( obj);

		// Really expunge: remove library and free memory used by it
		seglist = obj->sl_SegList;

		Remove( (struct Node *) obj);

		libsize = obj->sl_NegSize + obj->sl_PosSize;	// Calc size

		FreeMem( (char *) obj - obj->sl_NegSize, libsize);
	}
	else
	{
		obj->sl_Flags |= LIBF_DELEXP;
	}

	// Return NULL or seglist
	return seglist;
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

int LIB_Reserved( REG(a6, struct SharedLib *obj))
{
	return 0;
}

/* ########################################################### */


/* ++++++++++++++++++++++++++++++++++++++++++++++++++++
   Here you can initialize your library's environment.
   It's only once called when the library is initialized by the system (MakeLibrary()). */

int User_Startup( REG(a6, struct SharedLib *obj))
{
	if ((GfxBase = obj->sl_GfxBase = (struct GfxBase *) OpenLibrary( GrafName, 33)))
	{
		if ((IntuitionBase = obj->sl_IntuitionBase = (struct IntuitionBase *) OpenLibrary( IntuitionName, 33)))
		{
			return TRUE;					// Success
		}
		CloseLibrary((struct Library *) obj->sl_GfxBase);
		obj->sl_GfxBase = 0;
	}
	return FALSE;
}

/* ++++++++++++++++++++++++++++++++++++++++++++++++++++
   Do here the necessary clanups your environment has produced.
   It's only called when the library is moved out off memory. */

void User_Cleanup(register __a6 struct SharedLib *obj)
{
	if (obj->sl_IntuitionBase)
		CloseLibrary((struct Library *) obj->sl_IntuitionBase);

	if (obj->sl_GfxBase)
		CloseLibrary((struct Library *) obj->sl_GfxBase);
}
