#include <clib/alib_protos.h>
#include <clib/cybergraphics_protos.h>
#include <clib/datatypes_protos.h>
#include <clib/dos_protos.h>
#include <clib/exec_protos.h>
#include <clib/graphics_protos.h>
#include <clib/iffparse_protos.h>
#include <clib/xpkmaster_protos.h>
#include <datatypes/pictureclass.h>
#include <exec/memory.h>
#include <libraries/rgfx.h>
#include <stdlib.h>

static char *ver="$VER: DT2RGFX 43.0 (30.11.1997) by Achim Stegemann";

static char *ifferrmsg[]=
{
0,
0,
0,
"No valid scope for property",
"Internal memory alloc failed",
"Stream read error",
"Stream write error",
"Stream seek error",
"Data in file is corrupt",
"IFF syntax error",
"Not an IFF file",
"No call-back hook provided"
};

static long ifferr,xpkerr,memerr;

static char xpkerrmsg[XPKERRMSGSIZE];

struct
{
char *from;
char *to;
char *xpk;
long planar;
long verbose;
} args;

/* Die dos.library wird automatisch dazu gelinkt */

struct Library *GfxBase;
struct Library *UtilityBase;
struct Library *DataTypesBase;
struct Library *IFFParseBase;
struct Library *XpkBase;
struct Library *CyberGfxBase;

static void Ende()
{
CloseLibrary(GfxBase);
CloseLibrary(UtilityBase);
CloseLibrary(DataTypesBase);
CloseLibrary(IFFParseBase);
CloseLibrary(XpkBase);
CloseLibrary(CyberGfxBase);
}

static void Init()
{
if(!(GfxBase=OpenLibrary("graphics.library",39)))
	{
	PutStr("No graphics.library\n");
	exit(20);
	}
if(!(UtilityBase=OpenLibrary("utility.library",39)))
	{
	PutStr("No utility.library\n");
	exit(20);
	}
if(!(DataTypesBase=OpenLibrary("datatypes.library",39)))
	{
	PutStr("No datatypes.library\n");
	exit(20);
	}
if(!(IFFParseBase=OpenLibrary("iffparse.library",39)))
	{
	PutStr("No iffparse.library\n");
	exit(20);
	}
XpkBase=OpenLibrary("xpkmaster.library",2);
CyberGfxBase=OpenLibrary("cybergraphics.library",40);
}

static BOOL PutRGHD(struct IFFHandle *iff,struct RGHD *rh)
{
BOOL ret=0;
if(!(ifferr=PushChunk(iff,0,ID_RGHD,sizeof(struct RGHD))))
	{
	if((ifferr=WriteChunkRecords(iff,rh,sizeof(struct RGHD),1))==1)
		{
		if(!(ifferr=PopChunk(iff))) ret=1;
		}
	}
return ret;
}

static BOOL PutRCOL(struct IFFHandle *iff,Object *pic)
{
BOOL ret=0;
if(!(ifferr=PushChunk(iff,0,ID_RCOL,sizeof(struct RCOL))))
	{
	struct RCOL *rc;
	if(rc=AllocVec(sizeof(struct RCOL),MEMF_CLEAR|MEMF_PUBLIC))
		{
		ULONG i,n,*cr;
		GetDTAttrs(pic,PDTA_NumColors,&n,PDTA_CRegs,&cr,TAG_END);
		for(i=0;i<n;i++)
			{
			UWORD j;
			for(j=0;j<3;j++) rc->rcol_Colors[i][j]=*(cr++)>>24;
			}
		if((ifferr=WriteChunkRecords(iff,rc,sizeof(struct RCOL),1))==1)
			{
			if(!(ifferr=PopChunk(iff))) ret=1;
			}
		FreeVec(rc);
		}
	else memerr=1;
	}
return ret;
}

static BOOL PutRSCM(struct IFFHandle *iff,Object *pic,struct RGHD *rh)
{
BOOL ret=0;
if(!(ifferr=PushChunk(iff,0,ID_RSCM,sizeof(struct RSCM))))
	{
	ULONG mode;
	struct RSCM rs;
	rs.rscm_AGA=INVALID_ID;
	rs.rscm_CGfx=INVALID_ID;
	rs.rscm_P96=INVALID_ID;
	GetDTAttrs(pic,PDTA_ModeID,&mode,TAG_END);
	if(CyberGfxBase&&IsCyberModeID(mode))
		{
		rs.rscm_CGfx=mode;
		if(rh->rgfx_Depth<=8) rs.rscm_AGA=BestModeID(BIDTAG_NominalWidth,rh->rgfx_Width,BIDTAG_NominalHeight,rh->rgfx_Height,BIDTAG_Depth,rh->rgfx_Depth,BIDTAG_MonitorID,DEFAULT_MONITOR_ID,TAG_END);
		}
	else
		{
		rs.rscm_AGA=mode;
		if(CyberGfxBase)
			{
			if(!(mode&(HAM_KEY|EXTRAHALFBRITE_KEY|LORESDPF_KEY))) rs.rscm_CGfx=BestCModeIDTags(CYBRBIDTG_NominalWidth,rh->rgfx_Width,CYBRBIDTG_NominalHeight,rh->rgfx_Height,CYBRBIDTG_Depth,rh->rgfx_Depth,TAG_END);
			}
		}
	if((ifferr=WriteChunkRecords(iff,&rs,sizeof(struct RSCM),1))==1)
		{
		if(!(ifferr=PopChunk(iff))) ret=1;
		}
	}
return ret;
}

static BOOL WritePlanar(struct IFFHandle *iff,struct RGHD *rh,struct BitMap *bm)
{
BOOL ret=1;
UBYTE d;
for(d=0;d<rh->rgfx_Depth;d++)
	{
	ULONG y;
	UBYTE *pl=bm->Planes[d];
	for(y=0;y<rh->rgfx_Height;y++,pl+=bm->BytesPerRow)
		{
		if((ifferr=WriteChunkRecords(iff,pl,rh->rgfx_BytesPerLine,1))!=1)
			{
			ret=0;
			break;
			}
		}
	}
return ret;
}

static BOOL WriteChunky(Object *obj,struct IFFHandle *iff,struct RGHD *rh,struct BitMap *bm)
{
BOOL ret=0;
struct BitMap *tbm;
if(tbm=AllocBitMap(rh->rgfx_Width,1,rh->rgfx_Depth,0,bm))
	{
	UBYTE *pix;
	if(pix=AllocVec(((rh->rgfx_Width+15)>>4)<<4,MEMF_PUBLIC))
		{
		ULONG y;
		struct RastPort rp,trp;
		InitRastPort(&rp);
		InitRastPort(&trp);
		rp.BitMap=bm;
		trp.BitMap=tbm;
		ret=1;
		for(y=0;y<rh->rgfx_Height;y++)
			{
			ReadPixelLine8(&rp,0,y,rh->rgfx_Width,pix,&trp);
			if((ifferr=WriteChunkRecords(iff,pix,rh->rgfx_Width,1))!=1)
				{
				ret=0;
				break;
				}
			}
		FreeVec(pix);
		}
	else memerr=1;
	FreeBitMap(tbm);
	}
else memerr=1;
return ret;
}

static BOOL Write24(Object *obj,struct IFFHandle *iff,struct RGHD *rh,struct BitMap *bm)
{
BOOL ret=0;
UBYTE *pix;
if(pix=AllocVec(rh->rgfx_BytesPerLine,MEMF_PUBLIC))
	{
	ULONG y;
	struct RastPort rp;
	InitRastPort(&rp);
	rp.BitMap=bm;
	ret=1;
	for(y=0;y<rh->rgfx_Height;y++)
		{
		ReadPixelArray(pix,0,0,rh->rgfx_BytesPerLine,&rp,0,y,rh->rgfx_Width,1,RECTFMT_RGB);
		if((ifferr=WriteChunkRecords(iff,pix,rh->rgfx_BytesPerLine,1))!=1)
			{
			ret=0;
			break;
			}
		}
	FreeVec(pix);
	}
else memerr=1;
return ret;
}

static BOOL WritePlanar_XPK(struct IFFHandle *iff,struct RGHD *rh,struct BitMap *bm)
{
BOOL ret=0;
ULONG tlen=rh->rgfx_BytesPerLine*rh->rgfx_Depth*rh->rgfx_Height;
UBYTE *mem;
if(mem=AllocVec(tlen,MEMF_ANY))
	{
	UBYTE *xpk=mem;
	ULONG i,xpklen,outlen;
	for(i=0;i<rh->rgfx_Depth;i++)
		{
		ULONG y;
		UBYTE *pl=bm->Planes[i];
		for(y=0;y<rh->rgfx_Height;y++,pl+=bm->BytesPerRow,xpk+=rh->rgfx_BytesPerLine) CopyMem(pl,xpk,bm->BytesPerRow);
		}
	if(!(xpkerr=XpkPackTags(XPK_InBuf,mem,XPK_InLen,tlen,XPK_GetOutBuf,&xpk,XPK_GetOutBufLen,&xpklen,XPK_GetOutLen,&outlen,XPK_PackMethod,args.xpk,XPK_PackMode,100,XPK_GetError,xpkerrmsg,TAG_END)))
		{
		if((ifferr=WriteChunkRecords(iff,xpk,outlen,1))==1) ret=1;
		FreeMem(xpk,xpklen);
		}
	FreeVec(mem);
	}
else memerr=1;
return ret;
}

static BOOL WriteChunky_XPK(Object *obj,struct IFFHandle *iff,struct RGHD *rh,struct BitMap *bm)
{
BOOL ret=0;
ULONG len=rh->rgfx_Width*rh->rgfx_Height;
UBYTE *mem;
if(mem=AllocVec(len,MEMF_ANY))
	{
	UBYTE *pix;
	if(pix=AllocVec(((rh->rgfx_Width+15)>>4)<<4,MEMF_ANY))
		{
		BitMap *tbm;
		if(tbm=AllocBitMap(rh->rgfx_Width,1,rh->rgfx_Depth,0,bm))
			{
			ULONG y,xpklen,outlen;
			UBYTE *xpk=mem;
			struct RastPort rp,trp;
			InitRastPort(&rp);
			InitRastPort(&trp);
			rp.BitMap=bm;
			trp.BitMap=tbm;
			for(y=0;y<rh->rgfx_Height;y++,xpk+=rh->rgfx_Width)
				{
				ReadPixelLine8(&rp,0,y,rh->rgfx_Width,pix,&trp);
				CopyMem(pix,xpk,rh->rgfx_Width);
				}
			if(!(xpkerr=XpkPackTags(XPK_InBuf,mem,XPK_InLen,len,XPK_GetOutBuf,&xpk,XPK_GetOutBufLen,&xpklen,XPK_GetOutLen,&outlen,XPK_PackMethod,args.xpk,XPK_PackMode,100,XPK_GetError,xpkerrmsg,TAG_END)))
				{
				if((ifferr=WriteChunkRecords(iff,xpk,outlen,1))==1) ret=1;
				FreeMem(xpk,xpklen);
				}
			FreeBitMap(tbm);
			}
		else memerr=1;
		FreeVec(pix);
		}
	else memerr=1;
	FreeVec(mem);
	}
else memerr=1;
return ret;
}

static BOOL Write24_XPK(Object *obj,struct IFFHandle *iff,struct RGHD *rh,struct BitMap *bm)
{
BOOL ret=0;
UBYTE *pix;
ULONG len=rh->rgfx_BytesPerLine*rh->rgfx_Height;
if(pix=AllocVec(len,MEMF_ANY))
	{
	ULONG xpklen,outlen;
	UBYTE *xpk;
	struct RastPort rp;
	InitRastPort(&rp);
	rp.BitMap=bm;
	ReadPixelArray(pix,0,0,rh->rgfx_BytesPerLine,&rp,0,0,rh->rgfx_Width,rh->rgfx_Height,RECTFMT_RGB);
	if(!(xpkerr=XpkPackTags(XPK_InBuf,pix,XPK_InLen,len,XPK_GetOutBuf,&xpk,XPK_GetOutBufLen,&xpklen,XPK_GetOutLen,&outlen,XPK_PackMethod,args.xpk,XPK_PackMode,100,XPK_GetError,xpkerrmsg,TAG_END)))
		{
		if((ifferr=WriteChunkRecords(iff,xpk,outlen,1))==1) ret=1;
		FreeMem(xpk,xpklen);
		}
	FreeVec(pix);
	}
else memerr=1;
return ret;
}

static BOOL PutBODY(struct IFFHandle *iff,Object *pic,struct RGHD *rh)
{
BOOL ret=0;
if(!(ifferr=PushChunk(iff,0,ID_RBOD,IFFSIZE_UNKNOWN)))
	{
	BOOL wok=0;
	struct BitMap *bm;
	GetDTAttrs(pic,PDTA_BitMap,&bm,TAG_END);
	if(args.verbose) PutStr("Writing IFF-RGFX file ...\n");
	if(!args.xpk)
		{
		if(rh->rgfx_Depth>8) wok=Write24(pic,iff,rh,bm);
		else if(args.planar) wok=WritePlanar(iff,rh,bm);
		else wok=WriteChunky(pic,iff,rh,bm);
		}
	else
		{
		if(rh->rgfx_Depth>8) wok=Write24_XPK(pic,iff,rh,bm);
		else if(args.planar) wok=WritePlanar_XPK(iff,rh,bm);
		else wok=WriteChunky_XPK(pic,iff,rh,bm);
		}
	if(wok)
		{
		if(!(ifferr=PopChunk(iff))) ret=1;
		}
	}
return ret;
}

void main()
{
ULONG ret=10;
struct RDArgs *rd;
atexit(Ende);
Init();
if(rd=ReadArgs("FROM/A,TO/A,XPK/K,PLANAR/S,VERBOSE/S",(long *)&args,0))
	{
	BOOL ok=0;
	Object *pic;
	if(XpkBase||!args.xpk)
		{
		if(args.verbose) Printf("Loading %s ...\n",args.from);
		if(pic=NewDTObject(args.from,DTA_SourceType,DTST_FILE,DTA_GroupID,GID_PICTURE,PDTA_Remap,0,PDTA_DestMode,PMODE_V43,TAG_END))
			{
			if(DoMethod(pic,DTM_PROCLAYOUT,0,1))
				{
				struct IFFHandle *iff;
				if(iff=AllocIFF())
					{
					if(iff->iff_Stream=Open(args.to,MODE_NEWFILE))
						{
						InitIFFasDOS(iff);
						if(!(ifferr=OpenIFF(iff,IFFF_WRITE)))
							{
							if(!(ifferr=PushChunk(iff,ID_RGFX,ID_FORM,IFFSIZE_UNKNOWN)))
								{
								struct BitMapHeader *bh;
								struct RGHD rh;
								GetDTAttrs(pic,PDTA_BitMapHeader,&bh,TAG_END);
								rh.rgfx_LeftEdge=bh->bmh_Left;
								rh.rgfx_TopEdge=bh->bmh_Top;
								rh.rgfx_Width=bh->bmh_Width;
								rh.rgfx_Height=bh->bmh_Height;
								rh.rgfx_PageWidth=bh->bmh_PageWidth;
								rh.rgfx_PageHeight=bh->bmh_PageHeight;
								rh.rgfx_Width=bh->bmh_Width;
								rh.rgfx_Height=bh->bmh_Height;
								rh.rgfx_Depth=bh->bmh_Depth;
								if(args.verbose) Printf("Image size: %lu*%lu*%lu\n",rh.rgfx_Width,rh.rgfx_Height,rh.rgfx_Depth);
								if(rh.rgfx_Depth<=8)
									{
									if(args.planar)
										{
										rh.rgfx_PixelBits=rh.rgfx_Depth;
										rh.rgfx_BytesPerLine=(rh.rgfx_Width+7)>>3;
										rh.rgfx_BitMapType=RMBT_BYTEPLANAR8;
										if(args.verbose) Printf("Saving in Planar mode.\n");
										}
									else
										{
										rh.rgfx_PixelBits=8;
										rh.rgfx_BytesPerLine=rh.rgfx_Width;
										rh.rgfx_BitMapType=RMBT_BYTECHUNKY8;
										if(args.verbose) Printf("Saving in Chunky mode.\n");
										}
									}
								else
									{
									rh.rgfx_PixelBits=24;
									rh.rgfx_BytesPerLine=rh.rgfx_Width*3;
									rh.rgfx_BitMapType=RMBT_3BYTERGB24;
									if(args.verbose) Printf("Saving in RGB24 mode.\n");
									}
								rh.rgfx_Compression=(args.xpk)?RCMT_XPK:RCMT_NOCOMPRESSION;
								if(args.verbose)
									{
									if(args.xpk) Printf("Using Xpk packer %s.\n",args.xpk);
									else PutStr("No compression\n.");
									}
								rh.rgfx_XAspect=bh->bmh_XAspect;
								rh.rgfx_YAspect=bh->bmh_YAspect;
								if(PutRGHD(iff,&rh))
									{
									if(bh->bmh_Depth>8||PutRCOL(iff,pic))
										{
										if(PutRSCM(iff,pic,&rh)) ok=PutBODY(iff,pic,&rh);
										}
									}
								if(ok)
									{
									if(ifferr=PopChunk(iff)) ok=0;
									}
								}
							CloseIFF(iff);
							}
						Close(iff->iff_Stream);
						if(ifferr<-2) Printf("IFF error: %s\n",ifferrmsg[-ifferr]);
						if(xpkerr) Printf("Xpk error: %s\n",xpkerrmsg);
						if(memerr) PutStr("Out of memory.\n");
						if(!ok) DeleteFile(args.to);
						else
							{
							if(args.verbose) PutStr("Conversion finished.\n");
							SetProtection(args.to,FIBF_EXECUTE);
							ret=0;
							}
						}
					FreeIFF(iff);
					}
				else PutStr("AllocIFF failed.\n");
				}
			else PutStr("DTObject Layout failed.\n");
			DisposeDTObject(pic);
			}
		else PutStr("Loading of DTObject %s failed.\n");
		}
	else PutStr("You need xpkmaster.library 2.x or better to use Xpk compression !!\n");
	FreeArgs(rd);
	}
if(IoErr()) PrintFault(IoErr(),"DOS error");
exit(ret);
}
