omnizip 0.4.7-aarch64-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.
Files changed (632) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +32 -0
  4. data/.rubocop_todo.yml +293 -0
  5. data/CHANGELOG.md +1647 -0
  6. data/CONTEXT.md +68 -0
  7. data/COPYING +502 -0
  8. data/Gemfile +19 -0
  9. data/LICENSE +12 -0
  10. data/README.adoc +1327 -0
  11. data/Rakefile +15 -0
  12. data/TODO.refactor/00-overview.md +67 -0
  13. data/TODO.refactor/01-registry-base-class.md +81 -0
  14. data/TODO.refactor/02-error-hierarchy.md +57 -0
  15. data/TODO.refactor/03-autoload-entry-point.md +69 -0
  16. data/TODO.refactor/04-filter-base-consolidation.md +49 -0
  17. data/TODO.refactor/05-send-private-methods.md +64 -0
  18. data/TODO.refactor/06-instance-variable-access.md +69 -0
  19. data/TODO.refactor/07-respond-to-replacement.md +237 -0
  20. data/TODO.refactor/08-spec-doubles.md +27 -0
  21. data/TODO.refactor/09-cli-shared-module.md +35 -0
  22. data/TODO.refactor/10-format-detector-ocp.md +34 -0
  23. data/TODO.refactor/11-convenience-decoupling.md +38 -0
  24. data/TODO.refactor/12-thread-safety.md +8 -0
  25. data/TODO.refactor/13-lutaml-model-migration.md +158 -0
  26. data/TODO.refactor/14-add-missing-specs.md +25 -0
  27. data/benchmark/README.md +260 -0
  28. data/benchmark/benchmark_suite.rb +125 -0
  29. data/benchmark/compression_bench.rb +181 -0
  30. data/benchmark/filter_bench.rb +180 -0
  31. data/benchmark/models/benchmark_result.rb +59 -0
  32. data/benchmark/models/comparison_result.rb +69 -0
  33. data/benchmark/profile_suite.rb +167 -0
  34. data/benchmark/reporter.rb +150 -0
  35. data/benchmark/run_benchmarks.rb +66 -0
  36. data/benchmark/test_data.rb +137 -0
  37. data/config/formats/rar3_spec.yml +92 -0
  38. data/config/formats/rar5_spec.yml +102 -0
  39. data/docs/.github/workflows/docs.yml +142 -0
  40. data/docs/.gitignore +21 -0
  41. data/docs/.lychee.toml +67 -0
  42. data/docs/Gemfile +13 -0
  43. data/docs/RAR_WRITE_SUPPORT.md +26 -0
  44. data/docs/README.md +101 -0
  45. data/docs/_config.yml +112 -0
  46. data/docs/assets/logo.svg +1 -0
  47. data/docs/assets/omnizip-logo.pdf +1540 -11
  48. data/docs/comparison/feature-matrix.adoc +694 -0
  49. data/docs/comparison/index.adoc +113 -0
  50. data/docs/comparison/vs-7zip.adoc +309 -0
  51. data/docs/comparison/vs-peazip.adoc +77 -0
  52. data/docs/comparison/vs-rubyzip.adoc +342 -0
  53. data/docs/comparison/vs-winrar.adoc +100 -0
  54. data/docs/compatibility.adoc +579 -0
  55. data/docs/concepts/index.adoc +129 -0
  56. data/docs/developer/architecture.adoc +256 -0
  57. data/docs/developer/contributing.adoc +158 -0
  58. data/docs/developer/index.adoc +25 -0
  59. data/docs/developer/testing.adoc +212 -0
  60. data/docs/getting-started/basic-usage.adoc +263 -0
  61. data/docs/getting-started/index.adoc +42 -0
  62. data/docs/getting-started/installation.adoc +138 -0
  63. data/docs/getting-started/quick-start.adoc +183 -0
  64. data/docs/getting-started/your-first-archive.adoc +219 -0
  65. data/docs/guides/advanced-features/encryption.adoc +277 -0
  66. data/docs/guides/advanced-features/index.adoc +59 -0
  67. data/docs/guides/advanced-features/parallel-processing.adoc +144 -0
  68. data/docs/guides/advanced-features/progress-tracking.adoc +124 -0
  69. data/docs/guides/advanced-features/streaming.adoc +142 -0
  70. data/docs/guides/archive-formats/gzip-format.adoc +105 -0
  71. data/docs/guides/archive-formats/index.adoc +160 -0
  72. data/docs/guides/archive-formats/ole-format.adoc +151 -0
  73. data/docs/guides/archive-formats/rar-format.adoc +104 -0
  74. data/docs/guides/archive-formats/rar5.adoc +521 -0
  75. data/docs/guides/archive-formats/rpm-format.adoc +108 -0
  76. data/docs/guides/archive-formats/seven-zip-format.adoc +35 -0
  77. data/docs/guides/archive-formats/tar-format.adoc +106 -0
  78. data/docs/guides/archive-formats/xz-format.adoc +118 -0
  79. data/docs/guides/archive-formats/zip-format.adoc +35 -0
  80. data/docs/guides/compression-algorithms/bzip2.adoc +114 -0
  81. data/docs/guides/compression-algorithms/deflate.adoc +313 -0
  82. data/docs/guides/compression-algorithms/index.adoc +190 -0
  83. data/docs/guides/compression-algorithms/lzma.adoc +394 -0
  84. data/docs/guides/compression-algorithms/lzma2.adoc +244 -0
  85. data/docs/guides/compression-algorithms/ppmd.adoc +313 -0
  86. data/docs/guides/compression-algorithms/zstandard.adoc +296 -0
  87. data/docs/guides/creating-archives.adoc +348 -0
  88. data/docs/guides/extracting-archives.adoc +53 -0
  89. data/docs/guides/format-conversion.adoc +64 -0
  90. data/docs/guides/index.adoc +49 -0
  91. data/docs/guides/migration-rubyzip.adoc +217 -0
  92. data/docs/guides/parity-archives.adoc +605 -0
  93. data/docs/guides/performance-tuning.adoc +88 -0
  94. data/docs/index.adoc +228 -0
  95. data/docs/lychee.toml +67 -0
  96. data/docs/reference/api/overview.adoc +187 -0
  97. data/docs/reference/cli/compress-command.adoc +114 -0
  98. data/docs/reference/cli/overview.adoc +140 -0
  99. data/docs/reference/index.adoc +26 -0
  100. data/docs/resources/faq.adoc +185 -0
  101. data/docs/resources/quick-reference.adoc +222 -0
  102. data/docs/rust-backend.md +0 -0
  103. data/docs/troubleshooting/index.adoc +207 -0
  104. data/docs/xar_format.md +216 -0
  105. data/examples/api_comparison.rb +205 -0
  106. data/examples/deflate64_example.rb +96 -0
  107. data/examples/par2_demo.rb +121 -0
  108. data/examples/quick_start_native.rb +150 -0
  109. data/examples/quick_start_rubyzip.rb +115 -0
  110. data/examples/rubyzip_compatibility_demo.rb +194 -0
  111. data/exe/omnizip +27 -0
  112. data/lib/omnizip/algorithm.rb +176 -0
  113. data/lib/omnizip/algorithm_registry.rb +65 -0
  114. data/lib/omnizip/algorithms/.keep +0 -0
  115. data/lib/omnizip/algorithms/bzip2/bwt.rb +236 -0
  116. data/lib/omnizip/algorithms/bzip2/bz2.rb +687 -0
  117. data/lib/omnizip/algorithms/bzip2/rle.rb +146 -0
  118. data/lib/omnizip/algorithms/bzip2.rb +108 -0
  119. data/lib/omnizip/algorithms/deflate/constants.rb +28 -0
  120. data/lib/omnizip/algorithms/deflate/decoder.rb +37 -0
  121. data/lib/omnizip/algorithms/deflate/encoder.rb +45 -0
  122. data/lib/omnizip/algorithms/deflate.rb +135 -0
  123. data/lib/omnizip/algorithms/deflate64/constants.rb +45 -0
  124. data/lib/omnizip/algorithms/deflate64/decoder.rb +151 -0
  125. data/lib/omnizip/algorithms/deflate64/encoder.rb +95 -0
  126. data/lib/omnizip/algorithms/deflate64/huffman_coder.rb +352 -0
  127. data/lib/omnizip/algorithms/deflate64/lz77_encoder.rb +140 -0
  128. data/lib/omnizip/algorithms/deflate64.rb +118 -0
  129. data/lib/omnizip/algorithms/lzma/bit_model.rb +120 -0
  130. data/lib/omnizip/algorithms/lzma/constants.rb +112 -0
  131. data/lib/omnizip/algorithms/lzma/decoder.rb +146 -0
  132. data/lib/omnizip/algorithms/lzma/dictionary.rb +70 -0
  133. data/lib/omnizip/algorithms/lzma/distance_coder.rb +326 -0
  134. data/lib/omnizip/algorithms/lzma/encoder.rb +139 -0
  135. data/lib/omnizip/algorithms/lzma/length_coder.rb +172 -0
  136. data/lib/omnizip/algorithms/lzma/literal_decoder.rb +204 -0
  137. data/lib/omnizip/algorithms/lzma/literal_encoder.rb +208 -0
  138. data/lib/omnizip/algorithms/lzma/lzip_decoder.rb +368 -0
  139. data/lib/omnizip/algorithms/lzma/lzma1_encoder.rb +105 -0
  140. data/lib/omnizip/algorithms/lzma/lzma_alone_decoder.rb +194 -0
  141. data/lib/omnizip/algorithms/lzma/lzma_state.rb +128 -0
  142. data/lib/omnizip/algorithms/lzma/match.rb +32 -0
  143. data/lib/omnizip/algorithms/lzma/match_finder.rb +233 -0
  144. data/lib/omnizip/algorithms/lzma/match_finder_config.rb +142 -0
  145. data/lib/omnizip/algorithms/lzma/match_finder_factory.rb +84 -0
  146. data/lib/omnizip/algorithms/lzma/optimal_encoder.rb +159 -0
  147. data/lib/omnizip/algorithms/lzma/probability_models.rb +72 -0
  148. data/lib/omnizip/algorithms/lzma/range_coder.rb +82 -0
  149. data/lib/omnizip/algorithms/lzma/range_decoder.rb +274 -0
  150. data/lib/omnizip/algorithms/lzma/range_encoder.rb +202 -0
  151. data/lib/omnizip/algorithms/lzma/state.rb +127 -0
  152. data/lib/omnizip/algorithms/lzma/xz_buffered_range_encoder.rb +323 -0
  153. data/lib/omnizip/algorithms/lzma/xz_encoder.rb +420 -0
  154. data/lib/omnizip/algorithms/lzma/xz_encoder_fast.rb +640 -0
  155. data/lib/omnizip/algorithms/lzma/xz_match_finder_adapter.rb +224 -0
  156. data/lib/omnizip/algorithms/lzma/xz_price_calculator.rb +167 -0
  157. data/lib/omnizip/algorithms/lzma/xz_probability_models.rb +258 -0
  158. data/lib/omnizip/algorithms/lzma/xz_range_encoder.rb +223 -0
  159. data/lib/omnizip/algorithms/lzma/xz_range_encoder_exact.rb +314 -0
  160. data/lib/omnizip/algorithms/lzma/xz_state.rb +108 -0
  161. data/lib/omnizip/algorithms/lzma/xz_utils_decoder.rb +1311 -0
  162. data/lib/omnizip/algorithms/lzma.rb +266 -0
  163. data/lib/omnizip/algorithms/lzma2/chunk_manager.rb +180 -0
  164. data/lib/omnizip/algorithms/lzma2/constants.rb +38 -0
  165. data/lib/omnizip/algorithms/lzma2/encoder.rb +145 -0
  166. data/lib/omnizip/algorithms/lzma2/lzma2_chunk.rb +165 -0
  167. data/lib/omnizip/algorithms/lzma2/properties.rb +179 -0
  168. data/lib/omnizip/algorithms/lzma2/simple_lzma2_encoder.rb +122 -0
  169. data/lib/omnizip/algorithms/lzma2/xz_encoder_adapter.rb +85 -0
  170. data/lib/omnizip/algorithms/lzma2.rb +133 -0
  171. data/lib/omnizip/algorithms/ppmd7/constants.rb +74 -0
  172. data/lib/omnizip/algorithms/ppmd7/context.rb +151 -0
  173. data/lib/omnizip/algorithms/ppmd7/decoder.rb +127 -0
  174. data/lib/omnizip/algorithms/ppmd7/encoder.rb +153 -0
  175. data/lib/omnizip/algorithms/ppmd7/model.rb +245 -0
  176. data/lib/omnizip/algorithms/ppmd7/symbol_state.rb +57 -0
  177. data/lib/omnizip/algorithms/ppmd7.rb +146 -0
  178. data/lib/omnizip/algorithms/ppmd8/constants.rb +61 -0
  179. data/lib/omnizip/algorithms/ppmd8/context.rb +32 -0
  180. data/lib/omnizip/algorithms/ppmd8/decoder.rb +104 -0
  181. data/lib/omnizip/algorithms/ppmd8/encoder.rb +135 -0
  182. data/lib/omnizip/algorithms/ppmd8/model.rb +246 -0
  183. data/lib/omnizip/algorithms/ppmd8/restoration_method.rb +78 -0
  184. data/lib/omnizip/algorithms/ppmd8.rb +122 -0
  185. data/lib/omnizip/algorithms/ppmd_base.rb +142 -0
  186. data/lib/omnizip/algorithms/registration.rb +28 -0
  187. data/lib/omnizip/algorithms/zstandard/constants.rb +158 -0
  188. data/lib/omnizip/algorithms/zstandard/decoder.rb +249 -0
  189. data/lib/omnizip/algorithms/zstandard/dictionary.rb +73 -0
  190. data/lib/omnizip/algorithms/zstandard/encoder.rb +294 -0
  191. data/lib/omnizip/algorithms/zstandard/frame/block.rb +143 -0
  192. data/lib/omnizip/algorithms/zstandard/frame/header.rb +233 -0
  193. data/lib/omnizip/algorithms/zstandard/frame.rb +33 -0
  194. data/lib/omnizip/algorithms/zstandard/fse/bitstream.rb +183 -0
  195. data/lib/omnizip/algorithms/zstandard/fse/encoder.rb +534 -0
  196. data/lib/omnizip/algorithms/zstandard/fse/interleaved.rb +92 -0
  197. data/lib/omnizip/algorithms/zstandard/fse/table.rb +188 -0
  198. data/lib/omnizip/algorithms/zstandard/fse/table_description.rb +203 -0
  199. data/lib/omnizip/algorithms/zstandard/fse.rb +53 -0
  200. data/lib/omnizip/algorithms/zstandard/huffman.rb +276 -0
  201. data/lib/omnizip/algorithms/zstandard/huffman_encoder.rb +327 -0
  202. data/lib/omnizip/algorithms/zstandard/ldm.rb +121 -0
  203. data/lib/omnizip/algorithms/zstandard/literals.rb +241 -0
  204. data/lib/omnizip/algorithms/zstandard/literals_encoder.rb +96 -0
  205. data/lib/omnizip/algorithms/zstandard/match_finder.rb +483 -0
  206. data/lib/omnizip/algorithms/zstandard/sequences.rb +350 -0
  207. data/lib/omnizip/algorithms/zstandard/sequences_encoder.rb +333 -0
  208. data/lib/omnizip/algorithms/zstandard/xxhash.rb +159 -0
  209. data/lib/omnizip/algorithms/zstandard.rb +210 -0
  210. data/lib/omnizip/algorithms.rb +35 -0
  211. data/lib/omnizip/archive/builder.rb +48 -0
  212. data/lib/omnizip/archive/reader_session.rb +67 -0
  213. data/lib/omnizip/archive.rb +86 -0
  214. data/lib/omnizip/archive_handler.rb +82 -0
  215. data/lib/omnizip/archive_handlers/cpio_handler.rb +47 -0
  216. data/lib/omnizip/archive_handlers/iso_handler.rb +54 -0
  217. data/lib/omnizip/archive_handlers/ole_handler.rb +37 -0
  218. data/lib/omnizip/archive_handlers/rar_handler.rb +96 -0
  219. data/lib/omnizip/archive_handlers/rpm_handler.rb +45 -0
  220. data/lib/omnizip/archive_handlers/seven_zip_handler.rb +89 -0
  221. data/lib/omnizip/archive_handlers/tar_handler.rb +75 -0
  222. data/lib/omnizip/archive_handlers/xar_handler.rb +49 -0
  223. data/lib/omnizip/archive_handlers/zip_handler.rb +115 -0
  224. data/lib/omnizip/archive_handlers.rb +18 -0
  225. data/lib/omnizip/backends.rb +160 -0
  226. data/lib/omnizip/buffer/memory_archive.rb +246 -0
  227. data/lib/omnizip/buffer/memory_extractor.rb +234 -0
  228. data/lib/omnizip/buffer/seven_zip_bridge.rb +171 -0
  229. data/lib/omnizip/buffer.rb +187 -0
  230. data/lib/omnizip/checksum_registry.rb +41 -0
  231. data/lib/omnizip/checksums/crc32.rb +101 -0
  232. data/lib/omnizip/checksums/crc64.rb +102 -0
  233. data/lib/omnizip/checksums/crc_base.rb +158 -0
  234. data/lib/omnizip/checksums/registration.rb +16 -0
  235. data/lib/omnizip/checksums/verifier.rb +130 -0
  236. data/lib/omnizip/checksums.rb +11 -0
  237. data/lib/omnizip/chunked/memory_manager.rb +194 -0
  238. data/lib/omnizip/chunked/reader.rb +78 -0
  239. data/lib/omnizip/chunked/writer.rb +123 -0
  240. data/lib/omnizip/chunked.rb +123 -0
  241. data/lib/omnizip/cli/output_formatter.rb +112 -0
  242. data/lib/omnizip/cli.rb +604 -0
  243. data/lib/omnizip/commands/.keep +0 -0
  244. data/lib/omnizip/commands/archive_create_command.rb +245 -0
  245. data/lib/omnizip/commands/archive_extract_command.rb +231 -0
  246. data/lib/omnizip/commands/archive_list_command.rb +204 -0
  247. data/lib/omnizip/commands/archive_repair_command.rb +129 -0
  248. data/lib/omnizip/commands/archive_verify_command.rb +115 -0
  249. data/lib/omnizip/commands/compress_command.rb +115 -0
  250. data/lib/omnizip/commands/decompress_command.rb +149 -0
  251. data/lib/omnizip/commands/list_command.rb +51 -0
  252. data/lib/omnizip/commands/metadata_command.rb +215 -0
  253. data/lib/omnizip/commands/parity_create_command.rb +114 -0
  254. data/lib/omnizip/commands/parity_repair_command.rb +120 -0
  255. data/lib/omnizip/commands/parity_verify_command.rb +122 -0
  256. data/lib/omnizip/commands/profile_list_command.rb +54 -0
  257. data/lib/omnizip/commands/profile_show_command.rb +44 -0
  258. data/lib/omnizip/commands.rb +21 -0
  259. data/lib/omnizip/convenience.rb +389 -0
  260. data/lib/omnizip/converter/conversion_registry.rb +50 -0
  261. data/lib/omnizip/converter/conversion_strategy.rb +108 -0
  262. data/lib/omnizip/converter/extract_repack_strategy.rb +51 -0
  263. data/lib/omnizip/converter/seven_zip_to_zip_strategy.rb +43 -0
  264. data/lib/omnizip/converter/zip_to_seven_zip_strategy.rb +54 -0
  265. data/lib/omnizip/converter.rb +124 -0
  266. data/lib/omnizip/crypto/aes256/cipher.rb +104 -0
  267. data/lib/omnizip/crypto/aes256/constants.rb +28 -0
  268. data/lib/omnizip/crypto/aes256/key_derivation.rb +100 -0
  269. data/lib/omnizip/crypto/aes256.rb +102 -0
  270. data/lib/omnizip/crypto.rb +11 -0
  271. data/lib/omnizip/entry.rb +44 -0
  272. data/lib/omnizip/error.rb +110 -0
  273. data/lib/omnizip/eta/exponential_smoothing_estimator.rb +95 -0
  274. data/lib/omnizip/eta/moving_average_estimator.rb +96 -0
  275. data/lib/omnizip/eta/rate_calculator.rb +104 -0
  276. data/lib/omnizip/eta/sample_history.rb +143 -0
  277. data/lib/omnizip/eta/time_estimator.rb +106 -0
  278. data/lib/omnizip/eta.rb +68 -0
  279. data/lib/omnizip/extraction/filter_chain.rb +179 -0
  280. data/lib/omnizip/extraction/glob_pattern.rb +140 -0
  281. data/lib/omnizip/extraction/pattern_matcher.rb +66 -0
  282. data/lib/omnizip/extraction/predicate_pattern.rb +52 -0
  283. data/lib/omnizip/extraction/regex_pattern.rb +50 -0
  284. data/lib/omnizip/extraction/selective_extractor.rb +247 -0
  285. data/lib/omnizip/extraction.rb +115 -0
  286. data/lib/omnizip/file_type/mime_classifier.rb +144 -0
  287. data/lib/omnizip/file_type.rb +117 -0
  288. data/lib/omnizip/filter.rb +141 -0
  289. data/lib/omnizip/filter_pipeline.rb +108 -0
  290. data/lib/omnizip/filter_registry.rb +113 -0
  291. data/lib/omnizip/filters/bcj.rb +277 -0
  292. data/lib/omnizip/filters/bcj2/constants.rb +53 -0
  293. data/lib/omnizip/filters/bcj2/decoder.rb +263 -0
  294. data/lib/omnizip/filters/bcj2/encoder.rb +58 -0
  295. data/lib/omnizip/filters/bcj2/stream_data.rb +91 -0
  296. data/lib/omnizip/filters/bcj2.rb +93 -0
  297. data/lib/omnizip/filters/bcj_arm.rb +174 -0
  298. data/lib/omnizip/filters/bcj_arm64.rb +242 -0
  299. data/lib/omnizip/filters/bcj_ia64.rb +194 -0
  300. data/lib/omnizip/filters/bcj_ppc.rb +188 -0
  301. data/lib/omnizip/filters/bcj_sparc.rb +174 -0
  302. data/lib/omnizip/filters/bcj_x86.rb +191 -0
  303. data/lib/omnizip/filters/delta.rb +195 -0
  304. data/lib/omnizip/filters/xz_delta.rb +258 -0
  305. data/lib/omnizip/filters.rb +20 -0
  306. data/lib/omnizip/format_detector.rb +166 -0
  307. data/lib/omnizip/formats/.keep +0 -0
  308. data/lib/omnizip/formats/bzip2_file.rb +159 -0
  309. data/lib/omnizip/formats/cpio/bounded_io.rb +66 -0
  310. data/lib/omnizip/formats/cpio/constants.rb +55 -0
  311. data/lib/omnizip/formats/cpio/entry.rb +389 -0
  312. data/lib/omnizip/formats/cpio/reader.rb +194 -0
  313. data/lib/omnizip/formats/cpio/writer.rb +232 -0
  314. data/lib/omnizip/formats/cpio.rb +132 -0
  315. data/lib/omnizip/formats/format_spec_loader.rb +228 -0
  316. data/lib/omnizip/formats/gzip.rb +236 -0
  317. data/lib/omnizip/formats/iso/directory_builder.rb +321 -0
  318. data/lib/omnizip/formats/iso/directory_record.rb +162 -0
  319. data/lib/omnizip/formats/iso/joliet.rb +204 -0
  320. data/lib/omnizip/formats/iso/path_table.rb +125 -0
  321. data/lib/omnizip/formats/iso/reader.rb +194 -0
  322. data/lib/omnizip/formats/iso/rock_ridge.rb +349 -0
  323. data/lib/omnizip/formats/iso/volume_builder.rb +319 -0
  324. data/lib/omnizip/formats/iso/volume_descriptor.rb +168 -0
  325. data/lib/omnizip/formats/iso/writer.rb +543 -0
  326. data/lib/omnizip/formats/iso.rb +133 -0
  327. data/lib/omnizip/formats/lzip.rb +161 -0
  328. data/lib/omnizip/formats/lzma_alone.rb +149 -0
  329. data/lib/omnizip/formats/msi/cab_extractor.rb +181 -0
  330. data/lib/omnizip/formats/msi/constants.rb +157 -0
  331. data/lib/omnizip/formats/msi/directory_resolver.rb +188 -0
  332. data/lib/omnizip/formats/msi/entry.rb +134 -0
  333. data/lib/omnizip/formats/msi/reader.rb +301 -0
  334. data/lib/omnizip/formats/msi/string_pool.rb +246 -0
  335. data/lib/omnizip/formats/msi/table_parser.rb +244 -0
  336. data/lib/omnizip/formats/msi.rb +73 -0
  337. data/lib/omnizip/formats/ole/allocation_table.rb +263 -0
  338. data/lib/omnizip/formats/ole/constants.rb +61 -0
  339. data/lib/omnizip/formats/ole/dirent.rb +439 -0
  340. data/lib/omnizip/formats/ole/header.rb +199 -0
  341. data/lib/omnizip/formats/ole/ranges_io.rb +347 -0
  342. data/lib/omnizip/formats/ole/storage.rb +482 -0
  343. data/lib/omnizip/formats/ole/types/variant.rb +338 -0
  344. data/lib/omnizip/formats/ole.rb +143 -0
  345. data/lib/omnizip/formats/rar/archive_repairer.rb +240 -0
  346. data/lib/omnizip/formats/rar/archive_verifier.rb +192 -0
  347. data/lib/omnizip/formats/rar/block_parser.rb +282 -0
  348. data/lib/omnizip/formats/rar/compression/bit_stream.rb +180 -0
  349. data/lib/omnizip/formats/rar/compression/dispatcher.rb +210 -0
  350. data/lib/omnizip/formats/rar/compression/lz77_huffman/decoder.rb +212 -0
  351. data/lib/omnizip/formats/rar/compression/lz77_huffman/encoder.rb +154 -0
  352. data/lib/omnizip/formats/rar/compression/lz77_huffman/huffman_builder.rb +217 -0
  353. data/lib/omnizip/formats/rar/compression/lz77_huffman/huffman_coder.rb +189 -0
  354. data/lib/omnizip/formats/rar/compression/lz77_huffman/match_finder.rb +135 -0
  355. data/lib/omnizip/formats/rar/compression/lz77_huffman/sliding_window.rb +165 -0
  356. data/lib/omnizip/formats/rar/compression/lz77_huffman.rb +25 -0
  357. data/lib/omnizip/formats/rar/compression/ppmd/context.rb +103 -0
  358. data/lib/omnizip/formats/rar/compression/ppmd/decoder.rb +238 -0
  359. data/lib/omnizip/formats/rar/compression/ppmd/encoder.rb +250 -0
  360. data/lib/omnizip/formats/rar/compression/ppmd.rb +16 -0
  361. data/lib/omnizip/formats/rar/compression.rb +17 -0
  362. data/lib/omnizip/formats/rar/compression_method_registry.rb +106 -0
  363. data/lib/omnizip/formats/rar/constants.rb +88 -0
  364. data/lib/omnizip/formats/rar/decompressor.rb +316 -0
  365. data/lib/omnizip/formats/rar/external_writer.rb +311 -0
  366. data/lib/omnizip/formats/rar/header.rb +209 -0
  367. data/lib/omnizip/formats/rar/license_validator.rb +111 -0
  368. data/lib/omnizip/formats/rar/models/rar_archive.rb +77 -0
  369. data/lib/omnizip/formats/rar/models/rar_entry.rb +65 -0
  370. data/lib/omnizip/formats/rar/models/rar_volume.rb +56 -0
  371. data/lib/omnizip/formats/rar/models.rb +14 -0
  372. data/lib/omnizip/formats/rar/parity_handler.rb +299 -0
  373. data/lib/omnizip/formats/rar/rar5/compression/lzma.rb +200 -0
  374. data/lib/omnizip/formats/rar/rar5/compression/lzss.rb +584 -0
  375. data/lib/omnizip/formats/rar/rar5/compression/store.rb +60 -0
  376. data/lib/omnizip/formats/rar/rar5/crc32.rb +39 -0
  377. data/lib/omnizip/formats/rar/rar5/encryption/aes256_cbc.rb +96 -0
  378. data/lib/omnizip/formats/rar/rar5/encryption/encryption_header.rb +114 -0
  379. data/lib/omnizip/formats/rar/rar5/encryption/encryption_manager.rb +162 -0
  380. data/lib/omnizip/formats/rar/rar5/encryption/key_derivation.rb +97 -0
  381. data/lib/omnizip/formats/rar/rar5/header.rb +208 -0
  382. data/lib/omnizip/formats/rar/rar5/models/encryption_options.rb +74 -0
  383. data/lib/omnizip/formats/rar/rar5/models/recovery_options.rb +63 -0
  384. data/lib/omnizip/formats/rar/rar5/models/solid_options.rb +63 -0
  385. data/lib/omnizip/formats/rar/rar5/models/volume_options.rb +74 -0
  386. data/lib/omnizip/formats/rar/rar5/multi_volume/ARCHITECTURE.md +290 -0
  387. data/lib/omnizip/formats/rar/rar5/multi_volume/volume_manager.rb +260 -0
  388. data/lib/omnizip/formats/rar/rar5/multi_volume/volume_splitter.rb +155 -0
  389. data/lib/omnizip/formats/rar/rar5/multi_volume/volume_writer.rb +176 -0
  390. data/lib/omnizip/formats/rar/rar5/solid/solid_encoder.rb +108 -0
  391. data/lib/omnizip/formats/rar/rar5/solid/solid_manager.rb +139 -0
  392. data/lib/omnizip/formats/rar/rar5/solid/solid_stream.rb +121 -0
  393. data/lib/omnizip/formats/rar/rar5/vint.rb +53 -0
  394. data/lib/omnizip/formats/rar/rar5/writer.rb +540 -0
  395. data/lib/omnizip/formats/rar/rar_format_base.rb +239 -0
  396. data/lib/omnizip/formats/rar/reader.rb +377 -0
  397. data/lib/omnizip/formats/rar/recovery_record.rb +245 -0
  398. data/lib/omnizip/formats/rar/volume_manager.rb +166 -0
  399. data/lib/omnizip/formats/rar/writer.rb +437 -0
  400. data/lib/omnizip/formats/rar.rb +193 -0
  401. data/lib/omnizip/formats/rar3/compressor.rb +71 -0
  402. data/lib/omnizip/formats/rar3/decompressor.rb +64 -0
  403. data/lib/omnizip/formats/rar3/reader.rb +107 -0
  404. data/lib/omnizip/formats/rar3/writer.rb +220 -0
  405. data/lib/omnizip/formats/rar3.rb +15 -0
  406. data/lib/omnizip/formats/rar5/compressor.rb +71 -0
  407. data/lib/omnizip/formats/rar5/decompressor.rb +64 -0
  408. data/lib/omnizip/formats/rar5/reader.rb +130 -0
  409. data/lib/omnizip/formats/rar5/writer.rb +65 -0
  410. data/lib/omnizip/formats/rar5.rb +15 -0
  411. data/lib/omnizip/formats/rpm/constants.rb +58 -0
  412. data/lib/omnizip/formats/rpm/entry.rb +106 -0
  413. data/lib/omnizip/formats/rpm/header.rb +106 -0
  414. data/lib/omnizip/formats/rpm/lead.rb +132 -0
  415. data/lib/omnizip/formats/rpm/tag.rb +228 -0
  416. data/lib/omnizip/formats/rpm/writer.rb +652 -0
  417. data/lib/omnizip/formats/rpm.rb +458 -0
  418. data/lib/omnizip/formats/seven_zip/bcj2_stream_decompressor.rb +274 -0
  419. data/lib/omnizip/formats/seven_zip/coder_chain.rb +169 -0
  420. data/lib/omnizip/formats/seven_zip/constants.rb +126 -0
  421. data/lib/omnizip/formats/seven_zip/encoded_header.rb +110 -0
  422. data/lib/omnizip/formats/seven_zip/encrypted_header.rb +139 -0
  423. data/lib/omnizip/formats/seven_zip/file_collector.rb +143 -0
  424. data/lib/omnizip/formats/seven_zip/header.rb +102 -0
  425. data/lib/omnizip/formats/seven_zip/header_encryptor.rb +132 -0
  426. data/lib/omnizip/formats/seven_zip/header_writer.rb +425 -0
  427. data/lib/omnizip/formats/seven_zip/models/coder_info.rb +30 -0
  428. data/lib/omnizip/formats/seven_zip/models/file_entry.rb +65 -0
  429. data/lib/omnizip/formats/seven_zip/models/folder.rb +67 -0
  430. data/lib/omnizip/formats/seven_zip/models/stream_info.rb +42 -0
  431. data/lib/omnizip/formats/seven_zip/models.rb +20 -0
  432. data/lib/omnizip/formats/seven_zip/parser.rb +671 -0
  433. data/lib/omnizip/formats/seven_zip/reader.rb +626 -0
  434. data/lib/omnizip/formats/seven_zip/split_archive_reader.rb +611 -0
  435. data/lib/omnizip/formats/seven_zip/split_archive_writer.rb +308 -0
  436. data/lib/omnizip/formats/seven_zip/stream_compressor.rb +151 -0
  437. data/lib/omnizip/formats/seven_zip/stream_decompressor.rb +166 -0
  438. data/lib/omnizip/formats/seven_zip/writer.rb +801 -0
  439. data/lib/omnizip/formats/seven_zip.rb +179 -0
  440. data/lib/omnizip/formats/tar/constants.rb +73 -0
  441. data/lib/omnizip/formats/tar/entry.rb +98 -0
  442. data/lib/omnizip/formats/tar/header.rb +206 -0
  443. data/lib/omnizip/formats/tar/reader.rb +119 -0
  444. data/lib/omnizip/formats/tar/writer.rb +212 -0
  445. data/lib/omnizip/formats/tar.rb +74 -0
  446. data/lib/omnizip/formats/xar/constants.rb +119 -0
  447. data/lib/omnizip/formats/xar/entry.rb +268 -0
  448. data/lib/omnizip/formats/xar/header.rb +223 -0
  449. data/lib/omnizip/formats/xar/reader.rb +400 -0
  450. data/lib/omnizip/formats/xar/toc.rb +570 -0
  451. data/lib/omnizip/formats/xar/writer.rb +461 -0
  452. data/lib/omnizip/formats/xar.rb +145 -0
  453. data/lib/omnizip/formats/xz/reader.rb +121 -0
  454. data/lib/omnizip/formats/xz/writer.rb +416 -0
  455. data/lib/omnizip/formats/xz.rb +210 -0
  456. data/lib/omnizip/formats/xz_const.rb +49 -0
  457. data/lib/omnizip/formats/xz_impl/block_decoder.rb +761 -0
  458. data/lib/omnizip/formats/xz_impl/block_encoder.rb +304 -0
  459. data/lib/omnizip/formats/xz_impl/block_header.rb +210 -0
  460. data/lib/omnizip/formats/xz_impl/block_header_parser.rb +182 -0
  461. data/lib/omnizip/formats/xz_impl/index_decoder.rb +174 -0
  462. data/lib/omnizip/formats/xz_impl/index_encoder.rb +120 -0
  463. data/lib/omnizip/formats/xz_impl/stream_decoder.rb +470 -0
  464. data/lib/omnizip/formats/xz_impl/stream_encoder.rb +95 -0
  465. data/lib/omnizip/formats/xz_impl/stream_footer.rb +79 -0
  466. data/lib/omnizip/formats/xz_impl/stream_footer_parser.rb +115 -0
  467. data/lib/omnizip/formats/xz_impl/stream_header.rb +53 -0
  468. data/lib/omnizip/formats/xz_impl/stream_header_parser.rb +106 -0
  469. data/lib/omnizip/formats/xz_impl/vli.rb +128 -0
  470. data/lib/omnizip/formats/xz_impl.rb +52 -0
  471. data/lib/omnizip/formats/zip/central_directory_header.rb +223 -0
  472. data/lib/omnizip/formats/zip/constants.rb +69 -0
  473. data/lib/omnizip/formats/zip/end_of_central_directory.rb +132 -0
  474. data/lib/omnizip/formats/zip/local_file_header.rb +136 -0
  475. data/lib/omnizip/formats/zip/reader.rb +376 -0
  476. data/lib/omnizip/formats/zip/unix_extra_field.rb +153 -0
  477. data/lib/omnizip/formats/zip/writer.rb +489 -0
  478. data/lib/omnizip/formats/zip/zip64_end_of_central_directory.rb +102 -0
  479. data/lib/omnizip/formats/zip/zip64_end_of_central_directory_locator.rb +64 -0
  480. data/lib/omnizip/formats/zip/zip64_extra_field.rb +112 -0
  481. data/lib/omnizip/formats/zip.rb +50 -0
  482. data/lib/omnizip/formats.rb +29 -0
  483. data/lib/omnizip/implementations/base/lzma2_decoder_base.rb +75 -0
  484. data/lib/omnizip/implementations/base/lzma2_encoder_base.rb +128 -0
  485. data/lib/omnizip/implementations/base/lzma_decoder_base.rb +83 -0
  486. data/lib/omnizip/implementations/base/lzma_encoder_base.rb +108 -0
  487. data/lib/omnizip/implementations/base/state_machine_base.rb +182 -0
  488. data/lib/omnizip/implementations/base.rb +19 -0
  489. data/lib/omnizip/implementations/rust/archive.rb +93 -0
  490. data/lib/omnizip/implementations/rust/library.rb +221 -0
  491. data/lib/omnizip/implementations/rust.rb +26 -0
  492. data/lib/omnizip/implementations/seven_zip/lzma/decoder.rb +428 -0
  493. data/lib/omnizip/implementations/seven_zip/lzma/encoder.rb +456 -0
  494. data/lib/omnizip/implementations/seven_zip/lzma/match_finder.rb +285 -0
  495. data/lib/omnizip/implementations/seven_zip/lzma/range_decoder.rb +197 -0
  496. data/lib/omnizip/implementations/seven_zip/lzma/range_encoder.rb +194 -0
  497. data/lib/omnizip/implementations/seven_zip/lzma/state_machine.rb +139 -0
  498. data/lib/omnizip/implementations/seven_zip/lzma.rb +21 -0
  499. data/lib/omnizip/implementations/seven_zip/lzma2/encoder.rb +56 -0
  500. data/lib/omnizip/implementations/seven_zip/lzma2.rb +12 -0
  501. data/lib/omnizip/implementations/seven_zip.rb +11 -0
  502. data/lib/omnizip/implementations/xz_utils/lzma2/decoder.rb +547 -0
  503. data/lib/omnizip/implementations/xz_utils/lzma2/encoder.rb +748 -0
  504. data/lib/omnizip/implementations/xz_utils/lzma2.rb +13 -0
  505. data/lib/omnizip/implementations/xz_utils.rb +10 -0
  506. data/lib/omnizip/implementations.rb +25 -0
  507. data/lib/omnizip/io/buffered_input.rb +147 -0
  508. data/lib/omnizip/io/buffered_output.rb +106 -0
  509. data/lib/omnizip/io/source.rb +143 -0
  510. data/lib/omnizip/io/stream_manager.rb +117 -0
  511. data/lib/omnizip/io.rb +15 -0
  512. data/lib/omnizip/link_handler/hard_link.rb +79 -0
  513. data/lib/omnizip/link_handler/symbolic_link.rb +74 -0
  514. data/lib/omnizip/link_handler.rb +129 -0
  515. data/lib/omnizip/metadata/archive_metadata.rb +114 -0
  516. data/lib/omnizip/metadata/entry_metadata.rb +153 -0
  517. data/lib/omnizip/metadata/metadata_editor.rb +169 -0
  518. data/lib/omnizip/metadata/metadata_registry.rb +71 -0
  519. data/lib/omnizip/metadata/metadata_validator.rb +101 -0
  520. data/lib/omnizip/metadata.rb +58 -0
  521. data/lib/omnizip/models/.keep +0 -0
  522. data/lib/omnizip/models/algorithm_metadata.rb +41 -0
  523. data/lib/omnizip/models/attribute_apply.rb +40 -0
  524. data/lib/omnizip/models/compression_options.rb +53 -0
  525. data/lib/omnizip/models/conversion_options.rb +79 -0
  526. data/lib/omnizip/models/conversion_result.rb +141 -0
  527. data/lib/omnizip/models/eta_result.rb +45 -0
  528. data/lib/omnizip/models/extraction_rule.rb +115 -0
  529. data/lib/omnizip/models/filter_chain.rb +142 -0
  530. data/lib/omnizip/models/filter_config.rb +148 -0
  531. data/lib/omnizip/models/match_result.rb +141 -0
  532. data/lib/omnizip/models/optimization_suggestion.rb +95 -0
  533. data/lib/omnizip/models/parallel_options.rb +116 -0
  534. data/lib/omnizip/models/performance_result.rb +85 -0
  535. data/lib/omnizip/models/profile_report.rb +116 -0
  536. data/lib/omnizip/models/progress_options.rb +39 -0
  537. data/lib/omnizip/models/split_options.rb +116 -0
  538. data/lib/omnizip/optimization_registry.rb +45 -0
  539. data/lib/omnizip/parallel/engine.rb +52 -0
  540. data/lib/omnizip/parallel/job_queue.rb +209 -0
  541. data/lib/omnizip/parallel/job_scheduler.rb +225 -0
  542. data/lib/omnizip/parallel/parallel_compressor.rb +326 -0
  543. data/lib/omnizip/parallel/parallel_extractor.rb +238 -0
  544. data/lib/omnizip/parallel/thread_pool.rb +60 -0
  545. data/lib/omnizip/parallel/worker_pool.rb +227 -0
  546. data/lib/omnizip/parallel.rb +156 -0
  547. data/lib/omnizip/parity/chunked_block_processor.rb +194 -0
  548. data/lib/omnizip/parity/galois16.rb +145 -0
  549. data/lib/omnizip/parity/models/creator_packet.rb +71 -0
  550. data/lib/omnizip/parity/models/file_description_packet.rb +131 -0
  551. data/lib/omnizip/parity/models/ifsc_packet.rb +111 -0
  552. data/lib/omnizip/parity/models/main_packet.rb +126 -0
  553. data/lib/omnizip/parity/models/packet.rb +156 -0
  554. data/lib/omnizip/parity/models/packet_registry.rb +102 -0
  555. data/lib/omnizip/parity/models/recovery_slice_packet.rb +76 -0
  556. data/lib/omnizip/parity/models.rb +27 -0
  557. data/lib/omnizip/parity/par2_creator.rb +496 -0
  558. data/lib/omnizip/parity/par2_repairer.rb +409 -0
  559. data/lib/omnizip/parity/par2_verifier.rb +373 -0
  560. data/lib/omnizip/parity/par2cmdline_algorithm.rb +108 -0
  561. data/lib/omnizip/parity/par2cmdline_coefficients.rb +78 -0
  562. data/lib/omnizip/parity/reed_solomon_decoder.rb +263 -0
  563. data/lib/omnizip/parity/reed_solomon_encoder.rb +108 -0
  564. data/lib/omnizip/parity/reed_solomon_matrix.rb +339 -0
  565. data/lib/omnizip/parity.rb +207 -0
  566. data/lib/omnizip/password/encryption_registry.rb +26 -0
  567. data/lib/omnizip/password/encryption_strategy.rb +96 -0
  568. data/lib/omnizip/password/password_validator.rb +129 -0
  569. data/lib/omnizip/password/winzip_aes_strategy.rb +190 -0
  570. data/lib/omnizip/password/zip_crypto_strategy.rb +135 -0
  571. data/lib/omnizip/password.rb +88 -0
  572. data/lib/omnizip/pipe/stream_compressor.rb +124 -0
  573. data/lib/omnizip/pipe/stream_decompressor.rb +173 -0
  574. data/lib/omnizip/pipe.rb +120 -0
  575. data/lib/omnizip/platform/ntfs_streams.rb +200 -0
  576. data/lib/omnizip/platform.rb +189 -0
  577. data/lib/omnizip/profile/archive_profile.rb +36 -0
  578. data/lib/omnizip/profile/balanced_profile.rb +31 -0
  579. data/lib/omnizip/profile/binary_profile.rb +33 -0
  580. data/lib/omnizip/profile/compression_profile.rb +157 -0
  581. data/lib/omnizip/profile/custom_profile.rb +155 -0
  582. data/lib/omnizip/profile/fast_profile.rb +31 -0
  583. data/lib/omnizip/profile/maximum_profile.rb +31 -0
  584. data/lib/omnizip/profile/profile_detector.rb +108 -0
  585. data/lib/omnizip/profile/profile_registry.rb +160 -0
  586. data/lib/omnizip/profile/text_profile.rb +33 -0
  587. data/lib/omnizip/profile.rb +261 -0
  588. data/lib/omnizip/profiler/memory_profiler.rb +66 -0
  589. data/lib/omnizip/profiler/method_profiler.rb +49 -0
  590. data/lib/omnizip/profiler/report_generator.rb +158 -0
  591. data/lib/omnizip/profiler.rb +192 -0
  592. data/lib/omnizip/progress/callback_reporter.rb +34 -0
  593. data/lib/omnizip/progress/console_reporter.rb +59 -0
  594. data/lib/omnizip/progress/log_reporter.rb +89 -0
  595. data/lib/omnizip/progress/operation_progress.rb +118 -0
  596. data/lib/omnizip/progress/progress_bar.rb +156 -0
  597. data/lib/omnizip/progress/progress_reporter.rb +40 -0
  598. data/lib/omnizip/progress/progress_tracker.rb +215 -0
  599. data/lib/omnizip/progress/silent_reporter.rb +22 -0
  600. data/lib/omnizip/progress.rb +131 -0
  601. data/lib/omnizip/registry.rb +111 -0
  602. data/lib/omnizip/rubyzip_compat.rb +61 -0
  603. data/lib/omnizip/temp/safe_extract.rb +124 -0
  604. data/lib/omnizip/temp/temp_file.rb +123 -0
  605. data/lib/omnizip/temp/temp_file_pool.rb +109 -0
  606. data/lib/omnizip/temp.rb +184 -0
  607. data/lib/omnizip/version.rb +5 -0
  608. data/lib/omnizip/zip/entry.rb +160 -0
  609. data/lib/omnizip/zip/file.rb +485 -0
  610. data/lib/omnizip/zip/input_stream.rb +270 -0
  611. data/lib/omnizip/zip/output_stream.rb +320 -0
  612. data/lib/omnizip/zip.rb +23 -0
  613. data/lib/omnizip.rb +157 -0
  614. data/lib/tasks/msi_test_files.rake +66 -0
  615. data/lib/tasks/platform_gems.rake +147 -0
  616. data/lib/tasks/rust_backend.rake +48 -0
  617. data/readme-docs/advanced-features.adoc +514 -0
  618. data/readme-docs/api-usage.adoc +445 -0
  619. data/readme-docs/architecture.adoc +450 -0
  620. data/readme-docs/archive-formats.adoc +479 -0
  621. data/readme-docs/cli-usage.adoc +222 -0
  622. data/readme-docs/compression-algorithms.adoc +446 -0
  623. data/readme-docs/compression-profiles.adoc +247 -0
  624. data/readme-docs/encryption-checksums.adoc +328 -0
  625. data/readme-docs/format-converter.adoc +316 -0
  626. data/readme-docs/installation.adoc +228 -0
  627. data/readme-docs/par2-archives.adoc +608 -0
  628. data/readme-docs/performance-profiler.adoc +389 -0
  629. data/readme-docs/preprocessing-filters.adoc +280 -0
  630. data/vendor/libomnizip_ffi.so +0 -0
  631. data/xz-file-format-1.2.1.txt +1174 -0
  632. metadata +768 -0
@@ -0,0 +1,626 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "set"
5
+
6
+ module Omnizip
7
+ module Formats
8
+ module SevenZip
9
+ # .7z archive reader
10
+ # Provides read-only access to .7z archives
11
+ class Reader
12
+ include Omnizip::Formats::SevenZip::Constants
13
+
14
+ attr_reader :file_path, :header, :entries, :stream_info, :split_reader
15
+
16
+ # Initialize reader with file path
17
+ #
18
+ # @param file_path [String] Path to .7z file
19
+ # @param options [Hash] Reader options
20
+ # @option options [String] :password Password for encrypted headers
21
+ # @option options [Integer] :offset Offset for embedded archives
22
+ def initialize(file_path, options = {})
23
+ @file_path = file_path
24
+ @entries = []
25
+ @stream_info = nil
26
+ @split_reader = nil
27
+ @password = options[:password]
28
+ @offset = options[:offset] || 0
29
+ @solid_cache = {}
30
+ @solid_cache_extracted = {}
31
+ @opened = false
32
+ end
33
+
34
+ # Open and parse .7z archive
35
+ #
36
+ # @raise [RuntimeError] if file cannot be opened or parsed
37
+ def open
38
+ # Check if this is a split archive
39
+ if split_archive?
40
+ @split_reader = SplitArchiveReader.new(@file_path)
41
+ @split_reader.open
42
+ @header = @split_reader.header
43
+ @entries = @split_reader.entries
44
+ @stream_info = @split_reader.stream_info
45
+ else
46
+ File.open(@file_path, "rb") do |io|
47
+ # Seek to offset for embedded archives
48
+ io.seek(@offset) if @offset.positive?
49
+ parse_archive(io)
50
+ end
51
+ end
52
+ self
53
+ end
54
+
55
+ # Check if archive is split
56
+ #
57
+ # @return [Boolean] true if split across multiple volumes
58
+ def split?
59
+ @split_reader&.split? || false
60
+ end
61
+
62
+ # Get total number of volumes (for split archives)
63
+ #
64
+ # @return [Integer] Number of volumes
65
+ def total_volumes
66
+ @split_reader&.total_volumes || 1
67
+ end
68
+
69
+ # Get volume size (for split archives)
70
+ #
71
+ # @return [Integer] Volume size in bytes
72
+ def volume_size
73
+ @split_reader&.volume_size || File.size(@file_path)
74
+ end
75
+
76
+ # Get list of volumes (for split archives)
77
+ #
78
+ # @return [Array<String>] Volume paths
79
+ def volumes
80
+ @split_reader&.volumes || [@file_path]
81
+ end
82
+
83
+ # List all files in archive, parsing on first use — a Reader
84
+ # is always usable; #open remains for eager loading
85
+ #
86
+ # @return [Array<Models::FileEntry>] File entries
87
+ def list_files
88
+ ensure_open
89
+ @entries.reject { |e| e.name.nil? || e.name.empty? }
90
+ end
91
+
92
+ # Extract file to output path
93
+ #
94
+ # @param entry_name [String] File name to extract
95
+ # @param output_path [String] Destination path
96
+ # @raise [RuntimeError] if entry not found or extraction fails
97
+ def extract_entry(entry_name, output_path)
98
+ ensure_open
99
+ # Delegate to split reader if available
100
+ if @split_reader
101
+ @split_reader.extract_entry(entry_name, output_path)
102
+ return
103
+ end
104
+
105
+ entry = @entries.find { |e| e.name == entry_name }
106
+ raise Errno::ENOENT, "Entry not found: #{entry_name}" unless entry
107
+
108
+ # Create directory if needed
109
+ FileUtils.mkdir_p(File.dirname(output_path))
110
+
111
+ # Extract file
112
+ if entry.directory?
113
+ FileUtils.mkdir_p(output_path)
114
+ elsif entry.has_stream?
115
+ File.open(@file_path, "rb") do |io|
116
+ # Seek to offset for embedded archives
117
+ io.seek(@offset) if @offset.positive?
118
+ data = extract_entry_data(io, entry)
119
+ File.binwrite(output_path, data)
120
+ end
121
+
122
+ # Set timestamp if available
123
+ if entry.mtime
124
+ File.utime(entry.atime || entry.mtime || Time.now,
125
+ entry.mtime || Time.now,
126
+ output_path)
127
+ end
128
+ else
129
+ # Empty file
130
+ FileUtils.touch(output_path)
131
+ end
132
+ end
133
+
134
+ # Extract all files to directory
135
+ #
136
+ # @param output_dir [String] Destination directory
137
+ # @raise [RuntimeError] on extraction error
138
+ def extract_all(output_dir)
139
+ ensure_open
140
+ FileUtils.mkdir_p(output_dir)
141
+
142
+ real_out = File.realpath(output_dir)
143
+
144
+ @entries.each do |entry|
145
+ next if entry.name.nil? || entry.name.empty?
146
+
147
+ output_path = File.join(real_out, entry.name)
148
+ expanded = File.expand_path(output_path)
149
+ unless expanded.start_with?("#{real_out}#{File::SEPARATOR}") || expanded == real_out
150
+ raise Omnizip::FormatError,
151
+ "Path traversal detected: #{entry.name}"
152
+ end
153
+
154
+ extract_entry(entry.name, output_path)
155
+ end
156
+ ensure
157
+ @solid_cache.clear
158
+ @solid_cache_extracted.clear
159
+ end
160
+
161
+ # Check if archive is valid .7z format
162
+ #
163
+ # @return [Boolean] true if valid
164
+ def valid?
165
+ !@header.nil? && @header.valid?
166
+ end
167
+
168
+ # Check if headers are encrypted
169
+ #
170
+ # @return [Boolean] true if headers are encrypted
171
+ def encrypted?
172
+ !@encrypted_header.nil?
173
+ end
174
+
175
+ # Check if can decrypt headers (password provided)
176
+ #
177
+ # @return [Boolean] true if password available for encrypted headers
178
+ def can_decrypt?
179
+ encrypted? && !@password.nil?
180
+ end
181
+
182
+ # Extract raw entry payload from the archive. Public so converters
183
+ # and parallel extractors can reuse the stream-positioning +
184
+ # decompression logic.
185
+ #
186
+ # @param io [IO] Archive file handle
187
+ # @param entry [Models::FileEntry] Entry to extract
188
+ # @return [String] Extracted bytes
189
+ def extract_entry_data(io, entry)
190
+ extract_entry_data_private(io, entry)
191
+ end
192
+
193
+ private
194
+
195
+ # Parse the archive once, on demand
196
+ def ensure_open
197
+ open unless @opened
198
+ end
199
+
200
+ # Parse .7z archive structure
201
+ #
202
+ # @param io [io] Input stream
203
+ def parse_archive(io)
204
+ # Read and validate start header
205
+ @header = Header.read(io)
206
+
207
+ # Read next header metadata
208
+ # NOTE: next_header_offset is from the END of the Start Header (byte 32)
209
+ # NOT from the end of the file
210
+ # For embedded archives, add offset to get absolute position
211
+ next_header_pos = @offset + Constants::START_HEADER_SIZE + @header.next_header_offset
212
+ io.seek(next_header_pos)
213
+ next_header_data = io.read(@header.next_header_size)
214
+
215
+ # Check if header is encoded (compressed or encrypted)
216
+ # ENCODED_HEADER (0x17) can mean compressed or encrypted
217
+ first_byte = next_header_data.getbyte(0)
218
+ if first_byte == PropertyId::ENCODED_HEADER
219
+ # Try to parse as encrypted first (if enough data and has structure)
220
+ if next_header_data.bytesize >= 54
221
+ begin
222
+ # Check if it's actually an encrypted header
223
+ EncryptedHeader.from_binary(next_header_data)
224
+ # If we got here, it's encrypted - try to decrypt
225
+ next_header_data = decrypt_header(next_header_data)
226
+ rescue Omnizip::PasswordError
227
+ # Decryption refused or failed — a password problem,
228
+ # never a compressed header
229
+ raise
230
+ rescue StandardError
231
+ # Not an encrypted header — it's a compressed header;
232
+ # decompress it using the encoded stream info
233
+ next_header_data = decompress_encoded_header(io,
234
+ next_header_data)
235
+ end
236
+ else
237
+ # Too short for encrypted header, must be compressed
238
+ next_header_data = decompress_encoded_header(io, next_header_data)
239
+ end
240
+ end
241
+
242
+ # Parse metadata
243
+ parser = Parser.new(next_header_data)
244
+ begin
245
+ @stream_info, @entries = parse_metadata(parser)
246
+ rescue StandardError => e
247
+ if @headers_decrypted
248
+ raise Omnizip::PasswordError, "Failed to decrypt headers: incorrect password " \
249
+ "or corrupted data (#{e.message})"
250
+ end
251
+
252
+ raise
253
+ end
254
+
255
+ # Map entries to their folders/streams
256
+ map_entries_to_streams
257
+ end
258
+
259
+ # Decrypt encrypted header
260
+ #
261
+ # @param encrypted_data [String] Encrypted header bytes
262
+ # @return [String] Decrypted header data
263
+ # @raise [RuntimeError] if password not provided
264
+ def decrypt_header(encrypted_data)
265
+ # Parse encrypted header structure
266
+ @encrypted_header = EncryptedHeader.from_binary(encrypted_data)
267
+
268
+ unless @password
269
+ raise Omnizip::PasswordError, "Archive headers are encrypted. Password required to access."
270
+ end
271
+
272
+ # Decrypt using password
273
+ encryptor = HeaderEncryptor.new(@password)
274
+ decrypted = encryptor.decrypt(
275
+ @encrypted_header.encrypted_data,
276
+ @encrypted_header.salt,
277
+ @encrypted_header.iv,
278
+ )
279
+ # AES-CBC 'succeeds' with any key and yields garbage; wrong
280
+ # passwords surface as later header-parse failures, so mark
281
+ # the archive and translate those into the password error
282
+ @headers_decrypted = true
283
+ decrypted
284
+ rescue OpenSSL::Cipher::CipherError => e
285
+ raise Omnizip::PasswordError, "Failed to decrypt headers: incorrect password (#{e.message})"
286
+ end
287
+
288
+ # Decompress encoded (compressed) header
289
+ #
290
+ # @param io [IO] Archive file handle
291
+ # @param encoded_data [String] Encoded header bytes
292
+ # @return [String] Decompressed header data
293
+ def decompress_encoded_header(io, encoded_data)
294
+ # Skip ENCODED_HEADER marker
295
+ parser = Parser.new(encoded_data[1..])
296
+
297
+ # Parse the streams info for the encoded header
298
+ stream_info = Models::StreamInfo.new
299
+
300
+ # Read streams info - can be either MAIN_STREAMS_INFO or direct stream properties
301
+ type = parser.read_byte
302
+
303
+ if type == PropertyId::MAIN_STREAMS_INFO
304
+ parse_streams_info(parser, stream_info)
305
+ elsif type == PropertyId::PACK_INFO
306
+ # Direct PackInfo without MAIN_STREAMS_INFO wrapper
307
+ parser.read_pack_info(stream_info)
308
+
309
+ # Read UNPACK_INFO
310
+ type = parser.read_byte
311
+ if type == PropertyId::UNPACK_INFO
312
+ parser.read_unpack_info(stream_info)
313
+ end
314
+ else
315
+ raise Omnizip::InvalidArchiveError, "Unexpected property in encoded header: 0x#{type.to_s(16)}"
316
+ end
317
+
318
+ # Decompress the header using the stream info
319
+ # For embedded archives, add offset to get absolute file position
320
+ pack_pos = @offset + @header.start_pos_after_header + stream_info.pack_pos
321
+ folder = stream_info.folders[0]
322
+ pack_size = stream_info.pack_sizes[0]
323
+ unpack_size = folder.uncompressed_size
324
+
325
+ decompressor = StreamDecompressor.new(io, folder, pack_pos, pack_size)
326
+ decompressor.decompress(unpack_size)
327
+ end
328
+
329
+ # Parse archive metadata
330
+ #
331
+ # @param parser [Parser] Parser instance
332
+ # @return [Array<StreamInfo, Array<Models::FileEntry>>] Parsed data
333
+ def parse_metadata(parser)
334
+ stream_info = Models::StreamInfo.new
335
+ entries = []
336
+
337
+ # Read main header
338
+ type = parser.read_byte
339
+ raise Omnizip::InvalidArchiveError, "Expected Header, got 0x#{type.to_s(16)}" unless
340
+ type == PropertyId::HEADER
341
+
342
+ # Parse header sections
343
+ until parser.eof?
344
+ prop_type = parser.read_byte
345
+
346
+ case prop_type
347
+ when PropertyId::MAIN_STREAMS_INFO
348
+ parse_streams_info(parser, stream_info)
349
+ when PropertyId::FILES_INFO
350
+ entries = parser.read_files_info
351
+ when PropertyId::K_END
352
+ break
353
+ else
354
+ # Skip unknown properties
355
+ parser.skip_data if !parser.eof? &&
356
+ parser.peek_byte != PropertyId::K_END
357
+ end
358
+ end
359
+
360
+ parser.read_byte if !parser.eof? &&
361
+ parser.peek_byte == PropertyId::K_END
362
+
363
+ [stream_info, entries]
364
+ end
365
+
366
+ # Parse streams info section
367
+ #
368
+ # @param parser [Parser] Parser instance
369
+ # @param stream_info [Models::StreamInfo] Stream info to populate
370
+ def parse_streams_info(parser, stream_info)
371
+ until parser.eof?
372
+ prop_type = parser.read_byte
373
+
374
+ case prop_type
375
+ when PropertyId::PACK_INFO
376
+ parser.read_pack_info(stream_info)
377
+ when PropertyId::UNPACK_INFO
378
+ parser.read_unpack_info(stream_info)
379
+ when PropertyId::SUBSTREAMS_INFO
380
+ parser.read_substreams_info(stream_info)
381
+ when PropertyId::K_END
382
+ break
383
+ else
384
+ # Unknown property within streams_info - skip it
385
+ parser.skip_data if !parser.eof? && parser.peek_byte != PropertyId::K_END
386
+ end
387
+ end
388
+
389
+ # Consume final K_END for MAIN_STREAMS_INFO section
390
+ parser.read_byte if !parser.eof? && parser.peek_byte == PropertyId::K_END
391
+ end
392
+
393
+ # Map entries to their folders and streams
394
+ def map_entries_to_streams
395
+ return if @stream_info.nil?
396
+
397
+ stream_idx = 0
398
+ @entries.each_with_index do |entry, i|
399
+ next unless entry.has_stream?
400
+
401
+ # Find which folder this stream belongs to
402
+ folder_idx = 0
403
+ accumulated = 0
404
+ @stream_info.num_unpack_streams_in_folders.each_with_index do |num, fi|
405
+ if stream_idx < accumulated + num
406
+ folder_idx = fi
407
+ break
408
+ end
409
+ accumulated += num
410
+ end
411
+
412
+ entry.folder_index = folder_idx
413
+ entry.file_index = i
414
+ entry.size = @stream_info.unpack_sizes[stream_idx] if @stream_info.unpack_sizes[stream_idx]
415
+
416
+ # Single-stream folders own their packed bytes outright,
417
+ # so the entry's compressed size is exact. Solid folders
418
+ # share packed bytes across streams — leave nil rather
419
+ # than fabricate a split.
420
+ num_in_folder = @stream_info.num_unpack_streams_in_folders[folder_idx]
421
+ if num_in_folder == 1
422
+ folder = @stream_info.folders[folder_idx]
423
+ packed = folder.pack_stream_indices.sum do |pi|
424
+ @stream_info.pack_sizes[pi] || 0
425
+ end
426
+ entry.compressed_size = packed
427
+ end
428
+
429
+ stream_idx += 1
430
+ end
431
+ end
432
+
433
+ # Extract entry data (private implementation)
434
+ #
435
+ # @param io [IO] Archive file handle
436
+ # @param entry [Models::FileEntry] Entry to extract
437
+ # @return [String] Extracted data
438
+ def extract_entry_data_private(io, entry)
439
+ return "" unless entry.has_stream?
440
+ return "" unless @stream_info
441
+
442
+ folder = @stream_info.folders[entry.folder_index]
443
+ return "" unless folder
444
+
445
+ # Calculate pack position (add offset for embedded archives)
446
+ pack_pos = @offset + @header.start_pos_after_header +
447
+ @stream_info.pack_pos
448
+
449
+ # Get pack sizes for this folder
450
+ pack_idx = 0
451
+ entry.folder_index.times do |i|
452
+ num_streams = @stream_info.folders[i].pack_stream_indices.size
453
+ pack_idx += num_streams
454
+ end
455
+
456
+ # Pack streams are laid out sequentially; skip past every
457
+ # stream owned by earlier folders to reach this folder's
458
+ # bytes (with stored archives pack sizes equal unpack
459
+ # sizes, which masked this offset in the past)
460
+ pack_idx.times { |i| pack_pos += @stream_info.pack_sizes[i] || 0 }
461
+
462
+ # Check if this is a BCJ2 multi-stream folder
463
+ if Bcj2StreamDecompressor.bcj2_folder?(folder)
464
+ extract_bcj2_entry(io, entry, folder, pack_pos, pack_idx)
465
+ else
466
+ extract_regular_entry(io, entry, folder, pack_pos, pack_idx)
467
+ end
468
+ rescue StandardError => e
469
+ warn "Extraction failed for #{entry.name}: #{e.message}"
470
+ raise
471
+ end
472
+
473
+ # Extract entry from BCJ2 multi-stream folder
474
+ #
475
+ # @param io [IO] Archive file handle
476
+ # @param entry [Models::FileEntry] Entry to extract
477
+ # @param folder [Models::Folder] Folder specification
478
+ # @param pack_pos [Integer] Base pack position
479
+ # @param pack_idx [Integer] Starting pack index
480
+ # @return [String] Extracted data
481
+ def extract_bcj2_entry(io, entry, folder, pack_pos, pack_idx)
482
+ # Cache solid block to avoid re-decompressing for every file
483
+ full_data = @solid_cache[entry.folder_index]
484
+
485
+ unless full_data
486
+ # BCJ2 folders have multiple pack streams
487
+ num_pack_streams = folder.pack_stream_indices.size
488
+ pack_sizes = Array.new(num_pack_streams) do |i|
489
+ @stream_info.pack_sizes[pack_idx + i] || 0
490
+ end
491
+
492
+ # Decompress the entire BCJ2 folder
493
+ decompressor = Bcj2StreamDecompressor.new(
494
+ io, folder, pack_pos, pack_sizes, @stream_info
495
+ )
496
+ full_data = decompressor.decompress(folder.uncompressed_size)
497
+
498
+ # Cache for other entries in the same folder
499
+ num_files_in_folder = @stream_info.num_unpack_streams_in_folders[entry.folder_index] || 1
500
+ if num_files_in_folder > 1
501
+ @solid_cache[entry.folder_index] = full_data
502
+ @solid_cache_extracted[entry.folder_index] = Set.new
503
+ end
504
+ end
505
+
506
+ # For solid archives, extract this file's portion
507
+ num_files_in_folder = @stream_info.num_unpack_streams_in_folders[entry.folder_index] || 1
508
+
509
+ if num_files_in_folder > 1
510
+ # Find offset of this file within the uncompressed stream
511
+ file_offset = 0
512
+ @entries.each do |e|
513
+ break if e.file_index == entry.file_index
514
+
515
+ file_offset += e.size if e.has_stream? && e.folder_index == entry.folder_index
516
+ end
517
+ data = full_data[file_offset, entry.size]
518
+ else
519
+ data = full_data[0, entry.size]
520
+ end
521
+
522
+ # Verify CRC if available
523
+ if entry.crc
524
+ crc = Omnizip::Checksums::Crc32.new
525
+ crc.update(data)
526
+ unless crc.value == entry.crc
527
+ raise Omnizip::ChecksumError, "CRC mismatch for #{entry.name}: expected 0x#{entry.crc.to_s(16)}, got 0x#{crc.value.to_s(16)}"
528
+ end
529
+ end
530
+
531
+ # Evict solid cache entry when all files in folder have been extracted
532
+ if @solid_cache_extracted.key?(entry.folder_index)
533
+ @solid_cache_extracted[entry.folder_index].add(entry.file_index)
534
+ if @solid_cache_extracted[entry.folder_index].size >= num_files_in_folder
535
+ @solid_cache.delete(entry.folder_index)
536
+ @solid_cache_extracted.delete(entry.folder_index)
537
+ end
538
+ end
539
+
540
+ data
541
+ end
542
+
543
+ # Extract entry from regular (non-BCJ2) folder
544
+ #
545
+ # @param io [IO] Archive file handle
546
+ # @param entry [Models::FileEntry] Entry to extract
547
+ # @param folder [Models::Folder] Folder specification
548
+ # @param pack_pos [Integer] Base pack position
549
+ # @param pack_idx [Integer] Starting pack index
550
+ # @return [String] Extracted data
551
+ def extract_regular_entry(io, entry, folder, pack_pos, pack_idx)
552
+ pack_size = @stream_info.pack_sizes[pack_idx] || 0
553
+
554
+ # For solid archives, multiple files share one compressed stream
555
+ # We need to decompress the entire folder and extract the correct portion
556
+ num_files_in_folder = @stream_info.num_unpack_streams_in_folders[entry.folder_index] || 1
557
+
558
+ if num_files_in_folder > 1
559
+ # Cache solid block to avoid re-decompressing for every file
560
+ full_data = @solid_cache[entry.folder_index]
561
+
562
+ unless full_data
563
+ total_unpack_size = folder.uncompressed_size
564
+ decompressor = StreamDecompressor.new(io, folder, pack_pos,
565
+ pack_size)
566
+ full_data = decompressor.decompress(total_unpack_size)
567
+ @solid_cache[entry.folder_index] = full_data
568
+ @solid_cache_extracted[entry.folder_index] = Set.new
569
+ end
570
+
571
+ # Find offset of this file within the uncompressed stream
572
+ file_offset = 0
573
+ @entries.each do |e|
574
+ break if e.file_index == entry.file_index
575
+
576
+ file_offset += e.size if e.has_stream? && e.folder_index == entry.folder_index
577
+ end
578
+
579
+ # Extract this file's data
580
+ data = full_data[file_offset, entry.size]
581
+
582
+ # Verify CRC if available
583
+ if entry.crc
584
+ crc = Omnizip::Checksums::Crc32.new
585
+ crc.update(data)
586
+ unless crc.value == entry.crc
587
+ raise Omnizip::ChecksumError, "CRC mismatch for #{entry.name}: expected 0x#{entry.crc.to_s(16)}, got 0x#{crc.value.to_s(16)}"
588
+ end
589
+ end
590
+
591
+ # Evict solid cache entry when all files in folder have been extracted
592
+ if @solid_cache_extracted.key?(entry.folder_index)
593
+ @solid_cache_extracted[entry.folder_index].add(entry.file_index)
594
+ if @solid_cache_extracted[entry.folder_index].size >= num_files_in_folder
595
+ @solid_cache.delete(entry.folder_index)
596
+ @solid_cache_extracted.delete(entry.folder_index)
597
+ end
598
+ end
599
+
600
+ data
601
+ else
602
+ # Non-solid: each file has its own compressed stream
603
+ decompressor = StreamDecompressor.new(io, folder, pack_pos,
604
+ pack_size)
605
+ expected_crc = entry.crc
606
+ decompressor.decompress_and_verify(entry.size, expected_crc)
607
+ end
608
+ end
609
+
610
+ # Check if file path indicates a split archive
611
+ #
612
+ # @return [Boolean] true if appears to be split
613
+ def split_archive?
614
+ # Numeric pattern: .001, .002, etc.
615
+ return true if /\.\d{3}$/.match?(@file_path)
616
+
617
+ # Alpha pattern: .7z.aa, .tar.ab, etc. (but not .7z, .tar, .zip alone)
618
+ # Must have a known archive extension followed by alpha suffix
619
+ return true if /\.(7z|tar|zip|rar)\.[a-z]{2,}$/.match?(@file_path)
620
+
621
+ false
622
+ end
623
+ end
624
+ end
625
+ end
626
+ end