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,297 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Omnizip
|
|
4
|
+
module Formats
|
|
5
|
+
module Iso
|
|
6
|
+
# ISO 9660 Directory Structure Builder
|
|
7
|
+
#
|
|
8
|
+
# Builds the hierarchical directory structure for an ISO image,
|
|
9
|
+
# allocating sectors for directories and files.
|
|
10
|
+
class DirectoryBuilder
|
|
11
|
+
# @return [Array<Hash>] Files to add
|
|
12
|
+
attr_reader :files
|
|
13
|
+
|
|
14
|
+
# @return [Array<Hash>] Directories to add
|
|
15
|
+
attr_reader :directories
|
|
16
|
+
|
|
17
|
+
# @return [Integer] ISO level
|
|
18
|
+
attr_reader :level
|
|
19
|
+
|
|
20
|
+
# @return [Boolean] Rock Ridge enabled
|
|
21
|
+
attr_reader :rock_ridge
|
|
22
|
+
|
|
23
|
+
# Initialize directory builder
|
|
24
|
+
#
|
|
25
|
+
# @param files [Array<Hash>] Files to include
|
|
26
|
+
# @param directories [Array<Hash>] Directories to include
|
|
27
|
+
# @param options [Hash] Builder options
|
|
28
|
+
def initialize(files, directories, options = {})
|
|
29
|
+
@files = files
|
|
30
|
+
@directories = directories
|
|
31
|
+
@level = options.fetch(:level, 2)
|
|
32
|
+
@rock_ridge = options.fetch(:rock_ridge, false)
|
|
33
|
+
@current_sector = 22 # After volume descriptors and path tables
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Build directory structure
|
|
37
|
+
#
|
|
38
|
+
# @return [Hash] Complete directory structure with allocations
|
|
39
|
+
def build
|
|
40
|
+
# Build directory tree
|
|
41
|
+
tree = build_directory_tree
|
|
42
|
+
|
|
43
|
+
# Allocate sectors for directories and files
|
|
44
|
+
allocate_sectors(tree)
|
|
45
|
+
|
|
46
|
+
# Build path table
|
|
47
|
+
path_table = build_path_table(tree)
|
|
48
|
+
|
|
49
|
+
{
|
|
50
|
+
root: tree,
|
|
51
|
+
directories: flatten_directories(tree),
|
|
52
|
+
files: @files,
|
|
53
|
+
path_table: path_table,
|
|
54
|
+
path_table_size: path_table.bytesize,
|
|
55
|
+
total_sectors: @current_sector,
|
|
56
|
+
}
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
# Build directory tree from files and directories
|
|
62
|
+
#
|
|
63
|
+
# @return [Hash] Root directory node
|
|
64
|
+
def build_directory_tree
|
|
65
|
+
root = {
|
|
66
|
+
name: "\x00", # Root directory identifier
|
|
67
|
+
iso_path: "",
|
|
68
|
+
children: [],
|
|
69
|
+
directory: true,
|
|
70
|
+
stat: nil,
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# Add all directories first
|
|
74
|
+
@directories.each do |dir_info|
|
|
75
|
+
add_to_tree(root, dir_info[:iso_path], dir_info)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Add all files
|
|
79
|
+
@files.each do |file_info|
|
|
80
|
+
add_to_tree(root, file_info[:iso_path], file_info)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
root
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Add entry to directory tree
|
|
87
|
+
#
|
|
88
|
+
# @param root [Hash] Root node
|
|
89
|
+
# @param path [String] Entry path
|
|
90
|
+
# @param info [Hash] Entry information
|
|
91
|
+
def add_to_tree(root, path, info)
|
|
92
|
+
parts = path.split("/")
|
|
93
|
+
current = root
|
|
94
|
+
|
|
95
|
+
# Navigate/create directory structure
|
|
96
|
+
parts[0...-1].each do |part|
|
|
97
|
+
# Ensure current node has children array (it should be a directory)
|
|
98
|
+
current[:children] ||= []
|
|
99
|
+
child = current[:children].find { |c| c[:name] == part }
|
|
100
|
+
|
|
101
|
+
unless child
|
|
102
|
+
child = {
|
|
103
|
+
name: part,
|
|
104
|
+
iso_path: [current[:iso_path], part].reject(&:empty?).join("/"),
|
|
105
|
+
children: [],
|
|
106
|
+
directory: true,
|
|
107
|
+
stat: nil,
|
|
108
|
+
}
|
|
109
|
+
current[:children] << child
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
current = child
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Add the actual entry
|
|
116
|
+
entry_name = parts.last
|
|
117
|
+
entry = {
|
|
118
|
+
name: entry_name,
|
|
119
|
+
iso_path: path,
|
|
120
|
+
children: info[:directory] ? [] : nil,
|
|
121
|
+
directory: info[:directory] || false,
|
|
122
|
+
stat: info[:stat],
|
|
123
|
+
source: info[:source],
|
|
124
|
+
size: info[:directory] ? 0 : File.size(info[:source]),
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
# Ensure current node has children array
|
|
128
|
+
current[:children] ||= []
|
|
129
|
+
current[:children] << entry
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Allocate sectors for all entries
|
|
133
|
+
#
|
|
134
|
+
# @param tree [Hash] Directory tree
|
|
135
|
+
def allocate_sectors(tree)
|
|
136
|
+
# Allocate for root directory
|
|
137
|
+
allocate_directory_sectors(tree)
|
|
138
|
+
|
|
139
|
+
# Recursively allocate for children
|
|
140
|
+
allocate_children_sectors(tree)
|
|
141
|
+
|
|
142
|
+
# Allocate for files
|
|
143
|
+
allocate_file_sectors
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Allocate sectors for a directory
|
|
147
|
+
#
|
|
148
|
+
# @param dir_node [Hash] Directory node
|
|
149
|
+
def allocate_directory_sectors(dir_node)
|
|
150
|
+
# Calculate directory size
|
|
151
|
+
dir_size = calculate_directory_size(dir_node)
|
|
152
|
+
sectors_needed = (dir_size.to_f / Iso::SECTOR_SIZE).ceil
|
|
153
|
+
|
|
154
|
+
# Allocate location
|
|
155
|
+
dir_node[:location] = @current_sector
|
|
156
|
+
dir_node[:size] = dir_size
|
|
157
|
+
|
|
158
|
+
@current_sector += sectors_needed
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Calculate directory data size
|
|
162
|
+
#
|
|
163
|
+
# @param dir_node [Hash] Directory node
|
|
164
|
+
# @return [Integer] Size in bytes
|
|
165
|
+
def calculate_directory_size(dir_node)
|
|
166
|
+
size = 0
|
|
167
|
+
|
|
168
|
+
# Size for "." entry (34 bytes minimum)
|
|
169
|
+
size += 34
|
|
170
|
+
|
|
171
|
+
# Size for ".." entry
|
|
172
|
+
size += 34
|
|
173
|
+
|
|
174
|
+
# Size for each child entry
|
|
175
|
+
dir_node[:children].each do |child|
|
|
176
|
+
name_len = child[:name].bytesize
|
|
177
|
+
padding = name_len.even? ? 1 : 0
|
|
178
|
+
entry_size = 33 + name_len + padding
|
|
179
|
+
|
|
180
|
+
# Add Rock Ridge System Use fields if enabled
|
|
181
|
+
entry_size += calculate_rock_ridge_size(child) if @rock_ridge
|
|
182
|
+
|
|
183
|
+
size += entry_size
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Round up to sector boundary
|
|
187
|
+
((size.to_f / Iso::SECTOR_SIZE).ceil * Iso::SECTOR_SIZE)
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Calculate Rock Ridge System Use field size
|
|
191
|
+
#
|
|
192
|
+
# @param entry [Hash] Entry information
|
|
193
|
+
# @return [Integer] Size in bytes
|
|
194
|
+
def calculate_rock_ridge_size(_entry)
|
|
195
|
+
# Basic Rock Ridge fields:
|
|
196
|
+
# - PX (POSIX attributes): 44 bytes
|
|
197
|
+
# - TF (timestamps): 26 bytes
|
|
198
|
+
# - NM (alternate name): variable
|
|
199
|
+
# For now, estimate 100 bytes
|
|
200
|
+
100
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# Allocate sectors for children recursively
|
|
204
|
+
#
|
|
205
|
+
# @param dir_node [Hash] Directory node
|
|
206
|
+
def allocate_children_sectors(dir_node)
|
|
207
|
+
dir_node[:children].each do |child|
|
|
208
|
+
next unless child[:directory]
|
|
209
|
+
|
|
210
|
+
allocate_directory_sectors(child)
|
|
211
|
+
allocate_children_sectors(child)
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# Allocate sectors for files
|
|
216
|
+
def allocate_file_sectors
|
|
217
|
+
@files.each do |file_info|
|
|
218
|
+
file_size = file_info[:stat].size
|
|
219
|
+
sectors_needed = (file_size.to_f / Iso::SECTOR_SIZE).ceil
|
|
220
|
+
|
|
221
|
+
file_info[:location] = @current_sector
|
|
222
|
+
file_info[:size] = file_size
|
|
223
|
+
|
|
224
|
+
@current_sector += sectors_needed
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# Build path table from directory tree
|
|
229
|
+
#
|
|
230
|
+
# @param tree [Hash] Directory tree
|
|
231
|
+
# @return [String] Path table data
|
|
232
|
+
def build_path_table(tree)
|
|
233
|
+
table = +""
|
|
234
|
+
directories = []
|
|
235
|
+
|
|
236
|
+
# Collect all directories in path table order
|
|
237
|
+
collect_directories_for_path_table(tree, directories)
|
|
238
|
+
|
|
239
|
+
# Build path table entries
|
|
240
|
+
directories.each_with_index do |dir, _idx|
|
|
241
|
+
name = dir[:name] == "\x00" ? "\x00" : dir[:name]
|
|
242
|
+
parent_idx = dir[:parent_idx] || 0
|
|
243
|
+
|
|
244
|
+
# Name length
|
|
245
|
+
table << [name.bytesize].pack("C")
|
|
246
|
+
|
|
247
|
+
# Extended attribute length
|
|
248
|
+
table << [0].pack("C")
|
|
249
|
+
|
|
250
|
+
# Location of extent
|
|
251
|
+
table << [dir[:location]].pack("V")
|
|
252
|
+
|
|
253
|
+
# Parent directory number (1-based)
|
|
254
|
+
table << [parent_idx + 1].pack("v")
|
|
255
|
+
|
|
256
|
+
# Directory name
|
|
257
|
+
table << name
|
|
258
|
+
|
|
259
|
+
# Pad to even length
|
|
260
|
+
table << "\x00" if name.bytesize.odd?
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
table
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# Collect directories in depth-first order for path table
|
|
267
|
+
#
|
|
268
|
+
# @param node [Hash] Current node
|
|
269
|
+
# @param list [Array<Hash>] Output list
|
|
270
|
+
# @param parent_idx [Integer, nil] Parent index
|
|
271
|
+
def collect_directories_for_path_table(node, list, parent_idx = nil)
|
|
272
|
+
current_idx = list.size
|
|
273
|
+
node[:parent_idx] = parent_idx
|
|
274
|
+
list << node
|
|
275
|
+
|
|
276
|
+
node[:children].select { |c| c[:directory] }.each do |child|
|
|
277
|
+
collect_directories_for_path_table(child, list, current_idx)
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# Flatten directory tree to array
|
|
282
|
+
#
|
|
283
|
+
# @param tree [Hash] Directory tree
|
|
284
|
+
# @return [Array<Hash>] Flat directory list
|
|
285
|
+
def flatten_directories(tree)
|
|
286
|
+
result = [tree]
|
|
287
|
+
|
|
288
|
+
tree[:children].select { |c| c[:directory] }.each do |child|
|
|
289
|
+
result.concat(flatten_directories(child))
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
result
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
end
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Omnizip
|
|
4
|
+
module Formats
|
|
5
|
+
module Iso
|
|
6
|
+
# ISO 9660 Directory Record
|
|
7
|
+
# Represents a file or directory entry
|
|
8
|
+
class DirectoryRecord
|
|
9
|
+
attr_reader :length, :extended_attr_length, :location, :data_length,
|
|
10
|
+
:recording_date, :flags, :file_unit_size, :interleave_gap_size,
|
|
11
|
+
:volume_sequence_number, :name, :system_use
|
|
12
|
+
|
|
13
|
+
# Parse directory record from binary data
|
|
14
|
+
#
|
|
15
|
+
# @param data [String] Binary record data
|
|
16
|
+
# @param offset [Integer] Offset in data to start parsing
|
|
17
|
+
# @return [DirectoryRecord] Parsed record
|
|
18
|
+
def self.parse(data, offset = 0)
|
|
19
|
+
new.tap { |record| record.parse(data, offset) }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Parse record data
|
|
23
|
+
#
|
|
24
|
+
# @param data [String] Binary data
|
|
25
|
+
# @param offset [Integer] Starting offset
|
|
26
|
+
def parse(data, offset = 0)
|
|
27
|
+
# Byte 0: Length of directory record
|
|
28
|
+
@length = data.getbyte(offset)
|
|
29
|
+
return if @length.zero? # Padding
|
|
30
|
+
|
|
31
|
+
# Byte 1: Extended attribute record length
|
|
32
|
+
@extended_attr_length = data.getbyte(offset + 1)
|
|
33
|
+
|
|
34
|
+
# Bytes 2-9: Location of extent (both-endian)
|
|
35
|
+
@location = data[offset + 2, 4].unpack1("V")
|
|
36
|
+
|
|
37
|
+
# Bytes 10-17: Data length (both-endian)
|
|
38
|
+
@data_length = data[offset + 10, 4].unpack1("V")
|
|
39
|
+
|
|
40
|
+
# Bytes 18-24: Recording date and time
|
|
41
|
+
@recording_date = parse_record_datetime(data[offset + 18, 7])
|
|
42
|
+
|
|
43
|
+
# Byte 25: File flags
|
|
44
|
+
@flags = data.getbyte(offset + 25)
|
|
45
|
+
|
|
46
|
+
# Byte 26: File unit size (for interleaved files)
|
|
47
|
+
@file_unit_size = data.getbyte(offset + 26)
|
|
48
|
+
|
|
49
|
+
# Byte 27: Interleave gap size
|
|
50
|
+
@interleave_gap_size = data.getbyte(offset + 27)
|
|
51
|
+
|
|
52
|
+
# Bytes 28-31: Volume sequence number (both-endian)
|
|
53
|
+
@volume_sequence_number = data[offset + 28, 2].unpack1("v")
|
|
54
|
+
|
|
55
|
+
# Byte 32: Length of file identifier
|
|
56
|
+
name_length = data.getbyte(offset + 32)
|
|
57
|
+
|
|
58
|
+
# Bytes 33+: File identifier
|
|
59
|
+
@name = data[offset + 33, name_length]
|
|
60
|
+
|
|
61
|
+
# Parse file identifier
|
|
62
|
+
parse_name
|
|
63
|
+
|
|
64
|
+
# System Use field (Rock Ridge extensions, etc.)
|
|
65
|
+
# Located after name and padding
|
|
66
|
+
su_offset = offset + 33 + name_length
|
|
67
|
+
su_offset += 1 if name_length.even? # Padding byte
|
|
68
|
+
|
|
69
|
+
return unless su_offset < offset + @length
|
|
70
|
+
|
|
71
|
+
@system_use = data[su_offset, offset + @length - su_offset]
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Check if entry is a directory
|
|
75
|
+
#
|
|
76
|
+
# @return [Boolean] true if directory
|
|
77
|
+
def directory?
|
|
78
|
+
@flags.anybits?(Iso::FLAG_DIRECTORY)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Check if entry is hidden
|
|
82
|
+
#
|
|
83
|
+
# @return [Boolean] true if hidden
|
|
84
|
+
def hidden?
|
|
85
|
+
@flags.anybits?(Iso::FLAG_HIDDEN)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Check if this is the current directory entry
|
|
89
|
+
#
|
|
90
|
+
# @return [Boolean] true if current directory
|
|
91
|
+
def current_directory?
|
|
92
|
+
@name == "\x00"
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Check if this is the parent directory entry
|
|
96
|
+
#
|
|
97
|
+
# @return [Boolean] true if parent directory
|
|
98
|
+
def parent_directory?
|
|
99
|
+
@name == "\x01"
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Get file size
|
|
103
|
+
#
|
|
104
|
+
# @return [Integer] Size in bytes
|
|
105
|
+
def size
|
|
106
|
+
@data_length
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Get modification time
|
|
110
|
+
#
|
|
111
|
+
# @return [Time, nil] Modification time
|
|
112
|
+
def mtime
|
|
113
|
+
@recording_date
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
private
|
|
117
|
+
|
|
118
|
+
# Parse directory record datetime (7-byte format)
|
|
119
|
+
#
|
|
120
|
+
# @param data [String] 7-byte datetime
|
|
121
|
+
# @return [Time, nil] Parsed time
|
|
122
|
+
def parse_record_datetime(data)
|
|
123
|
+
return nil if data.nil? || data.bytesize < 7
|
|
124
|
+
|
|
125
|
+
year = 1900 + data.getbyte(0)
|
|
126
|
+
month = data.getbyte(1)
|
|
127
|
+
day = data.getbyte(2)
|
|
128
|
+
hour = data.getbyte(3)
|
|
129
|
+
minute = data.getbyte(4)
|
|
130
|
+
second = data.getbyte(5)
|
|
131
|
+
# Timezone offset at byte 6 (15-minute intervals from GMT)
|
|
132
|
+
|
|
133
|
+
Time.new(year, month, day, hour, minute, second)
|
|
134
|
+
rescue ArgumentError
|
|
135
|
+
nil
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Parse file identifier name
|
|
139
|
+
def parse_name
|
|
140
|
+
# Special cases for current and parent directory
|
|
141
|
+
return if @name == "\x00" || @name == "\x01"
|
|
142
|
+
|
|
143
|
+
# Remove version number (;1) if present
|
|
144
|
+
@name = @name.split(";").first if @name.include?(";")
|
|
145
|
+
|
|
146
|
+
# Convert to UTF-8 and strip
|
|
147
|
+
@name = @name.force_encoding("UTF-8").strip
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Omnizip
|
|
4
|
+
module Formats
|
|
5
|
+
module Iso
|
|
6
|
+
# Joliet Extensions for ISO 9660
|
|
7
|
+
#
|
|
8
|
+
# Implements Microsoft's Joliet extensions to support long filenames
|
|
9
|
+
# (up to 64 characters) and Unicode (UCS-2) encoding in ISO 9660 images.
|
|
10
|
+
#
|
|
11
|
+
# Joliet creates a parallel directory structure in a Supplementary Volume
|
|
12
|
+
# Descriptor with UCS-2 encoded filenames, allowing Windows systems to
|
|
13
|
+
# display proper long filenames while maintaining ISO 9660 compatibility.
|
|
14
|
+
module Joliet
|
|
15
|
+
# Joliet escape sequences for UCS-2 encoding
|
|
16
|
+
# Level 1: %/@
|
|
17
|
+
# Level 2: %/C
|
|
18
|
+
# Level 3: %/E (most common, supports UCS-2)
|
|
19
|
+
ESCAPE_SEQUENCE_LEVEL_3 = "%/E"
|
|
20
|
+
|
|
21
|
+
# Maximum Joliet filename length in characters
|
|
22
|
+
MAX_FILENAME_LENGTH = 64
|
|
23
|
+
|
|
24
|
+
# Encode string to UCS-2 (UTF-16BE)
|
|
25
|
+
#
|
|
26
|
+
# @param str [String] String to encode
|
|
27
|
+
# @param max_length [Integer] Maximum length in characters
|
|
28
|
+
# @return [String] UCS-2 encoded string
|
|
29
|
+
def self.encode_ucs2(str, max_length = MAX_FILENAME_LENGTH)
|
|
30
|
+
# Truncate to max length
|
|
31
|
+
str = str[0, max_length] if str.length > max_length
|
|
32
|
+
|
|
33
|
+
# Encode to UTF-16BE (UCS-2)
|
|
34
|
+
str.encode("UTF-16BE")
|
|
35
|
+
rescue Encoding::UndefinedConversionError
|
|
36
|
+
# Fallback to ASCII if conversion fails
|
|
37
|
+
str.encode("UTF-16BE", undef: :replace, replace: "_")
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Decode UCS-2 string to UTF-8
|
|
41
|
+
#
|
|
42
|
+
# @param ucs2_data [String] UCS-2 encoded data
|
|
43
|
+
# @return [String] UTF-8 string
|
|
44
|
+
def self.decode_ucs2(ucs2_data)
|
|
45
|
+
ucs2_data.force_encoding("UTF-16BE").encode("UTF-8")
|
|
46
|
+
rescue Encoding::UndefinedConversionError
|
|
47
|
+
# Fallback if decoding fails
|
|
48
|
+
ucs2_data.force_encoding("UTF-16BE").encode("UTF-8",
|
|
49
|
+
undef: :replace,
|
|
50
|
+
replace: "?")
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Build Joliet directory record
|
|
54
|
+
#
|
|
55
|
+
# @param name [String] Entry name
|
|
56
|
+
# @param entry_info [Hash] Entry information
|
|
57
|
+
# @param is_directory [Boolean] Is this a directory
|
|
58
|
+
# @return [String] Joliet directory record
|
|
59
|
+
def self.build_directory_record(name, entry_info, is_directory: false)
|
|
60
|
+
# Encode name to UCS-2
|
|
61
|
+
name_ucs2 = encode_ucs2(name)
|
|
62
|
+
name_len = name_ucs2.bytesize
|
|
63
|
+
|
|
64
|
+
# Calculate record length
|
|
65
|
+
# No padding needed for UCS-2 names (always even length)
|
|
66
|
+
record_len = 33 + name_len
|
|
67
|
+
|
|
68
|
+
record = +""
|
|
69
|
+
|
|
70
|
+
# Byte 0: Length of directory record
|
|
71
|
+
record << [record_len].pack("C")
|
|
72
|
+
|
|
73
|
+
# Byte 1: Extended attribute record length
|
|
74
|
+
record << [0].pack("C")
|
|
75
|
+
|
|
76
|
+
# Bytes 2-9: Location of extent (both-endian)
|
|
77
|
+
location = entry_info[:location] || 0
|
|
78
|
+
record << [location].pack("V")
|
|
79
|
+
record << [location].pack("N")
|
|
80
|
+
|
|
81
|
+
# Bytes 10-17: Data length (both-endian)
|
|
82
|
+
data_length = entry_info[:size] || 0
|
|
83
|
+
record << [data_length].pack("V")
|
|
84
|
+
record << [data_length].pack("N")
|
|
85
|
+
|
|
86
|
+
# Bytes 18-24: Recording date and time
|
|
87
|
+
mtime = entry_info[:mtime] || entry_info[:stat]&.mtime || Time.now
|
|
88
|
+
record << encode_record_datetime(mtime)
|
|
89
|
+
|
|
90
|
+
# Byte 25: File flags
|
|
91
|
+
flags = 0
|
|
92
|
+
flags |= Iso::FLAG_DIRECTORY if is_directory
|
|
93
|
+
record << [flags].pack("C")
|
|
94
|
+
|
|
95
|
+
# Byte 26: File unit size
|
|
96
|
+
record << [0].pack("C")
|
|
97
|
+
|
|
98
|
+
# Byte 27: Interleave gap size
|
|
99
|
+
record << [0].pack("C")
|
|
100
|
+
|
|
101
|
+
# Bytes 28-31: Volume sequence number (both-endian)
|
|
102
|
+
record << [1].pack("v")
|
|
103
|
+
record << [1].pack("n")
|
|
104
|
+
|
|
105
|
+
# Byte 32: Length of file identifier
|
|
106
|
+
record << [name_len].pack("C")
|
|
107
|
+
|
|
108
|
+
# Bytes 33+: File identifier (UCS-2 encoded)
|
|
109
|
+
record << name_ucs2
|
|
110
|
+
|
|
111
|
+
record
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Sanitize filename for Joliet
|
|
115
|
+
#
|
|
116
|
+
# @param name [String] Original filename
|
|
117
|
+
# @return [String] Sanitized filename
|
|
118
|
+
def self.sanitize_filename(name)
|
|
119
|
+
# Joliet allows most Unicode characters
|
|
120
|
+
# Maximum 64 characters
|
|
121
|
+
# Disallow: / * ? < > | " : \
|
|
122
|
+
|
|
123
|
+
sanitized = name.gsub(/[\/*?<>|":\\]/, "_")
|
|
124
|
+
if sanitized.length > MAX_FILENAME_LENGTH
|
|
125
|
+
sanitized = sanitized[0,
|
|
126
|
+
MAX_FILENAME_LENGTH]
|
|
127
|
+
end
|
|
128
|
+
sanitized
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Check if filename requires Joliet
|
|
132
|
+
#
|
|
133
|
+
# @param name [String] Filename
|
|
134
|
+
# @return [Boolean] true if Joliet needed
|
|
135
|
+
def self.requires_joliet?(name)
|
|
136
|
+
# Check if name exceeds ISO 9660 Level 2 limits
|
|
137
|
+
return true if name.length > 31
|
|
138
|
+
|
|
139
|
+
# Check if name contains non-ASCII or lowercase
|
|
140
|
+
return true if /[^A-Z0-9_.-]/.match?(name)
|
|
141
|
+
|
|
142
|
+
# Check if name contains Unicode
|
|
143
|
+
name.encoding != Encoding::ASCII && name.bytes.any? { |b| b > 127 }
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Build Joliet supplementary volume descriptor
|
|
147
|
+
#
|
|
148
|
+
# @param primary_vd [String] Primary volume descriptor data
|
|
149
|
+
# @param root_dir [Hash] Root directory information
|
|
150
|
+
# @return [String] Joliet SVD (2048 bytes)
|
|
151
|
+
def self.build_supplementary_vd(primary_vd, _root_dir)
|
|
152
|
+
# Start with primary VD as template
|
|
153
|
+
svd = primary_vd.dup
|
|
154
|
+
|
|
155
|
+
# Change type to supplementary
|
|
156
|
+
svd[0] = [Iso::VD_SUPPLEMENTARY].pack("C")
|
|
157
|
+
|
|
158
|
+
# Add Joliet escape sequence
|
|
159
|
+
# Bytes 88-90: Escape sequences
|
|
160
|
+
svd[88, 3] = ESCAPE_SEQUENCE_LEVEL_3
|
|
161
|
+
|
|
162
|
+
# Encode volume identifier to UCS-2
|
|
163
|
+
volume_id = primary_vd[40, 32].strip
|
|
164
|
+
volume_id_ucs2 = encode_ucs2(volume_id, 16)
|
|
165
|
+
svd[40, 32] = pad_ucs2_string(volume_id_ucs2, 32)
|
|
166
|
+
|
|
167
|
+
# Update root directory record with UCS-2 name
|
|
168
|
+
# Root is always "\x00" so no change needed
|
|
169
|
+
|
|
170
|
+
svd
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Pad UCS-2 string to specified length
|
|
174
|
+
#
|
|
175
|
+
# @param ucs2_str [String] UCS-2 string
|
|
176
|
+
# @param byte_length [Integer] Target length in bytes
|
|
177
|
+
# @return [String] Padded string
|
|
178
|
+
def self.pad_ucs2_string(ucs2_str, byte_length)
|
|
179
|
+
if ucs2_str.bytesize > byte_length
|
|
180
|
+
ucs2_str[0, byte_length]
|
|
181
|
+
else
|
|
182
|
+
ucs2_str + (" ".encode("UTF-16BE") * ((byte_length - ucs2_str.bytesize) / 2))
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Encode recording date/time (7-byte format)
|
|
187
|
+
#
|
|
188
|
+
# @param time [Time] Time to encode
|
|
189
|
+
# @return [String] 7-byte encoded time
|
|
190
|
+
def self.encode_record_datetime(time)
|
|
191
|
+
[
|
|
192
|
+
time.year - 1900,
|
|
193
|
+
time.month,
|
|
194
|
+
time.day,
|
|
195
|
+
time.hour,
|
|
196
|
+
time.min,
|
|
197
|
+
time.sec,
|
|
198
|
+
0, # GMT offset
|
|
199
|
+
].pack("C7")
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|