/*  ReadDCTV     by Garrick Meeker  */

#include <dos/dos.h>
#include <exec/memory.h>
#include <iffp/ilbmapp.h>
#include <iffp/packer.h>
#include <libraries/dctv.h>
#include <libraries/iffparse.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <clib/dctv_protos.h>
#include <clib/exec_protos.h>
#include <clib/graphics_protos.h>
#include <clib/iffparse_protos.h>

#include <pragmas/dctv_pragmas.h>
#include <pragmas/exec_pragmas.h>
#include <pragmas/graphics_pragmas.h>
#include <pragmas/iffparse_pragmas.h>

char *vers = "\0$VER: ReadDCTV 1.0";
char *Copyright = "ReadDCTV v1.0 by Garrick Meeker";
char *usage = "Usage: ReadDCTV dctvfile iff24file";

char *write_err = "Error writing file";

LONG Convert(void);
struct IFFHandle *OpenOutfile(void);
BOOL WriteRGB(struct IFFHandle *iff24, struct DCTVCvtHandle *cvt);
BOOL WriteChunkyLine(struct IFFHandle *iff,UBYTE *chunks,int width);
void ConvertPlane(UBYTE planemask, UBYTE *line, int width);
void CloseOutfile(struct IFFHandle *iff24);
void bye(UBYTE *s, int e);
void cleanup(void);

struct Library *DCTVBase = NULL;
struct Library *GfxBase = NULL;
struct Library *IFFParseBase = NULL;

struct ILBMInfo ilbm = {0};

LONG error=NULL;
UBYTE *infile=NULL;
UBYTE *outfile=NULL;

UWORD *plane=NULL;
BYTE *compressed=NULL;

LONG ilbmprops[] = {
	ID_ILBM, ID_BMHD,
	ID_ILBM, ID_CMAP,
	ID_ILBM, ID_CAMG,
	TAG_DONE
};

LONG ilbmstops[] = {
	ID_ILBM, ID_BODY,
	TAG_DONE
};
void main(int argc, char **argv)
{

	if((argc<3)||(argv[argc-1][0]=='?'))
	{
		printf("%s\n%s\n",Copyright,usage);
		bye("",RETURN_OK);
	}
	
	infile = argv[1];
	outfile = argv[2];
	
	if(!(GfxBase = OpenLibrary("graphics.library", 0)))
	  bye("Unable to open graphics.library.\n",RETURN_WARN);
	
	if(!(IFFParseBase = OpenLibrary("iffparse.library",0)))
	  bye("Unable to open iffparse.library.\n",RETURN_WARN);
	
	if(!(DCTVBase = OpenLibrary("dctv.library",3)))
	  bye("Unable to open dctv.library v3.\n",RETURN_WARN);
	
/* Load image */

	ilbm.ParseInfo.propchks = ilbmprops;
	ilbm.ParseInfo.stopchks = ilbmstops;
	if(!(ilbm.ParseInfo.iff = AllocIFF()))
	  bye(IFFerr(IFFERR_NOMEM),RETURN_FAIL);
	
	if (error = loadbrush(&ilbm,infile))
	{
		printf("Unable to load ilbm \"%s\", ifferr=%s\n",infile,IFFerr(error));
		bye("",RETURN_WARN);
	}
	error=Convert();
	
	unloadbrush(&ilbm);
	bye("",error);
}
	
/* Convert to 24 bit */
LONG Convert()
{
	struct IFFHandle *iff24 = NULL;
	struct DCTVCvtHandle *cvt = NULL;

	if(!(TestDCTVSignature(ilbm.brbitmap))) {
		puts("This is not a DCTV picture.");
		return RETURN_WARN;
	}
	
	if (!(cvt = AllocDCTVCvtTags(		ilbm.brbitmap,
			DCTVCVTA_Type,		(ULONG)DCTVCVTT_DCTVtoRGB,
			DCTVCVTA_Width,		(ULONG)((ilbm.brbitmap->BytesPerRow)<<3),
			DCTVCVTA_Height,	(ULONG)ilbm.brbitmap->Rows,
			DCTVCVTA_Flags,		(ULONG)((ilbm.camg & LACE) ? DCTVCVTF_Lace : 0),
			DCTVCVTA_ColorTable,	ilbm.colortable,
			DCTVCVTA_ErrorCode,	&error,
			TAG_END))) {
		printf("DCTV conversion failed %lu\n",error);
		error=RETURN_WARN;
		goto abortconvert;
	}

	if (!(plane=(AllocMem(ilbm.brbitmap->BytesPerRow,MEMF_ANY)))) {
		puts("Out of memory.");
		error=RETURN_WARN;
		goto abortconvert;
	}
	
	if (!(compressed=(AllocMem(MaxPackedSize(ilbm.brbitmap->BytesPerRow),MEMF_ANY)))) {
		puts("Out of memory.");
		error=RETURN_WARN;
		goto abortconvert;
	}
	
	if (!(iff24=OpenOutfile())) {
		puts("Unable to open output file.");
		error=RETURN_FAIL;
		goto abortconvert;
	}
	
	while (cvt->DstLineNum < cvt->Height) {
		/* Break on Ctrl-C */
		if (SetSignal(0,SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) return RETURN_WARN;
		CvtDCTVLine(cvt);
		if(cvt->DstLineNum>0)
		  if(!(WriteRGB(iff24,cvt))) {
			printf("%s\n",write_err);
			error=RETURN_FAIL;
			goto abortconvert;
		}
	}

	if(PopChunk(iff24)) {	/* Close BODY chunk */
		printf("%s\n",write_err);
		error=RETURN_FAIL;
		goto abortconvert;
	}
	
	if(PopChunk(iff24)) {	/* Close FORM */
		printf("%s\n",write_err);
		error=RETURN_FAIL;
		goto abortconvert;
	}
	
	error = RETURN_OK;
abortconvert:
	if(compressed)	FreeMem(compressed,MaxPackedSize(ilbm.brbitmap->BytesPerRow));
	if(plane)	FreeMem(plane,ilbm.brbitmap->BytesPerRow);
	if(cvt)		FreeDCTVCvt(cvt);
	CloseOutfile(iff24);
	return error;
}

/* Open 24-bit IFF */
struct IFFHandle *OpenOutfile()
{
	struct IFFHandle *iff=NULL;
	BitMapHeader bmhd;
	
	if (!(iff = AllocIFF())) return NULL;
	if (!(iff->iff_Stream=Open(outfile,MODE_NEWFILE)))
		goto abortopen;
	InitIFFasDOS(iff);
	if (OpenIFF(iff,IFFF_WRITE)) goto abortopen;
	
	/* Write header */
	if (PushChunk(iff,ID_ILBM,ID_FORM,IFFSIZE_UNKNOWN))
		goto abortopen;
	
/* Write BMHD */
	if (PushChunk(iff,ID_ILBM,ID_BMHD,sizeof(BitMapHeader)))
		goto abortopen;
	/* Set BMHD values */
	CopyMem(&ilbm.Bmhd,&bmhd,sizeof(bmhd));
	bmhd.nPlanes=24;
	bmhd.masking=mskNone;
	bmhd.compression=cmpByteRun1;
	bmhd.reserved1=0;
	
	if (WriteChunkBytes(iff,&bmhd,sizeof(bmhd)) != sizeof(bmhd))
		goto abortopen;
	if (PopChunk(iff))
		goto abortopen;
	
/* Write BODY header */
	if (PushChunk(iff,ID_ILBM,ID_BODY,IFFSIZE_UNKNOWN))
		goto abortopen;
	return iff;

abortopen:
	CloseOutfile(iff);
	return NULL;
}
	
void CloseOutfile(struct IFFHandle *iff)
{
	if(iff) {
		CloseIFF(iff);
		if (iff->iff_Stream)
			Close(iff->iff_Stream);
		FreeIFF(iff);
	}
}
	
BOOL WriteRGB(struct IFFHandle *iff, struct DCTVCvtHandle *cvt)
{
	if (!(WriteChunkyLine(iff,cvt->Red,cvt->Width)!=cvt->Width))
	  return FALSE;
	if (!(WriteChunkyLine(iff,cvt->Green,cvt->Width)!=cvt->Width))
	  return FALSE;
	if (!(WriteChunkyLine(iff,cvt->Blue,cvt->Width)!=cvt->Width))
	  return FALSE;
	return TRUE;
}

/* Write one line of a primary color */
BOOL WriteChunkyLine(struct IFFHandle *iff,UBYTE *chunks,int width)
{
	UBYTE	i;
	LONG	length;
	/* Pointers are incremented by packrow() so we use a copy */
	BYTE	*temp_plane;
	BYTE	*temp_comp;
	
	/* Ends when i overflows */
	for (i=1; i!=0; i+=i) {
		temp_plane=(BYTE *)plane;
		temp_comp=compressed;
		ConvertPlane(i, chunks, width);
		length=packrow(&temp_plane,&temp_comp,(LONG)ilbm.brbitmap->BytesPerRow);
		if (WriteChunkBytes(iff,compressed,length)!=length)
	  		return FALSE;
	}
	return TRUE;
}

void ConvertPlane(UBYTE planemask, UBYTE *line, int width)
{
	int i,j,end;
	UWORD currbit;
	
	for (i=0,j=0; i<width; j++) {
		for (plane[j]=0,currbit=0x8000,end=min(width,i+16); i<end; i++,currbit=currbit>>1) {
			if (line[i]&planemask)
				plane[j] |= currbit;
		}
	}
}

void bye(UBYTE *s, int e)
{
	if(s&&(*s)) printf("%s\n",s);
	cleanup();
	exit(e);
}

void cleanup()
{
	if(ilbm.ParseInfo.iff)	FreeIFF(ilbm.ParseInfo.iff);
	if(IFFParseBase)	CloseLibrary(IFFParseBase);
	if(GfxBase)		CloseLibrary(GfxBase);
	if(DCTVBase)		CloseLibrary(DCTVBase);
}

