/* 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_vectors.h"
#include "../xfdmaster_internal.h"

/****** xfdmaster.library/xfdRecogLinker ******************************************
*
*   NAME
*      xfdRecogLinker -- Description
*
*   SYNOPSIS
*      BOOL xfdRecogLinker(struct xfdLinkerInfo * linkerinfo);
*
*   FUNCTION
*
*   INPUTS
*       linkerinfo - 
*
*   RESULT
*       The result ...
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
*****************************************************************************
*
*/

BOOL _xfdMaster_xfdRecogLinker(struct xfdMasterIFace *Self,
	struct xfdLinkerInfo *linkerinfo)
{
	DEBUGF("xfdRecogLinker(0x%08lx, 0x%08lx)\n", Self, linkerinfo);
	struct xfdMasterBase *libBase = (struct xfdMasterBase *)Self->Data.LibBase;
	struct ExecIFace *IExec = libBase->xfdm_IExec;
	ULONG res;
	APTR hunk, hunkp, linker;

	DEBUGF("xfdli_Buffer:     0x%08lx\n", linkerinfo->xfdli_Buffer);
	DEBUGF("xfdli_BufLen:     %lu\n",     linkerinfo->xfdli_BufLen);
	DEBUGF("xfdli_LinkerName: 0x%08lx\n", linkerinfo->xfdli_LinkerName);
	DEBUGF("xfdli_Unlink:     0x%08lx\n", linkerinfo->xfdli_Unlink);
	DEBUGF("xfdli_Reserved:   %lu\n",     linkerinfo->xfdli_Reserved);
	DEBUGF("xfdli_Error:      %lu\n",     linkerinfo->xfdli_Error);
	DEBUGF("xfdli_Hunk1:      %lu\n",     linkerinfo->xfdli_Hunk1);
	DEBUGF("xfdli_Hunk2:      %lu\n",     linkerinfo->xfdli_Hunk2);
	DEBUGF("xfdli_Amount1:    %lu\n",     linkerinfo->xfdli_Amount1);
	DEBUGF("xfdli_Amount2:    %lu\n",     linkerinfo->xfdli_Amount2);
	DEBUGF("xfdli_Save1:      0x%08lx\n", linkerinfo->xfdli_Save1);
	DEBUGF("xfdli_Save2:      0x%08lx\n", linkerinfo->xfdli_Save2);
	DEBUGF("xfdli_SaveLen1:   %lu\n",     linkerinfo->xfdli_SaveLen1);
	DEBUGF("xfdli_SaveLen2:   %lu\n",     linkerinfo->xfdli_SaveLen2);

	if (linkerinfo->xfdli_Buffer == NULL ||
		linkerinfo->xfdli_BufLen == 0)
	{
		linkerinfo->xfdli_Error = XFDERR_NOSOURCE;
		return FALSE;
	}

	if (IExec->IsNative(LB_AnalyseDiz)) {
		res = LB_AnalyseDiz(Self, NULL, linkerinfo);
	} else {
		res = IExec->EmulateTags(LB_AnalyseDiz,
			ET_SaveParamRegs, TRUE,
			ET_RegisterA5,    linkerinfo,
			ET_RegisterA6,    libBase,
			TAG_END);
	}
	if (res) {
		linkerinfo->xfdli_LinkerName = LB_NameDiz;
		linkerinfo->xfdli_Unlink = LB_UnlinkDiz;
		linkerinfo->xfdli_Error = XFDERR_OK;
		return TRUE;
	}

	hunk = xfdGetHunk(libBase, linkerinfo->xfdli_Buffer, linkerinfo->xfdli_BufLen, 0);
	if (hunk == NULL) {
		linkerinfo->xfdli_Error = XFDERR_NOHUNKHEADER;
		return FALSE;
	}

	linker = libBase->xfdm_LinkerBrain;
	if (linker != NULL) {
		LONG longs;
		while ((longs = READ_WORD_INC(linker)) >= 0) {
			hunkp = hunk;
			if (longs > 0) {
				longs++;
				do {
					if (*(ULONG *)linker != 0xdeadbeef &&
						*(ULONG *)hunkp != *(ULONG *)linker)
					{
						break;
					}
					hunkp += sizeof(ULONG);
					linker += sizeof(ULONG);
				} while (--longs > 0);
			}
			if (longs == 0) {
				xfdAnalyseFunc func = READ_APTR_INC(linker);
				linkerinfo->xfdli_Unlink = READ_APTR_INC(linker);
				linkerinfo->xfdli_LinkerName = READ_APTR_INC(linker);
				if (IExec->IsNative(func)) {
					res = func(Self, hunk, linkerinfo);
				} else {
					res = IExec->EmulateTags(func,
						ET_SaveParamRegs, TRUE,
						ET_RegisterA0,    hunk,
						ET_RegisterA1,    hunkp,
						ET_RegisterA5,    linkerinfo,
						ET_RegisterA6,    libBase,
						TAG_END);
				}
				if (res) {
					APTR buffer = linkerinfo->xfdli_Buffer;
					LONG amount = 1 + *(LONG *)(buffer + 16) - *(LONG *)(buffer + 12) - linkerinfo->xfdli_Hunk2;
					if (amount > 0) {
						linkerinfo->xfdli_Amount2 = amount;
						linkerinfo->xfdli_Error = XFDERR_OK;
						return TRUE;
					}
				}
			} else {
				linker += (longs * sizeof(ULONG)) + (3 * sizeof(APTR));
			}
		}
	}

	linkerinfo->xfdli_Unlink = NULL;
	linkerinfo->xfdli_LinkerName = NULL;
	linkerinfo->xfdli_Error = XFDERR_UNKNOWN;
	return FALSE;
}

