RARは良い「悪い」アーカイバです
RARは同胞のEugene Roshalによって開発されていることを思い出させてください。 彼から、彼はRoshal Archiverという名前を受け取りました。 この形式は閉鎖されており、ロシアと世界中の両方での配布にはまったく影響しませんでした。 私が見なければならなかったほとんどすべてのワークステーションは、RARアーカイバがインストールされていて
その開発とアーカイバーである間、それはすぐに3番目のバージョン( 4番目になると思います)に成長しました。これはほとんどの「自作」アーカイバーに影響を与えました。 それにもかかわらず、開発者のサイトには、さまざまな開発プラットフォームおよび環境のRARファイルを解凍するための十分な量のあらゆる種類のソースコードが含まれています。
PHPに関しては、 PECL拡張モジュールは「安定した」最初のバージョンに成長しており、ホスティングサイトにインストールされることはほとんどありません。 ちなみに、この拡張機能は、ソースコードがプログラムのWebサイトにある「unrar」を使用します。 さらに、正直に言って、5.3(Windows)で
RAR-どうですか?
データ圧縮のためのソースコードが存在するという事実にもかかわらず、形式の機密性のため、その形式に関するドキュメントはほとんどありません。 当然のことながら、約600 kbのソースコードを検討したい人はほとんどいません。 それにもかかわらず、愛好家はまだいます(私の人のことを考えているなら神は禁じています:)-したがって、 UniquE RAR File Libraryプロジェクトは一度に作成されました。これにより、アーカイバーの2番目のバージョンで作成されたファイルを解凍するためのソースコードが削減されました。
そのため、前述のライブラリのソース、およびアーカイバの古い2.02バージョンに関する最小限の(少なくとも一部の) ドキュメントに出会いました。 それでは、RARアーカイブがどのように見えるかを見てみましょう。
RARアーカイブは、それぞれ7バイトのヘッダーを持つ可変長ブロックで構成されています。 アーカイブには、少なくとも2つのMARK_HEADブロックとMAIN_HEADブロックが含まれます。 最初の行にはRARがあるという情報が含まれており、HEXでは「
52 61 72 21 1a 07 00
」のように見えます。 3番目のバイト
072
は、これがマーカーヘッダーであることを示しています。 リトルエンディアンのワード
00 07
には、ブロックの長さが含まれています。 ちょうど同じ7バイト。
2番目のMain Headerブロックは最初のブロックの直後に始まり、13バイトを含み、マーキングバイトが
0x73
等しい必要があります。 その後、ファイルは既にデータを開始しています-圧縮ファイル(ブロックヘッダーの3バイト目の
074
市場)、アーカイブに関するコメント、追加情報、または回復記録などです。
ファイルのリストを取得するアルゴリズムは複雑ではありません(暗号化されたディレクトリ構造のアーカイブを考慮しない場合、その読み取りはこの記事の範囲外です)。
- ヘッダーの最初の7バイトを読み取ります。 そこで見出しの長さを見つけて、最後まで読みます。
- ブロックが「ファイル」であるかどうかを確認してください。
- 「はい」の場合、7番目の位置のDWORDはアーカイブファイルのサイズ(および次のブロックの前に読み取る必要があるデータの量)、次のダブルワードはソースファイルのサイズ、28の位置にはファイル属性(DWORD)があり、アドレス26と32は、ファイル名の長さ(2バイト)と名前そのものです。 さらに、作成日、ファイルが作成されたOSコード、CRCを確認できます。
- ブロックが「ファイル」ではない場合、3番目の位置の単語を読み取り、その15番目のビットの値を確認する必要があります。これは、ブロックに関連する追加の情報量の原因となります。 この位置で「1」の場合、ADD_SIZEバイト(ブロックヘッダーの後の最初のダブルワード)をスキップする必要があります。
- そして、ファイルの最後まで...
難しいですか? 実際には、.docファイルと比較して。
ソースコード
- //使用せずに$ filenameからファイルのリストを読み取る関数
- // PECL拡張機能rar。
- 関数 rar_getFileList ( $ filename ) {
- //文字列からCOUNTバイトを取得する関数(リトルエンディアン)。
- //関数のグローバル空間を散らかさないために-送信する
- //母体を反転します。
- if ( ! function_exists ( "temp_getBytes" ) )) {
- function temp_getBytes ( $ data 、 $ from 、 $ count ) {
- $ string = substr ( $ data 、 $ from 、 $ count ) ;
- $ string = strrev ( $ string ) ;
- return hexdec ( bin2hex ( $ string ) ) ;
- }
- }
- //ファイルを開こうとします
- $ id = fopen ( $ filename 、 "rb" ) ;
- if ( ! $ id )
- falseを 返し ます 。
- //ファイルがRARアーカイブかどうかを確認します
- $ markHead = fread ( $ id 、 7 ) ;
- if ( bin2hex ( $ markHead ) != "526172211a0700" )
- falseを 返し ます 。
- // MAIN_HEADブロックを読み取ろうとしています
- $ mainHead = fread ( $ id 、 7 ) ;
- if ( ord ( $ mainHead [ 2 ] ) != 0x73 )
- falseを 返し ます 。
- $ headSize = temp_getBytes ( $ mainHead 、 5、2 ) ;
- //ファイル内の最初の「重要な」ブロックの位置に移動します
- fseek ( $ id 、 $ headSize - 7 、 SEEK_CUR ) ;
- $ files = array ( ) ;
- while ( ! feof ( $ id ) ) {
- //ブロックヘッダーを読み取ります
- $ block = fread ( $ id 、 7 ) ;
- $ headSize = temp_getBytes ( $ block 、 5、2 ) ;
- if ( $ headSize <= 7 )
- 休憩 ;
- //ヘッダーの長さに基づいてブロックの残りを読み取ります
- //対応するオフセット
- $ブロック = fread ( $ id 、 $ headSize - 7 ) ;
- //これがファイルブロックの場合、処理を開始します
- if ( ord ( $ block [ 2 ] ) == 0x74 ) {
- //圧縮されたファイルがアーカイブでどれくらいかかるかを調べ、
- //次の位置に移動します。
- $ packSize = temp_getBytes ( $ブロック 、 7、4 ) ;
- fseek ( $ id 、 $ packSize 、 SEEK_CUR ) ;
- //ファイル属性を読み取ります:r-読み取り専用、h-非表示、
- // s-システム、d-ディレクトリ、a-アーカイブ済み
- $ attr = temp_getBytes ( $ block 、 28、4 ) ;
- $属性 = "" ;
- if ( $ attr & 0x01 )
- $ attributes 。= "r" ;
- if ( $ attr & 0x02 )
- $ attributes 。= "h" ;
- if ( $ attr & 0x04 )
- $ attributes 。= "s" ;
- if ( $ attr & 0x10 || $ attr & 0x4000 )
- $属性 = "d" ;
- if ( $ attr & 0x20 )
- $ attributes 。= "a" ;
- //ファイル名、パッキング前後のサイズ、CRC、属性を読み取ります
- $ファイル [ ] = 配列 (
- "ファイル名" => substr ( $ block 、 32 、 temp_getBytes ( $ block 、 26、2 ) ) 、
- "size_compressed" => $ packSize 、
- "size_uncompressed" => temp_getBytes ( $ block 、 11、4 ) 、
- "crc" => temp_getBytes ( $ block 、 16、4 ) 、
- 「属性」 => $ attributes 、
- ) ;
- } else {
- //このブロックがファイルでない場合、可能性を考慮に入れずにスキップします
- //余分なオフセットADD_SIZE
- $ flags = temp_getBytes ( $ block 、 3、2 ) ;
- if ( $ flags & 0x8000 ) {
- $ addSize = temp_getBytes ( $ブロック 、 7、4 ) ;
- fseek ( $ id 、 $ addSize 、 SEEK_CUR ) ;
- }
- }
- }
- fclose ( $ id ) ;
- //ファイルのリストを返します
- $ファイルを 返し ます 。
- }
GitHubにコメントを付けてコードを取得できます 。
文学
さて、いつものように、レビュー用の文献:
見込み
アーカイブからのファイルの読み取りに関しては、理論的には、UniquEからライブラリをリファクタリングすることでPHPで実行できますが、これはバージョン2.90より前に作成されたアーカイブにのみ適しています。 ライブラリは新しいアーカイブを読みません...しかし、あなたはあなた自身が500キロバイトのコードを理解する方法を理解しています。