Class: Cabriolet::CAB::Decompressor
- Inherits:
-
Object
- Object
- Cabriolet::CAB::Decompressor
- Defined in:
- lib/cabriolet/cab/decompressor.rb
Overview
Decompressor is the main interface for CAB file operations
Instance Attribute Summary collapse
-
#buffer_size ⇒ Object
Returns the value of attribute buffer_size.
-
#fix_mszip ⇒ Object
Returns the value of attribute fix_mszip.
-
#io_system ⇒ Object
readonly
Returns the value of attribute io_system.
-
#parser ⇒ Object
readonly
Returns the value of attribute parser.
-
#salvage ⇒ Object
Returns the value of attribute salvage.
-
#search_buffer_size ⇒ Object
Returns the value of attribute search_buffer_size.
Instance Method Summary collapse
-
#append(cabinet, next_cabinet) ⇒ Boolean
Append a cabinet to another, merging their folders and files.
-
#create_decompressor(folder, input, output) ⇒ Decompressors::Base
Create appropriate decompressor for a folder.
-
#extract_all(cabinet, output_dir, **options) ⇒ Integer
Extract all files from the cabinet.
-
#extract_file(file, output_path, **options) ⇒ Integer
Extract a single file from the cabinet.
-
#initialize(io_system = nil) ⇒ Decompressor
constructor
Initialize a new CAB decompressor.
-
#open(filename) ⇒ Models::Cabinet
Open and parse a CAB file.
-
#prepend(cabinet, prev_cabinet) ⇒ Boolean
Prepend a cabinet to another, merging their folders and files.
-
#search(filename) ⇒ Models::Cabinet?
Search for embedded CAB files within a file.
Constructor Details
#initialize(io_system = nil) ⇒ Decompressor
Initialize a new CAB decompressor
13 14 15 16 17 18 19 20 |
# File 'lib/cabriolet/cab/decompressor.rb', line 13 def initialize(io_system = nil) @io_system = io_system || System::IOSystem.new @parser = Parser.new(@io_system) @buffer_size = Cabriolet.default_buffer_size @fix_mszip = false @salvage = false @search_buffer_size = 32_768 end |
Instance Attribute Details
#buffer_size ⇒ Object
Returns the value of attribute buffer_size.
8 9 10 |
# File 'lib/cabriolet/cab/decompressor.rb', line 8 def buffer_size @buffer_size end |
#fix_mszip ⇒ Object
Returns the value of attribute fix_mszip.
8 9 10 |
# File 'lib/cabriolet/cab/decompressor.rb', line 8 def fix_mszip @fix_mszip end |
#io_system ⇒ Object (readonly)
Returns the value of attribute io_system.
7 8 9 |
# File 'lib/cabriolet/cab/decompressor.rb', line 7 def io_system @io_system end |
#parser ⇒ Object (readonly)
Returns the value of attribute parser.
7 8 9 |
# File 'lib/cabriolet/cab/decompressor.rb', line 7 def parser @parser end |
#salvage ⇒ Object
Returns the value of attribute salvage.
8 9 10 |
# File 'lib/cabriolet/cab/decompressor.rb', line 8 def salvage @salvage end |
#search_buffer_size ⇒ Object
Returns the value of attribute search_buffer_size.
8 9 10 |
# File 'lib/cabriolet/cab/decompressor.rb', line 8 def search_buffer_size @search_buffer_size end |
Instance Method Details
#append(cabinet, next_cabinet) ⇒ Boolean
Append a cabinet to another, merging their folders and files
86 87 88 |
# File 'lib/cabriolet/cab/decompressor.rb', line 86 def append(cabinet, next_cabinet) merge_cabinets(cabinet, next_cabinet) end |
#create_decompressor(folder, input, output) ⇒ Decompressors::Base
Create appropriate decompressor for a folder
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/cabriolet/cab/decompressor.rb', line 59 def create_decompressor(folder, input, output) case folder.compression_method when Constants::COMP_TYPE_NONE Decompressors::None.new(@io_system, input, output, @buffer_size) when Constants::COMP_TYPE_MSZIP Decompressors::MSZIP.new(@io_system, input, output, @buffer_size, fix_mszip: @fix_mszip) when Constants::COMP_TYPE_LZX window_bits = folder.compression_level Decompressors::LZX.new(@io_system, input, output, @buffer_size, window_bits: window_bits) when Constants::COMP_TYPE_QUANTUM window_bits = folder.compression_level Decompressors::Quantum.new(@io_system, input, output, @buffer_size, window_bits: window_bits) else raise UnsupportedFormatError, "Unsupported compression type: #{folder.compression_method}" end end |
#extract_all(cabinet, output_dir, **options) ⇒ Integer
Extract all files from the cabinet
48 49 50 51 |
# File 'lib/cabriolet/cab/decompressor.rb', line 48 def extract_all(cabinet, output_dir, **) extractor = Extractor.new(@io_system, self) extractor.extract_all(cabinet, output_dir, **) end |
#extract_file(file, output_path, **options) ⇒ Integer
Extract a single file from the cabinet
37 38 39 40 |
# File 'lib/cabriolet/cab/decompressor.rb', line 37 def extract_file(file, output_path, **) extractor = Extractor.new(@io_system, self) extractor.extract_file(file, output_path, **) end |
#open(filename) ⇒ Models::Cabinet
Open and parse a CAB file
27 28 29 |
# File 'lib/cabriolet/cab/decompressor.rb', line 27 def open(filename) @parser.parse(filename) end |
#prepend(cabinet, prev_cabinet) ⇒ Boolean
Prepend a cabinet to another, merging their folders and files
96 97 98 |
# File 'lib/cabriolet/cab/decompressor.rb', line 96 def prepend(cabinet, prev_cabinet) merge_cabinets(prev_cabinet, cabinet) end |
#search(filename) ⇒ Models::Cabinet?
Search for embedded CAB files within a file
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
# File 'lib/cabriolet/cab/decompressor.rb', line 104 def search(filename) search_buf = Array.new(@search_buffer_size) first_cabinet = nil link_cabinet = nil first_len = 0 false_cabs = 0 handle = @io_system.open(filename, Constants::MODE_READ) file_length = handle.size # Check for InstallShield header at start of file if file_length >= 4 header = @io_system.read(handle, 4) @io_system.seek(handle, 0, Constants::SEEK_START) if header.unpack1("V") == 0x28635349 @io_system.(handle, "WARNING; found InstallShield header. Use unshield " \ "(https://github.com/twogood/unshield) to unpack this file") end end offset = 0 while offset < file_length # Calculate read length length = [file_length - offset, @search_buffer_size].min # Read chunk @io_system.seek(handle, offset, Constants::SEEK_START) bytes_read = @io_system.read(handle, length) break if bytes_read.nil? || bytes_read.empty? search_buf[0, bytes_read.bytesize] = bytes_read.bytes # Search for cabinets in this chunk cab_offset = find_cabinet_in_buffer(search_buf, bytes_read.size, offset, handle, filename, file_length) if cab_offset # Try to parse cabinet at this offset cabinet = try_parse_cab_at_offset(handle, filename, cab_offset) if cabinet # Capture first cabinet length first_len = cabinet.length if cab_offset.zero? # Link into list if first_cabinet.nil? first_cabinet = cabinet else link_cabinet.next = cabinet end link_cabinet = cabinet # Continue searching after this cabinet offset = cab_offset + cabinet.length else false_cabs += 1 # Restart search after signature offset = cab_offset + 4 end else # No cabinet found in this chunk, move to next offset += length end end @io_system.close(handle) # Warn about truncated/extra data if first_len.positive? && first_len != file_length && (first_cabinet.nil? || first_cabinet.base_offset.zero?) if first_len < file_length @io_system.(handle, "WARNING; possible #{file_length - first_len} extra bytes at end of file.") else @io_system.(handle, "WARNING; file possibly truncated by #{first_len - file_length} bytes.") end end if false_cabs.positive? && Cabriolet.verbose @io_system.(handle, "#{false_cabs} false cabinets found") end first_cabinet rescue StandardError @io_system.close(handle) if handle raise end |