omnizip 0.4.7-x86_64-linux
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 +293 -0
- data/CHANGELOG.md +1647 -0
- data/CONTEXT.md +68 -0
- data/COPYING +502 -0
- data/Gemfile +19 -0
- data/LICENSE +12 -0
- data/README.adoc +1327 -0
- data/Rakefile +15 -0
- data/TODO.refactor/00-overview.md +67 -0
- data/TODO.refactor/01-registry-base-class.md +81 -0
- data/TODO.refactor/02-error-hierarchy.md +57 -0
- data/TODO.refactor/03-autoload-entry-point.md +69 -0
- data/TODO.refactor/04-filter-base-consolidation.md +49 -0
- data/TODO.refactor/05-send-private-methods.md +64 -0
- data/TODO.refactor/06-instance-variable-access.md +69 -0
- data/TODO.refactor/07-respond-to-replacement.md +237 -0
- data/TODO.refactor/08-spec-doubles.md +27 -0
- data/TODO.refactor/09-cli-shared-module.md +35 -0
- data/TODO.refactor/10-format-detector-ocp.md +34 -0
- data/TODO.refactor/11-convenience-decoupling.md +38 -0
- data/TODO.refactor/12-thread-safety.md +8 -0
- data/TODO.refactor/13-lutaml-model-migration.md +158 -0
- data/TODO.refactor/14-add-missing-specs.md +25 -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 +92 -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 +263 -0
- data/docs/getting-started/index.adoc +42 -0
- data/docs/getting-started/installation.adoc +138 -0
- data/docs/getting-started/quick-start.adoc +183 -0
- data/docs/getting-started/your-first-archive.adoc +219 -0
- data/docs/guides/advanced-features/encryption.adoc +277 -0
- data/docs/guides/advanced-features/index.adoc +59 -0
- data/docs/guides/advanced-features/parallel-processing.adoc +144 -0
- data/docs/guides/advanced-features/progress-tracking.adoc +124 -0
- data/docs/guides/advanced-features/streaming.adoc +142 -0
- data/docs/guides/archive-formats/gzip-format.adoc +105 -0
- data/docs/guides/archive-formats/index.adoc +160 -0
- data/docs/guides/archive-formats/ole-format.adoc +151 -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/rpm-format.adoc +108 -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 +114 -0
- data/docs/guides/compression-algorithms/deflate.adoc +313 -0
- data/docs/guides/compression-algorithms/index.adoc +190 -0
- data/docs/guides/compression-algorithms/lzma.adoc +394 -0
- data/docs/guides/compression-algorithms/lzma2.adoc +244 -0
- data/docs/guides/compression-algorithms/ppmd.adoc +313 -0
- data/docs/guides/compression-algorithms/zstandard.adoc +296 -0
- data/docs/guides/creating-archives.adoc +348 -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 +228 -0
- data/docs/lychee.toml +67 -0
- data/docs/reference/api/overview.adoc +187 -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/rust-backend.md +0 -0
- data/docs/troubleshooting/index.adoc +207 -0
- data/docs/xar_format.md +216 -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 +176 -0
- data/lib/omnizip/algorithm_registry.rb +65 -0
- data/lib/omnizip/algorithms/.keep +0 -0
- data/lib/omnizip/algorithms/bzip2/bwt.rb +236 -0
- data/lib/omnizip/algorithms/bzip2/bz2.rb +687 -0
- data/lib/omnizip/algorithms/bzip2/rle.rb +146 -0
- data/lib/omnizip/algorithms/bzip2.rb +108 -0
- data/lib/omnizip/algorithms/deflate/constants.rb +28 -0
- data/lib/omnizip/algorithms/deflate/decoder.rb +37 -0
- data/lib/omnizip/algorithms/deflate/encoder.rb +45 -0
- data/lib/omnizip/algorithms/deflate.rb +135 -0
- data/lib/omnizip/algorithms/deflate64/constants.rb +45 -0
- data/lib/omnizip/algorithms/deflate64/decoder.rb +151 -0
- data/lib/omnizip/algorithms/deflate64/encoder.rb +95 -0
- data/lib/omnizip/algorithms/deflate64/huffman_coder.rb +352 -0
- data/lib/omnizip/algorithms/deflate64/lz77_encoder.rb +140 -0
- data/lib/omnizip/algorithms/deflate64.rb +118 -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 +146 -0
- data/lib/omnizip/algorithms/lzma/dictionary.rb +70 -0
- data/lib/omnizip/algorithms/lzma/distance_coder.rb +326 -0
- data/lib/omnizip/algorithms/lzma/encoder.rb +139 -0
- data/lib/omnizip/algorithms/lzma/length_coder.rb +172 -0
- data/lib/omnizip/algorithms/lzma/literal_decoder.rb +204 -0
- data/lib/omnizip/algorithms/lzma/literal_encoder.rb +208 -0
- data/lib/omnizip/algorithms/lzma/lzip_decoder.rb +368 -0
- data/lib/omnizip/algorithms/lzma/lzma1_encoder.rb +105 -0
- data/lib/omnizip/algorithms/lzma/lzma_alone_decoder.rb +194 -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 +233 -0
- data/lib/omnizip/algorithms/lzma/match_finder_config.rb +142 -0
- data/lib/omnizip/algorithms/lzma/match_finder_factory.rb +84 -0
- data/lib/omnizip/algorithms/lzma/optimal_encoder.rb +159 -0
- data/lib/omnizip/algorithms/lzma/probability_models.rb +72 -0
- data/lib/omnizip/algorithms/lzma/range_coder.rb +82 -0
- data/lib/omnizip/algorithms/lzma/range_decoder.rb +274 -0
- data/lib/omnizip/algorithms/lzma/range_encoder.rb +202 -0
- data/lib/omnizip/algorithms/lzma/state.rb +127 -0
- data/lib/omnizip/algorithms/lzma/xz_buffered_range_encoder.rb +323 -0
- data/lib/omnizip/algorithms/lzma/xz_encoder.rb +420 -0
- data/lib/omnizip/algorithms/lzma/xz_encoder_fast.rb +640 -0
- data/lib/omnizip/algorithms/lzma/xz_match_finder_adapter.rb +224 -0
- data/lib/omnizip/algorithms/lzma/xz_price_calculator.rb +167 -0
- data/lib/omnizip/algorithms/lzma/xz_probability_models.rb +258 -0
- data/lib/omnizip/algorithms/lzma/xz_range_encoder.rb +223 -0
- data/lib/omnizip/algorithms/lzma/xz_range_encoder_exact.rb +314 -0
- data/lib/omnizip/algorithms/lzma/xz_state.rb +108 -0
- data/lib/omnizip/algorithms/lzma/xz_utils_decoder.rb +1311 -0
- data/lib/omnizip/algorithms/lzma.rb +266 -0
- data/lib/omnizip/algorithms/lzma2/chunk_manager.rb +180 -0
- data/lib/omnizip/algorithms/lzma2/constants.rb +38 -0
- data/lib/omnizip/algorithms/lzma2/encoder.rb +145 -0
- data/lib/omnizip/algorithms/lzma2/lzma2_chunk.rb +165 -0
- data/lib/omnizip/algorithms/lzma2/properties.rb +179 -0
- data/lib/omnizip/algorithms/lzma2/simple_lzma2_encoder.rb +122 -0
- data/lib/omnizip/algorithms/lzma2/xz_encoder_adapter.rb +85 -0
- data/lib/omnizip/algorithms/lzma2.rb +133 -0
- data/lib/omnizip/algorithms/ppmd7/constants.rb +74 -0
- data/lib/omnizip/algorithms/ppmd7/context.rb +151 -0
- data/lib/omnizip/algorithms/ppmd7/decoder.rb +127 -0
- data/lib/omnizip/algorithms/ppmd7/encoder.rb +153 -0
- data/lib/omnizip/algorithms/ppmd7/model.rb +245 -0
- data/lib/omnizip/algorithms/ppmd7/symbol_state.rb +57 -0
- data/lib/omnizip/algorithms/ppmd7.rb +146 -0
- data/lib/omnizip/algorithms/ppmd8/constants.rb +61 -0
- data/lib/omnizip/algorithms/ppmd8/context.rb +32 -0
- data/lib/omnizip/algorithms/ppmd8/decoder.rb +104 -0
- data/lib/omnizip/algorithms/ppmd8/encoder.rb +135 -0
- data/lib/omnizip/algorithms/ppmd8/model.rb +246 -0
- data/lib/omnizip/algorithms/ppmd8/restoration_method.rb +78 -0
- data/lib/omnizip/algorithms/ppmd8.rb +122 -0
- data/lib/omnizip/algorithms/ppmd_base.rb +142 -0
- data/lib/omnizip/algorithms/registration.rb +28 -0
- data/lib/omnizip/algorithms/zstandard/constants.rb +158 -0
- data/lib/omnizip/algorithms/zstandard/decoder.rb +249 -0
- data/lib/omnizip/algorithms/zstandard/dictionary.rb +73 -0
- data/lib/omnizip/algorithms/zstandard/encoder.rb +294 -0
- data/lib/omnizip/algorithms/zstandard/frame/block.rb +143 -0
- data/lib/omnizip/algorithms/zstandard/frame/header.rb +233 -0
- data/lib/omnizip/algorithms/zstandard/frame.rb +33 -0
- data/lib/omnizip/algorithms/zstandard/fse/bitstream.rb +183 -0
- data/lib/omnizip/algorithms/zstandard/fse/encoder.rb +534 -0
- data/lib/omnizip/algorithms/zstandard/fse/interleaved.rb +92 -0
- data/lib/omnizip/algorithms/zstandard/fse/table.rb +188 -0
- data/lib/omnizip/algorithms/zstandard/fse/table_description.rb +203 -0
- data/lib/omnizip/algorithms/zstandard/fse.rb +53 -0
- data/lib/omnizip/algorithms/zstandard/huffman.rb +276 -0
- data/lib/omnizip/algorithms/zstandard/huffman_encoder.rb +327 -0
- data/lib/omnizip/algorithms/zstandard/ldm.rb +121 -0
- data/lib/omnizip/algorithms/zstandard/literals.rb +241 -0
- data/lib/omnizip/algorithms/zstandard/literals_encoder.rb +96 -0
- data/lib/omnizip/algorithms/zstandard/match_finder.rb +483 -0
- data/lib/omnizip/algorithms/zstandard/sequences.rb +350 -0
- data/lib/omnizip/algorithms/zstandard/sequences_encoder.rb +333 -0
- data/lib/omnizip/algorithms/zstandard/xxhash.rb +159 -0
- data/lib/omnizip/algorithms/zstandard.rb +210 -0
- data/lib/omnizip/algorithms.rb +35 -0
- data/lib/omnizip/archive/builder.rb +48 -0
- data/lib/omnizip/archive/reader_session.rb +67 -0
- data/lib/omnizip/archive.rb +86 -0
- data/lib/omnizip/archive_handler.rb +82 -0
- data/lib/omnizip/archive_handlers/cpio_handler.rb +47 -0
- data/lib/omnizip/archive_handlers/iso_handler.rb +54 -0
- data/lib/omnizip/archive_handlers/ole_handler.rb +37 -0
- data/lib/omnizip/archive_handlers/rar_handler.rb +96 -0
- data/lib/omnizip/archive_handlers/rpm_handler.rb +45 -0
- data/lib/omnizip/archive_handlers/seven_zip_handler.rb +89 -0
- data/lib/omnizip/archive_handlers/tar_handler.rb +75 -0
- data/lib/omnizip/archive_handlers/xar_handler.rb +49 -0
- data/lib/omnizip/archive_handlers/zip_handler.rb +115 -0
- data/lib/omnizip/archive_handlers.rb +18 -0
- data/lib/omnizip/backends.rb +160 -0
- data/lib/omnizip/buffer/memory_archive.rb +246 -0
- data/lib/omnizip/buffer/memory_extractor.rb +234 -0
- data/lib/omnizip/buffer/seven_zip_bridge.rb +171 -0
- data/lib/omnizip/buffer.rb +187 -0
- data/lib/omnizip/checksum_registry.rb +41 -0
- data/lib/omnizip/checksums/crc32.rb +101 -0
- data/lib/omnizip/checksums/crc64.rb +102 -0
- data/lib/omnizip/checksums/crc_base.rb +158 -0
- data/lib/omnizip/checksums/registration.rb +16 -0
- data/lib/omnizip/checksums/verifier.rb +130 -0
- data/lib/omnizip/checksums.rb +11 -0
- data/lib/omnizip/chunked/memory_manager.rb +194 -0
- data/lib/omnizip/chunked/reader.rb +78 -0
- data/lib/omnizip/chunked/writer.rb +123 -0
- data/lib/omnizip/chunked.rb +123 -0
- data/lib/omnizip/cli/output_formatter.rb +112 -0
- data/lib/omnizip/cli.rb +604 -0
- data/lib/omnizip/commands/.keep +0 -0
- data/lib/omnizip/commands/archive_create_command.rb +245 -0
- data/lib/omnizip/commands/archive_extract_command.rb +231 -0
- data/lib/omnizip/commands/archive_list_command.rb +204 -0
- data/lib/omnizip/commands/archive_repair_command.rb +129 -0
- data/lib/omnizip/commands/archive_verify_command.rb +115 -0
- data/lib/omnizip/commands/compress_command.rb +115 -0
- data/lib/omnizip/commands/decompress_command.rb +149 -0
- data/lib/omnizip/commands/list_command.rb +51 -0
- data/lib/omnizip/commands/metadata_command.rb +215 -0
- data/lib/omnizip/commands/parity_create_command.rb +114 -0
- data/lib/omnizip/commands/parity_repair_command.rb +120 -0
- data/lib/omnizip/commands/parity_verify_command.rb +122 -0
- data/lib/omnizip/commands/profile_list_command.rb +54 -0
- data/lib/omnizip/commands/profile_show_command.rb +44 -0
- data/lib/omnizip/commands.rb +21 -0
- data/lib/omnizip/convenience.rb +389 -0
- data/lib/omnizip/converter/conversion_registry.rb +50 -0
- data/lib/omnizip/converter/conversion_strategy.rb +108 -0
- data/lib/omnizip/converter/extract_repack_strategy.rb +51 -0
- data/lib/omnizip/converter/seven_zip_to_zip_strategy.rb +43 -0
- data/lib/omnizip/converter/zip_to_seven_zip_strategy.rb +54 -0
- data/lib/omnizip/converter.rb +124 -0
- data/lib/omnizip/crypto/aes256/cipher.rb +104 -0
- data/lib/omnizip/crypto/aes256/constants.rb +28 -0
- data/lib/omnizip/crypto/aes256/key_derivation.rb +100 -0
- data/lib/omnizip/crypto/aes256.rb +102 -0
- data/lib/omnizip/crypto.rb +11 -0
- data/lib/omnizip/entry.rb +44 -0
- data/lib/omnizip/error.rb +110 -0
- data/lib/omnizip/eta/exponential_smoothing_estimator.rb +95 -0
- data/lib/omnizip/eta/moving_average_estimator.rb +96 -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 +68 -0
- data/lib/omnizip/extraction/filter_chain.rb +179 -0
- data/lib/omnizip/extraction/glob_pattern.rb +140 -0
- data/lib/omnizip/extraction/pattern_matcher.rb +66 -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 +247 -0
- data/lib/omnizip/extraction.rb +115 -0
- data/lib/omnizip/file_type/mime_classifier.rb +144 -0
- data/lib/omnizip/file_type.rb +117 -0
- data/lib/omnizip/filter.rb +141 -0
- data/lib/omnizip/filter_pipeline.rb +108 -0
- data/lib/omnizip/filter_registry.rb +113 -0
- data/lib/omnizip/filters/bcj.rb +277 -0
- data/lib/omnizip/filters/bcj2/constants.rb +53 -0
- data/lib/omnizip/filters/bcj2/decoder.rb +263 -0
- data/lib/omnizip/filters/bcj2/encoder.rb +58 -0
- data/lib/omnizip/filters/bcj2/stream_data.rb +91 -0
- data/lib/omnizip/filters/bcj2.rb +93 -0
- data/lib/omnizip/filters/bcj_arm.rb +174 -0
- data/lib/omnizip/filters/bcj_arm64.rb +242 -0
- data/lib/omnizip/filters/bcj_ia64.rb +194 -0
- data/lib/omnizip/filters/bcj_ppc.rb +188 -0
- data/lib/omnizip/filters/bcj_sparc.rb +174 -0
- data/lib/omnizip/filters/bcj_x86.rb +191 -0
- data/lib/omnizip/filters/delta.rb +195 -0
- data/lib/omnizip/filters/xz_delta.rb +258 -0
- data/lib/omnizip/filters.rb +20 -0
- data/lib/omnizip/format_detector.rb +166 -0
- data/lib/omnizip/formats/.keep +0 -0
- data/lib/omnizip/formats/bzip2_file.rb +159 -0
- data/lib/omnizip/formats/cpio/bounded_io.rb +66 -0
- data/lib/omnizip/formats/cpio/constants.rb +55 -0
- data/lib/omnizip/formats/cpio/entry.rb +389 -0
- data/lib/omnizip/formats/cpio/reader.rb +194 -0
- data/lib/omnizip/formats/cpio/writer.rb +232 -0
- data/lib/omnizip/formats/cpio.rb +132 -0
- data/lib/omnizip/formats/format_spec_loader.rb +228 -0
- data/lib/omnizip/formats/gzip.rb +236 -0
- data/lib/omnizip/formats/iso/directory_builder.rb +321 -0
- data/lib/omnizip/formats/iso/directory_record.rb +162 -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 +194 -0
- data/lib/omnizip/formats/iso/rock_ridge.rb +349 -0
- data/lib/omnizip/formats/iso/volume_builder.rb +319 -0
- data/lib/omnizip/formats/iso/volume_descriptor.rb +168 -0
- data/lib/omnizip/formats/iso/writer.rb +543 -0
- data/lib/omnizip/formats/iso.rb +133 -0
- data/lib/omnizip/formats/lzip.rb +161 -0
- data/lib/omnizip/formats/lzma_alone.rb +149 -0
- data/lib/omnizip/formats/msi/cab_extractor.rb +181 -0
- data/lib/omnizip/formats/msi/constants.rb +157 -0
- data/lib/omnizip/formats/msi/directory_resolver.rb +188 -0
- data/lib/omnizip/formats/msi/entry.rb +134 -0
- data/lib/omnizip/formats/msi/reader.rb +301 -0
- data/lib/omnizip/formats/msi/string_pool.rb +246 -0
- data/lib/omnizip/formats/msi/table_parser.rb +244 -0
- data/lib/omnizip/formats/msi.rb +73 -0
- data/lib/omnizip/formats/ole/allocation_table.rb +263 -0
- data/lib/omnizip/formats/ole/constants.rb +61 -0
- data/lib/omnizip/formats/ole/dirent.rb +439 -0
- data/lib/omnizip/formats/ole/header.rb +199 -0
- data/lib/omnizip/formats/ole/ranges_io.rb +347 -0
- data/lib/omnizip/formats/ole/storage.rb +482 -0
- data/lib/omnizip/formats/ole/types/variant.rb +338 -0
- data/lib/omnizip/formats/ole.rb +143 -0
- data/lib/omnizip/formats/rar/archive_repairer.rb +240 -0
- data/lib/omnizip/formats/rar/archive_verifier.rb +192 -0
- data/lib/omnizip/formats/rar/block_parser.rb +282 -0
- data/lib/omnizip/formats/rar/compression/bit_stream.rb +180 -0
- data/lib/omnizip/formats/rar/compression/dispatcher.rb +210 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/decoder.rb +212 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/encoder.rb +154 -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/lz77_huffman.rb +25 -0
- data/lib/omnizip/formats/rar/compression/ppmd/context.rb +103 -0
- data/lib/omnizip/formats/rar/compression/ppmd/decoder.rb +238 -0
- data/lib/omnizip/formats/rar/compression/ppmd/encoder.rb +250 -0
- data/lib/omnizip/formats/rar/compression/ppmd.rb +16 -0
- data/lib/omnizip/formats/rar/compression.rb +17 -0
- data/lib/omnizip/formats/rar/compression_method_registry.rb +106 -0
- data/lib/omnizip/formats/rar/constants.rb +88 -0
- data/lib/omnizip/formats/rar/decompressor.rb +316 -0
- data/lib/omnizip/formats/rar/external_writer.rb +311 -0
- data/lib/omnizip/formats/rar/header.rb +209 -0
- data/lib/omnizip/formats/rar/license_validator.rb +111 -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/models.rb +14 -0
- data/lib/omnizip/formats/rar/parity_handler.rb +299 -0
- data/lib/omnizip/formats/rar/rar5/compression/lzma.rb +200 -0
- data/lib/omnizip/formats/rar/rar5/compression/lzss.rb +584 -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 +96 -0
- data/lib/omnizip/formats/rar/rar5/encryption/encryption_header.rb +114 -0
- data/lib/omnizip/formats/rar/rar5/encryption/encryption_manager.rb +162 -0
- data/lib/omnizip/formats/rar/rar5/encryption/key_derivation.rb +97 -0
- data/lib/omnizip/formats/rar/rar5/header.rb +208 -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 +260 -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 +176 -0
- data/lib/omnizip/formats/rar/rar5/solid/solid_encoder.rb +108 -0
- data/lib/omnizip/formats/rar/rar5/solid/solid_manager.rb +139 -0
- data/lib/omnizip/formats/rar/rar5/solid/solid_stream.rb +121 -0
- data/lib/omnizip/formats/rar/rar5/vint.rb +53 -0
- data/lib/omnizip/formats/rar/rar5/writer.rb +540 -0
- data/lib/omnizip/formats/rar/rar_format_base.rb +239 -0
- data/lib/omnizip/formats/rar/reader.rb +377 -0
- data/lib/omnizip/formats/rar/recovery_record.rb +245 -0
- data/lib/omnizip/formats/rar/volume_manager.rb +166 -0
- data/lib/omnizip/formats/rar/writer.rb +437 -0
- data/lib/omnizip/formats/rar.rb +193 -0
- data/lib/omnizip/formats/rar3/compressor.rb +71 -0
- data/lib/omnizip/formats/rar3/decompressor.rb +64 -0
- data/lib/omnizip/formats/rar3/reader.rb +107 -0
- data/lib/omnizip/formats/rar3/writer.rb +220 -0
- data/lib/omnizip/formats/rar3.rb +15 -0
- data/lib/omnizip/formats/rar5/compressor.rb +71 -0
- data/lib/omnizip/formats/rar5/decompressor.rb +64 -0
- data/lib/omnizip/formats/rar5/reader.rb +130 -0
- data/lib/omnizip/formats/rar5/writer.rb +65 -0
- data/lib/omnizip/formats/rar5.rb +15 -0
- data/lib/omnizip/formats/rpm/constants.rb +58 -0
- data/lib/omnizip/formats/rpm/entry.rb +106 -0
- data/lib/omnizip/formats/rpm/header.rb +106 -0
- data/lib/omnizip/formats/rpm/lead.rb +132 -0
- data/lib/omnizip/formats/rpm/tag.rb +228 -0
- data/lib/omnizip/formats/rpm/writer.rb +652 -0
- data/lib/omnizip/formats/rpm.rb +458 -0
- data/lib/omnizip/formats/seven_zip/bcj2_stream_decompressor.rb +274 -0
- data/lib/omnizip/formats/seven_zip/coder_chain.rb +169 -0
- data/lib/omnizip/formats/seven_zip/constants.rb +126 -0
- data/lib/omnizip/formats/seven_zip/encoded_header.rb +110 -0
- data/lib/omnizip/formats/seven_zip/encrypted_header.rb +139 -0
- data/lib/omnizip/formats/seven_zip/file_collector.rb +143 -0
- data/lib/omnizip/formats/seven_zip/header.rb +102 -0
- data/lib/omnizip/formats/seven_zip/header_encryptor.rb +132 -0
- data/lib/omnizip/formats/seven_zip/header_writer.rb +425 -0
- data/lib/omnizip/formats/seven_zip/models/coder_info.rb +30 -0
- data/lib/omnizip/formats/seven_zip/models/file_entry.rb +65 -0
- data/lib/omnizip/formats/seven_zip/models/folder.rb +67 -0
- data/lib/omnizip/formats/seven_zip/models/stream_info.rb +42 -0
- data/lib/omnizip/formats/seven_zip/models.rb +20 -0
- data/lib/omnizip/formats/seven_zip/parser.rb +671 -0
- data/lib/omnizip/formats/seven_zip/reader.rb +626 -0
- data/lib/omnizip/formats/seven_zip/split_archive_reader.rb +611 -0
- data/lib/omnizip/formats/seven_zip/split_archive_writer.rb +308 -0
- data/lib/omnizip/formats/seven_zip/stream_compressor.rb +151 -0
- data/lib/omnizip/formats/seven_zip/stream_decompressor.rb +166 -0
- data/lib/omnizip/formats/seven_zip/writer.rb +801 -0
- data/lib/omnizip/formats/seven_zip.rb +179 -0
- data/lib/omnizip/formats/tar/constants.rb +73 -0
- data/lib/omnizip/formats/tar/entry.rb +98 -0
- data/lib/omnizip/formats/tar/header.rb +206 -0
- data/lib/omnizip/formats/tar/reader.rb +119 -0
- data/lib/omnizip/formats/tar/writer.rb +212 -0
- data/lib/omnizip/formats/tar.rb +74 -0
- data/lib/omnizip/formats/xar/constants.rb +119 -0
- data/lib/omnizip/formats/xar/entry.rb +268 -0
- data/lib/omnizip/formats/xar/header.rb +223 -0
- data/lib/omnizip/formats/xar/reader.rb +400 -0
- data/lib/omnizip/formats/xar/toc.rb +570 -0
- data/lib/omnizip/formats/xar/writer.rb +461 -0
- data/lib/omnizip/formats/xar.rb +145 -0
- data/lib/omnizip/formats/xz/reader.rb +121 -0
- data/lib/omnizip/formats/xz/writer.rb +416 -0
- data/lib/omnizip/formats/xz.rb +210 -0
- data/lib/omnizip/formats/xz_const.rb +49 -0
- data/lib/omnizip/formats/xz_impl/block_decoder.rb +761 -0
- data/lib/omnizip/formats/xz_impl/block_encoder.rb +304 -0
- data/lib/omnizip/formats/xz_impl/block_header.rb +210 -0
- data/lib/omnizip/formats/xz_impl/block_header_parser.rb +182 -0
- data/lib/omnizip/formats/xz_impl/index_decoder.rb +174 -0
- data/lib/omnizip/formats/xz_impl/index_encoder.rb +120 -0
- data/lib/omnizip/formats/xz_impl/stream_decoder.rb +470 -0
- data/lib/omnizip/formats/xz_impl/stream_encoder.rb +95 -0
- data/lib/omnizip/formats/xz_impl/stream_footer.rb +79 -0
- data/lib/omnizip/formats/xz_impl/stream_footer_parser.rb +115 -0
- data/lib/omnizip/formats/xz_impl/stream_header.rb +53 -0
- data/lib/omnizip/formats/xz_impl/stream_header_parser.rb +106 -0
- data/lib/omnizip/formats/xz_impl/vli.rb +128 -0
- data/lib/omnizip/formats/xz_impl.rb +52 -0
- data/lib/omnizip/formats/zip/central_directory_header.rb +223 -0
- data/lib/omnizip/formats/zip/constants.rb +69 -0
- data/lib/omnizip/formats/zip/end_of_central_directory.rb +132 -0
- data/lib/omnizip/formats/zip/local_file_header.rb +136 -0
- data/lib/omnizip/formats/zip/reader.rb +376 -0
- data/lib/omnizip/formats/zip/unix_extra_field.rb +153 -0
- data/lib/omnizip/formats/zip/writer.rb +489 -0
- data/lib/omnizip/formats/zip/zip64_end_of_central_directory.rb +102 -0
- data/lib/omnizip/formats/zip/zip64_end_of_central_directory_locator.rb +64 -0
- data/lib/omnizip/formats/zip/zip64_extra_field.rb +112 -0
- data/lib/omnizip/formats/zip.rb +50 -0
- data/lib/omnizip/formats.rb +29 -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/base.rb +19 -0
- data/lib/omnizip/implementations/rust/archive.rb +93 -0
- data/lib/omnizip/implementations/rust/library.rb +221 -0
- data/lib/omnizip/implementations/rust.rb +26 -0
- data/lib/omnizip/implementations/seven_zip/lzma/decoder.rb +428 -0
- data/lib/omnizip/implementations/seven_zip/lzma/encoder.rb +456 -0
- data/lib/omnizip/implementations/seven_zip/lzma/match_finder.rb +285 -0
- data/lib/omnizip/implementations/seven_zip/lzma/range_decoder.rb +197 -0
- data/lib/omnizip/implementations/seven_zip/lzma/range_encoder.rb +194 -0
- data/lib/omnizip/implementations/seven_zip/lzma/state_machine.rb +139 -0
- data/lib/omnizip/implementations/seven_zip/lzma.rb +21 -0
- data/lib/omnizip/implementations/seven_zip/lzma2/encoder.rb +56 -0
- data/lib/omnizip/implementations/seven_zip/lzma2.rb +12 -0
- data/lib/omnizip/implementations/seven_zip.rb +11 -0
- data/lib/omnizip/implementations/xz_utils/lzma2/decoder.rb +547 -0
- data/lib/omnizip/implementations/xz_utils/lzma2/encoder.rb +748 -0
- data/lib/omnizip/implementations/xz_utils/lzma2.rb +13 -0
- data/lib/omnizip/implementations/xz_utils.rb +10 -0
- data/lib/omnizip/implementations.rb +25 -0
- data/lib/omnizip/io/buffered_input.rb +147 -0
- data/lib/omnizip/io/buffered_output.rb +106 -0
- data/lib/omnizip/io/source.rb +143 -0
- data/lib/omnizip/io/stream_manager.rb +117 -0
- data/lib/omnizip/io.rb +15 -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 +129 -0
- data/lib/omnizip/metadata/archive_metadata.rb +114 -0
- data/lib/omnizip/metadata/entry_metadata.rb +153 -0
- data/lib/omnizip/metadata/metadata_editor.rb +169 -0
- data/lib/omnizip/metadata/metadata_registry.rb +71 -0
- data/lib/omnizip/metadata/metadata_validator.rb +101 -0
- data/lib/omnizip/metadata.rb +58 -0
- data/lib/omnizip/models/.keep +0 -0
- data/lib/omnizip/models/algorithm_metadata.rb +41 -0
- data/lib/omnizip/models/attribute_apply.rb +40 -0
- data/lib/omnizip/models/compression_options.rb +53 -0
- data/lib/omnizip/models/conversion_options.rb +79 -0
- data/lib/omnizip/models/conversion_result.rb +141 -0
- data/lib/omnizip/models/eta_result.rb +45 -0
- data/lib/omnizip/models/extraction_rule.rb +115 -0
- data/lib/omnizip/models/filter_chain.rb +142 -0
- data/lib/omnizip/models/filter_config.rb +148 -0
- data/lib/omnizip/models/match_result.rb +141 -0
- data/lib/omnizip/models/optimization_suggestion.rb +95 -0
- data/lib/omnizip/models/parallel_options.rb +116 -0
- data/lib/omnizip/models/performance_result.rb +85 -0
- data/lib/omnizip/models/profile_report.rb +116 -0
- data/lib/omnizip/models/progress_options.rb +39 -0
- data/lib/omnizip/models/split_options.rb +116 -0
- data/lib/omnizip/optimization_registry.rb +45 -0
- data/lib/omnizip/parallel/engine.rb +52 -0
- data/lib/omnizip/parallel/job_queue.rb +209 -0
- data/lib/omnizip/parallel/job_scheduler.rb +225 -0
- data/lib/omnizip/parallel/parallel_compressor.rb +326 -0
- data/lib/omnizip/parallel/parallel_extractor.rb +238 -0
- data/lib/omnizip/parallel/thread_pool.rb +60 -0
- data/lib/omnizip/parallel/worker_pool.rb +227 -0
- data/lib/omnizip/parallel.rb +156 -0
- data/lib/omnizip/parity/chunked_block_processor.rb +194 -0
- data/lib/omnizip/parity/galois16.rb +145 -0
- data/lib/omnizip/parity/models/creator_packet.rb +71 -0
- data/lib/omnizip/parity/models/file_description_packet.rb +131 -0
- data/lib/omnizip/parity/models/ifsc_packet.rb +111 -0
- data/lib/omnizip/parity/models/main_packet.rb +126 -0
- data/lib/omnizip/parity/models/packet.rb +156 -0
- data/lib/omnizip/parity/models/packet_registry.rb +102 -0
- data/lib/omnizip/parity/models/recovery_slice_packet.rb +76 -0
- data/lib/omnizip/parity/models.rb +27 -0
- data/lib/omnizip/parity/par2_creator.rb +496 -0
- data/lib/omnizip/parity/par2_repairer.rb +409 -0
- data/lib/omnizip/parity/par2_verifier.rb +373 -0
- data/lib/omnizip/parity/par2cmdline_algorithm.rb +108 -0
- data/lib/omnizip/parity/par2cmdline_coefficients.rb +78 -0
- data/lib/omnizip/parity/reed_solomon_decoder.rb +263 -0
- data/lib/omnizip/parity/reed_solomon_encoder.rb +108 -0
- data/lib/omnizip/parity/reed_solomon_matrix.rb +339 -0
- data/lib/omnizip/parity.rb +207 -0
- data/lib/omnizip/password/encryption_registry.rb +26 -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 +190 -0
- data/lib/omnizip/password/zip_crypto_strategy.rb +135 -0
- data/lib/omnizip/password.rb +88 -0
- data/lib/omnizip/pipe/stream_compressor.rb +124 -0
- data/lib/omnizip/pipe/stream_decompressor.rb +173 -0
- data/lib/omnizip/pipe.rb +120 -0
- data/lib/omnizip/platform/ntfs_streams.rb +200 -0
- data/lib/omnizip/platform.rb +189 -0
- data/lib/omnizip/profile/archive_profile.rb +36 -0
- data/lib/omnizip/profile/balanced_profile.rb +31 -0
- data/lib/omnizip/profile/binary_profile.rb +33 -0
- data/lib/omnizip/profile/compression_profile.rb +157 -0
- data/lib/omnizip/profile/custom_profile.rb +155 -0
- data/lib/omnizip/profile/fast_profile.rb +31 -0
- data/lib/omnizip/profile/maximum_profile.rb +31 -0
- data/lib/omnizip/profile/profile_detector.rb +108 -0
- data/lib/omnizip/profile/profile_registry.rb +160 -0
- data/lib/omnizip/profile/text_profile.rb +33 -0
- data/lib/omnizip/profile.rb +261 -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 +158 -0
- data/lib/omnizip/profiler.rb +192 -0
- data/lib/omnizip/progress/callback_reporter.rb +34 -0
- data/lib/omnizip/progress/console_reporter.rb +59 -0
- data/lib/omnizip/progress/log_reporter.rb +89 -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 +215 -0
- data/lib/omnizip/progress/silent_reporter.rb +22 -0
- data/lib/omnizip/progress.rb +131 -0
- data/lib/omnizip/registry.rb +111 -0
- data/lib/omnizip/rubyzip_compat.rb +61 -0
- data/lib/omnizip/temp/safe_extract.rb +124 -0
- data/lib/omnizip/temp/temp_file.rb +123 -0
- data/lib/omnizip/temp/temp_file_pool.rb +109 -0
- data/lib/omnizip/temp.rb +184 -0
- data/lib/omnizip/version.rb +5 -0
- data/lib/omnizip/zip/entry.rb +160 -0
- data/lib/omnizip/zip/file.rb +485 -0
- data/lib/omnizip/zip/input_stream.rb +270 -0
- data/lib/omnizip/zip/output_stream.rb +320 -0
- data/lib/omnizip/zip.rb +23 -0
- data/lib/omnizip.rb +157 -0
- data/lib/tasks/msi_test_files.rake +66 -0
- data/lib/tasks/platform_gems.rake +147 -0
- data/lib/tasks/rust_backend.rake +48 -0
- data/readme-docs/advanced-features.adoc +514 -0
- data/readme-docs/api-usage.adoc +445 -0
- data/readme-docs/architecture.adoc +450 -0
- data/readme-docs/archive-formats.adoc +479 -0
- data/readme-docs/cli-usage.adoc +222 -0
- data/readme-docs/compression-algorithms.adoc +446 -0
- data/readme-docs/compression-profiles.adoc +247 -0
- data/readme-docs/encryption-checksums.adoc +328 -0
- data/readme-docs/format-converter.adoc +316 -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/vendor/libomnizip_ffi.so +0 -0
- data/xz-file-format-1.2.1.txt +1174 -0
- metadata +768 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Omnizip
|
|
4
|
+
module Formats
|
|
5
|
+
module Iso
|
|
6
|
+
# ISO 9660 Volume Descriptor
|
|
7
|
+
# Represents the primary volume descriptor containing metadata
|
|
8
|
+
class VolumeDescriptor
|
|
9
|
+
attr_reader :type, :identifier, :version, :volume_identifier,
|
|
10
|
+
:volume_space_size, :volume_set_size, :volume_sequence_number,
|
|
11
|
+
:logical_block_size, :path_table_size, :path_table_location,
|
|
12
|
+
:root_directory_record, :volume_set_identifier,
|
|
13
|
+
:publisher_identifier, :preparer_identifier,
|
|
14
|
+
:application_identifier, :creation_date, :modification_date,
|
|
15
|
+
:system_identifier
|
|
16
|
+
|
|
17
|
+
# Standard identifier for ISO 9660
|
|
18
|
+
ISO_IDENTIFIER = "CD001"
|
|
19
|
+
|
|
20
|
+
# Parse volume descriptor from binary data
|
|
21
|
+
#
|
|
22
|
+
# @param data [String] Binary sector data (2048 bytes)
|
|
23
|
+
# @return [VolumeDescriptor] Parsed descriptor
|
|
24
|
+
def self.parse(data)
|
|
25
|
+
new.tap { |vd| vd.parse(data) }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Parse descriptor data
|
|
29
|
+
#
|
|
30
|
+
# @param data [String] Binary data
|
|
31
|
+
def parse(data)
|
|
32
|
+
raise Omnizip::InvalidArchiveError, "Invalid volume descriptor size" unless data.bytesize >= 2048
|
|
33
|
+
|
|
34
|
+
# Byte 0: Volume descriptor type
|
|
35
|
+
@type = data.getbyte(0)
|
|
36
|
+
|
|
37
|
+
# Bytes 1-5: Standard identifier "CD001"
|
|
38
|
+
@identifier = data[1, 5]
|
|
39
|
+
unless @identifier == ISO_IDENTIFIER
|
|
40
|
+
raise Omnizip::InvalidArchiveError, "Invalid ISO identifier: expected #{ISO_IDENTIFIER}, got #{@identifier}"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Byte 6: Version (should be 1)
|
|
44
|
+
@version = data.getbyte(6)
|
|
45
|
+
|
|
46
|
+
# Parse based on type
|
|
47
|
+
case @type
|
|
48
|
+
when Iso::VD_PRIMARY
|
|
49
|
+
parse_primary_volume_descriptor(data)
|
|
50
|
+
when Iso::VD_TERMINATOR
|
|
51
|
+
# Terminator has no additional data
|
|
52
|
+
else
|
|
53
|
+
# Skip other types for now
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Check if this is a primary volume descriptor
|
|
58
|
+
#
|
|
59
|
+
# @return [Boolean] true if primary VD
|
|
60
|
+
def primary?
|
|
61
|
+
@type == Iso::VD_PRIMARY
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Check if this is a terminator
|
|
65
|
+
#
|
|
66
|
+
# @return [Boolean] true if terminator
|
|
67
|
+
def terminator?
|
|
68
|
+
@type == Iso::VD_TERMINATOR
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
# Parse primary volume descriptor fields
|
|
74
|
+
#
|
|
75
|
+
# @param data [String] Binary data
|
|
76
|
+
def parse_primary_volume_descriptor(data)
|
|
77
|
+
# Byte 7: Unused (0)
|
|
78
|
+
# Bytes 8-39: System identifier
|
|
79
|
+
@system_identifier = data[8, 32].strip
|
|
80
|
+
|
|
81
|
+
# Bytes 40-71: Volume identifier
|
|
82
|
+
@volume_identifier = data[40, 32].strip
|
|
83
|
+
|
|
84
|
+
# Bytes 72-79: Unused (zeros)
|
|
85
|
+
|
|
86
|
+
# Bytes 80-87: Volume space size (both-endian)
|
|
87
|
+
@volume_space_size = data[80, 4].unpack1("V")
|
|
88
|
+
|
|
89
|
+
# Bytes 88-119: Unused (zeros)
|
|
90
|
+
|
|
91
|
+
# Bytes 120-123: Volume set size (both-endian)
|
|
92
|
+
@volume_set_size = data[120, 2].unpack1("v")
|
|
93
|
+
|
|
94
|
+
# Bytes 124-127: Volume sequence number (both-endian)
|
|
95
|
+
@volume_sequence_number = data[124, 2].unpack1("v")
|
|
96
|
+
|
|
97
|
+
# Bytes 128-131: Logical block size (both-endian)
|
|
98
|
+
@logical_block_size = data[128, 2].unpack1("v")
|
|
99
|
+
|
|
100
|
+
# Bytes 132-139: Path table size (both-endian)
|
|
101
|
+
@path_table_size = data[132, 4].unpack1("V")
|
|
102
|
+
|
|
103
|
+
# Bytes 140-143: Path table location (little-endian)
|
|
104
|
+
@path_table_location = data[140, 4].unpack1("V")
|
|
105
|
+
|
|
106
|
+
# Bytes 144-147: Optional path table location
|
|
107
|
+
# Bytes 148-151: Path table location (big-endian)
|
|
108
|
+
# Bytes 152-155: Optional path table location
|
|
109
|
+
|
|
110
|
+
# Bytes 156-189: Root directory record (34 bytes)
|
|
111
|
+
@root_directory_record = DirectoryRecord.parse(data[156, 34])
|
|
112
|
+
|
|
113
|
+
# Bytes 190-317: Volume set identifier
|
|
114
|
+
@volume_set_identifier = data[190, 128].strip
|
|
115
|
+
|
|
116
|
+
# Bytes 318-445: Publisher identifier
|
|
117
|
+
@publisher_identifier = data[318, 128].strip
|
|
118
|
+
|
|
119
|
+
# Bytes 446-573: Data preparer identifier
|
|
120
|
+
@preparer_identifier = data[446, 128].strip
|
|
121
|
+
|
|
122
|
+
# Bytes 574-701: Application identifier
|
|
123
|
+
@application_identifier = data[574, 128].strip
|
|
124
|
+
|
|
125
|
+
# Bytes 702-738: Copyright file identifier
|
|
126
|
+
# Bytes 739-775: Abstract file identifier
|
|
127
|
+
# Bytes 776-812: Bibliographic file identifier
|
|
128
|
+
|
|
129
|
+
# Bytes 813-829: Volume creation date/time
|
|
130
|
+
@creation_date = parse_datetime(data[813, 17])
|
|
131
|
+
|
|
132
|
+
# Bytes 830-846: Volume modification date/time
|
|
133
|
+
@modification_date = parse_datetime(data[830, 17])
|
|
134
|
+
|
|
135
|
+
# Bytes 847-863: Volume expiration date/time
|
|
136
|
+
# Bytes 864-880: Volume effective date/time
|
|
137
|
+
|
|
138
|
+
# Byte 881: File structure version
|
|
139
|
+
# Bytes 882-1395: Application use
|
|
140
|
+
# Bytes 1396-2047: Reserved
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Parse ISO 9660 datetime format
|
|
144
|
+
#
|
|
145
|
+
# @param data [String] 17-byte datetime string
|
|
146
|
+
# @return [Time, nil] Parsed time or nil if invalid
|
|
147
|
+
def parse_datetime(data)
|
|
148
|
+
return nil if data.nil? || data.bytesize < 17
|
|
149
|
+
|
|
150
|
+
year = data[0, 4].to_i
|
|
151
|
+
month = data[4, 2].to_i
|
|
152
|
+
day = data[6, 2].to_i
|
|
153
|
+
hour = data[8, 2].to_i
|
|
154
|
+
minute = data[10, 2].to_i
|
|
155
|
+
second = data[12, 2].to_i
|
|
156
|
+
# Centiseconds at 14-15 (ignored)
|
|
157
|
+
# Timezone offset at 16 (ignored for now)
|
|
158
|
+
|
|
159
|
+
return nil if year.zero?
|
|
160
|
+
|
|
161
|
+
Time.new(year, month, day, hour, minute, second)
|
|
162
|
+
rescue ArgumentError
|
|
163
|
+
nil
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
@@ -0,0 +1,543 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "time"
|
|
5
|
+
|
|
6
|
+
module Omnizip
|
|
7
|
+
module Formats
|
|
8
|
+
module Iso
|
|
9
|
+
# ISO 9660 image writer
|
|
10
|
+
#
|
|
11
|
+
# Creates ISO 9660 filesystem images (CD/DVD images) from files and directories.
|
|
12
|
+
# Supports ISO 9660 levels 1-3, Rock Ridge, and Joliet extensions.
|
|
13
|
+
#
|
|
14
|
+
# @example Create simple ISO image
|
|
15
|
+
# writer = Iso::Writer.new('cdrom.iso')
|
|
16
|
+
# writer.add_directory('files/')
|
|
17
|
+
# writer.write
|
|
18
|
+
#
|
|
19
|
+
# @example With Rock Ridge and Joliet
|
|
20
|
+
# writer = Iso::Writer.new('backup.iso',
|
|
21
|
+
# volume_id: 'BACKUP_2024',
|
|
22
|
+
# rock_ridge: true,
|
|
23
|
+
# joliet: true
|
|
24
|
+
# )
|
|
25
|
+
# writer.add_directory('documents/')
|
|
26
|
+
# writer.write
|
|
27
|
+
class Writer
|
|
28
|
+
# @return [String] Output ISO file path
|
|
29
|
+
attr_reader :output_path
|
|
30
|
+
|
|
31
|
+
# @return [Hash] Writer options
|
|
32
|
+
attr_reader :options
|
|
33
|
+
|
|
34
|
+
# @return [String] Volume identifier (label)
|
|
35
|
+
attr_accessor :volume_id
|
|
36
|
+
|
|
37
|
+
# @return [String] System identifier
|
|
38
|
+
attr_accessor :system_id
|
|
39
|
+
|
|
40
|
+
# @return [String] Publisher
|
|
41
|
+
attr_accessor :publisher
|
|
42
|
+
|
|
43
|
+
# @return [String] Preparer
|
|
44
|
+
attr_accessor :preparer
|
|
45
|
+
|
|
46
|
+
# @return [String] Application identifier
|
|
47
|
+
attr_accessor :application
|
|
48
|
+
|
|
49
|
+
# @return [Integer] ISO 9660 level (1, 2, or 3)
|
|
50
|
+
attr_accessor :level
|
|
51
|
+
|
|
52
|
+
# @return [Boolean] Use Rock Ridge extensions
|
|
53
|
+
attr_accessor :rock_ridge
|
|
54
|
+
|
|
55
|
+
# @return [Boolean] Use Joliet extensions
|
|
56
|
+
attr_accessor :joliet
|
|
57
|
+
|
|
58
|
+
# @return [Array<Hash>] Files to add
|
|
59
|
+
attr_reader :files
|
|
60
|
+
|
|
61
|
+
# @return [Array<Hash>] Directories to add
|
|
62
|
+
attr_reader :directories
|
|
63
|
+
|
|
64
|
+
# Initialize ISO writer
|
|
65
|
+
#
|
|
66
|
+
# @param output_path [String] Output ISO file path
|
|
67
|
+
# @param options [Hash] Writer options
|
|
68
|
+
# @option options [String] :volume_id Volume label
|
|
69
|
+
# @option options [String] :system_id System identifier
|
|
70
|
+
# @option options [String] :publisher Publisher name
|
|
71
|
+
# @option options [String] :preparer Preparer name
|
|
72
|
+
# @option options [String] :application Application name
|
|
73
|
+
# @option options [Integer] :level ISO 9660 level (1-3)
|
|
74
|
+
# @option options [Boolean] :rock_ridge Enable Rock Ridge
|
|
75
|
+
# @option options [Boolean] :joliet Enable Joliet
|
|
76
|
+
def initialize(output_path, options = {})
|
|
77
|
+
@output_path = output_path
|
|
78
|
+
@options = default_options.merge(options)
|
|
79
|
+
|
|
80
|
+
@volume_id = @options[:volume_id]
|
|
81
|
+
@system_id = @options[:system_id]
|
|
82
|
+
@publisher = @options[:publisher]
|
|
83
|
+
@preparer = @options[:preparer]
|
|
84
|
+
@application = @options[:application]
|
|
85
|
+
@level = @options[:level]
|
|
86
|
+
@rock_ridge = @options[:rock_ridge]
|
|
87
|
+
@joliet = @options[:joliet]
|
|
88
|
+
|
|
89
|
+
@files = []
|
|
90
|
+
@directories = []
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Add file to ISO image
|
|
94
|
+
#
|
|
95
|
+
# @param file_path [String] Source file path
|
|
96
|
+
# @param iso_path [String, nil] Path in ISO (defaults to basename)
|
|
97
|
+
# @raise [ArgumentError] if file doesn't exist
|
|
98
|
+
def add_file(file_path, iso_path = nil)
|
|
99
|
+
raise ArgumentError, "File not found: #{file_path}" unless
|
|
100
|
+
File.exist?(file_path)
|
|
101
|
+
|
|
102
|
+
iso_path ||= File.basename(file_path)
|
|
103
|
+
|
|
104
|
+
@files << {
|
|
105
|
+
source: File.expand_path(file_path),
|
|
106
|
+
iso_path: sanitize_path(iso_path),
|
|
107
|
+
stat: File.stat(file_path),
|
|
108
|
+
}
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Add directory to ISO image
|
|
112
|
+
#
|
|
113
|
+
# @param dir_path [String] Source directory path
|
|
114
|
+
# @param recursive [Boolean] Include subdirectories
|
|
115
|
+
# @param iso_path [String, nil] Path in ISO (defaults to basename)
|
|
116
|
+
# @raise [ArgumentError] if directory doesn't exist
|
|
117
|
+
def add_directory(dir_path, recursive: true, iso_path: nil)
|
|
118
|
+
raise ArgumentError, "Directory not found: #{dir_path}" unless
|
|
119
|
+
Dir.exist?(dir_path)
|
|
120
|
+
|
|
121
|
+
iso_path ||= File.basename(dir_path)
|
|
122
|
+
iso_path = sanitize_path(iso_path)
|
|
123
|
+
|
|
124
|
+
if recursive
|
|
125
|
+
add_directory_recursive(dir_path, iso_path)
|
|
126
|
+
else
|
|
127
|
+
add_directory_flat(dir_path, iso_path)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Create ISO image
|
|
132
|
+
#
|
|
133
|
+
# @raise [IOError] if write fails
|
|
134
|
+
def write
|
|
135
|
+
File.open(@output_path, "wb") do |io|
|
|
136
|
+
# Build directory structure
|
|
137
|
+
builder = DirectoryBuilder.new(
|
|
138
|
+
@files,
|
|
139
|
+
@directories,
|
|
140
|
+
level: @level,
|
|
141
|
+
rock_ridge: @rock_ridge,
|
|
142
|
+
)
|
|
143
|
+
dir_structure = builder.build
|
|
144
|
+
|
|
145
|
+
# Build volume descriptor
|
|
146
|
+
volume_builder = VolumeBuilder.new(
|
|
147
|
+
volume_id: @volume_id,
|
|
148
|
+
system_id: @system_id,
|
|
149
|
+
publisher: @publisher,
|
|
150
|
+
preparer: @preparer,
|
|
151
|
+
application: @application,
|
|
152
|
+
level: @level,
|
|
153
|
+
rock_ridge: @rock_ridge,
|
|
154
|
+
joliet: @joliet,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
# Write ISO structure
|
|
158
|
+
write_system_area(io)
|
|
159
|
+
write_volume_descriptors(io, volume_builder, dir_structure)
|
|
160
|
+
write_path_tables(io, dir_structure)
|
|
161
|
+
write_directories(io, dir_structure)
|
|
162
|
+
write_file_data(io)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
@output_path
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
private
|
|
169
|
+
|
|
170
|
+
# Default writer options
|
|
171
|
+
#
|
|
172
|
+
# @return [Hash] Default options
|
|
173
|
+
def default_options
|
|
174
|
+
{
|
|
175
|
+
volume_id: "OMNIZIP_ISO",
|
|
176
|
+
system_id: "LINUX",
|
|
177
|
+
publisher: "OMNIZIP",
|
|
178
|
+
preparer: "OMNIZIP #{Omnizip::VERSION}",
|
|
179
|
+
application: "OMNIZIP",
|
|
180
|
+
level: 2,
|
|
181
|
+
# Rock Ridge System Use fields and Joliet UCS-2 directory
|
|
182
|
+
# trees are not implemented; advertising either extension
|
|
183
|
+
# makes readers misparse the image (e.g. 7-Zip reads the
|
|
184
|
+
# ASCII names as UCS-2 via the cloned SVD), so both stay
|
|
185
|
+
# off until real implementations land.
|
|
186
|
+
rock_ridge: false,
|
|
187
|
+
joliet: false,
|
|
188
|
+
}
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Add directory recursively
|
|
192
|
+
#
|
|
193
|
+
# @param dir_path [String] Source directory
|
|
194
|
+
# @param iso_path [String] ISO path
|
|
195
|
+
def add_directory_recursive(dir_path, iso_path)
|
|
196
|
+
# Add directory itself
|
|
197
|
+
@directories << {
|
|
198
|
+
source: File.expand_path(dir_path),
|
|
199
|
+
iso_path: iso_path,
|
|
200
|
+
stat: File.stat(dir_path),
|
|
201
|
+
directory: true,
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
# Add all contents
|
|
205
|
+
Dir.foreach(dir_path) do |entry|
|
|
206
|
+
next if [".", ".."].include?(entry)
|
|
207
|
+
|
|
208
|
+
source_path = File.join(dir_path, entry)
|
|
209
|
+
target_path = "#{iso_path}/#{entry}"
|
|
210
|
+
|
|
211
|
+
if File.directory?(source_path)
|
|
212
|
+
add_directory_recursive(source_path, target_path)
|
|
213
|
+
else
|
|
214
|
+
add_file(source_path, target_path)
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# Add directory flat (non-recursive)
|
|
220
|
+
#
|
|
221
|
+
# @param dir_path [String] Source directory
|
|
222
|
+
# @param iso_path [String] ISO path
|
|
223
|
+
def add_directory_flat(dir_path, iso_path)
|
|
224
|
+
@directories << {
|
|
225
|
+
source: File.expand_path(dir_path),
|
|
226
|
+
iso_path: iso_path,
|
|
227
|
+
stat: File.stat(dir_path),
|
|
228
|
+
directory: true,
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
Dir.foreach(dir_path) do |entry|
|
|
232
|
+
next if [".", ".."].include?(entry)
|
|
233
|
+
|
|
234
|
+
source_path = File.join(dir_path, entry)
|
|
235
|
+
next if File.directory?(source_path)
|
|
236
|
+
|
|
237
|
+
add_file(source_path, "#{iso_path}/#{entry}")
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# Sanitize path for ISO filesystem
|
|
242
|
+
#
|
|
243
|
+
# @param path [String] Input path
|
|
244
|
+
# @return [String] Sanitized path
|
|
245
|
+
def sanitize_path(path)
|
|
246
|
+
# Remove leading/trailing slashes
|
|
247
|
+
path = path.gsub(%r{^/+}, "").gsub(%r{/+$}, "")
|
|
248
|
+
|
|
249
|
+
# Normalize separators
|
|
250
|
+
path.split("/").map do |component|
|
|
251
|
+
sanitize_filename(component)
|
|
252
|
+
end.join("/")
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# Sanitize filename for ISO level
|
|
256
|
+
#
|
|
257
|
+
# @param name [String] Filename
|
|
258
|
+
# @return [String] Sanitized filename
|
|
259
|
+
def sanitize_filename(name)
|
|
260
|
+
case @level
|
|
261
|
+
when 1
|
|
262
|
+
# Level 1: 8.3 format, A-Z 0-9 _ only
|
|
263
|
+
base = name.upcase.gsub(/[^A-Z0-9_]/, "_")[0, 8]
|
|
264
|
+
ext = ""
|
|
265
|
+
if name.include?(".")
|
|
266
|
+
ext = "." + name.split(".").last.upcase.gsub(/[^A-Z0-9]/, "")[0,
|
|
267
|
+
3]
|
|
268
|
+
end
|
|
269
|
+
base + ext
|
|
270
|
+
when 2
|
|
271
|
+
# Level 2: Up to 31 chars, more relaxed
|
|
272
|
+
name.gsub(/[^A-Za-z0-9_.-]/, "_")[0, 31]
|
|
273
|
+
else
|
|
274
|
+
# Level 3: No restrictions
|
|
275
|
+
name
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
# Write system area (first 16 sectors)
|
|
280
|
+
#
|
|
281
|
+
# @param io [IO] Output IO
|
|
282
|
+
def write_system_area(io)
|
|
283
|
+
# System area is reserved, write zeros
|
|
284
|
+
io.write("\x00" * (Iso::SECTOR_SIZE * Iso::SYSTEM_AREA_SECTORS))
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
# Write volume descriptors
|
|
288
|
+
#
|
|
289
|
+
# @param io [IO] Output IO
|
|
290
|
+
# @param builder [VolumeBuilder] Volume builder
|
|
291
|
+
# @param dir_structure [Hash] Directory structure
|
|
292
|
+
def write_volume_descriptors(io, builder, dir_structure)
|
|
293
|
+
# Fixed layout: 16 PVD, 17 terminator, 18 path table (L),
|
|
294
|
+
# 19 path table (BE), directory data from sector 22.
|
|
295
|
+
root = dir_structure[:root].merge(
|
|
296
|
+
total_sectors: dir_structure[:total_sectors],
|
|
297
|
+
path_table_size: dir_structure[:path_table_size],
|
|
298
|
+
path_table_location: 18,
|
|
299
|
+
path_table_location_be: 19,
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
# Write primary volume descriptor
|
|
303
|
+
pvd = builder.build_primary(root)
|
|
304
|
+
io.write(pvd)
|
|
305
|
+
|
|
306
|
+
# Write Joliet supplementary descriptor if enabled
|
|
307
|
+
if @joliet
|
|
308
|
+
svd = builder.build_joliet(root)
|
|
309
|
+
io.write(svd)
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
# Write terminator
|
|
313
|
+
terminator = build_terminator
|
|
314
|
+
io.write(terminator)
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
# Build volume descriptor set terminator
|
|
318
|
+
#
|
|
319
|
+
# @return [String] Terminator sector (2048 bytes)
|
|
320
|
+
def build_terminator
|
|
321
|
+
sector = +""
|
|
322
|
+
sector << [Iso::VD_TERMINATOR].pack("C") # Type
|
|
323
|
+
sector << VolumeDescriptor::ISO_IDENTIFIER # "CD001"
|
|
324
|
+
sector << [1].pack("C") # Version
|
|
325
|
+
sector << ("\x00" * (Iso::SECTOR_SIZE - 7))
|
|
326
|
+
sector
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
# Write path tables
|
|
330
|
+
#
|
|
331
|
+
# @param io [IO] Output IO
|
|
332
|
+
# @param dir_structure [Hash] Directory structure
|
|
333
|
+
def write_path_tables(io, dir_structure)
|
|
334
|
+
# Path tables provide quick directory lookup
|
|
335
|
+
# For simplicity, we'll write minimal path tables
|
|
336
|
+
|
|
337
|
+
# Little-endian path table
|
|
338
|
+
path_table = build_path_table(dir_structure[:directories])
|
|
339
|
+
io.write(path_table)
|
|
340
|
+
|
|
341
|
+
# Pad to sector boundary
|
|
342
|
+
padding = Iso::SECTOR_SIZE - (path_table.bytesize % Iso::SECTOR_SIZE)
|
|
343
|
+
io.write("\x00" * padding) if padding < Iso::SECTOR_SIZE
|
|
344
|
+
|
|
345
|
+
# Big-endian path table (required by spec)
|
|
346
|
+
path_table_be = build_path_table(dir_structure[:directories],
|
|
347
|
+
big_endian: true)
|
|
348
|
+
io.write(path_table_be)
|
|
349
|
+
|
|
350
|
+
# Pad to sector boundary
|
|
351
|
+
padding = Iso::SECTOR_SIZE - (path_table_be.bytesize % Iso::SECTOR_SIZE)
|
|
352
|
+
io.write("\x00" * padding) if padding < Iso::SECTOR_SIZE
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
# Build path table
|
|
356
|
+
#
|
|
357
|
+
# @param directories [Array<Hash>] Directory list
|
|
358
|
+
# @param big_endian [Boolean] Use big-endian format
|
|
359
|
+
# @return [String] Path table data
|
|
360
|
+
def build_path_table(directories, big_endian: false)
|
|
361
|
+
table = +""
|
|
362
|
+
|
|
363
|
+
directories.each_with_index do |dir, _idx|
|
|
364
|
+
name = dir[:name]
|
|
365
|
+
parent = dir[:parent_idx] || 1
|
|
366
|
+
|
|
367
|
+
# Name length
|
|
368
|
+
table << [name.bytesize].pack("C")
|
|
369
|
+
|
|
370
|
+
# Extended attribute record length
|
|
371
|
+
table << [0].pack("C")
|
|
372
|
+
|
|
373
|
+
# Location of extent
|
|
374
|
+
pack_format = big_endian ? "N" : "V"
|
|
375
|
+
table << [dir[:location]].pack(pack_format)
|
|
376
|
+
|
|
377
|
+
# Parent directory number (1-based)
|
|
378
|
+
pack_format = big_endian ? "n" : "v"
|
|
379
|
+
table << [parent + 1].pack(pack_format)
|
|
380
|
+
|
|
381
|
+
# Directory name
|
|
382
|
+
table << name
|
|
383
|
+
|
|
384
|
+
# Pad to even length
|
|
385
|
+
table << "\x00" if name.bytesize.odd?
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
table
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
# Write directory records
|
|
392
|
+
#
|
|
393
|
+
# @param io [IO] Output IO
|
|
394
|
+
# @param dir_structure [Hash] Directory structure
|
|
395
|
+
def write_directories(io, dir_structure)
|
|
396
|
+
dir_structure[:directories].each do |dir_info|
|
|
397
|
+
write_directory_record(io, dir_info)
|
|
398
|
+
end
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
# Write single directory record
|
|
402
|
+
#
|
|
403
|
+
# @param io [IO] Output IO
|
|
404
|
+
# @param dir_info [Hash] Directory information
|
|
405
|
+
def write_directory_record(io, dir_info)
|
|
406
|
+
# Seek to directory location
|
|
407
|
+
io.seek(dir_info[:location] * Iso::SECTOR_SIZE)
|
|
408
|
+
|
|
409
|
+
# Write directory entries
|
|
410
|
+
dir_data = build_directory_data(dir_info)
|
|
411
|
+
io.write(dir_data)
|
|
412
|
+
|
|
413
|
+
# Pad to sector boundary
|
|
414
|
+
padding = Iso::SECTOR_SIZE - (dir_data.bytesize % Iso::SECTOR_SIZE)
|
|
415
|
+
io.write("\x00" * padding) if padding < Iso::SECTOR_SIZE
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
# Build directory data with all entries
|
|
419
|
+
#
|
|
420
|
+
# @param dir_info [Hash] Directory information
|
|
421
|
+
# @return [String] Directory record data
|
|
422
|
+
def build_directory_data(dir_info)
|
|
423
|
+
data = +""
|
|
424
|
+
|
|
425
|
+
# Add "." entry (current directory)
|
|
426
|
+
data << build_directory_entry("\x00", dir_info, is_self: true)
|
|
427
|
+
|
|
428
|
+
# Add ".." entry (parent directory)
|
|
429
|
+
parent = dir_info[:parent] || dir_info
|
|
430
|
+
data << build_directory_entry("\x01", parent, is_self: false)
|
|
431
|
+
|
|
432
|
+
# Add child entries
|
|
433
|
+
dir_info[:children].each do |child|
|
|
434
|
+
name = child[:name]
|
|
435
|
+
data << build_directory_entry(name, child, is_self: false)
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
data
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
# Build single directory entry
|
|
442
|
+
#
|
|
443
|
+
# @param name [String] Entry name
|
|
444
|
+
# @param entry_info [Hash] Entry information
|
|
445
|
+
# @param is_self [Boolean] Is this the "." entry
|
|
446
|
+
# @return [String] Directory entry record
|
|
447
|
+
def build_directory_entry(name, entry_info, is_self:)
|
|
448
|
+
record = +""
|
|
449
|
+
|
|
450
|
+
# Extended attribute length (0)
|
|
451
|
+
ext_attr_len = 0
|
|
452
|
+
|
|
453
|
+
# Calculate record length
|
|
454
|
+
name_len = name.bytesize
|
|
455
|
+
padding = name_len.even? ? 1 : 0
|
|
456
|
+
record_len = 33 + name_len + padding
|
|
457
|
+
|
|
458
|
+
# Byte 0: Length of directory record
|
|
459
|
+
record << [record_len].pack("C")
|
|
460
|
+
|
|
461
|
+
# Byte 1: Extended attribute record length
|
|
462
|
+
record << [ext_attr_len].pack("C")
|
|
463
|
+
|
|
464
|
+
# Bytes 2-9: Location of extent (both-endian)
|
|
465
|
+
location = entry_info[:location] || 0
|
|
466
|
+
record << [location].pack("V") # Little-endian
|
|
467
|
+
record << [location].pack("N") # Big-endian
|
|
468
|
+
|
|
469
|
+
# Bytes 10-17: Data length (both-endian)
|
|
470
|
+
data_length = entry_info[:size] || 0
|
|
471
|
+
record << [data_length].pack("V")
|
|
472
|
+
record << [data_length].pack("N")
|
|
473
|
+
|
|
474
|
+
# Bytes 18-24: Recording date and time
|
|
475
|
+
record << encode_record_datetime(entry_info[:mtime] || Time.now)
|
|
476
|
+
|
|
477
|
+
# Byte 25: File flags
|
|
478
|
+
flags = 0
|
|
479
|
+
flags |= Iso::FLAG_DIRECTORY if entry_info[:directory]
|
|
480
|
+
record << [flags].pack("C")
|
|
481
|
+
|
|
482
|
+
# Byte 26: File unit size (0 for non-interleaved)
|
|
483
|
+
record << [0].pack("C")
|
|
484
|
+
|
|
485
|
+
# Byte 27: Interleave gap size
|
|
486
|
+
record << [0].pack("C")
|
|
487
|
+
|
|
488
|
+
# Bytes 28-31: Volume sequence number (both-endian)
|
|
489
|
+
record << [1].pack("v")
|
|
490
|
+
record << [1].pack("n")
|
|
491
|
+
|
|
492
|
+
# Byte 32: Length of file identifier
|
|
493
|
+
record << [name_len].pack("C")
|
|
494
|
+
|
|
495
|
+
# Bytes 33+: File identifier
|
|
496
|
+
record << name
|
|
497
|
+
|
|
498
|
+
# Padding byte if name length is even
|
|
499
|
+
record << "\x00" if name_len.even?
|
|
500
|
+
|
|
501
|
+
record
|
|
502
|
+
end
|
|
503
|
+
|
|
504
|
+
# Encode recording date/time (7-byte format)
|
|
505
|
+
#
|
|
506
|
+
# @param time [Time] Time to encode
|
|
507
|
+
# @return [String] 7-byte encoded time
|
|
508
|
+
def encode_record_datetime(time)
|
|
509
|
+
[
|
|
510
|
+
time.year - 1900, # Years since 1900
|
|
511
|
+
time.month, # Month (1-12)
|
|
512
|
+
time.day, # Day (1-31)
|
|
513
|
+
time.hour, # Hour (0-23)
|
|
514
|
+
time.min, # Minute (0-59)
|
|
515
|
+
time.sec, # Second (0-59)
|
|
516
|
+
0, # GMT offset (0 = GMT)
|
|
517
|
+
].pack("C7")
|
|
518
|
+
end
|
|
519
|
+
|
|
520
|
+
# Write file data
|
|
521
|
+
#
|
|
522
|
+
# @param io [IO] Output IO
|
|
523
|
+
def write_file_data(io)
|
|
524
|
+
@files.each do |file_info|
|
|
525
|
+
# Seek to file location
|
|
526
|
+
io.seek(file_info[:location] * Iso::SECTOR_SIZE)
|
|
527
|
+
|
|
528
|
+
# Write file data
|
|
529
|
+
File.open(file_info[:source], "rb") do |src|
|
|
530
|
+
while (data = src.read(Iso::SECTOR_SIZE))
|
|
531
|
+
# Pad to sector boundary
|
|
532
|
+
if data.bytesize < Iso::SECTOR_SIZE
|
|
533
|
+
data += "\x00" * (Iso::SECTOR_SIZE - data.bytesize)
|
|
534
|
+
end
|
|
535
|
+
io.write(data)
|
|
536
|
+
end
|
|
537
|
+
end
|
|
538
|
+
end
|
|
539
|
+
end
|
|
540
|
+
end
|
|
541
|
+
end
|
|
542
|
+
end
|
|
543
|
+
end
|