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

#include "utils.h"
#include <string.h>
#include <stdio.h>
#include <stdarg.h>

static const char USED version[] = "\0$VER: xfdScan 53.2 (11.9.2014)";

static const char template[] = "SOURCE/A,DEST/A,NE=NOEXTERN/S,SO=SCANODD/S";

enum {
	ARG_SOURCE,
	ARG_DEST,
	ARG_NOEXTERN,
	ARG_SCANODD,
	ARG_MAX
};

struct global_data {
	struct xfdMasterIFace *ixfd;
	struct xfdScanHook *xsh;
	APTR buffer;
	ULONG buflen;
	LONG args[ARG_MAX];
};

static APTR read_file(const char *fn, ULONG *buflen);
static BOOL hook_func(struct xfdMasterIFace *Self, struct xfdScanHook *xsh);
static BOOL scan_file(struct global_data *gd);
static BOOL scan_node(struct global_data *gd, struct xfdScanNode *xsn, LONG num);

int main(void) {
	struct global_data gd;
	struct xfdMasterBase *XfdBase;
	struct xfdMasterIFace *IXfd;
	struct RDArgs *rda;
	int rc = RETURN_ERROR;

	signal(SIGINT, SIG_IGN);

	memset(&gd, 0, sizeof(gd));

	IDOS->Printf(CSI"1m%s"CSI"0;32m (C) 2014 Fredrik Wikstrom"CSI";31m\n\n",
		version + 7);

	XfdBase = (struct xfdMasterBase *)IExec->OpenLibrary("xfdmaster.library", 53);
	IXfd = (struct xfdMasterIFace *)IExec->GetInterface((struct Library *)XfdBase, "main", 1, NULL);
	if (IXfd == NULL || !LIB_IS_AT_LEAST(&XfdBase->LibNode, 53, 7)) {
		IExec->DropInterface((struct Interface *)IXfd);
		IExec->CloseLibrary((struct Library *)XfdBase);
		IDOS->PutErrStr("You need xfdmaster.library v53.7+\n");
		return RETURN_FAIL;
	}

	rda = IDOS->ReadArgs(template, gd.args, NULL);
	if (rda != NULL) {
		gd.ixfd = IXfd;

		gd.buffer = read_file((const char *)gd.args[ARG_SOURCE], &gd.buflen);
		if (gd.buffer != NULL) {
			gd.xsh = IXfd->xfdInitScanHook(hook_func, NULL);
			if (gd.xsh != NULL) {
				if (scan_file(&gd)) {
					rc = RETURN_OK;
				}
				IXfd->xfdFreeObject(gd.xsh);
			} else {
				IDOS->PrintFault(ERROR_NO_FREE_STORE, NULL);
			}
			IExec->FreeVec(gd.buffer);
		}

		IDOS->FreeArgs(rda);
	} else {
		IDOS->PrintFault(IDOS->IoErr(), NULL);
	}

	IExec->DropInterface((struct Interface *)IXfd);
	IExec->CloseLibrary((struct Library *)XfdBase);

	return rc;
}

static APTR read_file(const char *fn, ULONG *buflen) {
	BPTR fh;
	int64 len;
	APTR buffer = NULL;

	*buflen = 0;
	IDOS->Printf("Reading \"%s\"...\n", fn);

	fh = IDOS->Open(fn, MODE_OLDFILE);
	if (fh != ZERO) {
		len = IDOS->GetFileSize(fh);

		if (len >= 0 && len <= 0x7fffffff) {
			buffer = IExec->AllocVecTags(len,
				AVT_Type, MEMF_SHARED,
				AVT_Lock, FALSE,
				TAG_END);
			if (buffer != NULL) {
				if (IDOS->Read(fh, buffer, len) == len) {
					*buflen = len;
				} else {
					IExec->FreeVec(buffer);
					buffer = NULL;
					IDOS->PrintFault(IDOS->IoErr(), fn);
				}
			} else {
				IDOS->PrintFault(ERROR_NO_FREE_STORE, fn);
			}
		} else {
			if (len == -1) {
				IDOS->PrintFault(IDOS->IoErr(), fn);
			} else {
				IDOS->PrintFault(ERROR_OBJECT_TOO_LARGE, fn);
			}
		}

		IDOS->Close(fh);
	} else {
		IDOS->PrintFault(IDOS->IoErr(), fn);
	}

	return buffer;
}

static BOOL hook_func(struct xfdMasterIFace *Self, struct xfdScanHook *xsh) {
	return !IDOS->CheckSignal(SIGBREAKF_CTRL_C);
}

static BOOL scan_file(struct global_data *gd) {
	struct xfdMasterIFace *IXfd = gd->ixfd;
	UWORD error, flags;
	ULONG result;
	BOOL res = FALSE;

	flags = 0;
	if (!gd->args[ARG_NOEXTERN]) flags |= XFDSDF_USEEXTERN;
	if (gd->args[ARG_SCANODD]) flags |= XFDSDF_SCANODD;

	error = IXfd->xfdScanData(gd->buffer, gd->buflen, &result, flags, gd->xsh);

	if (error == XFDERR_OK) {
		if ((APTR)result != NULL) {
			struct xfdScanNode *xsn = (struct xfdScanNode *)result;
			LONG num = 0;

			res = TRUE;

			do {
				res &= !!scan_node(gd, xsn, num++);
			} while ((xsn = xsn->xfdsn_Next) != NULL);

			IXfd->xfdFreeScanList((struct xfdScanNode *)result);
		} else {
			IDOS->PutErrStr("Nothing found!\n");
		}
	} else {
		IXfd->xfdPrintFault(error, "Scan failed");
	}

	return res;
}

static BOOL scan_node(struct global_data *gd, struct xfdScanNode *xsn, LONG num) {
	char fn[PATHMAX];
	BPTR fh;
	BOOL res = FALSE;

	snprintf(fn, sizeof(fn), "%s.%02ld", (const char *)gd->args[ARG_DEST], num);

	IDOS->Printf("Writing \"%s\" (%s)...\n", fn, xsn->xfdsn_PackerName);

	fh = IDOS->Open(fn, MODE_NEWFILE);
	if (fh != ZERO) {
		if (IDOS->Write(fh, xsn->xfdsn_Save, xsn->xfdsn_SaveLen) == xsn->xfdsn_SaveLen) {
			res = TRUE;
		}
		IDOS->Close(fh);
	}

	if (!res) IDOS->PrintFault(IDOS->IoErr(), fn);

	return res;
}

