SWF Binary Code Golf on FlashLite 1.1 サイボウズ・ラボ株式会社 竹迫 良範 http://labs.cybozu.co.jp/blog/takesako/
質問
Q. この意味が わかる人? ”CWS\006”
正解: “CWS\006” = 43 57 53 06 SWFファイルを16進数でダンプ 000000 43 57 53 06 fa 00 00 00 78 9c 5d cf 41 0a c2 30 000010 10 85 e1 3f 6d 15 0d 45 8b 1b 37 29 d8 0b 48 45 000020 5c a7 58 70 5d 3c 41 97 7a 00 37 3d 48 a0 0b af 000030 22 de 4c a7 29 85 28 84 b7 78 f3 4d 60 6e 64 15 000040 49 06 a9 a2 8e 00 1b bf 24 dd 16 0e 65 09 97 7b 000050 d7 fd a4 69 72 b3 7e 46 f4 b8 14 ce 8f 61 e0 d3 000060 b8 f9 44 5c 22 db 27 ac b0 0d 4e 8f b5 47 7d c4 000070 95 00 c6 30 ba 95 6f 27 b3 ff 33 c7 c0 f8 56 8c 000080 0e 4c 31 a0 1d 45 2e 7d fd a1 59 2a 5a 6d df f2 000090 da 85 ff 73 a6 14 0a 2a 39 ed 0b 95 09 2f ff 00009f +00 (3byte) : File magic “FWS” or “CWS” – 圧縮 +04 (1byte) : File version 6
Adobe Player Licensing から入手可能 SWFファイルフォーマットの仕様書 Adobe Player Licensing から入手可能 http://www.adobe.com/licensing/developer/ メールアドレス(Adobe ID)を登録 ライセンスに同意する必要がある Flash6以降の情報のみしか記載されていない Alexis‘ SWF Reference (お勧め) http://sswf.sourceforge.net/SWFalexref.html Flash6以前の情報についても書いてある Flash 1.0 とか Flash 4.0 (FlashLite 1.1相当) 2001年からの蓄積(これはすごい)
Alexis' SWF Reference : : : : http://sswf.sourceforge.net/SWFalexref.html : : : :
SWF File Header, SWF Tags… struct swf_header { unsigned char f_magic[3]; 'FWS' or 'CWS' unsigned char f_version; unsigned long f_file_length; } struct swf_header_movie { swf_rect f_frame_size; unsigned short fixed f_frame_rate; unsigned short f_frame_count; }; struct swf_csmtextsettings { swf_tag f_tag; /* 74 */ unsigned short f_text_id_ref; unsigned f_use_flag_type : 2; unsigned f_grid_fit : 3; unsigned f_reserved : 3; long float f_thickness; long float f_sharpness; unsigned char f_reserved; };
SWFファイルを DISアセンブル しながら勉強したい…
swfdumpコマンドで DISアセンブル SWFTOOLS の swfdump コマンドを使うと SWFファイルの内容をダンプすることができる http://www.swftools.org/documentation.html > swfdump --full FlashProxy.swf [HEADER] File version: 8 [HEADER] File is zlib compressed. Ratio: 63% [HEADER] File size: 796 (Depacked) [HEADER] Frame rate: 20.000000 [HEADER] Frame count: 1 [HEADER] Movie width: 450.00 [HEADER] Movie height: 325.00 [009] 3 SETBACKGROUNDCOLOR (ff/ff/ff) [027] 4 DEFINESPRITE defines id 20480 [000] 0 END [038] 21 EXPORTASSETS exports 20480 as "__Packages.MTASC" [03b] 643 DOINITACTION adds information to id 20480 ( 206 bytes) action: Constantpool(22 entries) String:"FlashProxy" String:"_global" String:"target" String:"_root" String:"className" String:"dispatch" String:"flash" String:"external" String:"ExternalInterface" String:"addCallback" String:"addListener" String:"prototype" String:"Function" String:"apply" String:"event" String:"arguments" String:"id" String:"FlashProxy.onTrigger" String:"call" String:"main" String:"proxy" String:"ASSetPropFlags" ( 2 bytes) action: Push Lookup:0 ("FlashProxy") ( 0 bytes) action: GetVariable ( 0 bytes) action: Not ( 2 bytes) action: If 418
swfdumpは新しいアクションレコードが苦手 : ( 8 bytes) action: unknown[8e] (remainder of 8 bytes:"\0\0\0\2)\0p\0") アクションレコード[8e]の抽出に失敗 … orz [8e] Declare Function (Flash Version 7 で追加されたバイトコード)
Sothink SWF Decompiler (試用期間30日) http://www.sothink.com/product/flashdecompiler/
DISアセンブル結果(Sothink SWF Decompiler)
あと CPAN の SWF::File もお勧め dumpswf.plx dumpswf Flash.swf Flash Flash.swf そのものを生成する Perlスクリプト「Flash.pl」を生成 Windows ActivePerl なら ppm install SWF-File ですぐに使えるよ http://www.nmt.ne.jp/~ysas/butaperl/swf/File.sjis.pod.html
Sothink SWF Decompiler (試用期間30日) DISアセンブルまとめ Swftools – swfdump タグやバイトコードのデータ構造を理解できる Flash V6相当のSWFファイルならそこそこいける Sothink SWF Decompiler (試用期間30日) できればフリーのがいいなぁ ぼく ActionScript わからないし(><) SWF::File – dumpswf.plx DISアセンブルした結果が「Perlスクリプト」になる さらにそれを修正してSWFを再生成できる
Flasm 知らなかった! (><) こんな便利なものがあったとは・・・ http://flasm.sourceforge.net/ Yossyさん情報より
本題 前フリ ここまで
いったい何バイトになるんだろう? (ActionScript で意味のあるコード) 世界で一番小さい SWFファイル いったい何バイトになるんだろう? (ActionScript で意味のあるコード)
最小の “Hello world!\n” プログラム Code Golf チャレンジ 57 byte 46 57 53 04 4b 00 00 00 60 00 3f c0 00 3f c0 00 0c 02 00 43 02 33 33 33 17 03 96 12 00 00 6f 00 00 48 65 6c 6c 6f 20 77 6f 72 6c 64 21 0a 00 1d 00 4d 09 01 00 60 0a 3e 80 0a 3e 80 60 08 6f 00 05 01 01 00 01 00 00 40 00 00 00 http://namazu.org/~takesako/swf/ hello57.swf traceを使わないパターンなので、開発環境がなくても表示可能 (traceを使ってよければもっと小さくなるかも)
hello57.swf コード解説 SWF File magic (4byte) 46 57 53 04 4b 00 00 00 60 00 3f c0 00 3f c0 00 0c 02 00 43 02 33 33 33 17 03 96 12 00 00 6f 00 00 48 65 6c 6c 6f 20 77 6f 72 6c 64 21 0a 00 1d 00 4d 09 01 00 60 0a 3e 80 0a 3e 80 60 08 6f 00 05 01 01 00 01 00 00 40 00 00 00 struct swf_header { unsigned char f_magic[3]; 'FWS' or 'CWS' unsigned char f_version; unsigned long f_file_length; }
32bit整数(リトルエンディアン) ファイルサイズ情報 (4byte) 46 57 53 04 4b 00 00 00 60 00 3f c0 00 3f c0 00 0c 02 00 43 02 33 33 33 17 03 96 12 00 00 6f 00 00 48 65 6c 6c 6f 20 77 6f 72 6c 64 21 0a 00 1d 00 4d 09 01 00 60 0a 3e 80 0a 3e 80 60 08 6f 00 05 01 01 00 01 00 00 40 00 00 00 struct swf_header { unsigned char f_magic[3]; 'FWS' or 'CWS' unsigned char f_version; unsigned long f_file_length; } 57 = 4b 00 00 00
Rect構造体 (可変サイズ) が入っている swf_header_movie Rect構造体 (可変サイズ) が入っている 46 57 53 04 4b 00 00 00 60 00 3f c0 00 3f c0 00 0c 02 00 43 02 33 33 33 17 03 96 12 00 00 6f 00 00 48 65 6c 6c 6f 20 77 6f 72 6c 64 21 0a 00 1d 00 4d 09 01 00 60 0a 3e 80 0a 3e 80 60 08 6f 00 05 01 01 00 01 00 00 40 00 00 00 struct swf_header_movie { swf_rect f_frame_size; unsigned short fixed f_frame_rate; unsigned short f_frame_count; };
これ重要、テストにでます Rect構造体 60 00 3f c0 00 3f c0 をデコード 可変 5bit 12bit 12bit 12bit 12bit Zero padding | ssss sxxx | xxxx xxxx | xXXX XXXX | XXXX Xyyy | yyyy yyyy | yYYY YYYY | YYYY Y000 | | 0110 0000 | 0000 0000 | 0011 1111 | 1100 0000 | 0000 0000 | 0011 1111 | 1100 0000 | | 6 0 | 0 0 | 3 f | c 0 | 0 0 | 3 f | c 0 | f_size = sssss(5bit) = 011000 = 12 ※次から符号付整数が12bit(可変)×4個並ぶ f_x_min = xxxxxxxxxxxx(12bit) = 0 twips f_x_max = XXXXXXXXXXXX(12bit) = +2040 twips (104px) f_y_min = yyyyyyyyyyyy(12bit) = 0 twips f_y_max = YYYYYYYYYYYY(12bit) = +2040 twips (104px) 2^12 = -2047~+2047 の数値範囲 struct swf_rect { char align; unsigned f_size : 5; signed twips f_x_min : f_size; signed twips f_x_max : f_size; signed twips f_y_min : f_size; signed twips f_y_max : f_size; };
フレームレート(2byte) 例:12.0 frame/秒 swf_header_movie フレームレート(2byte) 例:12.0 frame/秒 46 57 53 04 4b 00 00 00 60 00 3f c0 00 3f c0 00 0c 02 00 43 02 33 33 33 17 03 96 12 00 00 6f 00 00 48 65 6c 6c 6f 20 77 6f 72 6c 64 21 0a 00 1d 00 4d 09 01 00 60 0a 3e 80 0a 3e 80 60 08 6f 00 05 01 01 00 01 00 00 40 00 00 00 struct swf_header_movie { swf_rect f_frame_size; unsigned short fixed f_frame_rate; unsigned short f_frame_count; }; 8.8bit固定小数点形式 frame rate (例) 12.0 = 00 0c
swf_header_movie フレーム総数 (16bit整数) 46 57 53 04 4b 00 00 00 60 00 3f c0 00 3f c0 00 0c 02 00 43 02 33 33 33 17 03 96 12 00 00 6f 00 00 48 65 6c 6c 6f 20 77 6f 72 6c 64 21 0a 00 1d 00 4d 09 01 00 60 0a 3e 80 0a 3e 80 60 08 6f 00 05 01 01 00 01 00 00 40 00 00 00 struct swf_header_movie { swf_rect f_frame_size; unsigned short fixed f_frame_rate; unsigned short f_frame_count; }; 16bit整数 frame count 通常は自動計算
お疲れ様でした ここまで SWFヘッダ終了! 46 57 53 04 4b 00 00 00 60 00 3f c0 00 3f c0 00 00 48 65 6c 6c 6f 20 77 6f 72 6c 64 21 0a 00 1d 00 4d 09 01 00 60 0a 3e 80 0a 3e 80 60 08 6f 00 05 01 01 00 01 00 00 40 00 00 00 お疲れ様でした
SWFファイルの基本構造 図で表すと・・・ ・ SWFヘッダ情報(ファイル先頭) タグ情報(TagID、size) いろいろ ActionScriptのバイトコードも タグ構造体の中に入る ・ タグ情報(TagID、size) END で終了
[HEADER] File version: 4 [HEADER] File size: 75 [HEADER] Frame rate: 12.000000 [HEADER] Frame count: 2 [HEADER] Movie width: 102.00 [HEADER] Movie height: 102.00 [009] 3 SETBACKGROUNDCOLOR (33/33/33) [00c] 23 DOACTION ( 18 bytes) action: Push String:"o" String:"Hello world!\n" ( 0 bytes) action: SetVariable ( 0 bytes) action: End [025] 13 DEFINEEDITTEXT defines id 0001 variable "o" [004] 5 PLACEOBJECT places id 0001 at depth 0001 | Matrix | CXForm r g b a | 1.000 0.000 0.00 | mul 1.0 1.0 1.0 1.0 | 0.000 1.000 0.00 | add 0 0 0 0 [001] 0 SHOWFRAME 1 (00:00:00,000) [000] 0 END
(2) SWFタグ構造体(可変サイズ) 46 57 53 04 4b 00 00 00 60 00 3f c0 00 3f c0 00 00 48 65 6c 6c 6f 20 77 6f 72 6c 64 21 0a 00 1d 00 4d 09 01 00 60 0a 3e 80 0a 3e 80 60 08 6f 00 05 01 01 00 01 00 00 40 00 00 00 ややこしいことに bit計算しないとタグIDとサイズがわからない! struct swf_tag { unsigned short f_tag_and_size; f_tag = f_tag_and_size >> 6; f_tag_data_size = f_tag_and_size & 0x3F; if(f_tag_data_size == 63) { unsigned long f_tag_data_real_size; } else { f_tag_data_real_size = f_tag_data_size; }; 43 02 ↓ TagID:09 Size:3 SetBackgroundColor(TagID:09) → RR GG BB (3byte)
4 3 0 2 タグ構造体のヘッダ(2byteの場合) 1. Sizeが62byte以下の場合 t1 t0 s5 s4 s3 s2 s1 s0 t9 t8 t7 t6 t5 t4 t3 t2 0 1 0 0 0 0 1 1 0 0 0 0 0 0 1 0 4 3 0 2 TagID = t0*2^0 + t1*2^1 + t2*2^2 + t3*2^3 + … t9*2^9 = t0*1 +t3*8 = 1 + 8 = 9 Size = s0*2^0 + s1*2^1 + s2*2^2 + s3*2^3 + … t5*2^5 = s0*1 +s1*2 = 1 + 2 = 3
3 f 0 3 a 6 0 1 0 0 0 0 タグ構造体のヘッダ(6byteの場合) 2. Sizeが63byte以上の場合 t1 t0 s5 s4 s3 s2 s1 s0 t9 t8 t7 t6 t5 t4 t3 t2 0 0 1 1 1 1 1 1 0 0 0 0 0 0 1 1 3 f 0 3 TagID = t0*2^0 + t1*2^1 + t2*2^2 + t3*2^3 + … t9*2^9 = t2*4 +t3*8 = 4 + 8 = 12 Size = 2^0 + 2^1 + 2^2 + 2^3 + 2^4 + 2^5 = 63 (0x3f) ← magic number 本当のSize情報は次の4byte(32bit整数) で表す a 6 0 1 422 byte 0 0 0 0
たとえば、タグのデータはこんな感じのバイト列に変換されます 演習問題 たとえば、タグのデータはこんな感じのバイト列に変換されます 3byteのデータ 442byteのデータ SetBgColor (TagID= 9, Size=3) → 43 02 RR GG BB doAction (TagID=12,Size=422)→ 3f 03 a6 01 00 00 xx … xx PlaceObject (TagID= 4, Size=7) → 07 01 xx xx xx xx xx xx xx PlaceObject2 (TagID=26, Size=8) → 88 06 xx xx xx xx xx xx xx xx ShowFrame (TagID= 1, Size=0) → 40 00 End (TagID= 0, Size=0) → 00 00 ※ size の大きさは TagID の種類やデータのフラグによって異なります(可変長に対応)
(3) やっとここで doAction 46 57 53 04 4b 00 00 00 60 00 3f c0 00 3f c0 00 00 48 65 6c 6c 6f 20 77 6f 72 6c 64 21 0a 00 1d 00 4d 09 01 00 60 0a 3e 80 0a 3e 80 60 08 6f 00 05 01 01 00 01 00 00 40 00 00 00 TagID Size [00c] 23 DOACTION ( 18 bytes) action: Push String:"o" String:"Hello world!\n" ( 0 bytes) action: SetVariable ( 0 bytes) action: End 17 03 = TagID:12 Size:23byte __bytecode__(”96120000...”) がここに出現!
__bytecode__ について わかりやすい資料 参考 http://www.be-interactive.org/index.php?itemid=235 Powered by yossyさん
(4) DefinedEditText 46 57 53 04 4b 00 00 00 60 00 3f c0 00 3f c0 00 00 48 65 6c 6c 6f 20 77 6f 72 6c 64 21 0a 00 1d 00 4d 09 01 00 60 0a 3e 80 0a 3e 80 60 08 6f 00 05 01 01 00 01 00 00 40 00 00 00 TagID Size [025] 13 DEFINEEDITTEXT defines id 0001 variable "o" [004] 5 PLACEOBJECT places id 0001 at depth 0001 | Matrix | CXForm r g b a | 1.000 0.000 0.00 | mul 1.0 1.0 1.0 1.0 | 0.000 1.000 0.00 | add 0 0 0 0 [001] 0 SHOWFRAME 1 (00:00:00,000) [000] 0 END
(5) PlaceObject 46 57 53 04 4b 00 00 00 60 00 3f c0 00 3f c0 00 0c 02 00 43 02 33 33 33 17 03 96 12 00 00 6f 00 00 48 65 6c 6c 6f 20 77 6f 72 6c 64 21 0a 00 1d 00 4d 09 01 00 60 0a 3e 80 0a 3e 80 60 08 6f 00 05 01 01 00 01 00 00 40 00 00 00 TagID Size [025] 13 DEFINEEDITTEXT defines id 0001 variable "o" [004] 5 PLACEOBJECT places id 0001 at depth 0001 | Matrix | CXForm r g b a | 1.000 0.000 0.00 | mul 1.0 1.0 1.0 1.0 | 0.000 1.000 0.00 | add 0 0 0 0 [001] 0 SHOWFRAME 1 (00:00:00,000) [000] 0 END
まめ知識:Matrix構造体も可変長 ここで、f_has_scale=0, f_has_rotate=0 とすれば2byte以上の節約、 struct swf_matrix { char align; unsigned f_has_scale : 1; if(f_has_scale) { unsigned f_scale_bits : 5; signed fixed f_scale_x : f_scale_bits; signed fixed f_scale_y : f_scale_bits; } unsigned f_has_rotate : 1; if(f_has_rotate) { unsigned f_rotate_bits : 5; signed fixed f_rotate_skew0 : f_rotate_bits; signed fixed f_rotate_skew1 : f_rotate_bits; unsigned f_translate_bits : 5; signed f_translate_x : f_rotate_bits; signed f_translate_y : f_rotate_bits; }; ここで、f_has_scale=0, f_has_rotate=0 とすれば2byte以上の節約、 TranslateX, TranslateY の数値範囲を小さくすればさらに節約できる
(6) ShowFrame 46 57 53 04 4b 00 00 00 60 00 3f c0 00 3f c0 00 0c 02 00 43 02 33 33 33 17 03 96 12 00 00 6f 00 00 48 65 6c 6c 6f 20 77 6f 72 6c 64 21 0a 00 1d 00 4d 09 01 00 60 0a 3e 80 0a 3e 80 60 08 6f 00 05 01 01 00 01 00 00 40 00 00 00 TagID Size [025] 13 DEFINEEDITTEXT defines id 0001 variable "o" [004] 5 PLACEOBJECT places id 0001 at depth 0001 | Matrix | CXForm r g b a | 1.000 0.000 0.00 | mul 1.0 1.0 1.0 1.0 | 0.000 1.000 0.00 | add 0 0 0 0 [001] 0 SHOWFRAME 1 (00:00:00,000) [000] 0 END
(7) End 46 57 53 04 4b 00 00 00 60 00 3f c0 00 3f c0 00 0c 02 00 43 02 33 33 33 17 03 96 12 00 00 6f 00 00 48 65 6c 6c 6f 20 77 6f 72 6c 64 21 0a 00 1d 00 4d 09 01 00 60 0a 3e 80 0a 3e 80 60 08 6f 00 05 01 01 00 01 00 00 40 00 00 00 TagID Size [025] 13 DEFINEEDITTEXT defines id 0001 variable "o" [004] 5 PLACEOBJECT places id 0001 at depth 0001 | Matrix | CXForm r g b a | 1.000 0.000 0.00 | mul 1.0 1.0 1.0 1.0 | 0.000 1.000 0.00 | add 0 0 0 0 [001] 0 SHOWFRAME 1 (00:00:00,000) [000] 0 END
おわり
FizzBuzzやってみました おまけ
携帯でも動くよ FizzBuzzではじめるABC入門 FizzBuzz Binary Golf on FlashLite1.1/2.0 http://namazu.org/~takesako/swf/ 携帯でも動くよ trace使ってないので FlashLite1.1 で 251 byte まで (Flash4相当…)、 FlashLite2.0 で 159 byte まで削減できました! yossyさんのASバイトコードの話が聞けるっていうので、 予習として SWF Code Golf やってみました。
ベースのActionScriptの部分はこんな感じで。 FizzBuzz スタート ベースのActionScriptの部分はこんな感じで。 for (i = 1; i <= 100; i++) { 変数 x に以下の文字列を順に追記する ((i%15)?((i%5)?((i%3)?i:"Fizz"):"Buzz"):"FizzBuzz"); } DefineEditText で 変数 x を関連付け
しかし実際どのバイトコードの機能が使えるのかWebの情報からわからない FlashLite について FlashLite は Flash4ベース Flash5 の一部の機能も使える http://m-school.biz/event/files/2006_0602_flash_lite/FlashLite1x20060602.pdf しかし実際どのバイトコードの機能が使えるのかWebの情報からわからない (例)Math.sin は使えるけど、それはコンパイラが四則演算で近似値を求めるアクションのバイトコード列に変換してくれるから → ActionScriptの文法的に使える・使えないとは違う
FlashLite1.1で動くFlash5のバイトコード 鴨志田さん(サイボウズ・ラボ)調べによる http://labs.cybozu.co.jp/blog/kamoshida/ [4D] Swap [4C] Duplicate [50] Increment [51] Decrement [60] BitAnd [61] BitOr [62] BitXor [3F] Modulo [63] ShiftLeft [64] ShiftRight [65] ShiftRightUnsigned FlashLite1.1 (Flash4相当)で これらのFlash5の命令が使える 残念ながら Modulo などの命令は FlashLite2.0 からサポート
ASバイトコードをさらに最適化 とりあえず (1) 正解のFizzBuzz文字列をそのまま出力 -> 478 byte (FlashLite1.1) http://namazu.org/~takesako/swf/fizz478.swf http://namazu.org/~takesako/swf/fizz478.txt (2) FizzBuzzを計算しながら出力 -> 251 byte (FlashLite1.1) http://namazu.org/~takesako/swf/fizz251.swf http://namazu.org/~takesako/swf/fizz251.txt (3) FizzBuzzを計算しながら出力 -> 159 byte (FlashLite2.0) http://namazu.org/~takesako/swf/fizz159.swf http://namazu.org/~takesako/swf/fizz159.txt ここまで圧縮できました。
バイトコード最適化のテクニック 鴨志田さん情報 西尾さんのアイデア 社内でいろいろ教えてもらいました!(><)ノ まとめpush http://labs.cybozu.co.jp/blog/kamoshida/2007/01/flash_push.html http://labs.cybozu.co.jp/blog/kamoshida/2007/03/flashlite_2.html FlashLiteの剰余計算 http://labs.cybozu.co.jp/blog/kamoshida/2007/01/flashlite_1.html 西尾さんのアイデア 変数の初期化を省略 i=0 の場合は、変数を初期化しなくても i++ で 1 となる 圧縮率を考慮したプログラミング 圧縮率が高くなるよう変数のネーミングを変える 社内でいろいろ教えてもらいました!(><)ノ
(例) fizz287.swf → fizz161.swf --- fizz287.flm +++ fizz161.flm @@ -1,71 +1,48 @@ -movie 'fizz287.swf' // flash 4, total frames: 1, frame rate: 12 fps, 104x104 px +movie 'fizz161.swf' compressed // flash 6, total frames: 1, frame rate: 12 fps, 104x104 px 287(Flash4圧縮なし) – 161(Flash6圧縮あり) = 126 byte の削減 frame 0 - push 'i', '0' - setVariable label1: - push 'i', 'i' + push '100', 'i', 'i', 'i' getVariable increment setVariable - push '100', 'i' oldLessThan - not branchIfTrue label6 - push 'x', 'x', 'x' - getVariable - push 'i' + push 'x', 'x' push 'i' push '15' - divide - int - push '15' - multiply - subtract + modulo branchIfTrue label2 push 'FizzBuzz' branch label5 label2: push 'i' getVariable - push 'i' - getVariable push '5' - push '5' branchIfTrue label3 push 'Buzz' label3: push 'i' getVariable - push 'i' - getVariable push '3' - divide - int - push '3' - multiply - subtract + modulo branchIfTrue label4 push 'Fizz' branch label5 label4: label5: concat push ' ' setVariable branch label1 label6: end // of frame 0 end
SWF Binary Code Golf on FlashLite 1.1 以上、 ご清聴ありがとうございました