2009-04-18 8 views
2

私はへの解決策に取り組んでいます。これは、PNGの 'zTXt'チャンクのデータを読み込んでいます。私は、ファイル内のチャンクの位置を特定し、zTXtのキーワードを読む限りです。私はzTXtの圧縮された部分の読み込みに問題があります。私は以前はDeflateStreamオブジェクトで作業したことがありませんし、それにいくつか問題があります。読み込み時には、lengthパラメータが 'uncompressed'バイトであると思われます。私の場合、私は「圧縮された」バイトのデータの長さしか知りません。これをうまく回避するために、圧縮解除する必要があるすべてのデータをMemoryStreamに格納してから、DeflateStreamを使用して「読み込み終了」します。これは単なるpeachyです。ただし、 "ブロック長がその補数と一致しません"というメッセージとともにInvalidDataExceptionがスローされる点が異なります。今私はこれがどういう意味か分かりません。何がうまくいかないでしょうか?ファイルの一部にDeflateStreamをどのように使用しますか?

チャンクのフォーマットはID( "zTXt")の4バイト、データ長のビッグエンディアンの32ビットint、データ、そして今は無視しているCRC32チェックサムです。

zTXtチャンクのフォーマットは、最初にヌルで終わる(キーワードとしての文字列)、圧縮メソッドの場合は1バイト(常に0、DEFLATEメソッド)で残りのデータは圧縮テキストです。

私のメソッドは、新しいFileStreamを取り込み、zTXtキーワードとデータを含む辞書を返します。ここで

は今モンスターです:

public static List<KeyValuePair<string, string>> GetZtxt(FileStream stream) 
{ 
    var ret = new List<KeyValuePair<string, string>>(); 
    try { 
     stream.Position = 0; 
     var br = new BinaryReader(stream, Encoding.ASCII); 
     var head = br.ReadBytes(8); // The header is the same for all PNGs. 
     if (!head.SequenceEqual(new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A })) return null; // Not a PNG. 
     while (stream.Position < stream.Length) { 
      int len; // Length of chunk data. 
      if (BitConverter.IsLittleEndian) 
       len = BitConverter.ToInt32(br.ReadBytes(4).Reverse().ToArray(), 0); 
      else 
       len = br.ReadInt32(); 

      char[] cName = br.ReadChars(4); // The chunk type. 
      if (cName.SequenceEqual(new[] { 'z', 'T', 'X', 't' })) { 
       var sb = new StringBuilder(); // Builds the null-terminated keyword associated with the chunk. 
       char c = br.ReadChar(); 
       do { 
        sb.Append(c); 
        c = br.ReadChar(); 
       } 
       while (c != '\0'); 
       byte method = br.ReadByte(); // The compression method. Should always be 0. (DEFLATE method.) 
       if (method != 0) { 
        stream.Seek(len - sb.Length + 3, SeekOrigin.Current); // If not 0, skip the rest of the chunk. 
        continue; 
       } 
       var data = br.ReadBytes(len - sb.Length - 1); // Rest of the chunk data... 
       var ms = new MemoryStream(data, 0, data.Length); // ...in a MemoryStream... 
       var ds = new DeflateStream(ms, CompressionMode.Decompress); // ...read by a DeflateStream... 
       var sr = new StreamReader(ds); // ... and a StreamReader. Yeesh. 
       var str = sr.ReadToEnd(); // !!! InvalidDataException !!! 
       ret.Add(new KeyValuePair<string, string>(sb.ToString(), str)); 
       stream.Seek(4, SeekOrigin.Current); // Skip the CRC check. 
      } 
      else { 
       stream.Seek(len + 4, SeekOrigin.Current); // Skip the rest of the chunk. 
      } 
     } 
    } 
    catch (IOException) { } 
    catch (InvalidDataException) { } 
    catch (ArgumentOutOfRangeException) { } 
    return ret; 
} 

これが取り組まれたら、私は、ファイルにこれらのzTXtチャンクを追加する関数を記述する必要があります。ですから、これが解決されたら、DeflateStreamがどのように動作するのかを理解できます。

ありがとうございます!

答えて

5

今回も、私はついにこの問題を発見しました。データはzlib形式であり、DEFLATEだけを使用するよりも少しだけデータが格納されます。私が圧縮されたデータを取得する前に、ちょうど2つの余分なバイトを読み込んだだけでは、ファイルは正しく読み込まれます。

this feedback pageを参照してください。 (私はそれを提出しませんでした)

私は今思っています。これらの2バイトの値は、それぞれ0x78と0x9Cです。それ以外の値を見つけたら、DEFLATEが失敗すると思いますか?

+3

http://www.faqs.org/rfcs/rfc1950.htmlにはZLib形式が記載されています。最初のバイト0x78はCMFと呼ばれ、その値はCM = 8、CINFO = 7を意味します。CM = 8はウィンドウサイズが32Kまでの "deflate"圧縮メソッドを表します。これは、gzipとPNGによって使用されるメソッドです。 CINFO = 7は、32Kのウィンドウサイズを示します。 0x9CはFLGと呼ばれ、値はFLEVEL = 2、CHECK = 28を意味します。FLEVELの情報は圧縮解除には必要ありません。それは再圧縮が価値があるかどうかを示すためのものです。 CHECKは、CMF * 256 + FLGが31の倍数になるように必要な値に設定されます。 –

関連する問題