omnizip 0.3.43 → 0.3.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 436437db6697f7363305eda4e6a3e2b78b922aa12b3c43e29756efe187310b60
4
- data.tar.gz: 6e0b17e143c50beae378cdb5b7902e773d888bca14183fcf83e61dfeabe56a5e
3
+ metadata.gz: 358c03758aa086cbb4f3a22845af9f309542df3c272fc640faaaf807d3b2adb0
4
+ data.tar.gz: 96c8735854c4413b2cb802611eb7acd2d4480d1983b5dd4e4e4a45ebf35d3725
5
5
  SHA512:
6
- metadata.gz: f1e20ae301093cacb8c582e1b554a678c72cefa6bf21095d129bebc14b8c4095a68a87da653ef5ea16cb65c00de9edd6ae7b0d92191e49c25e05438ceba264ab
7
- data.tar.gz: a6ad938ab6bdfd75586bcef013ba056cc523681ef34449d03bc44daf18a591e6485b292b7c62bd17c72c13c20c7ff8c45caf4faf6735d5cff79a0c2f4a5a010b
6
+ metadata.gz: 67ab99068276c2eef687be2d7497c911cdb96f920dea552b9392fe860cbc805345c8bf87f5f6640934b09d46b7fc3c598c8bb140a296f37395d2020ff9e0bbd6
7
+ data.tar.gz: 256599715a8f9aa94ee2e94376122a6571b8a7eb0e80548b74e57f14d2d2522a12fc6f3b5566f08c3b6a63692b06e3845a3d8948dfdf24fc53f22d79eebd434d
data/CHANGELOG.md CHANGED
@@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.3.43] - 2026-08-29
11
+
12
+ ### Changed
13
+ - Reader invariant, library-wide: `Formats::SevenZip::Reader` and
14
+ `Formats::Rar::Reader` parse on first use, so `list_files`/
15
+ `extract_entry`/`extract_all` no longer silently operate on empty
16
+ entries when `#open` was not called first. "A reader is always
17
+ usable" is recorded in `CONTEXT.md`; `#open` remains for eager
18
+ loading. New specs pin the behavior for both readers.
19
+ - 7z entries in single-stream folders report their exact
20
+ `compressed_size` (folder pack sizes were parsed but never mapped
21
+ onto entries); solid folders share packed bytes across streams, so
22
+ those entries keep nil rather than a fabricated split.
23
+
10
24
  ## [0.3.42] - 2026-08-29
11
25
 
12
26
  ### Changed
data/lib/omnizip/cli.rb CHANGED
@@ -344,6 +344,17 @@ module Omnizip
344
344
  rescue StandardError => e
345
345
  handle_error(e)
346
346
  end
347
+
348
+ desc "verify ARCHIVE", "Verify archive integrity"
349
+ long_desc <<~DESC
350
+ Verify the integrity of an archive (currently RAR archives via
351
+ header and recovery-record checks).
352
+ DESC
353
+ def verify(archive)
354
+ Omnizip::Commands::ArchiveVerifyCommand.new(options).run(archive)
355
+ rescue StandardError => e
356
+ handle_error(e)
357
+ end
347
358
  end
348
359
 
349
360
  # Command-line interface for Omnizip.
@@ -165,8 +165,8 @@ module Omnizip
165
165
  #
166
166
  # @param archive_path [String] Path to RAR archive
167
167
  # @return [ArchiveVerifier::VerificationResult] Verification result
168
- def verify(archive_path)
169
- ArchiveVerifier.new(archive_path).verify
168
+ def verify(archive_path, use_recovery: true)
169
+ ArchiveVerifier.new(archive_path).verify(use_recovery: use_recovery)
170
170
  end
171
171
 
172
172
  # Repair corrupted archive
@@ -458,6 +458,12 @@ module Omnizip
458
458
  pack_idx += num_streams
459
459
  end
460
460
 
461
+ # Pack streams are laid out sequentially; skip past every
462
+ # stream owned by earlier folders to reach this folder's
463
+ # bytes (with stored archives pack sizes equal unpack
464
+ # sizes, which masked this offset in the past)
465
+ pack_idx.times { |i| pack_pos += @stream_info.pack_sizes[i] || 0 }
466
+
461
467
  # Check if this is a BCJ2 multi-stream folder
462
468
  if Bcj2StreamDecompressor.bcj2_folder?(folder)
463
469
  extract_bcj2_entry(io, entry, folder, pack_pos, pack_idx)
@@ -169,15 +169,20 @@ module Omnizip
169
169
  [lzma2_chunk, [compressed_size]]
170
170
  end
171
171
 
172
- # Build packed data for non-solid mode (COPY - no compression)
172
+ # Build packed data for non-solid mode: every file is its own
173
+ # LZMA2 stream. Empty files keep a zero-size pack stream.
173
174
  def build_non_solid_packed_data(file_data)
174
- # For non-solid mode, concatenate raw file data without compression
175
- packed_data = String.new(encoding: "BINARY")
175
+ packed_data = String.new(encoding: Encoding::BINARY)
176
176
  packed_sizes = []
177
177
 
178
178
  file_data[:streams].each do |stream|
179
- packed_data << stream[:data]
180
- packed_sizes << stream[:size]
179
+ if stream[:size].positive?
180
+ compressed = compress_with_lzma2(stream[:data])
181
+ packed_data << compressed
182
+ packed_sizes << compressed.bytesize
183
+ else
184
+ packed_sizes << 0
185
+ end
181
186
  end
182
187
 
183
188
  [packed_data, packed_sizes]
@@ -255,7 +260,8 @@ module Omnizip
255
260
  num_files)
256
261
  else
257
262
  # Non-solid mode: one pack stream per file, one folder per file
258
- build_non_solid_streams_info(metadata, file_data[:streams])
263
+ build_non_solid_streams_info(metadata, file_data[:streams],
264
+ packed_sizes)
259
265
  end
260
266
 
261
267
  # kEnd for MainStreamsInfo
@@ -375,7 +381,7 @@ _num_files)
375
381
  metadata << [PropertyId::K_END].pack("C")
376
382
  end
377
383
 
378
- def build_non_solid_streams_info(metadata, streams)
384
+ def build_non_solid_streams_info(metadata, streams, packed_sizes)
379
385
  num_streams = streams.size
380
386
 
381
387
  # kPackInfo property (0x06)
@@ -383,10 +389,10 @@ _num_files)
383
389
  metadata << write_number(0) # Pack position
384
390
  metadata << write_number(num_streams) # Number of pack streams
385
391
 
386
- # kSize property - sizes of each pack stream
392
+ # kSize property - sizes of each pack stream (compressed)
387
393
  metadata << [PropertyId::SIZE].pack("C")
388
- streams.each do |stream|
389
- metadata << write_number(stream[:size])
394
+ packed_sizes.each do |packed|
395
+ metadata << write_number(packed)
390
396
  end
391
397
 
392
398
  # kEnd for PackInfo
@@ -402,18 +408,20 @@ _num_files)
402
408
  # External flag for all folders
403
409
  metadata << [0].pack("C")
404
410
 
405
- # Folder definitions (coders only, no sizes yet)
406
- streams.each do |_stream|
407
- # Number of coders
408
- metadata << write_number(1)
409
- # Coder info for COPY
410
- # MainByte format: bits 0-3 = num bytes for CodecID (0-15), bit 4 = is_complex, bits 5-7 = num props
411
- # For COPY: 1 byte CodecID (0x00), no properties
412
- metadata << COPY_MAIN_BYTE # MainByte: 1 byte for CodecID, no props
413
- metadata << COPY_METHOD_ID # Method ID: COPY = 0x00
411
+ # Folder definitions (coders only, no sizes yet):
412
+ # one LZMA2 coder per non-empty stream, COPY for empties
413
+ streams.each do |stream|
414
+ if stream[:size].positive?
415
+ build_folder_coder(metadata)
416
+ else
417
+ metadata << write_number(1)
418
+ metadata << COPY_MAIN_BYTE
419
+ metadata << COPY_METHOD_ID
420
+ end
414
421
  end
415
422
 
416
423
  # kCodersUnpackSize - comes after ALL folder definitions
424
+ # (original sizes; kSize above carries the packed sizes)
417
425
  metadata << [PropertyId::CODERS_UNPACK_SIZE].pack("C")
418
426
  streams.each do |stream|
419
427
  metadata << write_number(stream[:size])
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Omnizip
4
- VERSION = "0.3.43"
4
+ VERSION = "0.3.44"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: omnizip
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.43
4
+ version: 0.3.44
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-08-29 00:00:00.000000000 Z
11
+ date: 2026-08-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: base64