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,530 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "time"
|
|
5
|
+
require_relative "volume_builder"
|
|
6
|
+
require_relative "directory_builder"
|
|
7
|
+
require_relative "../iso"
|
|
8
|
+
|
|
9
|
+
module Omnizip
|
|
10
|
+
module Formats
|
|
11
|
+
module Iso
|
|
12
|
+
# ISO 9660 image writer
|
|
13
|
+
#
|
|
14
|
+
# Creates ISO 9660 filesystem images (CD/DVD images) from files and directories.
|
|
15
|
+
# Supports ISO 9660 levels 1-3, Rock Ridge, and Joliet extensions.
|
|
16
|
+
#
|
|
17
|
+
# @example Create simple ISO image
|
|
18
|
+
# writer = Iso::Writer.new('cdrom.iso')
|
|
19
|
+
# writer.add_directory('files/')
|
|
20
|
+
# writer.write
|
|
21
|
+
#
|
|
22
|
+
# @example With Rock Ridge and Joliet
|
|
23
|
+
# writer = Iso::Writer.new('backup.iso',
|
|
24
|
+
# volume_id: 'BACKUP_2024',
|
|
25
|
+
# rock_ridge: true,
|
|
26
|
+
# joliet: true
|
|
27
|
+
# )
|
|
28
|
+
# writer.add_directory('documents/')
|
|
29
|
+
# writer.write
|
|
30
|
+
class Writer
|
|
31
|
+
# @return [String] Output ISO file path
|
|
32
|
+
attr_reader :output_path
|
|
33
|
+
|
|
34
|
+
# @return [Hash] Writer options
|
|
35
|
+
attr_reader :options
|
|
36
|
+
|
|
37
|
+
# @return [String] Volume identifier (label)
|
|
38
|
+
attr_accessor :volume_id
|
|
39
|
+
|
|
40
|
+
# @return [String] System identifier
|
|
41
|
+
attr_accessor :system_id
|
|
42
|
+
|
|
43
|
+
# @return [String] Publisher
|
|
44
|
+
attr_accessor :publisher
|
|
45
|
+
|
|
46
|
+
# @return [String] Preparer
|
|
47
|
+
attr_accessor :preparer
|
|
48
|
+
|
|
49
|
+
# @return [String] Application identifier
|
|
50
|
+
attr_accessor :application
|
|
51
|
+
|
|
52
|
+
# @return [Integer] ISO 9660 level (1, 2, or 3)
|
|
53
|
+
attr_accessor :level
|
|
54
|
+
|
|
55
|
+
# @return [Boolean] Use Rock Ridge extensions
|
|
56
|
+
attr_accessor :rock_ridge
|
|
57
|
+
|
|
58
|
+
# @return [Boolean] Use Joliet extensions
|
|
59
|
+
attr_accessor :joliet
|
|
60
|
+
|
|
61
|
+
# @return [Array<Hash>] Files to add
|
|
62
|
+
attr_reader :files
|
|
63
|
+
|
|
64
|
+
# @return [Array<Hash>] Directories to add
|
|
65
|
+
attr_reader :directories
|
|
66
|
+
|
|
67
|
+
# Initialize ISO writer
|
|
68
|
+
#
|
|
69
|
+
# @param output_path [String] Output ISO file path
|
|
70
|
+
# @param options [Hash] Writer options
|
|
71
|
+
# @option options [String] :volume_id Volume label
|
|
72
|
+
# @option options [String] :system_id System identifier
|
|
73
|
+
# @option options [String] :publisher Publisher name
|
|
74
|
+
# @option options [String] :preparer Preparer name
|
|
75
|
+
# @option options [String] :application Application name
|
|
76
|
+
# @option options [Integer] :level ISO 9660 level (1-3)
|
|
77
|
+
# @option options [Boolean] :rock_ridge Enable Rock Ridge
|
|
78
|
+
# @option options [Boolean] :joliet Enable Joliet
|
|
79
|
+
def initialize(output_path, options = {})
|
|
80
|
+
@output_path = output_path
|
|
81
|
+
@options = default_options.merge(options)
|
|
82
|
+
|
|
83
|
+
@volume_id = @options[:volume_id]
|
|
84
|
+
@system_id = @options[:system_id]
|
|
85
|
+
@publisher = @options[:publisher]
|
|
86
|
+
@preparer = @options[:preparer]
|
|
87
|
+
@application = @options[:application]
|
|
88
|
+
@level = @options[:level]
|
|
89
|
+
@rock_ridge = @options[:rock_ridge]
|
|
90
|
+
@joliet = @options[:joliet]
|
|
91
|
+
|
|
92
|
+
@files = []
|
|
93
|
+
@directories = []
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Add file to ISO image
|
|
97
|
+
#
|
|
98
|
+
# @param file_path [String] Source file path
|
|
99
|
+
# @param iso_path [String, nil] Path in ISO (defaults to basename)
|
|
100
|
+
# @raise [ArgumentError] if file doesn't exist
|
|
101
|
+
def add_file(file_path, iso_path = nil)
|
|
102
|
+
raise ArgumentError, "File not found: #{file_path}" unless
|
|
103
|
+
File.exist?(file_path)
|
|
104
|
+
|
|
105
|
+
iso_path ||= File.basename(file_path)
|
|
106
|
+
|
|
107
|
+
@files << {
|
|
108
|
+
source: File.expand_path(file_path),
|
|
109
|
+
iso_path: sanitize_path(iso_path),
|
|
110
|
+
stat: File.stat(file_path),
|
|
111
|
+
}
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Add directory to ISO image
|
|
115
|
+
#
|
|
116
|
+
# @param dir_path [String] Source directory path
|
|
117
|
+
# @param recursive [Boolean] Include subdirectories
|
|
118
|
+
# @param iso_path [String, nil] Path in ISO (defaults to basename)
|
|
119
|
+
# @raise [ArgumentError] if directory doesn't exist
|
|
120
|
+
def add_directory(dir_path, recursive: true, iso_path: nil)
|
|
121
|
+
raise ArgumentError, "Directory not found: #{dir_path}" unless
|
|
122
|
+
Dir.exist?(dir_path)
|
|
123
|
+
|
|
124
|
+
iso_path ||= File.basename(dir_path)
|
|
125
|
+
iso_path = sanitize_path(iso_path)
|
|
126
|
+
|
|
127
|
+
if recursive
|
|
128
|
+
add_directory_recursive(dir_path, iso_path)
|
|
129
|
+
else
|
|
130
|
+
add_directory_flat(dir_path, iso_path)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Create ISO image
|
|
135
|
+
#
|
|
136
|
+
# @raise [IOError] if write fails
|
|
137
|
+
def write
|
|
138
|
+
File.open(@output_path, "wb") do |io|
|
|
139
|
+
# Build directory structure
|
|
140
|
+
builder = DirectoryBuilder.new(
|
|
141
|
+
@files,
|
|
142
|
+
@directories,
|
|
143
|
+
level: @level,
|
|
144
|
+
rock_ridge: @rock_ridge,
|
|
145
|
+
)
|
|
146
|
+
dir_structure = builder.build
|
|
147
|
+
|
|
148
|
+
# Build volume descriptor
|
|
149
|
+
volume_builder = VolumeBuilder.new(
|
|
150
|
+
volume_id: @volume_id,
|
|
151
|
+
system_id: @system_id,
|
|
152
|
+
publisher: @publisher,
|
|
153
|
+
preparer: @preparer,
|
|
154
|
+
application: @application,
|
|
155
|
+
level: @level,
|
|
156
|
+
rock_ridge: @rock_ridge,
|
|
157
|
+
joliet: @joliet,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
# Write ISO structure
|
|
161
|
+
write_system_area(io)
|
|
162
|
+
write_volume_descriptors(io, volume_builder, dir_structure)
|
|
163
|
+
write_path_tables(io, dir_structure)
|
|
164
|
+
write_directories(io, dir_structure)
|
|
165
|
+
write_file_data(io)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
@output_path
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
private
|
|
172
|
+
|
|
173
|
+
# Default writer options
|
|
174
|
+
#
|
|
175
|
+
# @return [Hash] Default options
|
|
176
|
+
def default_options
|
|
177
|
+
{
|
|
178
|
+
volume_id: "OMNIZIP_ISO",
|
|
179
|
+
system_id: "LINUX",
|
|
180
|
+
publisher: "OMNIZIP",
|
|
181
|
+
preparer: "OMNIZIP #{Omnizip::VERSION}",
|
|
182
|
+
application: "OMNIZIP",
|
|
183
|
+
level: 2,
|
|
184
|
+
rock_ridge: true,
|
|
185
|
+
joliet: true,
|
|
186
|
+
}
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Add directory recursively
|
|
190
|
+
#
|
|
191
|
+
# @param dir_path [String] Source directory
|
|
192
|
+
# @param iso_path [String] ISO path
|
|
193
|
+
def add_directory_recursive(dir_path, iso_path)
|
|
194
|
+
# Add directory itself
|
|
195
|
+
@directories << {
|
|
196
|
+
source: File.expand_path(dir_path),
|
|
197
|
+
iso_path: iso_path,
|
|
198
|
+
stat: File.stat(dir_path),
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
# Add all contents
|
|
202
|
+
Dir.foreach(dir_path) do |entry|
|
|
203
|
+
next if [".", ".."].include?(entry)
|
|
204
|
+
|
|
205
|
+
source_path = File.join(dir_path, entry)
|
|
206
|
+
target_path = "#{iso_path}/#{entry}"
|
|
207
|
+
|
|
208
|
+
if File.directory?(source_path)
|
|
209
|
+
add_directory_recursive(source_path, target_path)
|
|
210
|
+
else
|
|
211
|
+
add_file(source_path, target_path)
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Add directory flat (non-recursive)
|
|
217
|
+
#
|
|
218
|
+
# @param dir_path [String] Source directory
|
|
219
|
+
# @param iso_path [String] ISO path
|
|
220
|
+
def add_directory_flat(dir_path, iso_path)
|
|
221
|
+
@directories << {
|
|
222
|
+
source: File.expand_path(dir_path),
|
|
223
|
+
iso_path: iso_path,
|
|
224
|
+
stat: File.stat(dir_path),
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
Dir.foreach(dir_path) do |entry|
|
|
228
|
+
next if [".", ".."].include?(entry)
|
|
229
|
+
|
|
230
|
+
source_path = File.join(dir_path, entry)
|
|
231
|
+
next if File.directory?(source_path)
|
|
232
|
+
|
|
233
|
+
add_file(source_path, "#{iso_path}/#{entry}")
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# Sanitize path for ISO filesystem
|
|
238
|
+
#
|
|
239
|
+
# @param path [String] Input path
|
|
240
|
+
# @return [String] Sanitized path
|
|
241
|
+
def sanitize_path(path)
|
|
242
|
+
# Remove leading/trailing slashes
|
|
243
|
+
path = path.gsub(%r{^/+}, "").gsub(%r{/+$}, "")
|
|
244
|
+
|
|
245
|
+
# Normalize separators
|
|
246
|
+
path.split("/").map do |component|
|
|
247
|
+
sanitize_filename(component)
|
|
248
|
+
end.join("/")
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
# Sanitize filename for ISO level
|
|
252
|
+
#
|
|
253
|
+
# @param name [String] Filename
|
|
254
|
+
# @return [String] Sanitized filename
|
|
255
|
+
def sanitize_filename(name)
|
|
256
|
+
case @level
|
|
257
|
+
when 1
|
|
258
|
+
# Level 1: 8.3 format, A-Z 0-9 _ only
|
|
259
|
+
base = name.upcase.gsub(/[^A-Z0-9_]/, "_")[0, 8]
|
|
260
|
+
ext = ""
|
|
261
|
+
if name.include?(".")
|
|
262
|
+
ext = "." + name.split(".").last.upcase.gsub(/[^A-Z0-9]/, "")[0,
|
|
263
|
+
3]
|
|
264
|
+
end
|
|
265
|
+
base + ext
|
|
266
|
+
when 2
|
|
267
|
+
# Level 2: Up to 31 chars, more relaxed
|
|
268
|
+
name.gsub(/[^A-Za-z0-9_.-]/, "_")[0, 31]
|
|
269
|
+
else
|
|
270
|
+
# Level 3: No restrictions
|
|
271
|
+
name
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
# Write system area (first 16 sectors)
|
|
276
|
+
#
|
|
277
|
+
# @param io [IO] Output IO
|
|
278
|
+
def write_system_area(io)
|
|
279
|
+
# System area is reserved, write zeros
|
|
280
|
+
io.write("\x00" * (Iso::SECTOR_SIZE * Iso::SYSTEM_AREA_SECTORS))
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
# Write volume descriptors
|
|
284
|
+
#
|
|
285
|
+
# @param io [IO] Output IO
|
|
286
|
+
# @param builder [VolumeBuilder] Volume builder
|
|
287
|
+
# @param dir_structure [Hash] Directory structure
|
|
288
|
+
def write_volume_descriptors(io, builder, dir_structure)
|
|
289
|
+
# Write primary volume descriptor
|
|
290
|
+
pvd = builder.build_primary(dir_structure[:root])
|
|
291
|
+
io.write(pvd)
|
|
292
|
+
|
|
293
|
+
# Write Joliet supplementary descriptor if enabled
|
|
294
|
+
if @joliet
|
|
295
|
+
svd = builder.build_joliet(dir_structure[:root])
|
|
296
|
+
io.write(svd)
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
# Write terminator
|
|
300
|
+
terminator = build_terminator
|
|
301
|
+
io.write(terminator)
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
# Build volume descriptor set terminator
|
|
305
|
+
#
|
|
306
|
+
# @return [String] Terminator sector (2048 bytes)
|
|
307
|
+
def build_terminator
|
|
308
|
+
sector = +""
|
|
309
|
+
sector << [Iso::VD_TERMINATOR].pack("C") # Type
|
|
310
|
+
sector << VolumeDescriptor::ISO_IDENTIFIER # "CD001"
|
|
311
|
+
sector << [1].pack("C") # Version
|
|
312
|
+
sector << ("\x00" * (Iso::SECTOR_SIZE - 7))
|
|
313
|
+
sector
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
# Write path tables
|
|
317
|
+
#
|
|
318
|
+
# @param io [IO] Output IO
|
|
319
|
+
# @param dir_structure [Hash] Directory structure
|
|
320
|
+
def write_path_tables(io, dir_structure)
|
|
321
|
+
# Path tables provide quick directory lookup
|
|
322
|
+
# For simplicity, we'll write minimal path tables
|
|
323
|
+
|
|
324
|
+
# Little-endian path table
|
|
325
|
+
path_table = build_path_table(dir_structure[:directories])
|
|
326
|
+
io.write(path_table)
|
|
327
|
+
|
|
328
|
+
# Pad to sector boundary
|
|
329
|
+
padding = Iso::SECTOR_SIZE - (path_table.bytesize % Iso::SECTOR_SIZE)
|
|
330
|
+
io.write("\x00" * padding) if padding < Iso::SECTOR_SIZE
|
|
331
|
+
|
|
332
|
+
# Big-endian path table (required by spec)
|
|
333
|
+
path_table_be = build_path_table(dir_structure[:directories],
|
|
334
|
+
big_endian: true)
|
|
335
|
+
io.write(path_table_be)
|
|
336
|
+
|
|
337
|
+
# Pad to sector boundary
|
|
338
|
+
padding = Iso::SECTOR_SIZE - (path_table_be.bytesize % Iso::SECTOR_SIZE)
|
|
339
|
+
io.write("\x00" * padding) if padding < Iso::SECTOR_SIZE
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
# Build path table
|
|
343
|
+
#
|
|
344
|
+
# @param directories [Array<Hash>] Directory list
|
|
345
|
+
# @param big_endian [Boolean] Use big-endian format
|
|
346
|
+
# @return [String] Path table data
|
|
347
|
+
def build_path_table(directories, big_endian: false)
|
|
348
|
+
table = +""
|
|
349
|
+
|
|
350
|
+
directories.each_with_index do |dir, _idx|
|
|
351
|
+
name = dir[:name]
|
|
352
|
+
parent = dir[:parent_idx] || 1
|
|
353
|
+
|
|
354
|
+
# Name length
|
|
355
|
+
table << [name.bytesize].pack("C")
|
|
356
|
+
|
|
357
|
+
# Extended attribute record length
|
|
358
|
+
table << [0].pack("C")
|
|
359
|
+
|
|
360
|
+
# Location of extent
|
|
361
|
+
pack_format = big_endian ? "N" : "V"
|
|
362
|
+
table << [dir[:location]].pack(pack_format)
|
|
363
|
+
|
|
364
|
+
# Parent directory number (1-based)
|
|
365
|
+
pack_format = big_endian ? "n" : "v"
|
|
366
|
+
table << [parent + 1].pack(pack_format)
|
|
367
|
+
|
|
368
|
+
# Directory name
|
|
369
|
+
table << name
|
|
370
|
+
|
|
371
|
+
# Pad to even length
|
|
372
|
+
table << "\x00" if name.bytesize.odd?
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
table
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
# Write directory records
|
|
379
|
+
#
|
|
380
|
+
# @param io [IO] Output IO
|
|
381
|
+
# @param dir_structure [Hash] Directory structure
|
|
382
|
+
def write_directories(io, dir_structure)
|
|
383
|
+
dir_structure[:directories].each do |dir_info|
|
|
384
|
+
write_directory_record(io, dir_info)
|
|
385
|
+
end
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
# Write single directory record
|
|
389
|
+
#
|
|
390
|
+
# @param io [IO] Output IO
|
|
391
|
+
# @param dir_info [Hash] Directory information
|
|
392
|
+
def write_directory_record(io, dir_info)
|
|
393
|
+
# Seek to directory location
|
|
394
|
+
io.seek(dir_info[:location] * Iso::SECTOR_SIZE)
|
|
395
|
+
|
|
396
|
+
# Write directory entries
|
|
397
|
+
dir_data = build_directory_data(dir_info)
|
|
398
|
+
io.write(dir_data)
|
|
399
|
+
|
|
400
|
+
# Pad to sector boundary
|
|
401
|
+
padding = Iso::SECTOR_SIZE - (dir_data.bytesize % Iso::SECTOR_SIZE)
|
|
402
|
+
io.write("\x00" * padding) if padding < Iso::SECTOR_SIZE
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
# Build directory data with all entries
|
|
406
|
+
#
|
|
407
|
+
# @param dir_info [Hash] Directory information
|
|
408
|
+
# @return [String] Directory record data
|
|
409
|
+
def build_directory_data(dir_info)
|
|
410
|
+
data = +""
|
|
411
|
+
|
|
412
|
+
# Add "." entry (current directory)
|
|
413
|
+
data << build_directory_entry("\x00", dir_info, is_self: true)
|
|
414
|
+
|
|
415
|
+
# Add ".." entry (parent directory)
|
|
416
|
+
parent = dir_info[:parent] || dir_info
|
|
417
|
+
data << build_directory_entry("\x01", parent, is_self: false)
|
|
418
|
+
|
|
419
|
+
# Add child entries
|
|
420
|
+
dir_info[:children].each do |child|
|
|
421
|
+
name = child[:name]
|
|
422
|
+
data << build_directory_entry(name, child, is_self: false)
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
data
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
# Build single directory entry
|
|
429
|
+
#
|
|
430
|
+
# @param name [String] Entry name
|
|
431
|
+
# @param entry_info [Hash] Entry information
|
|
432
|
+
# @param is_self [Boolean] Is this the "." entry
|
|
433
|
+
# @return [String] Directory entry record
|
|
434
|
+
def build_directory_entry(name, entry_info, is_self:)
|
|
435
|
+
record = +""
|
|
436
|
+
|
|
437
|
+
# Extended attribute length (0)
|
|
438
|
+
ext_attr_len = 0
|
|
439
|
+
|
|
440
|
+
# Calculate record length
|
|
441
|
+
name_len = name.bytesize
|
|
442
|
+
padding = name_len.even? ? 1 : 0
|
|
443
|
+
record_len = 33 + name_len + padding
|
|
444
|
+
|
|
445
|
+
# Byte 0: Length of directory record
|
|
446
|
+
record << [record_len].pack("C")
|
|
447
|
+
|
|
448
|
+
# Byte 1: Extended attribute record length
|
|
449
|
+
record << [ext_attr_len].pack("C")
|
|
450
|
+
|
|
451
|
+
# Bytes 2-9: Location of extent (both-endian)
|
|
452
|
+
location = entry_info[:location] || 0
|
|
453
|
+
record << [location].pack("V") # Little-endian
|
|
454
|
+
record << [location].pack("N") # Big-endian
|
|
455
|
+
|
|
456
|
+
# Bytes 10-17: Data length (both-endian)
|
|
457
|
+
data_length = entry_info[:size] || 0
|
|
458
|
+
record << [data_length].pack("V")
|
|
459
|
+
record << [data_length].pack("N")
|
|
460
|
+
|
|
461
|
+
# Bytes 18-24: Recording date and time
|
|
462
|
+
record << encode_record_datetime(entry_info[:mtime] || Time.now)
|
|
463
|
+
|
|
464
|
+
# Byte 25: File flags
|
|
465
|
+
flags = 0
|
|
466
|
+
flags |= Iso::FLAG_DIRECTORY if entry_info[:directory]
|
|
467
|
+
record << [flags].pack("C")
|
|
468
|
+
|
|
469
|
+
# Byte 26: File unit size (0 for non-interleaved)
|
|
470
|
+
record << [0].pack("C")
|
|
471
|
+
|
|
472
|
+
# Byte 27: Interleave gap size
|
|
473
|
+
record << [0].pack("C")
|
|
474
|
+
|
|
475
|
+
# Bytes 28-31: Volume sequence number (both-endian)
|
|
476
|
+
record << [1].pack("v")
|
|
477
|
+
record << [1].pack("n")
|
|
478
|
+
|
|
479
|
+
# Byte 32: Length of file identifier
|
|
480
|
+
record << [name_len].pack("C")
|
|
481
|
+
|
|
482
|
+
# Bytes 33+: File identifier
|
|
483
|
+
record << name
|
|
484
|
+
|
|
485
|
+
# Padding byte if name length is even
|
|
486
|
+
record << "\x00" if name_len.even?
|
|
487
|
+
|
|
488
|
+
record
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
# Encode recording date/time (7-byte format)
|
|
492
|
+
#
|
|
493
|
+
# @param time [Time] Time to encode
|
|
494
|
+
# @return [String] 7-byte encoded time
|
|
495
|
+
def encode_record_datetime(time)
|
|
496
|
+
[
|
|
497
|
+
time.year - 1900, # Years since 1900
|
|
498
|
+
time.month, # Month (1-12)
|
|
499
|
+
time.day, # Day (1-31)
|
|
500
|
+
time.hour, # Hour (0-23)
|
|
501
|
+
time.min, # Minute (0-59)
|
|
502
|
+
time.sec, # Second (0-59)
|
|
503
|
+
0, # GMT offset (0 = GMT)
|
|
504
|
+
].pack("C7")
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
# Write file data
|
|
508
|
+
#
|
|
509
|
+
# @param io [IO] Output IO
|
|
510
|
+
def write_file_data(io)
|
|
511
|
+
@files.each do |file_info|
|
|
512
|
+
# Seek to file location
|
|
513
|
+
io.seek(file_info[:location] * Iso::SECTOR_SIZE)
|
|
514
|
+
|
|
515
|
+
# Write file data
|
|
516
|
+
File.open(file_info[:source], "rb") do |src|
|
|
517
|
+
while (data = src.read(Iso::SECTOR_SIZE))
|
|
518
|
+
# Pad to sector boundary
|
|
519
|
+
if data.bytesize < Iso::SECTOR_SIZE
|
|
520
|
+
data += "\x00" * (Iso::SECTOR_SIZE - data.bytesize)
|
|
521
|
+
end
|
|
522
|
+
io.write(data)
|
|
523
|
+
end
|
|
524
|
+
end
|
|
525
|
+
end
|
|
526
|
+
end
|
|
527
|
+
end
|
|
528
|
+
end
|
|
529
|
+
end
|
|
530
|
+
end
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "iso/reader"
|
|
4
|
+
require_relative "iso/writer"
|
|
5
|
+
require_relative "iso/volume_descriptor"
|
|
6
|
+
require_relative "iso/volume_builder"
|
|
7
|
+
require_relative "iso/directory_record"
|
|
8
|
+
require_relative "iso/directory_builder"
|
|
9
|
+
require_relative "iso/path_table"
|
|
10
|
+
require_relative "iso/rock_ridge"
|
|
11
|
+
require_relative "iso/joliet"
|
|
12
|
+
|
|
13
|
+
module Omnizip
|
|
14
|
+
module Formats
|
|
15
|
+
# ISO 9660 CD-ROM filesystem format support
|
|
16
|
+
# Provides read-only access to ISO images
|
|
17
|
+
#
|
|
18
|
+
# ISO 9660 is the standard filesystem for CD-ROMs and DVD-ROMs.
|
|
19
|
+
# This implementation supports:
|
|
20
|
+
# - Primary Volume Descriptor parsing
|
|
21
|
+
# - Directory structure traversal
|
|
22
|
+
# - File extraction
|
|
23
|
+
# - Rock Ridge extensions (basic)
|
|
24
|
+
module Iso
|
|
25
|
+
# ISO 9660 constants
|
|
26
|
+
SECTOR_SIZE = 2048
|
|
27
|
+
SYSTEM_AREA_SECTORS = 16
|
|
28
|
+
VOLUME_DESCRIPTOR_START = SYSTEM_AREA_SECTORS
|
|
29
|
+
|
|
30
|
+
# Volume descriptor types
|
|
31
|
+
VD_BOOT_RECORD = 0
|
|
32
|
+
VD_PRIMARY = 1
|
|
33
|
+
VD_SUPPLEMENTARY = 2
|
|
34
|
+
VD_PARTITION = 3
|
|
35
|
+
VD_TERMINATOR = 255
|
|
36
|
+
|
|
37
|
+
# File flags
|
|
38
|
+
FLAG_HIDDEN = 0x01
|
|
39
|
+
FLAG_DIRECTORY = 0x02
|
|
40
|
+
FLAG_ASSOCIATED = 0x04
|
|
41
|
+
FLAG_EXTENDED = 0x08
|
|
42
|
+
FLAG_PERMISSIONS = 0x10
|
|
43
|
+
FLAG_NOT_FINAL = 0x80
|
|
44
|
+
|
|
45
|
+
# Open existing ISO image
|
|
46
|
+
#
|
|
47
|
+
# @param path [String] Path to ISO file
|
|
48
|
+
# @yield [reader] Block for reading archive
|
|
49
|
+
# @yieldparam reader [Reader] ISO reader
|
|
50
|
+
# @return [Reader] ISO reader
|
|
51
|
+
def self.open(path)
|
|
52
|
+
reader = Reader.new(path)
|
|
53
|
+
reader.open
|
|
54
|
+
|
|
55
|
+
if block_given?
|
|
56
|
+
begin
|
|
57
|
+
yield reader
|
|
58
|
+
ensure
|
|
59
|
+
reader.close
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
reader
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# List contents of ISO image
|
|
67
|
+
#
|
|
68
|
+
# @param path [String] Path to ISO file
|
|
69
|
+
# @return [Array<DirectoryRecord>] Directory entries
|
|
70
|
+
def self.list(path)
|
|
71
|
+
open(path, &:entries)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Extract ISO contents
|
|
75
|
+
#
|
|
76
|
+
# @param iso_path [String] Path to ISO file
|
|
77
|
+
# @param output_dir [String] Output directory
|
|
78
|
+
def self.extract(iso_path, output_dir)
|
|
79
|
+
open(iso_path) do |iso|
|
|
80
|
+
iso.extract_all(output_dir)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Get ISO volume information
|
|
85
|
+
#
|
|
86
|
+
# @param path [String] Path to ISO file
|
|
87
|
+
# @return [Hash] Volume information
|
|
88
|
+
def self.info(path)
|
|
89
|
+
open(path) do |iso|
|
|
90
|
+
{
|
|
91
|
+
format: "ISO 9660",
|
|
92
|
+
volume_id: iso.volume_identifier,
|
|
93
|
+
system_id: iso.system_identifier,
|
|
94
|
+
size: iso.volume_size,
|
|
95
|
+
files: iso.entries.count { |e| !e.directory? },
|
|
96
|
+
directories: iso.entries.count(&:directory?),
|
|
97
|
+
}
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Create ISO 9660 image
|
|
102
|
+
#
|
|
103
|
+
# @param path [String] Output ISO file path
|
|
104
|
+
# @param options [Hash] Creation options
|
|
105
|
+
# @yield [writer] Block for adding files/directories
|
|
106
|
+
# @yieldparam writer [Writer] ISO writer
|
|
107
|
+
# @return [String] Path to created ISO
|
|
108
|
+
#
|
|
109
|
+
# @example Create ISO image
|
|
110
|
+
# Omnizip::Formats::Iso.create('backup.iso') do |iso|
|
|
111
|
+
# iso.volume_id = 'BACKUP_2024'
|
|
112
|
+
# iso.add_directory('documents/')
|
|
113
|
+
# end
|
|
114
|
+
#
|
|
115
|
+
# @example With Rock Ridge and Joliet
|
|
116
|
+
# Omnizip::Formats::Iso.create('cdrom.iso',
|
|
117
|
+
# rock_ridge: true,
|
|
118
|
+
# joliet: true
|
|
119
|
+
# ) do |iso|
|
|
120
|
+
# iso.add_directory('files/')
|
|
121
|
+
# end
|
|
122
|
+
def self.create(path, options = {})
|
|
123
|
+
writer = Writer.new(path, options)
|
|
124
|
+
|
|
125
|
+
yield writer if block_given?
|
|
126
|
+
|
|
127
|
+
writer.write
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Auto-register ISO format when loaded
|
|
131
|
+
def self.register!
|
|
132
|
+
require_relative "../format_registry"
|
|
133
|
+
FormatRegistry.register(".iso", Reader)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Auto-register on load
|
|
140
|
+
Omnizip::Formats::Iso.register!
|