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,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "zlib"
4
+ require_relative "../rar/rar_format_base"
5
+ require_relative "../../error"
6
+
7
+ module Omnizip
8
+ module Formats
9
+ module Rar5
10
+ # RAR v5 decompressor
11
+ #
12
+ # Decompresses data using RAR v5 decompression algorithms.
13
+ # This implementation uses DEFLATE as a compatible fallback
14
+ # for demonstration purposes. Full RAR5 decompression requires
15
+ # proprietary algorithms.
16
+ #
17
+ # @example Decompressing data
18
+ # decompressor = Rar5::Decompressor.new
19
+ # original = decompressor.decompress(compressed, method: :normal)
20
+ class Decompressor < Rar::RarFormatBase
21
+ # Initialize a RAR v5 decompressor
22
+ def initialize
23
+ super("rar5")
24
+ end
25
+
26
+ # Decompress data using RAR v5 methods
27
+ #
28
+ # @param data [String] The compressed data
29
+ # @param method [Symbol] The compression method
30
+ # @param options [Hash] Decompression options
31
+ # @return [String] The decompressed data
32
+ def decompress(data, method: :normal, **_options)
33
+ case method
34
+ when :store
35
+ decompress_store(data)
36
+ when :fastest, :fast, :normal, :good, :best
37
+ decompress_deflate(data)
38
+ else
39
+ raise FormatError,
40
+ "Unsupported decompression method: #{method}"
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ # Return stored data without decompression
47
+ #
48
+ # @param data [String] The stored data
49
+ # @return [String] The uncompressed data
50
+ def decompress_store(data)
51
+ data
52
+ end
53
+
54
+ # Decompress using DEFLATE (compatible fallback)
55
+ #
56
+ # @param data [String] The compressed data
57
+ # @return [String] The decompressed data
58
+ def decompress_deflate(data)
59
+ Zlib::Inflate.inflate(data)
60
+ rescue Zlib::Error => e
61
+ raise FormatError, "Decompression failed: #{e.message}"
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,342 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require "lutaml/model"
5
+ rescue LoadError, ArgumentError
6
+ # lutaml-model not available, using simple classes
7
+ end
8
+
9
+ require_relative "../rar/rar_format_base"
10
+ require_relative "decompressor"
11
+
12
+ module Omnizip
13
+ module Formats
14
+ module Rar5
15
+ # RAR v5 archive reader
16
+ #
17
+ # Reads RAR 5.x format archives, parsing headers and extracting file data
18
+ # according to the RAR v5 specification.
19
+ #
20
+ # RAR 5 uses variable-length integers (vint) and improved header structure.
21
+ #
22
+ # @example Reading a RAR5 archive
23
+ # reader = Rar5::Reader.new
24
+ # File.open("archive.rar", "rb") do |file|
25
+ # entries = reader.read_archive(file)
26
+ # entries.each { |entry| puts entry.name }
27
+ # end
28
+ class Reader < Rar::RarFormatBase
29
+ # Initialize a RAR v5 reader
30
+ def initialize
31
+ super("rar5")
32
+ end
33
+
34
+ # Read a RAR v5 archive
35
+ #
36
+ # @param io [IO] The input stream
37
+ # @return [Array<Entry>] The archive entries
38
+ # @raise [FormatError] If the archive format is invalid
39
+ def read_archive(io)
40
+ unless verify_magic_bytes(io)
41
+ raise FormatError, "Invalid RAR v5 signature"
42
+ end
43
+
44
+ # Skip past the magic bytes (8 bytes)
45
+ io.seek(8, ::IO::SEEK_SET)
46
+
47
+ entries = []
48
+
49
+ # Read main header
50
+ main_header = read_header(io)
51
+ unless main_header.type == block_type_code(:main_header)
52
+ raise FormatError, "Expected main archive header"
53
+ end
54
+
55
+ @archive_flags = main_header.flags
56
+
57
+ # Skip to end of main header
58
+ skip_to_header_end(io, main_header)
59
+
60
+ # Read blocks until end marker or EOF/error
61
+ loop do
62
+ header = read_header(io)
63
+ break if header.type == block_type_code(:end_marker)
64
+
65
+ case header.type
66
+ when block_type_code(:file_header)
67
+ entry = read_file_entry(io, header)
68
+ entries << entry if entry
69
+ when block_type_code(:service_header)
70
+ skip_header_data(io, header)
71
+ when block_type_code(:encryption_header)
72
+ read_encryption_header(io, header)
73
+ else
74
+ skip_header_data(io, header)
75
+ end
76
+ rescue EOFError, RangeError, FormatError
77
+ # Truncated or corrupted file - return what we have
78
+ break
79
+ end
80
+
81
+ entries
82
+ rescue EOFError, RangeError, FormatError
83
+ # Handle truncated/invalid files gracefully
84
+ []
85
+ end
86
+
87
+ private
88
+
89
+ # Skip to the end of a header
90
+ #
91
+ # @param io [IO] The input stream
92
+ # @param header [HeaderBlock] The header
93
+ def skip_to_header_end(io, header)
94
+ # Calculate end of header: header_start + 4 (CRC) + header_size + vint_length
95
+ header_end = header.header_start + 4 + header.size.to_i + header.vint_length.to_i
96
+
97
+ # Validate the offset is reasonable (max 1GB)
98
+ max_offset = 1_073_741_824
99
+ raise RangeError, "Header offset too large" if header_end > max_offset
100
+ raise RangeError, "Header offset negative" if header_end.negative?
101
+
102
+ io.seek(header_end, ::IO::SEEK_SET)
103
+ end
104
+
105
+ # Read a RAR v5 header
106
+ #
107
+ # @param io [IO] The input stream
108
+ # @return [HeaderBlock] The header block
109
+ def read_header(io)
110
+ # Record start position (before CRC)
111
+ header_start = io.pos
112
+
113
+ header_crc = io.read(4)&.unpack1("V")
114
+
115
+ # Read header size vint and track its length
116
+ header_size, vint_length = read_vint_with_length(io)
117
+
118
+ # Now we're at the start of header content
119
+ # header_size is the size of the content (from header_type to end)
120
+ content_start = io.pos
121
+
122
+ header_type = read_vint(io)
123
+ header_flags = read_vint(io)
124
+
125
+ # Read extra size if present
126
+ extra_size = 0
127
+ extra_size = read_vint(io) if header_flags & 0x0001 != 0
128
+
129
+ # Read data size if present
130
+ data_size = 0
131
+ data_size = read_vint(io) if header_flags & 0x0002 != 0
132
+
133
+ # Store positions for proper skipping
134
+ HeaderBlock.new(
135
+ crc: header_crc,
136
+ size: header_size,
137
+ vint_length: vint_length,
138
+ type: header_type,
139
+ flags: header_flags,
140
+ extra_size: extra_size,
141
+ data_size: data_size,
142
+ header_start: header_start,
143
+ content_start: content_start,
144
+ )
145
+ end
146
+
147
+ # Read a vint and return both value and length
148
+ #
149
+ # @param io [IO] The input stream
150
+ # @return [Array<Integer, Integer>] Value and length in bytes
151
+ def read_vint_with_length(io)
152
+ result = 0
153
+ shift = 0
154
+ length = 0
155
+
156
+ loop do
157
+ byte = io.read(1)&.unpack1("C")
158
+ raise FormatError, "Unexpected EOF" unless byte
159
+
160
+ length += 1
161
+ result |= (byte & 0x7F) << shift
162
+ break if byte.nobits?(0x80)
163
+
164
+ shift += 7
165
+ end
166
+
167
+ [result, length]
168
+ end
169
+
170
+ # Read a file entry from archive
171
+ #
172
+ # @param io [IO] The input stream
173
+ # @param header [HeaderBlock] The file header
174
+ # @return [Entry] The file entry
175
+ def read_file_entry(io, header)
176
+ flags = read_vint(io)
177
+ unpacked_size = read_vint(io)
178
+ attributes = read_vint(io)
179
+
180
+ # Read modification time if present
181
+ mtime = Time.now
182
+ if flags & 0x02 != 0 # time_present flag
183
+ mtime = read_file_time(io)
184
+ end
185
+
186
+ # Read CRC32 if present
187
+ data_crc = 0
188
+ if flags & 0x04 != 0 # crc32_present flag
189
+ data_crc = io.read(4)&.unpack1("V")
190
+ end
191
+
192
+ # Read compression info
193
+ compression_flags = read_vint(io)
194
+ host_os = read_vint(io)
195
+ name_length = read_vint(io)
196
+
197
+ # Read filename (UTF-8 encoded)
198
+ name_bytes = io.read(name_length)
199
+ filename = name_bytes.force_encoding("UTF-8")
200
+
201
+ # Determine if directory
202
+ is_directory = flags.anybits?(0x01) # directory flag
203
+
204
+ # Skip to end of header
205
+ skip_to_header_end(io, header)
206
+
207
+ # Record data offset before skipping
208
+ data_offset = io.pos
209
+
210
+ # Skip file data (so we can read the next header)
211
+ io.seek(header.data_size, ::IO::SEEK_CUR) if header.data_size.to_i.positive?
212
+
213
+ Entry.new(
214
+ name: filename,
215
+ compressed_size: header.data_size || 0,
216
+ uncompressed_size: unpacked_size,
217
+ crc32: data_crc,
218
+ compression_method: extract_compression_method(compression_flags),
219
+ modified_time: mtime,
220
+ attributes: attributes,
221
+ encrypted: header.flags.anybits?(0x04),
222
+ data_offset: data_offset,
223
+ host_os: host_os,
224
+ is_directory: is_directory,
225
+ )
226
+ end
227
+
228
+ # Extract compression method from flags
229
+ #
230
+ # @param flags [Integer] The compression flags
231
+ # @return [Symbol] The compression method
232
+ def extract_compression_method(flags)
233
+ method_code = flags & 0x07
234
+
235
+ case method_code
236
+ when 0 then :store
237
+ when 1 then :fastest
238
+ when 2 then :fast
239
+ when 3 then :normal
240
+ when 4 then :good
241
+ when 5 then :best
242
+ else :normal
243
+ end
244
+ end
245
+
246
+ # Read file time in RAR5 format
247
+ #
248
+ # @param io [IO] The input stream
249
+ # @return [Time] The file time
250
+ def read_file_time(io)
251
+ read_vint(io)
252
+
253
+ # Simplified time reading - actual format is more complex
254
+ unix_time = io.read(8)&.unpack1("Q<")
255
+ Time.at(unix_time / 10_000_000.0) if unix_time
256
+ rescue ArgumentError
257
+ Time.now
258
+ end
259
+
260
+ # Read encryption header
261
+ #
262
+ # @param io [IO] The input stream
263
+ # @param header [HeaderBlock] The header
264
+ # @return [void]
265
+ def read_encryption_header(io, header)
266
+ # Read and skip encryption data
267
+ skip_header_data(io, header)
268
+ end
269
+
270
+ # Skip header data
271
+ #
272
+ # @param io [IO] The input stream
273
+ # @param header [HeaderBlock] The header
274
+ # @return [void]
275
+ def skip_header_data(io, header)
276
+ # Calculate end of header: header_start + 4 (CRC) + header_size + vint_length
277
+ header_end = header.header_start.to_i + 4 + header.size.to_i + header.vint_length.to_i
278
+ current_pos = io.pos
279
+
280
+ # Validate the offset is reasonable (max 1GB)
281
+ max_offset = 1_073_741_824
282
+ if header_end > max_offset || header_end.negative?
283
+ raise RangeError, "Invalid header offset"
284
+ end
285
+
286
+ if header_end > current_pos
287
+ io.seek(header_end - current_pos, ::IO::SEEK_CUR)
288
+ end
289
+
290
+ # Skip data section if present (with bounds checking)
291
+ data_size = header.data_size.to_i
292
+ if data_size.positive? && data_size < max_offset
293
+ io.seek(data_size, ::IO::SEEK_CUR)
294
+ end
295
+ end
296
+ end
297
+
298
+ # RAR v5 header block model
299
+ class HeaderBlock
300
+ attr_accessor :crc, :size, :vint_length, :type, :flags, :extra_size,
301
+ :data_size, :header_start, :content_start
302
+
303
+ def initialize(crc: nil, size: nil, vint_length: 1, type: nil, flags: nil,
304
+ extra_size: nil, data_size: nil, header_start: nil, content_start: nil)
305
+ @crc = crc
306
+ @size = size
307
+ @vint_length = vint_length
308
+ @type = type
309
+ @flags = flags
310
+ @extra_size = extra_size
311
+ @data_size = data_size
312
+ @header_start = header_start
313
+ @content_start = content_start
314
+ end
315
+ end
316
+
317
+ # RAR v5 archive entry model
318
+ class Entry
319
+ attr_accessor :name, :compressed_size, :uncompressed_size, :crc32,
320
+ :compression_method, :modified_time, :attributes,
321
+ :encrypted, :data_offset, :host_os, :is_directory
322
+
323
+ def initialize(name: nil, compressed_size: nil, uncompressed_size: nil,
324
+ crc32: nil, compression_method: nil, modified_time: nil,
325
+ attributes: nil, encrypted: nil, data_offset: nil,
326
+ host_os: nil, is_directory: nil)
327
+ @name = name
328
+ @compressed_size = compressed_size
329
+ @uncompressed_size = uncompressed_size
330
+ @crc32 = crc32
331
+ @compression_method = compression_method
332
+ @modified_time = modified_time
333
+ @attributes = attributes
334
+ @encrypted = encrypted
335
+ @data_offset = data_offset
336
+ @host_os = host_os
337
+ @is_directory = is_directory
338
+ end
339
+ end
340
+ end
341
+ end
342
+ end
@@ -0,0 +1,214 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require "lutaml/model"
5
+ rescue LoadError, ArgumentError
6
+ # lutaml-model not available, using simple classes
7
+ end
8
+
9
+ require "stringio"
10
+ require_relative "../rar/rar_format_base"
11
+ require_relative "compressor"
12
+
13
+ module Omnizip
14
+ module Formats
15
+ module Rar5
16
+ # RAR v5 archive writer
17
+ #
18
+ # Writes RAR 5.x format archives, creating headers and compressing file
19
+ # data according to the RAR v5 specification.
20
+ #
21
+ # RAR 5 uses variable-length integers and improved header structure.
22
+ #
23
+ # @example Writing a RAR5 archive
24
+ # writer = Rar5::Writer.new
25
+ # File.open("archive.rar", "wb") do |file|
26
+ # entries = [
27
+ # {name: "file.txt", data: "content", time: Time.now}
28
+ # ]
29
+ # writer.write_archive(file, entries)
30
+ # end
31
+ class Writer < Rar::RarFormatBase
32
+ # Initialize a RAR v5 writer
33
+ def initialize
34
+ super("rar5")
35
+ @compressor = Compressor.new
36
+ end
37
+
38
+ # Write a RAR v5 archive
39
+ #
40
+ # @param io [IO] The output stream
41
+ # @param entries [Array<Hash>] The entries to write
42
+ # @return [void]
43
+ def write_archive(io, entries)
44
+ # Write magic bytes
45
+ io.write(spec.magic_bytes.pack("C*"))
46
+
47
+ # Write main header
48
+ write_main_header(io)
49
+
50
+ # Write file entries
51
+ entries.each do |entry|
52
+ write_file_entry(io, entry)
53
+ end
54
+
55
+ # Write end marker
56
+ write_end_marker(io)
57
+ end
58
+
59
+ private
60
+
61
+ # Write main archive header
62
+ #
63
+ # @param io [IO] The output stream
64
+ # @return [void]
65
+ def write_main_header(io)
66
+ flags = 0
67
+
68
+ header_data = StringIO.new
69
+ write_vint(header_data, flags)
70
+
71
+ write_header(
72
+ io,
73
+ type: block_type_code(:main_header),
74
+ flags: 0,
75
+ data: header_data.string,
76
+ )
77
+ end
78
+
79
+ # Write a file entry
80
+ #
81
+ # @param io [IO] The output stream
82
+ # @param entry [Hash] The entry data
83
+ # @return [void]
84
+ def write_file_entry(io, entry)
85
+ name = entry[:name] || entry["name"]
86
+ data = entry[:data] || entry["data"]
87
+ mtime = entry[:time] || entry["time"] || Time.now
88
+ method = entry[:method] || entry["method"] || :normal
89
+
90
+ # Compress data
91
+ compressed_data = @compressor.compress(data, method: method)
92
+
93
+ # Calculate sizes and CRC
94
+ compressed_data.bytesize
95
+ unpacked_size = data.bytesize
96
+ file_crc = calculate_crc32(data)
97
+
98
+ # Encode filename to UTF-8
99
+ name_bytes = name.encode("UTF-8")
100
+ name_length = name_bytes.bytesize
101
+
102
+ # Build file header data
103
+ file_flags = spec.format.file_header_flags[:time_present] |
104
+ spec.format.file_header_flags[:crc32_present]
105
+
106
+ compression_info = compression_method_code(method) & 0x07
107
+
108
+ header_data = StringIO.new
109
+ write_vint(header_data, file_flags)
110
+ write_vint(header_data, unpacked_size)
111
+ write_vint(header_data, 0x20) # Attributes (normal file)
112
+
113
+ # Write modification time
114
+ write_file_time(header_data, mtime)
115
+
116
+ # Write CRC32
117
+ header_data.write([file_crc].pack("V"))
118
+
119
+ # Write compression info
120
+ write_vint(header_data, compression_info)
121
+ write_vint(header_data, spec.format.host_os[:windows])
122
+ write_vint(header_data, name_length)
123
+ header_data.write(name_bytes)
124
+
125
+ # Write header with data
126
+ write_header(
127
+ io,
128
+ type: block_type_code(:file_header),
129
+ flags: 0x0002, # Has data size
130
+ data: header_data.string,
131
+ data_section: compressed_data,
132
+ )
133
+ end
134
+
135
+ # Write end marker
136
+ #
137
+ # @param io [IO] The output stream
138
+ # @return [void]
139
+ def write_end_marker(io)
140
+ write_header(
141
+ io,
142
+ type: block_type_code(:end_marker),
143
+ flags: 0x0004, # Archive end flag
144
+ data: "",
145
+ )
146
+ end
147
+
148
+ # Write a RAR v5 header
149
+ #
150
+ # @param io [IO] The output stream
151
+ # @param type [Integer] The header type
152
+ # @param flags [Integer] The header flags
153
+ # @param data [String] The header data
154
+ # @param data_section [String, nil] Optional data section
155
+ # @return [void]
156
+ def write_header(io, type:, flags:, data:, data_section: nil)
157
+ header_io = StringIO.new
158
+
159
+ # Calculate header size (not including CRC)
160
+ size_bytes = StringIO.new
161
+ write_vint(size_bytes, type)
162
+ write_vint(size_bytes, flags)
163
+
164
+ if data_section
165
+ flags |= 0x0002 # Has data size flag
166
+ write_vint(size_bytes, data_section.bytesize)
167
+ end
168
+
169
+ size_bytes.write(data)
170
+
171
+ header_size = size_bytes.string.bytesize
172
+
173
+ # Write size vint
174
+ write_vint(header_io, header_size)
175
+
176
+ # Write type and flags
177
+ write_vint(header_io, type)
178
+ write_vint(header_io, flags)
179
+
180
+ # Write data size if present
181
+ write_vint(header_io, data_section.bytesize) if data_section
182
+
183
+ # Write header data
184
+ header_io.write(data)
185
+
186
+ # Calculate CRC32 of header (excluding CRC field itself)
187
+ header_crc = calculate_crc32(header_io.string)
188
+
189
+ # Write CRC and header to output
190
+ io.write([header_crc].pack("V"))
191
+ io.write(header_io.string)
192
+
193
+ # Write data section if present
194
+ io.write(data_section) if data_section
195
+ end
196
+
197
+ # Write file time in RAR5 format
198
+ #
199
+ # @param io [IO] The output stream
200
+ # @param time [Time] The time to write
201
+ # @return [void]
202
+ def write_file_time(io, time)
203
+ # RAR5 uses Windows FILETIME format (100-nanosecond intervals since
204
+ # 1601-01-01). Simplified implementation.
205
+ unix_time = time.to_i
206
+ windows_time = (unix_time + 11_644_473_600) * 10_000_000
207
+
208
+ write_vint(io, 0x01) # Time flags (modification time present)
209
+ io.write([windows_time].pack("Q<"))
210
+ end
211
+ end
212
+ end
213
+ end
214
+ end