/*	
 *	clipswf.c
 *
 *	Flash SWF Player
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2009 Naoyuki Sawa
 *
 *	* Mon Jun 08 00:15:20 JST 2009 Naoyuki Sawa
 *	- 1st リリース。
 *	- 現在は、SWF4(Flash Lite)の、一部の機能だけに対応しています。
 *	  対応している機能については、ソースコードを参照してください。
 *	* Sun Jun 14 16:50:39 JST 2009 Naoyuki Sawa
 *	- SWF_BIT_STREAM_get_RECT,MATRIX,CXFORMWITHALPHAの読み出し開始時に、バイト整列を追加しました。
 *	  これまでは、読み出し開始時にはバイト整列を行わず、読み出し終了時にのみバイト整列を行っていました。
 *	  現在対応しているタグ構造の範囲では、ビットフィールドの直後にRECT,MATRIX,CXFORMWITHALPHAが無いので、
 *	  SWF_BIT_STREAM_get_RECT,MATRIX,CXFORMWITHALPHAの読み出し開始時に、バイト整列を行わなくても同じです。
 *	  しかし念のために、「SWF File Format Specification」に従って、バイト整列を追加しておくことにしました。
 *	- SWF_OBJECT_DefineShape_2_3()に、ShapeRecordsのバイト整列が不要であることのコメントを追記しました。
 *	  この点について、プログラムの変更はありません。
 *	- SWF_OBJECT_exec()に、タグの読み出しが終端を超過した場合に停止するよう、検査コードを追加しました。
 *	* Fri Aug 21 16:48:57 JST 2009 Naoyuki Sawa
 *	- SWF_OBJECT_PlaceObject2()を修正し、グラフィックシンボルの差し替え機能に対応しました。
 *	  これまでは、{PlaceFlagMove=1,PlaceFlagHasCharacter=1}のケースを、正しく処理できていませんでした。
 *	* Sat Aug 22 02:06:31 JST 2009 Naoyuki Sawa
 *	- 親タイムラインがEndでループしたとき、子ムービークリップがリセットせずに継続するよう修正しました。
 *	  たとえば、10フレーム長のムービークリップを作成し、1フレーム長のメインタイムラインに置いた場合に、
 *	  メインタイムラインが1フレーム毎にループしても、ムービークリップは独立して10フレーム間継続します。
 *	  これまでは、メインタイムラインがループする度に、ムービークリップもリセットされていました。
 *	- 修正のために、SWF_OBJECT_STATE_END、SWF_DISPLAY_LIST_end1()、SWF_DISPLAY_LIST_end2()を追加しました。
 *	  修正内容の詳細は、これらのフラグや関数が使用されている辺りの、コメントを参照してください。
 *	* Fri Aug 28 02:03:40 JST 2009 Naoyuki Sawa
 *	- アクションスクリプトの実行順序を修正しました。
 *	  アクションスクリプトはすぐに実行せず、すべてのオブジェクトの処理がShowFrameに到達した後で、まとめて実行する仕様です。
 *	  そうしないと、ターゲットとの処理順序によって、アクションスクリプトの効果が1フレームずれるなどの問題が生じるからです。
 *	* Wed Sep 02 17:21:17 JST 2009 Naoyuki Sawa
 *	- 静止テキストの表示を対応しました。
 *	- FontFlagsShiftJISによって10ドット日本語フォントと6ドット英語フォントを切り替えていた、独自仕様を廃止しました。
 *	  Flash MXは、静止テキストをDefineText,DefineFont,DefineFontInfoの組み合わせで生成しますが、
 *	  英語フォントでも日本語フォントでもDefineFontInfoのFontFlagsShiftJISが1になってしまうようで、アテになりません。
 *	  Suzukaは、静止テキストをDefineText2,DefineFont2の組み合わせで生成しますが、
 *	  英語フォントでも日本語フォントでもDefineFontInfoのFontFlagsShiftJISが0になってしまうようで、アテになりません。
 *	  混乱を避けるために、今後は10ドット日本語フォントのみを使うことにしました。
 *	- ダイナミックテキストに対するFontFlagsShiftJISは正常かも知れないのですが、もう10ドット日本語フォントに統一することにしました。
 *	* Thu Sep 03 14:21:33 JST 2009 Naoyuki Sawa
 *	- jpeg_decode()にcomponent引数を追加したことに追従し、Y成分を取得するよう、引数component=0の指定を追加しました。
 *	* Fri Sep 04 01:09:48 JST 2009 Naoyuki Sawa
 *	- swf_init()内のコメント追記。プログラムには変更有りません。
 *	* Tue Sep 08 23:06:41 JST 2009 Naoyuki Sawa
 *	- ダイナミックテキストの座標ズレを修正しました。
 *	  具体的には、SWF_EDIT_TEXT_draw()関数内のコメントを参照してください。
 *	* Wed Sep 09 00:29:21 JST 2009 Naoyuki Sawa
 *	- swf_set_text_drawing_method()関数を追加しました。
 *	  これまでは、静止テキストを文字として描いていましたが、図形として描く方式も選択できるようにしました。
 *	  現在は、Windows用コードブロックでテスト出力を行うのみですが、あとでP/ECE用コードも対応する予定です。
 *	* Wed Sep 09 05:19:23 JST 2009 Naoyuki Sawa
 *	- SWF_TEXT_dump_using_GlyphShapeTable()関数を調整しました。
 *	  Windows用コードブロックでテスト出力を行なう際の、多角形処理やフィルモードの調整です。
 *	* Fri Sep 11 03:15:56 JST 2009 Naoyuki Sawa
 *	- P/ECE上でも、静止テキストを図形として描く処理を実装しました。
 *	  swf_set_text_drawing_method()関数による設定が、P/ECE上でも反映されるようになりました。
 *	* Sat Sep 12 05:08:07 JST 2009 Naoyuki Sawa
 *	- SWF_TEXT_draw_using_GlyphShapeTable()に、線がピクセル間を通って消えてしまわないようにするための調整を追加しました。
 *	* Sat Sep 12 13:36:28 JST 2009 Naoyuki Sawa
 *	- SWF_TEXT_draw_using_GlyphShapeTable()の中で、間違ってfloor()を使っていた部分を、floorf()に修正しました。
 *	* Sat Sep 12 19:50:46 JST 2009 Naoyuki Sawa
 *	- SWF_TEXT_draw_using_CodeTable()に、行列の平行移動成分だけでなく、拡縮回転成分も影響するよう、修正しました。
 *	  文字そのものの拡縮回転は未対応ですが、文字の左上原点が、本来の拡縮回転された文字の左上原点位置に一致するようになりました。
 *	* Sat Sep 12 21:17:14 JST 2009 Naoyuki Sawa
 *	- SWF_TEXT_draw_using_GlyphShapeTable()が、render_vec2f_alternate_concaves()でなくrender_vec2i_alternate_concaves()を使うよう変更し、高速化しました。
 *	  この変更に伴い、上記 Sat Sep 12 05:08:07 JST 2009 コメントの修正箇所は無くなりました。
 */
#include "clip.h"

/*****************************************************************************
 *	「SWF File Format Specification」に定義されているレコード
 *****************************************************************************/

typedef struct _SWF_RGB { /* RGBレコードに相当 */
	unsigned char Red;
	unsigned char Green;
	unsigned char Blue;
} SWF_RGB;

typedef struct _SWF_RGBA { /* RGBAレコードに相当 */
	unsigned char Red;
	unsigned char Green;
	unsigned char Blue;
	unsigned char Alpha;
} SWF_RGBA;

typedef struct _SWF_RECT { /* RECTレコードに相当 */
	float Xmin;
	float Xmax;
	float Ymin;
	float Ymax;
} SWF_RECT;

typedef struct _SWF_MATRIX { /* MATRIXレコードに相当 */
	float ScaleX;
	float ScaleY;
	float RotateSkew0;
	float RotateSkew1;
	float TranslateX;
	float TranslateY;
} SWF_MATRIX;

static const SWF_MATRIX SWF_MATRIX_1 = { /* 無変換 */
	1.0f, /* ScaleX */
	1.0f, /* ScaleY */
	0.0f, /* RotateSkew0 */
	0.0f, /* RotateSkew1 */
	0.0f, /* TranslateX */
	0.0f, /* TranslateY */
};

typedef struct _SWF_CXFORMWITHALPHA { /* CXFORMWITHALPHAレコードに相当 */
	float RedMultTerm;
	float GreenMultTerm;
	float BlueMultTerm;
	float AlphaMultTerm;
	float RedAddTerm;
	float GreenAddTerm;
	float BlueAddTerm;
	float AlphaAddTerm;
} SWF_CXFORMWITHALPHA;

static const SWF_CXFORMWITHALPHA SWF_CXFORMWITHALPHA_1 = { /* 無変換 */
	1.0f, /* RedMultTerm */
	1.0f, /* GreenMultTerm */
	1.0f, /* BlueMultTerm */
	1.0f, /* AlphaMultTerm */
	0.0f, /* RedAddTerm */
	0.0f, /* GreenAddTerm */
	0.0f, /* BlueAddTerm */
	0.0f, /* AlphaAddTerm */
};

/*****************************************************************************
 *	SWF_BIT_STREAM
 *****************************************************************************/

typedef struct _SWF_BIT_STREAM {
	const unsigned char* data;
	int bit_pos;
} SWF_BIT_STREAM;

static int SWF_BIT_STREAM_get_UB(SWF_BIT_STREAM* self, int nBits);
static int SWF_BIT_STREAM_get_SB(SWF_BIT_STREAM* self, int nBits);
static float SWF_BIT_STREAM_get_FB(SWF_BIT_STREAM* self, int nBits);
static const void* SWF_BIT_STREAM_get_data(SWF_BIT_STREAM* self, int n);
static int SWF_BIT_STREAM_get_UI8(SWF_BIT_STREAM* self);
static int SWF_BIT_STREAM_get_UI16(SWF_BIT_STREAM* self);
static int SWF_BIT_STREAM_get_SI16(SWF_BIT_STREAM* self);
static int SWF_BIT_STREAM_get_SI32(SWF_BIT_STREAM* self);
static const char* SWF_BIT_STREAM_get_STRING(SWF_BIT_STREAM* self);
static void SWF_BIT_STREAM_get_RGB(SWF_BIT_STREAM* self, SWF_RGB* out/*nullok*/);
static void SWF_BIT_STREAM_get_RGBA(SWF_BIT_STREAM* self, SWF_RGBA* out/*nullok*/);
static void SWF_BIT_STREAM_get_RECT(SWF_BIT_STREAM* self, SWF_RECT* out/*nullok*/);
static void SWF_BIT_STREAM_get_MATRIX(SWF_BIT_STREAM* self, SWF_MATRIX* out/*nullok*/);
static void SWF_BIT_STREAM_get_CXFORMWITHALPHA(SWF_BIT_STREAM* self, SWF_CXFORMWITHALPHA* out/*nullok*/);

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

static int SWF_BIT_STREAM_get_UB(SWF_BIT_STREAM* self, int nBits) {
	int value = 0;
	while(nBits--) {
		value = (value << 1) | ((self->data[self->bit_pos >> 3] >> (~self->bit_pos & 7)) & 1);
		self->bit_pos++;
	}
	return value;
}

static int SWF_BIT_STREAM_get_SB(SWF_BIT_STREAM* self, int nBits) {
	return (SWF_BIT_STREAM_get_UB(self, nBits) << (32 - nBits)) >> (32 - nBits);
}

static float SWF_BIT_STREAM_get_FB(SWF_BIT_STREAM* self, int nBits) {
	return SWF_BIT_STREAM_get_SB(self, nBits) / 65536.0f;
}

static const void* SWF_BIT_STREAM_get_data(SWF_BIT_STREAM* self, int n) {
	int byte_pos = (self->bit_pos + 7) >> 3;
	const void* data = &self->data[byte_pos];
	self->bit_pos = (byte_pos + n) << 3;
	return data;
}

static int SWF_BIT_STREAM_get_UI8(SWF_BIT_STREAM* self) {
	const unsigned char* data = SWF_BIT_STREAM_get_data(self, 1);
	return data[0];
}

static int SWF_BIT_STREAM_get_UI16(SWF_BIT_STREAM* self) {
	const unsigned char* data = SWF_BIT_STREAM_get_data(self, 2);
	return data[0] | (data[1] << 8);
}

static int SWF_BIT_STREAM_get_SI16(SWF_BIT_STREAM* self) {
	return (short)SWF_BIT_STREAM_get_UI16(self);
}

static int SWF_BIT_STREAM_get_SI32(SWF_BIT_STREAM* self) {
	const unsigned char* data = SWF_BIT_STREAM_get_data(self, 4);
	return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
}

static const char* SWF_BIT_STREAM_get_STRING(SWF_BIT_STREAM* self) {
	const char* string = SWF_BIT_STREAM_get_data(self, 0);
	while(SWF_BIT_STREAM_get_UI8(self)) { /** no job **/ }
	return string;
}

static void SWF_BIT_STREAM_get_RGB(SWF_BIT_STREAM* self, SWF_RGB* out/*nullok*/) {
	SWF_RGB tmp;
	tmp.Red   = SWF_BIT_STREAM_get_UI8(self);
	tmp.Green = SWF_BIT_STREAM_get_UI8(self);
	tmp.Blue  = SWF_BIT_STREAM_get_UI8(self);
	if(out) {
		*out = tmp;
	}
}

static void SWF_BIT_STREAM_get_RGBA(SWF_BIT_STREAM* self, SWF_RGBA* out/*nullok*/) {
	SWF_RGBA tmp;
	tmp.Red   = SWF_BIT_STREAM_get_UI8(self);
	tmp.Green = SWF_BIT_STREAM_get_UI8(self);
	tmp.Blue  = SWF_BIT_STREAM_get_UI8(self);
	tmp.Alpha = SWF_BIT_STREAM_get_UI8(self);
	if(out) {
		*out = tmp;
	}
}

static void SWF_BIT_STREAM_get_RECT(SWF_BIT_STREAM* self, SWF_RECT* out/*nullok*/) {
	/* The RECT record must be byte aligned. */
	SWF_BIT_STREAM_get_data(self, 0); /* バイト整列 */
	{
		SWF_RECT tmp;
		int Nbits = SWF_BIT_STREAM_get_UB(self, 5);
		tmp.Xmin  = SWF_BIT_STREAM_get_SB(self, Nbits) / 20.0f;
		tmp.Xmax  = SWF_BIT_STREAM_get_SB(self, Nbits) / 20.0f;
		tmp.Ymin  = SWF_BIT_STREAM_get_SB(self, Nbits) / 20.0f;
		tmp.Ymax  = SWF_BIT_STREAM_get_SB(self, Nbits) / 20.0f;
		if(out) {
			*out = tmp;
		}
	}
	SWF_BIT_STREAM_get_data(self, 0); /* バイト整列 */
}

static void SWF_BIT_STREAM_get_MATRIX(SWF_BIT_STREAM* self, SWF_MATRIX* out/*nullok*/) {
	/* The MATRIX record must be byte aligned. */
	SWF_BIT_STREAM_get_data(self, 0); /* バイト整列 */
	{
		SWF_MATRIX tmp = SWF_MATRIX_1;
		if(SWF_BIT_STREAM_get_UB(self, 1)) { /* HasScale */
			int NScaleBits = SWF_BIT_STREAM_get_UB(self, 5);
			tmp.ScaleX     = SWF_BIT_STREAM_get_FB(self, NScaleBits);
			tmp.ScaleY     = SWF_BIT_STREAM_get_FB(self, NScaleBits);
		}
		if(SWF_BIT_STREAM_get_UB(self, 1)) { /* HasRotate */
			int NRotateBits = SWF_BIT_STREAM_get_UB(self, 5);
			tmp.RotateSkew0 = SWF_BIT_STREAM_get_FB(self, NRotateBits);
			tmp.RotateSkew1 = SWF_BIT_STREAM_get_FB(self, NRotateBits);
		}
		{
			int NTranslateBits = SWF_BIT_STREAM_get_UB(self, 5);
			tmp.TranslateX     = SWF_BIT_STREAM_get_SB(self, NTranslateBits) / 20.0f;
			tmp.TranslateY     = SWF_BIT_STREAM_get_SB(self, NTranslateBits) / 20.0f;
		}
		if(out) {
			*out = tmp;
		}
	}
	SWF_BIT_STREAM_get_data(self, 0); /* バイト整列 */
}

static void SWF_BIT_STREAM_get_CXFORMWITHALPHA(SWF_BIT_STREAM* self, SWF_CXFORMWITHALPHA* out/*nullok*/) {
	/* The CXFORM record must be byte aligned. */
	SWF_BIT_STREAM_get_data(self, 0); /* バイト整列 */
	{
		SWF_CXFORMWITHALPHA tmp = SWF_CXFORMWITHALPHA_1;
		int HasAddTerms  = SWF_BIT_STREAM_get_UB(self, 1);
		int HasMultTerms = SWF_BIT_STREAM_get_UB(self, 1);
		int Nbits        = SWF_BIT_STREAM_get_UB(self, 4);
		if(HasMultTerms) {
			tmp.RedMultTerm   = SWF_BIT_STREAM_get_UB(self, Nbits) / 256.0f;
			tmp.GreenMultTerm = SWF_BIT_STREAM_get_UB(self, Nbits) / 256.0f;
			tmp.BlueMultTerm  = SWF_BIT_STREAM_get_UB(self, Nbits) / 256.0f;
			tmp.AlphaMultTerm = SWF_BIT_STREAM_get_UB(self, Nbits) / 256.0f;
		}
		if(HasAddTerms) {
			tmp.RedAddTerm   = SWF_BIT_STREAM_get_UB(self, Nbits);
			tmp.GreenAddTerm = SWF_BIT_STREAM_get_UB(self, Nbits);
			tmp.BlueAddTerm  = SWF_BIT_STREAM_get_UB(self, Nbits);
			tmp.AlphaAddTerm = SWF_BIT_STREAM_get_UB(self, Nbits);
		}
		if(out) {
			*out = tmp;
		}
	}
	SWF_BIT_STREAM_get_data(self, 0); /* バイト整列 */
}

/*****************************************************************************
 *	SWF_CHARACTER
 *****************************************************************************/

/* SWF_CHARACTER.character_type */
//#define SWF_CHARACTER_TYPE_ROOT	0
#define SWF_CHARACTER_TYPE_BITMAP	1
#define SWF_CHARACTER_TYPE_SHAPE	2
#define SWF_CHARACTER_TYPE_FONT		3
#define SWF_CHARACTER_TYPE_TEXT		4
#define SWF_CHARACTER_TYPE_EDIT_TEXT	5
#define SWF_CHARACTER_TYPE_SPRITE	6

typedef struct _SWF_CHARACTER {
	LIST_ENTRY list_entry;
	int character_type;
	int CharacterId;
} SWF_CHARACTER;

typedef struct _SWF_BITMAP {
	SWF_CHARACTER character; /* 基底クラス */
	TEXTURE texture;
	/* unsigned char  buf[texture->header.w * texture->header.h / 4] */
	/* unsigned char mask[texture->header.w * texture->header.h / 8] */
} SWF_BITMAP;

typedef struct _SWF_SHAPE {
	SWF_CHARACTER character; /* 基底クラス */
	SWF_BITMAP* bitmap;
	SWF_MATRIX BitmapMatrix;
	struct _SWF_SHAPE* next;
} SWF_SHAPE;

typedef struct _SWF_FONT {
	SWF_CHARACTER character; /* 基底クラス */
	int FontFlagsWideOffsets;		/* DefineFont2 */
	const unsigned char* OffsetTable;	/* DefineFont,DefineFont2 */
	int FontFlagsWideCodes;			/* DefineFontInfo,DefineFont2 */
	const unsigned char* CodeTable;		/* DefineFontInfo,DefineFont2 */
	int FontFlagsHasLayout;			/* DefineFont2 */
	float FontDescent;			/* DefineFont2 */
	int FontFlagsBold;			/* DefineFontInfo,DefineFont2 */
#ifndef PIECE
	int FontNameLen;			/* DefineFontInfo,DefineFont2 */
	const char* FontName;			/* DefineFontInfo,DefineFont2 */
	int FontFlagsItalic;			/* DefineFontInfo,DefineFont2 */
#endif /*PIECE*/
} SWF_FONT;

typedef struct _SWF_TEXT {
	SWF_CHARACTER character; /* 基底クラス */
	SWF_MATRIX TextMatrix;
	int GlyphBits;
	int AdvanceBits;
	const unsigned char* TextRecords;
	int has_alpha;
#ifndef PIECE
	SWF_RECT TextBounds;
#endif /*PIECE*/
} SWF_TEXT;

typedef struct _SWF_EDIT_TEXT {
	SWF_CHARACTER character; /* 基底クラス */
	SWF_RECT Bounds;
	SWF_FONT* font;
	int color;
	const char* InitialText;
} SWF_EDIT_TEXT;

typedef struct _SWF_SPRITE {
	SWF_CHARACTER character; /* 基底クラス */
	const unsigned char* ControlTags;
} SWF_SPRITE;

/*****************************************************************************
 *	SWF
 *****************************************************************************/

typedef struct _SWF {
	SWF_INFO swf_info;
	LIST_ENTRY dictionary;
	LIST_ENTRY display_list;
	const unsigned char* JPEGData; /* JPEGTables */
	int text_drawing_method; /* 0 = using CodeTable, 1 = using GlyphShapeTable */
} SWF;

static SWF swf;

static void SWF_DICTIONARY_new_bitmap(int CharacterId, SURFACE* surface);
static SWF_CHARACTER* SWF_DICTIONARY_get_character(int CharacterId);
static void SWF_DICTIONARY_free();

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

static void SWF_DICTIONARY_new_bitmap(int CharacterId, SURFACE* surface) {
	SWF_BITMAP* bitmap = calloc(1, sizeof(SWF_BITMAP) + (surface->w * surface->h / 4)/*buf*/ + (surface->w * surface->h / 8)/*mask*/);
	if(!bitmap) {
		DIE(); /* メモリ不足 */
	}
	/*{{初期化*/
	bitmap->character.character_type = SWF_CHARACTER_TYPE_BITMAP;
	bitmap->character.CharacterId    = CharacterId;
	bitmap->texture.header.bpp       = 2;
	bitmap->texture.header.mask      = 1;
	bitmap->texture.header.w         = surface->w;
	bitmap->texture.header.h         = surface->h;
	bitmap->texture.buf              = (unsigned char*)(&bitmap->texture + 1);
	bitmap->texture.mask             = bitmap->texture.buf + (surface->w * surface->h / 4);
	texture_copy(&bitmap->texture, surface, 0, 0);
	/*}}初期化*/
	InsertTailList(&swf.dictionary, &bitmap->character.list_entry);
}

static SWF_CHARACTER* SWF_DICTIONARY_get_character(int CharacterId) {
	LIST_ENTRY* list_head = &swf.dictionary;
	LIST_ENTRY* list_entry = list_head->Flink;
	while(list_entry != list_head) {
		SWF_CHARACTER* character = CONTAINING_RECORD(list_entry, SWF_CHARACTER, list_entry);
		list_entry = list_entry->Flink;
		if(character->CharacterId == CharacterId) {
			return character;
		}
	}
	return NULL;
}

static void SWF_DICTIONARY_free() {
	while(!IsListEmpty(&swf.dictionary)) {
		LIST_ENTRY* list_entry = RemoveHeadList(&swf.dictionary);
		SWF_CHARACTER* character = CONTAINING_RECORD(list_entry, SWF_CHARACTER, list_entry);
		free(character); /* character_typeにかかわらず、メモリブロックは1つで確保している */
	}
}

/*****************************************************************************
 *	SWF_OBJECT
 *****************************************************************************/

/* SWF_OBJECT.object_state */
#define SWF_OBJECT_STATE_PLAY	(1 << 0)	/* 0 = スプライト停止中、または、非スプライト, 1 = スプライト再生中 */
#define SWF_OBJECT_STATE_END	(1 << 1)	/* 0 = 通常, 1 = Endでループした直後から、最初のShowFrameまでの期間 */

typedef struct _SWF_OBJECT SWF_OBJECT;
/*typedef*/ struct _SWF_OBJECT {
	LIST_ENTRY list_entry;
	int object_state;
	int Depth;
	const char* Name;
	SWF_MATRIX Matrix;
	SWF_OBJECT* parent;
	SWF_CHARACTER* character;
	SWF_BIT_STREAM bit_stream;
	LIST_ENTRY display_list;
	LIST_ENTRY action_list;
} /*SWF_OBJECT*/;

typedef struct _SWF_ACTION {
	LIST_ENTRY list_entry;
	const unsigned char* Actions;
} SWF_ACTION;

static SWF_OBJECT* SWF_DISPLAY_LIST_new_object(LIST_ENTRY* display_list, int Depth);
static SWF_OBJECT* SWF_DISPLAY_LIST_get_object(LIST_ENTRY* display_list, int Depth);
static void SWF_DISPLAY_LIST_free(LIST_ENTRY* display_list);
static void SWF_DISPLAY_LIST_exec(LIST_ENTRY* display_list);
static void SWF_DISPLAY_LIST_exec_action(LIST_ENTRY* display_list);
static void SWF_DISPLAY_LIST_end1(LIST_ENTRY* display_list);
static void SWF_DISPLAY_LIST_end2(LIST_ENTRY* display_list);
#ifdef PIECE
static void SWF_DISPLAY_LIST_draw(LIST_ENTRY* display_list, RENDER* render, const mat2f* m);
#endif /*PIECE*/

static void SWF_ACTION_LIST_add_action(LIST_ENTRY* action_list, const unsigned char* Actions);
static const unsigned char* SWF_ACTION_LIST_get_action(LIST_ENTRY* action_list);
static void SWF_ACTION_LIST_free(LIST_ENTRY* action_list);

static void SWF_OBJECT_free(SWF_OBJECT* self);
static void SWF_OBJECT_exec(SWF_OBJECT* self);
static void SWF_OBJECT_exec_action(SWF_OBJECT* self);
#ifdef PIECE
static void SWF_OBJECT_draw(SWF_OBJECT* self, RENDER* render, const mat2f* m);
#endif /*PIECE*/

static void SWF_OBJECT_DefineShape(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream);         //  2 DefineShape
static void SWF_OBJECT_DefineBits(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream);          //  6 DefineBits
static void SWF_OBJECT_JPEGTables(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream);          //  8 JPEGTables
static void SWF_OBJECT_SetBackgroundColor(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream);  //  9 SetBackgroundColor
static void SWF_OBJECT_DefineFont(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream);          // 10 DefineFont
static void SWF_OBJECT_DefineText(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream);          // 11 DefineText
static void SWF_OBJECT_DoAction(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream);            // 12 DoAction
static void SWF_OBJECT_DefineFontInfo(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream);      // 13 DefineFontInfo
static void SWF_OBJECT_DefineBitsLossless(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream);  // 20 DefineBitsLossless
static void SWF_OBJECT_DefineBitsJPEG2(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream);     // 21 DefineBitsJPEG2
static void SWF_OBJECT_DefineShape2(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream);        // 22 DefineShape2
static void SWF_OBJECT_PlaceObject2(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream);        // 26 PlaceObject2
static void SWF_OBJECT_RemoveObject2(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream);       // 28 RemoveObject2
static void SWF_OBJECT_DefineShape3(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream);        // 32 DefineShape3
static void SWF_OBJECT_DefineText2(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream);         // 33 DefineText2
static void SWF_OBJECT_DefineBitsJPEG3(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream);     // 35 DefineBitsJPEG3
static void SWF_OBJECT_DefineBitsLossless2(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream); // 36 DefineBitsLossless2
static void SWF_OBJECT_DefineEditText(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream);      // 37 DefineEditText
static void SWF_OBJECT_DefineSprite(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream);        // 39 DefineSprite
static void SWF_OBJECT_DefineFont2(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream);         // 48 DefineFont2

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

static SWF_OBJECT* SWF_DISPLAY_LIST_new_object(LIST_ENTRY* display_list, int Depth) {
	SWF_OBJECT* object;
	LIST_ENTRY* list_entry = display_list->Flink;
	while(list_entry != display_list) {
		object = CONTAINING_RECORD(list_entry, SWF_OBJECT, list_entry);
		/*{{この順番必須*/
		if(object->Depth >= Depth) {
			break;
		}
		list_entry = list_entry->Flink;
		/*}}この順番必須*/
	}
	object = calloc(1, sizeof(SWF_OBJECT));
	if(!object) {
		DIE(); /* メモリ不足 */
	}
	/*{{初期化*/
	object->Depth  = Depth;
	object->Name   = "";
	object->Matrix = SWF_MATRIX_1;
	InitializeListHead(&object->display_list);
	InitializeListHead(&object->action_list);
	/*}}初期化*/
	InsertTailList(list_entry, &object->list_entry);
	return object;
}

static SWF_OBJECT* SWF_DISPLAY_LIST_get_object(LIST_ENTRY* display_list, int Depth) {
	LIST_ENTRY* list_entry = display_list->Flink;
	while(list_entry != display_list) {
		SWF_OBJECT* object = CONTAINING_RECORD(list_entry, SWF_OBJECT, list_entry);
		list_entry = list_entry->Flink;
		if(object->Depth == Depth) {
			return object;
		}
	}
	return NULL;
}

static void SWF_DISPLAY_LIST_free(LIST_ENTRY* display_list) {
	while(!IsListEmpty(display_list)) {
		LIST_ENTRY* list_entry = display_list->Flink;
		SWF_OBJECT* object = CONTAINING_RECORD(list_entry, SWF_OBJECT, list_entry);
		SWF_OBJECT_free(object); /* この中でRemoveEntryList(&object->list_entry)が呼ばれる */
	}
}

static void SWF_DISPLAY_LIST_exec(LIST_ENTRY* display_list) {
	LIST_ENTRY* list_entry = display_list->Flink;
	while(list_entry != display_list) {
		SWF_OBJECT* object = CONTAINING_RECORD(list_entry, SWF_OBJECT, list_entry);
		list_entry = list_entry->Flink;
		SWF_OBJECT_exec(object);
	}
}

static void SWF_DISPLAY_LIST_exec_action(LIST_ENTRY* display_list) {
	LIST_ENTRY* list_entry = display_list->Flink;
	while(list_entry != display_list) {
		SWF_OBJECT* object = CONTAINING_RECORD(list_entry, SWF_OBJECT, list_entry);
		list_entry = list_entry->Flink;
		SWF_OBJECT_exec_action(object);
	}
}

static void SWF_DISPLAY_LIST_end1(LIST_ENTRY* display_list) {
	LIST_ENTRY* list_entry = display_list->Flink;
	while(list_entry != display_list) {
		SWF_OBJECT* object = CONTAINING_RECORD(list_entry, SWF_OBJECT, list_entry);
		list_entry = list_entry->Flink;
		object->object_state |= SWF_OBJECT_STATE_END;
	}
}

static void SWF_DISPLAY_LIST_end2(LIST_ENTRY* display_list) {
	LIST_ENTRY* list_entry = display_list->Flink;
	while(list_entry != display_list) {
		SWF_OBJECT* object = CONTAINING_RECORD(list_entry, SWF_OBJECT, list_entry);
		list_entry = list_entry->Flink;
		if(object->object_state & SWF_OBJECT_STATE_END) {
			SWF_OBJECT_free(object); /* この中でRemoveEntryList(&object->list_entry)が呼ばれる */
		}
	}
}

#ifdef PIECE
static void SWF_DISPLAY_LIST_draw(LIST_ENTRY* display_list, RENDER* render, const mat2f* m) {
	LIST_ENTRY* list_entry = display_list->Flink;
	while(list_entry != display_list) {
		SWF_OBJECT* object = CONTAINING_RECORD(list_entry, SWF_OBJECT, list_entry);
		list_entry = list_entry->Flink;
		SWF_OBJECT_draw(object, render, m);
	}
}
#endif /*PIECE*/

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

static void SWF_ACTION_LIST_add_action(LIST_ENTRY* action_list, const unsigned char* Actions) {
	SWF_ACTION* action = calloc(1, sizeof(SWF_ACTION));
	if(!action) {
		DIE(); /* メモリ不足 */
	}
	action->Actions = Actions;
	InsertTailList(action_list, &action->list_entry);
}

static const unsigned char* SWF_ACTION_LIST_get_action(LIST_ENTRY* action_list) {
	const unsigned char* Actions;
	if(!IsListEmpty(action_list)) {
		LIST_ENTRY* list_entry = RemoveHeadList(action_list);
		SWF_ACTION* action = CONTAINING_RECORD(list_entry, SWF_ACTION, list_entry);
		Actions = action->Actions;
		free(action);
	} else {
		Actions = NULL;
	}
	return Actions;
}

static void SWF_ACTION_LIST_free(LIST_ENTRY* action_list) {
	while(SWF_ACTION_LIST_get_action(action_list)) {
		/** no job **/
	}
}

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

static void SWF_OBJECT_free(SWF_OBJECT* self) {
	SWF_DISPLAY_LIST_free(&self->display_list);
	SWF_ACTION_LIST_free(&self->action_list);
	RemoveEntryList(&self->list_entry);
	free(self);
}

static void SWF_OBJECT_exec(SWF_OBJECT* self) {
	if(self->object_state & SWF_OBJECT_STATE_PLAY) {
		int TagCodeAndLength;
		int tag_type;
		int tag_length;
		const void* tag_data;
		do {
			void (*fn)(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream) = NULL;
			TagCodeAndLength = SWF_BIT_STREAM_get_UI16(&self->bit_stream);
			tag_type   = TagCodeAndLength >> 6;
			tag_length = TagCodeAndLength & 63;
			if(tag_length == 63) {
				tag_length = SWF_BIT_STREAM_get_SI32(&self->bit_stream); /* Length */
			}
			tag_data = SWF_BIT_STREAM_get_data(&self->bit_stream, tag_length);
			switch(tag_type) {
			case  0: //  0 End
				/* Endでループする時、すぐディスプレイリストを開放せずに、SWF_OBJECT_STATE_ENDをマークしておきます。 */
				SWF_DISPLAY_LIST_end1(&self->display_list);
				self->bit_stream.bit_pos = 0; /* ループ */
				if(!self->character->character_type) { /* 唯一のルートオブジェクトだったら… */
					swf.swf_info.loop_count++;     /* ループ回数をカウントアップします。 */
				}
				break;
			case  2: //  2 DefineShape
				fn = SWF_OBJECT_DefineShape;
				break;
			case  6: //  6 DefineBits
				fn = SWF_OBJECT_DefineBits;
				break;
			case  8: //  8 JPEGTables
				fn = SWF_OBJECT_JPEGTables;
				break;
			case  9: //  9 SetBackgroundColor
				fn = SWF_OBJECT_SetBackgroundColor;
				break;
			case 10: // 10 DefineFont
				fn = SWF_OBJECT_DefineFont;
				break;
			case 11: // 11 DefineText
				fn = SWF_OBJECT_DefineText;
				break;
			case 12: // 12 DoAction
				fn = SWF_OBJECT_DoAction;
				break;
			case 13: // 13 DefineFontInfo
				fn = SWF_OBJECT_DefineFontInfo;
				break;
			case 20: // 20 DefineBitsLossless
				fn = SWF_OBJECT_DefineBitsLossless;
				break;
			case 21: // 21 DefineBitsJPEG2
				fn = SWF_OBJECT_DefineBitsJPEG2;
				break;
			case 22: // 22 DefineShape2
				fn = SWF_OBJECT_DefineShape2;
				break;
			case 26: // 26 PlaceObject2
				fn = SWF_OBJECT_PlaceObject2;
				break;
			case 28: // 28 RemoveObject2
				fn = SWF_OBJECT_RemoveObject2;
				break;
			case 32: // 32 DefineShape3
				fn = SWF_OBJECT_DefineShape3;
				break;
			case 33: // 33 DefineText2
				fn = SWF_OBJECT_DefineText2;
				break;
			case 35: // 35 DefineBitsJPEG3
				fn = SWF_OBJECT_DefineBitsJPEG3;
				break;
			case 36: // 36 DefineBitsLossless2
				fn = SWF_OBJECT_DefineBitsLossless2;
				break;
			case 37: // 37 DefineEditText
				fn = SWF_OBJECT_DefineEditText;
				break;
			case 39: // 39 DefineSprite
				fn = SWF_OBJECT_DefineSprite;
				break;
			case 48: // 48 DefineFont2
				fn = SWF_OBJECT_DefineFont2;
				break;
			}
			if(fn) {
				SWF_BIT_STREAM tag_bit_stream = { tag_data };
				(*fn)(self, &tag_bit_stream);
				if(tag_bit_stream.bit_pos > tag_length << 3) {
					DIE(); /* タグの読み出しが終端を超過した。ここで止まったらたぶんこのプログラムのバグ?です */
				}
			}
		} while(tag_type != 1); //  1 ShowFrame
		/* Endでループした直後、「最初のShowFrameまでの間に、タイムラインを継続したスプライト」*以外*を、実際に開放します。
		 * Endでループした直後でなければ、SWF_OBJECT_STATE_ENDをマークしたオブジェクトが無いので、無駄ですが支障ありません。
		 */
		SWF_DISPLAY_LIST_end2(&self->display_list);
	}
	SWF_DISPLAY_LIST_exec(&self->display_list);
}

static void SWF_OBJECT_exec_action(SWF_OBJECT* self) {
	const unsigned char* Actions;
	while((Actions = SWF_ACTION_LIST_get_action(&self->action_list)) != NULL) {
		//{{ 12 DoAction
		SWF_BIT_STREAM bit_stream = { Actions };
		SWF_OBJECT* target = self;
		for(;;) {
			int ActionCode = SWF_BIT_STREAM_get_UI8(&bit_stream);
			if(!ActionCode) { // 0 ActionEndFlag
				break;
			} else {
				int Length = (ActionCode >= 0x80) ? SWF_BIT_STREAM_get_UI16(&bit_stream) : 0;
				const void* action_data = SWF_BIT_STREAM_get_data(&bit_stream, Length);
				SWF_BIT_STREAM action_bit_stream = { action_data };
				switch(ActionCode) {
				//case 0x81: // 0x81 ActionGotoFrame
				//case 0x83: // 0x83 ActionGetURL
				//case 0x04: // 0x04 ActionNextFrame
				//case 0x05: // 0x05 ActionPreviousFrame
				case 0x06: // 0x06 ActionPlay
					{
						target->object_state |= SWF_OBJECT_STATE_PLAY;
					}
					break;
				case 0x07: // 0x07 ActionStop
					{
						target->object_state &= ~SWF_OBJECT_STATE_PLAY;
					}
					break;
				//case 0x08: // 0x08 ActionToggleQuality
				//case 0x09: // 0x09 ActionStopSounds
				//case 0x8A: // 0x8A ActionWaitForFrame
				case 0x8B: // 0x8B ActionSetTarget
					{
						const char* TargetName = SWF_BIT_STREAM_get_STRING(&action_bit_stream);
						char str[256]; /* 充分な長さ */
						char* p;
						target = self;
						if(strlen(TargetName) > sizeof str - 1) {
							DIE(); /* パスが長すぎる */
						}
						p = strncpy(str, TargetName, sizeof str);
						if(*p == '/') {
							while(target->parent) {
								target = target->parent;
							}
							//p++; 最初のstrtok()で自動的にスキップするので、この処理は不要
						}
						p = strtok(p, "/");
						while(p) {
							if(!strcmp(p, "..")) {
								if(!target->parent) {
									DIE(); /* 不正なパス */
								}
								target = target->parent;
							} else {
								LIST_ENTRY* list_head = &target->display_list;
								LIST_ENTRY* list_entry = list_head->Flink;
								SWF_OBJECT* object;
								for(;;) {
									if(list_entry == list_head) {
										DIE(); /* 不正なパス */
									}
									object = CONTAINING_RECORD(list_entry, SWF_OBJECT, list_entry);
									list_entry = list_entry->Flink;
									if(!strcmp(object->Name, p)) {
										target = object;
										break;
									}
								}
							}
							p = strtok(NULL, "/");
						}
					}
					break;
				//case 0x8C: // 0x8C ActionGoToLabel
				}
			}
		}
		//}} 12 DoAction
	}
	SWF_DISPLAY_LIST_exec_action(&self->display_list);
}

#ifdef PIECE
static void SWF_SHAPE_draw(SWF_SHAPE* self, RENDER* render, const mat2f* m) {
	do {
		mat2f m1 = *m;
		mat2f m2 = {
			self->BitmapMatrix.ScaleX, self->BitmapMatrix.RotateSkew1, self->BitmapMatrix.TranslateX,
			self->BitmapMatrix.RotateSkew0, self->BitmapMatrix.ScaleY, self->BitmapMatrix.TranslateY,
		};
		mat2f_xform(&m1, &m2);
		if(m1.a00 == 1.0f && m1.a01 == 0.0f &&
		   m1.a10 == 0.0f && m1.a11 == 1.0f) {
			/* 拡縮回転無しで、平行移動だけの場合は、等倍描画を使うことにします。
			 * 平行移動だけの場合の方が多いと思うので、高速化と見栄えのためです。
			 */
			TEXTURE* texture = &self->bitmap->texture;
			int x = floorf(m1.a02 + 0.5f);
			int y = floorf(m1.a12 + 0.5f);
			int w = texture->header.w;
			int h = texture->header.h;
			render->context->texture = texture;
			render_object(render, x, y, 0, 0, w, h, DRW_NOMAL);
		} else {
			TEXTURE* texture = &self->bitmap->texture;
			int w = texture->header.w;
			int h = texture->header.h;
			VERTEX vertex[4] = {
				{ 0, 0,     0,     0 },
				{ w, 0, w - 1,     0 },
				{ w, h, w - 1, h - 1 },
				{ 0, h,     0, h - 1 },
			};
			int i;
			for(i = 0; i < 4; i++) {
				vec2f v;
				v.x = vertex[i].x;
				v.y = vertex[i].y;
				vec2f_xform(&m1, &v);
				vertex[i].x = floorf(v.x + 0.5f);
				vertex[i].y = floorf(v.y + 0.5f);
			}
			render->context->texture = texture;
			render_polygon(render, vertex, 4, 0);
		}
		self = self->next;
	} while(self);
}

static void SWF_TEXT_draw_using_CodeTable(SWF_TEXT* self, RENDER* render, const mat2f* m) {
	SWF_BIT_STREAM bit_stream = { self->TextRecords };
	int StyleFlagsHasFont;
	int StyleFlagsHasColor;
	int StyleFlagsHasYOffset;
	int StyleFlagsHasXOffset;
	int FontId;
	SWF_FONT* font = NULL; /*警告抑制*/
	SWF_RGBA TextColor;
	float XOffset = 0.0f; /*警告抑制*/
	float YOffset = 0.0f; /*警告抑制*/
	float TextHeight = 0.0f; /*警告抑制*/
	int GlyphCount;
	int GlyphIndex;
	float GlyphAdvance;
	int i;
	int code;
	int x;
	int y;
	int color;
	unsigned short (*fn)(RENDER* render, int x, int y, unsigned short code, int type, int color);
	mat2f m1 = *m;
	mat2f m2 = {
		self->TextMatrix.ScaleX, self->TextMatrix.RotateSkew1, self->TextMatrix.TranslateX,
		self->TextMatrix.RotateSkew0, self->TextMatrix.ScaleY, self->TextMatrix.TranslateY,
	};
	mat2f_xform(&m1, &m2);

	for(;;) {
		if(!SWF_BIT_STREAM_get_UB(&bit_stream, 1)) { /* TextRecordType */
			break;
		}
		SWF_BIT_STREAM_get_UB(&bit_stream, 3); /* StyleFlagsReserved */
		StyleFlagsHasFont    = SWF_BIT_STREAM_get_UB(&bit_stream, 1);
		StyleFlagsHasColor   = SWF_BIT_STREAM_get_UB(&bit_stream, 1);
		StyleFlagsHasYOffset = SWF_BIT_STREAM_get_UB(&bit_stream, 1);
		StyleFlagsHasXOffset = SWF_BIT_STREAM_get_UB(&bit_stream, 1);
		if(StyleFlagsHasFont) {
			FontId = SWF_BIT_STREAM_get_UI16(&bit_stream);
			font = (SWF_FONT*)SWF_DICTIONARY_get_character(FontId);
			if(font && (font->character.character_type != SWF_CHARACTER_TYPE_FONT)) {
				DIE(); /* 不正なデータ */
			}
		}
		if(StyleFlagsHasColor) {
			if(self->has_alpha) {
				SWF_BIT_STREAM_get_RGBA(&bit_stream, &TextColor);
			} else {
				SWF_BIT_STREAM_get_RGB(&bit_stream, (SWF_RGB*)&TextColor);
			}
		}
		if(StyleFlagsHasXOffset) {
			XOffset = SWF_BIT_STREAM_get_SI16(&bit_stream) / 20.0f;
		}
		if(StyleFlagsHasYOffset) {
			YOffset = SWF_BIT_STREAM_get_SI16(&bit_stream) / 20.0f;
		}
		if(StyleFlagsHasFont) {
			TextHeight = SWF_BIT_STREAM_get_SI16(&bit_stream) / 20.0f;
		}
		GlyphCount = SWF_BIT_STREAM_get_UI8(&bit_stream);
		/* GlyphEntries */
		for(i = 0; i < GlyphCount; i++) {
			GlyphIndex   = SWF_BIT_STREAM_get_UB(&bit_stream, self->GlyphBits);
			GlyphAdvance = SWF_BIT_STREAM_get_UB(&bit_stream, self->AdvanceBits) / 20.0f;
			if(font && font->CodeTable) {
				/* * Wed Sep 02 13:41:37 JST 2009 Naoyuki Sawa
				 * - 「SWF File Format Specification Version 10」の191ページ下段に、以下の説明があります。
				 *   │The XOffset field defines the offset from the left of the TextBounds rectangle to the reference point of the glyph.
				 *   │The YOffset field defines the offset from the top of the TextBounds rectangle to the reference point of the glyph.
				 *   XOffset,YOffsetはTextBoundsに相対となっていますが、実際のデータを調べたところ、オブジェクト原点に相対と思われます。
				 *   TextBoundsは、単に描画範囲を示すヒントであり、TextBoundsの値が何であっても、XOffset,YOffsetの値は影響を受けません。
				 *   TextBoundsが単なるヒントであると仕組みは、DefineShapeにおけるShapeBoundの役割と似通っています。
				 *   おそらく、この理解で間違っていないと思います。
				 */
				vec2f v = { XOffset, YOffset - TextHeight };
				//                           ~~~~~~~~~~~~
				// 本当はアセンダ分だけ引くべきなのですが、P/ECE上ではTEXTMETRICが取得できないため、止むを得ずテキストの高さを引きます。
				// ディセンダ分が引き過ぎになりますので、P/ECE上ではテキストサイズが大きいほど上にずれてしまいますが、仕方が有りません。
				if(font->FontFlagsHasLayout) { /* レイアウト情報有り */
					v.y += TextHeight * font->FontDescent;
				}
				vec2f_xform(&m1, &v);
				x = floorf(v.x + 0.5f);
				y = floorf(v.y + 0.5f);
				color = ((((TextColor.Red * 30) + (TextColor.Green * 59) + (TextColor.Blue * 11)) / 100) >> 6) ^ 3;
				if(font->FontFlagsBold) {
					fn = render_font_framed;
					if(color <= 1) {
						color |= 0x330; /* ボールドの代わりに、逆の色で縁取り */
					}
				} else {
					fn = render_font;
				}
				if(font->FontFlagsWideCodes) {
					code = font->CodeTable[(GlyphIndex * 2) + 0] | font->CodeTable[(GlyphIndex * 2) + 1] << 8;
				} else {
					code = font->CodeTable[GlyphIndex];
				}
				(*fn)(render, x, y, code, 0, color);
			}
			XOffset += GlyphAdvance;
		}
		SWF_BIT_STREAM_get_data(&bit_stream, 0); /* バイト整列 */
	}
}

static void SWF_TEXT_draw_using_GlyphShapeTable(SWF_TEXT* self, RENDER* render, const mat2f* m) {
	SWF_BIT_STREAM bit_stream = { self->TextRecords };
	int StyleFlagsHasFont;
	int StyleFlagsHasColor;
	int StyleFlagsHasYOffset;
	int StyleFlagsHasXOffset;
	int FontId;
	SWF_FONT* font = NULL; /*警告抑制*/
	SWF_RGBA TextColor;
	float XOffset = 0.0f; /*警告抑制*/
	float YOffset = 0.0f; /*警告抑制*/
	float TextHeight = 0.0f; /*警告抑制*/
	int GlyphCount;
	int GlyphIndex;
	float GlyphAdvance;
	int i;
	int offset;
	int NumFillBits;
	int NumLineBits;
	int StraightFlag;
	int NumBits;
	float DeltaX;
	float DeltaY;
	float ControlDeltaX;
	float ControlDeltaY;
	float AnchorDeltaX;
	float AnchorDeltaY;
	int StateNewStyles;
	int StateLineStyle;
	int StateFillStyle1;
	int StateFillStyle0;
	int StateMoveTo;
	int MoveBits;
	float MoveDeltaX;
	float MoveDeltaY;
	float x = 0.0f; /*警告抑制*/
	float y = 0.0f; /*警告抑制*/
	int n_point;				/* 頂点の通算数 */
	int n_polygon;				/* 多角形の数 */
	struct work {				/* _def_vbuff[]上に確保するワークエリアの構造体 */
		vec2i point[1024/*調整可*/];	/* 8*1024=8192 すべての多角形の頂点を入れる配列 */
		int polygon[256/*調整可*/];	/* 4* 256=1024 各多角形の頂点の数を入れる配列 */
	};					/* (合計)=9216 _def_vbuff[11264]を超えないこと */
	#define point	(((struct work*)_def_vbuff)->point)
	#define polygon	(((struct work*)_def_vbuff)->polygon)
	mat2f m1 = *m;
	mat2f m2 = {
		self->TextMatrix.ScaleX, self->TextMatrix.RotateSkew1, self->TextMatrix.TranslateX,
		self->TextMatrix.RotateSkew0, self->TextMatrix.ScaleY, self->TextMatrix.TranslateY,
	};
	mat2f_xform(&m1, &m2);

	for(;;) {
		if(!SWF_BIT_STREAM_get_UB(&bit_stream, 1)) { /* TextRecordType */
			break;
		}
		SWF_BIT_STREAM_get_UB(&bit_stream, 3); /* StyleFlagsReserved */
		StyleFlagsHasFont    = SWF_BIT_STREAM_get_UB(&bit_stream, 1);
		StyleFlagsHasColor   = SWF_BIT_STREAM_get_UB(&bit_stream, 1);
		StyleFlagsHasYOffset = SWF_BIT_STREAM_get_UB(&bit_stream, 1);
		StyleFlagsHasXOffset = SWF_BIT_STREAM_get_UB(&bit_stream, 1);
		if(StyleFlagsHasFont) {
			FontId = SWF_BIT_STREAM_get_UI16(&bit_stream);
			font = (SWF_FONT*)SWF_DICTIONARY_get_character(FontId);
			if(font && (font->character.character_type != SWF_CHARACTER_TYPE_FONT)) {
				DIE(); /* 不正なデータ */
			}
		}
		if(StyleFlagsHasColor) {
			if(self->has_alpha) {
				SWF_BIT_STREAM_get_RGBA(&bit_stream, &TextColor);
			} else {
				SWF_BIT_STREAM_get_RGB(&bit_stream, (SWF_RGB*)&TextColor);
			}
		}
		if(StyleFlagsHasXOffset) {
			XOffset = SWF_BIT_STREAM_get_SI16(&bit_stream) / 20.0f;
		}
		if(StyleFlagsHasYOffset) {
			YOffset = SWF_BIT_STREAM_get_SI16(&bit_stream) / 20.0f;
		}
		if(StyleFlagsHasFont) {
			TextHeight = SWF_BIT_STREAM_get_SI16(&bit_stream) / 20.0f;
		}
		GlyphCount = SWF_BIT_STREAM_get_UI8(&bit_stream);
		/* GlyphEntries */
		for(i = 0; i < GlyphCount; i++) {
			GlyphIndex   = SWF_BIT_STREAM_get_UB(&bit_stream, self->GlyphBits);
			GlyphAdvance = SWF_BIT_STREAM_get_UB(&bit_stream, self->AdvanceBits) / 20.0f;
			if(font && font->OffsetTable) {
				if(font->FontFlagsWideOffsets) {
					offset = font->OffsetTable[(GlyphIndex * 4) + 0]
					       | font->OffsetTable[(GlyphIndex * 4) + 1] <<  8
					       | font->OffsetTable[(GlyphIndex * 4) + 2] << 16
					       | font->OffsetTable[(GlyphIndex * 4) + 3] << 24;
				} else {
					offset = font->OffsetTable[(GlyphIndex * 2) + 0]
					       | font->OffsetTable[(GlyphIndex * 2) + 1] <<  8;
				}
				{
					SWF_BIT_STREAM shape_bit_stream = { font->OffsetTable + offset };
					n_point = 0;
					n_polygon = 0;
					/* SHAPE */
					NumFillBits = SWF_BIT_STREAM_get_UB(&shape_bit_stream, 4);
					NumLineBits = SWF_BIT_STREAM_get_UB(&shape_bit_stream, 4);
					for(;;) {
						if(SWF_BIT_STREAM_get_UB(&shape_bit_stream, 1)) { /* TypeFlag */
							StraightFlag = SWF_BIT_STREAM_get_UB(&shape_bit_stream, 1);
							NumBits      = SWF_BIT_STREAM_get_UB(&shape_bit_stream, 4) + 2;
							if(StraightFlag) {
								/* STRAIGHTEDGERECORD */
								if(SWF_BIT_STREAM_get_UB(&shape_bit_stream, 1)) {          /* GeneralLineFlag */
									DeltaX = SWF_BIT_STREAM_get_SB(&shape_bit_stream, NumBits) / 1024.0f;
									DeltaY = SWF_BIT_STREAM_get_SB(&shape_bit_stream, NumBits) / 1024.0f;
								} else {
									if(SWF_BIT_STREAM_get_UB(&shape_bit_stream, 1)) { /* VertLineFlag */
										DeltaY = SWF_BIT_STREAM_get_SB(&shape_bit_stream, NumBits) / 1024.0f;
										DeltaX = 0.0f;
									} else {
										DeltaX = SWF_BIT_STREAM_get_SB(&shape_bit_stream, NumBits) / 1024.0f;
										DeltaY = 0.0f;
									}
								}
								x += DeltaX * TextHeight;
								y += DeltaY * TextHeight;
							} else {
								/* CURVEDEDGERECORD */
								ControlDeltaX = SWF_BIT_STREAM_get_SB(&shape_bit_stream, NumBits) / 1024.0f;
								ControlDeltaY = SWF_BIT_STREAM_get_SB(&shape_bit_stream, NumBits) / 1024.0f;
								x += ControlDeltaX * TextHeight;
								y += ControlDeltaY * TextHeight;
								/* 本当はベジェ曲線なのですが、手抜きして、制御点を抜いて始点-終点を直線で結んでしまうことにしました。
								 * 始点-制御点-終点を直線で結ぶ方法もありますが、始点-終点をダイレクトに結ぶ方が綺麗^H^Hマシに見えるようです。
								 */
								AnchorDeltaX  = SWF_BIT_STREAM_get_SB(&shape_bit_stream, NumBits) / 1024.0f;
								AnchorDeltaY  = SWF_BIT_STREAM_get_SB(&shape_bit_stream, NumBits) / 1024.0f;
								x += AnchorDeltaX * TextHeight;
								y += AnchorDeltaY * TextHeight;
							}
							/* 現在の多角形に頂点を追加する */
							if(n_polygon) {
								if(n_point < ARRAY_SIZE(point)) {
									vec2f v = { x, y };
									vec2f_xform(&m1, &v);
									point[n_point].x = floorf(v.x + 0.5f);
									point[n_point].y = floorf(v.y + 0.5f);
									n_point++;
									polygon[n_polygon - 1]++;
								}
							}
						} else {
							/* 現在の多角形を終了する */
							if(n_polygon) {
								/* フォントのシェイプは、終点が始点と同じ座標に戻る、明示的に閉じたパスとしてデータ格納されている。
								 * 描画処理においては無駄なので、(始点と同じ座標であるはずの)終点を削除する。
								 */
								if(polygon[n_polygon - 1]) {
									n_point--; /* 忘れないで!! */
									polygon[n_polygon - 1]--;
								}
								/* 上記の処理の結果、頂点数が3未満ならば、多角形として無意味なので、この多角形をキャンセルする。 */
								if(polygon[n_polygon - 1] < 3) {
									n_point -= polygon[n_polygon - 1]; /* この多角形が使っていた頂点を全て破棄。忘れないで!! */
									n_polygon--;
								}
							}
							StateNewStyles  = SWF_BIT_STREAM_get_UB(&shape_bit_stream, 1);
							StateLineStyle  = SWF_BIT_STREAM_get_UB(&shape_bit_stream, 1);
							StateFillStyle1 = SWF_BIT_STREAM_get_UB(&shape_bit_stream, 1);
							StateFillStyle0 = SWF_BIT_STREAM_get_UB(&shape_bit_stream, 1);
							StateMoveTo     = SWF_BIT_STREAM_get_UB(&shape_bit_stream, 1);
							if(StateNewStyles || StateLineStyle || StateFillStyle1 || StateFillStyle0 || StateMoveTo) {
								/* STYLECHANGERECORD */
								if(StateMoveTo) {
									MoveBits   = SWF_BIT_STREAM_get_UB(&shape_bit_stream, 5);
									MoveDeltaX = SWF_BIT_STREAM_get_SB(&shape_bit_stream, MoveBits) / 1024.0f;
									MoveDeltaY = SWF_BIT_STREAM_get_SB(&shape_bit_stream, MoveBits) / 1024.0f;
									/* - 「SWF File Format Specification Version 10」の136ページ中断に、以下の説明があります。
									 *   ｜ In the first shape record MoveDeltaX and MoveDeltaY are relative to the shape origin.
									 *   ｜ In subsequent shape records, MoveDeltaX and MoveDeltaY are relative to the current drawing position.
									 *   この説明は間違っており、MoveDeltaXとMoveDeltaYは、常に、シェイプ原点からの相対座標です。
									 */
									x = XOffset + MoveDeltaX * TextHeight;
									y = YOffset + MoveDeltaY * TextHeight;
								}
								if(StateFillStyle0) {
									SWF_BIT_STREAM_get_UB(&shape_bit_stream, NumFillBits); /* FillStyle0 */
								}
								if(StateFillStyle1) {
									SWF_BIT_STREAM_get_UB(&shape_bit_stream, NumFillBits); /* FillStyle1 */
								}
								if(StateLineStyle) {
									SWF_BIT_STREAM_get_UB(&shape_bit_stream, NumLineBits); /* LineStyle */
								}
								if(StateNewStyles) {
									DIE(); /* 有り得ない */
								}
							} else {
								/* ENDSHAPERECORD */
								break;
							}
							/* 新しい多角形を開始する。 */
							if(n_polygon == ARRAY_SIZE(polygon)) {
								break;
							}
							polygon[n_polygon++] = 0;
							/* 現在の多角形に頂点を追加する */
							if(n_point < ARRAY_SIZE(point)) {
								vec2f v = { x, y };
								vec2f_xform(&m1, &v);
								point[n_point].x = floorf(v.x + 0.5f);
								point[n_point].y = floorf(v.y + 0.5f);
								n_point++;
								polygon[n_polygon - 1]++;
							}
						}
					}
					/* ALTERNATEモードのPolyPolygonとして、文字を描画 */
					if(n_polygon) { /* 空白(' ')フォントが、空図形として格納されるようです。スキップします。 */
						int color = ((((TextColor.Red * 30) + (TextColor.Green * 59) + (TextColor.Blue * 11)) / 100) >> 6) ^ 3;
						render_vec2i_alternate_concaves(render, point, polygon, n_polygon, color); /* n_polygon=0でこの関数を呼ぶとハングアップするので注意!! */
					}
				}
			}
			XOffset += GlyphAdvance;
		}
		SWF_BIT_STREAM_get_data(&bit_stream, 0); /* バイト整列 */
	}

	#undef point
	#undef polygon
}

static void SWF_EDIT_TEXT_draw(SWF_EDIT_TEXT* self, RENDER* render, const mat2f* m) {
	/* * Tue Sep 08 23:06:41 JST 2009 Naoyuki Sawa
	 * - シェイプのShapeBoundsや、静止テキストのTextBoundsは、実効描画範囲を示すヒントの役割しか持たず、無視することができましたが、
	 *   ダイナミックテキストのBoundsは、座標オフセットを示す役割を持っており、無視することはできないようです。
	 *   シェイプや静止テキストと違って、ダイナミックテキストは行列を持たない代わりに、Boundsが座標オフセットを持っているようです。
	 * - Flash MXでデータを作っていると、Bounds.XminとPlaceObjectのX座標がそれぞれ大きな値を持ち、相殺するようなデータが生成されることがあります。
	 *   Boundsを無視すると、そのようなデータが正しく描画できません。
	 * - たとえば、Flash MXで以下のような編集作業を行うと、上述のようなデータが生成されます。
	 *   1. ステージ中央に、50ドットのフォントで、1文字のダイナミックテキストを配置する。テキスト矩形は、1文字ぴったりのサイズとする。
	 *   2. 右揃えに変更する。テキスト矩形が1文字ぴったりなので、見た目は変化しない。
	 *   3. 100ドットのフォントに変更する。テキスト矩形は、左下方向に拡大される。
	 *   4. 左揃えに戻す。
	 *   5. ダイナミックテキストの座標を、X = 0.0, Y = 0.0 と入力する。
	 *   以上の編集を行った後、パブリッシュすると、以下のようなデータが生成されます。
	 *     DefineEditText : Rectangle = { Xmin = -1040, Xmax = 1044, Ymin = -40, Ymax = 2040 }
	 *     PlaceObject2   : Matrix    = { TranslateX = 1040, TranslateY = 40 }
	 */
	int x = floorf(self->Bounds.Xmin + m->a02 + 0.5f);
	int y = floorf(self->Bounds.Ymin + m->a12 + 0.5f);
	int color = self->color;
	unsigned int (*fn)(RENDER* render, int x, int y, const char* str, int type, int color);
	if(self->font->FontFlagsBold) {
		fn = render_string_framed;
		if(color <= 1) {
			color |= 0x330; /* ボールドの代わりに、逆の色で縁取り */
		}
	} else {
		fn = render_string;
	}
	(*fn)(render, x, y, self->InitialText, 0, color);
}

static void SWF_OBJECT_draw(SWF_OBJECT* self, RENDER* render, const mat2f* m) {
	mat2f m1 = *m;
	mat2f m2 = {
		self->Matrix.ScaleX, self->Matrix.RotateSkew1, self->Matrix.TranslateX,
		self->Matrix.RotateSkew0, self->Matrix.ScaleY, self->Matrix.TranslateY,
	};
	mat2f_xform(&m1, &m2);
	switch(self->character->character_type) {
	case SWF_CHARACTER_TYPE_SHAPE:
		SWF_SHAPE_draw((SWF_SHAPE*)self->character, render, &m1);
		break;
	case SWF_CHARACTER_TYPE_TEXT:
		switch(swf.text_drawing_method) {
		case 0: /* using CodeTable */
			SWF_TEXT_draw_using_CodeTable((SWF_TEXT*)self->character, render, &m1);
			break;
		case 1: /* using GlyphShapeTable */
			SWF_TEXT_draw_using_GlyphShapeTable((SWF_TEXT*)self->character, render, &m1);
			break;
		default:
			DIE(); /* swf_set_text_drawing_method()の呼び出しが不正 */
		}
		break;
	case SWF_CHARACTER_TYPE_EDIT_TEXT:
		SWF_EDIT_TEXT_draw((SWF_EDIT_TEXT*)self->character, render, &m1);
		break;
	}
	SWF_DISPLAY_LIST_draw(&self->display_list, render, &m1);
}
#endif /*PIECE*/

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

static void SWF_OBJECT_SetBackgroundColor(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream) { //  9 SetBackgroundColor
	SWF_BIT_STREAM_get_RGB(bit_stream, (SWF_RGB*)&swf.swf_info.BackgroundColor);
}

static void SWF_OBJECT_DoAction(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream) { // 12 DoAction
	/* アクションスクリプトはすぐに実行せず、すべてのオブジェクトの処理がShowFrameに到達した後で、まとめて実行する仕様です。
	 * そうしないと、ターゲットとの処理順序によって、アクションスクリプトの効果が1フレームずれるなどの問題が生じるからです。
	 */
	const unsigned char* Actions = SWF_BIT_STREAM_get_data(bit_stream, 0);
	SWF_ACTION_LIST_add_action(&self->action_list, Actions);
}

static void SWF_OBJECT_DefineShape_2_3(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream, int has_alpha) {
	int CharacterId;
	int FillStyleCount;
	int FillStyleType;
	int BitmapId;
	SWF_MATRIX BitmapMatrix;
	int LineStyleCount;
	int NumFillBits;
	int NumLineBits;
	int StraightFlag;
	int NumBits;
	int StateNewStyles;
	int StateLineStyle;
	int StateFillStyle1;
	int StateFillStyle0;
	int StateMoveTo;
	int MoveBits;
	int i;
	SWF_BITMAP* bitmap;
	SWF_SHAPE* shape;
	SWF_SHAPE* prev = NULL;

	CharacterId = SWF_BIT_STREAM_get_UI16(bit_stream);
	if(SWF_DICTIONARY_get_character(CharacterId)) {
		return; /* 1ループ目で定義済み */
	}
	SWF_BIT_STREAM_get_RECT(bit_stream, NULL); /* ShapeBounds */
	/* Shapes */
	for(;;) {
		/* FillStyles */
		FillStyleCount = SWF_BIT_STREAM_get_UI8(bit_stream);
		if(FillStyleCount == 255) {
			FillStyleCount = SWF_BIT_STREAM_get_UI16(bit_stream); /* FillStyleCountExtended */
		}
		for(i = 0; i < FillStyleCount; i++) {
			FillStyleType = SWF_BIT_STREAM_get_UI8(bit_stream);
			switch(FillStyleType >> 4) {
			case 0: // 0x00 = solid fill
				if(has_alpha) {
					SWF_BIT_STREAM_get_RGBA(bit_stream, NULL); /* Color */
				} else {
					SWF_BIT_STREAM_get_RGB(bit_stream, NULL); /* Color */
				}
				break;
			case 1: // 0x10 = linear gradient fill, 0x12 = radial gradient fill, 0x13 = focal radial gradient fill
				return; /* 未対応 */
			case 4: // 0x40 = repeating bitmap fill, 0x41 = clipped bitmap fill, 0x42 = non-smoothed repeating bitmap, 0x43 = non-smoothed clipped bitmap
				BitmapId = SWF_BIT_STREAM_get_UI16(bit_stream);
				SWF_BIT_STREAM_get_MATRIX(bit_stream, &BitmapMatrix);
				BitmapMatrix.ScaleX      /= 20.0f;
				BitmapMatrix.ScaleY      /= 20.0f;
				BitmapMatrix.RotateSkew0 /= 20.0f;
				BitmapMatrix.RotateSkew1 /= 20.0f;
				bitmap = (SWF_BITMAP*)SWF_DICTIONARY_get_character(BitmapId);
				if(bitmap) {
					if(bitmap->character.character_type != SWF_CHARACTER_TYPE_BITMAP) {
						DIE(); /* 不正なデータ */
					}
					shape = calloc(1, sizeof(SWF_SHAPE));
					if(!shape) {
						DIE(); /* メモリ不足 */
					}
					/*{{初期化*/
					shape->character.character_type = SWF_CHARACTER_TYPE_SHAPE;
					shape->character.CharacterId    = CharacterId;
					shape->bitmap                   = bitmap;
					shape->BitmapMatrix             = BitmapMatrix;
					/*}}初期化*/
					InsertTailList(&swf.dictionary, &shape->character.list_entry);
					if(prev) {
						prev->next = shape;
					}
					prev = shape;
				}
				break;
			default:
				return; /* 不明なFillStyleType */
			}
		}
		/* LineStyles */
		LineStyleCount = SWF_BIT_STREAM_get_UI8(bit_stream);
		if(LineStyleCount == 255) {
			LineStyleCount = SWF_BIT_STREAM_get_UI16(bit_stream); /* LineStyleCountExtended */
		}
		for(i = 0; i < LineStyleCount; i++) {
			SWF_BIT_STREAM_get_UI16(bit_stream); /* Width */
			if(has_alpha) {
				SWF_BIT_STREAM_get_RGBA(bit_stream, NULL); /* Color */
			} else {
				SWF_BIT_STREAM_get_RGB(bit_stream, NULL); /* Color */
			}
		}
		/* NumFillBits */
		NumFillBits = SWF_BIT_STREAM_get_UB(bit_stream, 4);
		/* NumLineBits */
		NumLineBits = SWF_BIT_STREAM_get_UB(bit_stream, 4);
		/* ShapeRecords */
		for(;;) {
			/* * Sun Jun 14 16:50:39 JST 2009 Naoyuki Sawa
			 * - 「SWF File Format Specification Version 10」の134ページ下段に、以下の説明があります。
			 *   ｜ Each individual shape record is byte-aligned within an array of shape records;
			 *   ｜ one shape record is padded to a byte boundary before the next shape record begins.
			 *   この説明を読むと、各ShapeRecordsに対してバイト整列が必要に思うのですが、実際にそうすると、正しくデータを読み込めませんでした。
			 *   バイト整列を行わなければ、ちょうどタグデータサイズに一致して読み込みを完了できたので、バイト整列を行わないのが正解のようです。
			 * - 「#afunikki(http://afunishi.blog69.fc2.com/)」さんの、2008/11/20の記事(http://afunishi.blog69.fc2.com/blog-entry-360.html)に、
			 *   この件が説明されていました。引用させていただきます。
			 *   ｜ swf仕様書のShape Recordsの説明で
			 *   ｜   Each individual shape record is byte-aligned within an array of shape records;
			 *   ｜   one shape record is padded to a byte boundary before the next shape record begins. 
			 *   ｜ と書いてありますが、実際のところ、レコードが半端なところで終わっても、そのまま続けて次のレコードのビットを置いていって、
			 *   ｜ 最後の end レコードの後だけ原則通りにbyte-aligned にすればOKです。
			 *   ｜ swf version7の仕様書には同等の記述がなかったのですが、ただのミスなのかな？
			 *   やはり、各ShapeRecordsに対してバイト整列を行わないのが正解のようです。
			 */
			//SWF_BIT_STREAM_get_data(bit_stream, 0); → 各ShapeRecordsに対してバイト整列を行ってはいけない。上記コメント参照。
			{
				if(SWF_BIT_STREAM_get_UB(bit_stream, 1)) { /* TypeFlag */
					StraightFlag = SWF_BIT_STREAM_get_UB(bit_stream, 1);
					NumBits      = SWF_BIT_STREAM_get_UB(bit_stream, 4) + 2;
					if(StraightFlag) {
						/* STRAIGHTEDGERECORD */
						if(SWF_BIT_STREAM_get_UB(bit_stream, 1)) {          /* GeneralLineFlag */
							SWF_BIT_STREAM_get_SB(bit_stream, NumBits); /* DeltaX */
							SWF_BIT_STREAM_get_SB(bit_stream, NumBits); /* DeltaY */
						} else {
							SWF_BIT_STREAM_get_UB(bit_stream, 1);       /* VertLineFlag */
							SWF_BIT_STREAM_get_SB(bit_stream, NumBits); /* DeltaX or DeltaY */
						}
					} else {
						/* CURVEDEDGERECORD */
						SWF_BIT_STREAM_get_SB(bit_stream, NumBits); /* ControlDeltaX */
						SWF_BIT_STREAM_get_SB(bit_stream, NumBits); /* ControlDeltaY */
						SWF_BIT_STREAM_get_SB(bit_stream, NumBits); /* AnchorDeltaX */
						SWF_BIT_STREAM_get_SB(bit_stream, NumBits); /* AnchorDeltaY */
					}
				} else {
					StateNewStyles  = SWF_BIT_STREAM_get_UB(bit_stream, 1);
					StateLineStyle  = SWF_BIT_STREAM_get_UB(bit_stream, 1);
					StateFillStyle1 = SWF_BIT_STREAM_get_UB(bit_stream, 1);
					StateFillStyle0 = SWF_BIT_STREAM_get_UB(bit_stream, 1);
					StateMoveTo     = SWF_BIT_STREAM_get_UB(bit_stream, 1);
					if(StateNewStyles || StateLineStyle || StateFillStyle1 || StateFillStyle0 || StateMoveTo) {
						/* STYLECHANGERECORD */
						if(StateMoveTo) {
							MoveBits = SWF_BIT_STREAM_get_UB(bit_stream, 5);
							SWF_BIT_STREAM_get_SB(bit_stream, MoveBits); /* MoveDeltaX */
							SWF_BIT_STREAM_get_SB(bit_stream, MoveBits); /* MoveDeltaY */
						}
						if(StateFillStyle0) {
							SWF_BIT_STREAM_get_UB(bit_stream, NumFillBits); /* FillStyle0 */
						}
						if(StateFillStyle1) {
							SWF_BIT_STREAM_get_UB(bit_stream, NumFillBits); /* FillStyle1 */
						}
						if(StateLineStyle) {
							SWF_BIT_STREAM_get_UB(bit_stream, NumLineBits); /* LineStyle */
						}
						if(StateNewStyles) {
							break; /* FillStyles,LineStyles,NumFillBits,NumLineBits */
						}
					} else {
						/* ENDSHAPERECORD */
						return;
					}
				}
			}
			//SWF_BIT_STREAM_get_data(bit_stream, 0); → 各ShapeRecordsに対してバイト整列を行ってはいけない。上記コメント参照。
		}
	}
}

static void SWF_OBJECT_DefineShape(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream) { //  2 DefineShape
	SWF_OBJECT_DefineShape_2_3(self, bit_stream, 0/*has_alpha*/);
}

static void SWF_OBJECT_DefineShape2(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream) { // 22 DefineShape2
	SWF_OBJECT_DefineShape_2_3(self, bit_stream, 0/*has_alpha*/);
}

static void SWF_OBJECT_DefineShape3(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream) { // 32 DefineShape3
	SWF_OBJECT_DefineShape_2_3(self, bit_stream, 1/*has_alpha*/);
}

static void SWF_OBJECT_DefineBitsLossless_2(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream, int has_alpha) {
	int CharacterId;
	int BitmapFormat;
	int BitmapWidth;
	int BitmapHeight;
	int BitmapColorTableSize;
	const unsigned char* ZlibBitmapData;
	unsigned char* ColorTableRGB;   /*  RGB or RGBA */
	unsigned char* BitmapPixelData; /* xRGB or ARGB */
	int BitmapWidth4;
	int BitmapWidth8;
	int x;
	int y;
	int c;
	int len;
	int outlen;
	int components;
	SURFACE* surface;
	unsigned char* dst;
	unsigned char* src;

	CharacterId = SWF_BIT_STREAM_get_UI16(bit_stream);
	if(SWF_DICTIONARY_get_character(CharacterId)) {
		return; /* 1ループ目で定義済み */
	}
	BitmapFormat = SWF_BIT_STREAM_get_UI8(bit_stream);
	BitmapWidth  = SWF_BIT_STREAM_get_UI16(bit_stream);
	BitmapHeight = SWF_BIT_STREAM_get_UI16(bit_stream);
	BitmapWidth8 = (BitmapWidth + 7) & ~7; /* PIECE_BMPの仕様のため、幅を8ピクセルの倍数に切り上げる */
	surface = malloc(sizeof(SURFACE) + (BitmapWidth8 * BitmapHeight)/*vbuff*/);
	if(!surface) {
		DIE(); /* メモリ不足 */
	}
	surface->w = BitmapWidth8;
	surface->h = BitmapHeight;
	surface->vbuff = (unsigned char*)(surface + 1);

	switch(BitmapFormat) {
	case 3: // 8-bit colormapped image
		BitmapColorTableSize = SWF_BIT_STREAM_get_UI8(bit_stream) + 1;
		ZlibBitmapData       = SWF_BIT_STREAM_get_data(bit_stream, 0);
		BitmapWidth4 = (BitmapWidth + 3) & ~3; /* DefineBitsLossless(2)の仕様のため、幅を4バイトの倍数に切り上げる */
		components = has_alpha ? 4/*RGBA*/ : 3/*RGB*/;
		len = (components * BitmapColorTableSize)/*ColorTableRGB*/ + (BitmapWidth4 * BitmapHeight)/*ColormapPixelData*/;
		ColorTableRGB = malloc(len);
		if(!ColorTableRGB) {
			DIE(); /* メモリ不足 */
		}
		outlen = zlib_uncompress(ColorTableRGB, len, ZlibBitmapData, (INT_MAX / 8 - 1)/*don't care*/);
		if(outlen != len) {
			DIE(); /* 不正なデータ */
		}
		src = ColorTableRGB + (components * BitmapColorTableSize); /* ColormapPixelData */
		dst = surface->vbuff;
		for(y = 0; y < BitmapHeight; y++) {
			for(x = 0; x < BitmapWidth8; x++) {
				c = 4/*透明*/;
				if(x < BitmapWidth4) {
					int i = *src++;
					if(x < BitmapWidth) {
						unsigned char* p = &ColorTableRGB[components * i];
						int Red   = *p++;
						int Green = *p++;
						int Blue  = *p++;
						if(!has_alpha || *p/*Alpha*/) {
							c = ((((Red * 30) + (Green * 59) + (Blue * 11)) / 100) >> 6) ^ 3;
						}
					}
				}
				*dst++ = c;
			}
		}
		free(ColorTableRGB); /* 忘れないで!! */
		break;
	case 4: // 15-bit RGB image
		DIE(); /* 未対応 */
	case 5: // 24-bit RGB image or 32-bit ARGB image
		ZlibBitmapData = SWF_BIT_STREAM_get_data(bit_stream, 0);
		len = 4/*xRGB or ARGB*/ * (BitmapWidth * BitmapHeight); /* BitmapPixelData */
		BitmapPixelData = malloc(len);
		if(!BitmapPixelData) {
			DIE(); /* メモリ不足 */
		}
		outlen = zlib_uncompress(BitmapPixelData, len, ZlibBitmapData, (INT_MAX / 8 - 1)/*don't care*/);
		if(outlen != len) {
			DIE(); /* 不正なデータ */
		}
		src = BitmapPixelData;
		dst = surface->vbuff;
		for(y = 0; y < BitmapHeight; y++) {
			for(x = 0; x < BitmapWidth8; x++) {
				c = 4/*透明*/;
				if(x < BitmapWidth) {
					int Alpha = *src++; /* or Pix24Reserved (Always 0) */
					int Red   = *src++;
					int Green = *src++;
					int Blue  = *src++;
					if(!has_alpha || Alpha) {
						c = ((((Red * 30) + (Green * 59) + (Blue * 11)) / 100) >> 6) ^ 3;
					}
				}
				*dst++ = c;
			}
		}
		free(BitmapPixelData); /* 忘れないで!! */
		break;
	default:
		DIE(); /* 不正なデータ */
	}

	SWF_DICTIONARY_new_bitmap(CharacterId, surface);
	free(surface); /* 忘れないで!! */
}

static void SWF_OBJECT_DefineBitsLossless(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream) { // 20 DefineBitsLossless
	SWF_OBJECT_DefineBitsLossless_2(self, bit_stream, 0/*has_alpha*/);
}

static void SWF_OBJECT_DefineBitsLossless2(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream) { // 36 DefineBitsLossless2
	SWF_OBJECT_DefineBitsLossless_2(self, bit_stream, 1/*has_alpha*/);
}

typedef struct _SWF_OBJECT_DefineBits_JPEG2_3_USER_DATA {
	SURFACE* surface;
	const unsigned char* ImageData;
	const unsigned char* BitmapAlphaData;
} SWF_OBJECT_DefineBits_JPEG2_3_USER_DATA;

static void SWF_OBJECT_DefineBits_JPEG2_3_size_proc(int w, int h, void* _user_data) {
	SWF_OBJECT_DefineBits_JPEG2_3_USER_DATA* user_data = _user_data;
	int w8 = (w + 7) & ~7; /* PIECE_BMPの仕様のため、幅を8ピクセルの倍数に切り上げる */
	int x;
	int y;
	int c;
	int outlen;
	unsigned char* dst;
	unsigned char* src;

	if(user_data->surface) {
		DIE(); /* 不正なデータ */
	}
	user_data->surface = malloc(sizeof(SURFACE) + (w8 * h)/*vbuff*/);
	if(!user_data->surface) {
		DIE(); /* メモリ不足 */
	}
	user_data->surface->w = w8;
	user_data->surface->h = h;
	user_data->surface->vbuff = (unsigned char*)(user_data->surface + 1);

	dst = user_data->surface->vbuff;
	if(user_data->BitmapAlphaData) { // 35 DefineBitsJPEG3
		src = &user_data->surface->vbuff[(w8 - w) * h]; /* 後ろ詰めで展開 */
		outlen = zlib_uncompress(src, w * h, user_data->BitmapAlphaData, (INT_MAX / 8 - 1)/*don't care*/);
		if(outlen != w * h) {
			DIE(); /* 不正なデータ */
		}
		for(y = 0; y < h; y++) {
			for(x = 0; x < w8; x++) {
				if(x < w) {
					c = *src++;
					c = c ? 0/*不透明*/ : 4/*透明*/;
				} else {
					c = 4/*透明*/;
				}
				*dst++ = c;
			}
		}
	} else { // 21 DefineBitsJPEG2
		for(y = 0; y < h; y++) {
			for(x = 0; x < w8; x++) {
				*dst++ = (x < w) ? 0/*不透明*/ : 4/*透明*/;
			}
		}
	}
}

static void SWF_OBJECT_DefineBits_JPEG2_3_draw_proc(int x, int y, int c, void* _user_data) {
	SWF_OBJECT_DefineBits_JPEG2_3_USER_DATA* user_data = _user_data;
	unsigned char* p = &user_data->surface->vbuff[user_data->surface->w * y + x];
	if(*p == 0/*不透明*/) {
		*p = (c >> 6) ^ 3;
	}
}

static const void* SWF_OBJECT_DefineBits_JPEG2_3_data_proc(const void* data_ptr, void* _user_data) {
	SWF_OBJECT_DefineBits_JPEG2_3_USER_DATA* user_data = _user_data;
	/* 画像展開が完了していたら、データ継続しない。
	 * 新しいSWFファイルのDefineBitsJPEG2(3)の場合は、一度もデータ継続せずに、画像展開が完了するはずです。
	 * DefineBits、または、古いSWFファイルのDefineBitsJPEG2(3)の場合は、一度だけデータ継続したあとに、画像展開が完了するはずです。
	 */
	if(user_data->surface) {
		return NULL;
	}
	/* DefineBitsの場合、JPEGTablesのJPEGData(SOI〜DQT〜DHT〜EOI)から、DefineBitsのImageData(SOI〜SOF0〜SOS〜EOI)へ切り替える。 */
	if(user_data->ImageData) {
		return user_data->ImageData;
	}
	/* 古いDefineBitsJPEG2(3)の場合は、ImageDataの前半にSOI〜DQT〜DHT〜EOIしか含まれておらず、画像展開が未完了でコールバックする。
	 * その場合は、ImageDataの後半がSOI〜SOF0〜SOS〜EOIなので、ImageDataの前半が終了したポインタをそのまま使って、データ継続する。
	 */
	return data_ptr;
}

static void SWF_OBJECT_DefineBits_JPEG2_3(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream, int has_alpha, const unsigned char* JPEGData) {
	int CharacterId;
	int AlphaDataOffset;
	const unsigned char* ImageData;
	const unsigned char* BitmapAlphaData;
	SWF_OBJECT_DefineBits_JPEG2_3_USER_DATA user_data;

	CharacterId = SWF_BIT_STREAM_get_UI16(bit_stream);
	if(SWF_DICTIONARY_get_character(CharacterId)) {
		return; /* 1ループ目で定義済み */
	}
	AlphaDataOffset = has_alpha ? SWF_BIT_STREAM_get_SI32(bit_stream) : 0;
	ImageData       =             SWF_BIT_STREAM_get_data(bit_stream, AlphaDataOffset);
	BitmapAlphaData = has_alpha ? SWF_BIT_STREAM_get_data(bit_stream, 0) : NULL;

	/* SWFの、既知の不具合回避。JPEG SOIマーカーの前に、余分なEOIマーカーが入っている場合がある。 */
	if((ImageData[0] == 0xFF) && (ImageData[1] == 0xD9)) { /* EOI */
		ImageData += 2;
	}
	if((ImageData[0] != 0xFF) || (ImageData[1] != 0xD8)) { /* SOI */
		DIE(); /* 不正なデータ */
	}

	memset(&user_data, 0, sizeof user_data);
	user_data.BitmapAlphaData = BitmapAlphaData;
	if(JPEGData) {
		/* DefineBitsの場合、JPEGDataからImageDataへデータ継続。 */
		user_data.ImageData = ImageData;
		jpeg_decode(JPEGData, 0/*Y component*/,
			SWF_OBJECT_DefineBits_JPEG2_3_size_proc,
			SWF_OBJECT_DefineBits_JPEG2_3_draw_proc,
			SWF_OBJECT_DefineBits_JPEG2_3_data_proc, &user_data);
	} else {
		/* DefineBitsJPEG2(3)の場合、ImageDataの前半から後半へデータ継続。(必要に応じて) */
		jpeg_decode(ImageData, 0/*Y component*/,
			SWF_OBJECT_DefineBits_JPEG2_3_size_proc,
			SWF_OBJECT_DefineBits_JPEG2_3_draw_proc,
			SWF_OBJECT_DefineBits_JPEG2_3_data_proc, &user_data);
	}
	if(!user_data.surface) {
		DIE(); /* 不正なデータ */
	}

	SWF_DICTIONARY_new_bitmap(CharacterId, user_data.surface);
	free(user_data.surface); /* 忘れないで!! */
}

static void SWF_OBJECT_JPEGTables(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream) { //  8 JPEGTables
	const unsigned char* JPEGData = SWF_BIT_STREAM_get_data(bit_stream, 0);
	/* SWFの、既知の不具合回避。JPEG SOIマーカーの前に、余分なEOIマーカーが入っている場合がある。 */
	if((JPEGData[0] == 0xFF) && (JPEGData[1] == 0xD9)) { /* EOI */
		JPEGData += 2;
	}
	if((JPEGData[0] != 0xFF) || (JPEGData[1] != 0xD8)) { /* SOI */
		DIE(); /* 不正なデータ */
	}
	swf.JPEGData = JPEGData;
}

static void SWF_OBJECT_DefineBits(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream) { //  6 DefineBits
	SWF_OBJECT_DefineBits_JPEG2_3(self, bit_stream, 0/*has_alpha*/, swf.JPEGData);
}

static void SWF_OBJECT_DefineBitsJPEG2(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream) { // 21 DefineBitsJPEG2
	SWF_OBJECT_DefineBits_JPEG2_3(self, bit_stream, 0/*has_alpha*/, NULL/*JPEGData*/);
}

static void SWF_OBJECT_DefineBitsJPEG3(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream) { // 35 DefineBitsJPEG3
	SWF_OBJECT_DefineBits_JPEG2_3(self, bit_stream, 1/*has_alpha*/, NULL/*JPEGData*/);
}

static void SWF_OBJECT_PlaceObject2(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream) { // 26 PlaceObject2
	int PlaceFlagHasName;
	int PlaceFlagHasRatio;
	int PlaceFlagHasColorTransform;
	int PlaceFlagHasMatrix;
	int PlaceFlagHasCharacter;
	int PlaceFlagMove;
	int Depth;
	SWF_CHARACTER* character;
	SWF_OBJECT* object;

	SWF_BIT_STREAM_get_UB(bit_stream, 1); /* PlaceFlagHasClipActions */
	SWF_BIT_STREAM_get_UB(bit_stream, 1); /* PlaceFlagHasClipDepth */
	PlaceFlagHasName           = SWF_BIT_STREAM_get_UB(bit_stream, 1);
	PlaceFlagHasRatio          = SWF_BIT_STREAM_get_UB(bit_stream, 1);
	PlaceFlagHasColorTransform = SWF_BIT_STREAM_get_UB(bit_stream, 1);
	PlaceFlagHasMatrix         = SWF_BIT_STREAM_get_UB(bit_stream, 1);
	PlaceFlagHasCharacter      = SWF_BIT_STREAM_get_UB(bit_stream, 1);
	PlaceFlagMove              = SWF_BIT_STREAM_get_UB(bit_stream, 1);
	Depth                      = SWF_BIT_STREAM_get_UI16(bit_stream);

	if(PlaceFlagHasCharacter) {
		int CharacterId = SWF_BIT_STREAM_get_UI16(bit_stream);
		character = SWF_DICTIONARY_get_character(CharacterId);
		if(!character) {
			return; /* 未定義 */
		}
	} else {
		character = NULL;
	}
	object = SWF_DISPLAY_LIST_get_object(&self->display_list, Depth);

	/* ステージにシンボルを置いた瞬間の場合 */
	if(!PlaceFlagMove) {
		/* 置くときは、キャラクタ必須です。{PlaceFlagMove=0,PlaceFlagHasCharacter=0}は、SWFの仕様違反です。 */
		if(!character) {
			return; /* 不正なデータ */
		}
		/* Endでループした直後に、同じDepthで同じスプライトを置いたら、End時のタイムラインとディスプレイリストを継続します。 */
		if(object && (object->object_state & SWF_OBJECT_STATE_END) &&
		  (object->character == character) && (character->character_type == SWF_CHARACTER_TYPE_SPRITE)) {
			object->object_state &= ~SWF_OBJECT_STATE_END;
		/* それ以外の通常時は、既にこのDepthのオブジェクトが有るか無いかにかかわらず、このDepthのオブジェクトを新規作成します。
		 * 既にこのDepthのオブジェクトが有った場合、ディスプレイリスト内に同じDepthのオブジェクトが二つできますが、構いません。
		 * 本物のフラッシュプレイヤーも、同じ挙動です。
		 */
		} else {
			object = SWF_DISPLAY_LIST_new_object(&self->display_list, Depth);
			object->parent    = self;
			object->character = character;
			if(character->character_type == SWF_CHARACTER_TYPE_SPRITE) {
				object->object_state      |= SWF_OBJECT_STATE_PLAY;
				object->bit_stream.data    = ((SWF_SPRITE*)character)->ControlTags;
				object->bit_stream.bit_pos = 0; /* 既に0なので省略しても構わない */
			}
		}
	/* 既に置いてあるシンボルを移動したり、グラフィックシンボルでキャラクタの差し替えが発生した場合 */
	} else {
		/* 既にこのDepthのオブジェクトが有るはずです。
		 * ※いずれフレームジャンプに対応した場合は、見つからなかった場合は新規作成すべきかも知れません。
		 */
		if(!object) {
			return; /* 未定義 */
		}
		/* グラフィックシンボルでキャラクタの差し替えが発生した場合 */
		if(character) {
			/* 現在のキャラクタと変化していたら、差し替えます。 */
			if(object->character != character) {
				object->character = character;
				if(character->character_type == SWF_CHARACTER_TYPE_SPRITE) {
					object->object_state      |= SWF_OBJECT_STATE_PLAY;
					object->bit_stream.data    = ((SWF_SPRITE*)character)->ControlTags;
					object->bit_stream.bit_pos = 0;    /* これは必要です!! */
				} else {
					object->object_state      &= ~SWF_OBJECT_STATE_PLAY;
					object->bit_stream.data    = NULL; /* 無意味なので省略しても構わない */
					object->bit_stream.bit_pos = 0;    /* 無意味なので省略しても構わない */
				}
				/* どの差し替えパターンでも、差し替え直後のオブジェクトのディスプレイリストは空とします。
				 * ・スプライト→別のスプライト
				 * ・スプライト→非スプライト
				 * ・非スプライト→スプライト  (既に空なのでダミー処理)
				 * ・非スプライト→非スプライト(既に空なのでダミー処理)
				 * ※そもそも、スプライトの差し替えが有り得るのかどうか、要確認です。
				 * ※少なくとも、グラフィックシンボルの差し替え機能は、非スプライトだけに対応しています。
				 * ※もし、スプライトの差し替えが有り得ない場合は、上のスプライト初期化コードも不要です。
				 */
				SWF_DISPLAY_LIST_free(&object->display_list);
			}
		}
	}

	if(PlaceFlagHasMatrix) {
		SWF_BIT_STREAM_get_MATRIX(bit_stream, &object->Matrix);
	}
	if(PlaceFlagHasColorTransform) {
		SWF_BIT_STREAM_get_CXFORMWITHALPHA(bit_stream, NULL); /* ColorTransform */
	}
	if(PlaceFlagHasRatio) {
		SWF_BIT_STREAM_get_UI16(bit_stream); /* Ratio */
	}
	if(PlaceFlagHasName) {
		object->Name = SWF_BIT_STREAM_get_STRING(bit_stream);
	}
}

static void SWF_OBJECT_RemoveObject2(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream) { // 28 RemoveObject2
	int Depth = SWF_BIT_STREAM_get_UI16(bit_stream);
	SWF_OBJECT* object = SWF_DISPLAY_LIST_get_object(&self->display_list, Depth);
	if(object) {
		SWF_OBJECT_free(object);
	}
}

#ifndef PIECE
static void SWF_TEXT_dump_using_CodeTable(SWF_TEXT* self) {
	SWF_BIT_STREAM bit_stream = { self->TextRecords };
	int StyleFlagsHasFont;
	int StyleFlagsHasColor;
	int StyleFlagsHasYOffset;
	int StyleFlagsHasXOffset;
	int FontId;
	SWF_FONT* font = NULL; /*警告抑制*/
	SWF_RGBA TextColor;
	float XOffset = 0.0f; /*警告抑制*/
	float YOffset = 0.0f; /*警告抑制*/
	float TextHeight = 0.0f; /*警告抑制*/
	int GlyphCount;
	int GlyphIndex;
	float GlyphAdvance;
	int i;
	int code;

	/*---------------------------------------------------------------------------*/
	HDC hDC;
	int ImageWidth;
	int ImageHeight;
	unsigned char (*ImageData)[4/*BGRA*/];
	LOGFONT lf;
	HFONT hFont;
	/* メモリDCを作成 */
	hDC = CreateCompatibleDC(NULL);
	if(!hDC) {
		DIE();
	}
	/* イメージデータを確保 */
	ImageWidth  = ceil(self->TextBounds.Xmax - self->TextBounds.Xmin);
	ImageHeight = ceil(self->TextBounds.Ymax - self->TextBounds.Ymin);
	ImageData = calloc(1, ImageWidth * ImageHeight * 4/*BGRA*/);
	if(!ImageData) {
		DIE();
	}
	/*---------------------------------------------------------------------------*/
	for(;;) {
		if(!SWF_BIT_STREAM_get_UB(&bit_stream, 1)) { /* TextRecordType */
			break;
		}
		SWF_BIT_STREAM_get_UB(&bit_stream, 3); /* StyleFlagsReserved */
		StyleFlagsHasFont    = SWF_BIT_STREAM_get_UB(&bit_stream, 1);
		StyleFlagsHasColor   = SWF_BIT_STREAM_get_UB(&bit_stream, 1);
		StyleFlagsHasYOffset = SWF_BIT_STREAM_get_UB(&bit_stream, 1);
		StyleFlagsHasXOffset = SWF_BIT_STREAM_get_UB(&bit_stream, 1);
		if(StyleFlagsHasFont) {
			FontId = SWF_BIT_STREAM_get_UI16(&bit_stream);
			font = (SWF_FONT*)SWF_DICTIONARY_get_character(FontId);
			if(font && (font->character.character_type != SWF_CHARACTER_TYPE_FONT)) {
				DIE(); /* 不正なデータ */
			}
		}
		if(StyleFlagsHasColor) {
			if(self->has_alpha) {
				SWF_BIT_STREAM_get_RGBA(&bit_stream, &TextColor);
			} else {
				SWF_BIT_STREAM_get_RGB(&bit_stream, (SWF_RGB*)&TextColor);
				TextColor.Alpha = 255;
			}
		}
		if(StyleFlagsHasXOffset) {
			XOffset = SWF_BIT_STREAM_get_SI16(&bit_stream) / 20.0f;
		}
		if(StyleFlagsHasYOffset) {
			YOffset = SWF_BIT_STREAM_get_SI16(&bit_stream) / 20.0f;
		}
		if(StyleFlagsHasFont) {
			TextHeight = SWF_BIT_STREAM_get_SI16(&bit_stream) / 20.0f;
		}
		GlyphCount = SWF_BIT_STREAM_get_UI8(&bit_stream);
		/*---------------------------------------------------------------------------*/
		if(font) {
			/* フォントを作成 */
			memset(&lf, 0, sizeof lf);
			lf.lfHeight = TextHeight;
			if(font->FontFlagsBold) {
				lf.lfWeight = FW_BOLD;
			}
			if(font->FontFlagsItalic) {
				lf.lfItalic = TRUE;
			}
			lf.lfCharSet = DEFAULT_CHARSET; /* フォント名だけで検索する。DEFAULT_CHARSETは0ではなく1なので明示必須! */
			//lf.lfQuality = ANTIALIASED_QUALITY; → GetGlyphOutline()を使う場合は、常にアンチエリアスがかかるので不要
			memcpy(lf.lfFaceName, font->FontName, font->FontNameLen);
			hFont = CreateFontIndirect(&lf);
			if(!hFont) {
				DIE();
			}
			/* フォントを選択 */
			hFont = SelectObject(hDC, hFont);
			if(!hFont) {
				DIE();
			}
		}
		/*---------------------------------------------------------------------------*/
		/* GlyphEntries */
		for(i = 0; i < GlyphCount; i++) {
			GlyphIndex   = SWF_BIT_STREAM_get_UB(&bit_stream, self->GlyphBits);
			GlyphAdvance = SWF_BIT_STREAM_get_UB(&bit_stream, self->AdvanceBits) / 20.0f;
			if(font && font->CodeTable) {
				if(font->FontFlagsWideCodes) {
					code = font->CodeTable[(GlyphIndex * 2) + 0] | font->CodeTable[(GlyphIndex * 2) + 1] << 8;
				} else {
					code = font->CodeTable[GlyphIndex];
				}
				/*---------------------------------------------------------------------------*/
				{
					static const MAT2 mat2 = {{0,1},{0,0},{0,0},{0,1}}; /* 単位行列 */
					TEXTMETRIC tm;
					GLYPHMETRICS gm;
					int size;
					unsigned char* buffer;
					int pitch;
					int sx;
					int sy;
					int dx;
					int dy;
					/* フォントの情報を取得 */
					GetTextMetrics(hDC, &tm);
					/* バッファサイズを取得 */
					size = GetGlyphOutline(hDC, code, GGO_GRAY8_BITMAP, &gm, 0, NULL, &mat2);
					/* バッファを確保 */
					buffer = calloc(size, 1);
					if(!buffer) {
						DIE();
					}
					/* ビットマップを取得 */
					GetGlyphOutline(hDC, code, GGO_GRAY8_BITMAP, &gm, size, buffer, &mat2);
					/* ラインサイズを求める */
					pitch = (gm.gmBlackBoxX + 3) & ~3;
					/* ビットマップの各ピクセルについて… */
					for(sy = 0; sy < (int)gm.gmBlackBoxY; sy++) {
						for(sx = 0; sx < (int)gm.gmBlackBoxX; sx++) {
							/* ビットマップのアルファ値を取得 */
							int a = buffer[sy * pitch + sx];
							/* [0〜64]→[0〜255]に変換 */
							a = a * 255 / 64/*GGO_GRAY8_BITMAPの最大値*/;
							/* テキストカラーのアルファ値を掛け合わせる */
							a = a * TextColor.Alpha / 255;
							/* 完全透明でなければ… */
							if(a) {
								/* ビットマップのピクセル位置に対応する、イメージのピクセル位置を求める。
								 * - Flash MXでswfを作ると、レイアウト情報無しで、ベースライン基準のY座標になるので、特に難しい点はありません。
								 *   ところが、Suzukaでswfを作ると、レイアウト情報有りで、FontDescent=1024、つまり、上端基準のY座標になります。
								 *   Suzukaで作ったswfの静止テキストを表示するために、DefineFont2のレイアウト情報に対応せざるを得ませんでした。
								 * - 以下の計算式は、試行錯誤で調整したものです。間違っているかも知れません。
								 */
								dx = floor(XOffset + gm.gmptGlyphOrigin.x + sx - self->TextBounds.Xmin + self->TextMatrix.TranslateX + 0.5f); /* TextMatrixの拡縮回転は未対応 */
								dy = floor(YOffset - gm.gmptGlyphOrigin.y + sy - self->TextBounds.Ymin + self->TextMatrix.TranslateY + 0.5f); /* TextMatrixの拡縮回転は未対応 */
								if(font->FontFlagsHasLayout) { /* レイアウト情報有り */
									dy += tm.tmHeight * font->FontDescent - tm.tmDescent;
								}
								/* イメージの範囲内ならば、テキストカラーでプロット */
								if((dx >= 0) && (dx <= ImageWidth  - 1) && (dy >= 0) && (dy <= ImageHeight - 1)) {
									int j = (ImageHeight - 1 - dy)/*TGAは下から上が標準*/ * ImageWidth + dx;
									ImageData[j][0] = TextColor.Blue;
									ImageData[j][1] = TextColor.Green;
									ImageData[j][2] = TextColor.Red;
									ImageData[j][3] = a;
								}
							}
						}
					}
					/* バッファを開放 */
					free(buffer);
				}
				/*---------------------------------------------------------------------------*/
			}
			XOffset += GlyphAdvance;
		}
		SWF_BIT_STREAM_get_data(&bit_stream, 0); /* バイト整列 */
		/*---------------------------------------------------------------------------*/
		if(font) {
			/* フォントを選択解除 */
			hFont = SelectObject(hDC, hFont);
			if(!hFont) {
				DIE();
			}
			/* フォントを削除 */
			DeleteObject(hFont);
		}
		/*---------------------------------------------------------------------------*/
	}
	/*---------------------------------------------------------------------------*/
	/* イメージをセーブ */
	{
		FILE* fp;
		char fname[_MAX_FNAME];
		unsigned char TGAFileHeader[18] = {
			0x00,		// + 0,1 ID Length
			0x00,		// + 1,1 Color Map Type
			0x02,		// + 2,1 Image Type
			0x00, 0x00,	// + 3,2 First Entry Index
			0x00, 0x00,	// + 5,2 Color map Length
			0x00,		// + 7,1 Color map Entry Size
			0x00, 0x00,	// + 8,2 X origin of Image
			0x00, 0x00,	// +10,2 Y origin of Image
			0x80, 0x02,	// +12,2 Image Width
			0x90, 0x01,	// +14,2 Image Height
			0x20,		// +16,1 Pixel Depth
			0x08,		// +17,1 Image Descriptor
		};			// =18
		sprintf(fname, "text%04x.tga", self->character.CharacterId);
		fp = fopen(fname, "wb");
		if(!fp) {
			DIE();
		}
		*(short*)&TGAFileHeader[12] = ImageWidth;
		*(short*)&TGAFileHeader[14] = ImageHeight;
		fwrite(TGAFileHeader, 1, sizeof TGAFileHeader, fp);
		fwrite(ImageData, 1, ImageWidth * ImageHeight * 4/*BGRA*/, fp);
		fclose(fp);
	}
	/* イメージデータを開放 */
	free(ImageData);
	/* メモリDCを削除 */
	DeleteDC(hDC);
	/*---------------------------------------------------------------------------*/
}
static void SWF_TEXT_dump_using_GlyphShapeTable(SWF_TEXT* self) {
	SWF_BIT_STREAM bit_stream = { self->TextRecords };
	int StyleFlagsHasFont;
	int StyleFlagsHasColor;
	int StyleFlagsHasYOffset;
	int StyleFlagsHasXOffset;
	int FontId;
	SWF_FONT* font = NULL; /*警告抑制*/
	SWF_RGBA TextColor;
	float XOffset = 0.0f; /*警告抑制*/
	float YOffset = 0.0f; /*警告抑制*/
	float TextHeight;
	int GlyphCount;
	int GlyphIndex;
	float GlyphAdvance;
	int i;
	int offset;
	int NumFillBits;
	int NumLineBits;
	int StraightFlag;
	int NumBits;
	float DeltaX;
	float DeltaY;
	float ControlDeltaX;
	float ControlDeltaY;
	float AnchorDeltaX;
	float AnchorDeltaY;
	int StateNewStyles;
	int StateLineStyle;
	int StateFillStyle1;
	int StateFillStyle0;
	int StateMoveTo;
	int MoveBits;
	float MoveDeltaX;
	float MoveDeltaY;
	float x = 0.0f; /*警告抑制*/
	float y = 0.0f; /*警告抑制*/
	int n_point;			/* 頂点の通算数 */
	int n_polygon;			/* 多角形の数 */
	POINT point[1024/*調整可*/];	/* すべての多角形の頂点を入れる配列 */
	int polygon[256/*調整可*/];	/* 各多角形の頂点の数を入れる配列 */

	/*---------------------------------------------------------------------------*/
	HDC hDC;
	BITMAPINFOHEADER bi;
	unsigned char (*ImageData)[4/*BGRA*/];
	HBITMAP hBitmap;
	HPEN hPen;
	HBRUSH hBrush;
	/* メモリDCを作成 */
	hDC = CreateCompatibleDC(NULL);
	if(!hDC) {
		DIE();
	}
	/* DIBセクションを作成 */
	memset(&bi, 0, sizeof bi);
	bi.biSize     = sizeof(BITMAPINFOHEADER);
	bi.biWidth    = ceil(self->TextBounds.Xmax - self->TextBounds.Xmin);
	bi.biHeight   = ceil(self->TextBounds.Ymax - self->TextBounds.Ymin);
	bi.biPlanes   = 1;
	bi.biBitCount = 32;
	hBitmap = CreateDIBSection(hDC, (BITMAPINFO*)&bi, DIB_RGB_COLORS, (void**)&ImageData, NULL, 0);
	if(!hBitmap) {
		DIE();
	}
	/* DIBセクションを選択 */
	SelectObject(hDC, hBitmap);
	/* 32ビットDIBの各ピクセルの4バイト目は、そのピクセルが描画されたときに、0が設定されるようです。(Undoc'd)
	 * この性質を利用し、あらかじめ0以外の値を書き込んでおくことで、どのピクセルが描画されたかを検出できます。
	 */
	memset(ImageData, -1, bi.biWidth * bi.biHeight * 4/*BGRA*/);
	/* 塗り潰しモードを交互モードに設定
	 * * Wed Sep 09 04:15:45 JST 2009 Naoyuki Sawa
	 * - DefineFont(2)のシェイプは、ドーナツ型の場合、外側の多角形を右回り、内側の多角形を左回りで出力するようです。
	 *   外側と内側の回り方向が逆であるため、SetPolyFillMode()をALTERNATEにしてもWINDINGにしても、同じ描画結果となります。
	 *   WINDINGは、回り方向が逆の多角形が重なっている部分を、塗り潰さないからです。
	 *   しかし、上述がDefineFont(2)のシェイプの出力仕様か確認できなかったため、ALTERNATEで描く方が確実と思います。
	 */
	//SetPolyFillMode(hDC, ALTERNATE); → 初期設定でALTERNATEになっているので設定不要
	/*---------------------------------------------------------------------------*/
	for(;;) {
		if(!SWF_BIT_STREAM_get_UB(&bit_stream, 1)) { /* TextRecordType */
			break;
		}
		SWF_BIT_STREAM_get_UB(&bit_stream, 3); /* StyleFlagsReserved */
		StyleFlagsHasFont    = SWF_BIT_STREAM_get_UB(&bit_stream, 1);
		StyleFlagsHasColor   = SWF_BIT_STREAM_get_UB(&bit_stream, 1);
		StyleFlagsHasYOffset = SWF_BIT_STREAM_get_UB(&bit_stream, 1);
		StyleFlagsHasXOffset = SWF_BIT_STREAM_get_UB(&bit_stream, 1);
		if(StyleFlagsHasFont) {
			FontId = SWF_BIT_STREAM_get_UI16(&bit_stream);
			font = (SWF_FONT*)SWF_DICTIONARY_get_character(FontId);
			if(font && (font->character.character_type != SWF_CHARACTER_TYPE_FONT)) {
				DIE(); /* 不正なデータ */
			}
		}
		if(StyleFlagsHasColor) {
			if(self->has_alpha) {
				SWF_BIT_STREAM_get_RGBA(&bit_stream, &TextColor);
			} else {
				SWF_BIT_STREAM_get_RGB(&bit_stream, (SWF_RGB*)&TextColor);
				TextColor.Alpha = 255;
			}
		}
		if(StyleFlagsHasXOffset) {
			XOffset = SWF_BIT_STREAM_get_SI16(&bit_stream) / 20.0f;
		}
		if(StyleFlagsHasYOffset) {
			YOffset = SWF_BIT_STREAM_get_SI16(&bit_stream) / 20.0f;
		}
		if(StyleFlagsHasFont) {
			TextHeight = SWF_BIT_STREAM_get_SI16(&bit_stream) / 20.0f;
		}
		GlyphCount = SWF_BIT_STREAM_get_UI8(&bit_stream);
		/*---------------------------------------------------------------------------*/
		/* * Sat Sep 12 05:03:25 JST 2009 Naoyuki Sawa
		 * - 小さな文字は、ペンとブラシを両方使って描かないと、線が消えてしまいます。
		 *   大きな文字は、ペンを使わず、ブラシだけの方が綺麗に描けます。
		 *   今回は、小さな文字の線が消えないことを優先して、両方使うことにしました。
		 */
		/* ペンを作成 */
		hPen = CreatePen(PS_SOLID, 0, RGB(TextColor.Red, TextColor.Green, TextColor.Blue));
		if(!hPen) {
			DIE();
		}
		/* ペンを選択 */
		hPen = SelectObject(hDC, hPen);
		if(!hPen) {
			DIE();
		}
		/* ブラシを作成 */
		hBrush = CreateSolidBrush(RGB(TextColor.Red, TextColor.Green, TextColor.Blue));
		if(!hBrush) {
			DIE();
		}
		/* ブラシを選択 */
		hBrush = SelectObject(hDC, hBrush);
		if(!hBrush) {
			DIE();
		}
		/*---------------------------------------------------------------------------*/
		/* GlyphEntries */
		for(i = 0; i < GlyphCount; i++) {
			GlyphIndex   = SWF_BIT_STREAM_get_UB(&bit_stream, self->GlyphBits);
			GlyphAdvance = SWF_BIT_STREAM_get_UB(&bit_stream, self->AdvanceBits) / 20.0f;
			if(font && font->OffsetTable) {
				if(font->FontFlagsWideOffsets) {
					offset = font->OffsetTable[(GlyphIndex * 4) + 0]
					       | font->OffsetTable[(GlyphIndex * 4) + 1] <<  8
					       | font->OffsetTable[(GlyphIndex * 4) + 2] << 16
					       | font->OffsetTable[(GlyphIndex * 4) + 3] << 24;
				} else {
					offset = font->OffsetTable[(GlyphIndex * 2) + 0]
					       | font->OffsetTable[(GlyphIndex * 2) + 1] <<  8;
				}
				{
					SWF_BIT_STREAM shape_bit_stream = { font->OffsetTable + offset };
					n_point = 0;
					n_polygon = 0;
					/* SHAPE */
					NumFillBits = SWF_BIT_STREAM_get_UB(&shape_bit_stream, 4);
					NumLineBits = SWF_BIT_STREAM_get_UB(&shape_bit_stream, 4);
					for(;;) {
						if(SWF_BIT_STREAM_get_UB(&shape_bit_stream, 1)) { /* TypeFlag */
							StraightFlag = SWF_BIT_STREAM_get_UB(&shape_bit_stream, 1);
							NumBits      = SWF_BIT_STREAM_get_UB(&shape_bit_stream, 4) + 2;
							if(StraightFlag) {
								/* STRAIGHTEDGERECORD */
								if(SWF_BIT_STREAM_get_UB(&shape_bit_stream, 1)) {          /* GeneralLineFlag */
									DeltaX = SWF_BIT_STREAM_get_SB(&shape_bit_stream, NumBits) / 1024.0f;
									DeltaY = SWF_BIT_STREAM_get_SB(&shape_bit_stream, NumBits) / 1024.0f;
								} else {
									if(SWF_BIT_STREAM_get_UB(&shape_bit_stream, 1)) { /* VertLineFlag */
										DeltaY = SWF_BIT_STREAM_get_SB(&shape_bit_stream, NumBits) / 1024.0f;
										DeltaX = 0.0f;
									} else {
										DeltaX = SWF_BIT_STREAM_get_SB(&shape_bit_stream, NumBits) / 1024.0f;
										DeltaY = 0.0f;
									}
								}
								x += DeltaX * TextHeight;
								y += DeltaY * TextHeight;
							} else {
								/* CURVEDEDGERECORD */
								ControlDeltaX = SWF_BIT_STREAM_get_SB(&shape_bit_stream, NumBits) / 1024.0f;
								ControlDeltaY = SWF_BIT_STREAM_get_SB(&shape_bit_stream, NumBits) / 1024.0f;
								x += ControlDeltaX * TextHeight;
								y += ControlDeltaY * TextHeight;
								/* 本当はベジェ曲線なのですが、手抜きして、制御点を抜いて始点-終点を直線で結んでしまうことにしました。
								 * 始点-制御点-終点を直線で結ぶ方法もありますが、始点-終点をダイレクトに結ぶ方が綺麗^H^Hマシに見えるようです。
								 */
								AnchorDeltaX  = SWF_BIT_STREAM_get_SB(&shape_bit_stream, NumBits) / 1024.0f;
								AnchorDeltaY  = SWF_BIT_STREAM_get_SB(&shape_bit_stream, NumBits) / 1024.0f;
								x += AnchorDeltaX * TextHeight;
								y += AnchorDeltaY * TextHeight;
							}
							/* 現在の多角形に頂点を追加する */
							if(n_polygon) {
								if(n_point < ARRAY_SIZE(point)) {
									point[n_point].x = x;
									point[n_point].y = y;
									n_point++;
									polygon[n_polygon - 1]++;
								}
							}
						} else {
							/* 現在の多角形を終了する */
							if(n_polygon) {
								/* フォントのシェイプは、終点が始点と同じ座標に戻る、明示的に閉じたパスとしてデータ格納されている。
								 * 描画処理においては無駄なので、(始点と同じ座標であるはずの)終点を削除する。
								 */
								if(polygon[n_polygon - 1]) {
									n_point--; /* 忘れないで!! */
									polygon[n_polygon - 1]--;
								}
								/* 上記の処理の結果、頂点数が3未満ならば、多角形として無意味なので、この多角形をキャンセルする。 */
								if(polygon[n_polygon - 1] < 3) {
									n_point -= polygon[n_polygon - 1]; /* この多角形が使っていた頂点を全て破棄。忘れないで!! */
									n_polygon--;
								}
							}
							StateNewStyles  = SWF_BIT_STREAM_get_UB(&shape_bit_stream, 1);
							StateLineStyle  = SWF_BIT_STREAM_get_UB(&shape_bit_stream, 1);
							StateFillStyle1 = SWF_BIT_STREAM_get_UB(&shape_bit_stream, 1);
							StateFillStyle0 = SWF_BIT_STREAM_get_UB(&shape_bit_stream, 1);
							StateMoveTo     = SWF_BIT_STREAM_get_UB(&shape_bit_stream, 1);
							if(StateNewStyles || StateLineStyle || StateFillStyle1 || StateFillStyle0 || StateMoveTo) {
								/* STYLECHANGERECORD */
								if(StateMoveTo) {
									MoveBits   = SWF_BIT_STREAM_get_UB(&shape_bit_stream, 5);
									MoveDeltaX = SWF_BIT_STREAM_get_SB(&shape_bit_stream, MoveBits) / 1024.0f;
									MoveDeltaY = SWF_BIT_STREAM_get_SB(&shape_bit_stream, MoveBits) / 1024.0f;
									/* - 「SWF File Format Specification Version 10」の136ページ中断に、以下の説明があります。
									 *   ｜ In the first shape record MoveDeltaX and MoveDeltaY are relative to the shape origin.
									 *   ｜ In subsequent shape records, MoveDeltaX and MoveDeltaY are relative to the current drawing position.
									 *   この説明は間違っており、MoveDeltaXとMoveDeltaYは、常に、シェイプ原点からの相対座標です。
									 */
									x = XOffset + MoveDeltaX * TextHeight - self->TextBounds.Xmin + self->TextMatrix.TranslateX; /* TextMatrixの拡縮回転は未対応 */
									y = YOffset + MoveDeltaY * TextHeight - self->TextBounds.Ymin + self->TextMatrix.TranslateY; /* TextMatrixの拡縮回転は未対応 */
								}
								if(StateFillStyle0) {
									SWF_BIT_STREAM_get_UB(&shape_bit_stream, NumFillBits); /* FillStyle0 */
								}
								if(StateFillStyle1) {
									SWF_BIT_STREAM_get_UB(&shape_bit_stream, NumFillBits); /* FillStyle1 */
								}
								if(StateLineStyle) {
									SWF_BIT_STREAM_get_UB(&shape_bit_stream, NumLineBits); /* LineStyle */
								}
								if(StateNewStyles) {
									DIE(); /* 有り得ない */
								}
							} else {
								/* ENDSHAPERECORD */
								break;
							}
							/* 新しい多角形を開始する。 */
							if(n_polygon == ARRAY_SIZE(polygon)) {
								break;
							}
							polygon[n_polygon++] = 0;
							/* 現在の多角形に頂点を追加する */
							if(n_point < ARRAY_SIZE(point)) {
								point[n_point].x = x;
								point[n_point].y = y;
								n_point++;
								polygon[n_polygon - 1]++;
							}
						}
					}
					/*---------------------------------------------------------------------------*/
					/* ALTERNATEモードのPolyPolygonとして、文字を描画 */
					if(n_polygon) { /* 空白(' ')フォントが、空図形として格納されるようです。スキップします。 */
						PolyPolygon(hDC, point, polygon, n_polygon);
					}
					/*---------------------------------------------------------------------------*/
				}
			}
			XOffset += GlyphAdvance;
		}
		SWF_BIT_STREAM_get_data(&bit_stream, 0); /* バイト整列 */
		/*---------------------------------------------------------------------------*/
		/* ブラシを選択解除 */
		hBrush = SelectObject(hDC, hBrush);
		if(!hBrush) {
			DIE();
		}
		/* ブラシを削除 */
		DeleteObject(hBrush);
		/* ペンを選択解除 */
		hPen = SelectObject(hDC, hPen);
		if(!hPen) {
			DIE();
		}
		/* ペンを削除 */
		DeleteObject(hPen);
		/*---------------------------------------------------------------------------*/
	}
	/*---------------------------------------------------------------------------*/
	/* イメージをセーブ */
	{
		FILE* fp;
		char fname[_MAX_FNAME];
		unsigned char TGAFileHeader[18] = {
			0x00,		// + 0,1 ID Length
			0x00,		// + 1,1 Color Map Type
			0x02,		// + 2,1 Image Type
			0x00, 0x00,	// + 3,2 First Entry Index
			0x00, 0x00,	// + 5,2 Color map Length
			0x00,		// + 7,1 Color map Entry Size
			0x00, 0x00,	// + 8,2 X origin of Image
			0x00, 0x00,	// +10,2 Y origin of Image
			0x80, 0x02,	// +12,2 Image Width
			0x90, 0x01,	// +14,2 Image Height
			0x20,		// +16,1 Pixel Depth
			0x08,		// +17,1 Image Descriptor
		};			// =18
		sprintf(fname, "text%04x.tga", self->character.CharacterId);
		fp = fopen(fname, "wb");
		if(!fp) {
			DIE();
		}
		*(short*)&TGAFileHeader[12] = bi.biWidth;
		*(short*)&TGAFileHeader[14] = bi.biHeight;
		fwrite(TGAFileHeader, 1, sizeof TGAFileHeader, fp);
		for(i = 0; i < bi.biWidth * bi.biHeight; i++) {
			if(ImageData[i][3] == 255) {	/* 描画無し */
				ImageData[i][0] = 0;	/* Blue */
				ImageData[i][1] = 0;	/* Green */
				ImageData[i][2] = 0;	/* Red */
				ImageData[i][3] = 0;	/* Alpha */
			} else {			/* 描画有り */
				ImageData[i][3] = 255;	/* Alpha */
			}
		}
		fwrite(ImageData, 1, bi.biWidth * bi.biHeight * 4/*BGRA*/, fp);
		fclose(fp);
	}
	/* メモリDCを削除。自動的にDIBセクションが選択解除される */
	DeleteDC(hDC);
	/* DIBセクションを削除 */
	DeleteObject(hBitmap);
	/*---------------------------------------------------------------------------*/
}
#endif /*PIECE*/

static void SWF_OBJECT_DefineText_2(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream, int has_alpha) {
	int CharacterId;
	SWF_RECT TextBounds;
	SWF_MATRIX TextMatrix;
	int GlyphBits;
	int AdvanceBits;
	const unsigned char* TextRecords;
	SWF_TEXT* text;

	CharacterId = SWF_BIT_STREAM_get_UI16(bit_stream);
	if(SWF_DICTIONARY_get_character(CharacterId)) {
		return; /* 1ループ目で定義済み */
	}
	SWF_BIT_STREAM_get_RECT(bit_stream, &TextBounds);
	SWF_BIT_STREAM_get_MATRIX(bit_stream, &TextMatrix);
	GlyphBits = SWF_BIT_STREAM_get_UI8(bit_stream);
	AdvanceBits = SWF_BIT_STREAM_get_UI8(bit_stream);
	TextRecords = SWF_BIT_STREAM_get_data(bit_stream, 0);

	text = calloc(1, sizeof(SWF_TEXT));
	if(!text) {
		DIE(); /* メモリ不足 */
	}
	/*{{初期化*/
	text->character.character_type = SWF_CHARACTER_TYPE_TEXT;
	text->character.CharacterId    = CharacterId;
	text->TextMatrix               = TextMatrix;
	text->GlyphBits                = GlyphBits;
	text->AdvanceBits              = AdvanceBits;
	text->TextRecords              = TextRecords;
	text->has_alpha                = has_alpha;
#ifndef PIECE
	text->TextBounds               = TextBounds;
#endif /*PIECE*/
	/*}}初期化*/
	InsertTailList(&swf.dictionary, &text->character.list_entry);

#ifndef PIECE
	switch(swf.text_drawing_method) {
	case 0: /* using CodeTable */
		SWF_TEXT_dump_using_CodeTable(text);
		break;
	case 1: /* using GlyphShapeTable */
		SWF_TEXT_dump_using_GlyphShapeTable(text);
		break;
	default:
		DIE(); /* swf_set_text_drawing_method()の呼び出しが不正 */
	}
#endif /*PIECE*/
}

static void SWF_OBJECT_DefineText(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream) { // 11 DefineText
	SWF_OBJECT_DefineText_2(self, bit_stream, 0/*has_alpha*/);
}

static void SWF_OBJECT_DefineText2(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream) { // 33 DefineText2
	SWF_OBJECT_DefineText_2(self, bit_stream, 1/*has_alpha*/);
}

static void SWF_OBJECT_DefineEditText(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream) { // 37 DefineEditText
	int CharacterId;
	SWF_RECT Bounds;
	int HasText;
	int HasTextColor;
	int HasMaxLength;
	int HasFont;
	int HasFontClass;
	int HasLayout;
	int FontId;
	SWF_FONT* font;
	SWF_RGBA TextColor;
	const char* InitialText;
	SWF_EDIT_TEXT* edit_text;

	CharacterId = SWF_BIT_STREAM_get_UI16(bit_stream);
	if(SWF_DICTIONARY_get_character(CharacterId)) {
		return; /* 1ループ目で定義済み */
	}
	SWF_BIT_STREAM_get_RECT(bit_stream, &Bounds); /* Bounds */
	HasText = SWF_BIT_STREAM_get_UB(bit_stream, 1);
	SWF_BIT_STREAM_get_UB(bit_stream, 1); /* WordWrap */
	SWF_BIT_STREAM_get_UB(bit_stream, 1); /* Multiline */
	SWF_BIT_STREAM_get_UB(bit_stream, 1); /* Password */
	SWF_BIT_STREAM_get_UB(bit_stream, 1); /* ReadOnly */
	HasTextColor = SWF_BIT_STREAM_get_UB(bit_stream, 1);
	HasMaxLength = SWF_BIT_STREAM_get_UB(bit_stream, 1);
	HasFont = SWF_BIT_STREAM_get_UB(bit_stream, 1);
	HasFontClass = SWF_BIT_STREAM_get_UB(bit_stream, 1);
	SWF_BIT_STREAM_get_UB(bit_stream, 1); /* AutoSize */
	HasLayout = SWF_BIT_STREAM_get_UB(bit_stream, 1);
	SWF_BIT_STREAM_get_UB(bit_stream, 1); /* NoSelect */
	SWF_BIT_STREAM_get_UB(bit_stream, 1); /* Border */
	SWF_BIT_STREAM_get_UB(bit_stream, 1); /* WasStatic */
	SWF_BIT_STREAM_get_UB(bit_stream, 1); /* HTML */
	SWF_BIT_STREAM_get_UB(bit_stream, 1); /* UseOutlines */

	if(HasFont) {
		FontId = SWF_BIT_STREAM_get_UI16(bit_stream);
		font = (SWF_FONT*)SWF_DICTIONARY_get_character(FontId);
		if(!font) {
			return; /* 未定義 */
		}
		if(font->character.character_type != SWF_CHARACTER_TYPE_FONT) {
			DIE(); /* 不正なデータ */
		}
	} else {
		return; /* 未対応 */
	}
	if(HasFontClass) {
		SWF_BIT_STREAM_get_STRING(bit_stream); /* FontClass */
	}
	if(HasFont) {
		SWF_BIT_STREAM_get_UI16(bit_stream); /* FontHeight */
	}
	if(HasTextColor) {
		SWF_BIT_STREAM_get_RGBA(bit_stream, &TextColor);
	} else {
		memset(&TextColor, 0, sizeof TextColor);
	}
	if(HasMaxLength) {
		SWF_BIT_STREAM_get_UI16(bit_stream); /* MaxLength */
	}
	if(HasLayout) {
		SWF_BIT_STREAM_get_UI8(bit_stream); /* Align */
		SWF_BIT_STREAM_get_UI16(bit_stream); /* LeftMargin */
		SWF_BIT_STREAM_get_UI16(bit_stream); /* RightMargin */
		SWF_BIT_STREAM_get_UI16(bit_stream); /* Indent */
		SWF_BIT_STREAM_get_UI16(bit_stream); /* Leading */
	}
	SWF_BIT_STREAM_get_STRING(bit_stream); /* VariableName */
	if(HasText) {
		InitialText = SWF_BIT_STREAM_get_STRING(bit_stream);
	} else {
		return; /* 未対応 */
	}

	edit_text = calloc(1, sizeof(SWF_EDIT_TEXT));
	if(!edit_text) {
		DIE(); /* メモリ不足 */
	}
	/*{{初期化*/
	edit_text->character.character_type = SWF_CHARACTER_TYPE_EDIT_TEXT;
	edit_text->character.CharacterId    = CharacterId;
	edit_text->Bounds                   = Bounds;
	edit_text->font                     = font;
	edit_text->color                    = ((((TextColor.Red * 30) + (TextColor.Green * 59) + (TextColor.Blue * 11)) / 100) >> 6) ^ 3;
	edit_text->InitialText              = InitialText;
	/*}}初期化*/
	InsertTailList(&swf.dictionary, &edit_text->character.list_entry);
}

static void SWF_OBJECT_DefineSprite(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream) { // 39 DefineSprite
	int CharacterId;
	const unsigned char* ControlTags;
	SWF_SPRITE* sprite;

	CharacterId = SWF_BIT_STREAM_get_UI16(bit_stream);
	if(SWF_DICTIONARY_get_character(CharacterId)) {
		return; /* 1ループ目で定義済み */
	}
	SWF_BIT_STREAM_get_UI16(bit_stream); /* FrameCount */
	ControlTags = SWF_BIT_STREAM_get_data(bit_stream, 0);

	sprite = calloc(1, sizeof(SWF_SPRITE));
	if(!sprite) {
		DIE(); /* メモリ不足 */
	}
	/*{{初期化*/
	sprite->character.character_type = SWF_CHARACTER_TYPE_SPRITE;
	sprite->character.CharacterId    = CharacterId;
	sprite->ControlTags              = ControlTags;
	/*}}初期化*/
	InsertTailList(&swf.dictionary, &sprite->character.list_entry);
}

static void SWF_OBJECT_DefineFont(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream) { // 10 DefineFont
	int CharacterId;
	const unsigned char* OffsetTable;
	SWF_FONT* font;

	CharacterId = SWF_BIT_STREAM_get_UI16(bit_stream);
	if(SWF_DICTIONARY_get_character(CharacterId)) {
		return; /* 1ループ目で定義済み */
	}
	OffsetTable = SWF_BIT_STREAM_get_data(bit_stream, 0);

	font = calloc(1, sizeof(SWF_FONT));
	if(!font) {
		DIE(); /* メモリ不足 */
	}
	/*{{初期化*/
	font->character.character_type = SWF_CHARACTER_TYPE_FONT;
	font->character.CharacterId    = CharacterId;
//	font->FontFlagsWideOffsets     = 0;
	font->OffsetTable              = OffsetTable;
//	font->FontFlagsWideCodes       = 0;
//	font->CodeTable                = NULL;
//	font->FontFlagsHasLayout       = 0;
//	font->FontDescent              = 0.0f;
//	font->FontFlagsBold            = 0;
#ifndef PIECE
//	font->FontNameLen              = 0;
//	font->FontName                 = NULL;
//	font->FontFlagsItalic          = 0;
#endif /*PIECE*/
	/*}}初期化*/
	InsertTailList(&swf.dictionary, &font->character.list_entry);
}

static void SWF_OBJECT_DefineFontInfo(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream) { // 13 DefineFontInfo
	int FontId;
	int FontNameLen;
	const char* FontName;
	int FontFlagsItalic;
	int FontFlagsBold;
	int FontFlagsWideCodes;
	const unsigned char* CodeTable;
	SWF_FONT* font;

	FontId = SWF_BIT_STREAM_get_UI16(bit_stream);
	font = (SWF_FONT*)SWF_DICTIONARY_get_character(FontId);
	if(!font) {
		return; /* 未定義 */
	}
	if(font->character.character_type != SWF_CHARACTER_TYPE_FONT) {
		DIE(); /* 不正なデータ */
	}
	FontNameLen = SWF_BIT_STREAM_get_UI8(bit_stream);
	FontName = SWF_BIT_STREAM_get_data(bit_stream, FontNameLen);
	SWF_BIT_STREAM_get_UB(bit_stream, 2); /* FontFlagsReserved */
	SWF_BIT_STREAM_get_UB(bit_stream, 1); /* FontFlagsSmallText */
	SWF_BIT_STREAM_get_UB(bit_stream, 1); /* FontFlagsShiftJIS */
	SWF_BIT_STREAM_get_UB(bit_stream, 1); /* FontFlagsANSI */
	FontFlagsItalic = SWF_BIT_STREAM_get_UB(bit_stream, 1);
	FontFlagsBold = SWF_BIT_STREAM_get_UB(bit_stream, 1);
	FontFlagsWideCodes = SWF_BIT_STREAM_get_UB(bit_stream, 1);
	CodeTable = SWF_BIT_STREAM_get_data(bit_stream, 0);

	/*{{初期化*/
//	font->character.character_type = DefineFontで設定済み;
//	font->character.CharacterId    = DefineFontで設定済み;
//	font->FontFlagsWideOffsets     = DefineFontで設定済み;
//	font->OffsetTable              = DefineFontで設定済み;
	font->FontFlagsWideCodes       = FontFlagsWideCodes;
	font->CodeTable                = CodeTable;
//	font->FontFlagsHasLayout       = DefineFontで設定済み;
//	font->FontDescent              = DefineFontで設定済み;
	font->FontFlagsBold            = FontFlagsBold;
#ifndef PIECE
	font->FontNameLen              = FontNameLen;
	font->FontName                 = FontName;
	font->FontFlagsItalic          = FontFlagsItalic;
#endif /*PIECE*/
	/*}}初期化*/
}

static void SWF_OBJECT_DefineFont2(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream) { // 48 DefineFont2
	int CharacterId;
	int FontFlagsHasLayout;
	int FontFlagsWideOffsets;
	int FontFlagsWideCodes;
	int FontFlagsItalic;
	int FontFlagsBold;
	int FontNameLen;
	const char* FontName;
	int NumGlyphs;
	const unsigned char* OffsetTable;
	int CodeTableOffset;
	const unsigned char* CodeTable;
	float FontDescent;
	SWF_FONT* font;

	CharacterId = SWF_BIT_STREAM_get_UI16(bit_stream);
	if(SWF_DICTIONARY_get_character(CharacterId)) {
		return; /* 1ループ目で定義済み */
	}
	FontFlagsHasLayout = SWF_BIT_STREAM_get_UB(bit_stream, 1);
	SWF_BIT_STREAM_get_UB(bit_stream, 1); /* FontFlagsShiftJIS */
	SWF_BIT_STREAM_get_UB(bit_stream, 1); /* FontFlagsSmallText */
	SWF_BIT_STREAM_get_UB(bit_stream, 1); /* FontFlagsANSI */
	FontFlagsWideOffsets = SWF_BIT_STREAM_get_UB(bit_stream, 1);
	FontFlagsWideCodes = SWF_BIT_STREAM_get_UB(bit_stream, 1);
	FontFlagsItalic = SWF_BIT_STREAM_get_UB(bit_stream, 1);
	FontFlagsBold = SWF_BIT_STREAM_get_UB(bit_stream, 1);
	SWF_BIT_STREAM_get_UI8(bit_stream); /* LanguageCode */
	FontNameLen = SWF_BIT_STREAM_get_UI8(bit_stream);
	FontName = SWF_BIT_STREAM_get_data(bit_stream, FontNameLen);
	NumGlyphs = SWF_BIT_STREAM_get_UI16(bit_stream);
	if(FontFlagsWideOffsets) {
		OffsetTable = SWF_BIT_STREAM_get_data(bit_stream, NumGlyphs * 4);
		CodeTableOffset = SWF_BIT_STREAM_get_SI32(bit_stream);
		SWF_BIT_STREAM_get_data(bit_stream, CodeTableOffset - (NumGlyphs * 4)/*OffsetTable*/ - 4/*CodeTableOffset*/);
	} else {
		OffsetTable = SWF_BIT_STREAM_get_data(bit_stream, NumGlyphs * 2);
		CodeTableOffset = SWF_BIT_STREAM_get_UI16(bit_stream);
		SWF_BIT_STREAM_get_data(bit_stream, CodeTableOffset - (NumGlyphs * 2)/*OffsetTable*/ - 2/*CodeTableOffset*/);
	}
	if(FontFlagsWideCodes) {
		CodeTable = SWF_BIT_STREAM_get_data(bit_stream, NumGlyphs * 2);
	} else {
		CodeTable = SWF_BIT_STREAM_get_data(bit_stream, NumGlyphs * 1);
	}
	if(FontFlagsHasLayout) {
		SWF_BIT_STREAM_get_SI16(bit_stream); /* FontAscent */
		FontDescent = SWF_BIT_STREAM_get_SI16(bit_stream) / 1024.0f;
		SWF_BIT_STREAM_get_SI16(bit_stream); /* FontLeading */
	} else {
		FontDescent = 0.0f;
	}

	font = calloc(1, sizeof(SWF_FONT));
	if(!font) {
		DIE(); /* メモリ不足 */
	}
	/*{{初期化*/
	font->character.character_type = SWF_CHARACTER_TYPE_FONT;
	font->character.CharacterId    = CharacterId;
	font->FontFlagsWideOffsets     = FontFlagsWideOffsets;
	font->OffsetTable              = OffsetTable;
	font->FontFlagsWideCodes       = FontFlagsWideCodes;
	font->CodeTable                = CodeTable;
	font->FontFlagsHasLayout       = FontFlagsHasLayout;
	font->FontDescent              = FontDescent;
	font->FontFlagsBold            = FontFlagsBold;
#ifndef PIECE
	font->FontNameLen              = FontNameLen;
	font->FontName                 = FontName;
	font->FontFlagsItalic          = FontFlagsItalic;
#endif /*PIECE*/
	/*}}初期化*/
	InsertTailList(&swf.dictionary, &font->character.list_entry);
}

/*****************************************************************************
 *	グローバル関数
 *****************************************************************************/

void
swf_init(const void* data)
{
	static const SWF_CHARACTER character; /* 唯一のルートオブジェクト用のダミーキャラクタ */
	SWF_BIT_STREAM bit_stream = { data }; /* SWF File Headerを読むためのビットストリーム */
	SWF_OBJECT* object;

	/* まず、確実に未初期化に戻します。 */
	swf_free();

	/* SWF File Headerを検査します。 */
	if(memcmp(SWF_BIT_STREAM_get_data(&bit_stream, 3), "FWS", 3)) { /* Signature */
		DIE(); /* 不正なデータ */
	}
	if(SWF_BIT_STREAM_get_UI8(&bit_stream) > 4) { /* Version */
		//DIE(); /* 未対応 */
		//→旧いタグだけならば、処理できる可能性があるので、強行してみます。
	}
	SWF_BIT_STREAM_get_SI32(&bit_stream); /* FileLength */
	SWF_BIT_STREAM_get_RECT(&bit_stream, (SWF_RECT*)&swf.swf_info.FrameSize);
	swf.swf_info.FrameRate  = SWF_BIT_STREAM_get_UI16(&bit_stream) / 256.0f;
	swf.swf_info.FrameCount = SWF_BIT_STREAM_get_UI16(&bit_stream);

	/* 唯一のルートオブジェクトを作成します。 */
	//memset(&swf, 0, sizeof swf); → swf_free()でmemset()済みだから不要。ここでmemset()するとswf_infoが消えてしまうので不可。*/
	InitializeListHead(&swf.dictionary); /* ディクショナリ初期化 */
	InitializeListHead(&swf.display_list); /* 唯一のルートオブジェクトを登録するための、ディスプレイリスト初期化 */
	object = SWF_DISPLAY_LIST_new_object(&swf.display_list, 0);
	object->character       = (SWF_CHARACTER*)&character; /* 唯一のルートオブジェクト用のダミーキャラクタ */
	object->object_state   |= SWF_OBJECT_STATE_PLAY;
	object->bit_stream.data = SWF_BIT_STREAM_get_data(&bit_stream, 0); /* ループ時のために、最初のタグの位置をbit_pos=0とする */
}

void
swf_free()
{
	/* 未初期化でなければ、ルートオブジェクトを開放します。 */
	if(swf.display_list.Flink) {
		SWF_DISPLAY_LIST_free(&swf.display_list);
	}

	/* 未初期化でなければ、ディクショナリを開放します。 */
	if(swf.dictionary.Flink) {
		SWF_DICTIONARY_free();
	}

	/* 未初期化に戻します。 */
	memset(&swf, 0, sizeof swf);
}

const SWF_INFO*
swf_info()
{
	/* SWF情報の構造体のアドレスを返します。 */
	return &swf.swf_info;
}

void
swf_exec()
{
	/* 唯一のルートオブジェクトを実行します。
	 * これにより、唯一のルートオブジェクトが、階層構造を再帰的に実行します。
	 */
	SWF_DISPLAY_LIST_exec(&swf.display_list);

	/* 唯一のルートオブジェクトのアクションを実行します。
	 * これにより、唯一のルートオブジェクトが、階層構造を再帰的に実行します。
	 */
	SWF_DISPLAY_LIST_exec_action(&swf.display_list);
}

void
swf_set_text_drawing_method(int text_drawing_method)
{
	/* 静止テキストの描画方法を格納します。 */
	swf.text_drawing_method = text_drawing_method;
}

#ifdef PIECE
void
swf_draw(RENDER* render)
{
	/* 唯一のルートオブジェクトを描画します。
	 * これにより、唯一のルートオブジェクトが、階層構造を再帰的に描画します。
	 */
	SWF_DISPLAY_LIST_draw(&swf.display_list, render, &mat2f_1);
}
#endif /*PIECE*/

