/*---------------------------------------------------------------------------
 * BMPSAVER xmodem で画面を保存
 * bsxmodem.c (2000/07/19 - 2000/12/13)
 * Copyright (c) 2000, Kazuhide Hatsuyama, All rights reserved.
 *   mail    : hat@lares.dti.ne.jp
 *   HomePage: http://www.lares.dti.ne.jp/~hat/
 *---------------------------------------------------------------------------*/

#define BIOS_INLINE

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/bios.h>
#include <sys/oswork.h>
#include <fcntl.h>

#include "bsbmp_d.h"
#include "bmpsaver.h"

#define SOH		0x01
#define EOT		0x04
#define ACK		0x06
#define NAK		0x15
#define CAN		0x18

#define FAILED(x)		{if((x)==KH_BS_ERROR) { goto error; }}

typedef
struct tagBMPSAVXMODEMINFO {
	int				posx, posy;
	int				width, height;
	int				bpp;
	int				byte_per_line;
	unsigned char	packed_color;

	BYTE			block_num;
	BYTE far *		buf_p;
	int				written_byte;
} BMPSAVXMODEMINFO;

static BMPSAVXMODEMINFO bsinfo;

static int bs_xmodem_putc();
static int bs_xmodem_send();
static int bs_xmodem_write(BYTE far *p, int n);

/*------------------------------------------------*/

int bs_open_xmodem(BMPFILEINFO far *bfi)
{
	static datetime_t date;
	fent_t far *fent;
	int i, c;
	unsigned long pre_tick;

	bsinfo.width = bfi->w;
	bsinfo.height = bfi->h;

	/* １ラインにつき何バイト？ 色数は？*/
	bsinfo.bpp = bfi->bpp;
	bsinfo.byte_per_line = bsinfo.width;
	if(bsinfo.bpp==4) bsinfo.byte_per_line = (bsinfo.byte_per_line+1)/2;

	/* 4バイト境界に合わせる */
	bsinfo.byte_per_line = (bsinfo.byte_per_line+3)&(~3);

	/*----- 送信 -------------------------------------------*/
	comm_open();
	pre_tick = sys_get_tick_count();
	while((c=comm_receive_char())!=NAK) {
		if(c==CAN || (sys_get_tick_count()-pre_tick)>=75*60) {
			goto error;
		}
	}
	bsinfo.block_num = 1;
	bsinfo.buf_p = xmodembuf_p;
	bsinfo.written_byte = 0;

	/* fx 形式ヘッダー */
	bsinfo.buf_p[0] = '#'; bsinfo.buf_p[1] = '!';
	bsinfo.buf_p[2] = 'w'; bsinfo.buf_p[3] = 's';
	for(i=4;i<64;i++) bsinfo.buf_p[i] = 0xff;
	fent = (fent_t far*)&bsinfo.buf_p[64];
	strncpy(fent->name, bfi->fn, MAXFNAME);
	strncpy(fent->info, bfi->fn, MAXFINFO);
	for(i=0;i<sizeof(floc_t);i++) ((BYTE far*)&fent->loc)[i] = 0;
	fent->len = bitMapFileHeader.bfSize;
	fent->count = (bitMapFileHeader.bfSize+127)/128;
	fent->mode = FMODE_W | FMODE_R;
	rtc_get_datetime_struct(&date);
	fent->mtime = datetime2time_t(&date);
	fent->handler.appid = 0;
	fent->resource = -1L;
	FAILED(bs_xmodem_send());

	/* ヘッダー書き込み */
	FAILED(bs_xmodem_write((BYTE far *)&bitMapFileHeader, sizeof(BITMAPFILEHEADER)));
	FAILED(bs_xmodem_write((BYTE far *)&bitMapInfoHeader, sizeof(BITMAPINFOHEADER)));
	FAILED(bs_xmodem_write((BYTE far *)&colortable, sizeof(RGBQUAD)*(1<<bsinfo.bpp)));

	bsinfo.posx = 0;
	bsinfo.posy = 0;

	return KH_BS_OK;
error:
	comm_close();
	return KH_BS_ERROR;
}

/*------------------------------------------------*/
/* １画素出力 */
int bs_putcolor_xmodem(unsigned char color)
{
	if(bsinfo.bpp==4) {
		/* グレースケール */
		if(bsinfo.posx&1) {
			bsinfo.packed_color += color;
			FAILED(bs_xmodem_putc(bsinfo.packed_color));
		} else {
			bsinfo.packed_color = color<<4;
		}
		bsinfo.posx++;
		/* 次の行？ */
		if(bsinfo.posx>=bsinfo.width) {
			int i, n;
			if(bsinfo.posx&1) FAILED(bs_xmodem_putc(bsinfo.packed_color));
			n = bsinfo.byte_per_line - (bsinfo.width+1)/2;
			for(i=0;i<n;i++) FAILED(bs_xmodem_putc(0));
			bsinfo.posx = 0;
			bsinfo.posy++;
		}
	} else {
		/* 256カラー */
		FAILED(bs_xmodem_putc(color));
		bsinfo.posx++;
		/* 次の行？ */
		if(bsinfo.posx>=bsinfo.width) {
			int i, n;
			n = bsinfo.byte_per_line - bsinfo.width;
			for(i=0;i<n;i++) FAILED(bs_xmodem_putc(0));
			bsinfo.posx = 0;
			bsinfo.posy++;
		}
	}
	return KH_BS_OK;
error:
	return KH_BS_ERROR;
}

/*------------------------------------------------*/
int bs_close_xmodem()
{
	int i, n;
	/* 途中で書き込みが中断している時の処理 */
	if(bsinfo.bpp==4) {
		/* グレースケール */
		if(bsinfo.posy<bsinfo.height) {
			if(bsinfo.posx<bsinfo.width) {
				if(bsinfo.posx&1) FAILED(bs_xmodem_putc(bsinfo.packed_color));
				n = bsinfo.byte_per_line - (bsinfo.posx+1)/2;
				for(i=0;i<n;i++) FAILED(bs_xmodem_putc(0));
				bsinfo.posy++;
			}
			n = (bsinfo.height-bsinfo.posy)*bsinfo.byte_per_line;
			for(i=0;i<n;i++) FAILED(bs_xmodem_putc(0));
		}
	} else {
		/* カラー */
		if(bsinfo.posy<bsinfo.height) {
			if(bsinfo.posx<bsinfo.width) {
				n = bsinfo.byte_per_line - bsinfo.posx;
				for(i=0;i<n;i++) FAILED(bs_xmodem_putc(0));
				bsinfo.posy++;
			}
			n = (bsinfo.height-bsinfo.posy)*bsinfo.byte_per_line;
			for(i=0;i<n;i++) FAILED(bs_xmodem_putc(0));
		}
	}

	/* 余りのバイトを 0x1a で埋める */
	for(i=bsinfo.written_byte;i<128;i++) {
		FAILED(bs_xmodem_putc(0x1a));
	}

	/* 終了コード送信 */
	comm_send_char(EOT);
	if(comm_receive_char()!=ACK) goto error;
	comm_close();
	return KH_BS_OK;
error:
	comm_close();
	return KH_BS_ERROR;
}

/*====================================================================*/
/*------------------------------------------------*/
/* １バイト出力 */
int bs_xmodem_putc(BYTE ch)
{
	bsinfo.buf_p[bsinfo.written_byte++] = ch;
	/* １ブロック分（１２８バイト）たまったら送信 */
	if(bsinfo.written_byte>=128)
	{
		FAILED(bs_xmodem_send());
		bsinfo.written_byte = 0;
	}
	return KH_BS_OK;
error:
	return KH_BS_ERROR;
}

/*------------------------------------------------*/
/* 複数バイト出力 */
int bs_xmodem_write(BYTE far *p, int n)
{
	int i;
	for(i=0;i<n;i++) {
		FAILED(bs_xmodem_putc(p[i]));
	}
	return KH_BS_OK;
error:
	return KH_BS_ERROR;
}

/*------------------------------------------------*/
/* １ブロック送信 */
int bs_xmodem_send()
{
	unsigned long pre_tick;
	BYTE csum = 0;
	int i, c;
	do {
		comm_send_char(SOH);
		comm_send_char(bsinfo.block_num);
		comm_send_char(~bsinfo.block_num);
		/* データ送信 */
		for(i=0;i<128;i++) {
			comm_send_char(bsinfo.buf_p[i]);
			csum = csum+bsinfo.buf_p[i];
		}
		/* チェックサム */
		comm_send_char(csum);
		pre_tick = sys_get_tick_count();
		while((c=comm_receive_char())!=ACK && c!=NAK) {
			if((sys_get_tick_count()-pre_tick)>=75*5) {
				goto error;
			}
		}
	} while(c!=ACK);
	bsinfo.block_num++;
	return KH_BS_OK;
error:
	return KH_BS_ERROR;
}
/*====================================================================*/
