/* OpenAmiga.org
 *  This file is part of the OpenAmiga "xfdmaster library project"
 *
 * For more information look at:
 *   http://openamiga.org/?function=viewproject&projectid=67
 */

/****************************************************************************
*   XFD library system for cruncher handling
*   Copyright (C) 1994-1998 by Georg Hörmann
*   Copyright (C) 1998 by Dirk Stöcker <soft@dstoecker.de>
*   Copyright (C) 2014 by Fredrik Wikström <fredrik@a500.org>
*
*   This library is free software; you can redistribute it and/or
*   modify it under the terms of the GNU Lesser General Public
*   License as published by the Free Software Foundation; either
*   version 2.1 of the License, or (at your option) any later version.
*
*   This library is distributed in the hope that it will be useful,
*   but WITHOUT ANY WARRANTY; without even the implied warranty of
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
*   Lesser General Public License for more details.
*
*   You should have received a copy of the GNU Lesser General Public
*   License along with this library; if not, write to the Free Software
*   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
****************************************************************************/

#include <interfaces/xfdmaster.h>
#include "xfdmaster_internal.h"

/* Version Tag */
#include "xfdmaster.library_rev.h"
STATIC CONST UBYTE USED verstag[] = VERSTAG;

/*
 * The system (and compiler) rely on a symbol named _start which marks
 * the beginning of execution of an ELF file. To prevent others from 
 * executing this library, and to keep the compiler/linker happy, we
 * define an empty _start symbol here.
 *
 * On the classic system (pre-AmigaOS 4.x) this was usually done by
 * moveq #0,d0
 * rts
 *
 */
int32 _start(void) {
    /* If you feel like it, open DOS and print something to the user */
    return RETURN_FAIL;
}

/* Open the library */
STATIC struct Library *libOpen(struct LibraryManagerInterface *Self, ULONG version) {
	struct Library *libBase = Self->Data.LibBase; 

	if (version > VERSION) {
		return NULL;
	}

	/* Add any specific open code here 
	   Return 0 before incrementing OpenCnt to fail opening */

	/* Add up the open count */
	libBase->lib_OpenCnt++;
	return libBase;
}

/* Close the library */
STATIC BPTR libClose(struct LibraryManagerInterface *Self) {
	struct Library *libBase = Self->Data.LibBase;

	/* Make sure to undo what open did */

    /* Make the close count */
	libBase->lib_OpenCnt--;

	return ZERO;
}

/* Expunge the library */
STATIC BPTR libExpunge(struct LibraryManagerInterface *Self) {
    struct xfdMasterBase *libBase = (struct xfdMasterBase *)Self->Data.LibBase;
    BPTR result = ZERO;
    if (libBase->LibNode.lib_OpenCnt == 0) {
		struct ExecIFace *IExec = libBase->xfdm_IExec;
		struct DOSIFace *IDOS = libBase->xfdm_IDos;
		struct xfdForeMan *foreman, *nextforeman;

		result = libBase->xfdm_SegList;

		/* Undo what the init code did */
		foreman = libBase->xfdm_FirstForeMan;
		while (foreman != NULL) {
			nextforeman = foreman->xfdf_Next;
			IDOS->UnLoadSeg(foreman->xfdf_SegList);
			foreman = nextforeman;
		}

		IExec->DropInterface((struct Interface *)libBase->xfdm_IElf);
		IExec->CloseLibrary(libBase->xfdm_ElfBase);

		IExec->DropInterface((struct Interface *)libBase->xfdm_IUtility);
		IExec->CloseLibrary(libBase->xfdm_UtilityBase);

		IExec->DropInterface((struct Interface *)libBase->xfdm_IDos);
		IExec->CloseLibrary((struct Library *)libBase->xfdm_DosBase);

		IExec->Remove((struct Node *)libBase);
		IExec->DeleteLibrary((struct Library *)libBase);
	} else {
		result = ZERO;
		libBase->LibNode.lib_Flags |= LIBF_DELEXP;
	}
	return result;
}

/* The ROMTAG Init Function */
STATIC struct xfdMasterBase *libInit(struct xfdMasterBase *libBase,
	BPTR seglist, struct ExecIFace *IExec)
{
	struct xfdSlave *slave;
	BOOL native;
	ULONG minbuffersize;

    libBase->LibNode.lib_Node.ln_Type = NT_LIBRARY;
    libBase->LibNode.lib_Node.ln_Pri  = 0;
    libBase->LibNode.lib_Node.ln_Name = (STRPTR)"xfdmaster.library";
    libBase->LibNode.lib_Flags        = LIBF_SUMUSED|LIBF_CHANGED;
    libBase->LibNode.lib_Version      = VERSION;
    libBase->LibNode.lib_Revision     = REVISION;
    libBase->LibNode.lib_IdString     = (STRPTR)VSTRING;

    libBase->xfdm_SegList = seglist;
	libBase->xfdm_ExecBase = (struct ExecBase *)IExec->Data.LibBase;
	libBase->xfdm_IExec = IExec;

	libBase->xfdm_DosBase = (struct DosLibrary *)IExec->OpenLibrary("dos.library", 53);
	libBase->xfdm_IDos = (struct DOSIFace *)IExec->GetInterface((struct Library *)
		libBase->xfdm_DosBase, "main", 1, NULL);
	if (libBase->xfdm_IDos == NULL) {
		IExec->Alert(AG_OpenLib|AO_DOSLib);
		goto Error;
	}

	libBase->xfdm_UtilityBase = IExec->OpenLibrary("utility.library", 53);
	libBase->xfdm_IUtility = (struct UtilityIFace *)IExec->GetInterface(
		libBase->xfdm_UtilityBase, "main", 1, NULL);
	if (libBase->xfdm_IUtility == NULL) {
		IExec->Alert(AG_OpenLib|AO_UtilityLib);
		goto Error;
	}

	libBase->xfdm_ElfBase = IExec->OpenLibrary("elf.library", 53);
	libBase->xfdm_IElf = (struct ElfIFace *)IExec->GetInterface(
		libBase->xfdm_ElfBase, "main", 1, NULL);
	if (libBase->xfdm_IElf == NULL) {
		IExec->Alert(AG_OpenLib|AO_Unknown);
		goto Error;
	}

	SetupInternal(libBase);

	LoadExternal(libBase);

	slave = libBase->xfdm_FirstSlave;
	minbuffersize = 0;
	while (slave != NULL) {
		native = TRUE;
		if (slave->xfds_RecogBuffer) native &= !!IExec->IsNative(slave->xfds_RecogBuffer);
		if (slave->xfds_DecrunchBuffer) native &= !!IExec->IsNative(slave->xfds_DecrunchBuffer);
		if (slave->xfds_RecogSegment) native &= !!IExec->IsNative(slave->xfds_RecogSegment);
		if (slave->xfds_DecrunchSegment) native &= !!IExec->IsNative(slave->xfds_DecrunchSegment);
		if (!native) slave->xfds_PackerFlags |= XFDPFF_M68K;

		if (slave->xfds_Version >= 2 && slave->xfds_MinBufferSize > minbuffersize) {
			minbuffersize = slave->xfds_MinBufferSize;
		}

		slave = slave->xfds_Next;
	}
	libBase->xfdm_MinBufferSize = minbuffersize;

	libBase->xfdm_MinLinkerSize = XfdMinLinkSize;

	return libBase;

Error:

	IExec->DropInterface((struct Interface *)libBase->xfdm_IElf);
	IExec->CloseLibrary(libBase->xfdm_ElfBase);

	IExec->DropInterface((struct Interface *)libBase->xfdm_IUtility);
	IExec->CloseLibrary(libBase->xfdm_UtilityBase);

	IExec->DropInterface((struct Interface *)libBase->xfdm_IDos);
	IExec->CloseLibrary((struct Library *)libBase->xfdm_DosBase);

	IExec->DeleteLibrary((struct Library *)libBase);

	return NULL;
}

/* ------------------- Manager Interface ------------------------ */
/* These are generic. Replace if you need more fancy stuff */
STATIC uint32 _manager_Obtain(struct LibraryManagerInterface *Self) {
	uint32 res;
	__asm__ __volatile__(
	"1:	lwarx	%0,0,%1\n"
	"addic	%0,%0,1\n"
	"stwcx.	%0,0,%1\n"
	"bne-	1b"
	: "=&r" (res)
	: "r" (&Self->Data.RefCount)
	: "cc", "memory");

	return res;
}

STATIC uint32 _manager_Release(struct LibraryManagerInterface *Self) {
	uint32 res;
	__asm__ __volatile__(
	"1:	lwarx	%0,0,%1\n"
	"addic	%0,%0,-1\n"
	"stwcx.	%0,0,%1\n"
	"bne-	1b"
	: "=&r" (res)
	: "r" (&Self->Data.RefCount)
	: "cc", "memory");

	return res;
}

/* Manager interface vectors */
STATIC CONST APTR lib_manager_vectors[] = {
	_manager_Obtain,
	_manager_Release,
	NULL,
	NULL,
	libOpen,
	libClose,
	libExpunge,
	NULL,
	(APTR)-1
};

/* "__library" interface tag list */
STATIC CONST struct TagItem lib_managerTags[] = {
	{ MIT_Name,        (Tag)"__library"         },
	{ MIT_VectorTable, (Tag)lib_manager_vectors },
	{ MIT_Version,     1                        },
	{ TAG_DONE,        0                        }
};

/* ------------------- Library Interface(s) ------------------------ */

#include "xfdmaster_vectors.c"

/* Uncomment this line (and see below) if your library has a 68k jump table */
extern APTR VecTable68K[];

STATIC CONST struct TagItem main_v1_Tags[] = {
	{ MIT_Name,        (Tag)"main"          },
	{ MIT_VectorTable, (Tag)main_v1_vectors },
	{ MIT_Version,     1                    },
	{ TAG_DONE,        0                    }
};

STATIC CONST CONST_APTR libInterfaces[] = {
	lib_managerTags,
	main_v1_Tags,
	NULL
};

STATIC CONST struct TagItem libCreateTags[] = {
	{ CLT_DataSize,   sizeof(struct xfdMasterBase) },
	{ CLT_InitFunc,   (Tag)libInit                          },
	{ CLT_Interfaces, (Tag)libInterfaces                    },
	/* Uncomment the following line if you have a 68k jump table */
	{ CLT_Vector68K,  (Tag)VecTable68K                      },
	{ TAG_DONE,       0                                     }
};

/* ------------------- ROM Tag ------------------------ */
STATIC CONST struct Resident lib_res USED = {
	RTC_MATCHWORD,
	(struct Resident *)&lib_res,
	(APTR)(&lib_res + 1),
	RTF_NATIVE|RTF_AUTOINIT, /* Add RTF_COLDSTART if you want to be resident */
	VERSION,
	NT_LIBRARY, /* Make this NT_DEVICE if needed */
	0, /* PRI, usually not needed unless you're resident */
	"xfdmaster.library",
	VSTRING,
	(APTR)libCreateTags
};

