#include "defines.h"
/*     Bed a menu-driven multi dataformat binary editor for Linux            */
/*     Copyright (C) 1998 Jaap Korthals Altes <jkaltes@hetnet.nl>       */
/*                                                                           */
/*     This program is free software; you can redistribute it and/or modify  */
/*     it under the terms of the GNU General Public License as published by  */
/*     the Free Software Foundation; either version 2 of the License, or     */
/*     (at your option) any later version.                                   */
/*                                                                           */
/*     This program 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 General Public License for more details.                          */
/*                                                                           */
/*     You should have received a copy of the GNU General Public License     */
/*     along with this program; if not, write to the Free Software           */
/*     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.             */
/* Fri Dec  8 22:14:27 2000                                                  */
#include <string.h>
#include <alloca.h>
#include "filter.h"
#include "myalloc.h"
#include "editor.h"
#include "screenpart.h"
/*
At firstfilter stands the filter nearest to the file,
at lastfilter-1 the filter nearest to screen */

int filtersmaxlen=0,filtersnr=0;
char **filtersnames=NULL;
Filter *(**filtersfuncs)(int,int,char*,ScreenPart *)=NULL ;

Filter *(*getfilterfunc(char *name,int len)) (int,int,char *,ScreenPart*) {
	int i;
	for(i=0;i<filtersnr;i++)
		if(!memcmp(name,filtersnames[i],len)&&!filtersnames[i][len])
			return filtersfuncs[i];
	return NULL;
	};
char*	initfilter(char *str, Filter *(*func)(int,int,char *,ScreenPart *)) {
		int len=strlen(str);
		if(filtersmaxlen>=filtersnr) {
			filtersmaxlen=(filtersmaxlen+10)*2;
			char **tmp=filtersnames;
			filtersnames=new (char *)[filtersmaxlen];
			if(tmp) {
				memcpy(filtersnames,tmp,filtersnr*sizeof(char*));
				delete[] tmp;
				}
			Filter *(**tmpfunc)(int,int,char*,ScreenPart *) =filtersfuncs;
			filtersfuncs=new (Filter *(*)(int,int,char*,ScreenPart *))[filtersmaxlen];
			if(tmpfunc) {
				memcpy(filtersfuncs,tmpfunc,filtersnr*sizeof(Filter *(*)(int,int,char*)));
				delete[] tmpfunc;
				}
			}
		filtersnames[filtersnr]=new char[len+1];
		memcpy(filtersnames[filtersnr],str,len+1);
		filtersfuncs[filtersnr++]=func;
		return filtersnames[filtersnr-1];
		}
	Filter::Filter(int fb,int sb) {
		part=NULL;
		name="filter";
		label="x";
		err=0;
		bytesscreen=sb;
		bytesfile=fb;
		}
	Filter::Filter(Filter &fil) {
		part=NULL;
		bytesscreen=fil.bytesscreen;
		bytesfile=fil.bytesfile;
		label=fil.label;
		name=fil.name;
		err=fil.err;
		}
	int Filter::error(void) {
		return err;
		}

static  char noconvstr[]="";
	char *Filter::convstr(void) {
		return noconvstr;
		}
	int  Filter::equal(Filter *fil) {
		if(strcmp(fil->label,label))
			return 0;
		if(fil->bytesscreen!=bytesscreen)
			return 0;
		if(fil->bytesfile!=bytesfile)
			return 0;
		return 1;
		}

	int Filter::setpart(ScreenPart *p) {
		part=p;
		return 0;
		}
	Filter::~Filter() {}
	int Filter::screen2filepos(int pos) {
		return (pos*bytesfile)/bytesscreen;
		}
	int Filter::file2screenpos(int pos) {
		return (pos*bytesscreen)/bytesfile;
		}
/*If within a datatype an byte offset translates in a offset in what is
written to the screen, this function should make
*/

int ScreenPart::byte2screen(int pos) {
	int newpos=fromfilefilterspos(pos);
	return byteoff2screen(newpos);
	}
int ScreenPart::screen2byte(int pos) {
	int newpos=screen2byteoff(pos);
	if(newpos>=0)
		return tofilefilterspos(newpos);
	else 
		return newpos;
	}
int ScreenPart::fromfilefilterspos(int pos) {
	int newpos=pos;
	for(int i=firstfilter;i<lastfilter;i++	) 
		newpos=filters[i]->file2screenpos(newpos);
	return newpos;
	}
int ScreenPart::tofilefilterspos(int pos) {
	int newpos=pos;
	for(int i=(lastfilter-1);i>=firstfilter;i--	) 
		newpos=filters[i]->screen2filepos(newpos);
	return newpos;
	}
int ScreenPart::fromfilefilters(char *input,int &inplen,char *output) {
	int len=inplen;
	char *ptr=input;
	int byt=bytes;
	for(int i=firstfilter;i<lastfilter;i++	) {
		byt=filters[i]->bytesscreen;
		char *tmp=(char *)alloca(byt);
		int uit=filters[i]->fromfile((unsigned char *)ptr,len,(unsigned char *)tmp);
		if(i==firstfilter)
			inplen=len;
		len=uit;
		ptr=tmp;
		}
	if(output==ptr)
		return len;
	memmove(output,ptr,len);
	return len;
	}
int ScreenPart::tofilefilters(char *input,int &inplen,char *output) {
	int byt=bytes;
	char **tussen=(char **)alloca((lastfilter-firstfilter+1)*sizeof(char *));
	char *ptr;
	tussen[firstfilter]=output;
	for(int i=firstfilter;i<(lastfilter-1);i++	) {
		byt=filters[i]->bytesscreen;
		tussen[i+1]=(char *)alloca(byt);
		filters[i]->fromfile((unsigned char *)tussen[i],filters[i]->bytesfile,(unsigned char *)tussen[i+1]);
		}
	ptr=input;
	int len=inplen;
	for(int i=lastfilter-1;i>=firstfilter;i--	) {
		int uit=filters[i]->tofile((unsigned char *)ptr,len,(unsigned char *)tussen[i]);
		if(i==(lastfilter-1))
			inplen=len;
		len=uit;
		ptr=tussen[i];
		}
	if(output==ptr)
		return len;
	memmove(output,ptr,len);
	return len;
	}

int Editor::addfilter(Filter *fil) {
	int fors,bins;
	ScreenPart *sub=parts[mode]->getsubpart(fors,bins,parts[mode]->half);
	sub->addfilter(fil);
	int res;
	if( (res=parts[mode]->calcsizes())<=0) {
		sub->rmlastfilter();
		return -1;
		}
	return res;
	}

Filter *ScreenPart::getlastfilter(void) {
	if(lastfilter>firstfilter) {
		return filters[lastfilter-1];
		}
	else
		return NULL;
	}
char *ScreenPart::lastfiltername(void) {
	if(lastfilter>firstfilter) {
		return filters[lastfilter-1]->label;
		}
	else
		return NULL;
	}

int canresize(ScreenPart *sub,int negbyt) ;
int ScreenPart::rmlastfilt(char *name) {
	if(lastfilter>firstfilter&&(!name||!strcmp(name,filters[lastfilter-1]->label))) {
		if(filters[lastfilter-1]->wantaway()) {
			lastfilter--;
			int wasbytes=bytes;
			int byt=filters[lastfilter]->bytesscreen;
			if(screenupdatefilters( byt)>0&&canresize(this,bytes-wasbytes)>=0) {
					
				delete filters[lastfilter];
				mklabel();
				editor->updated=0;
				return 0;
				}
			lastfilter++;
			screenupdatefilters( byt);
			return -2;
			}
		else
			return -3;
		}
	return -1;
	}
int ScreenPart::rmlastfilter(void) {
	int fors,bins;
	ScreenPart *sub=getsubpart(fors,bins,half);
	if(sub)  {

		 int res= sub->rmlastfilt();
		 calcsizes();
		 mklabel();
		 return res;
		 }
		
	return -1;
	}
int Editor::rmlastfilter(void) {
	int res;
	if(( res=parts[mode]-> rmlastfilter() )==-1) {
		message("No filters set");
		return res;
		}
	switch(res) {
		case -2: {
			message("Can't remove filter. Try change bytes before removing.");
			return res;
			};
		case -3: {
			message("Can't remove filter, not wantaway()");
			return res;
			}
		}
	return 0;
	}

int ScreenPart::allocfilters(int nr) {
	{
	if(maxfilter<(lastfilter+nr)) {
		int oldmaxfilter=maxfilter;
		if(maxfilter<2)
			maxfilter=2;
		maxfilter*=(2*(nr+maxfilter)/maxfilter);
		filters=myrealloc(Filter **,filters,Filter *,maxfilter,oldmaxfilter);
		}
	}
	return maxfilter;
	}
int ScreenPart::addfilter(Filter *fil) {
	allocfilters(1);
	fil->setpart(this);
	filters[lastfilter++]=fil;
	int byt= filters[lastfilter-1]->bytesscreen;
	if((screenupdatefilters(byt))>0) {
		mklabel();
		editor->updated=0;
		return lastfilter;
		}
	lastfilter--;
	delete filters[lastfilter];
	bytes=screenupdatefilters(byt);
	return -1;
	}

int ScreenPart::setfilter(Filter *fil) {
	allocfilters(1);
	fil->setpart(this);
	filters[lastfilter++]=fil;
	return lastfilter;
	}
int ScreenPart::copyfiltersafter(ScreenPart *part) {
	int nrf=part->lastfilter-part->firstfilter;
	allocfilters(nrf);
	for(int i=0;i<nrf;i++) {
		filters[lastfilter]=part->filters[firstfilter+i]->newone();
		filters[lastfilter++]->setpart(this);
		}
	return lastfilter;
	}
/*
int ScreenPart::copyfiltersafter(ScreenPart *part) {
	int nrf=part->lastfilter-part->firstfilter;
	allocfilters(nrf);
	for(int i=0;i<nrf;i++) {
		filters[lastfilter]=part->filters[firstfilter+i]->newone();
		filters[lastfilter++]->setpart(this);
		}
	int byt= bytes;
	if(screenupdatefilters(byt)>0) {
		mklabel();
		editor->updated=0;
		return lastfilter;
		}
	for(int i=0;i<nrf;i++) 
		delete filters[--lastfilter];
	screenupdatefilters(byt);
	return -1;
	}
*/
#define writefilter	len+=sprintf(buf+len,"%s(%d,%d,\"%s\")",filters[i]->name,filters[i]->bytesfile,filters[i]->bytesscreen,filters[i]->convstr());
int ScreenPart::filtersstring(char *buf) {
	int len=0;;
	int i=firstfilter;
	if(i<lastfilter)
		writefilter;
	i++;
	for(;i<lastfilter;i++) {
		buf[len++]=',';
		writefilter;
		}
	return len;
	}

int ScreenPart::copyfiltersbefore(ScreenPart *part) {
	int nrf=part->lastfilter-part->firstfilter;
	allocfilters(nrf);
	memmove(filters+firstfilter+nrf,filters+firstfilter,(lastfilter-firstfilter)*sizeof(Filter *));
	for(int i=0;i<nrf;i++) {
		filters[i+firstfilter]= part->filters[i+part->firstfilter]->newone();
		filters[i+firstfilter]->setpart(this);
		}
	int byt= bytes;
	if(screenupdatefilters(byt)>0) {
		lastfilter+=nrf;
		mklabel();
		editor->updated=0;
		return lastfilter;
		}
	for(int i=0;i<nrf;i++)
		delete filters[i+firstfilter];
	memmove(filters+firstfilter,filters+firstfilter+nrf,(lastfilter-firstfilter)*sizeof(Filter *));
	screenupdatefilters(byt);
	return -1;
	}
int ScreenPart::filebytes(int b) {
	int byt=b,res;
	for(int i=firstfilter;i<lastfilter;i++	) {
		if((byt=filters[i]->chbytes(byt))<0)
			return -1;
		}
	if((res=chbytes(byt))<1)
		return res;
	return byt;
	}
int ScreenPart::screenupdatefilters(int byt) {
	for(int i=(lastfilter-1);i>=firstfilter;i--) {
		if((byt=filters[i]->chbytesback(byt))<0)
			return -1;
		filters[i]->err=0;
		}
	if(byt>0&&bytes!=byt) {
		bytes=byt;
		editor->shouldreinit=1;
		}
	return byt;
	}
int ScreenPart::equal(ScreenPart *part) {
	if(strcmp(part->getname(),getname()))
		return 0;
	if(bytes!=part->bytes)
		return 0;
	if(type!=part->type)
		return 0;
	if(firstfilter!=part->firstfilter)
		return 0;
	if(lastfilter!=part->lastfilter)
		return 0;
	for(int i=firstfilter;i<lastfilter;i++)
		if(!filters[i]->equal(part->filters[i]))
			return 0;
	return 1; 
	}
int ScreenPart::screenbytes(int b) {
	int res;
	if((res=chbytes(b))<1) {
		return res;
		}
	if(b==-1) {
		return 1;
		}
	return screenupdatefilters( res) ;
	}

#include "reverse.h"

registerfilterclass(Reverse) 
int ScreenPart::addreverse(int  len, int un) {
	Reverse *rev=new Reverse(len,un);
	return addfilter(rev);
	}
int ScreenPart::reverse(void) {
	Reverse *rev=new Reverse(datbytes(),1);
	if(lastfilter>firstfilter) {
		 if(rev->equal(filters[lastfilter-1])) {
			rmlastfilt();
			delete rev;
			return 0;
		 	}
		 }
	addfilter(rev);
	return 0;
	}
int addsubbin(ScreenPart *super,ScreenPart *sub,unsigned char *bin,unsigned char *subbin,int len) {
	if(super==sub) {
	    memmove(bin,subbin,len);	
	    return len;
	    }
	int forstart,binstart;
	ScreenPart *child=super->getchild(forstart,binstart,super->half);
	if(!child)
		return -1;
	int flen=super->getbytes();
	char nbin[super->datbytes()+20];
	int fillen=super->fromfilefilters((char *)bin,flen,nbin); 
	int nlen=addsubbin(child,sub,(unsigned char *)nbin+binstart,subbin,len);
	if(nlen>0) {
		super->tofilefilters(nbin,fillen,(char *)bin); 
		}
	return nlen;
	}
