圧縮SWFファイル(CWSファイル)を展開する方法を調べてC#で試してみた


圧縮SWFファイルとは何か

前の日記で色々なファイルをffmpegにかけていたところ、次のようなエラーで処理できないファイルがありました。

[swf @ 0034A5F0]Compressed SWF format not supported
in.swf: I/O error occurred
Usually that means that input file is truncated and/or corrupted.

検索してみたところ、swfファイルの中には圧縮されたものがあり、それは一旦展開しないとffmpegで扱えないとのこと。

参考:windows上のffmpegでCompressed SWFを変換する

さらに検索してみると色々情報とCWS->SWF展開の実装が出てきました。
(圧縮SWFファイルの呼び方がわからなかったので、便宜上CWSファイルと呼びます)

まとめると、次のようになります。

  1. CWSファイルは、先頭3バイトが0x43’C’,0x57’W’,0x53’S’になっている
  2. CWSファイルは、先頭8バイトがヘッダで、残りの部分が丸ごとzlibで圧縮されている
  3. CWSファイルの先頭の’C’を’F’に変更し、zlib圧縮されている部分を展開してくっつければSWFファイルに変換できる

単純に先頭3バイトを見て”CWS”と書いてあればそれは圧縮されているので、先頭8バイトを取り除いてzlibで展開し、最後に取り除いた8バイトの1バイト目を0x46’F”に書き換えzlibで展開したデータと結合して出力すればOKということに。

CWSファイルとSWFファイルをバイナリエディタで見たところ*1
binary_dump.png
一番手前がSWFファイルで、後ろ3枚がCWSファイル。
先頭3バイトがSWFファイルは”FWS”、CWSファイルは”CWS”となっているのがわかります。

*1 : 使用バイナリエディタはStirling

アーカイバライブラリzlib

ここで問題なのが、「zlibで展開」という部分。
サンプルではどれも純正のzlibを使っていたんですが、Windowsには普通はインストールされていないし、純正のzlib共有モジュールはマネージコードから直接使えないわけで……。
そもそも「zlib形式」で圧縮されているというのがよくわからず、zlibについて調べたところWikipediazlib入門サイトによるとgzip(tar.gzのgz)形式のことなのかな……と推測。
gzipを扱う方法を色々試してみました。
結果は次のようになるのですが、ZLIB.NET以外ではデータ形式が違うと門前払い。
zlib形式でありgzip形式とは違う……ということなのでしょうか?わかりません。

参考:オリジナルのzlib
http://www.zlib.net/

System.IO.Compression.GZipStream

byte[] body = 先頭8バイトを除去したCWSファイルのバイナリ列
MemoryStream bodyStream = new MemoryStream(body);
byte[] buffer = new byte[255];
int length = 0;
System.IO.Compression.GZipStream objGZipStream = new System.IO.Compression.GZipStream(bodyStream, System.IO.Compression.CompressionMode.Decompress);
List<byte> res = new List<byte>();
while (true)
{
length = objGZipStream.Read(buffer, 0, buffer.Length);
if (length <= 0) break;
for (int idx = 0; idx < length; idx++) res.Add(buffer[idx]);
}
body = res.ToArray();

InvalidDataExceptionで次のようなメッセージが返ります

GZip ヘッダーのマジック ナンバーが適切ではありません。GZip ストリームを渡していることを確認してください。

SharpZipLib

byte[] body = 先頭8バイトを除去したCWSファイルのバイナリ列
MemoryStream bodyStream = new MemoryStream(body);
byte[] buffer = new byte[255];
int length = 0;
ICSharpCode.SharpZipLib.GZip.GZipInputStream objGZipInputStream = new ICSharpCode.SharpZipLib.GZip.GZipInputStream(bodyStream);
List<byte> res = new List<byte>();
while (true)
{
length = objGZipInputStream.Read(buffer, 0, buffer.Length);
if (length <= 0) break;
for (int idx = 0; idx < length; idx++) res.Add(buffer[idx]);
}
body = res.ToArray();

GZipExceptionでこんなメッセージが返ります

Error GZIP header, first magic byte doesn’t match

ZLIB.NET

byte[] body = 先頭8バイトを除去したCWSファイルのバイナリ列
MemoryStream bodyStream = new MemoryStream(body);
byte[] buffer = new byte[255];
int length = 0;
zlib.ZInputStream objZlibStream = new zlib.ZInputStream(bodyStream);
List<byte> res = new List<byte>();
while (true)
{
// ReadだとBinaryReaderクラスのメソッドを読んでしまうので注意
length = objZlibStream.read(buffer, 0, buffer.Length);
if (length <= 0) break;
for (int idx = 0; idx < length; idx++) res.Add(buffer[idx]);
}
body = res.ToArray();

C#でCWSファイルからSWFファイルへの変換を行う

というわけで、以上の情報を元にCWSファイルをSWFファイルに変換するプログラムは簡単に作れます。

サンプル

簡単なサンプルのソースコードは次のような感じ。

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using zlib;
namespace CwsTest
{
class Program
{
static void Main(string[] args)
{
// 第一引数を入力ファイルパス
string input = args[0];
// 第二引数が出力ファイルパス
string output = args[1];
// CWSヘッダのサイズ
int SIZE_HEADER = 8;
// ファイルからバイト列に読み込み
List<byte> src = new List<byte>(File.ReadAllBytes(input));
// 先頭8バイト分をヘッダとして読み込み
byte[] header = src.GetRange(0, SIZE_HEADER).ToArray();
// 残りをzlib展開対象として取得
byte[] body = src.GetRange(SIZE_HEADER, src.Count - SIZE_HEADER).ToArray();
// ZLib.NETに渡すためにバイト列をストリームに接続
using (MemoryStream bodyStream = new MemoryStream(body))
{
// zlib展開読み込みバッファのサイズ
int BUFFER_SIZE = 255;
// Zlib.NETの展開用クラスはZInputStream
ZInputStream zis = new ZInputStream(bodyStream);
// 通常のストリームと同様に末尾まで読み込んでいく
List<byte> res = new List<byte>();
byte[] buffer = new byte[BUFFER_SIZE];
int len = 0;
while (true)
{
len = zis.read(buffer, 0, buffer.Length);
if (len == -1) break;
for (int lci = 0; lci < len; lci++) res.Add(buffer[lci]);
}
body = res.ToArray();
zis.Close();
bodyStream.Close();
}
// ヘッダの一文字目をFにしてヘッダと本体を結合して書き出す
List<byte> after = new List<byte>();
header[0] = Convert.ToByte('F');
after.AddRange(header);
after.AddRange(body);
File.WriteAllBytes(output, after.ToArray());
}
}
}

サンプルダウンロード:CwsTest.zip

次のように使います

CwsTest.exe 入力CWSファイル.swf 出力SWFファイル名.swf

ライブラリ

折角なので変換ツールとライブラリを作成してみました。
必要な環境は.NET Framework 2.0です。

ダウンロード:CsCws2Swf.zip

使い方

・”CsCws2FwsLib.dll”を参照に追加
・名前空間は”CsCws2FwsLib”
・参照できる場所に”zlib.dll”を置く

new CsCws2FwsEngine().Cws2Fws("入力CWSファイルパス", "出力SWFファイルパス");

Leave a Comment


NOTE - You can use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>