module y4d_draw.sprite;

private import std.stream;
private import ytl.y4d_result;
private import ytl.exception;
private import y4d_draw.drawcontext;
private import y4d_draw.drawbase;
private import y4d_draw.texture;
private import y4d_draw.screen;
private import y4d_draw.textureloader;
private import y4d_aux.filesys;
private import y4d_aux.lineparser;
private import y4d_aux.stringconv;

///	スプライトの表現
/**
	１枚のスプライトを表現する
	転送元サーフェースと、転送元矩形の情報しかない。<BR>

	スプライトの実体はTexture。

	TextureLoaderを経由して読み込ませるべきだとも思うが、
	スプライトは常時描画していることのほうが多いので、
	cacheは考えないことにする。
*/
struct SimpleSprite {
public:
	Texture			texture;	//!<	転送元テクスチャ

	// ---------- 使いたければ以下のを使ってもいい ----------
	int			nOx,nOy;	//!<	転送オフセット
	int			nHeight;	//!<	ベースライン算出用。キャラの足もとまでの距離を入れて使うと良い(かも知れない)
	bool		bFast;		//!<	抜き色無し(α無し)転送か？
	Rect		rcRect;		//!<	転送元矩形(pRectによって指されている)

	//	---補助

	///	サーフェース全域を一つのこのスプライトとする
	/**
		nOx=nOy=0,bFast=true,nHeight=0となる。
	*/
	void		set(Texture texture_){
		set(texture_,null);
	}

	///	サーフェースの一部をこのスプライトとする
	/**
		nOx=nOy=0,bFast=true,nHeight=高さ になる。
	*/
	void		set(Texture texture_,Rect* rc_){
		if (!rc_){
			if (texture_) {
				rcRect.setRect(0,0,texture.getWidth(),texture.getHeight());
			} else {
				rcRect.setRect(0,0,0,0);
			}
		} else {
			rcRect = *rc_;
		}

		texture = texture_;
		nOx = nOy = 0;
		nHeight	= cast(int)(rcRect.bottom-rcRect.top);
		bFast	= true;
	}

	///	rcRectに矩形を設定する
	void	setRect(float left_,float top_,float right_,float bottom_)	{
		rcRect.left = left_;
		rcRect.top	= top_;
		rcRect.right = right_;
		rcRect.bottom = bottom_;
	}

};

///	スプライトクラス。
/**
	SimpleSpriteのvectorのvectorをメンバに持つ、スプライトクラス<BR>

	※　スプライトとは、「プレーンにおける矩形」の集合として
	定義できる論理構造であって、描画とは切り離して考えるべき。<BR>

	よって、このクラスは描画に関することを行なわない。<BR>

<PRE>
	・SimpleSprite = ひとつのスプライト。(動きなし)

	・SpriteVector とは、 SimpleSprite のvector。
	これはスプライトの一連の動き(motion)を持っていると考える。
	例）歩行パターンetc..

	・SpriteVectorVectorは、SpriteVectorのvector
	これは、0番目が上向きの歩行パターン、1番目が下向きの歩行パターン、
	というように方向性(directional)を持っていると考える。

	*getAllSprite()[getDirection()][getMotion()]
	を画面に描画する。

	上記の表現は、
	*getDirectionalSprite()[m_nAnimation]
		や、単に
	*getSprite()
	と書ける。これは、現在表示している（表示する）スプライト情報を
	取得するものである。

	setSpriteしたとき、setDirection(0); setMotion(0); したことになる

	・animation (getMotionで取得できる)はアニメーションカウンタなので
	毎フレーム自動的に加算するような使いかたをするべき

	・direction (getDirectionで取得できる)は、キャラクタの向きを変更する。
	（向きごとに一連のアニメーションが定義されていると考える)
</PRE>
*/
class Sprite {
	//	変数名がハンガリアンになっているのは
	//	yaneSDK3rdからの移植だからなのだ(´Д`)

public:
	///	スプライトの実体
	alias SimpleSprite[] SpriteVector;

	///	スプライトの実体
	alias SpriteVector[] SpriteVectorVector;

	///	スプライト実体の取得
	SpriteVectorVector getAllSprite() { return m_vSprtieVector; }

	///	スプライト実体の取得
	SpriteVector getDirectionalSprite()
		{ return getAllSprite()[getDirection()]; }

	///	スプライト実体の取得
	SimpleSprite* getSprite()
		{ return &(getDirectionalSprite()[getMotion()]); }

	///	スプライトの設定
	/**
		スプライトを設定すると、direction = animation = 0に初期化される。
	*/
	void	setSprite(SpriteVectorVector v)
	{ m_vSprtieVector = v; 	m_nDirection = m_nAnimation = 0;}

	///	スプライト向きの設定
	/**
		注:setDirectionでは、方向の変更が無い場合はアニメーションカウンタを
		リセットしない。方向の変更がある場合は、アニメーションカウンタを
		0にする。
	*/
	void	setDirection(int nDirection){
		//	方向の変更が無い場合はアニメカウンタをリセットしない（仕様）
		if (m_nDirection != nDirection) {
			m_nAnimation = 0;
		}
		m_nDirection = nDirection;
	}

	///	スプライト向きの取得
	int		getDirection() { return m_nDirection; }

	///　追加オフセット量を設定する（初期値(0,0)）
	void	setOffset(int nOffsetX,int nOffsetY) {
		m_nOx = nOffsetX;
		m_nOy = nOffsetY;
	}

	///　追加オフセット量を取得する（初期値(0,0)）
	void	getOffset(out int nOffsetX,out int nOffsetY) {
		nOffsetX = m_nOx;
		nOffsetY = m_nOy;
	}

	///	現在のアニメーション番号を設定する
	void	setMotion(int n) { m_nAnimation=n; }

	///	現在のアニメーション番号を取得する
	int		getMotion() { return m_nAnimation; }

	///	現在のアニメーション番号をインクリメントする
	void	incMotion() {
		m_nAnimation++;
		if (m_nAnimation >= cast(int)getDirectionalSprite().length) {
			m_nAnimation = 0;
		}
	}

	///	アニメーションが終端にまで達したか?
	bool	isEnd()
	{ return cast(bool) ((cast(int) getDirectionalSprite().length) == m_nAnimation); }

	///	スプライトの表示座標を設定する
	void	setPos(int x,int y) { m_nX = x; m_nY = y; }

	///	スプライトの表示座標を取得する
	void	getPos(out int x,out int y) { x=m_nX; y=m_nY; }

	///	高さの設定(この情報を使おうが使うまいが構わない)
	void	setHeight(int nHeight) { m_nHeight=nHeight; }

	///	高さの取得(この情報を使おうが使うまいが構わない)
	int		getHeight() { return m_nHeight; }

	///	スプライトを有効にする
	/**
		(この情報を使おうが使うまいが構わない)
	*/
	void	enable(bool bEnable) { m_bVisible = bEnable; }

	///	スプライトが有効かどうかの状態を取得する
	/**
		(この情報を使おうが使うまいが構わない)
	*/
	bool	isEnable() { return m_bVisible; }

	///	スプライトのプライオリティを設定する
	/**
		(この情報を使おうが使うまいが構わない)
	*/
	void	setPriority(int nPriority) { m_nPriority = nPriority; }

	///	スプライトのプライオリティを取得する
	/**
		(この情報を使おうが使うまいが構わない)
	*/
	int		getPriority() { return m_nPriority;}

	this() {
		m_bVisible = true;
//		m_vSprtieVector = new SpriteVectorVector;
	}

protected:
	SpriteVectorVector			m_vSprtieVector;
	int		m_nDirection;		//	向いている方向
	int		m_nAnimation;		//	アニメーションカウンタ
	/**
		m_vSprtieVector[m_nDirection][m_nAnimation]
		を画面に描画すると考える。

		m_nAnimationはアニメーションカウンタなので
		毎フレーム自動的に加算される

		m_nDirectionは、キャラクタの向きを変更する。
		（向きごとに一連のアニメーションが定義されていると考える)
	*/

	//	以下のメンバはディフォルトで0
	int		m_nX,m_nY;			//	表示位置
	//	以下は使いたくなければ使わなくて良い
	int		m_nOx,m_nOy;		//	オフセット座標
	int		m_nHeight;			//	高さ(キャラのベースライン算出のため)
	bool	m_bVisible;			//	表示・非表示
	int		m_nPriority;		//	描画プライオリティ
};

///	Sprite クラスの簡単な描画機能つきのもの。
/**
	描画機能つきのスプライトクラス
	必要に応じて、このクラスを派生させてメンバを足すか、
	Spirteから別に派生させるかして使ってくんろ。<BR>

	Rect*を最後に指定できるほうのbltは、転送先の矩形クリップ機能つき。

<PRE>
			screen.blendSrcAlpha();
			DrawContext dc = screen.getDrawContext();
			if (lpClip) {
				dc.setClipRect(lpClip);
			} else {
				dc.disableClip();
			}
			screen.blt(s.texture,ox,oy,null,&s.rcRect);
</PRE>
	こういうコードで実現しているので、screenのblend設定、clip設定は
	変更される。これがまずければ、bltFixをオーバーライドするか、
	別のbltterを書いてちょーだい。
*/
class SpriteEx : Sprite {
public:

	///	通常描画(アニメーションカウント加算される)
	void	blt(Screen screen) { blt(screen,null); }
	void	blt(Screen screen,Rect* lpClip /* =NULL */) {
		blt(screen,0,0,lpClip);
	}

	///	座標指定つき描画(アニメーションカウント加算される)
	void	blt(Screen screen,int x,int y) { blt(screen,x,y,null); }
	void	blt(Screen screen,int x,int y,Rect* lpClip /* =NULL*/ ) {
		bltFix(screen,x,y,lpClip);
		incMotion();
	}

	///	アニメーションカウントは加算されない描画
	void	bltFix(Screen screen) { bltFix(screen,null); }
	void	bltFix(Screen screen,Rect* lpClip /* =NULL */ ) {
		int x,y;
		getPos(x,y);
		bltFix(screen,x,y,lpClip);
	}

	///	座標指定つき描画(アニメーションカウントは加算されない)
	void	bltFix(Screen screen,int x,int y) { bltFix(screen,x,y,null); }
	void	bltFix(Screen screen,int x,int y,Rect* lpClip/*=NULL*/){
		//	有効か？
		if (!isEnable()) return ;

		SimpleSprite* s = getSprite();
		int ox,oy;
		getOffset(ox,oy);
		ox+=x+s.nOx;
		oy+=y+s.nOy;

		//	そのまま委譲してまうとすっか～
		if (s.bFast) {
			screen.disableBlend();
			DrawContext dc = screen.getDrawContext();
			if (lpClip) {
				dc.setClipRect(lpClip);
			} else {
				dc.disableClip();
			}
			screen.blt(s.texture,ox,oy,&s.rcRect);
		} else {
			screen.blendSrcAlpha();
			DrawContext dc = screen.getDrawContext();
			if (lpClip) {
				dc.setClipRect(lpClip);
			} else {
				dc.disableClip();
			}
			screen.blt(s.texture,ox,oy,&s.rcRect);
		}
	}

	//	アニメーションカウントが最後になっていたらそれ以上は加算しない描画
	/**
		(それ以外の場合にはアニメーションカウントが加算される)
	*/
	void	bltOnce(Screen screen,int x,int y) { bltOnce(screen,x,y,null);}
	void	bltOnce(Screen screen,int x,int y,Rect* lpClip /* =NULL */){
		bltFix(screen,x,y,lpClip);

		//	ケツになっていたら、それ以上は加算しない
		int n=getMotion();
		incMotion();
		if (getMotion()==0) {
			setMotion(n);
		}
	}
};

///	ファイルからスプライト定義を読み込むためのローダー
/**
	(yaneSDK2nd/yaneSDK3rdのCSpriteChara互換)
<PRE>
	スプライト定義ファイルのフォーマット
	・大文字小文字は区別しない
	・１行は２５６文字まで
	・矩形定義は256まで
	・スプライトとは、ある一連の動き（CSimpleSprite）を定義する
	・スプライト定義は、矩形を順番に指定することによって行なう
	・各行、//以降はコメントとできる
	・プレーンは無限に利用できる
	・矩形とともに、足の位置（ベースライン）を指定することがある
	・矩形とともに、オフセットを指定することがある
	・スプライト・矩形番号は０から始まる
	#SpriteMax	4			//	スプライト定義数は４。これは先頭で指定する
	#Plane 0,"test.bmp"		//	プレーン0としてtest.bmpを読み込む
	#ColorKey 0,4,10		//	0番のプレーンの抜き色を位置で指定し、それは(4,10)とする(ディフォルトは(0,0))
	#Plane 1,"test2.bmp"	//	プレーン1としてtest2.bmpを読み込む
	#Rect 0,1,48,48,64,128	//	矩形0としてプレーン1の(48,48)からW64×H128の矩形とする
	#Rect 1,1,112,48,64,128	//	矩形1としてプレーン1の(112,48)からW64×H128の矩形とする
	#Sprite 1,0,1,1,0		//	スプライト1は矩形0→矩形1→矩形1→矩形0という循環とする
	#SpriteF 0,0,1,1,0		//	スプライト0は矩形0→矩形1→矩形1→矩形0という循環とする。ただし抜き色は無効


	⇒　一括定義
	#SpritePlane 0,0,34					//	スプライト0～34は、プレーン0から35個(プレーン0からプレーン34)とする。
	#SpritePlaneF 0,0,34				//	スプライト0～34は、プレーン0から35個(プレーン0からプレーン34)とする。ただし抜き色は無効
	#ColorKeyA 0,34,4,10				//	0～34のプレーンのカラーキーは位置で指定し、それは(4,10)とする(ディフォルトは(0,0))
	#ColorKeyB 0,107,0,255,0			
	//	0-107番のプレーンの抜き色をRGBで指定し、それは(0,255,0)とする

//	#SpriteA 0,0,34						//	スプライト0～34は、矩形0から35個目の矩形までとする。（未実装）

	#RectA	12,0,10,15,32,64,4,3	//	矩形12～は、プレーン0に対する、W32×H64のサイズであり、
									//	それは(10,15)に始まり、横4列、縦3行の12回分繰り替えし
									//	定義される。つまり矩形12～23までが一気に定義される
	#RectOffset 10,16,5,6	//	矩形10から16までに、(5,6)のオフセットを与える

//	#Layer 1,3,5			//	スプライト1から3はLayer5として定義（ディフォルトで5）
//
</PRE>
*/
class SpriteLoader {
public:

	///	キャラパターン定義ファイルをテキストファイルから読み込む
	/**
		読み込み解読上のエラーの場合、例外がthrowされる。
	*/
	y4d_result	load(char[] fileName)
	{
		ubyte[] mem = cast(ubyte[])(FileSys.read(fileName));
		if (!mem) return y4d_result.file_not_found; // 読み込みエラー
		loadRW(mem);
		return y4d_result.no_error;
	}

	///	メモリからloadするバージョン
	/**
		読み込み解読上のエラーの場合、例外がthrowされる。
	*/
	void	loadRW(void[] memory) {

		release();
//		m_vSpriteVector = new Sprite.SpriteVectorVector;
	//	これやる必要がないのか？

		//	ReadLine()で一行ずつ解析してスプライト設定を行なう

		//	スプライトスライスの用意
		SimpleSprite ssprite[256];
//		ZERO(ssprite);

		ubyte[] mem = cast(ubyte[])memory;
		std.stream.MemoryStream memStream = new std.stream.MemoryStream(mem);

		int		nLine = 0;
		static const long DEFAULT_VAL_MAX = int.max;
		static const long DEFAULT_VAL_MIN = int.min;
		
		LineParser		lineParser = new LineParser;
		while (!memStream.eof) {
			nLine++;

			char[] linebuf = cast(char[]) memStream.readLine();
			lineParser.setLine( linebuf );
			//	ライン パーサーに文字列をセットする

			//////////////////////////////////////////////////
			// 各コマンド別に実行させる

			// スプライト定義数の設定
			if ( lineParser.isMatch( cast(char[]) "#SpriteMax" ) ){
			int		n;
				n = cast(int) lineParser.getNum( DEFAULT_VAL_MIN );
				if ( n == DEFAULT_VAL_MIN ) {
					throw new y4d_error(this,cast(char[]) "SpriteLoader.loadRWで#SpriteMaxに失敗");
				}
				m_vSpriteVector.length = n;
				continue;
			}

			////	最大一致法 SpriteMax > SpriteF > Sprite
			//	スプライトxの矩形循環の設定 (ただし抜き色は無効)
			if ( lineParser.isMatch( cast(char[]) "#SpriteF" ) ){
				if (!m_vSpriteVector)
					throw new y4d_error(this,cast(char[]) "SpriteLoader.loadRWで#SpriteMaxなしに#Spriteが有る");

				int	n,m;
				n = cast(int) lineParser.getNum(DEFAULT_VAL_MAX);
				if (n==DEFAULT_VAL_MAX)
					throw new y4d_error(this,cast(char[]) "SpriteLoader.loadRWで#SpriteFで一つ目のパラメータが不正");

				while (true){
					m = cast(int) lineParser.getNum(DEFAULT_VAL_MAX);
					if (m == DEFAULT_VAL_MAX) break;
					ssprite[m].bFast=true;

					//	高さは、足元
					ssprite[m].nHeight = 0;
						//	今回はこれで統一したほうがよさげ...
						//	もし変更が必要な場合は、各キャラクラスで
						//	変更すること

					m_vSpriteVector[n] ~= ssprite[m];
				}
				continue;
			}

/*
	⇒　一括定義
	#SpritePlane 0,0,34					//	スプライト0～34は、プレーン0から35個目(プレーン34)とする。
	#SpritePlaneF 0,0,34					//	スプライト0～34は、プレーン0から35個目(プレーン34)とする。ただし抜き色は無効
*/

			//	非循環スプライトxの設定
			if ( lineParser.isMatch( cast(char[]) "#SpritePlaneF" ) ){
				if (!m_vSpriteVector)
					throw new y4d_error(this,cast(char[]) "SpriteLoader.loadRWで#SpriteMaxなしに#Spriteが有る");
				int		n,m,l;
				n = cast(int) lineParser.getNum(long.max);
				if (n == int.max)
					throw new y4d_error(this,cast(char[]) "SpriteLoader.loadRWで#SpritePlaneFで最初のパラメータが不正");
				m = cast(int) lineParser.getNum(long.max);
				if (m == int.max) break;
				l = cast(int) lineParser.getNum(long.max);
				if (l == int.max) break;

				SimpleSprite s;
				for(int i=n;i<n+l;i++){
					ssprite[i].bFast=true;
					//	高さは、足元
					ssprite[i].nHeight = 0;
					//	今回はこれで統一したほうがよさげ...
					//	もし変更が必要な場合は、各キャラクラスで変更すること
					s.texture = m_apSurface[m+i];
					if (s.texture)
						s.setRect(0,0,s.texture.getWidth(),
							s.texture.getHeight());
	//				m_lpSprite[i].SetSpriteAdd(&s);
					m_vSpriteVector[n] ~= s;
				}
				continue;
			}

			//	非循環スプライトxの設定
			if ( lineParser.isMatch( cast(char[]) "#SpritePlane" ) ){
				if (!m_vSpriteVector)
					throw new y4d_error(this,cast(char[]) "SpriteLoader.loadRWで#SpriteMaxなしに#Spriteが有る");

				int		n,m,l;
				n = cast(int) lineParser.getNum(DEFAULT_VAL_MAX);
				if (n == DEFAULT_VAL_MAX)
					throw new y4d_error(this,cast(char[]) "SpriteLoader.loadRWで#SpritePlaneFで最初のパラメータが不正");
				m = cast(int) lineParser.getNum(DEFAULT_VAL_MAX);
				if (m == DEFAULT_VAL_MAX) break;
				l = cast(int) lineParser.getNum(DEFAULT_VAL_MAX);
				if (l == DEFAULT_VAL_MAX) break;

				SimpleSprite s;
				for(int i=n;i<n+l;i++){
					ssprite[i].bFast=false;
					//	高さは、足元
					ssprite[i].nHeight = 0;
					//	もし変更が必要な場合は、各キャラクラスで変更すること
					s.texture = m_apSurface[m+i];
					if (s.texture)
						s.setRect(0,0,s.texture.getWidth(),
							s.texture.getHeight());
	//				m_lpSprite[i].SetSpriteAdd(&s);
					m_vSpriteVector[n] ~= s;
				}
				continue;
			}


			//	スプライトxの矩形循環の設定
			if ( lineParser.isMatch( cast(char[]) "#Sprite" ) ){
				if (!m_vSpriteVector)
					throw new y4d_error(this,cast(char[]) "SpriteLoader.loadRWで#SpriteMaxなしに#Spriteが有る");

				int		n,m;
				n = cast(int) lineParser.getNum(DEFAULT_VAL_MAX);
				if (n==DEFAULT_VAL_MAX)
					throw new y4d_error(this,cast(char[]) "SpriteLoader.loadRWで#Spriteで最初のパラメータが不正");

				while (true){
					m = cast(int) lineParser.getNum(DEFAULT_VAL_MAX);
					if (m == DEFAULT_VAL_MAX) break;
					ssprite[m].bFast=false;

					//	高さは、足元
					ssprite[m].nHeight = 0;
					//	もし変更が必要な場合は、各キャラクラスで変更すること

	//				m_lpSprite[n].SetSpriteAdd(&ssprite[m]);
					m_vSpriteVector[n] ~= ssprite[m];
				}
				continue;
			}

			// 指定したプレーンpにスプライトデータを読み込む
			if ( lineParser.isMatch( cast(char[]) "#Plane" ) ){
				int		n;
				n = cast(int) lineParser.getNum(DEFAULT_VAL_MAX);
				if (n==DEFAULT_VAL_MAX)
					throw new y4d_error(this,cast(char[]) "SpriteLoader.loadRWで#Planeでパラメータが不正");

				//	不足分は、newする
				while (cast(int)m_apSurface.length<=n){
					m_apSurface ~= new Texture;
				}
				char[] szFile = lineParser.getStr();
				m_apSurface[n].load( FileSys.makeFullName(szFile) );
				continue;
			}

			//	指定したプレーンpの抜き色を座標で指定
			if ( lineParser.isMatch( cast(char[]) "#ColorKeyA" ) ){
				int		n,m;
				n = cast(int) lineParser.getNum( DEFAULT_VAL_MAX );
				m = cast(int) lineParser.getNum( DEFAULT_VAL_MAX );

				if (n==DEFAULT_VAL_MAX || m==DEFAULT_VAL_MAX)
					throw new y4d_error(this,cast(char[]) "SpriteLoader.loadRWで#ColorKeyAでパラメータが不正");

			//	不足分は、newする
			//	(ただし、ここでnewしたものに対してSetColorKeyは不正だが．．	)
				while (cast(int)m_apSurface.length<=m){
					m_apSurface ~= new Texture;
				}
				int x,y;
				x = cast(int) lineParser.getNum( DEFAULT_VAL_MAX );
				y = cast(int) lineParser.getNum( DEFAULT_VAL_MAX );
				if (x==DEFAULT_VAL_MAX || y==DEFAULT_VAL_MAX)
					throw new y4d_error(this,cast(char[]) "SpriteLoader.loadRWで#ColorKeyAでパラメータが不正");

				for(int i=n;i<=m;i++) {
					m_apSurface[i].setColorKeyPos(x,y);
				}
				continue;
			}

			//	指定したプレーンpの抜き色をRGBで指定
			if ( lineParser.isMatch( cast(char[]) "#ColorKeyB" ) ){
				int		n,m;
				n = cast(int) lineParser.getNum( DEFAULT_VAL_MAX );
				m = cast(int) lineParser.getNum( DEFAULT_VAL_MAX );
				if (n==DEFAULT_VAL_MAX || m==DEFAULT_VAL_MAX)
					throw new y4d_error(this,cast(char[]) "SpriteLoader.loadRWで#ColorKeyBでパラメータが不正");

				//	不足分は、newする
				while (cast(int)m_apSurface.length<=m){
//					m_apSurface.insert(CPlane::GetFactory()->CreateInstance());
//	サーフェースの生成
					m_apSurface ~= new Texture;
				}
				int r,g,b;
				r = cast(int) lineParser.getNum( DEFAULT_VAL_MAX );
				g = cast(int) lineParser.getNum( DEFAULT_VAL_MAX );
				b = cast(int) lineParser.getNum( DEFAULT_VAL_MAX );
				if (r==DEFAULT_VAL_MAX || g==DEFAULT_VAL_MAX || b==DEFAULT_VAL_MAX)
					throw new y4d_error(this,cast(char[]) "SpriteLoader.loadRWで#ColorKeyBでパラメータが不正");

				for(int i=n;i<=m;i++) {
					m_apSurface[i].setColorKey(r,g,b);
				}
				continue;
			}

			//	指定したプレーンpの抜き色を座標で指定
			if ( lineParser.isMatch( cast(char[]) "#ColorKey" ) ){
				int		n;
				n = cast(int) lineParser.getNum( DEFAULT_VAL_MAX );
				if (n==DEFAULT_VAL_MAX)
					throw new y4d_error(this,cast(char[]) cast(char[]) "SpriteLoader.loadRWで#ColorKeyでパラメータが不正");

				//	不足分は、newする
				while (cast(int)m_apSurface.length<=n){
					m_apSurface ~= new Texture;
				}
				int x,y;
				x = cast(int) lineParser.getNum( DEFAULT_VAL_MAX );
				y = cast(int) lineParser.getNum( DEFAULT_VAL_MAX );
				if (x==DEFAULT_VAL_MAX || y==DEFAULT_VAL_MAX)
					throw new y4d_error(this,cast(char[]) "SpriteLoader.loadRWで#ColorKeyでx,yが不正");

				m_apSurface[n].setColorKeyPos(x,y);
				continue;
			}

			//	最大一致法 (RectOffset > RectA > Rect)
			//	矩形aからbまでに、(x,y)のオフセットを与える
			if ( lineParser.isMatch( cast(char[]) "#RectOffset" ) ){
				int		a,b;
				a = cast(int) lineParser.getNum( DEFAULT_VAL_MAX );
				b = cast(int) lineParser.getNum( DEFAULT_VAL_MAX );
				int		x,y;
				x = cast(int) lineParser.getNum( DEFAULT_VAL_MAX );
				y = cast(int) lineParser.getNum( DEFAULT_VAL_MAX );
				if (a==DEFAULT_VAL_MAX || b==DEFAULT_VAL_MAX || x==DEFAULT_VAL_MAX || y==DEFAULT_VAL_MAX)
					throw new y4d_error(this,cast(char[]) "SpriteLoader.loadRWで#RectOffsetでパラメターが不正");
				for(int i=a;i<=b;i++){
					ssprite[i].nOx = x;
					ssprite[i].nOy = y;
				}
				continue;
			}

			//	矩形aから、プレーンpに対して、(x,y)からW×Hで横x列、縦y行の繰り替えし定義を行う
			if ( lineParser.isMatch( cast(char[]) "#RectA" ) ){
				int		n;
				n = cast(int) lineParser.getNum( DEFAULT_VAL_MAX );
				int		nPlane;
				nPlane = cast(int) lineParser.getNum( DEFAULT_VAL_MAX );

				int		x,y,w,h,xt,yt;
				x = cast(int) lineParser.getNum( DEFAULT_VAL_MAX );
				y = cast(int) lineParser.getNum( DEFAULT_VAL_MAX );
				w = cast(int) lineParser.getNum( DEFAULT_VAL_MAX );
				h = cast(int) lineParser.getNum( DEFAULT_VAL_MAX );
				xt= cast(int) lineParser.getNum( DEFAULT_VAL_MAX );
				yt= cast(int) lineParser.getNum( DEFAULT_VAL_MAX );

				if (n==DEFAULT_VAL_MAX || nPlane==DEFAULT_VAL_MAX || x==DEFAULT_VAL_MAX || y==DEFAULT_VAL_MAX
					|| w==DEFAULT_VAL_MAX || h==DEFAULT_VAL_MAX || xt==DEFAULT_VAL_MAX || yt==DEFAULT_VAL_MAX)
					throw new y4d_error(this,cast(char[]) "SpriteLoader.loadRWで#RectAでパラメターが不正");

				for(int j=0;j<yt;j++){
					for(int i=0;i<xt;i++){
						ssprite[n+i+j*xt].setRect(x+w*i,y+h*j,
							x+w*(i+1),y+h*(j+1));
						ssprite[n+i+j*xt].texture = m_apSurface[nPlane];
					}
				}
				continue;
			}

			//	矩形aとしてプレーンpの(x,y)からW×Hの矩形とする
			if ( lineParser.isMatch( cast(char[]) "#Rect" ) ){
				int		n;
				n = cast(int) lineParser.getNum( DEFAULT_VAL_MAX );
				int		nPlane;
				nPlane = cast(int) lineParser.getNum( DEFAULT_VAL_MAX );
				int		x,y,sx,sy;
				x = cast(int) lineParser.getNum( DEFAULT_VAL_MAX );
				y = cast(int) lineParser.getNum( DEFAULT_VAL_MAX );
				sx = cast(int) lineParser.getNum( DEFAULT_VAL_MAX );
				sy = cast(int) lineParser.getNum( DEFAULT_VAL_MAX );

				if (n<0 || n>255 || nPlane==DEFAULT_VAL_MAX || x==DEFAULT_VAL_MAX || y==DEFAULT_VAL_MAX
					|| sx==DEFAULT_VAL_MAX || sy==DEFAULT_VAL_MAX)
					throw new y4d_error(this,cast(char[]) "SpriteLoader.loadRWで#Rectでパラメターが不正");

				ssprite[n].setRect(x,y,x+sx,y+sy);
				ssprite[n].texture = m_apSurface[nPlane];
				continue;
			}

			/*
			//	スプライトs1からs2はレイヤーLとして定義（ディフォルトで5）
			if ( lineParser.isMatch( "#Layer" ) ){
				int		a,b;
				a = lineParser.GetNum( int.max );
				b = lineParser.GetNum( int.max );
				int		nLayer;
				nLayer = lineParser.GetNum( int.max );
				for(int i=a;i<=b;i++){
					m_lpSprite[i].SetLayer(nLayer);
				}
				continue;
			}
			*/
			if (lineParser.isMatch(cast(char[]) "#")){
				throw new y4d_error(this,cast(char[]) "SpriteLoader.loadRWで" ~ 
						StringConv.toDec(nLine) ~ cast(char[]) "行に不明な命令");
			//	ファイル名はわかんね(´Ａ`)
			}
		}
	}

	///	キャラパターン定義ファイルを解放する
	/**
		読み込んでいた画像データも解放する。
	*/
	void	release() {
		if (m_apSurface) {
			foreach(Texture t;m_apSurface)
				if (t) t.release();
			m_apSurface = null;
		}
		m_vSpriteVector = null;
	}

	///	loadで読み込んだスプライト定義を取得する
	/**
		(ここで得たものをSprite::setSpriteに渡すと良い)
	*/
	Sprite.SpriteVectorVector getSprite()
		{ return m_vSpriteVector; }

	///	スプライト定義を渡す
	/**
		(通常使う必要はない)
	*/
	void	setSprite(Sprite.SpriteVectorVector v)
		{ m_vSpriteVector = v; }

protected:

	///	Loadで読み込んだときのサーフェースを保持しておくテクスチャーバッファ
	Texture[]	m_apSurface;

	///	展開したスプライト実体
	Sprite.SpriteVectorVector m_vSpriteVector;
};
