/*
 *	Susie plug-in  for  BMC format file  used by dviout for Windows
 *  	   March 18, 1998 (Ver.0.1 written by SHIMA)
 *         June  30, 2001 (Ver.0.2)
 */

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

#define	CLLBK	1

#define	DLLAccess __declspec(dllexport)

char *SpiVerString     ="00IN";
char *SpiAboutString   ="BMC to DIB filter ver 0.3 (c)Toshio OSHIMA";
char *SpiFileExtString ="*.bmc";
char *SpiFileTypeString="BMC (Compressed BMP)";
char *zero			   ="";

#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;
}

DLLAccess int __stdcall
	 GetPluginInfo(int infono,LPSTR buf,int buflen)
{
  int ret=0;
  char *bufstr;

  switch(infono){
  case 0:
		bufstr = SpiVerString;
		break;
  case 1:
		bufstr = SpiAboutString;
		break;
  case 2:
		bufstr = SpiFileExtString;
		break;
  case 3:
		bufstr = SpiFileTypeString;
		break;
  default:
		bufstr = zero;
  }
  strcpy(buf,bufstr);
  return
	ret=strlen(bufstr);
}

static int GetInt(unsigned char *s)
{
	return
	 s[0] + (s[1]<<8) + (s[2]<<16) + (s[3]<<24);
}

DLLAccess int __stdcall IsSupported(LPSTR filename,DWORD dw)
{
  int i;
  int ret=0;
  unsigned char *data;
  DWORD ReadBytes=0;

  if ((dw & 0xFFFF0000) == 0){
	/* ファイルハンドル */
	data=(unsigned char *)malloc(0x100);
	data[0] = 0;
	ReadFile((HANDLE)dw,data,0x100,&ReadBytes,NULL);
  }else{
  	/* メモリ上 */
	data=(unsigned char *)dw;
  }
  /* Is BMC? */ 
  if(data[0] == 'B' && data[1] == 'C'){
    i = GetInt(data + 4);
    if(i < 200){
    	data += i;
    	if(data[0] == 'B' && data[1] == 'M')
    		ret = 1; 
    }
  }
  if ((dw & 0xFFFF0000) == 0) free(data);

  return ret;
}

DLLAccess int __stdcall GetPictureInfo
(LPSTR buf,long len,unsigned int flag,struct PictureInfo *lpInfo)
{
  FILE *fp;
  unsigned char *buffer;
  int ret=OTHER_ERROR;
//int h_size;

  if ((flag & 7)== 0){
	/*ファイル*/
	fp = fopen(buf,"rb");
	if (fp == NULL) return FILE_READ_ERROR;
	buffer=(unsigned char *)malloc(0x200);

	if (buffer!=NULL)
		fread(buffer,1,0x200,fp);
	fclose(fp);

	if (buffer==NULL) return NO_MEMORY;
  }else
  	buffer = (unsigned char *)buf;

  switch(buffer[28] & 0xf){
  	case 2:	lpInfo->colorDepth=1; break;
  	case 3: lpInfo->colorDepth=4; break;
  	case 4: lpInfo->colorDepth=8; break;
  	case 5: lpInfo->colorDepth=24; break;
  	case 6: lpInfo->colorDepth=16; break;
  	case 7: lpInfo->colorDepth=32; break;
  	default: 
  			if((flag & 7) == 0)
				free(buffer);
  			return OUT_OF_ORDER;
  }
  lpInfo->left=0;
  lpInfo->top=0;
  lpInfo->hInfo=NULL;
  lpInfo->width=GetInt(buffer + 16);
  lpInfo->height=GetInt(buffer + 12);
/*
  if((h_size = GetInt(buffer + 4)) < 0x180){
	lpInfo->x_density = GetInt(buffer + h_size + 24);
 	lpInfo->y_density = GetInt(buffer + h_size + 28);
  }else
*/
  {
	lpInfo->x_density = lpInfo->y_density = 1;
  }
  if((flag & 7) == 0)
	free(buffer);
  return ALL_RIGHT;
}

unsigned char *bmc_buf, *bmc_pt;

static int Getc(FILE *fp)
{
	if(fp)
		return getc(fp);
	return *bmc_pt++;
}

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

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

static void my_decomp(unsigned char *s_buf, unsigned char *d_buf, 
	int width, int height, int f_comp
#ifdef	CLLBK
	,int (CALLBACK *lpProgressCallback)(int,int,long), long lData
#endif
	)
{
	#define	MAX_STEP 32
	int size, i, w, h, count, max_size, step, l_size;
	int ch;
	size = step = l_size = 0;

	max_size = width*height; 
#ifdef	CLLBK
	for(;;){
		while(l_size <= size){
			if(lpProgressCallback != NULL)
				lpProgressCallback(step, MAX_STEP, lData);
			l_size = (++step)*max_size/MAX_STEP;
			if(size >= max_size)
				goto end;
		}
#else
	while(size < max_size){
#endif
		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;
				}
			}
		}
		count++;
		while(count-- && size < max_size)
		d_buf[size++] = ch;
	}
end:
	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];
	}
}

static int bmc2bmp(FILE *fp, HLOCAL *pHBInfo, HLOCAL *pHBm
#ifdef	CLLBK
	,int (CALLBACK *lpProgressCallback)(int,int,long), long lData
#endif
	)
{
	int key, begin, width, height, size, flag;
	int bmp_top;
	unsigned char *s_buf;

	*pHBInfo = *pHBm = NULL;
	if(Getc(fp) != 'B' || Getc(fp) != 'C'){
		fclose(fp);
		return 1;
	}
	flag = Getc(fp);
	key = Getc(fp);
	begin = ReadInt(fp) + sizeof(BITMAPFILEHEADER);
	bmp_top = ReadInt(fp) - sizeof(BITMAPFILEHEADER);
	width = ReadInt(fp);
	height = ReadInt(fp);
	ReadInt(fp);			// bit_width
	size = ReadInt(fp) - sizeof(BITMAPFILEHEADER);
	if(fp){
		fseek(fp, begin, SEEK_SET);
		s_buf = (unsigned char*)LocalAlloc(LMEM_FIXED, size);
		fread(s_buf, size, 1, fp);
		fclose(fp);
	}else
		s_buf = bmc_buf + begin;
    *pHBInfo = LocalAlloc(LMEM_FIXED, bmp_top);
	memcpy(*pHBInfo, s_buf, bmp_top);
    *pHBm = LocalAlloc(LMEM_FIXED, width*height);
	if(flag & 1){
#ifdef	CLLBK
		lpProgressCallback(0,1,lData);
#endif
		memcpy(*pHBm, s_buf + bmp_top, width*height);
#ifdef	CLLBK
		lpProgressCallback(1,1,lData);
#endif
	}else
		my_decomp(s_buf + bmp_top, (unsigned char *)*pHBm, 
			width, height, key
#ifdef	CLLBK
			,lpProgressCallback, lData
#endif
			);
	if(fp){
		LocalFree(s_buf);
		fclose(fp);
	}
	return 0;
}

/*
 HLOCAL *pHBInfo,HLOCAL *pHBm,
 はポインタのポインタ
*/
DLLAccess int __stdcall GetPicture
(LPSTR buf,long len,unsigned int flag,
 HLOCAL *pHBInfo,HLOCAL *pHBm
 ,int (CALLBACK *	lpProgressCallback)(int,int,long),long lData)
{
  FILE *fp = NULL;

  if ((flag & 7)== 0){
	/* ファイルから読み出し */
	if((fp = fopen(buf,"rb")) == NULL)
		return FILE_READ_ERROR;
  }else
  	bmc_buf = bmc_pt = (unsigned char*)buf;

  switch(bmc2bmp(fp, pHBInfo, pHBm
#ifdef	CLLBK
	, lpProgressCallback, lData
#endif
  	)){
  	case 2:
	  	LocalFree(*pHBm);
  		LocalFree(*pHBInfo);
  	case 1:
  		return ABORT;
  	default:
  		break;
  }
  if(*pHBm!=NULL)
  	return ALL_RIGHT;
  if(*pHBInfo)
  	LocalFree(*pHBInfo);
  return OUT_OF_ORDER;
}

DLLAccess int __stdcall GetPreview
(LPSTR buf,long len,unsigned int flag,
 HANDLE *pHBInfo,HANDLE *pHBm,
 FARPROC lpPrgressCallback,long lData)
{
  return NO_FUNCTION;
}
