/*
 *	ABC export library for  BMC format file  used by dviout for Windows
 *  	   July  8, 2001 (Ver.0.1 written by SHIMA)
 */

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

#define	marea(x)	malloc(x)
#define	Free0(x)	free(x)

#define	IS_COL16	0x000400
#define	IS_BW		0x000100

#define XPI_NO_FUNCTION       -101  /* その機能はインプリメントされていない */
#define XPI_ALL_RIGHT            0  /* 正常終了 */
#define XPI_ABORT              101  /* コールバック関数が非0を返したので展開を中止した */
#define XPI_NOT_SUPPROT        102  /* 未知のフォーマット */
#define XPI_OUT_OF_ORDER       103  /* データが壊れている */
#define XPI_NO_MEMORY          104  /* メモリーが確保出来ない */
#define XPI_MEMORY_ERROR       105  /* メモリーエラー */
#define XPI_FILE_READ_ERROR    106  /* ファイルリードエラー */
#define	XPI_WINDOW_ERROR       107  /* 窓が開けない */
#define XPI_OTHER_ERROR        108  /* 内部エラー */
#define	XPI_FILE_WRITE_ERROR   109  /* 書き込みエラー */
#define	XPI_END_OF_FILE        110  /* ファイル終端 */

#define	CLLBK	1

#define	DLLAccess __declspec(dllexport)

#define ALL_RIGHT          0  /* 正常終了 */
#define NO_FUNCTION       -1  /* その機能はインプリメントされていない */
#define ABORT              1  /* コールバック関数が非0を返したので展開を中止した */
#define NOT_SUPPROT        2  /* 未知のフォーマット */
#define OUT_OF_ORDER       3  /* データが壊れている */
#define NO_MEMORY          4  /* メモリーが確保出来ない */
#define MEMORY_ERROR       5  /* メモリーエラー */
#define FILE_READ_ERROR    6  /* ファイルリードエラー */
#define OTHER_ERROR        8  /* 内部エラー */

#pragma pack(push)
#pragma pack(1)
/*
	構造体のメンバ境界を1バイトにする pragma
	ワード単位に設定されていると hInfoの位置がずれる
*/
struct PictureInfo
{
  long left,top;    /*画像を展開する位置*/
  long width;       /*画像の幅(pixel)*/
  long height;      /*画像の高さ(pixel)*/
  WORD x_density;   /*画素の水平方向密度*/
  WORD y_density;   /*画素の垂直方向密度*/
  short colorDepth; /*１画素当たりのbit数*/
  HLOCAL hInfo;     /*画像内のテキスト情報*/
};
#pragma pack(pop)


BOOL APIENTRY DllMain( HANDLE hModule, 
                        DWORD ul_reason_for_call, 
                        LPVOID lpReserved )
{
    switch( ul_reason_for_call) {
    case DLL_PROCESS_ATTACH:
		break;
    case DLL_THREAD_ATTACH:
		break;
    case DLL_THREAD_DETACH:
		break;
    case DLL_PROCESS_DETACH:
		break;
    }
	return TRUE;
}

char *information[] = {
	"T0XN",
	"BMC export library v0.1 (c)Toshio OSHIMA",
	"BMC",
	".bmc",
};

DLLAccess int __stdcall
	 GetPluginInfo(int infono,LPSTR buf,int buflen)
{
 	if(infono < 0 || infono >= (sizeof(information) / sizeof(char *)))
		return 0;
	strcpy(buf,information[infono]);
	return
		strlen(buf);
}

DLLAccess int __stdcall ConfigurationDlg(HWND parent,int fnc)
{
	return -1;
}

DLLAccess int __stdcall IsSupported(int colorDepth)
{
	int ret = 0;
    /* 色深度を確認 */
  	if (   colorDepth == 1
        || colorDepth == 4
        || colorDepth == 8
        || colorDepth == 16
        || colorDepth == 24
        || colorDepth == 32
       ) {
            ret = 1;
    }    

    return ret;
}

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

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

BOOL check_bmpfile(BITMAPINFOHEADER *bmif, unsigned char *buff)
{
	int	ch, count, col, bmp_width, x, size;
	int pal[256];
	unsigned char *pt, *pteof;

	x = bmif->biWidth;
	ch = bmif->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;
	}

	bmp_width = (bmp_width + 3)&~3;
	size = bmp_width * bmif->biHeight;
	count  = bmif->biClrUsed;

	if(count <= 16 && count > 0)
		f_mode_BMP |= IS_COL16;

	pt = (unsigned char *)bmif + sizeof(BITMAPINFOHEADER);

	if( (f_mode_BMP & 0xff) == 2)
	{
		bmp_rev = (*pt & 0x80)?0:1;
		goto quit;
	}
	if(f_mode_BMP == 3 || f_mode_BMP == 4){
		if(count == 2){					/* Use 2 colors */
			ch = *pt++;
			if((ch == 0 || ch == 0xff) && 
				ch == *pt++ && ch == *pt++){
				pt++;
				ch = *pt;
				if( (ch == 0 || ch == 0xff)
				  && ch == *pt++ && ch == *pt++){
					if(ch){
						bmp_on = 1;
						bmp_off = 0;
					 }else{
						bmp_on = 0;
						bmp_off = 1;
					}
					f_mode_BMP |= IS_BW;
				}
			}
		}else{								/* 16 or 256 colors */
			bmp_on = bmp_off = -1;
			if(!(ch = count))
				ch = (f_mode_BMP == 3)?16:256;
			for(count = 0; count < ch; count++){
				pal[count] = *pt|(pt[1] << 8)|(pt[2]<<16)|(pt[3]<<24);
				pt += 4;
			}
			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 |= IS_COL16;
			count = 0;

			pteof = (pt = buff) + size;
			if((f_mode_BMP & 0xf) == 3){	/* 16 color */
				col = (x + 7)&~7;
				while(pt < pteof){
					ch = *pt++;
					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(pt < pteof){
					ch = *pt++;
					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 |= 0x200|IS_BW;
		}
	}else if(f_mode_BMP == 5 || f_mode_BMP == 6){	/* 24bit or 16bit color */
		pteof = (pt = buff) + size;
		for(;;){
			if(pt >= pteof)
				goto bw_ok;
			for(count = x; count > 0; count--){
				if(	 ((ch = *pt++)!= 0 && ch != 0xff)
				  ||  ch != *pt++
				  || (f_mode_BMP == 5 && ch != *pt++) ){
					goto quit;
				}
			}
			count  = x * ((f_mode_BMP==5)?3:2) & 3;
			if(count){
				count = 4 - count;
				while(count-- > 0)
					pt++;
			}
		}
	}
quit:
	return TRUE;
}


static unsigned char *my_comp(unsigned char *s_buf, int width, int height, 
	int *comp_size, int *key)
{
	unsigned char *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(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;
						}
						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;
}

static BOOL bmp2bmc(char *name, BITMAPFILEHEADER *bmpfh, 
	BITMAPINFOHEADER *bmpif, unsigned char *buff, int width)
{
	unsigned char *d_buf;
	int pos, skip, d_size, key, i, flag=0;
	FILE *fp;

	if(check_bmpfile(bmpif, buff) == FALSE)
		return FALSE;
	pos = strlen(name);
	for(skip = pos-1; skip >= 0; skip--){
		if(name[skip] == '\\' || name[skip] == '/' || name[skip] == ':')
			break;
	}
	skip++;

	d_buf = my_comp(buff, width, bmpif->biHeight, &d_size, &key);
	fp = fopen(name, "wb");
	if(fp == NULL)
		return FALSE;
	if(d_buf == NULL){
		d_size = width*bmpif->biHeight;
		flag |= 1;
	}
	putc('B', fp);
	putc('C', fp);
	putc(flag, fp);
	putc(key, fp);
	putint(pos - skip + (32+1), fp);	// pos of "BM"
	putint(bmpfh->bfOffBits, fp);
	putint(width, fp);
	putint(bmpif->biHeight, fp); 
	putint(bmpif->biWidth, fp); 
	putint(bmpfh->bfOffBits + 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 = skip; i <= pos; i++)
		putc(name[i], fp);

	fwrite(bmpfh, sizeof(BITMAPFILEHEADER), 1, fp);
	fwrite(bmpif, bmpfh->bfOffBits - sizeof(BITMAPFILEHEADER), 1, fp);
	if(d_buf){
		fwrite(d_buf, d_size, 1, fp);
		Free0(d_buf);
	}else
		fwrite(buff, d_size, 1, fp);
	fclose(fp);
	return TRUE;
}

DLLAccess int __stdcall CreatePicture ( char *filepath, unsigned int flag, 
    HANDLE *pHBInfo, HANDLE *pHBm, struct PictureInfo *lpInfo,
    int (CALLBACK *lpPrgressCallback)(int,int,long), long lData)
{
	BITMAPFILEHEADER bmp_h;
    BITMAPINFOHEADER *pbmih;
    char *bmp_image;
    int ret = XPI_ALL_RIGHT;
    int timg_size;

#if 0
    if (lpPrgressCallback != NULL)
        if (lpPrgressCallback(0, 1, lData)) //0%
            return XPI_ABORT;
#endif

	if(flag & 1){
    	pbmih = (BITMAPINFOHEADER *)pHBInfo;
    	bmp_image = (char *)pHBm;
	}else{
    	pbmih = (BITMAPINFOHEADER *)LocalLock(*pHBInfo);
    	bmp_image = (char *)LocalLock(*pHBm);
	}

    /*bmpヘッダ作成*/
    bmp_h.bfType      = 0x4d42; /*"BM"*/
    bmp_h.bfReserved1 = 0; /*must be 0*/
    bmp_h.bfReserved2 = 0; /*must be 0*/

    bmp_h.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
    timg_size = pbmih->biWidth;
    switch (lpInfo->colorDepth) {
        case 1:
            bmp_h.bfOffBits += sizeof(RGBQUAD)*
				(pbmih->biClrUsed?pbmih->biClrUsed:2);
            timg_size = (timg_size +7) >> 3;
            break;
        case 4:
            bmp_h.bfOffBits += sizeof(RGBQUAD)*
				(pbmih->biClrUsed?pbmih->biClrUsed:16);
            timg_size = (timg_size +1) >> 1;
            break;
        case 8:
            bmp_h.bfOffBits += sizeof(RGBQUAD)*
				(pbmih->biClrUsed?pbmih->biClrUsed:156);
            break;
        case 16:
            if (pbmih->biCompression == BI_BITFIELDS)
                bmp_h.bfOffBits += sizeof(DWORD)*3;
            timg_size = timg_size * 2;
            break;
        case 24:
            timg_size = timg_size * 3;
            break;
        case 32:
            if (pbmih->biCompression == BI_BITFIELDS)
                bmp_h.bfOffBits += sizeof(DWORD)*3;
            timg_size = timg_size * 4;
            break;
  
      default:
            return XPI_NOT_SUPPROT;
    }
	timg_size = (timg_size + 3) & ~3;
    bmp_h.bfSize = bmp_h.bfOffBits + ((pbmih->biSizeImage==0)?
                timg_size * abs(pbmih->biHeight)
                :pbmih->biSizeImage);

	if(bmp2bmc(filepath, &bmp_h, pbmih, bmp_image, timg_size) == FALSE)
		ret = XPI_NOT_SUPPROT;

#if 0
    if (lpPrgressCallback != NULL)
        if (lpPrgressCallback(1, 1, lData)) //100%
            return XPI_ABORT;
#endif

    if(!(flag & 1)){
		LocalUnlock(*pHBInfo);
	    LocalUnlock(*pHBm);
	}
	return ret;
}
