Class: Omnizip::Formats::Rar3::Reader

Inherits:
Omnizip::Formats::Rar::RarFormatBase show all
Defined in:
lib/omnizip/formats/rar3/reader.rb

Overview

RAR v3 archive reader

Reads RAR 3.x format archives, parsing headers and extracting file data according to the RAR v3 specification.

Examples:

Reading a RAR3 archive

reader = Rar3::Reader.new
File.open("archive.rar", "rb") do |file|
  entries = reader.read_archive(file)
  entries.each { |entry| puts entry.name }
end

Instance Attribute Summary

Attributes inherited from Omnizip::Formats::Rar::RarFormatBase

#spec, #version

Instance Method Summary collapse

Methods inherited from Omnizip::Formats::Rar::RarFormatBase

#block_type_code, #block_type_name, #compress, #compression_method_code, #compression_method_name, #decompress, #dictionary_size_code, #encryption_algorithm, #supports_feature?, #verify_magic_bytes, #write_archive

Constructor Details

#initializeReader

Initialize a RAR v3 reader



25
26
27
# File 'lib/omnizip/formats/rar3/reader.rb', line 25

def initialize
  super("rar3")
end

Instance Method Details

#read_archive(io) ⇒ Array<Entry>

Read a RAR v3 archive

Parameters:

  • io (IO)

    The input stream

Returns:

  • (Array<Entry>)

    The archive entries

Raises:



34
35
36
37
38
39
40
41
42
43
44
45
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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/omnizip/formats/rar3/reader.rb', line 34

def read_archive(io)
  unless verify_magic_bytes(io)
    raise FormatError, "Invalid RAR v3 signature"
  end

  entries = []

  # RAR4 marker is the signature itself (7 bytes): "Rar!\x1a\x07\x00"
  # Skip past the marker to read the archive header
  io.seek(7, ::IO::SEEK_SET)

  # Read first block - could be archive header or file block (for minimal archives)
  first_block = read_block_header(io)

  if first_block.type == block_type_code(:archive)
    # Standard archive with archive header
    @archive_flags = first_block.flags

    # Skip past archive header block (header + data)
    # SIZE field contains total block size
    block_end = first_block.header_start + first_block.size
    io.seek(block_end, ::IO::SEEK_SET)
  elsif first_block.type == block_type_code(:file)
    # Minimal archive without archive header - process as file block
    entry = read_file_entry(io, first_block)
    entries << entry if entry
  else
    raise FormatError,
          "Expected archive header or file header, got type #{first_block.type}"
  end

  # Read file blocks until end
  loop do
    block = read_block_header(io)
    break if block.type == block_type_code(:terminator)

    case block.type
    when block_type_code(:file)
      entry = read_file_entry(io, block)
      entries << entry if entry
    when block_type_code(:comment)
      read_comment_block(io, block)
    when block_type_code(:recovery)
      skip_block_data(io, block)
    else
      skip_block_data(io, block)
    end
  end

  entries
rescue EOFError, FormatError
  # Handle truncated or malformed files gracefully
  entries
end