/*	
 *	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()に、タグの読み出しが終端を超過した場合に停止するよう、検査コードを追加しました。
 */
#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_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_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_SPRITE	5

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 FontFlagsShiftJIS : 1;
	int FontFlagsBold     : 1;
} SWF_FONT;

typedef struct _SWF_TEXT {
	SWF_CHARACTER character; /* 基底クラス */
	SWF_FONT* font;
	int TextColor;
	const char* InitialText;
} SWF_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 */
} 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(); /* メモリ不足 */
	}
	/*{{初期化*/
	InitializeListHead(&bitmap->character.list_entry);
	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_STOP	0
#define SWF_OBJECT_STATE_PLAY	1

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;
} /*SWF_OBJECT*/;

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);
#ifdef PIECE
static void SWF_DISPLAY_LIST_draw(LIST_ENTRY* display_list, RENDER* render, const mat2f* m);
#endif /*PIECE*/

static void SWF_OBJECT_free(SWF_OBJECT* self);
static void SWF_OBJECT_exec(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_DoAction(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream);            // 12 DoAction
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_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(); /* メモリ不足 */
	}
	/*{{初期化*/
	InitializeListHead(&object->list_entry);
	object->Depth  = Depth;
	object->Name   = "";
	object->Matrix = SWF_MATRIX_1;
	InitializeListHead(&object->display_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);
	}
}

#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_OBJECT_free(SWF_OBJECT* self) {
	SWF_DISPLAY_LIST_free(&self->display_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
				SWF_DISPLAY_LIST_free(&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 12: // 12 DoAction
				fn = SWF_OBJECT_DoAction;
				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 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
	}
	SWF_DISPLAY_LIST_exec(&self->display_list);
}

#ifdef PIECE
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* shape = (SWF_SHAPE*)self->character;
			do {
				mat2f m3 = m1;
				mat2f m4 = {
					shape->BitmapMatrix.ScaleX, shape->BitmapMatrix.RotateSkew1, shape->BitmapMatrix.TranslateX,
					shape->BitmapMatrix.RotateSkew0, shape->BitmapMatrix.ScaleY, shape->BitmapMatrix.TranslateY,
				};
				mat2f_xform(&m3, &m4);
				if(m3.a00 == 1.0f && m3.a01 == 0.0f &&
				   m3.a10 == 0.0f && m3.a11 == 1.0f) {
					/* 拡縮回転無しで、平行移動だけの場合は、等倍描画を使うことにします。
					 * 平行移動だけの場合の方が多いと思うので、高速化と見栄えのためです。
					 */
					TEXTURE* texture = &shape->bitmap->texture;
					int x = floorf(m3.a02 + 0.5f);
					int y = floorf(m3.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 = &shape->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(&m3, &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);
				}
				shape = shape->next;
			} while(shape);
		}
		break;
	case SWF_CHARACTER_TYPE_TEXT:
		{
			SWF_TEXT* text = (SWF_TEXT*)self->character;
			int x = floorf(m1.a02 + 0.5f);
			int y = floorf(m1.a12 + 0.5f);
			int type = text->font->FontFlagsShiftJIS ? 0 : 2;
			int color = text->TextColor;
			unsigned int (*fn)(RENDER* render, int x, int y, const char* str, int type, int color);
			if(text->font->FontFlagsBold) {
				fn = render_string_framed;
				if(color <= 1) {
					color |= 0x330; /* ボールドの代わりに、逆の色で縁取り */
				}
			} else {
				fn = render_string;
			}
			(*fn)(render, x, y, text->InitialText, type, color);
		}
		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
	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_STOP;
				}
				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
			}
		}
	}
}

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(); /* メモリ不足 */
					}
					/*{{初期化*/
					InitializeListHead(&shape->character.list_entry);
					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,
			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,
			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;
	int CharacterId;
	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);

	object = SWF_DISPLAY_LIST_get_object(&self->display_list, Depth);
	if(PlaceFlagHasCharacter) {
		if(PlaceFlagMove) {
			if(object) {
				SWF_OBJECT_free(object);
			}
		}
		CharacterId = SWF_BIT_STREAM_get_UI16(bit_stream);
		character = SWF_DICTIONARY_get_character(CharacterId);
		if(!character) {
			return; /* 未定義 */
		}
		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;
		}
	} else {
		if(PlaceFlagMove) {
			if(!object) {
				return; /* 未定義 */
			}
		} else {
			DIE(); /* 不正なデータ */
		}
	}
	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);
	}
}

static void SWF_OBJECT_DefineEditText(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream) { // 37 DefineEditText
	int CharacterId;
	int HasText;
	int HasTextColor;
	int HasMaxLength;
	int HasFont;
	int HasFontClass;
	int HasLayout;
	int FontId;
	SWF_FONT* font;
	SWF_RGBA TextColor;
	const char* InitialText;
	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, NULL);  /* 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; /* 未対応 */
	}

	text = calloc(1, sizeof(SWF_TEXT));
	if(!text) {
		DIE(); /* メモリ不足 */
	}
	/*{{初期化*/
	InitializeListHead(&text->character.list_entry);
	text->character.character_type = SWF_CHARACTER_TYPE_TEXT;
	text->character.CharacterId    = CharacterId;
	text->font                     = font;
	text->TextColor                = ((((TextColor.Red * 30) + (TextColor.Green * 59) + (TextColor.Blue * 11)) / 100) >> 6) ^ 3;
	text->InitialText              = InitialText;
	/*}}初期化*/
	InsertTailList(&swf.dictionary, &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(); /* メモリ不足 */
	}
	/*{{初期化*/
	InitializeListHead(&sprite->character.list_entry);
	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_DefineFont2(SWF_OBJECT* self, SWF_BIT_STREAM* bit_stream) { // 48 DefineFont2
	int CharacterId;
	int FontFlagsShiftJIS;
	int FontFlagsBold;
	SWF_FONT* font;

	CharacterId = SWF_BIT_STREAM_get_UI16(bit_stream);
	if(SWF_DICTIONARY_get_character(CharacterId)) {
		return; /* 1ループ目で定義済み */
	}
	SWF_BIT_STREAM_get_UB(bit_stream, 1); /* FontFlagsHasLayout */
	FontFlagsShiftJIS = SWF_BIT_STREAM_get_UB(bit_stream, 1);
	SWF_BIT_STREAM_get_UB(bit_stream, 1); /* FontFlagsSmallText */
	SWF_BIT_STREAM_get_UB(bit_stream, 1); /* FontFlagsANSI */
	SWF_BIT_STREAM_get_UB(bit_stream, 1); /* FontFlagsWideOffsets */
	SWF_BIT_STREAM_get_UB(bit_stream, 1); /* FontFlagsWideCodes */
	SWF_BIT_STREAM_get_UB(bit_stream, 1); /* FontFlagsItalic */
	FontFlagsBold = SWF_BIT_STREAM_get_UB(bit_stream, 1);

	font = calloc(1, sizeof(SWF_FONT));
	if(!font) {
		DIE(); /* メモリ不足 */
	}
	/*{{初期化*/
	InitializeListHead(&font->character.list_entry);
	font->character.character_type = SWF_CHARACTER_TYPE_FONT;
	font->character.CharacterId    = CharacterId;
	font->FontFlagsShiftJIS        = FontFlagsShiftJIS;
	font->FontFlagsBold            = FontFlagsBold;
	/*}}初期化*/
	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);
	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);
}

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

