/* 
 * 
 * SVP 0.1
 * A frontend for gs using svgalib
 * trying to emulate tmview's behaviour.
 * 
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libgen.h>
#include <vga.h>
#include "defsgen.h"
#include "font9x16.h"

#define CONFIG_FILENAME ".svprc"

#define MINDPI 30
#define MAXDPI 2400
#define STATUS_BG 0xff0000
#define STATUS_FG 0x404040

int xdim, ydim, bytesperline;
long long bw256[256], f256[256];
int multiplier=5000;
char basefname[256];
char fullname[256];
char bg_zero[8192];
int page, dpi=200, pageno;
int x, y;
int PDF;
int colorscheme=0;
int rx=0, ry=0;
int usecolor=0;
int autosize=0, autowidth=0;
int angle=0;
int savemode=1;
int verbose=0;
int malloced = 0;
unsigned char pal[770];
unsigned char *graphmem;

int mode2char=-1;
int number, lastnum;

int readconfigfile(int what, char *p) {
	FILE *f;
	char name[256];

	strncpy(name, getenv("HOME"), 248);
	strcat(name, "/"CONFIG_FILENAME);
	f=fopen(name, "r");
	if(!f) return -1;
	while (!feof(f)) {
		char s[256], t[256];
		int i, j, k, l, l1, c;
		fgets(s, 255, f);
		switch(what) {
			case 0:
				if(sscanf(s, "xres %i", &i) == 1 ) dpi = i; else
				if(sscanf(s, "yres %i", &i) == 1 ) dpi = i; else
				if(sscanf(s, "disx %i", &i) == 1 ) rx = i; else
				if(sscanf(s, "disy %i", &i) == 1 ) ry = i; else
				if(sscanf(s, "bw %i", &i) == 1 ) usecolor = i; else
				if(sscanf(s, "colo %i", &i) == 1 ) colorscheme = i; 
				if(!strncasecmp(s, "nosavemode", 10)) savemode = 0; 
				break;
			case 1:
				sprintf(t, "fmk \"%s\" %%i %%i %%i %%i %%i", p);
				if((c=sscanf(s, t, &l, &k, &j, &i, &l1)) >= 4) {
					page=l;
					x=k;
					y=j;
					dpi=i;
					if(c>=5) usecolor=l1;
				}
				break;
		}
	}

	fclose(f);

	return 0;
}

int writeconfigfile(int what, char *p) {
	FILE *f, *w;
	int wxr=0, wyr=0, wdx=0, wdy=0, wc=0, wuc=0;
	char name[256], newname[256];

	strncpy(name, getenv("HOME"), 248);
	strcat(name, "/"CONFIG_FILENAME);
	f=fopen(name, "r");
	strcpy(newname, name);
	strcat(newname, ".new");
	w=fopen(newname, "w");
				
	if(!w) {
		if(f)fclose(f);
		return -1;
	}

	while (f && !feof(f)) {
		char s[256], t[256];
		int i, j, k, l, l1;
		if(!fgets(s, 255, f)) break;
		switch(what) {
			case 0:
				if(sscanf(s, "xres %i", &i) == 1 ) {
					wxr = 1;
					sprintf(s, "xres %i\n", dpi);
				} else
				if(sscanf(s, "yres %i", &i) == 1 ) {
					wyr = 1;
					sprintf(s, "yres %i\n", dpi);
				} else
				if(savemode && sscanf(s, "disx %i", &i) == 1 ) {
					wdx = 1;
					sprintf(s, "disx %i\n", xdim);
				} else
				if(savemode && sscanf(s, "disy %i", &i) == 1 ) {
					wdy = 1;
					sprintf(s, "disy %i\n", ydim);
				} else
				if(sscanf(s, "bw %i", &i) == 1 ) {
					wuc = 1;
					sprintf(s, "bw %i\n", usecolor);
				} else
				if(sscanf(s, "colo %i", &i) == 1 ) {
					wc = 1;
					sprintf(s, "colo %i\n", colorscheme);
				}
				break;
			case 1:
				sprintf(t, "fmk \"%s\" %%i %%i %%i %%i %%i", p);
				if(sscanf(s, t, &l, &k, &j, &i, &l1) >= 4) {
					sprintf(s, "fmk \"%s\" %i %i %i %i %i\n", p, page, x, y, dpi, usecolor);
					wc=1;
				}
	
				break;
		}
		fprintf(w, "%s", s);
	}

	if(f)fclose(f);

	switch(what) {
		case 0:
			if(!wxr) {
				fprintf(w, "xres %i\n", dpi);
			}
			if(!wyr) {
				fprintf(w, "yres %i\n", dpi);
			} 
			if(savemode) {
				if(!wdx) {
					fprintf(w, "disx %i\n", xdim);
				} 
				if(!wdy) {
					fprintf(w, "disy %i\n", ydim);
				} 
			}
			if(!wc) {
				fprintf(w, "colo %i\n", colorscheme);
			}
			if(!wuc){
				fprintf(w, "bw %i\n", usecolor);
			}
			break;
		case 1:
			if(!wc) {
				fprintf(w, "fmk \"%s\" %i %i %i %i %i\n", p, page, x, y, dpi, usecolor);			
			}
			break;
	}
			
	fclose(w);
	
	rename(newname, name);
	
	return 0;
}


void display_status(int, int);	

uchar vgagetchar(int mode) {
	  /*
	   *     mode==0: get next key,  dont block
	   *     mode==1: get next key,  wait
	   *     mode==2: peek next key, dont block
	   */
	uchar input,next1,next2,next3;
	uchar com=KEYNOP;
	if(mode2char>=0){
	  	com=mode2char;
	  	if(mode!=2) mode2char=-1;
	  	return(com);
	}
  	if(mode==1){   /* hang until event occurs */
	    vga_waitevent(VGA_KEYEVENT, NULL, NULL, NULL, NULL);
	}
	
	input = vga_getkey();
	if(input >= ' ' && input < 128) com= input;
		else switch(input) {
			case 9: com=KEYTAB; break;
			case 10: com=KEYRET; break;
			case 27:                           /* escape */
				next1=0; next2=0; next3=0;
				next1=vga_getkey();
				switch(next1) {
					case 91:
						next2=vga_getkey();
						switch(next2) {
							case '6': com= KEYNEXT;   break;
							case '5': com= KEYPREV;   break;
							case 'A': com= KEYUP;     break;
							case 'B': com= KEYDOWN;   break;
							case 'D': com= KEYLEFT;   break;
							case 'C': com= KEYRIGHT;  break;
							case 'G': com= KEYCENTER; break;
							case '1': com= KEYHOME;   break;
							case '4': com= KEYEND;    break;
						}
						break;
					case 0: 
					case 27: 
						com=KEYESC; 
						break; /* allow wild escape abuse */
					default:
						 break;
				}
				break;
		}
	while(input !=0 && input != 255) input=vga_getkey();
	if(mode==2 && com!=KEYNOP) mode2char=com;
	return(com);
}

int display(unsigned char *image, int w, int h, int x, int y) {
	int dw, dh;
	int i,j;
	unsigned char *ip;

	if(x+xdim>w) dw=w-x; else dw=xdim;
	if(y+ydim-16>h) dh=h-y; else dh=ydim-16;

	if(usecolor) {
		for(j=0; j<dh; j++) {
			ip = image + (h-j-y-1)*w+x;
			vga_drawscansegment(ip, 0, j, dw);
			if(dw<xdim)vga_drawscansegment(bg_zero, dw, j, xdim-dw);
		}
		for ( ; j<ydim-16; j++) {
			vga_drawscansegment(bg_zero, 0, j, xdim);
		}
	} else {
		long long *gp;
		for(j=0; j<dh; j++) {
			gp=(long long *)(graphmem+j*bytesperline);
			ip = image + (((j+y)*w+x)>>3);
			for(i=0; i<dw; i+=8)*(gp++) = bw256[*(ip++)];
			for(; i<xdim;i+=8) *(gp++) = bw256[0];
		}
		for(; j<ydim-16; j++) {
			gp=(long long *)(graphmem+j*bytesperline);
			for(i=0; i<xdim;i+=8) *(gp++) = bw256[0];
		}
	}
	display_status(x, y);
	return 0;
}

void drawchar(int c, int x, int y){
    long long *gp;
	int i;
	
	gp=(long long *)(graphmem+y*bytesperline+x);
	for(i=0; i<FONTH; i++){
		*gp = f256[font[(c*FONTH+i)*2]];
		*(gp+1) = f256[font[(c*FONTH+i)*2+1]] ;
		gp += bytesperline>>3;
	}		
}

void drawstring(char *s, int x, int y) {
	while(*s){
		drawchar(*s, x, y);
		x+=FONTW;
		s++;
	}
}

void display_status(int x, int y) {
	char s[256];
	char *autos;
	int px, py;
	long long *gp;
	int i;
			
    gp=(long long *)(graphmem+(ydim-16)*bytesperline);
	for(i=0; i<16*bytesperline>>3; i++)
		*(gp++) = f256[0];
		
	sprintf(s, "%s [%i/%i]", basefname, page, pageno);
	drawstring(s, 8, ydim-16);
	px=x*72/dpi;
	py=y*72/dpi;
	if(autosize)autos="a"; else if(autowidth)autos="w"; else autos="";
	sprintf(s, "<%3i> [%i%s] (%i,%i)", angle, dpi, autos, px, py);
	drawstring(s, xdim-180, ydim-16);
	if(lastnum) {
		sprintf(s, "%i", number);
		drawstring(s, xdim/2, ydim-16);
	} else {
		sprintf(s, "{%i}", multiplier/100);
		drawstring(s, xdim/2-FONTW, ydim-16);
	}
}

const unsigned char BIT_REVERSE[256] = 
{
	0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
	0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
	0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
	0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, 
	0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 
	0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
	0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, 
	0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
	0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
	0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, 
	0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
	0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
	0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, 
	0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
	0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, 
	0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
};

int reverse_image(unsigned char **image, int angle, int color, int *width, int *height) {
	unsigned char *newimage, *t, *s;
	int w, h;

	w=*width;
	h=*height;

	while(angle>=360)angle-=360;
	while(angle<0)angle+=360;
		
	if(!w || !*image || !angle) return -1;
	
	switch(angle) {
		case 180:
			if(color) {
				newimage=malloc(w*h);
				t=newimage+w*h-1;
				s=*image;
				while(t>=newimage) {
					*t=*s;
					t--;
					s++;
				}
				s=*image;
				malloced = w*h;
				*image=newimage;
				free(s);
			} else { /* BW */
				newimage=malloc(w*h/8);
				t=newimage+w*h/8-1;
				s=*image;
				while(t>=newimage) {
					*t=BIT_REVERSE[*s];
					t--;
					s++;
				}
				s=*image;
				malloced = w*h/8;
				*image=newimage;
				free(s);
			}
			break;
		case 270:
			if(color) {
				int i,j;
				newimage=malloc(w*h);
				s=*image;
				for(i=h-1;i>=0;i--)for(j=0;j<w;j++) {
					newimage[j*h+i]=*s;
					s++;
				}
				s=*image;
				malloced = w*h;
				*image=newimage;
				free(s);
				*width=h;
				*height=w;
			} else { /* BW */
				int x,y,i,j,k;
				x=((h-1)|7)+1;
				y=w;
				newimage=calloc(x*y/8,1);
				s=*image;
				for(i=0;i<h;i++) for(j=w/8-1;j>=0;j--) {
					for(k=7;k>=0;k--) {
						if(*s&128) newimage[((j*8+k)*x+i)/8]|=128>>(i&7);
						*s<<=1;
					}
					s++;
				}
				s=*image;
				malloced=x*y/8;
				*image=newimage;
				free(s);
				*width=x;
				*height=y;
			}
			break;
		case 90:
			if(color) {
				int i,j;
				newimage=malloc(w*h);
				s=*image;
				for(i=0;i<h;i++)for(j=w-1;j>=0;j--) {
					newimage[j*h+i]=*s;
					s++;
				}
				s=*image;
				malloced = w*h;
				*image=newimage;
				free(s);
				*width=h;
				*height=w;
			} else { /* BW */
				int x,y,i,j,k;
				x=((h-1)|7)+1;
				y=w;
				newimage=calloc(x*y/8,1);
				s=*image;
				for(i=h-1;i>=0;i--) for(j=0;j<w/8;j++) {
					for(k=0;k<8;k++) {
						if(*s&128) newimage[((j*8+k)*x+i)/8]|=128>>(i&7);
						*s<<=1;
					}
					s++;
				}
				s=*image;
				malloced=x*y/8;
				*image=newimage;
				free(s);
				*width=x;
				*height=y;
			}
			break;

		default:
			break;
	}
			return 0;
}

int display_page(unsigned char **image, int w, int h) {
	int redraw, status_changed, o, ox, oy, dr;
	unsigned char *im=*image;
	
	lastnum=0;
	display(im, w, h, x, y);
	do {
		status_changed=0;
		o=vgagetchar(1);
		if(o>='0' && o<='9') {
			status_changed=1;
			if(!lastnum){
				lastnum=1;
				number=(o-'0');
			} else {
				number = number * 10 + (o-'0');
			}
		} else if(lastnum) {
			lastnum=0;
			status_changed=1;
			switch(o) {
				case 'g': case 'v':
					return o | (number<<16);
					break;
				default:
					if(number>0 && number<1000) multiplier = number*100;
			}
		}
		if('A' <= o && o<= 'Z') o+='a'-'A';
		
		redraw=0;
		
		dr=0;
		
		ox=x;
		oy=y;
		switch(o){
			case KEYUP:
				y-=multiplier/100;
				if(y<0)y=0;
				break;
			case KEYDOWN:
				y+=multiplier/100;
				if(y+ydim-16>h)y=h-ydim+16;
				if(y<0)y=0;
				break;
			case KEYLEFT:
				x-=multiplier/100;
				if(x<0)x=0;
				break;
			case KEYRIGHT:
				x+=multiplier/100;
				if(x+xdim>w)x=w-xdim;
				if(x<0)x=0;
				break;
			case '>':
				angle+=90;
				if(angle==360)angle=0;
				if(autowidth || autosize) {
					*image=im;
					return 'R';
				}
				reverse_image( &im, 90, usecolor, &w, &h);
				redraw=1;
				break;
			case '<':
				angle-=90;
				if(angle==-90)angle=270;
				if(autowidth || autosize) {
					*image=im;
					return 'R';
				}
				reverse_image( &im, 270, usecolor, &w, &h);
				redraw=1;
				break;
			case 'j':
				dr+=90;
			case 'k':
				dr+=90;
			case 'l':
				dr+=90;
			case 'i':
				if(angle!=dr) {
					if(autowidth || autosize) {
						angle=dr;
						*image = im;
						return 'R';
					}
					reverse_image( &im, dr-angle, usecolor, &w, &h);
					angle=dr;
					redraw=1;
				}
				break;
			case 'q': case '+': case '-': case KEYNEXT: case KEYPREV: case 'a': case 'w':
			case ' ':
				*image=im;
				return o;
				break;
		}
		if(ox!=x || oy!=y) redraw=1;
		if(redraw)display(im, w, h, x, y); 
			else if(status_changed) display_status(x,y);
	} while(1);		
}

void help() {
    fprintf(stderr, 
					"\nSVP-0.3 - a ghostscript frontend for svgalib\n"
					"\nUsage:\tsvp [-a] [-b] [-h] [-n] [-p] [-w] [-c color]\n"
					        "\t    [-d mode] [-r dpi] <filename>\n\n"
					"\t-a\t\tUse two color mode (black and white).\n"
					"\t-b\t\tUse 256 color mode.\n"
					"\t-h\t\tDisplay this help.\n"
					"\t-n\t\tDo not save config file when exiting.\n"
					"\t-o\t\tDo not save mode in config file.\n"
					"\t-p\t\tAutozoom to fit page.\n"
					"\t-w\t\tAutozoom to fit width.\n"
					"\t-c color\tColor scheme:\n"
					"\t\t\tcolor=0 - use standard colors.\n"
					"\t\t\tcolor=1 - reverse all colors.\n"
					"\t\t\tcolor=2 - reverse only in black and white mode.\n"
					"\t\t\tcolor=3 - reverse only the colors black and white.\n"
					"\t-d mode\t\tUse mode for display.\n"
					"\t\t\tmode can be WxY format (e.g. 800x600),\n"
					"\t\t\ta number of a 256 color mode (e.g. 12 for 1024x768),\n"
					"\t\t\tor an svgalib mode specification (e.g. G1600x1200x256).\n"
					"\t-r dpi\t\tPass dpi as the value for gs' -r option.\n"
					"\n");
    exit(0);
}

int main(int argc, char **argv) {
	FILE *f;
	char command[256], filename[256];
	int i, j, reread;
	int vmode=-1;
	char modename[64]="";
	unsigned char *pp;
	int writeconf=1, forcecolor=-1, forcedpi=-1;
	int writefmk=1;
	char *pbmraw="pbmraw", *bmp256="bmp256", *devicename;
	unsigned char *image=NULL;
	int height, width, fwidth;

	readconfigfile(0, NULL);
	
	while((i=getopt(argc, argv, "abc:d:hnopr:w"))!=-1) {
		switch(i) {
			case 'a':
				forcecolor=0;
				break;
			case 'b':
				forcecolor=1;
				break;
			case 'c':
				colorscheme=atoi(optarg);
				break;
			case 'd':
				if(sscanf(optarg, "%ix%i", &rx, &ry)!=2) {
					rx=0;
					ry=0;
					strncpy(modename, optarg, 63);
				}
				break;
			case 'h':
				help();
				break;
			case 'n':
				writeconf=0;
				break;
			case 'o':
				savemode=0;
				break;
			case 'p':
				autosize=1;
				autowidth=0;
				break;
			case 'r':
				j=atoi(optarg);
				if(j<=MAXDPI && j>=MINDPI) {
					forcedpi=j;
					autosize=0;
					autowidth=0;
				}
				break;
			case 'w':
				autosize=0;
				autowidth=1;
				break;
		}
	}
	
	if(optind>=argc) help();
	
	strncpy(filename, argv[optind],255);

	pp=(unsigned char *)bw256;
	for(i=0;i<256;i++) {
		for(j=128;j>0;j>>=1) *(pp++)= i&j ? 16 : 17;
	}
    pp=(unsigned char *)f256;
	for(i=0;i<256;i++) {
		for(j=128;j>0;j>>=1) *(pp++)= i&j ? 18 : 19;
	}
		 
	f=fopen(filename, "r");
	fgets(command, 255, f);

	if(!strncmp(command, "\033%-", 3)) {
		fgets(command, 255, f);
	}
	
	if(!strncmp(command, "%!PS", 4)) {
		int k, l;
		char s[256];
		
		k=0;
		while(k<100) {
			fgets(command, 255, f);
			if(sscanf(command, "%%%%Pages: %255s", s)) {
				if(strncmp("(atend)", s, 7)) {
					l=atoi(s);
					pageno = l;
					k=101;
				} else {
					pageno=0;
					while(!feof(f)) {
						fgets(command, 255, f);
						if(sscanf(command, "%%%%Page: %i", &l))pageno++;
					}
					k=101;
				}
			}
			k++;
		}
		fclose(f);
		PDF=0;
	} else {
		fclose(f);
		snprintf(command, 255, "gs -dBATCH -dNOPAUSE -dSAFER -sDEVICE=nullpage \"%s\" 2>&1", 
				 filename);
		f=popen(command, "r");
		pageno=0;
		while(!feof(f)) {
			char s[256];
			int p1, p2;
			fscanf(f, "%255[^\n]\n", s);
			if(sscanf(s, "Processing pages %i through %i", &p1, &p2)==2) {
				pageno=p2-p1+1;
				break;
			}
		}
		pclose(f);
		PDF=1;
	}

	if(pageno==0) {
		exit(1);
	}
	
	page=1;
	
	if(filename[0]=='/') strcpy(fullname, filename); else {
		getcwd(fullname, 254);
		strcat(fullname,"/");
		strcat(fullname, filename);
	}
	
	strncpy(command, filename, 255);
	strncpy(basefname, basename(command), 255);

	readconfigfile(1, fullname);
	
	if(forcecolor>-1) usecolor=forcecolor;
	if(forcedpi>-1) dpi = forcedpi;
	
	if(page<1 || page>pageno) {
		page=1;
		x=0;
		y=0;
	}

	if(dpi<MINDPI) dpi = MINDPI;
	if(dpi>MAXDPI) dpi = MAXDPI;
	
	if(colorscheme!=1)for(i=0;i<8192;i++)bg_zero[i]=244;

	vga_init();

	if(rx && ry) {
		int d, i;
		d=1000000;
		vmode=5;
		for(i=10; i<vga_lastmodenumber(); i++) {
			vga_modeinfo *mi;
			mi=vga_getmodeinfo(i);
			if(mi->colors==256 && mi->width>=rx && mi->height>=ry) {
				int diff;
				diff=(mi->width-rx)*(mi->width-rx)+(mi->width-ry)*(mi->width-ry);
				if(diff<d) {
					vmode=i;
					d=diff;
				}
			}
		}
	} else if(modename[0]) {
		vmode=vga_getmodenumber(modename);
	}
	
	if(vmode==-1 || !vga_hasmode(vmode) || vga_getmodeinfo(vmode)->colors!=256) {
		int x,i;
		x=0;
		for(i=10; i<vga_lastmodenumber(); i++) {
			vga_modeinfo *mi;
			mi=vga_getmodeinfo(i);
			if(mi->colors==256 && mi->width>x && vga_hasmode(i)) {
				x=mi->width;
				vmode=i;
			}
		}
		if(vmode==-1) vmode=10;
	}
	
	vga_setmode(vmode);
	vga_setlinearaddressing();
	graphmem=vga_getgraphmem();
	
	if(!usecolor) {
		if(colorscheme) {
			vga_setpalette(17, 0, 0, 0);
			vga_setpalette(16, 255, 255, 255);
		} else {
			vga_setpalette(16, 0, 0, 0);
			vga_setpalette(17, 255, 255, 255);
		}
		vga_setpalette(18, (STATUS_FG&0xff0000)>>16, (STATUS_FG&0xff00)>>8, STATUS_FG&0xff);
		vga_setpalette(19, (STATUS_BG&0xff0000)>>16, (STATUS_BG&0xff00)>>8, STATUS_BG&0xff);
	}

	xdim = vga_getxdim();
	ydim = vga_getydim();
	bytesperline = vga_getmodeinfo(vmode)->linewidth;
	
	reread=1;
	
	do {
		int odpi, param, cont;
		cont=1;
	
		while(cont) {
			if(reread) {
				int p;
				
				if(usecolor) devicename=bmp256; else devicename=pbmraw;
				
				snprintf(command, 255, "gs -q -dBATCH -dNOPAUSE -dSAFER -r%i -dFirstPage=%i"
							   " -dLastPage=%i -sDEVICE=%s -sOutputFile=- \"%s\"", 
							   dpi, page, page, devicename, filename);
				if(!verbose)strcat(command, " 2> /dev/null");
				f=popen(command, "r");
				
				if(PDF) p = 1; else p=page;
				
				if(usecolor) {
					do {
						unsigned char header[54];
						int palsize, i, size, fg, bg;
					
						fread(header, 54, 1, f);
						if(header[0]!='B' || header[1]!='M') {
							width=0;
							fwidth=0;
							height=0;
						} else {
							int mask;

							width=header[18]+(header[19]<<8)+(header[20]<<16)+(header[21]<<24);
							height=header[22]+(header[23]<<8)+(header[24]<<16)+(header[25]<<24);
							fwidth=((width-1)|3)+1;
							palsize=header[10]+(header[11]<<8)+(header[12]<<16)+(header[13]<<24);
							palsize=(palsize-54)>>2;
							if(palsize>256){
								/* error */
							}
							for(i=0;i<palsize;i++) {
								fread(pal+i*3, 4, 1, f);
							}
							size=fwidth*height;
							if(size>malloced) {
								if(image)free(image);
								image=malloc(size);
								if(image)malloced=size; else malloced=0;
							}
							mask = (colorscheme==1) ? 0x3f : 0;
							for(i=0;i<palsize;i++) {
								vga_setpalette(i, mask^(pal[i*3+2]>>2), mask^(pal[i*3+1]>>2), 
												  mask^(pal[i*3]>>2));
							}
							
							if(colorscheme==3) { /* always 332? */
								vga_setpalette(0,63,63,63);
								vga_setpalette(244,0,0,0);
								vga_setpalette(249,63,63,63);
								vga_setpalette(250,53,53,53);
								vga_setpalette(251,42,42,42);
								vga_setpalette(252,31,31,31);
								vga_setpalette(253,20,20,20);
								vga_setpalette(254,10,10,10);
								vga_setpalette(255,0,0,0);
							}
							
							pp=(unsigned char *)f256;
							
							fg=(((STATUS_FG&0xff0000)>>16)/37)*35 + 
								(((STATUS_FG&0xff00)>>8)/37)*5 +
								(((STATUS_FG&0xff)>>0)/52);
							bg=(((STATUS_BG&0xff0000)>>16)/37)*35 + 
								(((STATUS_BG&0xff00)>>8)/37)*5 +
								(((STATUS_BG&0xff)>>16)/52);
							if(colorscheme==1) {
								fg = 244 - fg;
								bg = 244 - bg;
							}
							
							for(i=0;i<256;i++) { /* always 332? */
								for(j=128;j>0;j>>=1) *(pp++)= i&j ? fg : bg;
							}
							fread(image, size, 1, f);
						}
						p--;
					} while(p);
					reverse_image(&image, angle, 1, &fwidth, &height);
				} else {
					do {
						int size;
						
						fscanf(f, "%s\n", command);
						if(command[0]!='P' || command[1]!='4') {
							width=0;
							fwidth=0;
							height=0;
						} else {
							fscanf(f, "%255[^\n]\n", command);
							i=fscanf(f, "%i %i\n", &width, &height);
							fwidth=((width-1)|7)+1;
							size=(fwidth>>3)*height;
							if(size>malloced) {
								if(image)free(image);
								image=malloc(size);
								if(image)malloced=size; else malloced = 0;
							}
							fread(image, size, 1, f);
						}
						p--;
					} while(p);
					
					reverse_image(&image, angle, 0, &fwidth, &height);
				}
				pclose(f);
					
				reread=0;
				if(y+ydim-16>height)y=height-ydim+16;
				if(y<0)y=0;
				if(x+xdim>fwidth)x=fwidth-xdim;
				if(x<0)x=0;								
			}
			cont=0;
			if(autosize||autowidth) {
				int px, py, pp;
				px=1000*fwidth/xdim;
				py=1000*height/(ydim-16);
				if((px>py)||autowidth)pp=px; else pp=py;
				if((pp>1010) || (pp<990)) { 
					dpi=1000*dpi/pp;
					cont=1;
					reread=1;
				}
			}
		}
	
		odpi=dpi;
		i = display_page(&image, fwidth, height);
		param=(i&0xffff0000)>>16;
		i&=0xffff;
		switch(i) {
			case 'q':
				i=0;
				break;
			case '+':
				autosize=0;
				autowidth=0;
				dpi+=multiplier/100;
				if(dpi>MAXDPI) {
					dpi=odpi;
				} else {
					reread=1;
					x=x*dpi/odpi;
					y=y*dpi/odpi;
				}
				break;
			case '-':
				autosize=0;
				autowidth=0;
				dpi-=multiplier/100;
				if(dpi<MINDPI) {
					dpi=odpi;
				} else { 
					reread=1; 
					x=x*dpi/odpi;
					y=y*dpi/odpi;
				}
				break;	
			case KEYNEXT:
			case ' ':
				if(page<pageno) {
					page++;
					y=0;
					reread=1;
				}
				break;
			case KEYPREV:
				if(page>1){
					page--;
					y=ydim;
					reread=1;
				}
				break;
			case 'g':
				if(param>=1 && param<=pageno && param!=page) {
					page=number;
					reread=1;
				}
				break;
			case 'v':
				if(param>=MINDPI && param <=MAXDPI) {
					dpi=param;
					if(dpi!=odpi)reread=1;
					x=x*dpi/odpi;
					y=y*dpi/odpi;
				}
				break;
			case 'a':
				autosize=!autosize;
				autowidth=0;
				reread=1;
				break;
			case 'w':
				autosize=0;
				autowidth=!autowidth;
				reread=1;
				break;
			case 'R':
				reread=1;
				break;
		}
	} while(i);
	
	vga_setmode(TEXT);

	if(writeconf) writeconfigfile(0, NULL);
	if(writefmk) writeconfigfile(1, fullname);
	
	return 0;

}
