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,195 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "recovery_record"
|
|
4
|
+
require_relative "parity_handler"
|
|
5
|
+
|
|
6
|
+
module Omnizip
|
|
7
|
+
module Formats
|
|
8
|
+
module Rar
|
|
9
|
+
# RAR archive verification
|
|
10
|
+
# Verifies archive integrity using CRCs and recovery records
|
|
11
|
+
class ArchiveVerifier
|
|
12
|
+
attr_reader :archive_path, :recovery_record, :parity_handler
|
|
13
|
+
|
|
14
|
+
# Verification result
|
|
15
|
+
class VerificationResult
|
|
16
|
+
attr_accessor :valid, :files_total, :files_ok, :files_corrupted,
|
|
17
|
+
:corrupted_files, :recoverable, :corrupt_blocks,
|
|
18
|
+
:recovery_available, :errors
|
|
19
|
+
|
|
20
|
+
def initialize
|
|
21
|
+
@valid = true
|
|
22
|
+
@files_total = 0
|
|
23
|
+
@files_ok = 0
|
|
24
|
+
@files_corrupted = 0
|
|
25
|
+
@corrupted_files = []
|
|
26
|
+
@recoverable = false
|
|
27
|
+
@corrupt_blocks = []
|
|
28
|
+
@recovery_available = false
|
|
29
|
+
@errors = []
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Check if archive is valid
|
|
33
|
+
#
|
|
34
|
+
# @return [Boolean] true if valid
|
|
35
|
+
def valid?
|
|
36
|
+
@valid && @files_corrupted.zero?
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Check if corruption can be repaired
|
|
40
|
+
#
|
|
41
|
+
# @return [Boolean] true if repairable
|
|
42
|
+
def can_repair?
|
|
43
|
+
@recovery_available && @recoverable
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Get summary string
|
|
47
|
+
#
|
|
48
|
+
# @return [String] Verification summary
|
|
49
|
+
def summary
|
|
50
|
+
if valid?
|
|
51
|
+
"Archive OK: #{@files_total} files verified"
|
|
52
|
+
else
|
|
53
|
+
msg = "Archive corrupted: #{@files_corrupted}/#{@files_total} files damaged"
|
|
54
|
+
msg += " (repairable)" if can_repair?
|
|
55
|
+
msg
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Initialize verifier
|
|
61
|
+
#
|
|
62
|
+
# @param archive_path [String] Path to RAR archive
|
|
63
|
+
def initialize(archive_path)
|
|
64
|
+
@archive_path = archive_path
|
|
65
|
+
@recovery_record = nil
|
|
66
|
+
@parity_handler = nil
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Verify archive integrity
|
|
70
|
+
#
|
|
71
|
+
# @param use_recovery [Boolean] Use recovery records for verification
|
|
72
|
+
# @param verbose [Boolean] Enable verbose output
|
|
73
|
+
# @return [VerificationResult] Verification results
|
|
74
|
+
def verify(use_recovery: true, verbose: false)
|
|
75
|
+
result = VerificationResult.new
|
|
76
|
+
|
|
77
|
+
begin
|
|
78
|
+
# Open and parse archive
|
|
79
|
+
reader = Reader.new(@archive_path)
|
|
80
|
+
reader.open
|
|
81
|
+
|
|
82
|
+
result.files_total = reader.entries.size
|
|
83
|
+
|
|
84
|
+
# Detect recovery records
|
|
85
|
+
detect_recovery_records(reader, result) if use_recovery
|
|
86
|
+
|
|
87
|
+
# Verify each file
|
|
88
|
+
reader.entries.each do |entry|
|
|
89
|
+
file_valid = verify_entry(entry, verbose)
|
|
90
|
+
|
|
91
|
+
if file_valid
|
|
92
|
+
result.files_ok += 1
|
|
93
|
+
else
|
|
94
|
+
result.files_corrupted += 1
|
|
95
|
+
result.corrupted_files << entry.name
|
|
96
|
+
result.valid = false
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Check if corruption is recoverable
|
|
101
|
+
if result.files_corrupted.positive? && result.recovery_available
|
|
102
|
+
result.recoverable = check_recoverability(result.corrupt_blocks)
|
|
103
|
+
end
|
|
104
|
+
rescue StandardError => e
|
|
105
|
+
result.valid = false
|
|
106
|
+
result.errors << "Verification failed: #{e.message}"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
result
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Verify single file entry
|
|
113
|
+
#
|
|
114
|
+
# @param entry [Models::RarEntry] Entry to verify
|
|
115
|
+
# @param verbose [Boolean] Print verbose output
|
|
116
|
+
# @return [Boolean] true if valid
|
|
117
|
+
def verify_entry(entry, verbose = false)
|
|
118
|
+
return true if entry.directory?
|
|
119
|
+
|
|
120
|
+
# For now, we rely on the decompressor to verify
|
|
121
|
+
# In a full implementation, we would check CRC32
|
|
122
|
+
valid = verify_entry_crc(entry)
|
|
123
|
+
|
|
124
|
+
puts "#{entry.name}: #{valid ? 'OK' : 'FAILED'}" if verbose
|
|
125
|
+
|
|
126
|
+
valid
|
|
127
|
+
rescue StandardError => e
|
|
128
|
+
puts "#{entry.name}: ERROR (#{e.message})" if verbose
|
|
129
|
+
false
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Quick test of archive
|
|
133
|
+
#
|
|
134
|
+
# @return [Boolean] true if archive can be opened
|
|
135
|
+
def quick_test
|
|
136
|
+
Reader.new(@archive_path).open
|
|
137
|
+
true
|
|
138
|
+
rescue StandardError
|
|
139
|
+
false
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
private
|
|
143
|
+
|
|
144
|
+
# Detect recovery records in archive
|
|
145
|
+
#
|
|
146
|
+
# @param reader [Reader] Archive reader
|
|
147
|
+
# @param result [VerificationResult] Result object to update
|
|
148
|
+
def detect_recovery_records(reader, result)
|
|
149
|
+
version = reader.header&.version || 4
|
|
150
|
+
|
|
151
|
+
@recovery_record = RecoveryRecord.new(version)
|
|
152
|
+
|
|
153
|
+
# Check for integrated recovery records
|
|
154
|
+
File.open(@archive_path, "rb") do |io|
|
|
155
|
+
@recovery_record.parse_from_archive(io, reader.archive_info.flags)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Check for external .rev files
|
|
159
|
+
rev_files = @recovery_record.detect_external_files(@archive_path)
|
|
160
|
+
@recovery_record.load_external_files(rev_files) if rev_files.any?
|
|
161
|
+
|
|
162
|
+
result.recovery_available = @recovery_record.available?
|
|
163
|
+
|
|
164
|
+
# Initialize parity handler if recovery available
|
|
165
|
+
return unless result.recovery_available
|
|
166
|
+
|
|
167
|
+
@parity_handler = ParityHandler.new(@recovery_record)
|
|
168
|
+
@parity_handler.load_parity_data(@recovery_record.external_files)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Verify entry CRC
|
|
172
|
+
#
|
|
173
|
+
# @param entry [Models::RarEntry] Entry to verify
|
|
174
|
+
# @return [Boolean] true if CRC matches
|
|
175
|
+
def verify_entry_crc(_entry)
|
|
176
|
+
# This is a placeholder - actual CRC verification would require
|
|
177
|
+
# extracting and checking the file
|
|
178
|
+
# For now, assume OK unless we have specific corruption data
|
|
179
|
+
true
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Check if corruption is recoverable
|
|
183
|
+
#
|
|
184
|
+
# @param corrupt_blocks [Array<Integer>] Corrupted block indices
|
|
185
|
+
# @return [Boolean] true if recoverable
|
|
186
|
+
def check_recoverability(corrupt_blocks)
|
|
187
|
+
return false unless @parity_handler
|
|
188
|
+
|
|
189
|
+
# Check if all corrupted blocks can be recovered
|
|
190
|
+
corrupt_blocks.all? { |idx| @parity_handler.can_recover?(idx) }
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "constants"
|
|
4
|
+
require_relative "models/rar_entry"
|
|
5
|
+
|
|
6
|
+
module Omnizip
|
|
7
|
+
module Formats
|
|
8
|
+
module Rar
|
|
9
|
+
# RAR block parser
|
|
10
|
+
# Parses different block types in RAR archives
|
|
11
|
+
class BlockParser
|
|
12
|
+
include Constants
|
|
13
|
+
|
|
14
|
+
attr_reader :version
|
|
15
|
+
|
|
16
|
+
# Initialize block parser
|
|
17
|
+
#
|
|
18
|
+
# @param version [Integer] RAR version (4 or 5)
|
|
19
|
+
def initialize(version)
|
|
20
|
+
@version = version
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Parse file block and create entry
|
|
24
|
+
#
|
|
25
|
+
# @param io [IO] Input stream
|
|
26
|
+
# @return [Models::RarEntry, nil] Parsed entry or nil
|
|
27
|
+
def parse_file_block(io)
|
|
28
|
+
if @version == 5
|
|
29
|
+
parse_rar5_file_block(io)
|
|
30
|
+
else
|
|
31
|
+
parse_rar4_file_block(io)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Skip to next block
|
|
36
|
+
#
|
|
37
|
+
# @param io [IO] Input stream
|
|
38
|
+
# @param block_size [Integer] Block size to skip
|
|
39
|
+
def skip_block(io, block_size)
|
|
40
|
+
io.read(block_size) if block_size.positive?
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
# Parse RAR4 file block
|
|
46
|
+
#
|
|
47
|
+
# @param io [IO] Input stream
|
|
48
|
+
# @return [Models::RarEntry, nil] Parsed entry or nil
|
|
49
|
+
def parse_rar4_file_block(io)
|
|
50
|
+
entry = Models::RarEntry.new
|
|
51
|
+
|
|
52
|
+
# Read block header
|
|
53
|
+
read_uint16(io)
|
|
54
|
+
head_type = io.read(1)&.ord
|
|
55
|
+
return nil unless head_type == BLOCK_FILE
|
|
56
|
+
|
|
57
|
+
head_flags = read_uint16(io)
|
|
58
|
+
head_size = read_uint16(io)
|
|
59
|
+
|
|
60
|
+
# Read file header data
|
|
61
|
+
pack_size = read_uint32(io)
|
|
62
|
+
unpack_size = read_uint32(io)
|
|
63
|
+
host_os = io.read(1)&.ord
|
|
64
|
+
file_crc = read_uint32(io)
|
|
65
|
+
file_time = read_uint32(io)
|
|
66
|
+
unpack_ver = io.read(1)&.ord
|
|
67
|
+
method = io.read(1)&.ord
|
|
68
|
+
name_size = read_uint16(io)
|
|
69
|
+
attr = read_uint32(io)
|
|
70
|
+
|
|
71
|
+
# Read extended sizes if large file
|
|
72
|
+
if head_flags.anybits?(FILE_LARGE)
|
|
73
|
+
high_pack_size = read_uint32(io)
|
|
74
|
+
high_unpack_size = read_uint32(io)
|
|
75
|
+
pack_size |= (high_pack_size << 32)
|
|
76
|
+
unpack_size |= (high_unpack_size << 32)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Read file name
|
|
80
|
+
name_bytes = io.read(name_size)
|
|
81
|
+
entry.name = decode_filename(name_bytes, head_flags)
|
|
82
|
+
|
|
83
|
+
# Set entry properties
|
|
84
|
+
entry.size = unpack_size
|
|
85
|
+
entry.compressed_size = pack_size
|
|
86
|
+
entry.crc = file_crc
|
|
87
|
+
entry.host_os = host_os
|
|
88
|
+
entry.method = method
|
|
89
|
+
entry.version = unpack_ver
|
|
90
|
+
entry.flags = head_flags
|
|
91
|
+
entry.attributes = attr
|
|
92
|
+
entry.mtime = dos_time_to_time(file_time)
|
|
93
|
+
|
|
94
|
+
# Set flags
|
|
95
|
+
entry.is_dir = head_flags.anybits?(FILE_DIRECTORY)
|
|
96
|
+
entry.encrypted = head_flags.anybits?(FILE_ENCRYPTED)
|
|
97
|
+
entry.split_before = head_flags.anybits?(FILE_SPLIT_BEFORE)
|
|
98
|
+
entry.split_after = head_flags.anybits?(FILE_SPLIT_AFTER)
|
|
99
|
+
|
|
100
|
+
# Skip remaining header data and file data
|
|
101
|
+
# Fixed fields: TYPE(1) + FLAGS(2) + SIZE(2) + PACK_SIZE(4) + UNPACK_SIZE(4) +
|
|
102
|
+
# HOST_OS(1) + FILE_CRC(4) + FILE_TIME(4) + VERSION(1) + METHOD(1) +
|
|
103
|
+
# NAME_SIZE(2) + ATTR(4) = 30 bytes
|
|
104
|
+
remaining = head_size - (name_size + 30)
|
|
105
|
+
remaining += 8 if head_flags.anybits?(FILE_LARGE)
|
|
106
|
+
io.read(remaining) if remaining.positive?
|
|
107
|
+
io.read(pack_size) # Skip compressed data
|
|
108
|
+
|
|
109
|
+
entry
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Parse RAR5 file block
|
|
113
|
+
#
|
|
114
|
+
# @param io [IO] Input stream
|
|
115
|
+
# @return [Models::RarEntry, nil] Parsed entry or nil
|
|
116
|
+
def parse_rar5_file_block(io)
|
|
117
|
+
entry = Models::RarEntry.new
|
|
118
|
+
|
|
119
|
+
# Read block header
|
|
120
|
+
read_uint32(io)
|
|
121
|
+
read_vint(io)
|
|
122
|
+
header_type = read_vint(io)
|
|
123
|
+
return nil unless header_type == RAR5_HEADER_FILE
|
|
124
|
+
|
|
125
|
+
header_flags = read_vint(io)
|
|
126
|
+
|
|
127
|
+
# Read file header
|
|
128
|
+
file_flags = read_vint(io)
|
|
129
|
+
unpack_size = read_vint(io)
|
|
130
|
+
attr = read_vint(io)
|
|
131
|
+
|
|
132
|
+
# Read modification time if present
|
|
133
|
+
mtime = nil
|
|
134
|
+
mtime = read_uint32(io) if file_flags.anybits?(0x02)
|
|
135
|
+
|
|
136
|
+
# Read CRC if present
|
|
137
|
+
crc = nil
|
|
138
|
+
crc = read_uint32(io) if file_flags.anybits?(0x04)
|
|
139
|
+
|
|
140
|
+
# Read compression info
|
|
141
|
+
read_vint(io)
|
|
142
|
+
host_os = read_vint(io)
|
|
143
|
+
name_length = read_vint(io)
|
|
144
|
+
|
|
145
|
+
# Read file name
|
|
146
|
+
name_bytes = io.read(name_length)
|
|
147
|
+
entry.name = name_bytes.force_encoding("UTF-8")
|
|
148
|
+
|
|
149
|
+
# Set entry properties
|
|
150
|
+
entry.size = unpack_size
|
|
151
|
+
entry.compressed_size = 0 # Not directly available in header
|
|
152
|
+
entry.crc = crc
|
|
153
|
+
entry.host_os = host_os
|
|
154
|
+
entry.flags = file_flags
|
|
155
|
+
entry.attributes = attr
|
|
156
|
+
entry.mtime = Time.at(mtime) if mtime
|
|
157
|
+
entry.is_dir = file_flags.anybits?(RAR5_FLAG_IS_DIR)
|
|
158
|
+
entry.version = 5
|
|
159
|
+
|
|
160
|
+
# Read extra area if present
|
|
161
|
+
if header_flags.anybits?(RAR5_FLAG_EXTRA_AREA)
|
|
162
|
+
extra_size = read_vint(io)
|
|
163
|
+
io.read(extra_size) if extra_size.positive?
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Read data area if present
|
|
167
|
+
if header_flags.anybits?(RAR5_FLAG_DATA_AREA)
|
|
168
|
+
data_size = read_vint(io)
|
|
169
|
+
io.read(data_size) if data_size.positive?
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
entry
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Decode filename from bytes
|
|
176
|
+
#
|
|
177
|
+
# @param bytes [String] Raw filename bytes
|
|
178
|
+
# @param flags [Integer] Block flags
|
|
179
|
+
# @return [String] Decoded filename
|
|
180
|
+
def decode_filename(bytes, flags)
|
|
181
|
+
if flags.nobits?(FILE_UNICODE)
|
|
182
|
+
# ASCII filename
|
|
183
|
+
bytes.force_encoding("ASCII-8BIT")
|
|
184
|
+
else
|
|
185
|
+
# Unicode filename
|
|
186
|
+
bytes.force_encoding("UTF-8")
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Convert DOS time to Ruby Time
|
|
191
|
+
#
|
|
192
|
+
# @param dos_time [Integer] DOS time value
|
|
193
|
+
# @return [Time] Ruby time object
|
|
194
|
+
def dos_time_to_time(dos_time)
|
|
195
|
+
sec = (dos_time & 0x1F) * 2
|
|
196
|
+
min = (dos_time >> 5) & 0x3F
|
|
197
|
+
hour = (dos_time >> 11) & 0x1F
|
|
198
|
+
day = (dos_time >> 16) & 0x1F
|
|
199
|
+
month = (dos_time >> 21) & 0x0F
|
|
200
|
+
year = ((dos_time >> 25) & 0x7F) + 1980
|
|
201
|
+
|
|
202
|
+
Time.new(year, month, day, hour, min, sec)
|
|
203
|
+
rescue ArgumentError
|
|
204
|
+
Time.now
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Read 16-bit unsigned integer (little-endian)
|
|
208
|
+
def read_uint16(io)
|
|
209
|
+
bytes = io.read(2)
|
|
210
|
+
return 0 unless bytes&.size == 2
|
|
211
|
+
|
|
212
|
+
bytes.unpack1("v")
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# Read 32-bit unsigned integer (little-endian)
|
|
216
|
+
def read_uint32(io)
|
|
217
|
+
bytes = io.read(4)
|
|
218
|
+
return 0 unless bytes&.size == 4
|
|
219
|
+
|
|
220
|
+
bytes.unpack1("V")
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# Read variable-length integer (RAR5)
|
|
224
|
+
def read_vint(io)
|
|
225
|
+
result = 0
|
|
226
|
+
shift = 0
|
|
227
|
+
|
|
228
|
+
loop do
|
|
229
|
+
byte = io.read(1)&.ord
|
|
230
|
+
return result unless byte
|
|
231
|
+
|
|
232
|
+
result |= (byte & 0x7F) << shift
|
|
233
|
+
break if byte.nobits?(0x80)
|
|
234
|
+
|
|
235
|
+
shift += 7
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
result
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
end
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright (C) 2025 Ribose Inc.
|
|
4
|
+
#
|
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a
|
|
6
|
+
# copy of this software and associated documentation files (the "Software"),
|
|
7
|
+
# to deal in the Software without restriction, including without limitation
|
|
8
|
+
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
9
|
+
# and/or sell copies of the Software, and to permit persons to whom the
|
|
10
|
+
# Software is furnished to do so, subject to the following conditions:
|
|
11
|
+
#
|
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
|
13
|
+
# all copies or substantial portions of the Software.
|
|
14
|
+
#
|
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
20
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
21
|
+
# DEALINGS IN THE SOFTWARE.
|
|
22
|
+
|
|
23
|
+
module Omnizip
|
|
24
|
+
module Formats
|
|
25
|
+
module Rar
|
|
26
|
+
module Compression
|
|
27
|
+
# Bit-level I/O stream for RAR compression algorithms
|
|
28
|
+
#
|
|
29
|
+
# Provides methods to read and write individual bits from byte streams.
|
|
30
|
+
# This is a shared utility used by PPMd, LZ77+Huffman, and other RAR
|
|
31
|
+
# compression algorithms that need bit-level access.
|
|
32
|
+
#
|
|
33
|
+
# Responsibilities:
|
|
34
|
+
# - ONE responsibility: Bit-level I/O operations
|
|
35
|
+
# - Read bits from byte stream
|
|
36
|
+
# - Write bits to byte stream
|
|
37
|
+
# - Manage bit buffer and byte alignment
|
|
38
|
+
class BitStream
|
|
39
|
+
# Initialize a new bit stream
|
|
40
|
+
#
|
|
41
|
+
# @param io [IO] The underlying byte stream
|
|
42
|
+
# @param mode [Symbol] :read or :write
|
|
43
|
+
def initialize(io, mode = :read)
|
|
44
|
+
@io = io
|
|
45
|
+
@mode = mode
|
|
46
|
+
@buffer = 0
|
|
47
|
+
@bits_in_buffer = 0
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Read specified number of bits
|
|
51
|
+
#
|
|
52
|
+
# @param count [Integer] Number of bits to read (1-32)
|
|
53
|
+
# @return [Integer] The bits read as an integer
|
|
54
|
+
def read_bits(count)
|
|
55
|
+
unless @mode == :read
|
|
56
|
+
raise ArgumentError,
|
|
57
|
+
"Can only read in read mode"
|
|
58
|
+
end
|
|
59
|
+
raise ArgumentError, "Count must be 1-32" unless count.between?(1,
|
|
60
|
+
32)
|
|
61
|
+
|
|
62
|
+
result = 0
|
|
63
|
+
|
|
64
|
+
count.times do
|
|
65
|
+
result = (result << 1) | read_bit
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
result
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Read a single bit
|
|
72
|
+
#
|
|
73
|
+
# @return [Integer] 0 or 1
|
|
74
|
+
def read_bit
|
|
75
|
+
unless @mode == :read
|
|
76
|
+
raise ArgumentError,
|
|
77
|
+
"Can only read in read mode"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
if @bits_in_buffer.zero?
|
|
81
|
+
fill_buffer
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
@bits_in_buffer -= 1
|
|
85
|
+
(@buffer >> @bits_in_buffer) & 1
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Write specified number of bits
|
|
89
|
+
#
|
|
90
|
+
# @param value [Integer] The value to write
|
|
91
|
+
# @param count [Integer] Number of bits to write (1-32)
|
|
92
|
+
# @return [void]
|
|
93
|
+
def write_bits(value, count)
|
|
94
|
+
unless @mode == :write
|
|
95
|
+
raise ArgumentError,
|
|
96
|
+
"Can only write in write mode"
|
|
97
|
+
end
|
|
98
|
+
raise ArgumentError, "Count must be 1-32" unless count.between?(1,
|
|
99
|
+
32)
|
|
100
|
+
|
|
101
|
+
count.times do |i|
|
|
102
|
+
bit = (value >> (count - 1 - i)) & 1
|
|
103
|
+
write_bit(bit)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Write a single bit
|
|
108
|
+
#
|
|
109
|
+
# @param bit [Integer] 0 or 1
|
|
110
|
+
# @return [void]
|
|
111
|
+
def write_bit(bit)
|
|
112
|
+
unless @mode == :write
|
|
113
|
+
raise ArgumentError,
|
|
114
|
+
"Can only write in write mode"
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
@buffer = (@buffer << 1) | (bit & 1)
|
|
118
|
+
@bits_in_buffer += 1
|
|
119
|
+
|
|
120
|
+
flush_buffer if @bits_in_buffer == 8
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Align to byte boundary (read mode)
|
|
124
|
+
#
|
|
125
|
+
# @return [void]
|
|
126
|
+
def align_to_byte
|
|
127
|
+
return unless @mode == :read
|
|
128
|
+
|
|
129
|
+
@bits_in_buffer = 0
|
|
130
|
+
@buffer = 0
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Flush any remaining bits (write mode)
|
|
134
|
+
#
|
|
135
|
+
# @return [void]
|
|
136
|
+
def flush
|
|
137
|
+
return unless @mode == :write
|
|
138
|
+
return if @bits_in_buffer.zero?
|
|
139
|
+
|
|
140
|
+
# Pad with zeros to complete byte
|
|
141
|
+
padding = 8 - @bits_in_buffer
|
|
142
|
+
@buffer <<= padding
|
|
143
|
+
@io.write([@buffer].pack("C"))
|
|
144
|
+
@buffer = 0
|
|
145
|
+
@bits_in_buffer = 0
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Check if at end of stream
|
|
149
|
+
#
|
|
150
|
+
# @return [Boolean] True if no more data available
|
|
151
|
+
def eof?
|
|
152
|
+
@mode == :read && @bits_in_buffer.zero? && @io.eof?
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
private
|
|
156
|
+
|
|
157
|
+
# Fill buffer with next byte from stream
|
|
158
|
+
#
|
|
159
|
+
# @return [void]
|
|
160
|
+
def fill_buffer
|
|
161
|
+
byte = @io.read(1)
|
|
162
|
+
raise EOFError, "Unexpected end of stream" if byte.nil?
|
|
163
|
+
|
|
164
|
+
@buffer = byte.unpack1("C")
|
|
165
|
+
@bits_in_buffer = 8
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Flush full buffer byte to stream
|
|
169
|
+
#
|
|
170
|
+
# @return [void]
|
|
171
|
+
def flush_buffer
|
|
172
|
+
@io.write([@buffer].pack("C"))
|
|
173
|
+
@buffer = 0
|
|
174
|
+
@bits_in_buffer = 0
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|