[C#]Flash(.swf)のヘッダ読み取り

C#で開発しているソフトからFlashの情報を読み取りたい!
ってコトがあって、ネットで調べてみても、
なかなか「これ!」っていうピッタリな情報が見つからなくて、
色々な情報を参考にして何とか実装できたので記しておくー

AxShockwaveFlashクラスとか使えば、
C#のフォーム上で再生は出来るんだけどねぇ・・・^^;
初期サイズが取れないっぽい。。。

まずFlashである.swfファイルのヘッダの詳細は、
SWF のヘッダ情報を読み解く」(Tanablog様)
ここがとっても参考になった。

ただ少し誤記があって、「ステージの幅と高さ」が
「9 ~ 17 バイト」と固定長になってるけど、
仕様的には可変長みたい。
詳しくは後で書くとして・・・

C#で読み取るにあたって、
基本的にはFileStreamクラスなどで、
先頭から↑のサイト通りに読めばおk。
ただ先頭の3バイトが”CWS”になっている場合は、
圧縮されているから解凍処理も必要。

圧縮されたswfに関して調べてみると、
C#で解凍を試みてる方を発見。
圧縮SWFファイル(CWSファイル)を
展開する方法を調べてC#で試してみた
」(mitc様)
先頭8バイト移行が全てzlib圧縮されてるコトが分かった。
その肝心の解凍処理にサードパーティ製のライブラリを使用されてる・・・

出来ればサードパーティ製のライブラリは避けたいから、
なんとか標準の機能だけで解凍する方法を探してみると、
また素晴らしい情報を発見。
Zlib.dll? Zlib.NET? DeflateStream?」(うぃずのひとりごと様)
.Net標準で実装されてるDeflateStreamクラスとzlib圧縮が
ヘッダの有無が違うだけで、実際の仕組みは全く同じみたい!

要するに最初、FileStreamクラスで開いて、
そのまま8バイトまで読み取って先頭が”CWS”なら、
zlib圧縮のヘッダ2バイト分シークして、
そのままDeflateStreamクラスに突っ込めば、
後は非圧縮のときと同じ処理で読めばいける!

そうして読み取った最初のデータがまた曲者で・・・。
始めの方にも少し書いた「ステージの幅と高さ」。
これを実際に扱える値にするまでが異様にややこしい・・・

とりあえず始めのサイトに書いてある読み方を引用。

  1. バイト列を 2 進数に直す
  2. 最初の 5 ビットを見る→ 01111 → 15。これは、続く数字が 15 ビットずつの区切りということを表す。
  3. 次の 15 ビットを見る→ 000000000000000 →0。これは X 座標の最小値を表す。
  4. 次の 15 ビットを見る→ 010101011111000 →11000 twips。これは X 座標の最大値を表す。1 twip = 1/20 ピクセルなので 550 ピクセルとなる。
  5. 次の 15 ビットを見る→ 000000000000000 →0。これは Y 座標の最小値を表す。
  6. 次の 15 ビットを見る→ 001111101000000 →8000 twips。これは Y 座標の最大値を表す。1 twip = 1/20 ピクセルなので 400 ピクセルとなる。

この最初の5bitで取得した後のビット数によって、
後のバイト数も変化するみたいだから可変長が正解。
そして区切りのビット数まで可変長となると、
読むのが非常に面倒くさい・・・
これに関しては自力で頑張った^^;

これで読み取るにあたっての情報は出揃ったので、
最後に作ったクラスを載っけておくー

public class FlashAnalyzer
{
	/// <summary>
	/// シグネチャを取得します。
	/// </summary>
	public string Signature { get; private set; }

	/// <summary>
	/// ファイルバージョンを取得します。
	/// </summary>
	public int Version { get; private set; }

	/// <summary>
	/// ファイルサイズを取得します。
	/// </summary>
	public uint FileLength { get; private set; }

	/// <summary>
	/// フレームサイズを取得します。
	/// </summary>
	public Rectangle FrameSize { get; private set; }

	/// <summary>
	/// フレームレートを取得します。
	/// </summary>
	public float FrameRate { get; private set; }

	/// <summary>
	/// フレーム数を取得します。
	/// </summary>
	public int FrameCount { get; private set; }

	/// <summary>
	/// FlashAnalyzer クラスを初期化し、swfファイルのヘッダを読み取ります。
	/// </summary>
	/// <param name="swfPath">読み取るswfのファイルパス</param>
	/// <exception cref="System.IO.InvalidDataException">
	/// 指定されたswfファイルの形式が正しくない場合に発生
	/// </exception>
	public FlashAnalyzer(string swfPath)
	{
		byte[] buf = new byte[3];

		using (var fStream = new System.IO.FileStream(
			swfPath, System.IO.FileMode.Open,
			System.IO.FileAccess.Read,
			System.IO.FileShare.Read))
		{
			System.IO.BinaryReader reader;

			// シグネチャを読み取り
			fStream.Read(buf, 0, 3);
			if ((buf[0] == 'F' || buf[0] == 'C') && buf[1] == 'W' && buf[2] == 'S')
			{
				// シグネチャ
				this.Signature = Encoding.ASCII.GetString(buf);
				// BinaryReaderを生成
				reader = new System.IO.BinaryReader(fStream);
				// プレーヤーバージョン
				this.Version = reader.ReadByte();
				// ファイルサイズ
				this.FileLength = reader.ReadUInt32();

				if (buf[0] == 'C')	// 圧縮
				{
					// zlibのヘッダをスキップ
					fStream.Seek(2, System.IO.SeekOrigin.Current);
					// Deflateストリームを生成
					var deflate = new System.IO.Compression.DeflateStream(
						fStream, System.IO.Compression.CompressionMode.Decompress, true);
					// BinaryReaderを再生成
					reader = new System.IO.BinaryReader(deflate);
				}
			}
			else
			{
				throw new System.IO.InvalidDataException
					("指定されたファイルの形式が正しくありません。");
			}

			// フレームサイズを表すデータ
			byte b = reader.ReadByte();
			int bitLen = b >> 3;
			int ofs = 5;
			buf = new byte[(int)Math.Ceiling((bitLen * 4 + 5) / 8.0f)];
			buf[0] = b;
			reader.Read(buf, 1, buf.Length - 1);

			// フレームサイズ
			int left = ExtractBits(buf, ofs, bitLen) / 20;
			ofs += bitLen;
			int right = ExtractBits(buf, ofs, bitLen) / 20;
			ofs += bitLen;
			int top = ExtractBits(buf, ofs, bitLen) / 20;
			ofs += bitLen;
			int bottom = ExtractBits(buf, ofs, bitLen) / 20;
			this.FrameSize = Rectangle.FromLTRB(left, top, right, bottom);

			// フレームレート
			ushort rawRate = reader.ReadUInt16();
			this.FrameRate = (rawRate >> 8) + (rawRate & 0x0F) / 100.0f;

			// フレーム数
			this.FrameCount = reader.ReadUInt16();
		}
	}

	private int ExtractBits(byte[] array, int bitIndex, int bitLength)
	{
		int i = bitIndex / 8;
		int len = bitIndex % 8;

		bitLength -= 8 - len;
		int ret = (array[i++] & (0xFF >> len)) << bitLength;

		for (; bitLength >= 8; ++i)
		{
			bitLength -= 8;
			ret |= array[i] << bitLength;
		}

		if (bitLength > 0)
			ret |= array[i] >> (8 - bitLength);

		return ret;
	}
}

ExtractBits関数の部分は、かなり怪しい(汗)
とりあえずいくつかのswfファイルを
読み取ってみたけど、正常に読み取れてそう。

使用は自己責任で。
でも不具合がある場合は教えて頂けると・・・(笑)

じゃ、ゲームして寝るー
バイニー☆

test?

コメントを残す