omnizip 0.3.1
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 +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +32 -0
- data/.rubocop_todo.yml +754 -0
- data/COPYING +502 -0
- data/Gemfile +17 -0
- data/LICENSE +12 -0
- data/README.adoc +1045 -0
- data/Rakefile +12 -0
- data/benchmark/README.md +260 -0
- data/benchmark/benchmark_suite.rb +125 -0
- data/benchmark/compression_bench.rb +181 -0
- data/benchmark/filter_bench.rb +180 -0
- data/benchmark/models/benchmark_result.rb +59 -0
- data/benchmark/models/comparison_result.rb +69 -0
- data/benchmark/profile_suite.rb +167 -0
- data/benchmark/reporter.rb +150 -0
- data/benchmark/run_benchmarks.rb +66 -0
- data/benchmark/test_data.rb +137 -0
- data/config/formats/rar3_spec.yml +91 -0
- data/config/formats/rar5_spec.yml +102 -0
- data/docs/.github/workflows/docs.yml +142 -0
- data/docs/.gitignore +21 -0
- data/docs/.lychee.toml +67 -0
- data/docs/Gemfile +13 -0
- data/docs/RAR_WRITE_SUPPORT.md +26 -0
- data/docs/README.md +101 -0
- data/docs/_config.yml +112 -0
- data/docs/assets/logo.svg +1 -0
- data/docs/assets/omnizip-logo.pdf +1540 -11
- data/docs/comparison/feature-matrix.adoc +694 -0
- data/docs/comparison/index.adoc +113 -0
- data/docs/comparison/vs-7zip.adoc +309 -0
- data/docs/comparison/vs-peazip.adoc +77 -0
- data/docs/comparison/vs-rubyzip.adoc +342 -0
- data/docs/comparison/vs-winrar.adoc +100 -0
- data/docs/compatibility.adoc +579 -0
- data/docs/concepts/index.adoc +129 -0
- data/docs/developer/architecture.adoc +256 -0
- data/docs/developer/contributing.adoc +158 -0
- data/docs/developer/index.adoc +25 -0
- data/docs/developer/testing.adoc +212 -0
- data/docs/getting-started/basic-usage.adoc +271 -0
- data/docs/getting-started/index.adoc +42 -0
- data/docs/getting-started/installation.adoc +138 -0
- data/docs/getting-started/quick-start.adoc +185 -0
- data/docs/getting-started/your-first-archive.adoc +218 -0
- data/docs/guides/advanced-features/encryption.adoc +300 -0
- data/docs/guides/advanced-features/index.adoc +49 -0
- data/docs/guides/advanced-features/parallel-processing.adoc +246 -0
- data/docs/guides/advanced-features/progress-tracking.adoc +320 -0
- data/docs/guides/advanced-features/streaming.adoc +212 -0
- data/docs/guides/archive-formats/gzip-format.adoc +107 -0
- data/docs/guides/archive-formats/index.adoc +130 -0
- data/docs/guides/archive-formats/rar-format.adoc +104 -0
- data/docs/guides/archive-formats/rar5.adoc +521 -0
- data/docs/guides/archive-formats/seven-zip-format.adoc +35 -0
- data/docs/guides/archive-formats/tar-format.adoc +106 -0
- data/docs/guides/archive-formats/xz-format.adoc +118 -0
- data/docs/guides/archive-formats/zip-format.adoc +35 -0
- data/docs/guides/compression-algorithms/bzip2.adoc +113 -0
- data/docs/guides/compression-algorithms/deflate.adoc +319 -0
- data/docs/guides/compression-algorithms/index.adoc +190 -0
- data/docs/guides/compression-algorithms/lzma.adoc +398 -0
- data/docs/guides/compression-algorithms/lzma2.adoc +327 -0
- data/docs/guides/compression-algorithms/ppmd.adoc +316 -0
- data/docs/guides/compression-algorithms/zstandard.adoc +361 -0
- data/docs/guides/creating-archives.adoc +354 -0
- data/docs/guides/extracting-archives.adoc +53 -0
- data/docs/guides/format-conversion.adoc +64 -0
- data/docs/guides/index.adoc +49 -0
- data/docs/guides/migration-rubyzip.adoc +217 -0
- data/docs/guides/parity-archives.adoc +605 -0
- data/docs/guides/performance-tuning.adoc +88 -0
- data/docs/index.adoc +218 -0
- data/docs/lychee.toml +67 -0
- data/docs/reference/api/overview.adoc +188 -0
- data/docs/reference/cli/compress-command.adoc +114 -0
- data/docs/reference/cli/overview.adoc +140 -0
- data/docs/reference/index.adoc +26 -0
- data/docs/resources/faq.adoc +185 -0
- data/docs/resources/quick-reference.adoc +222 -0
- data/docs/troubleshooting/index.adoc +208 -0
- data/examples/api_comparison.rb +205 -0
- data/examples/deflate64_example.rb +96 -0
- data/examples/par2_demo.rb +121 -0
- data/examples/quick_start_native.rb +150 -0
- data/examples/quick_start_rubyzip.rb +115 -0
- data/examples/rubyzip_compatibility_demo.rb +194 -0
- data/exe/omnizip +27 -0
- data/lib/omnizip/algorithm.rb +130 -0
- data/lib/omnizip/algorithm_registry.rb +86 -0
- data/lib/omnizip/algorithms/.keep +0 -0
- data/lib/omnizip/algorithms/bzip2/bwt.rb +225 -0
- data/lib/omnizip/algorithms/bzip2/decoder.rb +193 -0
- data/lib/omnizip/algorithms/bzip2/encoder.rb +237 -0
- data/lib/omnizip/algorithms/bzip2/huffman.rb +206 -0
- data/lib/omnizip/algorithms/bzip2/mtf.rb +101 -0
- data/lib/omnizip/algorithms/bzip2/rle.rb +151 -0
- data/lib/omnizip/algorithms/bzip2.rb +130 -0
- data/lib/omnizip/algorithms/deflate/constants.rb +28 -0
- data/lib/omnizip/algorithms/deflate/decoder.rb +38 -0
- data/lib/omnizip/algorithms/deflate/encoder.rb +46 -0
- data/lib/omnizip/algorithms/deflate.rb +128 -0
- data/lib/omnizip/algorithms/deflate64/constants.rb +45 -0
- data/lib/omnizip/algorithms/deflate64/decoder.rb +153 -0
- data/lib/omnizip/algorithms/deflate64/encoder.rb +98 -0
- data/lib/omnizip/algorithms/deflate64/huffman_coder.rb +354 -0
- data/lib/omnizip/algorithms/deflate64/lz77_encoder.rb +142 -0
- data/lib/omnizip/algorithms/deflate64.rb +109 -0
- data/lib/omnizip/algorithms/lzma/bit_model.rb +120 -0
- data/lib/omnizip/algorithms/lzma/constants.rb +112 -0
- data/lib/omnizip/algorithms/lzma/decoder.rb +148 -0
- data/lib/omnizip/algorithms/lzma/dictionary.rb +69 -0
- data/lib/omnizip/algorithms/lzma/distance_coder.rb +415 -0
- data/lib/omnizip/algorithms/lzma/encoder.rb +142 -0
- data/lib/omnizip/algorithms/lzma/length_coder.rb +260 -0
- data/lib/omnizip/algorithms/lzma/literal_decoder.rb +320 -0
- data/lib/omnizip/algorithms/lzma/literal_encoder.rb +210 -0
- data/lib/omnizip/algorithms/lzma/lzip_decoder.rb +341 -0
- data/lib/omnizip/algorithms/lzma/lzma_alone_decoder.rb +192 -0
- data/lib/omnizip/algorithms/lzma/lzma_state.rb +128 -0
- data/lib/omnizip/algorithms/lzma/match.rb +32 -0
- data/lib/omnizip/algorithms/lzma/match_finder.rb +205 -0
- data/lib/omnizip/algorithms/lzma/match_finder_config.rb +142 -0
- data/lib/omnizip/algorithms/lzma/match_finder_factory.rb +88 -0
- data/lib/omnizip/algorithms/lzma/optimal_encoder.rb +130 -0
- data/lib/omnizip/algorithms/lzma/probability_models.rb +72 -0
- data/lib/omnizip/algorithms/lzma/range_coder.rb +85 -0
- data/lib/omnizip/algorithms/lzma/range_decoder.rb +434 -0
- data/lib/omnizip/algorithms/lzma/range_encoder.rb +194 -0
- data/lib/omnizip/algorithms/lzma/state.rb +127 -0
- data/lib/omnizip/algorithms/lzma/xz_buffered_range_encoder.rb +325 -0
- data/lib/omnizip/algorithms/lzma/xz_encoder.rb +426 -0
- data/lib/omnizip/algorithms/lzma/xz_encoder_fast.rb +645 -0
- data/lib/omnizip/algorithms/lzma/xz_match_finder_adapter.rb +227 -0
- data/lib/omnizip/algorithms/lzma/xz_price_calculator.rb +169 -0
- data/lib/omnizip/algorithms/lzma/xz_probability_models.rb +261 -0
- data/lib/omnizip/algorithms/lzma/xz_range_encoder.rb +223 -0
- data/lib/omnizip/algorithms/lzma/xz_range_encoder_exact.rb +331 -0
- data/lib/omnizip/algorithms/lzma/xz_state.rb +116 -0
- data/lib/omnizip/algorithms/lzma/xz_utils_decoder.rb +2055 -0
- data/lib/omnizip/algorithms/lzma.rb +238 -0
- data/lib/omnizip/algorithms/lzma2/chunk_manager.rb +182 -0
- data/lib/omnizip/algorithms/lzma2/constants.rb +41 -0
- data/lib/omnizip/algorithms/lzma2/encoder.rb +147 -0
- data/lib/omnizip/algorithms/lzma2/lzma2_chunk.rb +161 -0
- data/lib/omnizip/algorithms/lzma2/properties.rb +179 -0
- data/lib/omnizip/algorithms/lzma2/simple_lzma2_encoder.rb +127 -0
- data/lib/omnizip/algorithms/lzma2/xz_encoder_adapter.rb +85 -0
- data/lib/omnizip/algorithms/lzma2.rb +141 -0
- data/lib/omnizip/algorithms/ppmd7/constants.rb +74 -0
- data/lib/omnizip/algorithms/ppmd7/context.rb +154 -0
- data/lib/omnizip/algorithms/ppmd7/decoder.rb +126 -0
- data/lib/omnizip/algorithms/ppmd7/encoder.rb +163 -0
- data/lib/omnizip/algorithms/ppmd7/model.rb +248 -0
- data/lib/omnizip/algorithms/ppmd7/symbol_state.rb +57 -0
- data/lib/omnizip/algorithms/ppmd7.rb +116 -0
- data/lib/omnizip/algorithms/ppmd8/constants.rb +61 -0
- data/lib/omnizip/algorithms/ppmd8/context.rb +34 -0
- data/lib/omnizip/algorithms/ppmd8/decoder.rb +107 -0
- data/lib/omnizip/algorithms/ppmd8/encoder.rb +138 -0
- data/lib/omnizip/algorithms/ppmd8/model.rb +250 -0
- data/lib/omnizip/algorithms/ppmd8/restoration_method.rb +78 -0
- data/lib/omnizip/algorithms/ppmd8.rb +82 -0
- data/lib/omnizip/algorithms/ppmd_base.rb +138 -0
- data/lib/omnizip/algorithms/sevenzip_lzma2.rb +123 -0
- data/lib/omnizip/algorithms/xz_lzma2.rb +118 -0
- data/lib/omnizip/algorithms/zstandard/constants.rb +25 -0
- data/lib/omnizip/algorithms/zstandard/decoder.rb +46 -0
- data/lib/omnizip/algorithms/zstandard/encoder.rb +51 -0
- data/lib/omnizip/algorithms/zstandard.rb +138 -0
- data/lib/omnizip/buffer/memory_archive.rb +251 -0
- data/lib/omnizip/buffer/memory_extractor.rb +224 -0
- data/lib/omnizip/buffer.rb +176 -0
- data/lib/omnizip/checksum_registry.rb +114 -0
- data/lib/omnizip/checksums/crc32.rb +100 -0
- data/lib/omnizip/checksums/crc64.rb +101 -0
- data/lib/omnizip/checksums/crc_base.rb +158 -0
- data/lib/omnizip/checksums/verifier.rb +131 -0
- data/lib/omnizip/chunked/memory_manager.rb +194 -0
- data/lib/omnizip/chunked/reader.rb +78 -0
- data/lib/omnizip/chunked/writer.rb +120 -0
- data/lib/omnizip/chunked.rb +129 -0
- data/lib/omnizip/cli/output_formatter.rb +104 -0
- data/lib/omnizip/cli.rb +572 -0
- data/lib/omnizip/commands/.keep +0 -0
- data/lib/omnizip/commands/archive_create_command.rb +427 -0
- data/lib/omnizip/commands/archive_extract_command.rb +272 -0
- data/lib/omnizip/commands/archive_list_command.rb +218 -0
- data/lib/omnizip/commands/archive_repair_command.rb +131 -0
- data/lib/omnizip/commands/archive_verify_command.rb +117 -0
- data/lib/omnizip/commands/compress_command.rb +117 -0
- data/lib/omnizip/commands/decompress_command.rb +120 -0
- data/lib/omnizip/commands/list_command.rb +53 -0
- data/lib/omnizip/commands/metadata_command.rb +153 -0
- data/lib/omnizip/commands/parity_create_command.rb +122 -0
- data/lib/omnizip/commands/parity_repair_command.rb +122 -0
- data/lib/omnizip/commands/parity_verify_command.rb +124 -0
- data/lib/omnizip/commands/profile_list_command.rb +56 -0
- data/lib/omnizip/commands/profile_show_command.rb +44 -0
- data/lib/omnizip/convenience.rb +359 -0
- data/lib/omnizip/converter/conversion_registry.rb +49 -0
- data/lib/omnizip/converter/conversion_strategy.rb +121 -0
- data/lib/omnizip/converter/seven_zip_to_zip_strategy.rb +97 -0
- data/lib/omnizip/converter/zip_to_seven_zip_strategy.rb +112 -0
- data/lib/omnizip/converter.rb +105 -0
- data/lib/omnizip/crypto/aes256/cipher.rb +100 -0
- data/lib/omnizip/crypto/aes256/constants.rb +28 -0
- data/lib/omnizip/crypto/aes256/key_derivation.rb +101 -0
- data/lib/omnizip/crypto/aes256.rb +102 -0
- data/lib/omnizip/error.rb +106 -0
- data/lib/omnizip/eta/exponential_smoothing_estimator.rb +98 -0
- data/lib/omnizip/eta/moving_average_estimator.rb +99 -0
- data/lib/omnizip/eta/rate_calculator.rb +104 -0
- data/lib/omnizip/eta/sample_history.rb +143 -0
- data/lib/omnizip/eta/time_estimator.rb +106 -0
- data/lib/omnizip/eta.rb +63 -0
- data/lib/omnizip/extraction/filter_chain.rb +177 -0
- data/lib/omnizip/extraction/glob_pattern.rb +140 -0
- data/lib/omnizip/extraction/pattern_matcher.rb +70 -0
- data/lib/omnizip/extraction/predicate_pattern.rb +52 -0
- data/lib/omnizip/extraction/regex_pattern.rb +50 -0
- data/lib/omnizip/extraction/selective_extractor.rb +240 -0
- data/lib/omnizip/extraction.rb +111 -0
- data/lib/omnizip/file_type/mime_classifier.rb +144 -0
- data/lib/omnizip/file_type.rb +113 -0
- data/lib/omnizip/filter.rb +139 -0
- data/lib/omnizip/filter_pipeline.rb +108 -0
- data/lib/omnizip/filter_registry.rb +166 -0
- data/lib/omnizip/filters/bcj.rb +279 -0
- data/lib/omnizip/filters/bcj2/constants.rb +53 -0
- data/lib/omnizip/filters/bcj2/decoder.rb +200 -0
- data/lib/omnizip/filters/bcj2/encoder.rb +61 -0
- data/lib/omnizip/filters/bcj2/stream_data.rb +93 -0
- data/lib/omnizip/filters/bcj2.rb +99 -0
- data/lib/omnizip/filters/bcj_arm.rb +176 -0
- data/lib/omnizip/filters/bcj_arm64.rb +244 -0
- data/lib/omnizip/filters/bcj_ia64.rb +196 -0
- data/lib/omnizip/filters/bcj_ppc.rb +190 -0
- data/lib/omnizip/filters/bcj_sparc.rb +176 -0
- data/lib/omnizip/filters/bcj_x86.rb +193 -0
- data/lib/omnizip/filters/delta.rb +196 -0
- data/lib/omnizip/filters/filter_base.rb +72 -0
- data/lib/omnizip/filters/registry.rb +123 -0
- data/lib/omnizip/filters/xz_delta.rb +258 -0
- data/lib/omnizip/format_detector.rb +162 -0
- data/lib/omnizip/format_registry.rb +59 -0
- data/lib/omnizip/formats/.keep +0 -0
- data/lib/omnizip/formats/bzip2_file.rb +172 -0
- data/lib/omnizip/formats/cpio/constants.rb +55 -0
- data/lib/omnizip/formats/cpio/entry.rb +385 -0
- data/lib/omnizip/formats/cpio/reader.rb +196 -0
- data/lib/omnizip/formats/cpio/writer.rb +234 -0
- data/lib/omnizip/formats/cpio.rb +140 -0
- data/lib/omnizip/formats/format_spec_loader.rb +230 -0
- data/lib/omnizip/formats/gzip.rb +238 -0
- data/lib/omnizip/formats/iso/directory_builder.rb +297 -0
- data/lib/omnizip/formats/iso/directory_record.rb +152 -0
- data/lib/omnizip/formats/iso/joliet.rb +204 -0
- data/lib/omnizip/formats/iso/path_table.rb +125 -0
- data/lib/omnizip/formats/iso/reader.rb +197 -0
- data/lib/omnizip/formats/iso/rock_ridge.rb +349 -0
- data/lib/omnizip/formats/iso/volume_builder.rb +320 -0
- data/lib/omnizip/formats/iso/volume_descriptor.rb +168 -0
- data/lib/omnizip/formats/iso/writer.rb +530 -0
- data/lib/omnizip/formats/iso.rb +140 -0
- data/lib/omnizip/formats/lzip.rb +175 -0
- data/lib/omnizip/formats/lzma_alone.rb +171 -0
- data/lib/omnizip/formats/rar/archive_repairer.rb +243 -0
- data/lib/omnizip/formats/rar/archive_verifier.rb +195 -0
- data/lib/omnizip/formats/rar/block_parser.rb +243 -0
- data/lib/omnizip/formats/rar/compression/bit_stream.rb +180 -0
- data/lib/omnizip/formats/rar/compression/dispatcher.rb +217 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/decoder.rb +216 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/encoder.rb +158 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/huffman_builder.rb +217 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/huffman_coder.rb +189 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/match_finder.rb +135 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/sliding_window.rb +165 -0
- data/lib/omnizip/formats/rar/compression/ppmd/context.rb +105 -0
- data/lib/omnizip/formats/rar/compression/ppmd/decoder.rb +219 -0
- data/lib/omnizip/formats/rar/compression/ppmd/encoder.rb +262 -0
- data/lib/omnizip/formats/rar/compression_method_registry.rb +106 -0
- data/lib/omnizip/formats/rar/constants.rb +82 -0
- data/lib/omnizip/formats/rar/decompressor.rb +238 -0
- data/lib/omnizip/formats/rar/external_writer.rb +312 -0
- data/lib/omnizip/formats/rar/header.rb +192 -0
- data/lib/omnizip/formats/rar/license_validator.rb +109 -0
- data/lib/omnizip/formats/rar/models/rar_archive.rb +77 -0
- data/lib/omnizip/formats/rar/models/rar_entry.rb +65 -0
- data/lib/omnizip/formats/rar/models/rar_volume.rb +56 -0
- data/lib/omnizip/formats/rar/parity_handler.rb +292 -0
- data/lib/omnizip/formats/rar/rar5/compression/lzma.rb +202 -0
- data/lib/omnizip/formats/rar/rar5/compression/lzss.rb +578 -0
- data/lib/omnizip/formats/rar/rar5/compression/store.rb +60 -0
- data/lib/omnizip/formats/rar/rar5/crc32.rb +39 -0
- data/lib/omnizip/formats/rar/rar5/encryption/aes256_cbc.rb +97 -0
- data/lib/omnizip/formats/rar/rar5/encryption/encryption_header.rb +114 -0
- data/lib/omnizip/formats/rar/rar5/encryption/encryption_manager.rb +166 -0
- data/lib/omnizip/formats/rar/rar5/encryption/key_derivation.rb +97 -0
- data/lib/omnizip/formats/rar/rar5/header.rb +187 -0
- data/lib/omnizip/formats/rar/rar5/models/encryption_options.rb +74 -0
- data/lib/omnizip/formats/rar/rar5/models/recovery_options.rb +63 -0
- data/lib/omnizip/formats/rar/rar5/models/solid_options.rb +63 -0
- data/lib/omnizip/formats/rar/rar5/models/volume_options.rb +74 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/ARCHITECTURE.md +290 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/volume_manager.rb +264 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/volume_splitter.rb +155 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/volume_writer.rb +194 -0
- data/lib/omnizip/formats/rar/rar5/solid/solid_encoder.rb +109 -0
- data/lib/omnizip/formats/rar/rar5/solid/solid_manager.rb +142 -0
- data/lib/omnizip/formats/rar/rar5/solid/solid_stream.rb +121 -0
- data/lib/omnizip/formats/rar/rar5/vint.rb +65 -0
- data/lib/omnizip/formats/rar/rar5/writer.rb +466 -0
- data/lib/omnizip/formats/rar/rar_format_base.rb +241 -0
- data/lib/omnizip/formats/rar/reader.rb +366 -0
- data/lib/omnizip/formats/rar/recovery_record.rb +245 -0
- data/lib/omnizip/formats/rar/volume_manager.rb +168 -0
- data/lib/omnizip/formats/rar/writer.rb +431 -0
- data/lib/omnizip/formats/rar.rb +205 -0
- data/lib/omnizip/formats/rar3/compressor.rb +73 -0
- data/lib/omnizip/formats/rar3/decompressor.rb +66 -0
- data/lib/omnizip/formats/rar3/reader.rb +386 -0
- data/lib/omnizip/formats/rar3/writer.rb +219 -0
- data/lib/omnizip/formats/rar5/compressor.rb +73 -0
- data/lib/omnizip/formats/rar5/decompressor.rb +66 -0
- data/lib/omnizip/formats/rar5/reader.rb +342 -0
- data/lib/omnizip/formats/rar5/writer.rb +214 -0
- data/lib/omnizip/formats/seven_zip/coder_chain.rb +150 -0
- data/lib/omnizip/formats/seven_zip/constants.rb +126 -0
- data/lib/omnizip/formats/seven_zip/encoded_header.rb +114 -0
- data/lib/omnizip/formats/seven_zip/encrypted_header.rb +142 -0
- data/lib/omnizip/formats/seven_zip/file_collector.rb +144 -0
- data/lib/omnizip/formats/seven_zip/header.rb +106 -0
- data/lib/omnizip/formats/seven_zip/header_encryptor.rb +134 -0
- data/lib/omnizip/formats/seven_zip/header_writer.rb +466 -0
- data/lib/omnizip/formats/seven_zip/models/coder_info.rb +30 -0
- data/lib/omnizip/formats/seven_zip/models/file_entry.rb +58 -0
- data/lib/omnizip/formats/seven_zip/models/folder.rb +69 -0
- data/lib/omnizip/formats/seven_zip/models/stream_info.rb +42 -0
- data/lib/omnizip/formats/seven_zip/parser.rb +660 -0
- data/lib/omnizip/formats/seven_zip/reader.rb +458 -0
- data/lib/omnizip/formats/seven_zip/split_archive_reader.rb +632 -0
- data/lib/omnizip/formats/seven_zip/split_archive_writer.rb +315 -0
- data/lib/omnizip/formats/seven_zip/stream_compressor.rb +151 -0
- data/lib/omnizip/formats/seven_zip/stream_decompressor.rb +162 -0
- data/lib/omnizip/formats/seven_zip/writer.rb +740 -0
- data/lib/omnizip/formats/seven_zip.rb +93 -0
- data/lib/omnizip/formats/tar/constants.rb +73 -0
- data/lib/omnizip/formats/tar/entry.rb +94 -0
- data/lib/omnizip/formats/tar/header.rb +168 -0
- data/lib/omnizip/formats/tar/reader.rb +121 -0
- data/lib/omnizip/formats/tar/writer.rb +216 -0
- data/lib/omnizip/formats/tar.rb +84 -0
- data/lib/omnizip/formats/xz/reader.rb +116 -0
- data/lib/omnizip/formats/xz.rb +237 -0
- data/lib/omnizip/formats/xz_impl/block_decoder.rb +754 -0
- data/lib/omnizip/formats/xz_impl/block_encoder.rb +306 -0
- data/lib/omnizip/formats/xz_impl/block_header.rb +210 -0
- data/lib/omnizip/formats/xz_impl/block_header_parser.rb +186 -0
- data/lib/omnizip/formats/xz_impl/constants.rb +49 -0
- data/lib/omnizip/formats/xz_impl/index_decoder.rb +174 -0
- data/lib/omnizip/formats/xz_impl/index_encoder.rb +122 -0
- data/lib/omnizip/formats/xz_impl/stream_decoder.rb +468 -0
- data/lib/omnizip/formats/xz_impl/stream_encoder.rb +99 -0
- data/lib/omnizip/formats/xz_impl/stream_footer.rb +81 -0
- data/lib/omnizip/formats/xz_impl/stream_footer_parser.rb +117 -0
- data/lib/omnizip/formats/xz_impl/stream_header.rb +55 -0
- data/lib/omnizip/formats/xz_impl/stream_header_parser.rb +108 -0
- data/lib/omnizip/formats/xz_impl/vli.rb +128 -0
- data/lib/omnizip/formats/xz_impl/writer.rb +421 -0
- data/lib/omnizip/formats/zip/central_directory_header.rb +195 -0
- data/lib/omnizip/formats/zip/constants.rb +69 -0
- data/lib/omnizip/formats/zip/end_of_central_directory.rb +133 -0
- data/lib/omnizip/formats/zip/local_file_header.rb +138 -0
- data/lib/omnizip/formats/zip/reader.rb +250 -0
- data/lib/omnizip/formats/zip/unix_extra_field.rb +153 -0
- data/lib/omnizip/formats/zip/writer.rb +375 -0
- data/lib/omnizip/formats/zip/zip64_end_of_central_directory.rb +104 -0
- data/lib/omnizip/formats/zip/zip64_end_of_central_directory_locator.rb +66 -0
- data/lib/omnizip/formats/zip/zip64_extra_field.rb +114 -0
- data/lib/omnizip/formats/zip.rb +50 -0
- data/lib/omnizip/implementations/base/lzma2_decoder_base.rb +75 -0
- data/lib/omnizip/implementations/base/lzma2_encoder_base.rb +128 -0
- data/lib/omnizip/implementations/base/lzma_decoder_base.rb +83 -0
- data/lib/omnizip/implementations/base/lzma_encoder_base.rb +108 -0
- data/lib/omnizip/implementations/base/state_machine_base.rb +182 -0
- data/lib/omnizip/implementations/seven_zip/lzma/decoder.rb +421 -0
- data/lib/omnizip/implementations/seven_zip/lzma/encoder.rb +465 -0
- data/lib/omnizip/implementations/seven_zip/lzma/match_finder.rb +288 -0
- data/lib/omnizip/implementations/seven_zip/lzma/range_decoder.rb +200 -0
- data/lib/omnizip/implementations/seven_zip/lzma/range_encoder.rb +197 -0
- data/lib/omnizip/implementations/seven_zip/lzma/state_machine.rb +141 -0
- data/lib/omnizip/implementations/seven_zip/lzma2/encoder.rb +519 -0
- data/lib/omnizip/implementations/xz_utils/lzma2/decoder.rb +723 -0
- data/lib/omnizip/implementations/xz_utils/lzma2/encoder.rb +750 -0
- data/lib/omnizip/io/buffered_input.rb +146 -0
- data/lib/omnizip/io/buffered_output.rb +105 -0
- data/lib/omnizip/io/stream_manager.rb +115 -0
- data/lib/omnizip/link_handler/hard_link.rb +79 -0
- data/lib/omnizip/link_handler/symbolic_link.rb +74 -0
- data/lib/omnizip/link_handler.rb +124 -0
- data/lib/omnizip/metadata/archive_metadata.rb +114 -0
- data/lib/omnizip/metadata/entry_metadata.rb +146 -0
- data/lib/omnizip/metadata/metadata_editor.rb +171 -0
- data/lib/omnizip/metadata/metadata_registry.rb +64 -0
- data/lib/omnizip/metadata/metadata_validator.rb +99 -0
- data/lib/omnizip/metadata.rb +57 -0
- data/lib/omnizip/models/.keep +0 -0
- data/lib/omnizip/models/algorithm_metadata.rb +73 -0
- data/lib/omnizip/models/compression_options.rb +71 -0
- data/lib/omnizip/models/conversion_options.rb +87 -0
- data/lib/omnizip/models/conversion_result.rb +135 -0
- data/lib/omnizip/models/eta_result.rb +46 -0
- data/lib/omnizip/models/extraction_rule.rb +115 -0
- data/lib/omnizip/models/filter_chain.rb +144 -0
- data/lib/omnizip/models/filter_config.rb +183 -0
- data/lib/omnizip/models/match_result.rb +124 -0
- data/lib/omnizip/models/optimization_suggestion.rb +91 -0
- data/lib/omnizip/models/parallel_options.rb +104 -0
- data/lib/omnizip/models/performance_result.rb +79 -0
- data/lib/omnizip/models/profile_report.rb +82 -0
- data/lib/omnizip/models/progress_options.rb +38 -0
- data/lib/omnizip/models/split_options.rb +116 -0
- data/lib/omnizip/optimization_registry.rb +81 -0
- data/lib/omnizip/parallel/job_queue.rb +209 -0
- data/lib/omnizip/parallel/job_scheduler.rb +203 -0
- data/lib/omnizip/parallel/parallel_compressor.rb +347 -0
- data/lib/omnizip/parallel/parallel_extractor.rb +329 -0
- data/lib/omnizip/parallel/worker_pool.rb +223 -0
- data/lib/omnizip/parallel.rb +149 -0
- data/lib/omnizip/parity/chunked_block_processor.rb +196 -0
- data/lib/omnizip/parity/galois16.rb +145 -0
- data/lib/omnizip/parity/models/creator_packet.rb +73 -0
- data/lib/omnizip/parity/models/file_description_packet.rb +133 -0
- data/lib/omnizip/parity/models/ifsc_packet.rb +123 -0
- data/lib/omnizip/parity/models/main_packet.rb +128 -0
- data/lib/omnizip/parity/models/packet.rb +156 -0
- data/lib/omnizip/parity/models/packet_registry.rb +109 -0
- data/lib/omnizip/parity/models/recovery_slice_packet.rb +78 -0
- data/lib/omnizip/parity/par2_creator.rb +531 -0
- data/lib/omnizip/parity/par2_repairer.rb +407 -0
- data/lib/omnizip/parity/par2_verifier.rb +364 -0
- data/lib/omnizip/parity/par2cmdline_algorithm.rb +110 -0
- data/lib/omnizip/parity/par2cmdline_coefficients.rb +78 -0
- data/lib/omnizip/parity/reed_solomon_decoder.rb +266 -0
- data/lib/omnizip/parity/reed_solomon_encoder.rb +111 -0
- data/lib/omnizip/parity/reed_solomon_matrix.rb +342 -0
- data/lib/omnizip/parity.rb +186 -0
- data/lib/omnizip/password/encryption_registry.rb +65 -0
- data/lib/omnizip/password/encryption_strategy.rb +96 -0
- data/lib/omnizip/password/password_validator.rb +129 -0
- data/lib/omnizip/password/winzip_aes_strategy.rb +192 -0
- data/lib/omnizip/password/zip_crypto_strategy.rb +141 -0
- data/lib/omnizip/password.rb +87 -0
- data/lib/omnizip/pipe/stream_compressor.rb +124 -0
- data/lib/omnizip/pipe/stream_decompressor.rb +174 -0
- data/lib/omnizip/pipe.rb +121 -0
- data/lib/omnizip/platform/ntfs_streams.rb +201 -0
- data/lib/omnizip/platform.rb +189 -0
- data/lib/omnizip/profile/archive_profile.rb +39 -0
- data/lib/omnizip/profile/balanced_profile.rb +33 -0
- data/lib/omnizip/profile/binary_profile.rb +36 -0
- data/lib/omnizip/profile/compression_profile.rb +158 -0
- data/lib/omnizip/profile/custom_profile.rb +157 -0
- data/lib/omnizip/profile/fast_profile.rb +33 -0
- data/lib/omnizip/profile/maximum_profile.rb +33 -0
- data/lib/omnizip/profile/profile_detector.rb +110 -0
- data/lib/omnizip/profile/profile_registry.rb +161 -0
- data/lib/omnizip/profile/text_profile.rb +36 -0
- data/lib/omnizip/profile.rb +190 -0
- data/lib/omnizip/profiler/memory_profiler.rb +66 -0
- data/lib/omnizip/profiler/method_profiler.rb +49 -0
- data/lib/omnizip/profiler/report_generator.rb +169 -0
- data/lib/omnizip/profiler.rb +204 -0
- data/lib/omnizip/progress/callback_reporter.rb +36 -0
- data/lib/omnizip/progress/console_reporter.rb +62 -0
- data/lib/omnizip/progress/log_reporter.rb +91 -0
- data/lib/omnizip/progress/operation_progress.rb +118 -0
- data/lib/omnizip/progress/progress_bar.rb +156 -0
- data/lib/omnizip/progress/progress_reporter.rb +40 -0
- data/lib/omnizip/progress/progress_tracker.rb +190 -0
- data/lib/omnizip/progress/silent_reporter.rb +24 -0
- data/lib/omnizip/progress.rb +127 -0
- data/lib/omnizip/rubyzip_compat.rb +63 -0
- data/lib/omnizip/temp/safe_extract.rb +168 -0
- data/lib/omnizip/temp/temp_file.rb +124 -0
- data/lib/omnizip/temp/temp_file_pool.rb +109 -0
- data/lib/omnizip/temp.rb +181 -0
- data/lib/omnizip/version.rb +5 -0
- data/lib/omnizip/zip/entry.rb +156 -0
- data/lib/omnizip/zip/file.rb +485 -0
- data/lib/omnizip/zip/input_stream.rb +273 -0
- data/lib/omnizip/zip/output_stream.rb +324 -0
- data/lib/omnizip.rb +156 -0
- data/readme-docs/advanced-features.adoc +515 -0
- data/readme-docs/api-usage.adoc +444 -0
- data/readme-docs/architecture.adoc +449 -0
- data/readme-docs/archive-formats.adoc +479 -0
- data/readme-docs/cli-usage.adoc +222 -0
- data/readme-docs/compression-algorithms.adoc +442 -0
- data/readme-docs/compression-profiles.adoc +247 -0
- data/readme-docs/encryption-checksums.adoc +328 -0
- data/readme-docs/format-converter.adoc +325 -0
- data/readme-docs/installation.adoc +228 -0
- data/readme-docs/par2-archives.adoc +608 -0
- data/readme-docs/performance-profiler.adoc +389 -0
- data/readme-docs/preprocessing-filters.adoc +280 -0
- data/xz-file-format-1.2.1.txt +1174 -0
- metadata +617 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "constants"
|
|
4
|
+
require_relative "parser"
|
|
5
|
+
|
|
6
|
+
module Omnizip
|
|
7
|
+
module Formats
|
|
8
|
+
module SevenZip
|
|
9
|
+
# .7z archive header parser
|
|
10
|
+
# Handles signature validation and start header
|
|
11
|
+
class Header
|
|
12
|
+
include Constants
|
|
13
|
+
|
|
14
|
+
attr_reader :major_version, :minor_version, :start_header_crc,
|
|
15
|
+
:next_header_offset, :next_header_size, :next_header_crc
|
|
16
|
+
|
|
17
|
+
# Parse header from IO
|
|
18
|
+
#
|
|
19
|
+
# @param io [IO] Input stream
|
|
20
|
+
# @return [Header] Parsed header
|
|
21
|
+
# @raise [RuntimeError] if signature or CRC invalid
|
|
22
|
+
def self.read(io)
|
|
23
|
+
header = new
|
|
24
|
+
header.parse(io)
|
|
25
|
+
header
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Parse header data from IO stream
|
|
29
|
+
#
|
|
30
|
+
# @param io [IO] Input stream positioned at start
|
|
31
|
+
# @raise [RuntimeError] if signature or version invalid
|
|
32
|
+
def parse(io)
|
|
33
|
+
# Read complete start header (32 bytes)
|
|
34
|
+
header_data = io.read(START_HEADER_SIZE)
|
|
35
|
+
raise "Invalid .7z file: too short" if header_data.nil? ||
|
|
36
|
+
header_data.bytesize < START_HEADER_SIZE
|
|
37
|
+
|
|
38
|
+
# Validate signature
|
|
39
|
+
signature = header_data[0, SIGNATURE_SIZE]
|
|
40
|
+
unless signature == SIGNATURE
|
|
41
|
+
raise "Invalid .7z signature: expected #{SIGNATURE.inspect}, " \
|
|
42
|
+
"got #{signature.inspect}"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Parse version
|
|
46
|
+
@major_version = header_data.getbyte(6)
|
|
47
|
+
@minor_version = header_data.getbyte(7)
|
|
48
|
+
|
|
49
|
+
unless @major_version == MAJOR_VERSION
|
|
50
|
+
raise "Unsupported .7z version: #{@major_version}.#{@minor_version}"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Parse start header CRC (bytes 8-11)
|
|
54
|
+
@start_header_crc = header_data[8, 4].unpack1("V")
|
|
55
|
+
|
|
56
|
+
# Parse next header info (bytes 12-31, 20 bytes total)
|
|
57
|
+
next_header_data = header_data[12, 20]
|
|
58
|
+
|
|
59
|
+
# NOTE: CRC validation temporarily disabled for Phase 2
|
|
60
|
+
# Will be refined in Phase 3 with proper CRC32 initialization
|
|
61
|
+
# calculated_crc = calculate_crc32(next_header_data)
|
|
62
|
+
# unless calculated_crc == @start_header_crc
|
|
63
|
+
# warn "CRC mismatch (non-fatal): expected " \
|
|
64
|
+
# "#{@start_header_crc.to_s(16)}, " \
|
|
65
|
+
# "got #{calculated_crc.to_s(16)}"
|
|
66
|
+
# end
|
|
67
|
+
|
|
68
|
+
# Parse next header offset and size
|
|
69
|
+
@next_header_offset = next_header_data[0, 8].unpack1("Q<")
|
|
70
|
+
@next_header_size = next_header_data[8, 8].unpack1("Q<")
|
|
71
|
+
@next_header_crc = next_header_data[16, 4].unpack1("V")
|
|
72
|
+
|
|
73
|
+
self
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Get position after start header
|
|
77
|
+
#
|
|
78
|
+
# @return [Integer] Byte position
|
|
79
|
+
def start_pos_after_header
|
|
80
|
+
START_HEADER_SIZE
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Check if header is valid
|
|
84
|
+
#
|
|
85
|
+
# @return [Boolean] true if valid
|
|
86
|
+
def valid?
|
|
87
|
+
!@next_header_offset.nil? && !@next_header_size.nil?
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
# Calculate CRC32 checksum
|
|
93
|
+
# Note: Needs refinement for .7z CRC compatibility
|
|
94
|
+
#
|
|
95
|
+
# @param data [String] Binary data
|
|
96
|
+
# @return [Integer] CRC32 value
|
|
97
|
+
def calculate_crc32(data)
|
|
98
|
+
require_relative "../../checksums/crc32"
|
|
99
|
+
crc = Omnizip::Checksums::Crc32.new
|
|
100
|
+
crc.update(data)
|
|
101
|
+
crc.value
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "openssl"
|
|
4
|
+
require_relative "constants"
|
|
5
|
+
require_relative "../../checksums/crc32"
|
|
6
|
+
|
|
7
|
+
module Omnizip
|
|
8
|
+
module Formats
|
|
9
|
+
module SevenZip
|
|
10
|
+
# 7z header encryption using AES-256
|
|
11
|
+
# Encrypts archive headers to hide filenames and structure
|
|
12
|
+
class HeaderEncryptor
|
|
13
|
+
include Constants
|
|
14
|
+
|
|
15
|
+
# AES-256 parameters
|
|
16
|
+
AES_KEY_SIZE = 32 # 256 bits
|
|
17
|
+
AES_IV_SIZE = 16 # 128 bits
|
|
18
|
+
SALT_SIZE = 16 # 128 bits
|
|
19
|
+
|
|
20
|
+
# PBKDF2 parameters
|
|
21
|
+
PBKDF2_ITERATIONS = 262_144 # 256K iterations for strong key derivation
|
|
22
|
+
|
|
23
|
+
attr_reader :password, :salt, :iv
|
|
24
|
+
|
|
25
|
+
# Initialize encryptor with password
|
|
26
|
+
#
|
|
27
|
+
# @param password [String] Encryption password
|
|
28
|
+
def initialize(password)
|
|
29
|
+
@password = password
|
|
30
|
+
@salt = nil
|
|
31
|
+
@iv = nil
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Encrypt header data
|
|
35
|
+
#
|
|
36
|
+
# @param header_data [String] Unencrypted header bytes
|
|
37
|
+
# @return [Hash] Encrypted data with metadata
|
|
38
|
+
# - :data [String] Encrypted bytes
|
|
39
|
+
# - :salt [String] Salt used for key derivation
|
|
40
|
+
# - :iv [String] Initialization vector
|
|
41
|
+
# - :size [Integer] Original size before encryption
|
|
42
|
+
def encrypt(header_data)
|
|
43
|
+
# Generate random salt and IV
|
|
44
|
+
@salt = OpenSSL::Random.random_bytes(SALT_SIZE)
|
|
45
|
+
@iv = OpenSSL::Random.random_bytes(AES_IV_SIZE)
|
|
46
|
+
|
|
47
|
+
# Derive encryption key from password
|
|
48
|
+
key = derive_key(@password, @salt)
|
|
49
|
+
|
|
50
|
+
# Encrypt data
|
|
51
|
+
cipher = OpenSSL::Cipher.new("AES-256-CBC")
|
|
52
|
+
cipher.encrypt
|
|
53
|
+
cipher.key = key
|
|
54
|
+
cipher.iv = @iv
|
|
55
|
+
|
|
56
|
+
encrypted = cipher.update(header_data) + cipher.final
|
|
57
|
+
|
|
58
|
+
{
|
|
59
|
+
data: encrypted,
|
|
60
|
+
salt: @salt,
|
|
61
|
+
iv: @iv,
|
|
62
|
+
size: header_data.bytesize,
|
|
63
|
+
}
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Decrypt header data
|
|
67
|
+
#
|
|
68
|
+
# @param encrypted_data [String] Encrypted bytes
|
|
69
|
+
# @param salt [String] Salt used during encryption
|
|
70
|
+
# @param iv [String] Initialization vector
|
|
71
|
+
# @return [String] Decrypted header bytes
|
|
72
|
+
# @raise [RuntimeError] if password is incorrect
|
|
73
|
+
def decrypt(encrypted_data, salt, iv)
|
|
74
|
+
# Derive decryption key from password
|
|
75
|
+
key = derive_key(@password, salt)
|
|
76
|
+
|
|
77
|
+
# Decrypt data
|
|
78
|
+
decipher = OpenSSL::Cipher.new("AES-256-CBC")
|
|
79
|
+
decipher.decrypt
|
|
80
|
+
decipher.key = key
|
|
81
|
+
decipher.iv = iv
|
|
82
|
+
# Ensure padding validation is enabled (default, but explicit)
|
|
83
|
+
decipher.padding = 1
|
|
84
|
+
|
|
85
|
+
begin
|
|
86
|
+
decipher.update(encrypted_data) + decipher.final
|
|
87
|
+
rescue OpenSSL::Cipher::CipherError => e
|
|
88
|
+
raise "Failed to decrypt header: incorrect password or corrupted data (#{e.message})"
|
|
89
|
+
rescue StandardError => e
|
|
90
|
+
raise "Failed to decrypt header: incorrect password or corrupted data (#{e.message})"
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Derive encryption key from password using PBKDF2
|
|
95
|
+
#
|
|
96
|
+
# @param password [String] User password
|
|
97
|
+
# @param salt [String] Random salt
|
|
98
|
+
# @return [String] Derived key
|
|
99
|
+
def derive_key(password, salt)
|
|
100
|
+
OpenSSL::PKCS5.pbkdf2_hmac(
|
|
101
|
+
password,
|
|
102
|
+
salt,
|
|
103
|
+
PBKDF2_ITERATIONS,
|
|
104
|
+
AES_KEY_SIZE,
|
|
105
|
+
OpenSSL::Digest.new("SHA256"),
|
|
106
|
+
)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Verify password against encrypted header
|
|
110
|
+
#
|
|
111
|
+
# @param encrypted_data [String] Encrypted header
|
|
112
|
+
# @param salt [String] Salt used
|
|
113
|
+
# @param iv [String] IV used
|
|
114
|
+
# @return [Boolean] true if password can decrypt
|
|
115
|
+
def verify_password(encrypted_data, salt, iv)
|
|
116
|
+
decrypt(encrypted_data, salt, iv)
|
|
117
|
+
true
|
|
118
|
+
rescue StandardError
|
|
119
|
+
false
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Calculate CRC of header data
|
|
123
|
+
#
|
|
124
|
+
# @param data [String] Header data
|
|
125
|
+
# @return [Integer] CRC32 value
|
|
126
|
+
def calculate_crc(data)
|
|
127
|
+
crc = Omnizip::Checksums::Crc32.new
|
|
128
|
+
crc.update(data)
|
|
129
|
+
crc.value
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "constants"
|
|
4
|
+
require_relative "../../checksums/crc32"
|
|
5
|
+
|
|
6
|
+
module Omnizip
|
|
7
|
+
module Formats
|
|
8
|
+
module SevenZip
|
|
9
|
+
# Writes .7z archive header with metadata
|
|
10
|
+
# Handles variable-length encoding and property sequences
|
|
11
|
+
class HeaderWriter
|
|
12
|
+
include Constants
|
|
13
|
+
|
|
14
|
+
attr_reader :buffer
|
|
15
|
+
|
|
16
|
+
# Initialize writer
|
|
17
|
+
def initialize
|
|
18
|
+
@buffer = String.new(encoding: "BINARY")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Write archive signature and header
|
|
22
|
+
#
|
|
23
|
+
# @param next_header_data [String] Encoded next header
|
|
24
|
+
# @param next_header_offset [Integer] Offset to next header
|
|
25
|
+
# @return [String] Complete archive header
|
|
26
|
+
def write_start_header(next_header_data, next_header_offset)
|
|
27
|
+
header = String.new(encoding: "BINARY")
|
|
28
|
+
|
|
29
|
+
# Signature (6 bytes)
|
|
30
|
+
header << SIGNATURE
|
|
31
|
+
|
|
32
|
+
# Version (2 bytes)
|
|
33
|
+
header << [MAJOR_VERSION, MINOR_VERSION].pack("CC")
|
|
34
|
+
|
|
35
|
+
# Calculate CRC for next header info
|
|
36
|
+
next_header_info = String.new(encoding: "BINARY")
|
|
37
|
+
next_header_info << [next_header_offset].pack("Q<")
|
|
38
|
+
next_header_info << [next_header_data.bytesize].pack("Q<")
|
|
39
|
+
|
|
40
|
+
crc = Omnizip::Checksums::Crc32.new
|
|
41
|
+
crc.update(next_header_data)
|
|
42
|
+
next_header_crc = crc.value
|
|
43
|
+
|
|
44
|
+
next_header_info << [next_header_crc].pack("V")
|
|
45
|
+
|
|
46
|
+
# Calculate CRC for next header info
|
|
47
|
+
info_crc = Omnizip::Checksums::Crc32.new
|
|
48
|
+
info_crc.update(next_header_info)
|
|
49
|
+
|
|
50
|
+
# Start header CRC (4 bytes)
|
|
51
|
+
header << [info_crc.value].pack("V")
|
|
52
|
+
|
|
53
|
+
# Next header info (20 bytes)
|
|
54
|
+
header << next_header_info
|
|
55
|
+
|
|
56
|
+
header
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Encode variable-length number (7-Zip format)
|
|
60
|
+
#
|
|
61
|
+
# @param value [Integer] Number to encode
|
|
62
|
+
# @return [String] Encoded bytes
|
|
63
|
+
def write_number(value)
|
|
64
|
+
return [value].pack("C") if value < 0x80
|
|
65
|
+
|
|
66
|
+
# Determine how many bytes needed and encode accordingly
|
|
67
|
+
if value < 0x4000 # 14-bit: 2 bytes
|
|
68
|
+
first_byte = 0x80 | (value >> 8)
|
|
69
|
+
second_byte = value & 0xFF
|
|
70
|
+
[first_byte, second_byte].pack("C*")
|
|
71
|
+
elsif value < 0x200000 # 21-bit: 3 bytes
|
|
72
|
+
first_byte = 0xC0 | (value >> 16)
|
|
73
|
+
second_byte = (value >> 8) & 0xFF
|
|
74
|
+
third_byte = value & 0xFF
|
|
75
|
+
[first_byte, second_byte, third_byte].pack("C*")
|
|
76
|
+
elsif value < 0x10000000 # 28-bit: 4 bytes
|
|
77
|
+
first_byte = 0xE0 | (value >> 24)
|
|
78
|
+
[
|
|
79
|
+
first_byte,
|
|
80
|
+
(value >> 16) & 0xFF,
|
|
81
|
+
(value >> 8) & 0xFF,
|
|
82
|
+
value & 0xFF,
|
|
83
|
+
].pack("C*")
|
|
84
|
+
elsif value < 0x800000000 # 35-bit: 5 bytes
|
|
85
|
+
first_byte = 0xF0 | (value >> 32)
|
|
86
|
+
[
|
|
87
|
+
first_byte,
|
|
88
|
+
(value >> 24) & 0xFF,
|
|
89
|
+
(value >> 16) & 0xFF,
|
|
90
|
+
(value >> 8) & 0xFF,
|
|
91
|
+
value & 0xFF,
|
|
92
|
+
].pack("C*")
|
|
93
|
+
elsif value < 0x40000000000 # 42-bit: 6 bytes
|
|
94
|
+
first_byte = 0xF8 | (value >> 40)
|
|
95
|
+
[
|
|
96
|
+
first_byte,
|
|
97
|
+
(value >> 32) & 0xFF,
|
|
98
|
+
(value >> 24) & 0xFF,
|
|
99
|
+
(value >> 16) & 0xFF,
|
|
100
|
+
(value >> 8) & 0xFF,
|
|
101
|
+
value & 0xFF,
|
|
102
|
+
].pack("C*")
|
|
103
|
+
elsif value < 0x2000000000000 # 49-bit: 7 bytes
|
|
104
|
+
first_byte = 0xFC | (value >> 48)
|
|
105
|
+
[
|
|
106
|
+
first_byte,
|
|
107
|
+
(value >> 40) & 0xFF,
|
|
108
|
+
(value >> 32) & 0xFF,
|
|
109
|
+
(value >> 24) & 0xFF,
|
|
110
|
+
(value >> 16) & 0xFF,
|
|
111
|
+
(value >> 8) & 0xFF,
|
|
112
|
+
value & 0xFF,
|
|
113
|
+
].pack("C*")
|
|
114
|
+
else # 56-bit: 8 bytes
|
|
115
|
+
first_byte = 0xFE | (value >> 56)
|
|
116
|
+
[
|
|
117
|
+
first_byte,
|
|
118
|
+
(value >> 48) & 0xFF,
|
|
119
|
+
(value >> 40) & 0xFF,
|
|
120
|
+
(value >> 32) & 0xFF,
|
|
121
|
+
(value >> 24) & 0xFF,
|
|
122
|
+
(value >> 16) & 0xFF,
|
|
123
|
+
(value >> 8) & 0xFF,
|
|
124
|
+
value & 0xFF,
|
|
125
|
+
].pack("C*")
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Write bit vector
|
|
130
|
+
#
|
|
131
|
+
# @param bits [Array<Boolean>] Bit values
|
|
132
|
+
# @return [String] Encoded bit vector
|
|
133
|
+
def write_bit_vector(bits)
|
|
134
|
+
return [1].pack("C") if bits.all?
|
|
135
|
+
|
|
136
|
+
data = [0].pack("C") # Not all defined
|
|
137
|
+
num_bytes = (bits.size + 7) / 8
|
|
138
|
+
|
|
139
|
+
bytes = Array.new(num_bytes, 0)
|
|
140
|
+
bits.each_with_index do |bit, i|
|
|
141
|
+
byte_idx = i / 8
|
|
142
|
+
bit_idx = 7 - (i % 8)
|
|
143
|
+
bytes[byte_idx] |= (1 << bit_idx) if bit
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
data << bytes.pack("C*")
|
|
147
|
+
data
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Write pack info section
|
|
151
|
+
#
|
|
152
|
+
# @param pack_pos [Integer] Pack position
|
|
153
|
+
# @param pack_sizes [Array<Integer>] Pack sizes
|
|
154
|
+
# @param pack_crcs [Array<Integer, nil>] Pack CRCs (optional)
|
|
155
|
+
# @return [String] Encoded pack info
|
|
156
|
+
def write_pack_info(pack_pos, pack_sizes, pack_crcs = [])
|
|
157
|
+
data = String.new(encoding: "BINARY")
|
|
158
|
+
|
|
159
|
+
data << [PropertyId::PACK_INFO].pack("C")
|
|
160
|
+
data << write_number(pack_pos)
|
|
161
|
+
data << write_number(pack_sizes.size)
|
|
162
|
+
|
|
163
|
+
# Sizes
|
|
164
|
+
data << [PropertyId::SIZE].pack("C")
|
|
165
|
+
pack_sizes.each do |size|
|
|
166
|
+
data << write_number(size)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# CRCs (optional)
|
|
170
|
+
unless pack_crcs.empty?
|
|
171
|
+
data << [PropertyId::CRC].pack("C")
|
|
172
|
+
defined_bits = pack_crcs.map { |crc| !crc.nil? }
|
|
173
|
+
data << write_bit_vector(defined_bits)
|
|
174
|
+
pack_crcs.each do |crc|
|
|
175
|
+
data << [crc].pack("V") if crc
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
data << [PropertyId::K_END].pack("C")
|
|
180
|
+
data
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Write coder info
|
|
184
|
+
#
|
|
185
|
+
# @param method_id [Integer] Compression method ID
|
|
186
|
+
# @param properties [String, nil] Coder properties
|
|
187
|
+
# @return [String] Encoded coder
|
|
188
|
+
def write_coder(method_id, properties = nil)
|
|
189
|
+
data = String.new(encoding: "BINARY")
|
|
190
|
+
|
|
191
|
+
# Determine ID size
|
|
192
|
+
id_bytes = []
|
|
193
|
+
temp_id = method_id
|
|
194
|
+
while temp_id.positive?
|
|
195
|
+
id_bytes.unshift(temp_id & 0xFF)
|
|
196
|
+
temp_id >>= 8
|
|
197
|
+
end
|
|
198
|
+
id_bytes = [0] if id_bytes.empty?
|
|
199
|
+
|
|
200
|
+
# Main byte
|
|
201
|
+
main_byte = id_bytes.size
|
|
202
|
+
main_byte |= 0x20 if properties # Has properties
|
|
203
|
+
|
|
204
|
+
data << [main_byte].pack("C")
|
|
205
|
+
data << id_bytes.pack("C*")
|
|
206
|
+
|
|
207
|
+
# Properties
|
|
208
|
+
if properties
|
|
209
|
+
data << write_number(properties.bytesize)
|
|
210
|
+
data << properties
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
data
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Write folder definition
|
|
217
|
+
#
|
|
218
|
+
# @param method_id [Integer] Compression method
|
|
219
|
+
# @param properties [String, nil] Properties
|
|
220
|
+
# @return [String] Encoded folder data
|
|
221
|
+
def write_folder(method_id, properties)
|
|
222
|
+
data = String.new(encoding: "BINARY")
|
|
223
|
+
|
|
224
|
+
# Number of coders
|
|
225
|
+
data << write_number(1)
|
|
226
|
+
|
|
227
|
+
# Coder info
|
|
228
|
+
data << write_coder(method_id, properties)
|
|
229
|
+
|
|
230
|
+
# For simple case: no bind pairs needed
|
|
231
|
+
# (single coder with single in/out stream)
|
|
232
|
+
|
|
233
|
+
data
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# Write folders section
|
|
237
|
+
#
|
|
238
|
+
# @param folders [Array<Hash>] Folder specs
|
|
239
|
+
# @return [String] Encoded folders
|
|
240
|
+
def write_folders(folders)
|
|
241
|
+
data = String.new(encoding: "BINARY")
|
|
242
|
+
data << [PropertyId::FOLDER].pack("C")
|
|
243
|
+
data << write_number(folders.size)
|
|
244
|
+
|
|
245
|
+
# Write external flag (0 = inline folders, 1 = external)
|
|
246
|
+
data << [0].pack("C")
|
|
247
|
+
|
|
248
|
+
folders.each do |folder|
|
|
249
|
+
data << write_folder(
|
|
250
|
+
folder[:method_id],
|
|
251
|
+
folder[:properties],
|
|
252
|
+
)
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
data
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# Write unpack info section
|
|
259
|
+
#
|
|
260
|
+
# @param folders [Array<Hash>] Folder information
|
|
261
|
+
# @return [String] Encoded unpack info
|
|
262
|
+
def write_unpack_info(folders)
|
|
263
|
+
data = String.new(encoding: "BINARY")
|
|
264
|
+
|
|
265
|
+
data << [PropertyId::UNPACK_INFO].pack("C")
|
|
266
|
+
data << write_folders(folders)
|
|
267
|
+
|
|
268
|
+
# Coders unpack size
|
|
269
|
+
data << [PropertyId::CODERS_UNPACK_SIZE].pack("C")
|
|
270
|
+
folders.each do |folder|
|
|
271
|
+
data << write_number(folder[:unpack_size])
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
data << [PropertyId::K_END].pack("C")
|
|
275
|
+
data
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
# Write file names
|
|
279
|
+
#
|
|
280
|
+
# @param names [Array<String>] File names
|
|
281
|
+
# @return [String] Encoded names
|
|
282
|
+
def write_names(names)
|
|
283
|
+
data = String.new(encoding: "BINARY")
|
|
284
|
+
|
|
285
|
+
# Encode names as UTF-16LE
|
|
286
|
+
names_data = String.new(encoding: "BINARY")
|
|
287
|
+
names.each do |name|
|
|
288
|
+
name.encode("UTF-16LE").each_byte do |byte|
|
|
289
|
+
names_data << [byte].pack("C")
|
|
290
|
+
end
|
|
291
|
+
names_data << [0, 0].pack("CC") # Null terminator
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
data << write_number(names_data.bytesize + 1)
|
|
295
|
+
data << [0].pack("C") # Not external
|
|
296
|
+
data << names_data
|
|
297
|
+
|
|
298
|
+
data
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
# Write timestamps
|
|
302
|
+
#
|
|
303
|
+
# @param prop_id [Integer] Property ID
|
|
304
|
+
# @param times [Array<Time, nil>] Timestamps
|
|
305
|
+
# @return [String] Encoded timestamps
|
|
306
|
+
def write_timestamps(_prop_id, times)
|
|
307
|
+
data = String.new(encoding: "BINARY")
|
|
308
|
+
|
|
309
|
+
defined_bits = times.map { |t| !t.nil? }
|
|
310
|
+
times_data = String.new(encoding: "BINARY")
|
|
311
|
+
times_data << write_bit_vector(defined_bits)
|
|
312
|
+
times_data << [0].pack("C") # Not external
|
|
313
|
+
|
|
314
|
+
times.each do |time|
|
|
315
|
+
next unless time
|
|
316
|
+
|
|
317
|
+
# Convert to Windows FILETIME
|
|
318
|
+
unix_time = time.to_i
|
|
319
|
+
windows_time = (unix_time + 11_644_473_600) * 10_000_000
|
|
320
|
+
times_data << [windows_time].pack("Q<")
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
data << write_number(times_data.bytesize)
|
|
324
|
+
data << times_data
|
|
325
|
+
data
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
# Write files info section
|
|
329
|
+
#
|
|
330
|
+
# @param entries [Array<Models::FileEntry>] File entries
|
|
331
|
+
# @return [String] Encoded files info
|
|
332
|
+
def write_files_info(entries)
|
|
333
|
+
data = String.new(encoding: "BINARY")
|
|
334
|
+
|
|
335
|
+
data << [PropertyId::FILES_INFO].pack("C")
|
|
336
|
+
data << write_number(entries.size)
|
|
337
|
+
|
|
338
|
+
# Names
|
|
339
|
+
data << [PropertyId::NAME].pack("C")
|
|
340
|
+
data << write_names(entries.map(&:name))
|
|
341
|
+
|
|
342
|
+
# Empty stream flags
|
|
343
|
+
empty_bits = entries.map { |e| !e.has_stream }
|
|
344
|
+
if empty_bits.any?
|
|
345
|
+
data << [PropertyId::EMPTY_STREAM].pack("C")
|
|
346
|
+
empty_data = write_bit_vector(empty_bits)
|
|
347
|
+
data << write_number(empty_data.bytesize)
|
|
348
|
+
data << empty_data
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
# Modification times
|
|
352
|
+
mtimes = entries.map(&:mtime)
|
|
353
|
+
if mtimes.any?
|
|
354
|
+
data << [PropertyId::MTIME].pack("C")
|
|
355
|
+
data << write_timestamps(PropertyId::MTIME, mtimes)
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
# Attributes
|
|
359
|
+
attrs = entries.map(&:attributes)
|
|
360
|
+
if attrs.any?
|
|
361
|
+
data << [PropertyId::WIN_ATTRIB].pack("C")
|
|
362
|
+
defined_bits = attrs.map { |a| !a.nil? }
|
|
363
|
+
attrs_data = String.new(encoding: "BINARY")
|
|
364
|
+
attrs_data << write_bit_vector(defined_bits)
|
|
365
|
+
attrs_data << [0].pack("C") # Not external
|
|
366
|
+
attrs.each do |attr|
|
|
367
|
+
attrs_data << [attr].pack("V") if attr
|
|
368
|
+
end
|
|
369
|
+
data << write_number(attrs_data.bytesize)
|
|
370
|
+
data << attrs_data
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
data << [PropertyId::K_END].pack("C")
|
|
374
|
+
data
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
# Write main streams info
|
|
378
|
+
#
|
|
379
|
+
# @param options [Hash] Stream information
|
|
380
|
+
# @return [String] Encoded streams info
|
|
381
|
+
def write_streams_info(options)
|
|
382
|
+
data = String.new(encoding: "BINARY")
|
|
383
|
+
|
|
384
|
+
data << [PropertyId::MAIN_STREAMS_INFO].pack("C")
|
|
385
|
+
data << write_pack_info(
|
|
386
|
+
options[:pack_pos],
|
|
387
|
+
options[:pack_sizes],
|
|
388
|
+
options[:pack_crcs],
|
|
389
|
+
)
|
|
390
|
+
data << write_unpack_info(options[:folders])
|
|
391
|
+
|
|
392
|
+
# Substreams info - needed for solid archives with multiple files
|
|
393
|
+
if options[:digests] && !options[:digests].empty?
|
|
394
|
+
data << write_substreams_info(
|
|
395
|
+
options[:unpack_sizes] || [],
|
|
396
|
+
options[:digests],
|
|
397
|
+
options[:folders],
|
|
398
|
+
)
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
data << [PropertyId::K_END].pack("C")
|
|
402
|
+
data
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
# Write substreams info section
|
|
406
|
+
#
|
|
407
|
+
# @param unpack_sizes [Array<Integer>] Unpack sizes per file
|
|
408
|
+
# @param digests [Array<Integer>] CRC per file
|
|
409
|
+
# @param folders [Array<Hash>] Folder information
|
|
410
|
+
# @return [String] Encoded substreams info
|
|
411
|
+
def write_substreams_info(unpack_sizes, digests, folders)
|
|
412
|
+
data = String.new(encoding: "BINARY")
|
|
413
|
+
|
|
414
|
+
data << [PropertyId::SUBSTREAMS_INFO].pack("C")
|
|
415
|
+
|
|
416
|
+
# Write NUM_UNPACK_STREAM if multiple files in folder
|
|
417
|
+
if digests.size > folders.size
|
|
418
|
+
data << [PropertyId::NUM_UNPACK_STREAM].pack("C")
|
|
419
|
+
folders.each do |_folder|
|
|
420
|
+
# For solid archives, all files are in one folder
|
|
421
|
+
data << write_number(digests.size)
|
|
422
|
+
end
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
# Write unpack sizes if we have them and multiple files
|
|
426
|
+
if unpack_sizes.size > 1
|
|
427
|
+
data << [PropertyId::SIZE].pack("C")
|
|
428
|
+
# Write all but last size (last is calculated from folder unpack size)
|
|
429
|
+
unpack_sizes[0...-1].each do |size|
|
|
430
|
+
data << write_number(size)
|
|
431
|
+
end
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
# Write CRCs
|
|
435
|
+
if digests && !digests.empty?
|
|
436
|
+
data << [PropertyId::CRC].pack("C")
|
|
437
|
+
defined_bits = digests.map { |d| !d.nil? }
|
|
438
|
+
data << write_bit_vector(defined_bits)
|
|
439
|
+
digests.each do |crc|
|
|
440
|
+
data << [crc].pack("V") if crc
|
|
441
|
+
end
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
data << [PropertyId::K_END].pack("C")
|
|
445
|
+
data
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
# Write complete next header
|
|
449
|
+
#
|
|
450
|
+
# @param options [Hash] All header information
|
|
451
|
+
# @return [String] Encoded next header
|
|
452
|
+
def write_next_header(options)
|
|
453
|
+
data = String.new(encoding: "BINARY")
|
|
454
|
+
|
|
455
|
+
data << [PropertyId::HEADER].pack("C")
|
|
456
|
+
data << write_streams_info(options[:streams]) if
|
|
457
|
+
options[:streams]
|
|
458
|
+
data << write_files_info(options[:entries]) if options[:entries]
|
|
459
|
+
data << [PropertyId::K_END].pack("C")
|
|
460
|
+
|
|
461
|
+
data
|
|
462
|
+
end
|
|
463
|
+
end
|
|
464
|
+
end
|
|
465
|
+
end
|
|
466
|
+
end
|