▼ 2008/10/26(日) 圧縮SWFファイル(CWSファイル)を展開する方法を調べてC#で試してみた
zlib.NET | 1.04 |
---|---|
.NET Framework | 2.0 |
■圧縮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ファイルと呼びます)
- Perl, Pythonでの例
- PHP拡張/C言語での例
- C言語での例
- CWSファイルは、先頭3バイトが0x43'C',0x57'W',0x53'S'になっている
- CWSファイルは、先頭8バイトがヘッダで、残りの部分が丸ごとzlibで圧縮されている
- CWSファイルの先頭の'C'を'F'に変更し、zlib圧縮されている部分を展開してくっつければSWFファイルに変換できる
CWSファイルとSWFファイルをバイナリエディタで見たところ*1

一番手前がSWFファイルで、後ろ3枚がCWSファイル。
先頭3バイトがSWFファイルは"FWS"、CWSファイルは"CWS"となっているのがわかります。
*1 : 使用バイナリエディタはStirling
■アーカイバライブラリzlib
ここで問題なのが、「zlibで展開」という部分。サンプルではどれも純正のzlibを使っていたんですが、Windowsには普通はインストールされていないし、純正のzlib共有モジュールはマネージコードから直接使えないわけで……。
そもそも「zlib形式」で圧縮されているというのがよくわからず、zlibについて調べたところWikipediaやzlib入門サイトによると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ファイルパス");
▼ コメント(0件)
- TB-URL http://mitc.s279.xrea.com/adiary.cgi/089/tb/