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,168 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "models/rar_volume"
|
|
4
|
+
|
|
5
|
+
module Omnizip
|
|
6
|
+
module Formats
|
|
7
|
+
module Rar
|
|
8
|
+
# Manages multi-volume RAR archives
|
|
9
|
+
# Handles volume detection, sequencing, and coordination
|
|
10
|
+
class VolumeManager
|
|
11
|
+
attr_reader :volumes, :base_path
|
|
12
|
+
|
|
13
|
+
# Initialize volume manager
|
|
14
|
+
#
|
|
15
|
+
# @param path [String] Path to any volume in the set
|
|
16
|
+
def initialize(path)
|
|
17
|
+
@base_path = path
|
|
18
|
+
@volumes = []
|
|
19
|
+
detect_volumes
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Get total number of volumes
|
|
23
|
+
#
|
|
24
|
+
# @return [Integer] Number of volumes
|
|
25
|
+
def volume_count
|
|
26
|
+
@volumes.size
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Check if this is a multi-volume archive
|
|
30
|
+
#
|
|
31
|
+
# @return [Boolean] true if multi-volume
|
|
32
|
+
def multi_volume?
|
|
33
|
+
@volumes.size > 1
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Get first volume
|
|
37
|
+
#
|
|
38
|
+
# @return [Models::RarVolume, nil] First volume or nil
|
|
39
|
+
def first_volume
|
|
40
|
+
@volumes.find(&:first?)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Get last volume
|
|
44
|
+
#
|
|
45
|
+
# @return [Models::RarVolume, nil] Last volume or nil
|
|
46
|
+
def last_volume
|
|
47
|
+
@volumes.find(&:last?)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Get volume by number
|
|
51
|
+
#
|
|
52
|
+
# @param number [Integer] Volume number (0-based)
|
|
53
|
+
# @return [Models::RarVolume, nil] Volume or nil
|
|
54
|
+
def volume_at(number)
|
|
55
|
+
@volumes.find { |v| v.volume_number == number }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Get all volume paths
|
|
59
|
+
#
|
|
60
|
+
# @return [Array<String>] Paths to all volumes
|
|
61
|
+
def volume_paths
|
|
62
|
+
@volumes.map(&:path)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Get recovery files for volumes
|
|
66
|
+
#
|
|
67
|
+
# @return [Array<String>] Paths to .rev files
|
|
68
|
+
def recovery_files
|
|
69
|
+
rev_files = []
|
|
70
|
+
|
|
71
|
+
@volumes.each do |volume|
|
|
72
|
+
# Check for .rev file for this volume
|
|
73
|
+
rev_path = "#{volume.path}.rev"
|
|
74
|
+
rev_files << rev_path if File.exist?(rev_path)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
rev_files
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Validate volume sequence
|
|
81
|
+
#
|
|
82
|
+
# @return [Boolean] true if all volumes exist and sequence is valid
|
|
83
|
+
def valid_sequence?
|
|
84
|
+
return true unless multi_volume?
|
|
85
|
+
|
|
86
|
+
# Check all volumes exist
|
|
87
|
+
return false unless @volumes.all?(&:exist?)
|
|
88
|
+
|
|
89
|
+
# Check sequence is continuous
|
|
90
|
+
expected_numbers = (0...@volumes.size).to_a
|
|
91
|
+
actual_numbers = @volumes.map(&:volume_number).sort
|
|
92
|
+
expected_numbers == actual_numbers
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
private
|
|
96
|
+
|
|
97
|
+
# Detect all volumes in the set
|
|
98
|
+
def detect_volumes
|
|
99
|
+
if rar5_naming?
|
|
100
|
+
detect_rar5_volumes
|
|
101
|
+
else
|
|
102
|
+
detect_rar4_volumes
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Mark first and last
|
|
106
|
+
@volumes.first.is_first = true if @volumes.any?
|
|
107
|
+
@volumes.last.is_last = true if @volumes.any?
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Check if using RAR5 naming (.partNN.rar)
|
|
111
|
+
#
|
|
112
|
+
# @return [Boolean] true if RAR5 naming
|
|
113
|
+
def rar5_naming?
|
|
114
|
+
@base_path.match?(/\.part\d+\.rar$/i)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Detect RAR5 volumes (.part01.rar, .part02.rar, ...)
|
|
118
|
+
def detect_rar5_volumes
|
|
119
|
+
# Extract base name and current part number
|
|
120
|
+
if @base_path =~ /^(.+)\.part(\d+)\.rar$/i
|
|
121
|
+
base_name = Regexp.last_match(1)
|
|
122
|
+
Regexp.last_match(2).to_i
|
|
123
|
+
|
|
124
|
+
# Find all volumes
|
|
125
|
+
volume_num = 0
|
|
126
|
+
(1..999).each do |i|
|
|
127
|
+
path = format("%s.part%02d.rar", base_name, i)
|
|
128
|
+
break unless File.exist?(path)
|
|
129
|
+
|
|
130
|
+
volume = Models::RarVolume.new(path, volume_num)
|
|
131
|
+
@volumes << volume
|
|
132
|
+
volume_num += 1
|
|
133
|
+
end
|
|
134
|
+
else
|
|
135
|
+
# Single volume
|
|
136
|
+
@volumes << Models::RarVolume.new(@base_path, 0)
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Detect RAR4 volumes (.rar, .r00, .r01, ...)
|
|
141
|
+
def detect_rar4_volumes
|
|
142
|
+
dir = File.dirname(@base_path)
|
|
143
|
+
basename = File.basename(@base_path, ".*")
|
|
144
|
+
|
|
145
|
+
# Check for .rar file
|
|
146
|
+
rar_path = File.join(dir, "#{basename}.rar")
|
|
147
|
+
if File.exist?(rar_path)
|
|
148
|
+
@volumes << Models::RarVolume.new(rar_path, 0)
|
|
149
|
+
|
|
150
|
+
# Find .r00, .r01, .r02, ...
|
|
151
|
+
volume_num = 1
|
|
152
|
+
(0..99).each do |i|
|
|
153
|
+
path = File.join(dir, format("%s.r%02d", basename, i))
|
|
154
|
+
break unless File.exist?(path)
|
|
155
|
+
|
|
156
|
+
volume = Models::RarVolume.new(path, volume_num)
|
|
157
|
+
@volumes << volume
|
|
158
|
+
volume_num += 1
|
|
159
|
+
end
|
|
160
|
+
else
|
|
161
|
+
# Single volume
|
|
162
|
+
@volumes << Models::RarVolume.new(@base_path, 0)
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "zlib"
|
|
5
|
+
require "stringio"
|
|
6
|
+
require_relative "constants"
|
|
7
|
+
require_relative "header"
|
|
8
|
+
require_relative "compression/dispatcher"
|
|
9
|
+
|
|
10
|
+
module Omnizip
|
|
11
|
+
module Formats
|
|
12
|
+
module Rar
|
|
13
|
+
# Pure Ruby RAR archive writer
|
|
14
|
+
#
|
|
15
|
+
# This class provides basic RAR archive creation in pure Ruby.
|
|
16
|
+
# It writes RAR4-compatible archives with basic compression support.
|
|
17
|
+
#
|
|
18
|
+
# @example Create a RAR archive
|
|
19
|
+
# writer = Writer.new('archive.rar')
|
|
20
|
+
# writer.add_file('document.pdf')
|
|
21
|
+
# writer.add_directory('photos/')
|
|
22
|
+
# writer.write
|
|
23
|
+
#
|
|
24
|
+
# @example Create with options
|
|
25
|
+
# writer = Writer.new('archive.rar',
|
|
26
|
+
# compression: :best,
|
|
27
|
+
# solid: true,
|
|
28
|
+
# recovery: 5
|
|
29
|
+
# )
|
|
30
|
+
class Writer
|
|
31
|
+
include Constants
|
|
32
|
+
|
|
33
|
+
# @return [String] Output archive path
|
|
34
|
+
attr_reader :output_path
|
|
35
|
+
|
|
36
|
+
# @return [Hash] Compression options
|
|
37
|
+
attr_reader :options
|
|
38
|
+
|
|
39
|
+
# @return [Array<Hash>] Files to add
|
|
40
|
+
attr_reader :files
|
|
41
|
+
|
|
42
|
+
# @return [Array<Hash>] Directories to add
|
|
43
|
+
attr_reader :directories
|
|
44
|
+
|
|
45
|
+
# Check if RAR creation is available
|
|
46
|
+
#
|
|
47
|
+
# @return [Boolean] always true for pure Ruby implementation
|
|
48
|
+
def self.available?
|
|
49
|
+
true
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Get RAR writer information
|
|
53
|
+
#
|
|
54
|
+
# @return [Hash] Writer type and version
|
|
55
|
+
def self.info
|
|
56
|
+
{
|
|
57
|
+
available: true,
|
|
58
|
+
type: :pure_ruby,
|
|
59
|
+
version: "4.0",
|
|
60
|
+
}
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Initialize RAR writer
|
|
64
|
+
#
|
|
65
|
+
# @param output_path [String] Output RAR file path
|
|
66
|
+
# @param options [Hash] Compression options
|
|
67
|
+
# @option options [Symbol] :compression Compression level
|
|
68
|
+
# (:store, :fastest, :fast, :normal, :good, :best)
|
|
69
|
+
# @option options [Boolean] :solid Create solid archive
|
|
70
|
+
# @option options [Integer] :recovery Recovery record percentage (0-10)
|
|
71
|
+
# @option options [Boolean] :encrypt_headers Encrypt file names
|
|
72
|
+
# @option options [String] :password Archive password
|
|
73
|
+
# @option options [Integer] :volume_size Split into volumes (bytes)
|
|
74
|
+
# @option options [Boolean] :test_after_create Test archive after creation
|
|
75
|
+
def initialize(output_path, options = {})
|
|
76
|
+
@output_path = output_path
|
|
77
|
+
@options = default_options.merge(options)
|
|
78
|
+
@files = []
|
|
79
|
+
@directories = []
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Add file to archive
|
|
83
|
+
#
|
|
84
|
+
# @param file_path [String] Path to file
|
|
85
|
+
# @param archive_path [String, nil] Path within archive
|
|
86
|
+
# @raise [ArgumentError] if file does not exist
|
|
87
|
+
def add_file(file_path, archive_path = nil)
|
|
88
|
+
raise ArgumentError, "File not found: #{file_path}" unless
|
|
89
|
+
File.exist?(file_path)
|
|
90
|
+
|
|
91
|
+
@files << {
|
|
92
|
+
source: File.expand_path(file_path),
|
|
93
|
+
archive_path: archive_path,
|
|
94
|
+
}
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Add directory to archive
|
|
98
|
+
#
|
|
99
|
+
# @param dir_path [String] Path to directory
|
|
100
|
+
# @param recursive [Boolean] Include subdirectories
|
|
101
|
+
# @param archive_path [String, nil] Path within archive
|
|
102
|
+
# @raise [ArgumentError] if directory does not exist
|
|
103
|
+
def add_directory(dir_path, recursive: true, archive_path: nil)
|
|
104
|
+
raise ArgumentError, "Directory not found: #{dir_path}" unless
|
|
105
|
+
Dir.exist?(dir_path)
|
|
106
|
+
|
|
107
|
+
@directories << {
|
|
108
|
+
source: File.expand_path(dir_path),
|
|
109
|
+
recursive: recursive,
|
|
110
|
+
archive_path: archive_path,
|
|
111
|
+
}
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Create RAR archive
|
|
115
|
+
#
|
|
116
|
+
# @return [String] Path to created archive
|
|
117
|
+
def write
|
|
118
|
+
File.open(@output_path, "wb") do |io|
|
|
119
|
+
write_signature(io)
|
|
120
|
+
write_marker_block(io)
|
|
121
|
+
write_archive_header(io)
|
|
122
|
+
write_file_entries(io)
|
|
123
|
+
write_end_block(io)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Test archive if requested
|
|
127
|
+
test_archive if @options[:test_after_create]
|
|
128
|
+
|
|
129
|
+
@output_path
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
private
|
|
133
|
+
|
|
134
|
+
# Default compression options
|
|
135
|
+
#
|
|
136
|
+
# @return [Hash] Default options
|
|
137
|
+
def default_options
|
|
138
|
+
{
|
|
139
|
+
compression: :normal,
|
|
140
|
+
solid: false,
|
|
141
|
+
recovery: 0,
|
|
142
|
+
encrypt_headers: false,
|
|
143
|
+
password: nil,
|
|
144
|
+
volume_size: nil,
|
|
145
|
+
test_after_create: false,
|
|
146
|
+
}
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Calculate CRC16-CCITT for block headers
|
|
150
|
+
#
|
|
151
|
+
# RAR uses CRC16-CCITT with polynomial 0x1021
|
|
152
|
+
# @param data [String] Header data to checksum
|
|
153
|
+
# @return [Integer] 16-bit CRC value
|
|
154
|
+
def calculate_header_crc16(data)
|
|
155
|
+
crc = 0
|
|
156
|
+
data.bytes.each do |byte|
|
|
157
|
+
crc ^= (byte << 8)
|
|
158
|
+
8.times do
|
|
159
|
+
crc = crc.anybits?(0x8000) ? ((crc << 1) ^ 0x1021) : (crc << 1)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
crc & 0xFFFF
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Convert Ruby Time to DOS time format
|
|
166
|
+
#
|
|
167
|
+
# @param time [Time] Ruby time object
|
|
168
|
+
# @return [Integer] DOS time format (32-bit)
|
|
169
|
+
def dos_time(time)
|
|
170
|
+
dos_date = ((time.year - 1980) << 9) | (time.month << 5) | time.day
|
|
171
|
+
dos_time_part = (time.hour << 11) | (time.min << 5) | (time.sec / 2)
|
|
172
|
+
(dos_date << 16) | dos_time_part
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Write RAR signature
|
|
176
|
+
#
|
|
177
|
+
# @param io [IO] Output stream
|
|
178
|
+
def write_signature(io)
|
|
179
|
+
io.write(RAR4_SIGNATURE.pack("C*"))
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Write marker block
|
|
183
|
+
#
|
|
184
|
+
# @param io [IO] Output stream
|
|
185
|
+
def write_marker_block(io)
|
|
186
|
+
# Marker block has fixed CRC of 0x6152
|
|
187
|
+
io.write([0x6152].pack("v")) # HEAD_CRC
|
|
188
|
+
io.write([BLOCK_MARKER].pack("C")) # HEAD_TYPE
|
|
189
|
+
io.write([0x0000].pack("v")) # HEAD_FLAGS
|
|
190
|
+
io.write([0x0007].pack("v")) # HEAD_SIZE
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Write archive header
|
|
194
|
+
#
|
|
195
|
+
# @param io [IO] Output stream
|
|
196
|
+
def write_archive_header(io)
|
|
197
|
+
flags = 0
|
|
198
|
+
flags |= ARCHIVE_SOLID if @options[:solid]
|
|
199
|
+
flags |= ARCHIVE_RECOVERY if @options[:recovery].positive?
|
|
200
|
+
flags |= ARCHIVE_ENCRYPTED if @options[:password]
|
|
201
|
+
flags |= ARCHIVE_VOLUME if @options[:volume_size]
|
|
202
|
+
|
|
203
|
+
# Build header data (without CRC)
|
|
204
|
+
# SIZE includes: TYPE(1) + FLAGS(2) + SIZE(2) + Reserved(6) = 11 bytes
|
|
205
|
+
header_data = [BLOCK_ARCHIVE].pack("C") +
|
|
206
|
+
[flags].pack("v") +
|
|
207
|
+
[0x000B].pack("v") +
|
|
208
|
+
[0, 0, 0].pack("vvv") # 6 bytes reserved (3 x uint16)
|
|
209
|
+
|
|
210
|
+
# Calculate and write CRC
|
|
211
|
+
crc = calculate_header_crc16(header_data)
|
|
212
|
+
io.write([crc].pack("v"))
|
|
213
|
+
io.write(header_data)
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Write file entries
|
|
217
|
+
#
|
|
218
|
+
# @param io [IO] Output stream
|
|
219
|
+
def write_file_entries(io)
|
|
220
|
+
@files.each do |file_info|
|
|
221
|
+
write_file_entry(io, file_info)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
@directories.each do |dir_info|
|
|
225
|
+
write_directory_entries(io, dir_info)
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# Write a single file entry
|
|
230
|
+
#
|
|
231
|
+
# @param io [IO] Output stream
|
|
232
|
+
# @param file_info [Hash] File information
|
|
233
|
+
def write_file_entry(io, file_info)
|
|
234
|
+
file_path = file_info[:source]
|
|
235
|
+
archive_path = file_info[:archive_path] || File.basename(file_path)
|
|
236
|
+
|
|
237
|
+
file_data = File.binread(file_path)
|
|
238
|
+
|
|
239
|
+
# Select appropriate compression method based on data
|
|
240
|
+
method = file_info[:method] || select_compression_method(file_data)
|
|
241
|
+
compressed_data = compress_data(file_data, method)
|
|
242
|
+
|
|
243
|
+
# File metadata
|
|
244
|
+
stat = File.stat(file_path)
|
|
245
|
+
file_attr = stat.mode
|
|
246
|
+
file_time = dos_time(stat.mtime)
|
|
247
|
+
data_crc = Zlib.crc32(file_data)
|
|
248
|
+
|
|
249
|
+
# Name encoding
|
|
250
|
+
name_bytes = archive_path.encode("UTF-8").bytes
|
|
251
|
+
|
|
252
|
+
# Flags
|
|
253
|
+
flags = FILE_UNICODE
|
|
254
|
+
flags |= FILE_ENCRYPTED if @options[:password]
|
|
255
|
+
flags |= FILE_LARGE if file_data.size > 0xFFFFFFFF
|
|
256
|
+
|
|
257
|
+
# Calculate header size
|
|
258
|
+
# Fixed fields: TYPE(1) + FLAGS(2) + SIZE(2) + PACK_SIZE(4) + UNPACK_SIZE(4) +
|
|
259
|
+
# HOST_OS(1) + FILE_CRC(4) + FILE_TIME(4) + VERSION(1) + METHOD(1) +
|
|
260
|
+
# NAME_SIZE(2) + ATTR(4) = 30 bytes
|
|
261
|
+
header_size = 30 + name_bytes.size
|
|
262
|
+
header_size += 8 if flags & FILE_LARGE != 0
|
|
263
|
+
|
|
264
|
+
# Build file header (without CRC)
|
|
265
|
+
header_data = [BLOCK_FILE].pack("C") +
|
|
266
|
+
[flags].pack("v") +
|
|
267
|
+
[header_size].pack("v") +
|
|
268
|
+
[compressed_data.bytesize & 0xFFFFFFFF].pack("V") +
|
|
269
|
+
[file_data.bytesize & 0xFFFFFFFF].pack("V") +
|
|
270
|
+
[OS_UNIX].pack("C") +
|
|
271
|
+
[data_crc].pack("V") +
|
|
272
|
+
[file_time].pack("V") +
|
|
273
|
+
[compression_version(method)].pack("C") + # VERSION first
|
|
274
|
+
[method].pack("C") + # METHOD second
|
|
275
|
+
[name_bytes.size].pack("v") +
|
|
276
|
+
[file_attr].pack("V")
|
|
277
|
+
|
|
278
|
+
# Add high 32 bits for large files
|
|
279
|
+
if flags & FILE_LARGE != 0
|
|
280
|
+
header_data += [(compressed_data.bytesize >> 32) & 0xFFFFFFFF].pack("V")
|
|
281
|
+
header_data += [(file_data.bytesize >> 32) & 0xFFFFFFFF].pack("V")
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
# Add filename
|
|
285
|
+
header_data += name_bytes.pack("C*")
|
|
286
|
+
|
|
287
|
+
# Calculate and write CRC
|
|
288
|
+
crc = calculate_header_crc16(header_data)
|
|
289
|
+
io.write([crc].pack("v"))
|
|
290
|
+
io.write(header_data)
|
|
291
|
+
io.write(compressed_data)
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
# Write directory entries
|
|
295
|
+
#
|
|
296
|
+
# @param io [IO] Output stream
|
|
297
|
+
# @param dir_info [Hash] Directory information
|
|
298
|
+
def write_directory_entries(io, dir_info)
|
|
299
|
+
dir_path = dir_info[:source]
|
|
300
|
+
recursive = dir_info[:recursive]
|
|
301
|
+
|
|
302
|
+
pattern = recursive ? "**/*" : "*"
|
|
303
|
+
Dir.glob(File.join(dir_path, pattern)).each do |path|
|
|
304
|
+
next unless File.file?(path)
|
|
305
|
+
|
|
306
|
+
relative_path = path.sub("#{dir_path}/", "")
|
|
307
|
+
write_file_entry(io, {
|
|
308
|
+
source: path,
|
|
309
|
+
archive_path: relative_path,
|
|
310
|
+
})
|
|
311
|
+
end
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
# Write end block
|
|
315
|
+
#
|
|
316
|
+
# @param io [IO] Output stream
|
|
317
|
+
def write_end_block(io)
|
|
318
|
+
flags = 0x4000 # ENDARC flag
|
|
319
|
+
|
|
320
|
+
# Build header data (without CRC)
|
|
321
|
+
header_data = [BLOCK_ENDARC].pack("C") +
|
|
322
|
+
[flags].pack("v") +
|
|
323
|
+
[0x0007].pack("v")
|
|
324
|
+
|
|
325
|
+
# Calculate and write CRC
|
|
326
|
+
crc = calculate_header_crc16(header_data)
|
|
327
|
+
io.write([crc].pack("v"))
|
|
328
|
+
io.write(header_data)
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
# Compress data using native RAR compression
|
|
332
|
+
#
|
|
333
|
+
# @param data [String] Data to compress
|
|
334
|
+
# @param method [Integer] RAR compression method (default: METHOD_NORMAL)
|
|
335
|
+
# @return [String] Compressed data
|
|
336
|
+
def compress_data(data, method = METHOD_NORMAL)
|
|
337
|
+
input = StringIO.new(data)
|
|
338
|
+
output = StringIO.new
|
|
339
|
+
|
|
340
|
+
# Use native compression dispatcher
|
|
341
|
+
Compression::Dispatcher.compress(method, input, output, @options)
|
|
342
|
+
|
|
343
|
+
output.string
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
# Select appropriate compression method based on data
|
|
347
|
+
#
|
|
348
|
+
# @param data [String] Data to compress
|
|
349
|
+
# @return [Integer] RAR compression method code
|
|
350
|
+
def select_compression_method(data)
|
|
351
|
+
# Small files: use METHOD_STORE to avoid Huffman tree overhead (258 bytes)
|
|
352
|
+
return METHOD_STORE if data.size < 300
|
|
353
|
+
|
|
354
|
+
# User-specified compression level takes precedence
|
|
355
|
+
case @options[:compression]
|
|
356
|
+
when :store
|
|
357
|
+
METHOD_STORE
|
|
358
|
+
when :fastest
|
|
359
|
+
METHOD_FASTEST
|
|
360
|
+
when :fast
|
|
361
|
+
METHOD_FAST
|
|
362
|
+
when :normal
|
|
363
|
+
METHOD_NORMAL
|
|
364
|
+
when :good
|
|
365
|
+
METHOD_GOOD
|
|
366
|
+
when :best
|
|
367
|
+
METHOD_BEST
|
|
368
|
+
else
|
|
369
|
+
# Default based on compression level option if numeric
|
|
370
|
+
if @options[:level] == 9
|
|
371
|
+
METHOD_BEST # PPMd for maximum compression
|
|
372
|
+
else
|
|
373
|
+
METHOD_NORMAL # LZ77+Huffman default
|
|
374
|
+
end
|
|
375
|
+
end
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
# Get Zlib compression level (DEPRECATED - kept for compatibility)
|
|
379
|
+
#
|
|
380
|
+
# @return [Integer] Zlib compression level
|
|
381
|
+
def compression_zlib_level
|
|
382
|
+
case @options[:compression]
|
|
383
|
+
when :store then Zlib::NO_COMPRESSION
|
|
384
|
+
when :fastest then Zlib::BEST_SPEED
|
|
385
|
+
when :fast then 3
|
|
386
|
+
when :normal then Zlib::DEFAULT_COMPRESSION
|
|
387
|
+
when :good then 7
|
|
388
|
+
when :best then Zlib::BEST_COMPRESSION
|
|
389
|
+
else Zlib::DEFAULT_COMPRESSION
|
|
390
|
+
end
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
# Get compression method code (DEPRECATED - use select_compression_method)
|
|
394
|
+
#
|
|
395
|
+
# @return [Integer] Method code
|
|
396
|
+
def compression_method
|
|
397
|
+
case @options[:compression]
|
|
398
|
+
when :store then METHOD_STORE
|
|
399
|
+
when :fastest then METHOD_FASTEST
|
|
400
|
+
when :fast then METHOD_FAST
|
|
401
|
+
when :normal then METHOD_NORMAL
|
|
402
|
+
when :good then METHOD_GOOD
|
|
403
|
+
when :best then METHOD_BEST
|
|
404
|
+
else METHOD_NORMAL
|
|
405
|
+
end
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
# Get compression version based on method
|
|
409
|
+
#
|
|
410
|
+
# @param method [Integer] Compression method
|
|
411
|
+
# @return [Integer] Version code
|
|
412
|
+
def compression_version(method)
|
|
413
|
+
method == METHOD_STORE ? 20 : 29
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
# Test archive integrity
|
|
417
|
+
#
|
|
418
|
+
# @return [Boolean] true if valid
|
|
419
|
+
def test_archive
|
|
420
|
+
# Basic validation: check if file exists and has RAR signature
|
|
421
|
+
return false unless File.exist?(@output_path)
|
|
422
|
+
|
|
423
|
+
File.open(@output_path, "rb") do |io|
|
|
424
|
+
signature = io.read(7)
|
|
425
|
+
signature&.bytes == RAR4_SIGNATURE[0..6]
|
|
426
|
+
end
|
|
427
|
+
end
|
|
428
|
+
end
|
|
429
|
+
end
|
|
430
|
+
end
|
|
431
|
+
end
|