/// <summary> /// zipファイル内のファイル名を指定しての読み込み。 /// </summary> /// <param name="filename">zipファイル名</param> /// <param name="innerFileName">zip内のファイル名</param> /// <param name="bRead">読み込むのか?読み込むときはbuffにその内容が返る</param> /// <param name="buff"></param> /// <returns> /// bRead=falseのとき、ヘッダ内に該当ファイルが存在すればtrue /// bRead=trueのとき、読み込みが成功すればtrue /// </returns> /// <summary> /// zipファイル内のファイル名を指定しての読み込み。 /// /// 大文字小文字の違いは無視する。 /// </summary> /// <param name="filename">zipファイル名</param> /// <param name="innerFileName">zip内のファイル名</param> /// <param name="bRead">読み込むのか?読み込むときはbuffにその内容が返る</param> /// <param name="buff"></param> /// <returns> /// bRead=falseのとき、ヘッダ内に該当ファイルが存在すればtrue /// bRead=trueのとき、読み込みが成功すればtrue /// </returns> public bool Read(string filename , string innerFileName , bool bRead , out byte[] buff) { // .NET Framework2.0には System.Io.CompressionにGZipStreamというクラスがある。 // これはZipStreamを扱うクラスなのでファイルを扱う部分は自前で用意してやる必要がある // cf. // http://msdn2.microsoft.com/en-us/library/zs4f0x23.aspx buff = null; if ( ! reader.IsExist(filename) ) goto Exit; Stream file = reader.Read(filename); if ( file == null ) goto Exit; // おそらくは存在しないか二重openか。 // 二重openできないので、使いおわったら必ずCloseしなくてはならない。 // そのためにtry~finallyで括ることにする。 try { StreamRandAccessor acc = new StreamRandAccessor(file); Dictionary<string,object> cacheList = null; // 読み込みcacheが有効らしい if ( readCache ) { filename = filename.ToLower(); if ( arcFileList.IsExistArchive(filename) ) { // このarcFileListのなかから探し出す // 格納されているファイル名をToLower等で正規化しておく必要あり object obj; try { innerFileName = innerFileName.ToLower(); obj = arcFileList.ArchiveList[filename][innerFileName]; } catch { return false; // 見つからない } // ここで取得したobjを元にファイルから読み込む InnerZipFileInfo info = obj as InnerZipFileInfo; uint localHeaderPos = info.localHeaderPos; uint compressed_size = info.compressed_size; uint uncompressed_size = info.uncompressed_size; return innerExtract(acc , localHeaderPos , bRead , compressed_size , uncompressed_size , out buff); } // このファイルにファイルリスト読み込むのが先決では… cacheList= new Dictionary<string,object>(); arcFileList.ArchiveList.Add(filename,cacheList); } // Find 'end record index' by searching backwards for signature long stoppos; // ulongって引き算とか比較が面倒くさいですなぁ..(´Д`) if ( file.Length < 66000 ) { stoppos = 0; } else { stoppos = acc.Length - 66000; } long endrecOffset = 0; for ( long i = acc.Length - 22 ; i >= stoppos ; --i ) { if ( acc.GetUint(i) == 0x06054b50 ) { // "PK\0x05\0x06" ushort endcommentlength = acc.GetUshort(i + 20); if ( i + 22 + endcommentlength != acc.Length ) continue; endrecOffset = i; } goto endrecFound; } // ダメジャン goto Exit; endrecFound: ; // ---- endrecOffsetが求まったナリよ! ushort filenum = acc.GetUshort(endrecOffset + 10); // zipに格納されているファイルの数(分割zipは非対応) long c_pos = acc.GetUint(endrecOffset + 16); // central directoryの位置 // printf("filenum %d",filenum); // ---- central directoryが求まったなりよ! while ( filenum-- > 0 ) { if ( acc.GetUint(c_pos) != 0x02014b50 ) { // シグネチャー確認! goto Exit; // return false; // おかしいで、このファイル } uint compressed_size = acc.GetUint(c_pos + 20); uint uncompressed_size = acc.GetUint(c_pos + 24); ushort filename_length = acc.GetUshort(c_pos + 28); ushort extra_field_length = acc.GetUshort(c_pos + 30); ushort file_comment_length = acc.GetUshort(c_pos + 32); //printf("filenamelength : %d",filename_length); // local_header_pos uint lh_pos = acc.GetUint(c_pos + 42); // ファイル名の取得 StringBuilderEncoding fname = new StringBuilderEncoding(filename_length); for ( int i = 0 ; i < filename_length ; ++i ) { fname.Add(acc.GetByte(i + c_pos + 46)); } // printf("%.*name\n",fname); // ファイル名が得られた。 // string fullfilename = dirname + fname; // yield return fullfilename; if ( cacheList!=null ) { // readCacheが有効なら、まずは格納していく。 cacheList.Add(fname.ToString().ToLower() , new InnerZipFileInfo(lh_pos , compressed_size , uncompressed_size)); } else { // ファイル名の一致比較は、大文字小文字の違いは無視する。 // // Windows環境ではファイルシステムは、ファイルの大文字小文字の違いは無視するが、 // いざリリースのときにzipフォルダにまとめたせいでいままで動いていたものが // 動かなくなると嫌なので。 if ( fname.Convert(codePage).Equals(innerFileName , StringComparison.CurrentCultureIgnoreCase) ) { // 一致したでー!これ読み込もうぜー! return innerExtract(acc , lh_pos , bRead , compressed_size , uncompressed_size , out buff); } } // さーて、来週のサザエさんは.. c_pos += 46 + filename_length + extra_field_length + file_comment_length; } } finally { if ( file != null ) file.Dispose(); } // readCacheが有効なのに、ここまで到達するというのは、 // archive内のfile listをcacheしていなかったので調べていたに違いない。 // よって、再帰的に呼び出すことによって解決できる。 if ( readCache ) return Read(filename , innerFileName , bRead , out buff); Exit: ; buff = null; return false; }
public override IEnumerator GetEnumerator() { // ZipStream zip = new GZipStream(filename); // foreach (ZipEntry e in zip) { // yield return e.Name; // } Stream file = null; if ( !reader.IsExist(filename) ) goto Exit; file = reader.Read(filename); if ( file == null ) goto Exit; // おそらくは存在しないか二重openか。 StreamRandAccessor acc = new StreamRandAccessor(file); // Find 'end record index' by searching backwards for signature long stoppos; // ulongって引き算とか比較が面倒くさいですなぁ..(´Д`) if ( file.Length < 66000 ) { stoppos = 0; } else { stoppos = acc.Length - 66000; } long endrecOffset = 0; for ( long i = acc.Length - 22 ; i >= stoppos ; --i ) { if ( acc.GetUint(i) == 0x06054b50 ) { // "PK\0x05\0x06" ushort endcommentlength = acc.GetUshort(i + 20); if ( i + 22 + endcommentlength != acc.Length ) continue; endrecOffset = i; } goto endrecFound; } // ダメジャン goto Exit; endrecFound: ; // ---- endrecOffsetが求まったナリよ! ushort filenum = acc.GetUshort(endrecOffset + 10); // zipに格納されているファイルの数(分割zipは非対応) long c_pos = acc.GetUint(endrecOffset + 16); // central directoryの位置 // printf("filenum %d",filenum); // ---- central directoryが求まったなりよ! while ( filenum-- > 0 ) { if ( acc.GetUint(c_pos) != 0x02014b50 ) { // シグネチャー確認! goto Exit; // return false; // おかしいで、このファイル } uint compressed_size = acc.GetUint(c_pos + 20); uint uncompressed_size = acc.GetUint(c_pos + 24); ushort filename_length = acc.GetUshort(c_pos + 28); ushort extra_field_length = acc.GetUshort(c_pos + 30); ushort file_comment_length = acc.GetUshort(c_pos + 32); //printf("filenamelength : %d",filename_length); // local_header_pos uint lh_pos = acc.GetUint(c_pos + 42); // ファイル名の取得 StringBuilderEncoding fname = new StringBuilderEncoding(filename_length); for ( int i = 0 ; i < filename_length ; ++i ) { fname.Add(acc.GetByte(i + c_pos + 46)); } // printf("%.*name\n",fname); // ファイル名が得られた。 string fullfilename = dirname + fname.Convert(codePage); yield return fullfilename; // さーて、来週のサザエさんは.. c_pos += 46 + filename_length + extra_field_length + file_comment_length; } Exit: ; if ( file != null ) file.Dispose(); }