Class: Omnizip::FormatDetector

Inherits:
Object
  • Object
show all
Defined in:
lib/omnizip/format_detector.rb

Overview

Detects archive format from file signature/magic bytes

This class identifies archive formats based on their file signatures. It distinguishes between XZ Utils format (.xz) and 7-Zip format (.7z) which are DIFFERENT implementations of LZMA/LZMA2 compression.

Examples:

Basic usage

format = Omnizip::FormatDetector.detect("archive.xz")
case format
when :xz
  # Use XZ Utils implementation
when :seven_zip
  # Use 7-Zip implementation
end

Constant Summary collapse

XZ_SIGNATURE =

XZ Utils format signature: "\xFD7zXZ\x00"

[0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00].freeze
SEVEN_ZIP_SIGNATURE =

7-Zip format signature: "7z\xBC\xAF\x27\x1C"

[0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C].freeze
RAR5_SIGNATURE =

RAR5 format signature: "Rar!\x1A\x07\x01\x00"

[0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x01, 0x00].freeze
RAR4_SIGNATURE =

RAR4 format signature: "Rar!\x1A\x07\x00"

[0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x00].freeze
ZIP_SIGNATURE =

ZIP format signature: "PK\x03\x04"

[0x50, 0x4B, 0x03, 0x04].freeze
GZIP_SIGNATURE =

GZIP format signature: "\x1F\x8B"

[0x1F, 0x8B].freeze
BZIP2_SIGNATURE =

BZIP2 format signature: "BZ"

[0x42, 0x5A].freeze
READER_FOR_FORMAT =

Mapping of detected format symbols to (extension, constant-path) tuples. The constant path is resolved lazily so autoload fires only when a format is actually requested.

{
  xz: ["omnizip/formats/xz", "Omnizip::Formats::Xz"],
  seven_zip: ["omnizip/formats/seven_zip", "Omnizip::Formats::SevenZip::Reader"],
  rar5: ["omnizip/formats/rar5", "Omnizip::Formats::Rar5::Reader"],
  rar4: ["omnizip/formats/rar3", "Omnizip::Formats::Rar3::Reader"],
  zip: ["omnizip/formats/zip", "Omnizip::Formats::Zip::Reader"],
  gzip: ["omnizip/formats/gzip", "Omnizip::Formats::Gzip"],
  bzip2: ["omnizip/formats/bzip2_file", "Omnizip::Formats::Bzip2File"],
}.freeze

Class Method Summary collapse

Class Method Details

.detect(file_path) ⇒ Symbol?

Detect archive format from file path

Parameters:

  • file_path (String)

    Path to the archive file

Returns:

  • (Symbol, nil)

    Format identifier (:xz, :seven_zip, :rar5, :rar4, :zip, :gzip, :bzip2, :lzma_alone) or nil if unknown



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/omnizip/format_detector.rb', line 46

def self.detect(file_path)
  return nil unless File.exist?(file_path)

  header = File.binread(file_path, 16)
  return nil if header.nil? || header.empty?

  bytes = header.bytes

  case bytes
  in [0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00, *]
    :xz
  in [0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C, *]
    :seven_zip
  in [0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x01, 0x00, *]
    :rar5
  in [0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x00, *]
    :rar4
  in [0x50, 0x4B, 0x03, 0x04, *]
    :zip
  in [0x1F, 0x8B, *]
    :gzip
  in [0x42, 0x5A, *]
    :bzip2
  else
    # Check for LZMA_Alone format (13-byte header with properties)
    detect_lzma_alone(bytes)
  end
end

.rar4?(file_path) ⇒ Boolean

Check if file is RAR4 format

Parameters:

  • file_path (String)

    Path to the file

Returns:

  • (Boolean)

    true if RAR4 format



103
104
105
# File 'lib/omnizip/format_detector.rb', line 103

def self.rar4?(file_path)
  detect(file_path) == :rar4
end

.rar5?(file_path) ⇒ Boolean

Check if file is RAR5 format

Parameters:

  • file_path (String)

    Path to the file

Returns:

  • (Boolean)

    true if RAR5 format



95
96
97
# File 'lib/omnizip/format_detector.rb', line 95

def self.rar5?(file_path)
  detect(file_path) == :rar5
end

.reader_for(file_path) ⇒ Class?

Get the appropriate reader class for the format

Parameters:

  • file_path (String)

    Path to the archive file

Returns:

  • (Class, nil)

    Reader class or nil if unknown format



124
125
126
127
128
129
130
131
132
133
# File 'lib/omnizip/format_detector.rb', line 124

def self.reader_for(file_path)
  format = detect(file_path)
  return nil unless format

  mapping = READER_FOR_FORMAT[format]
  return nil unless mapping

  _path, const_path = mapping
  Object.const_get(const_path)
end

.seven_zip?(file_path) ⇒ Boolean

Check if file is 7-Zip format

Parameters:

  • file_path (String)

    Path to the file

Returns:

  • (Boolean)

    true if 7-Zip format



87
88
89
# File 'lib/omnizip/format_detector.rb', line 87

def self.seven_zip?(file_path)
  detect(file_path) == :seven_zip
end

.xz?(file_path) ⇒ Boolean

Check if file is XZ Utils format

Parameters:

  • file_path (String)

    Path to the file

Returns:

  • (Boolean)

    true if XZ format



79
80
81
# File 'lib/omnizip/format_detector.rb', line 79

def self.xz?(file_path)
  detect(file_path) == :xz
end