//----------------------------------------------------------------------------------------------
//
//  gConsole+picloader.h : picLoader/cutLoader
//  X-BASIC'
//
//  Created by AIG-Soft(iMac) on 2013/01/23.
//  Copyright (c) 2013年 AIG-Soft. All rights reserved.
//

#import "gConsole+picloader.h"
#import "Console+animation.h"
#import "rgbhsv.h"
#import "ImageHelper.h"
#import "jpeg.h"
#import "malloc2.h"

@implementation gConsole (picloader)

//---------------------------------------------------------------------------------------
// picLoader関数
//---------------------------------------------------------------------------------------

-(void)displayImageWithExpandAndBitmapClose:(CGFloat)sx sy:(CGFloat)sy fbitmapOpen:(BOOL)fbitmapOpen fNoBitmapClose:(BOOL)fNoBitmapClose
{
	nslog(@"displayImageWithExpandAndBitmapClose:bx,by=%d,%d",self.bitmapWidth,self.bitmapHeight);
	// transformがかかっているときは、self.frame.size自体が変形されているので、オリジナルのサイズであるframe0を基準にする
	nslog(@"frame.size=%f,%f",self.frame.size.width,self.frame.size.height);
	nslog(@"frame0.size=%f,%f",frame0.size.width,frame0.size.height);
	
	CGFloat wx=CGSizeZero.width;	// 等倍の印
	CGFloat wy=CGSizeZero.height;	// 同上
	
	if (self.bitmapWidth >frame0.size.width
	||  self.bitmapHeight>frame0.size.height) {
		// 画像が画面内に入りきら無い時は縮小して入れ込む
		// 縦横の差分を計算する
		wx=frame0.size.width;
		wy=frame0.size.height;
		int dwx=self.bitmapWidth -frame0.size.width;
		int dwy=self.bitmapHeight-frame0.size.height;
		// はみ出る部分が大きいの縮尺に合わせる
		if (dwx>dwy) {
			wy=self.bitmapHeight*(frame0.size.width /self.bitmapWidth );
		} else {
			wx=self.bitmapWidth *(frame0.size.height/self.bitmapHeight);
		}
		nslog(@"縮小表示:wx,wy=%f,%f",wx,wy);
	} else { // 入りきるときは等倍
		nslog(@"等倍表示");
	}
	[self bitmapImgLoad:sx sy:sy wx:wx wy:wy ftile:NO];
		
	if (fbitmapOpen) {
		// 展開途中でエラーが出ても、出来たところまで画像にする
		if (!fNoBitmapClose) [self bitmapClose];
	}
}

//---------------------------------------------------------------------------------------
/* Oh!X 1990/2 p79-80
	PICファイルローダー C言語版
 　　1989-9 ver 0.01 by Akira-Y.
 を移植
 */

#define BUFF_SZ	1024		// iOSは速いので、あまり大きくする必要はない
static FILE  *fp;
static char *buff_p;		// buff's read pointer
static size_t buff_len;		// buff rest
static int  bit_len;		// *buff_p's effective bit length
static int  x_wid,y_wid; 	// pic size
static int	fpicerr;		// エラーコード;PIC_*

static
struct  {
	int  color;
	int  next;
	int  prev;
} table[128];			/* for use color cash table */
static int  color_p;	/* index of color table */

//------------------------------------------------------------------

// buff pointer inc. and read next buff
static void buff_next(void)
{
	static char buff0[BUFF_SZ];	// file read buffer

	fpicerr=PIC_OK;
	if (buff_len == 0) {
//		nslog(@"file read");
		if ((buff_len = fread( buff0, sizeof(char), BUFF_SZ ,fp)) == 0) {
			nslog(@"file read error");
			fpicerr=PIC_READERR;
			return;
		}
//		nslog(@"buff_len=%d",buff_len);
		buff_p = buff0;
	} else {
		buff_p++;
	}
	buff_len--;
	bit_len = 8;
}

// size bits load from file
static SLong bit_load(int size)
{
//	nslog(@"bit_load:size=%d",size);
	int	i;
	SLong  l = 0;
	while (size > bit_len) {
		for (i = 0; i < bit_len; i++) {
			l = l + l;
			if (*buff_p & 0x80) l++;
			*buff_p = *buff_p + *buff_p;
			size--;
		}
		buff_next();
		if (fpicerr!=PIC_OK) return (0); // 強制的に帰らせる
	}
	for (i = 0; i < size; i++) {
		l = l + l;
		if (*buff_p & 0x80) l++;
		*buff_p = *buff_p + *buff_p;
		bit_len--;
	}
	return (l);
}

// read next color diff. point from file
// Wyle符号
static SLong read_len(void)
{
	// 0が出てくるまでのビット数を数える
	int  l = 1;
	while (bit_load(1) != 0) {
		l++;
	}
	return (bit_load(l) + (1 << l) - 1);
}

//------------------------------------------------------------------

static int new_color(int c)
{
	color_p = table[color_p].prev;
	table[color_p].color = c;
	return (c<<1); // 輝度分(bit0)を嵩上げする
}

static int get_color(int idx)
{
	if (color_p != idx) {
		// idx take off from table
		table[table[idx].prev].next = table[idx].next;
		table[table[idx].next].prev = table[idx].prev;
		// idx set to new table point
		table[table[color_p].prev].next = idx;
		table[idx    ].prev = table[color_p].prev;
		table[color_p].prev = idx;
		table[idx    ].next = color_p;
		color_p = idx;
	}
	return (table[idx].color * 2);
}

static int read_color(void)
// read color code from file
{
	if (bit_load(1) == 0) {
		return (new_color((int)bit_load(15))); // X68は輝度1bit抜きで15bit色
	}
	return (	get_color((int)bit_load( 7))); // キャッシュ上にある色の場合は、その番号(128色)
}

static void color_cash_init(void)
{
	int  i;
	for (i = 0; i < 128; i++) {
		table[i].color = 0;
		table[i].prev = i + 1;
		table[i].next = i - 1;
	}
	table[127].prev = 0;
	table[0  ].next = 127;
	color_p = 0;
}

//------------------------------------------------------------------

#define THUNDER	100	// 稲妻のアルファ値(境界線)

-(void)pic_expand_chain:(int)x y:(int)y r:(int)r g:(int)g b:(int)b
// 境界線展開
{
	BOOL  y_over = YES;
	for ( ;; ) {
		switch (bit_load(2)) { // 連鎖符号の読み取り
			case 0: // %00 : それ以外
				if (bit_load(1) == 0) {	// 連鎖終了
					return;
				}
				// bit=1 : 連鎖継続
				if (bit_load(1) == 0) {
					x -= 2; // 2つ左
				} else {
					x += 2; // 2つ右
				}
				break;
			case 1: // %01 : 1つ左
				x--;
				break;
			case 2: // %10 : 真下
				// x+=0
				break;
			case 3: // %11 : 1つ左
				x++;
				break;
		}
		if (x   >= x_wid) break;
		if (++y >= y_wid) y_over = NO;
		if (y_over) {
			[self bitmapPset:x y:y r:r g:g b:b alpha:THUNDER]; // 境界線はalpha<255で描画しておく
		}
	} // for
}

-(void)pic_expand
// 画像展開
{
	int	x=-1,y=0; // xは最初の++で0になる
	int r=0,g=0,b=0; // X68のPICにはalphaはないので不要
	for ( ;; ) {
		SLong  l = read_len();
		if (fpicerr!=PIC_OK) {
			nslog(@"エラーで停止:fpicerr=%d",fpicerr);
			return;
		}
//		nslog(@"len=%ld/x=%d",l,x);
		while (--l) {
			if (++x == x_wid) {
				if (++y == y_wid) return; /* (^_^;) */
				x = 0;
//				nslog(@"(1)y=%d",y);
			}
			int r0,g0,b0,a0;
			[self bitmapPoint:x y:y r:&r0 g:&g0 b:&b0 alpha:&a0];
			if (a0==THUNDER) {
				// 境界線が先に引かれている時
				// その色を持って展開
				r=r0;
				g=g0;
				b=b0;
//				nslog(@"境界線=%d/%d/%d",r,g,b);
			}
			[self bitmapPset:x y:y r:r g:g b:b alpha:255];
		} // while
//		nslog(@"len分終了");
		if (++x == x_wid) {
			if (++y == y_wid) return; /* (^_^;) */
			x = 0;
//			nslog(@"(2)y=%d",y);
		}
		int c=read_color();
		//nslog(@"c=$%04x",c);
		x68Color2iOSColor(c /*read_color()*/,&r,&g,&b,NULL);
		if (fpicerr!=PIC_OK) {
			nslog(@"エラーで停止:fpicerr=%d",fpicerr);
			return;
		}
		[self bitmapPset:x y:y r:r g:g b:b alpha:255];
		if (bit_load(1) != 0) { // 連鎖有り
			// 境界線展開
			// nslog(@"rgb=%d/%d/%d",r,g,b);
			[self pic_expand_chain:x y:y r:r g:g b:b];
		}
	} // for
}

//------------------------------------------------------------------

-(int)getPicHeader:(NSString *)fname fsquare:(BOOL *)fsquare mode:(int *)mode bitLength:(int *)bitLength wx:(int *)wx wy:(int *)wy comment:(UByte *)comment maxCommentLength:(int)maxCommentLength
// PICのヘッダー情報を取得する(V2.2)
// ヘッダーの内容については仕様書参照のこと
{
	printSelfs(@"fname=%@",fname);

	NSString *path=getResourceOrSavedataPath(fname);
	if (path==nil) {
		nslog(@"file not found(getResourceOrSavedataPath):%@",path);
		return (PIC_FILENOTFOUND); // file can not open
	}
	if ((fp = fopen(nsStringTocString(path), "r" ))==NULL) {
		nslog(@"file not found:%@",path);
		return (PIC_FILENOTFOUND); // file can not open
	}
	
	// 初期化 : ここで暮らす内部の静的領域へのアクセスが有るためクラス化してある
	bit_len = buff_len = 0;	// file pointer
	color_cash_init();		// color table
	
	// ヘッダーチェック
	if ((int)bit_load(8) != 'P') {fpicerr=PIC_HEADERERR; goto PicHeaderEnd;}
	if ((int)bit_load(8) != 'I') {fpicerr=PIC_HEADERERR; goto PicHeaderEnd;}
	if ((int)bit_load(8) != 'C') {fpicerr=PIC_HEADERERR; goto PicHeaderEnd;}
	
	// コメント領域を読みだす。
	// ただし、maxCommentLength-1を超えるときは打ち切る。
	// comment[maxCommentLength]必要(EOSを含む)
#define MAX_COMMENT	256
	UByte comment0[MAX_COMMENT]; //
	if (comment==NULL || maxCommentLength==0) {
		// 外部から指定がないときは内部バッファーを仮に使う
		comment=comment0;
		maxCommentLength=MAX_COMMENT;
	}
	int i=0;
	SLong c;
	do {
		c = bit_load(8);
		if (c==0x1a) c='/'; // 0x1aのままでは表示できないので置き換える
		comment[i]=c;
		if (i==maxCommentLength-1) {
			comment[i]=0; // EOS
			while (c!=0) {
				// 残りEOSまで飛ばす
				c=bit_load(8);
			}
			break;
		}
		i++;
	} while (c!=0);
	nslog(@"comment=%s",comment);
	// 正方フラグ
	// PIC/MM/があると拡張ヘッダーを意味する
	if (fsquare!=NULL) {
		*fsquare=(memcmp(comment,"/MM/XSS",7)==0);
	}
	nslog(@"pic fsquare=%d",*fsquare);

	
	// Oh!Xではなく仕様書に従って解析を変更(V2.2)
	// 予約1バイト
	if (bit_load(8) != 0) {fpicerr=PIC_HEADERERR; goto PicHeaderEnd;}
	
	// pic mode
	if (mode!=NULL) {
		*mode=(int)bit_load(8);
		nslog(@"pic mode=%d",*mode);
	}
	// pic color bit length
	if (bitLength!=NULL) {
		*bitLength=(int)bit_load(16);
		nslog(@"pic bitLength=%d",*bitLength);
	}
	// 画像サイズ
	// wx,wyは省略しちゃダメ
	*wx = (int)bit_load(16);
	*wy = (int)bit_load(16);
	nslog(@"pic size=%d*%d",*wx,*wy);
	//
PicHeaderEnd:
	fclose(fp);
	nslog(@"ret=%d",fpicerr);
	return (fpicerr);
}

//------------------------------------------------------------------

-(int)picLoader:(NSString *)fname sx:(CGFloat)sx sy:(CGFloat)sy fbitmapOpen:(BOOL)fbitmapOpen fdirect:(BOOL)fdirect_ fNoBitmapClose:(BOOL)fNoBitmapClose
// fbitmapOpen : 内部でbitmapを確保し、画面に表示するところまでを処理する
//				 このときはsx,syは無視される
// fNoBitmapClose : fbitmapOpen=YESの時のみ有効 : YES=bitmapCloseを実行しない , NO=実行する
{
	printSelfs(@"fname=%@",fname);
	NSString *path=getResourceOrSavedataPath(fname);
	if (path==nil) {
		nslog(@"file not found(getResourceOrSavedataPath):%@",path);
		return (PIC_FILENOTFOUND); // file can not open
	}
	if ((fp = fopen(nsStringTocString(path), "r" ))==NULL) {
		nslog(@"file not found:%@",path);
		return (PIC_FILENOTFOUND); // file can not open
	}

	// 初期化
	bit_len = buff_len = 0;	// file pointer
	color_cash_init();		// color table
	
	// ヘッダーチェック
	if ((int)bit_load(8) != 'P') {fpicerr=PIC_HEADERERR; goto pic_end;}
	if ((int)bit_load(8) != 'I') {fpicerr=PIC_HEADERERR; goto pic_end;}
	if ((int)bit_load(8) != 'C') {fpicerr=PIC_HEADERERR; goto pic_end;}
	
	int  c;
	while ((c = (int)bit_load(8)) != 0x1a) { // text eof check
		// オリジナルでは表示していた
		//putchar(c);
	}
	while ((int)bit_load(8) != 0); // EOSまで飛ばす

	// Oh!Xではなく仕様書に従って解析を変更(V2.2)
	// 予約1バイト
	if (bit_load(8) != 0) {fpicerr=PIC_HEADERERR; goto pic_end;}

	// pic mode check
	if ((int)bit_load(8) !=  0) {fpicerr=PIC_NOTSUPPORT_MODE;  goto pic_end;}	// 現在はmode0のみ対応
	
	// pic color bit length check
	if ((int)bit_load(16) != 15) {fpicerr=PIC_NOTSUPPORT_COLOR; goto pic_end;}	// 現在は15bit colorのみ対応(16bit-1bit:輝度)

	// ビットマップに展開するのでサイズは自由
	x_wid = (int)bit_load(16);
	y_wid = (int)bit_load(16);
	if (fpicerr!=PIC_OK) goto pic_end;
	nslog(@"pic size=%d*%d",x_wid,y_wid);
	
	// 実展開処理
	if (sx!=0 || sy!=0) {
		nslog(@"fdirect解除:sx=%f,sy=%f",sx,sy);
		fdirect_=NO;
	} // fdirect_=YESはsx==0&&sy==0の時のみ有効
	if (fbitmapOpen) {
		if (![self bitmapOpen:x_wid height:y_wid fdirect:fdirect_]) {
			nslog(@"picLoader:bitmapOpen error");
			fpicerr=PIC_BITMAP_ALREADY_USE;
			goto pic_end;
		}
	}
	[self pic_expand];	// 展開 main
	[self displayImageWithExpandAndBitmapClose:sx sy:sy fbitmapOpen:fbitmapOpen fNoBitmapClose:fNoBitmapClose];
pic_end:;
	fclose(fp);
	return (fpicerr);
}

//---------------------------------------------------------------------------------------
// カットファイル表示
//---------------------------------------------------------------------------------------

-(int)gput8:(UByte)c x:(int)x y:(int)y wx:(int)wx
// 白黒8ドット書き込み
{
	int b=BIT7,i;
	for (i=0;i<8;i++) {
		int col=(c&b? 255:0);
		[self bitmapPset:x++ y:y r:col g:col b:col alpha:col];
		if ((--wx)==0) break;
		b>>=1;
	}
	return(wx);
}

/*****************************************************************************/

// カットファイル１行処理
-(void)cut1disp:(UByte *)buff y:(int)y wx:(int)wx wa:(int)wa bbuff:(UByte *)bbuff
{
	int i,j,b;
	UByte c,cc,*data;
	int x=0;
	b=*buff++; /* this line length */
	if (b==0x01) { // １つ上の行と同じ
		i=wx;
		for (j=0;j<i;j+=8) {
			wx=[self gput8:*bbuff++ x:x y:y wx:wx];
			if (wx<=0) break;
			x+=8; // gput8()で8ドット書き込むから
		}
	} else {
		// それ以外の行
		data=buff+wa;	/* データ先頭アドレス */
		for (j=0;j<wa;j++) { /* 圧縮フラグ分 */
			c=*buff++; /* 圧縮フラグ読み出し */
			b=BIT7;
			for (i=0;i<8;i++) { /* 圧縮８ビット展開 */
				if (c&b) { /* 圧縮フラグbit=1 : データ有り */
					cc=(*data++)^(*bbuff); /* １行上とXOR */
					wx=[self gput8:cc x:x y:y wx:wx];
					*bbuff++=cc; /* 今の行 -> １つ上の行 */
				} else { /* bit=0 : データ＝0x00 */
					/* この行のデータは0x00なので、
					 この行のデータ＝１つ上の行のデータ */
					wx=[self gput8:*bbuff++ x:x y:y wx:wx];
				}
				if (wx<=0) break;
				x+=8; // gput8()で8ドット書き込むから
				b>>=1;
			}
		}
	}
}

/*****************************************************************************/

-(int)cutLoader:(NSString *)fname sx:(CGFloat)sx sy:(CGFloat)sy fbitmapOpen:(BOOL)fbitmapOpen fdirect:(BOOL)fdirect_ fNoBitmapClose:(BOOL)fNoBitmapClose
// fbitmapOpen : 内部でbitmapを確保し、画面に表示するところまでを処理する
//				 このときはsx,syは無視される
// fNoBitmapClose : fbitmapOpen=YESの時のみ有効 : YES=bitmapCloseを実行しない , NO=実行する
{
	printSelfs(@"fname=%@",fname);
	NSString *path=getResourceOrSavedataPath(fname);
	if (path==nil) {
		nslog(@"file not found(getResourceOrSavedataPath):%@",path);
		return (PIC_FILENOTFOUND); // file can not open
	}
	if ((fp = fopen(nsStringTocString(path), "r" ))==NULL) {
		nslog(@"file not found:%@",path);
		return (PIC_FILENOTFOUND); // file can not open
	}

	int ret;
	int wx,wa,wy;
	UByte head[48+4];

	// ヘッダー文字列＋画像幅データ読み込み
	if (fread(head,48+4,1,fp)==0)  {ret=PIC_READERR;   goto cut_end;}
	if (memcmp(head,"CUT_V1.0",8)) {ret=PIC_HEADERERR; goto cut_end;} // CUTでない
	
	// 画像幅
	wx=(head[48]<<8)+head[49];	// 68形式で格納されている
	wa=((wx+7)/8+7)/8;			// 圧縮フラグバイト数（全行で同じ）
	wy=(head[50]<<8)+head[51];	// 68形式で格納されている
	
	if (sx!=0 || sy!=0) {
		nslog(@"fdirect解除:sx=%f,sy=%f",sx,sy);
		fdirect_=NO;
	} // fdirect_=YESはsx==0&&sy==0の時のみ有効
	if (fbitmapOpen) {
		if (![self bitmapOpen:wx height:wy fdirect:fdirect_]) {
			nslog(@"cutLoader:bitmapOpen error");
			ret=PIC_BITMAP_ALREADY_USE;
			goto cut_end;
		}
	}
	int i,y;
	UByte bbuff[256];
	UByte line[256]; // １行のデータは256バイト以下

	// 最初の行の１つ前の行＝全部0のライン
	for (i=0;i<(wx+7)/8;i++) bbuff[i]=0;

	ret=PIC_OK;
	for (y=0;y<wy;y++) {
		// １行分のデータ読み込み
		if ((i=fgetc(fp))==-1) {
			ret=PIC_READERR;
			break;
		}
		line[0]=i;
		if (i!=0x01) {
			if (fread(&line[1],i-1,1,fp)==0) {
				ret=PIC_READERR;
				break;
			}
		} // 0x01の時は、1つ上の行と同じ
		[self cut1disp:line y:y wx:wx wa:wa bbuff:bbuff];
	}
	// 展開途中でエラーが出ても、出来たところまで画像にする
	[self displayImageWithExpandAndBitmapClose:sx sy:sy fbitmapOpen:fbitmapOpen fNoBitmapClose:fNoBitmapClose];
cut_end:;
	fclose(fp);
	return (ret);
}

//---------------------------------------------------------------------------------------

int getCutHeader(NSString *fname,int *wx,int *wy)
{
	nslog(@"getCutHeader:fname=%@",fname);
	NSString *path=getResourceOrSavedataPath(fname);
	if (path==nil) {
		nslog(@"file not found(getResourceOrSavedataPath):%@",path);
		return (PIC_FILENOTFOUND); // file can not open
	}
	if ((fp = fopen(nsStringTocString(path), "r" ))==NULL) {
		nslog(@"file not found:%@",path);
		return (PIC_FILENOTFOUND); // file can not open
	}
	
	int ret=PIC_OK;
	UByte head[48+4];
	
	// ヘッダー文字列＋画像幅データ読み込み
	if (fread(head,48+4,1,fp)==0)  {ret=PIC_READERR;   goto cutHeaderend;}
	if (memcmp(head,"CUT_V1.0",8)) {ret=PIC_HEADERERR; goto cutHeaderend;} // CUTでない
	
	// 画像幅
	*wx=(head[48]<<8)+head[49];	// 68形式で格納されている
	*wy=(head[50]<<8)+head[51];	// 68形式で格納されている
	
cutHeaderend:;
	fclose(fp);
	return (ret);
}

//---------------------------------------------------------------------------------------
// MAG表示
//---------------------------------------------------------------------------------------
// 仕様は
//
//               ####  まぐろーだー仕様書・非売品  ####
//             - MAKIchan Graphic loader is not 鮪だ！ -
//                  By Woody RINN (MAKIchanNET #03)
// より。
// この仕様書の言う所のピクセルと、iOSにおけるピクセルは全く意味が違うので注意
// このプログラム中では、前者のことは単に４ドットと呼ぶ。
// 正しいMAG saverによって出力されたデータしか対象にしていない

//#define	DBG_FILEOUT		// デバッグ中;中間ファイルを書き出す
//-------------------------------------------------------------------------------
/* 全体の順
 チェックデータ＋コメント
 ヘッダ(32ﾊﾞｲﾄ)
 ↑readMAGHeader()
 パレット
 ↑readMAGPalette()
 フラグＡ
 フラグＢ
 ↑readAndExtendMAGFlags()
 ピクセルデータ
 ↑readAndExtendMAGPixels()
 */

#define	MAG_CHECK		"MAKI02  "	// 後ろ半角スペース2つ
#define	SZ_MAG_CHECK	(sizeof(MAG_CHECK)-1)	// EOSを含まないバイト数

/*
 0       b       ヘッダの先頭
 1       b       機種コード
 2       b       機種依存フラグ
 3       b       スクリーンモード
 4       w       表示開始位置Ｘ
 6       w       表示開始位置Ｙ
 8       w       表示終了位置Ｘ
 10      w       表示終了位置Ｙ
 12      l       フラグＡのオフセット
 16      l       フラグＢのオフセット
 20      l       フラグＢのサイズ
 24      l       ピクセルのオフセット
 28      l       ピクセルのサイズ
 */
#pragma pack(push,1) // 境界整合させない
typedef struct { // MAGヘッダー情報
	// ↓以下はファイル内にある情報
	UByte	top;				// 先頭
	UByte	machine;			// 機種コード
	UByte	fMachineDependent;	// 機種依存フラグ
	UByte	scrnMode;			// スクリーンモード
	UWord	topX;				// 表示開始位置x
	UWord	topY;				// 表示開始位置y
	UWord	endX;				// 表示終了位置x
	UWord	endY;				// 表示終了位置y
	ULong	offsetFlagA;		// フラグAのオフセット
	ULong	offsetFlagB;		// フラグBのオフセット
	ULong	sizeFlagB;			// フラグBのサイズ
	ULong	offsetPixel;		// ピクセルのオフセット
	ULong	sizePixel;			// ピクセルのサイズ
	// ↑以上はファイル内にある情報
	// 以下よく使う計算値を入れる場所(故に、ファイル読み込み時には対象外にすること)
	UWord	wx;					// 横ドット数
	UWord	wy;					// 縦ライン数
	ULong	sizeFlagA;			// フラグAのサイズ
} MAG_HEADER;
#pragma pack(pop)
// UWord/ULongデータは86形式(ARMも同じなので手抜き)
#define	SZ_MAG_HEADER	(sizeof(MAG_HEADER)-sizeof(UWord)*2-sizeof(ULong)*1)

// scrnModeのビット対応
#define	BIT_256COLOR		0x80
#define	BIT_VIRTICAL_DOUBLE	0x01
#define	BIT_8COLOR			0x02
#define	BIT_DIGITAL			0x04

#define	BYTE1DOT	4	// １ドットのバイト数;４バイトで固定記述している部分もあるので、これだけ変えてもだめ

//-------------------------------------------------------------------------------

int readMAGHeader(FILE *fp,UByte *machine,UByte *username,UByte *comment,int maxlen,MAG_HEADER *magheader)
// ヘッダ情報を読む
// machine,usernameはEOSを入れるためLEN_*+1バイト必要/格納不要ならNULL
// commentは[maxlen]必要
{
	char buff[32]; // 大きさは念のため
	if (fread(buff,1,SZ_MAG_CHECK,fp)<SZ_MAG_CHECK) {
		return (PIC_READERR);
	}
	// 識別子(8バイト)
	if (memcmp(buff,MAG_CHECK,SZ_MAG_CHECK)!=0) {
		if (memcmp(buff,"MAKI01",6)==0) {
			// MAKIフォーマットには対応しない
			printf("MAKIフォーマットには対応しない\n"); // 資料がない
			return(PIC_NOTSUPPORT_MODE);
		}
		printf("先頭に " MAG_CHECK " がない\n");
		return(PIC_HEADERERR);
	}
	// 機種名(4バイト)
	if (fread(buff,1,MAG_LEN_MACHINE,fp)<MAG_LEN_MACHINE) {
		return (PIC_READERR);
	}
	if (machine!=NULL) {
		memcpy((char *)machine,(char *)buff,MAG_LEN_MACHINE);
		machine[MAG_LEN_MACHINE]=0; // EOS
		nslog(@"機種:%s",machine); // SJISなので化けるかも
	}
	// ユーザー名(18バイト)
	if (fread(buff,1,MAG_LEN_USERNAME,fp)<MAG_LEN_USERNAME) {
		return (PIC_READERR);
	}
	if (username!=NULL) {
		memcpy((char *)username,(char *)buff,MAG_LEN_USERNAME);
		username[MAG_LEN_USERNAME]=0; // EOS
		nslog(@"ユーザー名:%s",username); // SJISなので化けるかも
	}
	
	// 以上、8+4+18=32バイト
	
	// コメントを読み出す
	// 0x1aまで
	int c;
	if (comment==NULL || maxlen==0) {
		// コメント読み出しなし
		// スキップだけ行う
		do {
			c=fgetc(fp);
			if (c<0) return (PIC_READERR);
		} while (c!=0x1a);
	} else {
		// コメント読み出しなし
		int i=0;
		do {
			c=fgetc(fp);
			if (c<0) return (PIC_READERR);
			comment[i]=(UByte)c;
			if (i++==maxlen-1) {
				// あふれる
				// iが１つ進んでいるが後で-1するからこれでよい
				// SHIFT-JISの２バイト目かどうかはチェックしないので、必要なら外部で行うこと
				// 残り0x1aまで読み飛ばしておく
				while (c!=0x1a) {
					c=fgetc(fp);
					if (c<0) return (PIC_READERR);
				}
				break;
			}
		} while (c!=0x1a);
		i--; // 0x1aを消すため
		comment[i]=0; // EOS
	}
	nslog(@"comment=%s",comment); // SJISなので化けるかも
	
	// ヘッダ情報を読み出す
	long offset=ftell(fp); // 以後の読み出しは全てここが基準となる
	if (fread(magheader,1,SZ_MAG_HEADER,fp)<SZ_MAG_HEADER) {
		return (PIC_READERR);
	}
#ifdef DEBUG
	printf("ヘッダーオフセット:%ld\n",offset);
	printf("ヘッダーサイズ:%d/$%x\n",(int)SZ_MAG_HEADER,(int)SZ_MAG_HEADER);
	//
	printf("機種コード:$%02x\n"		,magheader->machine);
	printf("機種依存フラグ:$%02x\n"	,magheader->fMachineDependent);
	printf("スクリーンモード:$%02x"	,magheader->scrnMode);
	BOOL f256=NO;
	if (magheader->scrnMode&BIT_256COLOR		) {printf("/256色"); f256=YES;}
	if (magheader->scrnMode&BIT_VIRTICAL_DOUBLE	) printf("/要縦倍"); 		else printf("/標準");
	if (magheader->scrnMode&BIT_8COLOR			) printf("/8色");		else printf(f256? "":"/16色");
	if (magheader->scrnMode&BIT_DIGITAL			) printf("/デジタル");	else printf("/アナログ");
	printf("\n");
#endif
	// 現在対応するのは256色か16色のアナログの標準のみ
	magheader->wx=(magheader->endX-magheader->topX+1);
	magheader->wy=(magheader->endY-magheader->topY+1);
	magheader->sizeFlagA=magheader->offsetFlagB-magheader->offsetFlagA;
	// ファイル先頭からのオフセットに変更しておく
	magheader->offsetFlagA+=offset;
	magheader->offsetFlagB+=offset;
	magheader->offsetPixel+=offset;
	printf("表示開始位置:%d～%d:%dドット\n"	,magheader->topX,magheader->endX,magheader->wx);
	printf("表示開始位置:%d～%d:%dライン\n"	,magheader->topY,magheader->endY,magheader->wy);
	printf("フラグAへのオフセット :%d/$%x\n",(int)magheader->offsetFlagA	,(int)magheader->offsetFlagA	);
	printf("フラグAのサイズ      :%d/$%x\n",(int)magheader->sizeFlagA		,(int)magheader->sizeFlagA		);
	printf("フラグBへのオフセット :%d/$%x\n",(int)magheader->offsetFlagB	,(int)magheader->offsetFlagB	);
	printf("フラグBのサイズ      :%d/$%x\n",(int)magheader->sizeFlagB		,(int)magheader->sizeFlagB		);
	printf("ピクセルへのオフセット:%d/$%x\n",(int)magheader->offsetPixel	,(int)magheader->offsetPixel	);
	printf("ピクセルのサイズ     :%d/$%x\n",(int)magheader->sizePixel		,(int)magheader->sizePixel		);
	
	// 正常読み出し完了
	return (PIC_OK);
}

//-------------------------------------------------------------------------------

#pragma pack(push,1) // 境界整合させない
typedef struct { // パレット=色情報
	UByte g;
	UByte r;
	UByte b;
} MAG_PALETTE;
#pragma pack(pop)
#define	SZ_MAG_PALETTE	sizeof(MAG_PALETTE)

// パレット情報を読む
int readMAGPalette(FILE *fp,MAG_HEADER *magheader,MAG_PALETTE pal[256])
{
	int sz=16; // 他は16色分(8色モードでも同じ)
	if (magheader->scrnMode&BIT_256COLOR) {
		// 256色
		sz=256;
	}
	if (fread(pal,SZ_MAG_PALETTE,sz,fp)<sz) {
		return (PIC_READERR);
	}
#ifdef DEBUG
	int i;
	for (i=0;i<sz;i++) {
		printf("[%3d].r/g/b=$%02x,%02x,%02x\n",i,pal[i].r,pal[i].g,pal[i].b);
	}
#endif
	// 正常読み出し完了
	return (PIC_OK);
}

//-------------------------------------------------------------------------------

// フラグ領域の展開
// 現代なので、メモリはけちらずがばっと取る
int readAndExtendMAGFlags(FILE *fp,MAG_HEADER *magheader,UByte *eflags)
{
	// オフセットは境界整合されているため、必ずしもバイト数の加算位置とは限らない
	
	// フラグA
	UByte *flagA=malloc2(magheader->sizeFlagA);
	//	printf("magheader->sizeFlagA=%d\n",magheader->sizeFlagA);
	if (flagA==NULL) {
		printf("フラグA用ワークが確保できません\n");
		return (PIC_NOMEMORY);
	}
	fseek(fp,magheader->offsetFlagA,SEEK_SET);
	if (fread(flagA,1,magheader->sizeFlagA,fp)<magheader->sizeFlagA) {
		free2(flagA);
		return (PIC_READERR);
	}
#ifdef DBG_FILEOUT
	{
		FILE *ofp=fopen("flagA.dat","w");
		if (ofp!=NULL) {
			fwrite(flagA,magheader->sizeFlagA,1,ofp);
			fclose(ofp);
		}
	}
#endif
	//
	// フラグB
	UByte *flagB=malloc2(magheader->sizeFlagB);
	//	printf("magheader->sizeFlagB=%d\n",magheader->sizeFlagB);
	if (flagB==NULL) {
		printf("フラグB用ワークが確保できません\n");
		free2(flagA);
		return (PIC_NOMEMORY);
	}
	fseek(fp,magheader->offsetFlagB,SEEK_SET);
	if (fread(flagB,1,magheader->sizeFlagB,fp)<magheader->sizeFlagB) {
		free2(flagA);
		free2(flagB);
		return (PIC_READERR);
	}
#ifdef DBG_FILEOUT
	{
		FILE *ofp=fopen("flagB.dat","w");
		if (ofp!=NULL) {
			fwrite(flagB,magheader->sizeFlagB,1,ofp);
			fclose(ofp);
		}
	}
#endif
	
	// フラグを展開
	UByte *flagA0=flagA;
	UByte *flagB0=flagB;
	UByte *eflags0=eflags;
	ULong szB=0,szE=0,szA=magheader->sizeFlagA;
	while (szA-->0) {
		UByte c;
		c=*flagA++;
		int i;
		int bit=BIT7; // bit7
		for (i=0;i<8;i++) {
			if (c&bit) {
				*eflags++=*flagB++;
				if (++szB>magheader->sizeFlagB) {
					// 万が一のためチェック
					printf("展開異常\n");
					free2(flagB0);
					free2(flagA0);
					return (PIC_HEADERERR);
				}
			} else {
				*eflags++=0;
			}
			szE++;
			bit>>=1;
		}
	}
	printf("eflags実サイズ=%d\n",(int)szE); // ==szA*8
	// 終了
	free2(flagB0);
	free2(flagA0);
	//	printf("flagA/B解放\n");
	
	// １つ上のラインとXORを取って戻す
	printf("yライン数:%d\n",magheader->wy);
	// １行分のフラグバイト数
	UWord wx=magheader->wx/8; // 16色:1バイト8ドット
	if (magheader->scrnMode&BIT_256COLOR) {
		wx=magheader->wx/4;   // 256色:1バイト4ドット
	}
//	printf("１行分のフラグバイト数:%d/$%x\n",wx,wx);
	//
	szE-=wx; // １行分を引いておく
	UByte *eflags1=(eflags0+wx); // １行下のライン
	//	printf("先頭行=%p : １行下=%p / サイズ=%ld\n",eflags0,eflags1,szE);
	while (szE-->0) {
		*eflags1++^=*eflags0++;
	}
	return (PIC_OK);
}

//-------------------------------------------------------------------------------

// pixelへの直書き込みをbitmapPsetに置き換えるため、座標管理も一体化したメソッドを作る
typedef struct {
	int x;
	int y;
} PIXEL_XY;

// 1ドット分の色データを書き込む
-(void)putPixel:(PIXEL_XY *)xy color:(MAG_PALETTE)col magheader:(MAG_HEADER *)magheader
{
	[self bitmapPset:xy->x y:xy->y r:col.r g:col.g b:col.b alpha:0xff];
	if (++xy->x>=magheader->wx) {
		xy->x=0;
		xy->y++;
	}
}

// ピクセルをフラグに従って現在からの相対位置からコピーする
-(void)pixelCopy:(PIXEL_XY *)xy flag:(int)no magheader:(MAG_HEADER *)magheader
{
	/* 指定された場所から4ドット(16色)/2ドット(256色)分コピーする
	 16色でのフラグ番号と相対位置関係は図の通り。256色ではx方向がこの半分になる。
     　 Ｘ→
     Ｙ	___!_______!_______!_______!_______!_______! 相対y
     ↓  ___!_______!_______!_______!_______!__15___!-16		No(4bit)	相対x	相対y
		___!_______!_______!_______!_______!_______!-15		0			0		 0
		___!_______!_______!_______!_______!_______!-14		1			-4		 0
		___!_______!_______!_______!_______!_______!-13		2			-8		 0
		___!_______!_______!_______!_______!_______!-12		3			-16		 0
		___!_______!_______!_______!_______!_______!-11		4			 0		-1
		___!_______!_______!_______!_______!_______!-10		5			-4		-1
		___!_______!_______!_______!_______!_______!-9		6			 0		-2
		___!_______!_______!__14___!__13___!__12___!-8		7			-4		-2
		___!_______!_______!_______!_______!_______!-7		8			-8		-2
		___!_______!_______!_______!_______!_______!-6		9			 0		-4
		___!_______!_______!_______!_______!_______!-5		10			-4		-4
		___!_______!_______!__11___!__10___!___9___!-4		11			-8		-4
		___!_______!_______!_______!_______!_______!-3		12			 0		-8
		___!_______!_______!___8___!___7___!___6___!-2		13			-4		-8
		___!_______!_______!_______!___5___!___4___!-1		14			-8		-8
		___!___3___!_______!___2___!___1___!___0___!+0		15			 0		-16
	相対x    -16		-12		-8		-4      0,1,2,3
	 */
	typedef struct {
		int ofstX;
		int ofstY;
	} OFSTTABLE;
	static
	OFSTTABLE ofstTable[16]={
					{0, 0},{4,0},{8,0},{16,0},
					{0, 1},{4,1},
					{0, 2},{4,2},{8,2},
					{0, 4},{4,4},{8,4},
					{0, 8},{4,8},{8,8},
					{0,16}
	};
	//	printf("no=%d : ofst=%d,%d\n",no,ofstTable[no].ofstX,ofstTable[no].ofstY);
	int i,sz;
	PIXEL_XY sxy=*xy;
	sxy.y-=ofstTable[no].ofstY;
	if (magheader->scrnMode&BIT_256COLOR) {
		// 256色
		sxy.x-=ofstTable[no].ofstX/2;
		sz=2; // 2ドット分
	} else	{
		sxy.x-=ofstTable[no].ofstX;
		sz=4; // 4ドット分
	}
	for (i=0;i<sz;i++) {
		int r,g,b;
		[self bitmapPoint:sxy.x y:sxy.y r:&r g:&g b:&b alpha:NULL];
		[self bitmapPset:xy->x y:xy->y r:r g:g b:b alpha:0xff];
		if (++xy->x>=magheader->wx) {
			xy->x=0;
			xy->y++;
		}
		sxy.x++; // 行はまたがないのでこれでよい
	}
}

// c=0～15
-(int)pixel1WithFlag:(FILE *)fp pixel:(PIXEL_XY *)xy flag:(int)c magheader:(MAG_HEADER *)magheader palette:(MAG_PALETTE *)pal
{
	if (c==0) {
		// 2バイト読み込む
		int p1,p2;
		p1=fgetc(fp); if (p1<0) return(PIC_READERR);
		p2=fgetc(fp); if (p2<0) return(PIC_READERR);
		if (magheader->scrnMode&BIT_256COLOR) {
			// 256色 : 8bit 1ドット
			[self putPixel:xy color:pal[p1] magheader:magheader];
			[self putPixel:xy color:pal[p2] magheader:magheader];
		} else {
			// 16色 : 4bit 1ドット
			[self putPixel:xy color:pal[p1>>4  ] magheader:magheader];
			[self putPixel:xy color:pal[p1&0x0f] magheader:magheader];
			[self putPixel:xy color:pal[p2>>4  ] magheader:magheader];
			[self putPixel:xy color:pal[p2&0x0f] magheader:magheader];
		}
	} else {
		// フラグに従い、相対位置からのコピー
		if ( // 念のためエラーチェック
				 (xy->y==1               && c>=6 )
			||	((xy->y>=2 && xy->y<=3 ) && c>=9 )
			||	((xy->y>=4 && xy->y<=7 ) && c>=12)
			||	((xy->y>=9 && xy->y<=15) && c>=15)
			||  ((xy->x==0 && (c!=4 && c!=6 && c!=9 && c!=12 && c!=15)))
			||  ((xy->x==1 && (c==2 || c==8 || c==11 || c==14 || c==3)))
			||  ((xy->x>=2 && xy->x<=3) && c==3)
			) {
			printf("\nオフセットエラー:x,y=%d,%d;c=%d\n",xy->x,xy->y,c);
			return (PIC_DATA_ILLEGAL);
		}
		[self pixelCopy:xy flag:c magheader:magheader];
	}
	return (PIC_OK);
}

// ピクセルデータの展開
-(int)readAndExtendMAGPixels:(FILE *)fp magheader:(MAG_HEADER *)magheader eflags:(UByte *)eflags pixel:(PIXEL_XY*)xy palette:(MAG_PALETTE *)pal
{
	// ピクセルデータ先頭へ飛ぶ
	fseek(fp,magheader->offsetPixel,SEEK_SET);
	printf("ピクセル先頭オフセット:%ld\n",ftell(fp));
	//
	ULong szE=magheader->sizeFlagA*8;
	printf("eflagsサイズ=%d\n",(int)szE);
	while (szE-->0) {
		UByte c;
		c=*eflags++; // これで2つのフラグを読んでいる
		// 1回のループで16色で4*2ドット、256色で2*2ドット処理される
		int ret;
		ret=[self pixel1WithFlag:fp pixel:xy flag:(c>>4)   magheader:magheader palette:pal];
		if (ret) return (ret);
		ret=[self pixel1WithFlag:fp pixel:xy flag:(c&0x0f) magheader:magheader palette:pal];
		if (ret) return (ret);
	}
	return (PIC_OK);
}

//-------------------------------------------------------------------------------

// fbitmapOpen : 内部でbitmapを確保し、画面に表示するところまでを処理する
//				 このときはsx,syは無視される
// fNoBitmapClose : fbitmapOpen=YESの時のみ有効 : YES=bitmapCloseを実行しない , NO=実行する
-(int)magLoader:(NSString *)fname sx:(CGFloat)sx sy:(CGFloat)sy fbitmapOpen:(BOOL)fbitmapOpen fdirect:(BOOL)fdirect_ fNoBitmapClose:(BOOL)fNoBitmapClose
{
int ret;
	printFunc(); nslog(@"fname=%@",fname);
	NSString *path=getResourceOrSavedataPath(fname);
	if (path==nil) {
		nslog(@"file not found(getResourceOrSavedataPath):%@",path);
		return (PIC_FILENOTFOUND); // file can not open
	}
	if ((fp = fopen(nsStringTocString(path), "r" ))==NULL) {
		nslog(@"file not found:%@",path);
		return (PIC_FILENOTFOUND); // file can not open
	}
	
	// 実展開処理
	if (sx!=0 || sy!=0) {
		nslog(@"fdirect解除:sx=%f,sy=%f",sx,sy);
		fdirect_=NO;
	} // fdirect_=YESはsx==0&&sy==0の時のみ有効
	
	MAG_HEADER magheader;
	ret=readMAGHeader(fp,NULL,NULL,NULL,0,&magheader);
	if (ret) goto mag_end;

	if (fbitmapOpen) {
		if (![self bitmapOpen:magheader.wx height:magheader.wy fdirect:fdirect_]) {
			nslog(@"magLoader:bitmapOpen error");
			ret=PIC_BITMAP_ALREADY_USE;
			goto mag_end;
		}
	}
	
	MAG_PALETTE pal[256];
	ret=readMAGPalette(fp,&magheader,pal);
	if (ret) goto mag_end;
	
	ULong szE=magheader.sizeFlagA*8;
	UByte *eflags=malloc2(szE);
	printf("eflags:%dバイト\n",(int)szE);
	if (eflags==NULL) {
		printf("eflagsが確保できない\n");
		ret=PIC_NOMEMORY;
		goto mag_end;
	}

	ret=readAndExtendMAGFlags(fp,&magheader,eflags);
	if (ret==PIC_OK) {
		PIXEL_XY xy={0,0}; // homeから表示
		ret=[self readAndExtendMAGPixels:fp magheader:&magheader eflags:eflags pixel:&xy palette:pal];
	} else {
		printf("readAndExtendMAGFlagsエラー:%d\n",ret);
	}
	free2(eflags);
	// 展開途中でエラーが出ても、出来たところまで画像にする
	[self displayImageWithExpandAndBitmapClose:sx sy:sy fbitmapOpen:fbitmapOpen fNoBitmapClose:fNoBitmapClose];
mag_end:;
	fclose(fp);
	return (ret);
}

//-------------------------------------------------------------------------------

int getMagHeader(NSString *fname,int *wx,int *wy,int *mode,UByte *machine,UByte *username,UByte *comment,int maxCommentLength)
// MAGのヘッダー情報を取得する(V2.2)
// ヘッダーの内容については仕様書参照のこと
// iOSではMAGヘッダー内座標は無視するので、ヘッダー情報としても返さない
{
	printFunc(); nslog(@"fname=%@",fname);
	NSString *path=getResourceOrSavedataPath(fname);
	if (path==nil) {
		nslog(@"file not found(getResourceOrSavedataPath):%@",path);
		return (PIC_FILENOTFOUND); // file can not open
	}
	if ((fp = fopen(nsStringTocString(path), "r" ))==NULL) {
		nslog(@"file not found:%@",path);
		return (PIC_FILENOTFOUND); // file can not open
	}

//	UByte machine[MAG_LEN_MACHINE+1];
//	UByte username[MAG_LEN_USERNAME+1];
	MAG_HEADER magheader;
	int ret=readMAGHeader(fp,machine,username,comment,maxCommentLength,&magheader);
	fclose(fp);
	*wx	 =magheader.wx;
	*wy	 =magheader.wy;
	*mode=magheader.scrnMode;
	return (ret);
}

//-------------------------------------------------------------------------------
// JPEG
//-------------------------------------------------------------------------------

// JPEGのヘッダー情報を取得する
// ヘッダーの内容については仕様書参照のこと
int getJpegHeader(NSString *fname,int *wx,int *wy)
{
	printFunc(); nslog(@"fname=%@",fname);
	NSString *path=getResourceOrSavedataPath(fname);
	if (path==nil) {
		nslog(@"file not found(getResourceOrSavedataPath):%@",path);
		return (PIC_FILENOTFOUND); // file can not open
	}
	if ((fp = fopen(nsStringTocString(path), "r" ))==NULL) {
		nslog(@"file not found:%@",path);
		return (PIC_FILENOTFOUND); // file can not open
	}
	
	int ret=jpegHeader(fp,wx,wy,NO,NO);
	nslog(@"JPEG size=%d,%d",*wx,*wy);
	fclose(fp);
	return (ret);
}

//-------------------------------------------------------------------------------
// PNG
//-------------------------------------------------------------------------------

// PNGのヘッダー情報を取得する
// ヘッダーの内容についてはhttp://homepage2.nifty.com/sophia0/png.html参照のこと
int getPngHeader(NSString *fname,SLong *wx,SLong *wy,int *bitLength)
{
	printFunc(); nslog(@"fname=%@",fname);
	NSString *path=getResourceOrSavedataPath(fname);
	if (path==nil) {
		nslog(@"file not found(getResourceOrSavedataPath):%@",path);
		return (PIC_FILENOTFOUND); // file can not open
	}
	nslog(@"path=%@",path);
	if ((fp = fopen(nsStringTocString(path), "r" ))==NULL) {
		nslog(@"file not found:%@",path);
		return (PIC_FILENOTFOUND); // file can not open
	}
	
#if 0	// ダンプ
	{
		for (int i=0;i<64;i++) {
			fprintf(stdout,"%02x,",fgetc(fp));
			if (((i+1)%16)==0) {
				fprintf(stdout,"\n");
			}
		}
		fprintf(stdout,"\n");
	}
#endif
	
	int ret=PIC_OK;
	UByte buff[64];	// LEN_IHDR_HEADER+16より大きくすること
#define PNG_SYMBOL	"\x89PNG\r\n\x1a\n"
#define LEN_PNG_SYMBOL	(sizeof(PNG_SYMBOL)-1)
	if (fread(buff,LEN_PNG_SYMBOL,1,fp)<1) {
		ret=PIC_READERR;
		goto png_end;
	}
	// PNG識別子チェック
	if (memcmp(buff,PNG_SYMBOL,LEN_PNG_SYMBOL)!=0) {
		nslog(@"識別子エラー");
		ret=PIC_HEADERERR;
		goto png_end;
	}
	
	// iOS内部に入れ込むと何故かヘッダーが改変されるので対処する
	// IHDRヘッダーを読み込む（つもり）
#define LEN_IHDR_HEADER	(16+((4+4)+4+4+1*5))	// (データ長.4+識別子.4)+データ +16はiOS内部での改変対応
	if (fread(buff,LEN_IHDR_HEADER,1,fp)<1) {
		ret=PIC_READERR;
		goto png_end;
	}
	UByte *b=buff;
	if (memcmp(&buff[0],"\x00\x00\x00\x04\x43\x67\x42\x49",4*2)==0) {
		nslog(@"ヘッダーに改変がある");
		b=b+16; // 16バイトのデータが付加されているので飛ばす
	}
	if (memcmp(&b[4],"IHDR",4)!=0) {
		nslog(@"IHDRエラー");
		ret=PIC_HEADERERR;
		goto png_end;
	}
	*wx=rd68l(&b[8]);
	*wy=rd68l(&b[12]);
	nslog(@"PNG size=%d,%d",(int)*wx,(int)*wy);
	if (bitLength!=NULL) {
		*bitLength=(int)b[16];
		nslog(@"PNGビット深度=%d",*bitLength);
	}
png_end:;
	fclose(fp);
	return (ret);
}

//-------------------------------------------------------------------------------
// GIF
//-------------------------------------------------------------------------------

// GIFのヘッダー情報を取得する
// ヘッダーの内容についてはhttp://www.tohoho-web.com/wwwgif.htm参照のこと
int getGifHeader(NSString *fname,int *wx,int *wy)
{
	printFunc(); nslog(@"fname=%@",fname);
	NSString *path=getResourceOrSavedataPath(fname);
	if (path==nil) {
		nslog(@"file not found(getResourceOrSavedataPath):%@",path);
		return (PIC_FILENOTFOUND); // file can not open
	}
	if ((fp = fopen(nsStringTocString(path), "r" ))==NULL) {
		nslog(@"file not found:%@",path);
		return (PIC_FILENOTFOUND); // file can not open
	}
	
	int ret=PIC_OK;
	UByte buff[32];
#define GIF_SYMBOL	"GIF8"
#define LEN_GIF_SYMBOL	(sizeof(GIF_SYMBOL)-1)
	if (fread(buff,LEN_GIF_SYMBOL,1,fp)<1) {
		ret=PIC_READERR;
		goto gif_end;
	}
	// 識別子チェック
	if (memcmp(buff,GIF_SYMBOL,LEN_GIF_SYMBOL)!=0) {
		nslog(@"識別子エラー");
		ret=PIC_HEADERERR;
		goto gif_end;
	}
	//
#define LEN_GIF_HEADER	(2+2+2)	// バージョン.2+width.2+height.2
	if (fread(buff,LEN_GIF_HEADER,1,fp)<1) {
		ret=PIC_READERR;
		goto gif_end;
	}
	if (memcmp(buff,"7a",2)!=0 && memcmp(buff,"9a",2)!=0) {
		nslog(@"バージョンエラー");
		ret=PIC_HEADERERR;
		goto gif_end;
	}
	*wx=(int)*(UWord *)(&buff[2]);
	*wy=(int)*(UWord *)(&buff[4]);
	nslog(@"GIF size=%d,%d",*wx,*wy);
gif_end:;
	fclose(fp);
	return (ret);
}

//-------------------------------------------------------------------------------
// BMP
//-------------------------------------------------------------------------------

// BMPのヘッダー情報を取得する
// ヘッダーの内容についてはhttp://www.kk.iij4u.or.jp/~kondo/bmp/参照のこと
int getBmpHeader(NSString *fname,SLong *wx,SLong *wy,int *bitLength)
{
	printFunc(); nslog(@"fname=%@",fname);
	NSString *path=getResourceOrSavedataPath(fname);
	if (path==nil) {
		nslog(@"file not found(getResourceOrSavedataPath):%@",path);
		return (PIC_FILENOTFOUND); // file can not open
	}
	if ((fp = fopen(nsStringTocString(path), "r" ))==NULL) {
		nslog(@"file not found:%@",path);
		return (PIC_FILENOTFOUND); // file can not open
	}
	
	int ret=PIC_OK;
	UByte buff[32];
	if (fread(buff,14,1,fp)<1) {
		ret=PIC_READERR;
		goto bmp_end;
	}
	// 識別子チェック
#define BMP_SYMBOL	"BM"
#define LEN_BMP_SYMBOL	(sizeof(BMP_SYMBOL)-1)
	if (memcmp(buff,BMP_SYMBOL,LEN_BMP_SYMBOL)!=0) {
		nslog(@"識別子エラー");
		ret=PIC_HEADERERR;
		goto bmp_end;
	}
	// 情報ヘッダーのサイズを得る
	SLong sz;
	if (fread(&sz,SZ_LONG,1,fp)<1) {
		ret=PIC_READERR;
		goto bmp_end;
	}
	if (fread(buff,(sz>sizeof(buff)? sizeof(buff):sz),1,fp)<1) {
		ret=PIC_READERR;
		goto bmp_end;
	}
	switch (sz) {
		case 12:
			nslog(@"情報ヘッダーサイズ=%d(OS/2)",(int)sz);
			*wx=(SLong)*(UWord *)(&buff[0]);
			*wy=(SLong)*(UWord *)(&buff[2]);
			if (bitLength!=NULL) {
				*bitLength=(int)*(UWord *)(&buff[6]);
				nslog(@"bitLength=%d",*bitLength);
			}
			break;
		case 40:
		default:
			nslog(@"情報ヘッダーサイズ=%d(Windows)",(int)sz);
			*wx=(SLong)*(UWord *)(&buff[0]);
			*wy=(SLong)*(UWord *)(&buff[4]);
			if (bitLength!=NULL) {
				*bitLength=(int)*(UWord *)(&buff[10]);
				nslog(@"bitLength=%d",*bitLength);
			}
			break;
	}
	nslog(@"BMP size=%d,%d",(int)*wx,(int)*wy);
bmp_end:;
	fclose(fp);
	return (ret);
}

//---------------------------------------------------------------------------------------
// bitmapImageFile
//---------------------------------------------------------------------------------------

// 画像ファイルをbitmapに読み込む
// 範囲指定はpic/cut/magには無効
-(int)bitmapImageFile:(NSString *)fname sx:(int)sx sy:(int)sy ex:(int)ex ey:(int)ey
{
	if (pixel==NULL) return(-1);
	nslog(@"bitmapImageFile:fname=%@,sx=%d,sy=%d,ex=%d,ey=%d",fname,sx,sy,ex,ey);
	
	NSString *ext=[[fname pathExtension]uppercaseString];
	if ([ext isEqualToString:@"PIC"]) {
		nslog(@"PIC");
		return ([self picLoader:fname sx:0 sy:0 fbitmapOpen:NO fdirect:NO fNoBitmapClose:YES]);
	}
	if ([ext isEqualToString:@"CUT"]) {
		nslog(@"CUT");
		return ([self cutLoader:fname sx:0 sy:0 fbitmapOpen:NO fdirect:NO fNoBitmapClose:YES]);
	}
	if ([ext isEqualToString:@"MAG"]) {
		nslog(@"MAG");
		return ([self magLoader:fname sx:0 sy:0 fbitmapOpen:NO fdirect:NO fNoBitmapClose:YES]);
	}
	
	nslog(@"PIC/CUT/MAG以外");
	NSString *path=getResourceOrSavedataPath(fname);
	if (path==nil) {
		nslog(@"ファイルが見つからない");
		return (PIC_FILENOTFOUND);
	}
	
	UIImage *loadImg=[UIImage imageWithContentsOfFile:path];
	if (loadImg==nil) {
		nslog(@"ファイルが見つからない(loadImg)");
		return (PIC_FILENOTFOUND);
	}

	int width =(int)loadImg.size.width;
	int height=(int)loadImg.size.height;
	nslog(@"画像サイズ=%d*%d",width,height);

	if (sx==0 && sy==0 && ex==0 && ey==0) {
		// 自動的に縮小表示
		// [self displayImageWithExpandAndBitmapClose:sx sy:sy fbitmapOpen:NO fNoBitmapClose:YES];に相当
		
		CGRect imageRect;
		CGFloat wx=width;	// 等倍
		CGFloat wy=height;
		if (width >frame0.size.width
		||  height>frame0.size.height) {
			// 画像が画面内に入りきら無い時は縮小して入れ込む
			// 縦横の差分を計算する
			wx=frame0.size.width;
			wy=frame0.size.height;
			int dwx=width -frame0.size.width;
			int dwy=height-frame0.size.height;
			// はみ出る部分が大きいの縮尺に合わせる
			if (dwx>dwy) {
				wy=height*(frame0.size.width /width );
			} else {
				wx=width *(frame0.size.height/height);
			}
			nslog(@"縮小表示:wx,wy=%f,%f",wx,wy);
		} else { // 入りきるときは等倍
			nslog(@"等倍表示");
		}
		imageRect.size=CGSizeMake(wx,wy);
		CGPoint point={sx,sy};
		imageRect.origin=point;
		printFrame(@"imageRect", imageRect);
		//
		pushContext();
		CGImageRef img = CGImageRetain(rotateImage(loadImg,-180).CGImage); // ここで鏡像変換と画像確定
		CGContextDrawImage(context, imageRect, img);
		CGImageRelease(img);
		[self draw];
	} else {
		// UIImageを専用ワークビットマップに展開する
		UByte *image=[ImageHelper convertUIImageToBitmapRGBA8:loadImg];
		if (image==NULL) {
			nslog(@"ワークが確保できない");
			return (PIC_NOMEMORY);
		}

		// 縮小表示なし
		// 読み込んだ画像を(sx,sy)〜(ex,ey)の領域に入れ込む
		// sx+width >exならex=sx+width-1
		// sy+height>eyならey=sy+height-1
		sx=[self checkX:sx];
		sy=[self checkY:sy];
		if (ex<0) ex=sx+width -1;
		if (ey<0) ey=sy+height-1;
		ex=[self checkX:ex];
		ey=[self checkY:ey];
		checkBitmapBoxArea(&sx,&sy,&ex,&ey);
		nslog(@"読み込み座標範囲:(%d,%d)〜(%d,%d)",sx,sy,ex,ey);
		
		// image → pixelへ転送
		UByte *image0=image;
		for (int y=sy;y<=ey;y++) {
			UByte *pixel0 =calcBitmapAdrs(sx,y);
			UByte *image1=image0;
			for (int x=sx;x<=ex;x++) {
				*pixel0++=*image1++; // r
				*pixel0++=*image1++; // g
				*pixel0++=*image1++; // b
				*pixel0++=*image1++; // alpha
			}
			image0+=width*4; // 4=1ピクセルのバイト数
		}
		free2(image);
	}
	return (PIC_OK);
}

//---------------------------------------------------------------------------------------

@end
