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,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "zlib"
|
|
4
|
+
require_relative "../rar/rar_format_base"
|
|
5
|
+
require_relative "../../error"
|
|
6
|
+
|
|
7
|
+
module Omnizip
|
|
8
|
+
module Formats
|
|
9
|
+
module Rar5
|
|
10
|
+
# RAR v5 decompressor
|
|
11
|
+
#
|
|
12
|
+
# Decompresses data using RAR v5 decompression algorithms.
|
|
13
|
+
# This implementation uses DEFLATE as a compatible fallback
|
|
14
|
+
# for demonstration purposes. Full RAR5 decompression requires
|
|
15
|
+
# proprietary algorithms.
|
|
16
|
+
#
|
|
17
|
+
# @example Decompressing data
|
|
18
|
+
# decompressor = Rar5::Decompressor.new
|
|
19
|
+
# original = decompressor.decompress(compressed, method: :normal)
|
|
20
|
+
class Decompressor < Rar::RarFormatBase
|
|
21
|
+
# Initialize a RAR v5 decompressor
|
|
22
|
+
def initialize
|
|
23
|
+
super("rar5")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Decompress data using RAR v5 methods
|
|
27
|
+
#
|
|
28
|
+
# @param data [String] The compressed data
|
|
29
|
+
# @param method [Symbol] The compression method
|
|
30
|
+
# @param options [Hash] Decompression options
|
|
31
|
+
# @return [String] The decompressed data
|
|
32
|
+
def decompress(data, method: :normal, **_options)
|
|
33
|
+
case method
|
|
34
|
+
when :store
|
|
35
|
+
decompress_store(data)
|
|
36
|
+
when :fastest, :fast, :normal, :good, :best
|
|
37
|
+
decompress_deflate(data)
|
|
38
|
+
else
|
|
39
|
+
raise FormatError,
|
|
40
|
+
"Unsupported decompression method: #{method}"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
# Return stored data without decompression
|
|
47
|
+
#
|
|
48
|
+
# @param data [String] The stored data
|
|
49
|
+
# @return [String] The uncompressed data
|
|
50
|
+
def decompress_store(data)
|
|
51
|
+
data
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Decompress using DEFLATE (compatible fallback)
|
|
55
|
+
#
|
|
56
|
+
# @param data [String] The compressed data
|
|
57
|
+
# @return [String] The decompressed data
|
|
58
|
+
def decompress_deflate(data)
|
|
59
|
+
Zlib::Inflate.inflate(data)
|
|
60
|
+
rescue Zlib::Error => e
|
|
61
|
+
raise FormatError, "Decompression failed: #{e.message}"
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
require "lutaml/model"
|
|
5
|
+
rescue LoadError, ArgumentError
|
|
6
|
+
# lutaml-model not available, using simple classes
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
require_relative "../rar/rar_format_base"
|
|
10
|
+
require_relative "decompressor"
|
|
11
|
+
|
|
12
|
+
module Omnizip
|
|
13
|
+
module Formats
|
|
14
|
+
module Rar5
|
|
15
|
+
# RAR v5 archive reader
|
|
16
|
+
#
|
|
17
|
+
# Reads RAR 5.x format archives, parsing headers and extracting file data
|
|
18
|
+
# according to the RAR v5 specification.
|
|
19
|
+
#
|
|
20
|
+
# RAR 5 uses variable-length integers (vint) and improved header structure.
|
|
21
|
+
#
|
|
22
|
+
# @example Reading a RAR5 archive
|
|
23
|
+
# reader = Rar5::Reader.new
|
|
24
|
+
# File.open("archive.rar", "rb") do |file|
|
|
25
|
+
# entries = reader.read_archive(file)
|
|
26
|
+
# entries.each { |entry| puts entry.name }
|
|
27
|
+
# end
|
|
28
|
+
class Reader < Rar::RarFormatBase
|
|
29
|
+
# Initialize a RAR v5 reader
|
|
30
|
+
def initialize
|
|
31
|
+
super("rar5")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Read a RAR v5 archive
|
|
35
|
+
#
|
|
36
|
+
# @param io [IO] The input stream
|
|
37
|
+
# @return [Array<Entry>] The archive entries
|
|
38
|
+
# @raise [FormatError] If the archive format is invalid
|
|
39
|
+
def read_archive(io)
|
|
40
|
+
unless verify_magic_bytes(io)
|
|
41
|
+
raise FormatError, "Invalid RAR v5 signature"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Skip past the magic bytes (8 bytes)
|
|
45
|
+
io.seek(8, ::IO::SEEK_SET)
|
|
46
|
+
|
|
47
|
+
entries = []
|
|
48
|
+
|
|
49
|
+
# Read main header
|
|
50
|
+
main_header = read_header(io)
|
|
51
|
+
unless main_header.type == block_type_code(:main_header)
|
|
52
|
+
raise FormatError, "Expected main archive header"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
@archive_flags = main_header.flags
|
|
56
|
+
|
|
57
|
+
# Skip to end of main header
|
|
58
|
+
skip_to_header_end(io, main_header)
|
|
59
|
+
|
|
60
|
+
# Read blocks until end marker or EOF/error
|
|
61
|
+
loop do
|
|
62
|
+
header = read_header(io)
|
|
63
|
+
break if header.type == block_type_code(:end_marker)
|
|
64
|
+
|
|
65
|
+
case header.type
|
|
66
|
+
when block_type_code(:file_header)
|
|
67
|
+
entry = read_file_entry(io, header)
|
|
68
|
+
entries << entry if entry
|
|
69
|
+
when block_type_code(:service_header)
|
|
70
|
+
skip_header_data(io, header)
|
|
71
|
+
when block_type_code(:encryption_header)
|
|
72
|
+
read_encryption_header(io, header)
|
|
73
|
+
else
|
|
74
|
+
skip_header_data(io, header)
|
|
75
|
+
end
|
|
76
|
+
rescue EOFError, RangeError, FormatError
|
|
77
|
+
# Truncated or corrupted file - return what we have
|
|
78
|
+
break
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
entries
|
|
82
|
+
rescue EOFError, RangeError, FormatError
|
|
83
|
+
# Handle truncated/invalid files gracefully
|
|
84
|
+
[]
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
private
|
|
88
|
+
|
|
89
|
+
# Skip to the end of a header
|
|
90
|
+
#
|
|
91
|
+
# @param io [IO] The input stream
|
|
92
|
+
# @param header [HeaderBlock] The header
|
|
93
|
+
def skip_to_header_end(io, header)
|
|
94
|
+
# Calculate end of header: header_start + 4 (CRC) + header_size + vint_length
|
|
95
|
+
header_end = header.header_start + 4 + header.size.to_i + header.vint_length.to_i
|
|
96
|
+
|
|
97
|
+
# Validate the offset is reasonable (max 1GB)
|
|
98
|
+
max_offset = 1_073_741_824
|
|
99
|
+
raise RangeError, "Header offset too large" if header_end > max_offset
|
|
100
|
+
raise RangeError, "Header offset negative" if header_end.negative?
|
|
101
|
+
|
|
102
|
+
io.seek(header_end, ::IO::SEEK_SET)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Read a RAR v5 header
|
|
106
|
+
#
|
|
107
|
+
# @param io [IO] The input stream
|
|
108
|
+
# @return [HeaderBlock] The header block
|
|
109
|
+
def read_header(io)
|
|
110
|
+
# Record start position (before CRC)
|
|
111
|
+
header_start = io.pos
|
|
112
|
+
|
|
113
|
+
header_crc = io.read(4)&.unpack1("V")
|
|
114
|
+
|
|
115
|
+
# Read header size vint and track its length
|
|
116
|
+
header_size, vint_length = read_vint_with_length(io)
|
|
117
|
+
|
|
118
|
+
# Now we're at the start of header content
|
|
119
|
+
# header_size is the size of the content (from header_type to end)
|
|
120
|
+
content_start = io.pos
|
|
121
|
+
|
|
122
|
+
header_type = read_vint(io)
|
|
123
|
+
header_flags = read_vint(io)
|
|
124
|
+
|
|
125
|
+
# Read extra size if present
|
|
126
|
+
extra_size = 0
|
|
127
|
+
extra_size = read_vint(io) if header_flags & 0x0001 != 0
|
|
128
|
+
|
|
129
|
+
# Read data size if present
|
|
130
|
+
data_size = 0
|
|
131
|
+
data_size = read_vint(io) if header_flags & 0x0002 != 0
|
|
132
|
+
|
|
133
|
+
# Store positions for proper skipping
|
|
134
|
+
HeaderBlock.new(
|
|
135
|
+
crc: header_crc,
|
|
136
|
+
size: header_size,
|
|
137
|
+
vint_length: vint_length,
|
|
138
|
+
type: header_type,
|
|
139
|
+
flags: header_flags,
|
|
140
|
+
extra_size: extra_size,
|
|
141
|
+
data_size: data_size,
|
|
142
|
+
header_start: header_start,
|
|
143
|
+
content_start: content_start,
|
|
144
|
+
)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Read a vint and return both value and length
|
|
148
|
+
#
|
|
149
|
+
# @param io [IO] The input stream
|
|
150
|
+
# @return [Array<Integer, Integer>] Value and length in bytes
|
|
151
|
+
def read_vint_with_length(io)
|
|
152
|
+
result = 0
|
|
153
|
+
shift = 0
|
|
154
|
+
length = 0
|
|
155
|
+
|
|
156
|
+
loop do
|
|
157
|
+
byte = io.read(1)&.unpack1("C")
|
|
158
|
+
raise FormatError, "Unexpected EOF" unless byte
|
|
159
|
+
|
|
160
|
+
length += 1
|
|
161
|
+
result |= (byte & 0x7F) << shift
|
|
162
|
+
break if byte.nobits?(0x80)
|
|
163
|
+
|
|
164
|
+
shift += 7
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
[result, length]
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Read a file entry from archive
|
|
171
|
+
#
|
|
172
|
+
# @param io [IO] The input stream
|
|
173
|
+
# @param header [HeaderBlock] The file header
|
|
174
|
+
# @return [Entry] The file entry
|
|
175
|
+
def read_file_entry(io, header)
|
|
176
|
+
flags = read_vint(io)
|
|
177
|
+
unpacked_size = read_vint(io)
|
|
178
|
+
attributes = read_vint(io)
|
|
179
|
+
|
|
180
|
+
# Read modification time if present
|
|
181
|
+
mtime = Time.now
|
|
182
|
+
if flags & 0x02 != 0 # time_present flag
|
|
183
|
+
mtime = read_file_time(io)
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Read CRC32 if present
|
|
187
|
+
data_crc = 0
|
|
188
|
+
if flags & 0x04 != 0 # crc32_present flag
|
|
189
|
+
data_crc = io.read(4)&.unpack1("V")
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Read compression info
|
|
193
|
+
compression_flags = read_vint(io)
|
|
194
|
+
host_os = read_vint(io)
|
|
195
|
+
name_length = read_vint(io)
|
|
196
|
+
|
|
197
|
+
# Read filename (UTF-8 encoded)
|
|
198
|
+
name_bytes = io.read(name_length)
|
|
199
|
+
filename = name_bytes.force_encoding("UTF-8")
|
|
200
|
+
|
|
201
|
+
# Determine if directory
|
|
202
|
+
is_directory = flags.anybits?(0x01) # directory flag
|
|
203
|
+
|
|
204
|
+
# Skip to end of header
|
|
205
|
+
skip_to_header_end(io, header)
|
|
206
|
+
|
|
207
|
+
# Record data offset before skipping
|
|
208
|
+
data_offset = io.pos
|
|
209
|
+
|
|
210
|
+
# Skip file data (so we can read the next header)
|
|
211
|
+
io.seek(header.data_size, ::IO::SEEK_CUR) if header.data_size.to_i.positive?
|
|
212
|
+
|
|
213
|
+
Entry.new(
|
|
214
|
+
name: filename,
|
|
215
|
+
compressed_size: header.data_size || 0,
|
|
216
|
+
uncompressed_size: unpacked_size,
|
|
217
|
+
crc32: data_crc,
|
|
218
|
+
compression_method: extract_compression_method(compression_flags),
|
|
219
|
+
modified_time: mtime,
|
|
220
|
+
attributes: attributes,
|
|
221
|
+
encrypted: header.flags.anybits?(0x04),
|
|
222
|
+
data_offset: data_offset,
|
|
223
|
+
host_os: host_os,
|
|
224
|
+
is_directory: is_directory,
|
|
225
|
+
)
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# Extract compression method from flags
|
|
229
|
+
#
|
|
230
|
+
# @param flags [Integer] The compression flags
|
|
231
|
+
# @return [Symbol] The compression method
|
|
232
|
+
def extract_compression_method(flags)
|
|
233
|
+
method_code = flags & 0x07
|
|
234
|
+
|
|
235
|
+
case method_code
|
|
236
|
+
when 0 then :store
|
|
237
|
+
when 1 then :fastest
|
|
238
|
+
when 2 then :fast
|
|
239
|
+
when 3 then :normal
|
|
240
|
+
when 4 then :good
|
|
241
|
+
when 5 then :best
|
|
242
|
+
else :normal
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# Read file time in RAR5 format
|
|
247
|
+
#
|
|
248
|
+
# @param io [IO] The input stream
|
|
249
|
+
# @return [Time] The file time
|
|
250
|
+
def read_file_time(io)
|
|
251
|
+
read_vint(io)
|
|
252
|
+
|
|
253
|
+
# Simplified time reading - actual format is more complex
|
|
254
|
+
unix_time = io.read(8)&.unpack1("Q<")
|
|
255
|
+
Time.at(unix_time / 10_000_000.0) if unix_time
|
|
256
|
+
rescue ArgumentError
|
|
257
|
+
Time.now
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# Read encryption header
|
|
261
|
+
#
|
|
262
|
+
# @param io [IO] The input stream
|
|
263
|
+
# @param header [HeaderBlock] The header
|
|
264
|
+
# @return [void]
|
|
265
|
+
def read_encryption_header(io, header)
|
|
266
|
+
# Read and skip encryption data
|
|
267
|
+
skip_header_data(io, header)
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# Skip header data
|
|
271
|
+
#
|
|
272
|
+
# @param io [IO] The input stream
|
|
273
|
+
# @param header [HeaderBlock] The header
|
|
274
|
+
# @return [void]
|
|
275
|
+
def skip_header_data(io, header)
|
|
276
|
+
# Calculate end of header: header_start + 4 (CRC) + header_size + vint_length
|
|
277
|
+
header_end = header.header_start.to_i + 4 + header.size.to_i + header.vint_length.to_i
|
|
278
|
+
current_pos = io.pos
|
|
279
|
+
|
|
280
|
+
# Validate the offset is reasonable (max 1GB)
|
|
281
|
+
max_offset = 1_073_741_824
|
|
282
|
+
if header_end > max_offset || header_end.negative?
|
|
283
|
+
raise RangeError, "Invalid header offset"
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
if header_end > current_pos
|
|
287
|
+
io.seek(header_end - current_pos, ::IO::SEEK_CUR)
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
# Skip data section if present (with bounds checking)
|
|
291
|
+
data_size = header.data_size.to_i
|
|
292
|
+
if data_size.positive? && data_size < max_offset
|
|
293
|
+
io.seek(data_size, ::IO::SEEK_CUR)
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
# RAR v5 header block model
|
|
299
|
+
class HeaderBlock
|
|
300
|
+
attr_accessor :crc, :size, :vint_length, :type, :flags, :extra_size,
|
|
301
|
+
:data_size, :header_start, :content_start
|
|
302
|
+
|
|
303
|
+
def initialize(crc: nil, size: nil, vint_length: 1, type: nil, flags: nil,
|
|
304
|
+
extra_size: nil, data_size: nil, header_start: nil, content_start: nil)
|
|
305
|
+
@crc = crc
|
|
306
|
+
@size = size
|
|
307
|
+
@vint_length = vint_length
|
|
308
|
+
@type = type
|
|
309
|
+
@flags = flags
|
|
310
|
+
@extra_size = extra_size
|
|
311
|
+
@data_size = data_size
|
|
312
|
+
@header_start = header_start
|
|
313
|
+
@content_start = content_start
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
# RAR v5 archive entry model
|
|
318
|
+
class Entry
|
|
319
|
+
attr_accessor :name, :compressed_size, :uncompressed_size, :crc32,
|
|
320
|
+
:compression_method, :modified_time, :attributes,
|
|
321
|
+
:encrypted, :data_offset, :host_os, :is_directory
|
|
322
|
+
|
|
323
|
+
def initialize(name: nil, compressed_size: nil, uncompressed_size: nil,
|
|
324
|
+
crc32: nil, compression_method: nil, modified_time: nil,
|
|
325
|
+
attributes: nil, encrypted: nil, data_offset: nil,
|
|
326
|
+
host_os: nil, is_directory: nil)
|
|
327
|
+
@name = name
|
|
328
|
+
@compressed_size = compressed_size
|
|
329
|
+
@uncompressed_size = uncompressed_size
|
|
330
|
+
@crc32 = crc32
|
|
331
|
+
@compression_method = compression_method
|
|
332
|
+
@modified_time = modified_time
|
|
333
|
+
@attributes = attributes
|
|
334
|
+
@encrypted = encrypted
|
|
335
|
+
@data_offset = data_offset
|
|
336
|
+
@host_os = host_os
|
|
337
|
+
@is_directory = is_directory
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
end
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
require "lutaml/model"
|
|
5
|
+
rescue LoadError, ArgumentError
|
|
6
|
+
# lutaml-model not available, using simple classes
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
require "stringio"
|
|
10
|
+
require_relative "../rar/rar_format_base"
|
|
11
|
+
require_relative "compressor"
|
|
12
|
+
|
|
13
|
+
module Omnizip
|
|
14
|
+
module Formats
|
|
15
|
+
module Rar5
|
|
16
|
+
# RAR v5 archive writer
|
|
17
|
+
#
|
|
18
|
+
# Writes RAR 5.x format archives, creating headers and compressing file
|
|
19
|
+
# data according to the RAR v5 specification.
|
|
20
|
+
#
|
|
21
|
+
# RAR 5 uses variable-length integers and improved header structure.
|
|
22
|
+
#
|
|
23
|
+
# @example Writing a RAR5 archive
|
|
24
|
+
# writer = Rar5::Writer.new
|
|
25
|
+
# File.open("archive.rar", "wb") do |file|
|
|
26
|
+
# entries = [
|
|
27
|
+
# {name: "file.txt", data: "content", time: Time.now}
|
|
28
|
+
# ]
|
|
29
|
+
# writer.write_archive(file, entries)
|
|
30
|
+
# end
|
|
31
|
+
class Writer < Rar::RarFormatBase
|
|
32
|
+
# Initialize a RAR v5 writer
|
|
33
|
+
def initialize
|
|
34
|
+
super("rar5")
|
|
35
|
+
@compressor = Compressor.new
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Write a RAR v5 archive
|
|
39
|
+
#
|
|
40
|
+
# @param io [IO] The output stream
|
|
41
|
+
# @param entries [Array<Hash>] The entries to write
|
|
42
|
+
# @return [void]
|
|
43
|
+
def write_archive(io, entries)
|
|
44
|
+
# Write magic bytes
|
|
45
|
+
io.write(spec.magic_bytes.pack("C*"))
|
|
46
|
+
|
|
47
|
+
# Write main header
|
|
48
|
+
write_main_header(io)
|
|
49
|
+
|
|
50
|
+
# Write file entries
|
|
51
|
+
entries.each do |entry|
|
|
52
|
+
write_file_entry(io, entry)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Write end marker
|
|
56
|
+
write_end_marker(io)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
# Write main archive header
|
|
62
|
+
#
|
|
63
|
+
# @param io [IO] The output stream
|
|
64
|
+
# @return [void]
|
|
65
|
+
def write_main_header(io)
|
|
66
|
+
flags = 0
|
|
67
|
+
|
|
68
|
+
header_data = StringIO.new
|
|
69
|
+
write_vint(header_data, flags)
|
|
70
|
+
|
|
71
|
+
write_header(
|
|
72
|
+
io,
|
|
73
|
+
type: block_type_code(:main_header),
|
|
74
|
+
flags: 0,
|
|
75
|
+
data: header_data.string,
|
|
76
|
+
)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Write a file entry
|
|
80
|
+
#
|
|
81
|
+
# @param io [IO] The output stream
|
|
82
|
+
# @param entry [Hash] The entry data
|
|
83
|
+
# @return [void]
|
|
84
|
+
def write_file_entry(io, entry)
|
|
85
|
+
name = entry[:name] || entry["name"]
|
|
86
|
+
data = entry[:data] || entry["data"]
|
|
87
|
+
mtime = entry[:time] || entry["time"] || Time.now
|
|
88
|
+
method = entry[:method] || entry["method"] || :normal
|
|
89
|
+
|
|
90
|
+
# Compress data
|
|
91
|
+
compressed_data = @compressor.compress(data, method: method)
|
|
92
|
+
|
|
93
|
+
# Calculate sizes and CRC
|
|
94
|
+
compressed_data.bytesize
|
|
95
|
+
unpacked_size = data.bytesize
|
|
96
|
+
file_crc = calculate_crc32(data)
|
|
97
|
+
|
|
98
|
+
# Encode filename to UTF-8
|
|
99
|
+
name_bytes = name.encode("UTF-8")
|
|
100
|
+
name_length = name_bytes.bytesize
|
|
101
|
+
|
|
102
|
+
# Build file header data
|
|
103
|
+
file_flags = spec.format.file_header_flags[:time_present] |
|
|
104
|
+
spec.format.file_header_flags[:crc32_present]
|
|
105
|
+
|
|
106
|
+
compression_info = compression_method_code(method) & 0x07
|
|
107
|
+
|
|
108
|
+
header_data = StringIO.new
|
|
109
|
+
write_vint(header_data, file_flags)
|
|
110
|
+
write_vint(header_data, unpacked_size)
|
|
111
|
+
write_vint(header_data, 0x20) # Attributes (normal file)
|
|
112
|
+
|
|
113
|
+
# Write modification time
|
|
114
|
+
write_file_time(header_data, mtime)
|
|
115
|
+
|
|
116
|
+
# Write CRC32
|
|
117
|
+
header_data.write([file_crc].pack("V"))
|
|
118
|
+
|
|
119
|
+
# Write compression info
|
|
120
|
+
write_vint(header_data, compression_info)
|
|
121
|
+
write_vint(header_data, spec.format.host_os[:windows])
|
|
122
|
+
write_vint(header_data, name_length)
|
|
123
|
+
header_data.write(name_bytes)
|
|
124
|
+
|
|
125
|
+
# Write header with data
|
|
126
|
+
write_header(
|
|
127
|
+
io,
|
|
128
|
+
type: block_type_code(:file_header),
|
|
129
|
+
flags: 0x0002, # Has data size
|
|
130
|
+
data: header_data.string,
|
|
131
|
+
data_section: compressed_data,
|
|
132
|
+
)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Write end marker
|
|
136
|
+
#
|
|
137
|
+
# @param io [IO] The output stream
|
|
138
|
+
# @return [void]
|
|
139
|
+
def write_end_marker(io)
|
|
140
|
+
write_header(
|
|
141
|
+
io,
|
|
142
|
+
type: block_type_code(:end_marker),
|
|
143
|
+
flags: 0x0004, # Archive end flag
|
|
144
|
+
data: "",
|
|
145
|
+
)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Write a RAR v5 header
|
|
149
|
+
#
|
|
150
|
+
# @param io [IO] The output stream
|
|
151
|
+
# @param type [Integer] The header type
|
|
152
|
+
# @param flags [Integer] The header flags
|
|
153
|
+
# @param data [String] The header data
|
|
154
|
+
# @param data_section [String, nil] Optional data section
|
|
155
|
+
# @return [void]
|
|
156
|
+
def write_header(io, type:, flags:, data:, data_section: nil)
|
|
157
|
+
header_io = StringIO.new
|
|
158
|
+
|
|
159
|
+
# Calculate header size (not including CRC)
|
|
160
|
+
size_bytes = StringIO.new
|
|
161
|
+
write_vint(size_bytes, type)
|
|
162
|
+
write_vint(size_bytes, flags)
|
|
163
|
+
|
|
164
|
+
if data_section
|
|
165
|
+
flags |= 0x0002 # Has data size flag
|
|
166
|
+
write_vint(size_bytes, data_section.bytesize)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
size_bytes.write(data)
|
|
170
|
+
|
|
171
|
+
header_size = size_bytes.string.bytesize
|
|
172
|
+
|
|
173
|
+
# Write size vint
|
|
174
|
+
write_vint(header_io, header_size)
|
|
175
|
+
|
|
176
|
+
# Write type and flags
|
|
177
|
+
write_vint(header_io, type)
|
|
178
|
+
write_vint(header_io, flags)
|
|
179
|
+
|
|
180
|
+
# Write data size if present
|
|
181
|
+
write_vint(header_io, data_section.bytesize) if data_section
|
|
182
|
+
|
|
183
|
+
# Write header data
|
|
184
|
+
header_io.write(data)
|
|
185
|
+
|
|
186
|
+
# Calculate CRC32 of header (excluding CRC field itself)
|
|
187
|
+
header_crc = calculate_crc32(header_io.string)
|
|
188
|
+
|
|
189
|
+
# Write CRC and header to output
|
|
190
|
+
io.write([header_crc].pack("V"))
|
|
191
|
+
io.write(header_io.string)
|
|
192
|
+
|
|
193
|
+
# Write data section if present
|
|
194
|
+
io.write(data_section) if data_section
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Write file time in RAR5 format
|
|
198
|
+
#
|
|
199
|
+
# @param io [IO] The output stream
|
|
200
|
+
# @param time [Time] The time to write
|
|
201
|
+
# @return [void]
|
|
202
|
+
def write_file_time(io, time)
|
|
203
|
+
# RAR5 uses Windows FILETIME format (100-nanosecond intervals since
|
|
204
|
+
# 1601-01-01). Simplified implementation.
|
|
205
|
+
unix_time = time.to_i
|
|
206
|
+
windows_time = (unix_time + 11_644_473_600) * 10_000_000
|
|
207
|
+
|
|
208
|
+
write_vint(io, 0x01) # Time flags (modification time present)
|
|
209
|
+
io.write([windows_time].pack("Q<"))
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|