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.
Files changed (511) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +32 -0
  4. data/.rubocop_todo.yml +754 -0
  5. data/COPYING +502 -0
  6. data/Gemfile +17 -0
  7. data/LICENSE +12 -0
  8. data/README.adoc +1045 -0
  9. data/Rakefile +12 -0
  10. data/benchmark/README.md +260 -0
  11. data/benchmark/benchmark_suite.rb +125 -0
  12. data/benchmark/compression_bench.rb +181 -0
  13. data/benchmark/filter_bench.rb +180 -0
  14. data/benchmark/models/benchmark_result.rb +59 -0
  15. data/benchmark/models/comparison_result.rb +69 -0
  16. data/benchmark/profile_suite.rb +167 -0
  17. data/benchmark/reporter.rb +150 -0
  18. data/benchmark/run_benchmarks.rb +66 -0
  19. data/benchmark/test_data.rb +137 -0
  20. data/config/formats/rar3_spec.yml +91 -0
  21. data/config/formats/rar5_spec.yml +102 -0
  22. data/docs/.github/workflows/docs.yml +142 -0
  23. data/docs/.gitignore +21 -0
  24. data/docs/.lychee.toml +67 -0
  25. data/docs/Gemfile +13 -0
  26. data/docs/RAR_WRITE_SUPPORT.md +26 -0
  27. data/docs/README.md +101 -0
  28. data/docs/_config.yml +112 -0
  29. data/docs/assets/logo.svg +1 -0
  30. data/docs/assets/omnizip-logo.pdf +1540 -11
  31. data/docs/comparison/feature-matrix.adoc +694 -0
  32. data/docs/comparison/index.adoc +113 -0
  33. data/docs/comparison/vs-7zip.adoc +309 -0
  34. data/docs/comparison/vs-peazip.adoc +77 -0
  35. data/docs/comparison/vs-rubyzip.adoc +342 -0
  36. data/docs/comparison/vs-winrar.adoc +100 -0
  37. data/docs/compatibility.adoc +579 -0
  38. data/docs/concepts/index.adoc +129 -0
  39. data/docs/developer/architecture.adoc +256 -0
  40. data/docs/developer/contributing.adoc +158 -0
  41. data/docs/developer/index.adoc +25 -0
  42. data/docs/developer/testing.adoc +212 -0
  43. data/docs/getting-started/basic-usage.adoc +271 -0
  44. data/docs/getting-started/index.adoc +42 -0
  45. data/docs/getting-started/installation.adoc +138 -0
  46. data/docs/getting-started/quick-start.adoc +185 -0
  47. data/docs/getting-started/your-first-archive.adoc +218 -0
  48. data/docs/guides/advanced-features/encryption.adoc +300 -0
  49. data/docs/guides/advanced-features/index.adoc +49 -0
  50. data/docs/guides/advanced-features/parallel-processing.adoc +246 -0
  51. data/docs/guides/advanced-features/progress-tracking.adoc +320 -0
  52. data/docs/guides/advanced-features/streaming.adoc +212 -0
  53. data/docs/guides/archive-formats/gzip-format.adoc +107 -0
  54. data/docs/guides/archive-formats/index.adoc +130 -0
  55. data/docs/guides/archive-formats/rar-format.adoc +104 -0
  56. data/docs/guides/archive-formats/rar5.adoc +521 -0
  57. data/docs/guides/archive-formats/seven-zip-format.adoc +35 -0
  58. data/docs/guides/archive-formats/tar-format.adoc +106 -0
  59. data/docs/guides/archive-formats/xz-format.adoc +118 -0
  60. data/docs/guides/archive-formats/zip-format.adoc +35 -0
  61. data/docs/guides/compression-algorithms/bzip2.adoc +113 -0
  62. data/docs/guides/compression-algorithms/deflate.adoc +319 -0
  63. data/docs/guides/compression-algorithms/index.adoc +190 -0
  64. data/docs/guides/compression-algorithms/lzma.adoc +398 -0
  65. data/docs/guides/compression-algorithms/lzma2.adoc +327 -0
  66. data/docs/guides/compression-algorithms/ppmd.adoc +316 -0
  67. data/docs/guides/compression-algorithms/zstandard.adoc +361 -0
  68. data/docs/guides/creating-archives.adoc +354 -0
  69. data/docs/guides/extracting-archives.adoc +53 -0
  70. data/docs/guides/format-conversion.adoc +64 -0
  71. data/docs/guides/index.adoc +49 -0
  72. data/docs/guides/migration-rubyzip.adoc +217 -0
  73. data/docs/guides/parity-archives.adoc +605 -0
  74. data/docs/guides/performance-tuning.adoc +88 -0
  75. data/docs/index.adoc +218 -0
  76. data/docs/lychee.toml +67 -0
  77. data/docs/reference/api/overview.adoc +188 -0
  78. data/docs/reference/cli/compress-command.adoc +114 -0
  79. data/docs/reference/cli/overview.adoc +140 -0
  80. data/docs/reference/index.adoc +26 -0
  81. data/docs/resources/faq.adoc +185 -0
  82. data/docs/resources/quick-reference.adoc +222 -0
  83. data/docs/troubleshooting/index.adoc +208 -0
  84. data/examples/api_comparison.rb +205 -0
  85. data/examples/deflate64_example.rb +96 -0
  86. data/examples/par2_demo.rb +121 -0
  87. data/examples/quick_start_native.rb +150 -0
  88. data/examples/quick_start_rubyzip.rb +115 -0
  89. data/examples/rubyzip_compatibility_demo.rb +194 -0
  90. data/exe/omnizip +27 -0
  91. data/lib/omnizip/algorithm.rb +130 -0
  92. data/lib/omnizip/algorithm_registry.rb +86 -0
  93. data/lib/omnizip/algorithms/.keep +0 -0
  94. data/lib/omnizip/algorithms/bzip2/bwt.rb +225 -0
  95. data/lib/omnizip/algorithms/bzip2/decoder.rb +193 -0
  96. data/lib/omnizip/algorithms/bzip2/encoder.rb +237 -0
  97. data/lib/omnizip/algorithms/bzip2/huffman.rb +206 -0
  98. data/lib/omnizip/algorithms/bzip2/mtf.rb +101 -0
  99. data/lib/omnizip/algorithms/bzip2/rle.rb +151 -0
  100. data/lib/omnizip/algorithms/bzip2.rb +130 -0
  101. data/lib/omnizip/algorithms/deflate/constants.rb +28 -0
  102. data/lib/omnizip/algorithms/deflate/decoder.rb +38 -0
  103. data/lib/omnizip/algorithms/deflate/encoder.rb +46 -0
  104. data/lib/omnizip/algorithms/deflate.rb +128 -0
  105. data/lib/omnizip/algorithms/deflate64/constants.rb +45 -0
  106. data/lib/omnizip/algorithms/deflate64/decoder.rb +153 -0
  107. data/lib/omnizip/algorithms/deflate64/encoder.rb +98 -0
  108. data/lib/omnizip/algorithms/deflate64/huffman_coder.rb +354 -0
  109. data/lib/omnizip/algorithms/deflate64/lz77_encoder.rb +142 -0
  110. data/lib/omnizip/algorithms/deflate64.rb +109 -0
  111. data/lib/omnizip/algorithms/lzma/bit_model.rb +120 -0
  112. data/lib/omnizip/algorithms/lzma/constants.rb +112 -0
  113. data/lib/omnizip/algorithms/lzma/decoder.rb +148 -0
  114. data/lib/omnizip/algorithms/lzma/dictionary.rb +69 -0
  115. data/lib/omnizip/algorithms/lzma/distance_coder.rb +415 -0
  116. data/lib/omnizip/algorithms/lzma/encoder.rb +142 -0
  117. data/lib/omnizip/algorithms/lzma/length_coder.rb +260 -0
  118. data/lib/omnizip/algorithms/lzma/literal_decoder.rb +320 -0
  119. data/lib/omnizip/algorithms/lzma/literal_encoder.rb +210 -0
  120. data/lib/omnizip/algorithms/lzma/lzip_decoder.rb +341 -0
  121. data/lib/omnizip/algorithms/lzma/lzma_alone_decoder.rb +192 -0
  122. data/lib/omnizip/algorithms/lzma/lzma_state.rb +128 -0
  123. data/lib/omnizip/algorithms/lzma/match.rb +32 -0
  124. data/lib/omnizip/algorithms/lzma/match_finder.rb +205 -0
  125. data/lib/omnizip/algorithms/lzma/match_finder_config.rb +142 -0
  126. data/lib/omnizip/algorithms/lzma/match_finder_factory.rb +88 -0
  127. data/lib/omnizip/algorithms/lzma/optimal_encoder.rb +130 -0
  128. data/lib/omnizip/algorithms/lzma/probability_models.rb +72 -0
  129. data/lib/omnizip/algorithms/lzma/range_coder.rb +85 -0
  130. data/lib/omnizip/algorithms/lzma/range_decoder.rb +434 -0
  131. data/lib/omnizip/algorithms/lzma/range_encoder.rb +194 -0
  132. data/lib/omnizip/algorithms/lzma/state.rb +127 -0
  133. data/lib/omnizip/algorithms/lzma/xz_buffered_range_encoder.rb +325 -0
  134. data/lib/omnizip/algorithms/lzma/xz_encoder.rb +426 -0
  135. data/lib/omnizip/algorithms/lzma/xz_encoder_fast.rb +645 -0
  136. data/lib/omnizip/algorithms/lzma/xz_match_finder_adapter.rb +227 -0
  137. data/lib/omnizip/algorithms/lzma/xz_price_calculator.rb +169 -0
  138. data/lib/omnizip/algorithms/lzma/xz_probability_models.rb +261 -0
  139. data/lib/omnizip/algorithms/lzma/xz_range_encoder.rb +223 -0
  140. data/lib/omnizip/algorithms/lzma/xz_range_encoder_exact.rb +331 -0
  141. data/lib/omnizip/algorithms/lzma/xz_state.rb +116 -0
  142. data/lib/omnizip/algorithms/lzma/xz_utils_decoder.rb +2055 -0
  143. data/lib/omnizip/algorithms/lzma.rb +238 -0
  144. data/lib/omnizip/algorithms/lzma2/chunk_manager.rb +182 -0
  145. data/lib/omnizip/algorithms/lzma2/constants.rb +41 -0
  146. data/lib/omnizip/algorithms/lzma2/encoder.rb +147 -0
  147. data/lib/omnizip/algorithms/lzma2/lzma2_chunk.rb +161 -0
  148. data/lib/omnizip/algorithms/lzma2/properties.rb +179 -0
  149. data/lib/omnizip/algorithms/lzma2/simple_lzma2_encoder.rb +127 -0
  150. data/lib/omnizip/algorithms/lzma2/xz_encoder_adapter.rb +85 -0
  151. data/lib/omnizip/algorithms/lzma2.rb +141 -0
  152. data/lib/omnizip/algorithms/ppmd7/constants.rb +74 -0
  153. data/lib/omnizip/algorithms/ppmd7/context.rb +154 -0
  154. data/lib/omnizip/algorithms/ppmd7/decoder.rb +126 -0
  155. data/lib/omnizip/algorithms/ppmd7/encoder.rb +163 -0
  156. data/lib/omnizip/algorithms/ppmd7/model.rb +248 -0
  157. data/lib/omnizip/algorithms/ppmd7/symbol_state.rb +57 -0
  158. data/lib/omnizip/algorithms/ppmd7.rb +116 -0
  159. data/lib/omnizip/algorithms/ppmd8/constants.rb +61 -0
  160. data/lib/omnizip/algorithms/ppmd8/context.rb +34 -0
  161. data/lib/omnizip/algorithms/ppmd8/decoder.rb +107 -0
  162. data/lib/omnizip/algorithms/ppmd8/encoder.rb +138 -0
  163. data/lib/omnizip/algorithms/ppmd8/model.rb +250 -0
  164. data/lib/omnizip/algorithms/ppmd8/restoration_method.rb +78 -0
  165. data/lib/omnizip/algorithms/ppmd8.rb +82 -0
  166. data/lib/omnizip/algorithms/ppmd_base.rb +138 -0
  167. data/lib/omnizip/algorithms/sevenzip_lzma2.rb +123 -0
  168. data/lib/omnizip/algorithms/xz_lzma2.rb +118 -0
  169. data/lib/omnizip/algorithms/zstandard/constants.rb +25 -0
  170. data/lib/omnizip/algorithms/zstandard/decoder.rb +46 -0
  171. data/lib/omnizip/algorithms/zstandard/encoder.rb +51 -0
  172. data/lib/omnizip/algorithms/zstandard.rb +138 -0
  173. data/lib/omnizip/buffer/memory_archive.rb +251 -0
  174. data/lib/omnizip/buffer/memory_extractor.rb +224 -0
  175. data/lib/omnizip/buffer.rb +176 -0
  176. data/lib/omnizip/checksum_registry.rb +114 -0
  177. data/lib/omnizip/checksums/crc32.rb +100 -0
  178. data/lib/omnizip/checksums/crc64.rb +101 -0
  179. data/lib/omnizip/checksums/crc_base.rb +158 -0
  180. data/lib/omnizip/checksums/verifier.rb +131 -0
  181. data/lib/omnizip/chunked/memory_manager.rb +194 -0
  182. data/lib/omnizip/chunked/reader.rb +78 -0
  183. data/lib/omnizip/chunked/writer.rb +120 -0
  184. data/lib/omnizip/chunked.rb +129 -0
  185. data/lib/omnizip/cli/output_formatter.rb +104 -0
  186. data/lib/omnizip/cli.rb +572 -0
  187. data/lib/omnizip/commands/.keep +0 -0
  188. data/lib/omnizip/commands/archive_create_command.rb +427 -0
  189. data/lib/omnizip/commands/archive_extract_command.rb +272 -0
  190. data/lib/omnizip/commands/archive_list_command.rb +218 -0
  191. data/lib/omnizip/commands/archive_repair_command.rb +131 -0
  192. data/lib/omnizip/commands/archive_verify_command.rb +117 -0
  193. data/lib/omnizip/commands/compress_command.rb +117 -0
  194. data/lib/omnizip/commands/decompress_command.rb +120 -0
  195. data/lib/omnizip/commands/list_command.rb +53 -0
  196. data/lib/omnizip/commands/metadata_command.rb +153 -0
  197. data/lib/omnizip/commands/parity_create_command.rb +122 -0
  198. data/lib/omnizip/commands/parity_repair_command.rb +122 -0
  199. data/lib/omnizip/commands/parity_verify_command.rb +124 -0
  200. data/lib/omnizip/commands/profile_list_command.rb +56 -0
  201. data/lib/omnizip/commands/profile_show_command.rb +44 -0
  202. data/lib/omnizip/convenience.rb +359 -0
  203. data/lib/omnizip/converter/conversion_registry.rb +49 -0
  204. data/lib/omnizip/converter/conversion_strategy.rb +121 -0
  205. data/lib/omnizip/converter/seven_zip_to_zip_strategy.rb +97 -0
  206. data/lib/omnizip/converter/zip_to_seven_zip_strategy.rb +112 -0
  207. data/lib/omnizip/converter.rb +105 -0
  208. data/lib/omnizip/crypto/aes256/cipher.rb +100 -0
  209. data/lib/omnizip/crypto/aes256/constants.rb +28 -0
  210. data/lib/omnizip/crypto/aes256/key_derivation.rb +101 -0
  211. data/lib/omnizip/crypto/aes256.rb +102 -0
  212. data/lib/omnizip/error.rb +106 -0
  213. data/lib/omnizip/eta/exponential_smoothing_estimator.rb +98 -0
  214. data/lib/omnizip/eta/moving_average_estimator.rb +99 -0
  215. data/lib/omnizip/eta/rate_calculator.rb +104 -0
  216. data/lib/omnizip/eta/sample_history.rb +143 -0
  217. data/lib/omnizip/eta/time_estimator.rb +106 -0
  218. data/lib/omnizip/eta.rb +63 -0
  219. data/lib/omnizip/extraction/filter_chain.rb +177 -0
  220. data/lib/omnizip/extraction/glob_pattern.rb +140 -0
  221. data/lib/omnizip/extraction/pattern_matcher.rb +70 -0
  222. data/lib/omnizip/extraction/predicate_pattern.rb +52 -0
  223. data/lib/omnizip/extraction/regex_pattern.rb +50 -0
  224. data/lib/omnizip/extraction/selective_extractor.rb +240 -0
  225. data/lib/omnizip/extraction.rb +111 -0
  226. data/lib/omnizip/file_type/mime_classifier.rb +144 -0
  227. data/lib/omnizip/file_type.rb +113 -0
  228. data/lib/omnizip/filter.rb +139 -0
  229. data/lib/omnizip/filter_pipeline.rb +108 -0
  230. data/lib/omnizip/filter_registry.rb +166 -0
  231. data/lib/omnizip/filters/bcj.rb +279 -0
  232. data/lib/omnizip/filters/bcj2/constants.rb +53 -0
  233. data/lib/omnizip/filters/bcj2/decoder.rb +200 -0
  234. data/lib/omnizip/filters/bcj2/encoder.rb +61 -0
  235. data/lib/omnizip/filters/bcj2/stream_data.rb +93 -0
  236. data/lib/omnizip/filters/bcj2.rb +99 -0
  237. data/lib/omnizip/filters/bcj_arm.rb +176 -0
  238. data/lib/omnizip/filters/bcj_arm64.rb +244 -0
  239. data/lib/omnizip/filters/bcj_ia64.rb +196 -0
  240. data/lib/omnizip/filters/bcj_ppc.rb +190 -0
  241. data/lib/omnizip/filters/bcj_sparc.rb +176 -0
  242. data/lib/omnizip/filters/bcj_x86.rb +193 -0
  243. data/lib/omnizip/filters/delta.rb +196 -0
  244. data/lib/omnizip/filters/filter_base.rb +72 -0
  245. data/lib/omnizip/filters/registry.rb +123 -0
  246. data/lib/omnizip/filters/xz_delta.rb +258 -0
  247. data/lib/omnizip/format_detector.rb +162 -0
  248. data/lib/omnizip/format_registry.rb +59 -0
  249. data/lib/omnizip/formats/.keep +0 -0
  250. data/lib/omnizip/formats/bzip2_file.rb +172 -0
  251. data/lib/omnizip/formats/cpio/constants.rb +55 -0
  252. data/lib/omnizip/formats/cpio/entry.rb +385 -0
  253. data/lib/omnizip/formats/cpio/reader.rb +196 -0
  254. data/lib/omnizip/formats/cpio/writer.rb +234 -0
  255. data/lib/omnizip/formats/cpio.rb +140 -0
  256. data/lib/omnizip/formats/format_spec_loader.rb +230 -0
  257. data/lib/omnizip/formats/gzip.rb +238 -0
  258. data/lib/omnizip/formats/iso/directory_builder.rb +297 -0
  259. data/lib/omnizip/formats/iso/directory_record.rb +152 -0
  260. data/lib/omnizip/formats/iso/joliet.rb +204 -0
  261. data/lib/omnizip/formats/iso/path_table.rb +125 -0
  262. data/lib/omnizip/formats/iso/reader.rb +197 -0
  263. data/lib/omnizip/formats/iso/rock_ridge.rb +349 -0
  264. data/lib/omnizip/formats/iso/volume_builder.rb +320 -0
  265. data/lib/omnizip/formats/iso/volume_descriptor.rb +168 -0
  266. data/lib/omnizip/formats/iso/writer.rb +530 -0
  267. data/lib/omnizip/formats/iso.rb +140 -0
  268. data/lib/omnizip/formats/lzip.rb +175 -0
  269. data/lib/omnizip/formats/lzma_alone.rb +171 -0
  270. data/lib/omnizip/formats/rar/archive_repairer.rb +243 -0
  271. data/lib/omnizip/formats/rar/archive_verifier.rb +195 -0
  272. data/lib/omnizip/formats/rar/block_parser.rb +243 -0
  273. data/lib/omnizip/formats/rar/compression/bit_stream.rb +180 -0
  274. data/lib/omnizip/formats/rar/compression/dispatcher.rb +217 -0
  275. data/lib/omnizip/formats/rar/compression/lz77_huffman/decoder.rb +216 -0
  276. data/lib/omnizip/formats/rar/compression/lz77_huffman/encoder.rb +158 -0
  277. data/lib/omnizip/formats/rar/compression/lz77_huffman/huffman_builder.rb +217 -0
  278. data/lib/omnizip/formats/rar/compression/lz77_huffman/huffman_coder.rb +189 -0
  279. data/lib/omnizip/formats/rar/compression/lz77_huffman/match_finder.rb +135 -0
  280. data/lib/omnizip/formats/rar/compression/lz77_huffman/sliding_window.rb +165 -0
  281. data/lib/omnizip/formats/rar/compression/ppmd/context.rb +105 -0
  282. data/lib/omnizip/formats/rar/compression/ppmd/decoder.rb +219 -0
  283. data/lib/omnizip/formats/rar/compression/ppmd/encoder.rb +262 -0
  284. data/lib/omnizip/formats/rar/compression_method_registry.rb +106 -0
  285. data/lib/omnizip/formats/rar/constants.rb +82 -0
  286. data/lib/omnizip/formats/rar/decompressor.rb +238 -0
  287. data/lib/omnizip/formats/rar/external_writer.rb +312 -0
  288. data/lib/omnizip/formats/rar/header.rb +192 -0
  289. data/lib/omnizip/formats/rar/license_validator.rb +109 -0
  290. data/lib/omnizip/formats/rar/models/rar_archive.rb +77 -0
  291. data/lib/omnizip/formats/rar/models/rar_entry.rb +65 -0
  292. data/lib/omnizip/formats/rar/models/rar_volume.rb +56 -0
  293. data/lib/omnizip/formats/rar/parity_handler.rb +292 -0
  294. data/lib/omnizip/formats/rar/rar5/compression/lzma.rb +202 -0
  295. data/lib/omnizip/formats/rar/rar5/compression/lzss.rb +578 -0
  296. data/lib/omnizip/formats/rar/rar5/compression/store.rb +60 -0
  297. data/lib/omnizip/formats/rar/rar5/crc32.rb +39 -0
  298. data/lib/omnizip/formats/rar/rar5/encryption/aes256_cbc.rb +97 -0
  299. data/lib/omnizip/formats/rar/rar5/encryption/encryption_header.rb +114 -0
  300. data/lib/omnizip/formats/rar/rar5/encryption/encryption_manager.rb +166 -0
  301. data/lib/omnizip/formats/rar/rar5/encryption/key_derivation.rb +97 -0
  302. data/lib/omnizip/formats/rar/rar5/header.rb +187 -0
  303. data/lib/omnizip/formats/rar/rar5/models/encryption_options.rb +74 -0
  304. data/lib/omnizip/formats/rar/rar5/models/recovery_options.rb +63 -0
  305. data/lib/omnizip/formats/rar/rar5/models/solid_options.rb +63 -0
  306. data/lib/omnizip/formats/rar/rar5/models/volume_options.rb +74 -0
  307. data/lib/omnizip/formats/rar/rar5/multi_volume/ARCHITECTURE.md +290 -0
  308. data/lib/omnizip/formats/rar/rar5/multi_volume/volume_manager.rb +264 -0
  309. data/lib/omnizip/formats/rar/rar5/multi_volume/volume_splitter.rb +155 -0
  310. data/lib/omnizip/formats/rar/rar5/multi_volume/volume_writer.rb +194 -0
  311. data/lib/omnizip/formats/rar/rar5/solid/solid_encoder.rb +109 -0
  312. data/lib/omnizip/formats/rar/rar5/solid/solid_manager.rb +142 -0
  313. data/lib/omnizip/formats/rar/rar5/solid/solid_stream.rb +121 -0
  314. data/lib/omnizip/formats/rar/rar5/vint.rb +65 -0
  315. data/lib/omnizip/formats/rar/rar5/writer.rb +466 -0
  316. data/lib/omnizip/formats/rar/rar_format_base.rb +241 -0
  317. data/lib/omnizip/formats/rar/reader.rb +366 -0
  318. data/lib/omnizip/formats/rar/recovery_record.rb +245 -0
  319. data/lib/omnizip/formats/rar/volume_manager.rb +168 -0
  320. data/lib/omnizip/formats/rar/writer.rb +431 -0
  321. data/lib/omnizip/formats/rar.rb +205 -0
  322. data/lib/omnizip/formats/rar3/compressor.rb +73 -0
  323. data/lib/omnizip/formats/rar3/decompressor.rb +66 -0
  324. data/lib/omnizip/formats/rar3/reader.rb +386 -0
  325. data/lib/omnizip/formats/rar3/writer.rb +219 -0
  326. data/lib/omnizip/formats/rar5/compressor.rb +73 -0
  327. data/lib/omnizip/formats/rar5/decompressor.rb +66 -0
  328. data/lib/omnizip/formats/rar5/reader.rb +342 -0
  329. data/lib/omnizip/formats/rar5/writer.rb +214 -0
  330. data/lib/omnizip/formats/seven_zip/coder_chain.rb +150 -0
  331. data/lib/omnizip/formats/seven_zip/constants.rb +126 -0
  332. data/lib/omnizip/formats/seven_zip/encoded_header.rb +114 -0
  333. data/lib/omnizip/formats/seven_zip/encrypted_header.rb +142 -0
  334. data/lib/omnizip/formats/seven_zip/file_collector.rb +144 -0
  335. data/lib/omnizip/formats/seven_zip/header.rb +106 -0
  336. data/lib/omnizip/formats/seven_zip/header_encryptor.rb +134 -0
  337. data/lib/omnizip/formats/seven_zip/header_writer.rb +466 -0
  338. data/lib/omnizip/formats/seven_zip/models/coder_info.rb +30 -0
  339. data/lib/omnizip/formats/seven_zip/models/file_entry.rb +58 -0
  340. data/lib/omnizip/formats/seven_zip/models/folder.rb +69 -0
  341. data/lib/omnizip/formats/seven_zip/models/stream_info.rb +42 -0
  342. data/lib/omnizip/formats/seven_zip/parser.rb +660 -0
  343. data/lib/omnizip/formats/seven_zip/reader.rb +458 -0
  344. data/lib/omnizip/formats/seven_zip/split_archive_reader.rb +632 -0
  345. data/lib/omnizip/formats/seven_zip/split_archive_writer.rb +315 -0
  346. data/lib/omnizip/formats/seven_zip/stream_compressor.rb +151 -0
  347. data/lib/omnizip/formats/seven_zip/stream_decompressor.rb +162 -0
  348. data/lib/omnizip/formats/seven_zip/writer.rb +740 -0
  349. data/lib/omnizip/formats/seven_zip.rb +93 -0
  350. data/lib/omnizip/formats/tar/constants.rb +73 -0
  351. data/lib/omnizip/formats/tar/entry.rb +94 -0
  352. data/lib/omnizip/formats/tar/header.rb +168 -0
  353. data/lib/omnizip/formats/tar/reader.rb +121 -0
  354. data/lib/omnizip/formats/tar/writer.rb +216 -0
  355. data/lib/omnizip/formats/tar.rb +84 -0
  356. data/lib/omnizip/formats/xz/reader.rb +116 -0
  357. data/lib/omnizip/formats/xz.rb +237 -0
  358. data/lib/omnizip/formats/xz_impl/block_decoder.rb +754 -0
  359. data/lib/omnizip/formats/xz_impl/block_encoder.rb +306 -0
  360. data/lib/omnizip/formats/xz_impl/block_header.rb +210 -0
  361. data/lib/omnizip/formats/xz_impl/block_header_parser.rb +186 -0
  362. data/lib/omnizip/formats/xz_impl/constants.rb +49 -0
  363. data/lib/omnizip/formats/xz_impl/index_decoder.rb +174 -0
  364. data/lib/omnizip/formats/xz_impl/index_encoder.rb +122 -0
  365. data/lib/omnizip/formats/xz_impl/stream_decoder.rb +468 -0
  366. data/lib/omnizip/formats/xz_impl/stream_encoder.rb +99 -0
  367. data/lib/omnizip/formats/xz_impl/stream_footer.rb +81 -0
  368. data/lib/omnizip/formats/xz_impl/stream_footer_parser.rb +117 -0
  369. data/lib/omnizip/formats/xz_impl/stream_header.rb +55 -0
  370. data/lib/omnizip/formats/xz_impl/stream_header_parser.rb +108 -0
  371. data/lib/omnizip/formats/xz_impl/vli.rb +128 -0
  372. data/lib/omnizip/formats/xz_impl/writer.rb +421 -0
  373. data/lib/omnizip/formats/zip/central_directory_header.rb +195 -0
  374. data/lib/omnizip/formats/zip/constants.rb +69 -0
  375. data/lib/omnizip/formats/zip/end_of_central_directory.rb +133 -0
  376. data/lib/omnizip/formats/zip/local_file_header.rb +138 -0
  377. data/lib/omnizip/formats/zip/reader.rb +250 -0
  378. data/lib/omnizip/formats/zip/unix_extra_field.rb +153 -0
  379. data/lib/omnizip/formats/zip/writer.rb +375 -0
  380. data/lib/omnizip/formats/zip/zip64_end_of_central_directory.rb +104 -0
  381. data/lib/omnizip/formats/zip/zip64_end_of_central_directory_locator.rb +66 -0
  382. data/lib/omnizip/formats/zip/zip64_extra_field.rb +114 -0
  383. data/lib/omnizip/formats/zip.rb +50 -0
  384. data/lib/omnizip/implementations/base/lzma2_decoder_base.rb +75 -0
  385. data/lib/omnizip/implementations/base/lzma2_encoder_base.rb +128 -0
  386. data/lib/omnizip/implementations/base/lzma_decoder_base.rb +83 -0
  387. data/lib/omnizip/implementations/base/lzma_encoder_base.rb +108 -0
  388. data/lib/omnizip/implementations/base/state_machine_base.rb +182 -0
  389. data/lib/omnizip/implementations/seven_zip/lzma/decoder.rb +421 -0
  390. data/lib/omnizip/implementations/seven_zip/lzma/encoder.rb +465 -0
  391. data/lib/omnizip/implementations/seven_zip/lzma/match_finder.rb +288 -0
  392. data/lib/omnizip/implementations/seven_zip/lzma/range_decoder.rb +200 -0
  393. data/lib/omnizip/implementations/seven_zip/lzma/range_encoder.rb +197 -0
  394. data/lib/omnizip/implementations/seven_zip/lzma/state_machine.rb +141 -0
  395. data/lib/omnizip/implementations/seven_zip/lzma2/encoder.rb +519 -0
  396. data/lib/omnizip/implementations/xz_utils/lzma2/decoder.rb +723 -0
  397. data/lib/omnizip/implementations/xz_utils/lzma2/encoder.rb +750 -0
  398. data/lib/omnizip/io/buffered_input.rb +146 -0
  399. data/lib/omnizip/io/buffered_output.rb +105 -0
  400. data/lib/omnizip/io/stream_manager.rb +115 -0
  401. data/lib/omnizip/link_handler/hard_link.rb +79 -0
  402. data/lib/omnizip/link_handler/symbolic_link.rb +74 -0
  403. data/lib/omnizip/link_handler.rb +124 -0
  404. data/lib/omnizip/metadata/archive_metadata.rb +114 -0
  405. data/lib/omnizip/metadata/entry_metadata.rb +146 -0
  406. data/lib/omnizip/metadata/metadata_editor.rb +171 -0
  407. data/lib/omnizip/metadata/metadata_registry.rb +64 -0
  408. data/lib/omnizip/metadata/metadata_validator.rb +99 -0
  409. data/lib/omnizip/metadata.rb +57 -0
  410. data/lib/omnizip/models/.keep +0 -0
  411. data/lib/omnizip/models/algorithm_metadata.rb +73 -0
  412. data/lib/omnizip/models/compression_options.rb +71 -0
  413. data/lib/omnizip/models/conversion_options.rb +87 -0
  414. data/lib/omnizip/models/conversion_result.rb +135 -0
  415. data/lib/omnizip/models/eta_result.rb +46 -0
  416. data/lib/omnizip/models/extraction_rule.rb +115 -0
  417. data/lib/omnizip/models/filter_chain.rb +144 -0
  418. data/lib/omnizip/models/filter_config.rb +183 -0
  419. data/lib/omnizip/models/match_result.rb +124 -0
  420. data/lib/omnizip/models/optimization_suggestion.rb +91 -0
  421. data/lib/omnizip/models/parallel_options.rb +104 -0
  422. data/lib/omnizip/models/performance_result.rb +79 -0
  423. data/lib/omnizip/models/profile_report.rb +82 -0
  424. data/lib/omnizip/models/progress_options.rb +38 -0
  425. data/lib/omnizip/models/split_options.rb +116 -0
  426. data/lib/omnizip/optimization_registry.rb +81 -0
  427. data/lib/omnizip/parallel/job_queue.rb +209 -0
  428. data/lib/omnizip/parallel/job_scheduler.rb +203 -0
  429. data/lib/omnizip/parallel/parallel_compressor.rb +347 -0
  430. data/lib/omnizip/parallel/parallel_extractor.rb +329 -0
  431. data/lib/omnizip/parallel/worker_pool.rb +223 -0
  432. data/lib/omnizip/parallel.rb +149 -0
  433. data/lib/omnizip/parity/chunked_block_processor.rb +196 -0
  434. data/lib/omnizip/parity/galois16.rb +145 -0
  435. data/lib/omnizip/parity/models/creator_packet.rb +73 -0
  436. data/lib/omnizip/parity/models/file_description_packet.rb +133 -0
  437. data/lib/omnizip/parity/models/ifsc_packet.rb +123 -0
  438. data/lib/omnizip/parity/models/main_packet.rb +128 -0
  439. data/lib/omnizip/parity/models/packet.rb +156 -0
  440. data/lib/omnizip/parity/models/packet_registry.rb +109 -0
  441. data/lib/omnizip/parity/models/recovery_slice_packet.rb +78 -0
  442. data/lib/omnizip/parity/par2_creator.rb +531 -0
  443. data/lib/omnizip/parity/par2_repairer.rb +407 -0
  444. data/lib/omnizip/parity/par2_verifier.rb +364 -0
  445. data/lib/omnizip/parity/par2cmdline_algorithm.rb +110 -0
  446. data/lib/omnizip/parity/par2cmdline_coefficients.rb +78 -0
  447. data/lib/omnizip/parity/reed_solomon_decoder.rb +266 -0
  448. data/lib/omnizip/parity/reed_solomon_encoder.rb +111 -0
  449. data/lib/omnizip/parity/reed_solomon_matrix.rb +342 -0
  450. data/lib/omnizip/parity.rb +186 -0
  451. data/lib/omnizip/password/encryption_registry.rb +65 -0
  452. data/lib/omnizip/password/encryption_strategy.rb +96 -0
  453. data/lib/omnizip/password/password_validator.rb +129 -0
  454. data/lib/omnizip/password/winzip_aes_strategy.rb +192 -0
  455. data/lib/omnizip/password/zip_crypto_strategy.rb +141 -0
  456. data/lib/omnizip/password.rb +87 -0
  457. data/lib/omnizip/pipe/stream_compressor.rb +124 -0
  458. data/lib/omnizip/pipe/stream_decompressor.rb +174 -0
  459. data/lib/omnizip/pipe.rb +121 -0
  460. data/lib/omnizip/platform/ntfs_streams.rb +201 -0
  461. data/lib/omnizip/platform.rb +189 -0
  462. data/lib/omnizip/profile/archive_profile.rb +39 -0
  463. data/lib/omnizip/profile/balanced_profile.rb +33 -0
  464. data/lib/omnizip/profile/binary_profile.rb +36 -0
  465. data/lib/omnizip/profile/compression_profile.rb +158 -0
  466. data/lib/omnizip/profile/custom_profile.rb +157 -0
  467. data/lib/omnizip/profile/fast_profile.rb +33 -0
  468. data/lib/omnizip/profile/maximum_profile.rb +33 -0
  469. data/lib/omnizip/profile/profile_detector.rb +110 -0
  470. data/lib/omnizip/profile/profile_registry.rb +161 -0
  471. data/lib/omnizip/profile/text_profile.rb +36 -0
  472. data/lib/omnizip/profile.rb +190 -0
  473. data/lib/omnizip/profiler/memory_profiler.rb +66 -0
  474. data/lib/omnizip/profiler/method_profiler.rb +49 -0
  475. data/lib/omnizip/profiler/report_generator.rb +169 -0
  476. data/lib/omnizip/profiler.rb +204 -0
  477. data/lib/omnizip/progress/callback_reporter.rb +36 -0
  478. data/lib/omnizip/progress/console_reporter.rb +62 -0
  479. data/lib/omnizip/progress/log_reporter.rb +91 -0
  480. data/lib/omnizip/progress/operation_progress.rb +118 -0
  481. data/lib/omnizip/progress/progress_bar.rb +156 -0
  482. data/lib/omnizip/progress/progress_reporter.rb +40 -0
  483. data/lib/omnizip/progress/progress_tracker.rb +190 -0
  484. data/lib/omnizip/progress/silent_reporter.rb +24 -0
  485. data/lib/omnizip/progress.rb +127 -0
  486. data/lib/omnizip/rubyzip_compat.rb +63 -0
  487. data/lib/omnizip/temp/safe_extract.rb +168 -0
  488. data/lib/omnizip/temp/temp_file.rb +124 -0
  489. data/lib/omnizip/temp/temp_file_pool.rb +109 -0
  490. data/lib/omnizip/temp.rb +181 -0
  491. data/lib/omnizip/version.rb +5 -0
  492. data/lib/omnizip/zip/entry.rb +156 -0
  493. data/lib/omnizip/zip/file.rb +485 -0
  494. data/lib/omnizip/zip/input_stream.rb +273 -0
  495. data/lib/omnizip/zip/output_stream.rb +324 -0
  496. data/lib/omnizip.rb +156 -0
  497. data/readme-docs/advanced-features.adoc +515 -0
  498. data/readme-docs/api-usage.adoc +444 -0
  499. data/readme-docs/architecture.adoc +449 -0
  500. data/readme-docs/archive-formats.adoc +479 -0
  501. data/readme-docs/cli-usage.adoc +222 -0
  502. data/readme-docs/compression-algorithms.adoc +442 -0
  503. data/readme-docs/compression-profiles.adoc +247 -0
  504. data/readme-docs/encryption-checksums.adoc +328 -0
  505. data/readme-docs/format-converter.adoc +325 -0
  506. data/readme-docs/installation.adoc +228 -0
  507. data/readme-docs/par2-archives.adoc +608 -0
  508. data/readme-docs/performance-profiler.adoc +389 -0
  509. data/readme-docs/preprocessing-filters.adoc +280 -0
  510. data/xz-file-format-1.2.1.txt +1174 -0
  511. 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!