/*
 *							    bmc.c
 *                  	BMP <-> BMC  Translater
 *					April 5, 1998, written by SHIMA
 */

#include <stdio.h>
#include <stdlib.h>
#include <io.h>
#include <fcntl.h>
#include <string.h>

#define	PLUG_IN	1

#ifdef	PLUG_IN
#include <windows.h>   /* Win32APIを使用する */
#include <winbase.h>

char inipath[MAX_PATH];
char spi[MAX_PATH];
int  dpi = -1;

enum {GET_BMP, PRINT_INFO, GET_BB};

struct PictureInfo
{
	long left,top;    /* 画像を展開する位置 */
	long width;       /* 画像の幅(pixel) */
	long height;      /* 画像の高さ(pixel) */
	WORD x_density;   /* 画素の水平方向密度 */
	WORD y_density;   /* 画素の垂直方向密度 */
	short colorDepth; /* １画素当たりのbit数 */
    HLOCAL hInfo;     /* 画像内のテキスト情報 */
};

static int (__stdcall *IsSupported)(LPSTR filename, DWORD dw);
static int (__stdcall *GetPicture)(LPSTR buf, long len, unsigned int flag, 
	HANDLE *pHBInfo,HANDLE *pHBm, FARPROC lpPrgressCallback, long lData);
static int (__stdcall *GetPluginInfo)(int infono, LPSTR buf, int buflen);
static int (__stdcall *GetPictureInfo)(LPSTR buf, long len, unsigned int flag,
	struct PictureInfo *liInfo);
char *open_plugin(char *, int mode);
#else

/*
 * header file for .bmp file header
 */
typedef	unsigned short	WORD;
typedef	unsigned long	DWORD;

typedef	struct	tagBITMAPFILEHEADER {
	WORD	bfType;
	DWORD	bfSize;
	WORD	bfReserved1;
	WORD	bfReserved2;
	DWORD	bfOffBits;
} BITMAPFILEHEADER;

typedef	struct	tagBITMAPINFOHEADER {
	DWORD	biSize;
	DWORD	biWidth;
	DWORD	biHeight;
	WORD	biPlanes;
	WORD	biBitCount;
	DWORD	biCompression;
	DWORD	biSizeImage;
	DWORD	biXPelsPerMeter;
	DWORD	biYPelsPerMeter;
	DWORD	biCirUsed;
	DWORD	biCirImportant;
} BITMAPINFOHEADER;
#endif

#define uchar  unsigned char

#define	TEST		0
#define	B_W			1

static char *print_info(FILE *fp, int, int);

/*
  id[2]				'B' 'C'
  flag[1]			flag & 1: Not compressed?
  key[1]			key:
  bmc_head_size[4]	
  byte_width[4]		
  height[4]			
  bit_width[4]		
  bmp_data_size[4]	
  f_mode[1]			kind of BMP, B&W et
  bmp_rev[1]		
  bmp_on[1]			
  bmp_off[1]		
  name[*]			original filename
  bmp_head[*]
  bmp_data[*]
*/

#if	TEST
int	c_num;
#endif

#if 0
typedef	struct	tagBITMAPFILEHEADER {
	uchar	bfType[2];
	uchar	bfSize[4];
	uchar	bfReserved1[2];
	uchar	bfReserved2[2];
	uchar	bfOffBits[4];
} BITMAPFILEHEADER;

typedef	struct	tagBITMAPINFOHEADER {
	uchar	biSize[4];
	uchar	biWidth[4];
	uchar	biHeight[4];
	uchar	biPlanes[2];
	uchar	biBitCount[2];
	uchar	biCompression[4];
	uchar	biSizeImage[4];
	uchar	biXPelsPerMeter[4];
	uchar	biYPelsPerMeter[4];
	uchar	biCirUsed[4];
	uchar	biCirImportant[4];
} BITMAPINFOHEADER;
#endif

void msg_out(void)
{
	printf(
		"\t    bmc.exe(Ver.0.5)  August 29, 1998 (written by SHIMA)\n\n"
		"Usage: bmc    <BMP file>\n"
		"       bmc -d <BMC|other graphic file>\n"
		"       bmc -b[:<dpi>] <BMP|BMC|EPSF|other graphic file>\n"
		"       bmc -p <EPSF file with a binary header>\n"
		"       bmc -v <BMP|BMC|other graphic file>\n\n"
		"\t    BMP (BMP file) -> BMC (compressed BMP file)\n"
#ifdef	PLUG_IN
		"\t-d: Graphic file   -> BMP (use Susie plug-in for other graphic files)\n"
		"\t-b: Graphic file   -> BoundingBox with the extension .bb\n"
		"\t\t\t <dpi> is dpi(default 72)\n"
		"\t-p: Extract PostScript codes from EPSF with a binary header\n"
		"\t-v: Print information of Graphic file\n\n"
#endif
		"Susie plug-ins (*.spi) are necessary for other graphic files.\n"
		"*.spi are searched from\n"
		"  current directory -> bmc's directory -> Susie's directory\n"
	);
}


char *msg[] = {
	"Cannot open the file to read",
	"A bad BMP/BMC file",
	"Cannot get memory",
	"Cannot open a file to write",
	"Cannot understand this file",
	"BoundingBox is not found",
	"Not EPSF with a binary header",
};

void err_exit(int mode)
{
	printf("\n%s!\n", msg[mode-1]);
	exit(mode);
}

static int ReadInt(FILE *fp)
{
	int num;

	num = getc(fp);
	num |= getc(fp)<<8;
	num |= getc(fp)<<16;
	return( num | (getc(fp)<<24) );
}


static int bmp_rev;
static int bmp_off;
static int bmp_on;
static int f_mode_BMP;

FILE *get_bmpfile(char *fname, int *top, int *width, int *height, 
	int *bit_width)
{
	int	ch, count, col, bmp_width, bmp_top, x;
#ifdef	B_W
	int pal[256];
#endif
	FILE *fp;

	fp = fopen(fname, "rb");
	if(fp == NULL)
		return NULL;

	if(getc(fp) != 'B' || getc(fp) != 'M')
		err_exit(2);					/* ID == "BM"? */
	for(ch = 0; ch < 8; ch++)
		getc(fp);
	*top = bmp_top = ReadInt(fp);		/* bfOffBits */
										/* EOF FILEHEADER */
	ReadInt(fp);
	*bit_width = x = ReadInt(fp);		/* biWidth */
	*height = ReadInt(fp);				/* biHeight */
	getc(fp); getc(fp);
	ch = getc(fp);
	ch += getc(fp) << 8;				/* biBitCount */
	switch(ch){
		case 1:		f_mode_BMP = 2;	
					bmp_width = (x + 7)/8;
					break;
		case 4:		f_mode_BMP = 3;
					bmp_width = (x + 1)/2;
					break;
		case 8:		f_mode_BMP = 4;
					bmp_width = x;
					break;
		case 16:	f_mode_BMP = 6;
					bmp_width = x*2;
					break;
		case 24:	f_mode_BMP = 5;
					bmp_width = x*3;
					break;
		case 32:	f_mode_BMP = 7;
					bmp_width = x*4;
					break;
	}
	*width = bmp_width = (bmp_width + 3)&~3;
#ifdef B_W
	ch = 16;
	while(ch-- > 0)
		getc(fp);
	count  = ReadInt(fp);				/* biCirUsed */
	if(count <= 16 && count > 0)
		f_mode_BMP |= 0x400;
	ReadInt(fp);
	if((f_mode_BMP & 0xff) == 2){
		bmp_rev = (getc(fp) & 0x80)?0:1;
		goto quit;
	}
	if(f_mode_BMP == 3 || f_mode_BMP == 4){
		if(count == 2){					/* Use 2 colors */
			if((ch == 0 || ch == 0xff) && 
				ch == getc(fp) && ch == getc(fp)){
				getc(fp);
				ch = getc(fp);
				if( (ch == 0 || ch == 0xff)
				  && ch == getc(fp) && ch == getc(fp)){
				  	if(ch){
				  		bmp_on = 1;
					  	bmp_off = 0;
					 }else{
					 	bmp_on = 0;
					 	bmp_off = 1;
					}
				  	f_mode_BMP |= 0x100;
				}
			}
		}else{								/* 16 or 256 colors */
			bmp_on = bmp_off = -1;
			if(!(ch = count))
				ch = (f_mode_BMP == 3)?16:256;
#if 0
			if(mode & IS_BW){
				for(count = 0; count < 16; count++)
					pal[count] = ReadInt(fp);
				for(; count < ch; count++)
					if(  pal[count & 0xf] != (col = ReadInt(fp))
					  && col != 0x7f7f7f && col != 0 )
						goto quit;			/* 256 colors in 8 bit */
				f_mode_BMP |= 0x400;
				goto quit;					/* 16 colors in 8 bit */
			}
#endif
			for(count = 0; count < ch; count++)
				pal[count] = ReadInt(fp);
			for(count = 16; count < ch; count++){
				if(  pal[count] != pal[count & 0xf]
				  && pal[count] != 0x7f7f7f
				  && pal[count] != 0 )
					break;
			}
			if (count >= ch)
				f_mode_BMP |= 0x400;
			count = 0;
			fseek(fp, bmp_top, SEEK_SET);
			if(f_mode_BMP == 3){	/* 16 color */
				col = (x + 7)&~7;
				while( (ch = getc(fp)) != EOF){
					count++;
					if(  (ch & 0xf) != bmp_off
					  && (ch & 0xf) != bmp_on
					  && count % col < x){
						if(pal[ch & 0xf] == 0xffffff){
							if(bmp_on < 0){
								bmp_on = ch & 0xf;
								goto n16_1;
							}
						}
						else if(pal[ch & 0xf] == 0){
							if(bmp_off < 0){
								bmp_off = ch & 0xf;
								goto n16_1;
							}
						}
						goto quit;
					}
n16_1:				count++;
					ch >>= 4;
					if(  ch != bmp_off
					  && ch != bmp_on
					  && count % col < x){
				  	if(pal[ch] == 0xffffff){
							if(bmp_on < 0){
								bmp_on = ch;
								goto n16_2;
							}
						}else if(pal[ch] == 0){
							if(bmp_off < 0){
								bmp_off = ch;
								goto n16_2;
							}
						}
						goto quit;
					}
n16_2:				count++;
				}
			}else{					/* 256 color */
				col = (x + 3)&~3;
				while( (ch = getc(fp)) != EOF){
					if(  ch != bmp_off
					  && ch != bmp_on
					  && count % col < x){
					  	if(pal[ch] == 0){
					  		if(bmp_off < 0){
					  			bmp_off = ch;
					  			goto n256;
					  		}
					  	}else if(pal[ch] == 0xffffff){
					  		if(bmp_on < 0){
					  			bmp_on = ch;
					  			goto n256;
					  		}
					  	}
						goto quit;
					}
n256:				count++;
				}
			}
bw_ok:		f_mode_BMP |= 0x300;
		}
	}else if(f_mode_BMP == 5 || f_mode_BMP == 6){	/* 24bit or 16bit color */
		fseek(fp, bmp_top, SEEK_SET);
		for(;;){
			for(count = x; count > 0; count--){
				if(  ((ch = getc(fp))!= 0 && ch != 0xff)
				  ||  ch != getc(fp)
				  || (f_mode_BMP == 5 && ch != getc(fp)) ){
					if(ch == EOF)
						goto bw_ok;
					goto quit;
				}
			}
			count  = x * ((f_mode_BMP==5)?3:2) & 3;
			if(count){
				count = 4 - count;
				while(count-- > 0)
					getc(fp);
			}
		}
	}
#endif
quit:
	return(fp);
}

char *marea(unsigned int size)
{
	if(size == 0)
		size = 1;

	return (char *)malloc(size);
}

void Free0(void *pt)
{
	if(pt != NULL)
		free(pt);
}


uchar *my_comp(uchar *s_buf, int width, int height, int *comp_size, int *key)
{
	uchar *d_buf;
	int w, h, ch, count, size, max_size, back, done, f_comp;
	int ch0;
	int f_count[16];

	/* frequency of the top 4 bits of bytes */
	for(w = 0; w < 16; w++)
		f_count[w] = 0;
	h = 0;
	size = w = back = 1;
	f_count[*s_buf >> 4]++;
	goto fs;
	for(; h < height; h++){
		back = width;
		for(w = 0; w < width; w++){
fs:			f_count[(s_buf[size] ^ s_buf[size-back]) >> 4]++;
			size++;
		}
	}
	count = f_count[1];
	f_comp = 1;
	for(w = 2; w < 16; w++){
		if(count > f_count[w]){
			f_comp = w;
			count = f_count[w];
		}
	} 
	f_comp <<= 4;
	max_size = width*height;
	d_buf = marea(max_size+count);
	if(d_buf == NULL)
		return NULL;
	/* first byte */
	ch0 = *s_buf++;
	count = size = 0;
	/* remaining bytes */
	w = back = 1;
	h = done = 0;
	goto rem;
	for(; h < height; h++){
		back = width;
		for(w = 0; w < width; w++, s_buf++){
rem:		ch = *s_buf ^ *(s_buf-back);
			if(ch == ch0){
				count++;
				continue;
			}else{
sk0:			if(ch0){
pc:				if( (ch0 & 0xf0) == f_comp){
						d_buf[size++] = f_comp;
						d_buf[size++] = ch0 & 0xf;
					}else
						d_buf[size++] = ch0;
				}
				if(count){
					if(!ch0){
#if	TEST
	printf("(%d)", count);
	if(c_num++ & 0x40){
		c_num = 0;
		printf("\n");
	}
#endif
						if(count < 16)
							d_buf[size++] = count + f_comp;
						else{
							d_buf[size++] = f_comp;
						 	if(count >= 0x1000000){
						 		d_buf[size++] = 0xfd;
					 			d_buf[size++] = count >> 24;
							 	goto sk1;
							}else if(count >= 0x10000){
								d_buf[size++] = 0xfe;
sk1:							d_buf[size++] = (count >> 16) & 0xff;
								goto sk2;
							}else if(count > 0xe0){
								d_buf[size++] = 0xff;
sk2:							d_buf[size++] = (count >> 8) & 0xff;
								goto sk3;
							}else
sk3:							d_buf[size++] = count & 0xff;
						}
					}else{
						if(count == 1){
							count = 0;
							goto pc;
/*							while(count--) */
/*							d_buf[size++] = ch0; */
/*							goto sk7; */
						}
#if	TEST
	printf("[%d:%d]", ch0, count-1);
	if(c_num++ & 0x40){
		c_num = 0;
		printf("\n");
	}
#endif
						d_buf[size++] = f_comp;
						if(--count < 0xf9 - 0xe0)
							d_buf[size++] = 0xe0 + count;
						else{
						 	if(count >= 0x1000000){
						 		d_buf[size++] = 0xf9;
							 	d_buf[size++] = count >> 24;
							 	goto sk4;
							}else if(count >= 0x10000){
								d_buf[size++] = 0xfa;
sk4:							d_buf[size++] = (count >> 16) & 0xff;
								goto sk5;
							}else if(count >= 0x100){
								d_buf[size++] = 0xfb;
sk5:							d_buf[size++] = (count >> 8) & 0xff;
								goto sk6;
							}else{
								d_buf[size++] = 0xfc;
sk6:							d_buf[size++] = count & 0xff;
							}
						}
					}
					count = 0;
				}else if(!ch0)
					d_buf[size++] = 0;
				if(done)
					goto end;
				ch0 = ch;
			}
		}
	}
	done = 1;
		goto sk0;
end:
 	if(size >= max_size){
		Free0(d_buf);
		return NULL;
	}
	*key = f_comp;
	*comp_size = size;
	return d_buf;
}

void my_decomp(uchar *s_buf, uchar *d_buf, int width, int height, int f_comp)
{
	int size, i, w, h, count, max_size;
	int ch;
	size = 0;
	max_size = width*height; 
	while(size < max_size){
rep:		if((*s_buf & 0xf0) != f_comp){
			d_buf[size++] = *s_buf++;
			continue;
		}
		ch = 0;
		if(!(count = *s_buf++ & 0xf)){
			if(!(*s_buf & 0xf0)){
				d_buf[size++] = (*s_buf++ & 0xf) + f_comp;
				continue;
			}
			count = *s_buf++;
			if(count > 0xfc){
				i = 0;
				while(count++ <= 0x100)
					i = (i << 8) + *s_buf++;
				count = i;
			}
			else if(count > 0xe0){
				ch = d_buf[size-1];
				if(count < 0xf9)
					count -= 0xe0;
				else{
					i = 0;
					while(count++ <= 0xfc)
						i = (i << 8) + *s_buf++;
					count = i;
				}
			}
		}
#if	TEST
	if(ch)
		printf("[%d:%d]", ch, count);
	else
		printf("(%d)", count);
	if(c_num++ & 0x40){
		c_num = 0;
		printf("\n");
	}
#endif
		count++;
		while(count-- && size < max_size)
		d_buf[size++] = ch;
		goto rep;
	}
	h = 0;
	w = count = size = 1;
	goto start;
	for( ; h < height; h++){
		count = width;
		for(w = 0; w < width; w++, size++)
start:			d_buf[size] ^= d_buf[size-count];
	}
}

void putint(int num, FILE *fp)
{
	int i;
	for(i = 0; i < 4; i++){
		putc(num & 0xff, fp);
		num >>= 8;
	}
}

void decomp(char *name)
{
	FILE *fp;
	int key, begin, width, height, size, top, flag, i;
	uchar *s_buf, *d_buf;
	uchar fname[256];

	fp = fopen(name, "rb");
	if(fp == NULL)
		err_exit(1);
	if(getc(fp) != 'B' || getc(fp) != 'C'){
		fclose(fp);
		strcpy(fname, open_plugin(name, GET_BMP));
		goto end;
	}
	flag = getc(fp);
	key = getc(fp);
	begin = ReadInt(fp);
	top = ReadInt(fp);
	width = ReadInt(fp);
	height = ReadInt(fp);
	ReadInt(fp);
	size = ReadInt(fp);
	getc(fp);
	getc(fp);
	getc(fp);
	getc(fp);
	for(i = 0; i < 256; i++){
		if((fname[i] = getc(fp)) == 0)
			break;
	}
	if(!access(fname, 0)){
		printf("%s already exists!", fname);
		fclose(fp);
		exit(5);
	}
	fseek(fp, begin, SEEK_SET);
	s_buf = marea(size);
	if(s_buf == NULL)
		err_exit(3);
	fread(s_buf, size, 1, fp);
	fclose(fp);
	if(flag & 1){
		fp = fopen(fname, "wb");
		if(fp == NULL)
			err_exit(4);
		fwrite(s_buf, size, 1, fp);
		Free0(s_buf);
		fclose(fp);
		return;
	}
	d_buf = marea(width*height);
	if(d_buf == NULL)
		err_exit(3);
	my_decomp(s_buf+top, d_buf, width, height, key);
	fp = fopen(fname, "wb");
	if(fp == NULL)
		err_exit(4);
	fwrite(s_buf, top, 1, fp);
	fwrite(d_buf, height, width, fp);
	Free0(s_buf);
	Free0(d_buf);
	fclose(fp);
end:
	printf("-> %s", fname);
}

void GetPostScript(char *path)
{
	int h_skip, h_size, x;
	FILE *spfp, *spofp;
	char fname[MAX_PATH + 8];
	char fname2[MAX_PATH + 8];

	spfp = fopen(path, "r");
	if(  !spfp
	  || getc(spfp) != 0xc5 || getc(spfp) != 0xd0
	  || getc(spfp) != 0xd3 || getc(spfp) != 0xc6 )
	 	err_exit(6);
	setmode(fileno(spfp), O_BINARY);		/* Binary Header */
	h_skip = getc(spfp);
	h_skip += (getc(spfp)<<8);
	h_skip += (getc(spfp)<<16);
	h_skip += (getc(spfp)<<24);
	h_size = getc(spfp);
	h_size += (getc(spfp)<<8);
	h_size += (getc(spfp)<<16);
	h_size += (getc(spfp)<<24);
	fseek(spfp, h_skip, SEEK_SET);

	strcpy(fname, path);
	strcat(fname, "$");
	strcpy(fname2, path);
	strcat(fname2, ".bak");
	spofp = fopen(fname, "wb");
	while((x = getc(spfp))!= EOF && h_size-- > 0)
		putc(x, spofp);
	fclose(spfp);
	fclose(spofp);
	if(rename(path, fname2) == 0){
		rename(fname, path);
		printf("Extract PostScript code.\nOriginal file -> %s\n", fname2);
	}else
		printf("Extract Postscript code in %s\n", fname);
}


char *GetBoundingBox(char *path)
{
	char *bb, *p;
	int h_skip;
	FILE *spfp;
	float x1, x2, y1, y2;
	int i,j,k;

	spfp = fopen(path, "r");
	if(spfp == NULL)
		return NULL;
	if(  getc(spfp) == 0xc5 && getc(spfp) == 0xd0
	  && getc(spfp) == 0xd3 && getc(spfp) == 0xc6 ){	/* Binary Header */
		setmode(fileno(spfp), O_BINARY);
		h_skip = getc(spfp);
		h_skip += (getc(spfp)<<8);
		h_skip += (getc(spfp)<<16);
		h_skip += (getc(spfp)<<24);
		fseek(spfp, h_skip, SEEK_SET);
		setmode(fileno(spfp), O_TEXT);
	}
	bb = marea(0x400);
	while(fgets(bb, 0x400, spfp)) {
		if((p = strstr(bb, "%%BoundingBox:" )) != NULL){
			while (*p++ != ':' );
			if(sscanf(p, "%f %f %f %f", &x1, &y1, &x2, &y2) == 4){
				fclose(spfp);
				bb[0] = bb[1] = '%';
				sprintf(bb+2, "BoundingBox: %f %f %f %f\n", x1, y1, x2, y2);
				for(i = 2; bb[i]; i++){
					if(bb[i] == '.'){
						for(j=i+1; bb[j]; j++){
							if(bb[j] == '0')
								continue;
							if(bb[j] >= '1' && bb[j] <= '9')
								break;
							k = i;
							while((bb[k++] = bb[j++]) != 0);
								break;
						}
					}
				}
				return bb;
			}
		}
	}
	Free0(bb);
	fclose(spfp);
	return NULL;
}

void main(int argc, char **argv)
{
	FILE *fp;
	unsigned char *s_buf, *d_buf;
	int top, width, height, bit_width, s_size, d_size, key, flag, pos, ext, i;
	unsigned char path[512];
	unsigned char fname[256];
	unsigned char fname2[256];

#ifdef	PLUG_IN
	strcpy(inipath, *argv);
#endif
	if(argc > 1){
		strcpy(path, argv[argc-1]);
		i = pos = ext = 0;
		while((key = path[i++]) != 0){
			if(key >= 0x81){
				if(key <= 0x9f || (key >= 0xe0 && key <= 0xfc)){
					if(path[i])
						i++;
					continue;
				}
			}else if(key == '\\' || key == '/' || key == ':')
				pos = i;
			else if(key == '.')
				ext = i-1;
		}
		if(argc > 2 && argv[1][0] == '-'){
			switch(argv[1][1]){
				case 'd':
					if(ext <= pos && access(path, 0))
						strcat(path, ".bmc");
					decomp(path);
					exit(0);
				case 'p':
					if(access(path, 0)){
						if(ext > pos)	goto no_file;
						strcpy(path+ext, ".ps");
						if(access(path, 0)){
							strcpy(path+ext, ".eps");
							if(access(path, 0))	goto no_file;
						}
					}
					GetPostScript(path);
					exit(0);
				case 'b':
					dpi = 0;
					if(argv[1][2] == ':' || argv[1][2] == '=')
						dpi = atoi(argv[1] + 3);
					if(  !stricmp(path + ext, ".ps")
					  || !stricmp(path + ext, ".eps") ){
						if(access(path, 0))
							goto no_file;
						if((d_buf = GetBoundingBox(path)) == NULL)
							err_exit(5);
						dpi = 0;
						goto put_bb;
					}
				case 'v':
					if(access(path, 0)){
						if(ext > pos){
no_file:					printf("%s is not found!\n", path);
							exit(1);
						}
						ext = strlen(path);
						strcpy(path+ext, ".bmc");
						if(access(path, 0)){
							strcpy(path + ext, ".bmp");
							if(access(path, 0))
								path[ext] = 0;
								goto no_file;
						}
					}
					fp = fopen(path, "rb");
					if(getc(fp) != 'B')
						goto v_other;
					switch(getc(fp)){
						case 'M':
							fseek(fp, 0, SEEK_SET);
							s_size = 0;
							goto put_info;
						case 'C':
							getc(fp); getc(fp);
							top = ReadInt(fp);
							fseek(fp, 0, SEEK_END);
							s_size = ftell(fp);
							fseek(fp, top, SEEK_SET);
put_info:					if(dpi == -1)
								print_info(fp, s_size, PRINT_INFO);
							else{
								d_buf = print_info(fp, s_size, GET_BB);
								goto put_bb;
							}
							break;
						default:
v_other:					d_buf = 
								open_plugin(path, 
									(dpi == -1)?PRINT_INFO:GET_BB);
							if(dpi != -1){
put_bb:							strcpy(fname, path);
								strcpy(fname+ext, ".bb");
								fp = fopen(fname, "w");
								if(fp == NULL){
									printf("Cannot create %s!\n", fname);
									exit(6);
								}
								fputs(d_buf, fp);
								fprintf(fp, "%c%c %s",'%', '%', path + pos);
								if(dpi > 0)
									fprintf(fp, "(%d dpi)", dpi);
								fputs("\n", fp);
								fclose(fp);
								printf("-> %s", fname);
							}
					}
					exit(0);
				default:
					goto msg;
				}
		}
		flag = 0;
		if(ext < pos && access(path, 0)){
			strcat(path, ".bmp");
			ext = strlen(path) - 4;
		}
		printf("%s", path);
		fp = get_bmpfile(path, &top, &width, &height, &bit_width);
		if(fp == NULL)
			err_exit(1);
		strcpy(fname, path + pos);
		strcpy(fname2, fname);
		ext -= pos;
		pos = strlen(fname);
		if(ext <= 0)
			ext = pos;
		strcpy(fname2 + ext, ".bmc");
		s_size = top + width*height;
		fseek(fp, 0L, SEEK_SET);
		s_buf = marea(s_size);
		if(s_buf == NULL)
			err_exit(3);
		fread(s_buf, s_size, 1, fp);
		fclose(fp);
		d_buf = my_comp(s_buf + top, width, height, &d_size, &key);
		fp = fopen(fname2, "wb");
		if(fp == NULL)
			err_exit(3);
		if(d_buf == NULL){
			flag |= 1;
			d_size = width*height;
		}
		putc('B', fp);
		putc('C', fp);
		putc(flag, fp);
		putc(key, fp);
		putint(pos + (32+1), fp);
		putint(top, fp);
		putint(width, fp);
		putint(height, fp); 
		putint(bit_width, fp); 
		putint(top+d_size, fp); 
		putc(((f_mode_BMP & 0xf00) >> 4)|(f_mode_BMP & 0x0f), fp); 
		putc(bmp_rev & 0xff, fp); 
		putc(bmp_on & 0xff, fp); 
		putc(bmp_off & 0xff, fp); 
		for(i = 0; i <= pos; i++)
			putc(fname[i], fp);

		fwrite(s_buf, top, 1, fp);
		if(d_buf){
			fwrite(d_buf, d_size, 1, fp);
			Free0(d_buf);
		}else
			fwrite(s_buf + top, d_size, 1, fp);
		Free0(s_buf);
		fclose(fp);
		printf("-> %s", fname2);
	}else
msg:	msg_out();
}


/* typedef unsigned char  uchar; */
typedef unsigned short ushort;
typedef unsigned int   uint;
typedef unsigned long  ulong;

char *print_info(FILE *fp, int size, int mode)
{
	BITMAPFILEHEADER fh;
	BITMAPINFOHEADER inf;
	int ratio, xx, yy;
	static char bb[256];

	fread((char *)&fh, sizeof(BITMAPFILEHEADER), 1, fp);
	fread((char *)&inf,sizeof(BITMAPINFOHEADER), 1, fp);

	if(mode == GET_BB){
		xx = inf.biWidth;
		yy = inf.biHeight;
		bb[0] = bb[1] = '%';
		if(!dpi)
			sprintf(bb+2, "BoundingBox: 0 0 %d %d\n", xx, yy);
		else
			sprintf(bb+2, "BoundingBox: 0 0 %fin %fin\n", 
				(double)xx/dpi, (double)yy/dpi);
		return bb;
	}
	printf("BMP file size: %9d\n", fh.bfSize);
	if(size){
		ratio = size*1000/fh.bfSize;
		printf("BMC file size: %9d (Reduced to %d.%d%%)\n", 
			size, ratio/10, ratio%10);
	}
	ratio = 1<<inf.biBitCount;
	printf("Image size   : %3d x %3d (%d colors with pallet size=%d)\n", 
		inf.biWidth, inf.biHeight, ratio, (fh.bfOffBits - 54)/4);
	if(inf.biCompression)
		printf("Compressed BMP file.");
	return bb;
}

#ifdef	PLUG_IN

int RestoreString(char* str, int size, char* szKey)
{
    HKEY hKey = NULL;
    DWORD dwType = REG_SZ;
    LONG lResult;
    DWORD copysize = size;
	char dummy[256];
	DWORD d_size = 256;

    str[0] = 0;
    if( RegOpenKeyEx( HKEY_CURRENT_USER, szKey, 0, KEY_READ, &hKey ) 
        != ERROR_SUCCESS  )
	    return 0;
    lResult = RegEnumValue(hKey, 0, dummy, &d_size, NULL, &dwType,
    	(LPBYTE)str, &copysize);
    if(lResult != ERROR_SUCCESS)
		str[0] = 0;
    RegCloseKey( hKey );
	return lstrlen(str);
}

int read_num(unsigned char *s, int byte)
{
	int num = *s;

	switch(byte){
		case 4:
			num += (s[3]<<24);
		case 3:
			num += (s[2]<<16);
		case 2:
			num += (s[1]<<8);
	}
	return num;
}

void ResolvePath(char *path, char *s)
{
	int len;

	if(s == NULL)
		s = "^x\\";
	if(*s == '^'){
		if(s[1] == 'x'){
			strcpy(path, inipath);
			len = strlen(path) - 4;
			while(len > 0
			  && path[len] != '\\' && path[len] != ':' && path[len] != '/')
				len--;
			path[len+1] = 0;
			goto rep;
		}else if(s[1] == 'w'){
		    GetWindowsDirectory(path, MAX_PATH);
rep:		strcat(path, s+3);
			goto quit;
		}
	}
	strcpy(path, s);
quit:
	len = strlen(path);
	if(len && path[--len] != '\\' && path[len] != '/')
		strcat(path, "\\");
}

char *open_plugin(char *fname, int mode)
{
	static HINSTANCE hlib;
	static FARPROC	func[4];
	static struct PictureInfo lpInfo;
	FILE *fh;
	HANDLE Info, Bm;
	unsigned char *tmp, *tmp1;
	char *spi_dll;
	int size, size1, col, pal, comp, pos, done, xx, yy;
	WIN32_FIND_DATA	wfd;
	HANDLE hfind32 = NULL;
	static unsigned char path[MAX_PATH];
	unsigned char path2[MAX_PATH];
	int count = 0;

	tmp = marea(0x800);
	for(size = 0; size < 0x800; )
		tmp[size++] = 0;
	fh = fopen(fname, "rb");
	fread(tmp, 0x800, 1, fh);
	fclose(fh);
	
	/* search *.spi */
	/*   Current directory  -> bmc.exe's directory -> Susie's directory */
	/* */
	if(hfind32 == NULL){
resume:	ResolvePath(path2, spi);
		pos = strlen(path2);
		strcpy(path, path2);
		strcat(path, "*.spi");
		if((hfind32 = FindFirstFile(path, &wfd)) == INVALID_HANDLE_VALUE)
			goto no_spi;
	}else{
nxt_spi:
		if(FindNextFile(hfind32, &wfd) == FALSE){
			FindClose(hfind32);
no_spi:		switch(count++){
				case 0:
					strcpy(spi, "^x");
					goto resume;

				case 1:
					if( RestoreString(spi, MAX_PATH, 
					  "Software\\Takechin\\Susie\\InstallRoot") )
						goto resume;

				default:
					Free0(tmp);
					err_exit(5);;
			}
		}
	}
	if( (wfd.dwFileAttributes
	 & (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_SYSTEM)) != 0)
		goto nxt_spi;
	strcpy(path2+pos, &(wfd.cFileName[0]));
	spi_dll = path2;

													/* load *.spi */
	if((hlib = LoadLibrary(spi_dll)) == NULL)
		goto nxt_spi;
	func[0] = GetProcAddress(hlib, "IsSupported");
	func[1] = GetProcAddress(hlib, "GetPicture");
	func[2] = GetProcAddress(hlib, "GetPictureInfo");
	func[3] = GetProcAddress(hlib, "GetPreview");

	if( (IsSupported = func[0]) == NULL
	 || (GetPicture = func[1]) == NULL
	 || !(*IsSupported)(fname, (DWORD)tmp)){
	 	FreeLibrary(hlib);
		goto nxt_spi;
	}
	if(mode == PRINT_INFO || mode == GET_BB){
		GetPictureInfo = func[2];
		(*GetPictureInfo)(fname, 0, 0, &lpInfo);
		if(mode == GET_BB){
			xx = lpInfo.width;
			yy = lpInfo.height;
			path[0] = path[1] = '%';
			if(!dpi)
				sprintf(path+2, "BoundingBox: 0 0 %d %d\n", xx, yy);
			else
				sprintf(path+2, "BoundingBox: 0 0 %fin %fin\n", 
					(double)xx/dpi, (double)yy/dpi);
quit:		FreeLibrary(hlib);
			return path;
		}else{
			GetPluginInfo = GetProcAddress(hlib, "GetPluginInfo");
			path[0] = 0;
			(*GetPluginInfo)(1, path, MAX_PATH);
			printf("%s:\n", path);
			printf("Image size   : %3d x %3d (%d bit for color)\n",
				lpInfo.width, lpInfo.height, lpInfo.colorDepth);
			goto quit;
		}
	}

	if((*GetPicture)(fname, 0, 0, &Info, &Bm, NULL, 0))
		err_exit(2);

	tmp1 = LocalLock(Bm);
	tmp = LocalLock(Info);
	size= read_num(tmp, 4);
	col = read_num(tmp + 14, 2);
	comp = read_num(tmp + 16, 4);
	size1 = read_num(tmp + 20, 4);
	if(size1 == 0){
		size1 = ((read_num(tmp + 4,4)*col+31)/32)*4;
		size1 *= read_num(tmp + 8,4);
	}
	pal = read_num(tmp + 32, 4);
	if(col < 16 && pal == 0)
		pal = 1<<col;
	if(comp == BI_BITFIELDS && (col == 16 || col == 32))
		pal += 3;
	size += pal*sizeof(RGBQUAD);

#if 0
	*buf = marea(size + size1);
	memcpy(*buf, tmp, size);
	memcpy(*buf+size, tmp1, size1);
	*buf_end = *buf + size + size1;
#endif

	strcpy(path, fname);
	for(done = 0, pos = strlen(fname); --pos > 0; ){
		if(path[pos] == '.'){
			if(!done){
				done = 1;
				strcpy(path+pos, ".bmp");
			}
		}else if(path[pos] == ':' || path[pos] == '/' || path[pos] == '\\'){
			pos++;
			break;
		}
	}
	if(!done)
		strcat(path, ".bmp");
	fh = fopen(path+pos, "wb");

	putc('B', fh);
	putc('M', fh);
	putint(size+size1+14, fh);	/* Size */
	putint(0, fh);				/* Reserved */
	putint(size+14, fh);		/* Offset */

	fwrite(tmp, size, 1, fh);
	fwrite(tmp1,  size1, 1, fh);
	fclose(fh);

	LocalUnlock(Bm);
	LocalFree(Bm);
	LocalUnlock(Info);
	LocalFree(Info);
	GetPluginInfo = GetProcAddress(hlib, "GetPluginInfo");
	path2[0] = 0;
	(*GetPluginInfo)(1, path2, MAX_PATH);
	printf("%s:\n", path2);
#if 0
	if(lpInfo.hInfo)
		GlobalFree(lpInfo.hInfo);
#endif
	FreeLibrary(hlib);
	return path+pos;
#if 0
	GetPreview = func[3];
#endif
}
#endif