Flash SWF 画像入れ替え(PHP 編)

前回の課題のうち、ファイルサイズの更新処理と CWS の圧縮処理を入れて、 JPEG画像入れ替えまで実装してみた。

CWS の圧縮で問題発生

PHP Warning:  gzuncompress(): buffer error in /home/yoya/prog/flash/FlashSWF.php on line 42

google で探しても解決した例が見つからないので、php のソースを読んでみた。
(yumPHP 5.1.6 (cli) を使っているが、)とりあえず最新版の 5.2.5 を取得。

  • php の中にはメッセージがない
% pwd
/home/yoya/src/php-5.2.5
% grep "buffer error" * */* */*/*
%
  • zlib の中を探す
% grep "buffer error" *.c
zutil.c:"buffer error",        /* Z_BUF_ERROR     (-5) */
zutil.c (メッセージを定義してる場所) 
const char * const z_errmsg[10] = {
"need dictionary",     /* Z_NEED_DICT       2  */
"stream end",          /* Z_STREAM_END      1  */
"",                    /* Z_OK              0  */
"file error",          /* Z_ERRNO         (-1) */
"stream error",        /* Z_STREAM_ERROR  (-2) */
"data error",          /* Z_DATA_ERROR    (-3) */
"insufficient memory", /* Z_MEM_ERROR     (-4) */
"buffer error",        /* Z_BUF_ERROR     (-5) */
"incompatible version",/* Z_VERSION_ERROR (-6) */
""};

#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)]
  • compress.c (実際に出力する場所)
     compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
   memory, Z_BUF_ERROR if there was not enough room in the output buffer,
   Z_STREAM_ERROR if the level parameter is invalid.
	<略>
   if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
    err = deflate(&stream, Z_FINISH);
    if (err != Z_STREAM_END) {
        deflateEnd(&stream);
        return err == Z_OK ? Z_BUF_ERROR : err;

伸張する時に長さを指定するけど、実際に伸張したらその長さを超えちゃったってだけか…
つまり、ファイル長フィールドの更新に失敗してると… ○rz
chara.swf と(それを read して write した) output.swf はファイルサイズが同じなんだけど。(そうなるように圧縮率を調整したし)

  addr :  0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x00000: 4357 5306 0048 2c00 789c 9cb9 6b5c 5259  CWS  H, x   k\RY
                   ~~~~~~~~
  • output.swf
  addr :  0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x00000: 4357 5306 a86f 2800 789c 9cb9 6b5c 5259  CWS  o( x   k\RY
                   ~~~~~~~~

確かに駄目だ

圧縮失敗の原因:

if ($magic == 'CWS') {
	movie_and_body = gzcompress($movie_and_body, 6);
}
<略>
$this->_header['filelength'] = strlen($header_without_length) + 4 + strlen($movie_and_body);

えーっと… ファイル長フィールドには伸張した時のファイルサイズを入れないと駄目なのに、圧縮後のファイルサイズを入れてました…
そりゃ、伸張した時にバッファも足りなくなるさ… ○rz

$this->_header['filelength'] = strlen($header_without_length) + 4 + strlen($movie_and_body);
<略>
if ($magic == 'CWS') {
	movie_and_body = gzcompress($movie_and_body, 6);
}

こうしないと。

問題 (chara.swf から吸い出した画像が表示できない)

saitama.swf の画像吸出し成功、表示も出来る。
chara.swf の画像吸出せたが、ビューアで表示できない。(;_;
saitama.swf の画像入れ替え成功
chara.swf の画像入れ替えも成功 (DefineBitsJPEG3 は未対応 alpha フィールドがよく分からないので…)
先頭 0x10 分を見る限り問題ない JPEG 画像に見えるので長さが足りないか、途中のどこかを壊してるかってとこだけど、調べるの面倒そう。

課題

  • chara.swf の画像抽出処理を見直す
  • JPEG 画像の SWF 仕様への変換を考える。
  • もう少しブラッシュアップ
  • 異常系を考える (とりあえず FlashSWFException 実装するか…)
  • デバッグ機能を入れる (動作確認をしやすいように)