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,386 @@
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 Rar3
15
+ # RAR v3 archive reader
16
+ #
17
+ # Reads RAR 3.x format archives, parsing headers and extracting file data
18
+ # according to the RAR v3 specification.
19
+ #
20
+ # @example Reading a RAR3 archive
21
+ # reader = Rar3::Reader.new
22
+ # File.open("archive.rar", "rb") do |file|
23
+ # entries = reader.read_archive(file)
24
+ # entries.each { |entry| puts entry.name }
25
+ # end
26
+ class Reader < Rar::RarFormatBase
27
+ # Initialize a RAR v3 reader
28
+ def initialize
29
+ super("rar3")
30
+ end
31
+
32
+ # Read a RAR v3 archive
33
+ #
34
+ # @param io [IO] The input stream
35
+ # @return [Array<Entry>] The archive entries
36
+ # @raise [FormatError] If the archive format is invalid
37
+ def read_archive(io)
38
+ unless verify_magic_bytes(io)
39
+ raise FormatError, "Invalid RAR v3 signature"
40
+ end
41
+
42
+ entries = []
43
+
44
+ # RAR4 marker is the signature itself (7 bytes): "Rar!\x1a\x07\x00"
45
+ # Skip past the marker to read the archive header
46
+ io.seek(7, ::IO::SEEK_SET)
47
+
48
+ # Read first block - could be archive header or file block (for minimal archives)
49
+ first_block = read_block_header(io)
50
+
51
+ if first_block.type == block_type_code(:archive)
52
+ # Standard archive with archive header
53
+ @archive_flags = first_block.flags
54
+
55
+ # Skip past archive header block (header + data)
56
+ # SIZE field contains total block size
57
+ block_end = first_block.header_start + first_block.size
58
+ io.seek(block_end, ::IO::SEEK_SET)
59
+ elsif first_block.type == block_type_code(:file)
60
+ # Minimal archive without archive header - process as file block
61
+ entry = read_file_entry(io, first_block)
62
+ entries << entry if entry
63
+ else
64
+ raise FormatError, "Expected archive header or file header, got type #{first_block.type}"
65
+ end
66
+
67
+ # Read file blocks until end
68
+ loop do
69
+ block = read_block_header(io)
70
+ break if block.type == block_type_code(:terminator)
71
+
72
+ case block.type
73
+ when block_type_code(:file)
74
+ entry = read_file_entry(io, block)
75
+ entries << entry if entry
76
+ when block_type_code(:comment)
77
+ read_comment_block(io, block)
78
+ when block_type_code(:recovery)
79
+ skip_block_data(io, block)
80
+ else
81
+ skip_block_data(io, block)
82
+ end
83
+ end
84
+
85
+ entries
86
+ rescue EOFError, FormatError
87
+ # Handle truncated or malformed files gracefully
88
+ entries
89
+ end
90
+
91
+ private
92
+
93
+ # Read a block header
94
+ #
95
+ # @param io [IO] The input stream
96
+ # @return [BlockHeader] The block header
97
+ def read_block_header(io)
98
+ # Record position BEFORE reading header
99
+ header_start = io.pos
100
+
101
+ header_crc = io.read(2)&.unpack1("v")
102
+ type = io.read(1)&.unpack1("C")
103
+ flags = io.read(2)&.unpack1("v")
104
+ size = io.read(2)&.unpack1("v")
105
+
106
+ raise FormatError, "Unexpected EOF" unless size
107
+
108
+ # For FILE blocks, the SIZE field directly contains the total header size
109
+ # The file_header structure starts immediately after the 7-byte block header
110
+ # No additional 4-byte field needed for FILE blocks
111
+ header_size = 7 # CRC(2) + TYPE(1) + FLAGS(2) + SIZE(2)
112
+
113
+ BlockHeader.new(
114
+ crc: header_crc,
115
+ type: type,
116
+ flags: flags,
117
+ size: size,
118
+ header_start: header_start,
119
+ header_size: header_size,
120
+ )
121
+ end
122
+
123
+ # Skip to the data portion of a block (after header)
124
+ #
125
+ # @param io [IO] The input stream
126
+ # @param block [BlockHeader] The block header
127
+ def skip_to_block_data(io, block)
128
+ target_pos = block.header_start + block.header_size
129
+ current_pos = io.pos
130
+ if target_pos > current_pos
131
+ io.seek(target_pos, ::IO::SEEK_SET)
132
+ end
133
+ end
134
+
135
+ # Read a file entry from archive
136
+ #
137
+ # @param io [IO] The input stream
138
+ # @param block [BlockHeader] The file block header
139
+ # @return [Entry] The file entry
140
+ def read_file_entry(io, block)
141
+ # The SIZE field contains the total size of the block header (including data after 7-byte prefix)
142
+ # The data portion after the 7-byte block header is: size - 7 bytes
143
+ block.header_start + 7 # Start of header data after 7-byte prefix
144
+ header_data_size = block.size - 7
145
+
146
+ # Read all header data at once
147
+ header_data = io.read(header_data_size)
148
+ raise FormatError, "Unexpected EOF reading file header" unless header_data
149
+
150
+ # Now parse the file_header from the start of header_data
151
+ pos = 0
152
+
153
+ packed_size = header_data[pos, 4].unpack1("V")
154
+ pos += 4
155
+
156
+ unpacked_size = header_data[pos, 4].unpack1("V")
157
+ pos += 4
158
+
159
+ host_os = header_data[pos, 1].unpack1("C")
160
+ pos += 1
161
+
162
+ file_crc = header_data[pos, 4].unpack1("V")
163
+ pos += 4
164
+
165
+ file_time = header_data[pos, 4].unpack1("V")
166
+ pos += 4
167
+
168
+ header_data[pos, 1].unpack1("C")
169
+ pos += 1
170
+
171
+ method = header_data[pos, 1].unpack1("C")
172
+ pos += 1
173
+
174
+ name_size = header_data[pos, 2].unpack1("v")
175
+ pos += 2
176
+
177
+ attr = header_data[pos, 4].unpack1("V")
178
+ pos += 4
179
+
180
+ # Read filename
181
+ name_bytes = header_data[pos, name_size]
182
+ pos += name_size
183
+ filename = decode_filename(name_bytes, block.flags)
184
+
185
+ # Handle large file sizes
186
+ if block.flags & 0x0100 != 0 # large_file flag
187
+ high_packed = header_data[pos, 4].unpack1("V")
188
+ high_unpacked = header_data[pos + 4, 4].unpack1("V")
189
+ packed_size |= (high_packed << 32)
190
+ unpacked_size |= (high_unpacked << 32)
191
+ pos += 8
192
+ end
193
+
194
+ # Read salt if encrypted
195
+ salt = nil
196
+ if block.flags & 0x0400 != 0 # salt flag
197
+ salt = header_data[pos, 8]
198
+ pos += 8
199
+ end
200
+
201
+ # Read extended time if present
202
+ mtime = parse_dos_time(file_time)
203
+ if block.flags & 0x1000 != 0 # ext_time flag
204
+ # Extended time format: mtime[4] + ctime[4] + atime[4] + arctime[4] + flags
205
+ # Plus additional 3 bytes for each present time
206
+ mtime, = read_extended_time_data(header_data, pos)
207
+ end
208
+
209
+ # We've already read all header data, no need to seek
210
+ # Just skip to end of block and then past file data
211
+
212
+ # Record data offset (start of file data)
213
+ data_offset = block.header_start + block.size
214
+
215
+ # Current position should be at end of header data
216
+ # Skip past file data to prepare for next block
217
+ # Use read instead of seek for better compatibility with non-seekable streams
218
+ if packed_size.positive?
219
+ begin
220
+ io.seek(packed_size, ::IO::SEEK_CUR)
221
+ rescue Errno::EINVAL, Errno::ESPIPE
222
+ # Stream doesn't support seeking - read and discard instead
223
+ remaining = packed_size
224
+ while remaining.positive?
225
+ chunk = io.read([remaining, 8192].min)
226
+ break unless chunk
227
+
228
+ remaining -= chunk.bytesize
229
+ end
230
+ end
231
+ end
232
+
233
+ Entry.new(
234
+ name: filename,
235
+ compressed_size: packed_size,
236
+ uncompressed_size: unpacked_size,
237
+ crc32: file_crc,
238
+ compression_method: compression_method_name(method),
239
+ modified_time: mtime,
240
+ attributes: attr,
241
+ encrypted: block.flags.anybits?(0x0004), # encrypted flag
242
+ data_offset: data_offset,
243
+ host_os: host_os,
244
+ salt: salt,
245
+ )
246
+ end
247
+
248
+ # Read extended time from header data
249
+ #
250
+ # @param data [String] The header data
251
+ # @param pos [Integer] Current position in data
252
+ # @return [Array<Time, Integer>] The modification time and new position
253
+ def read_extended_time_data(data, pos)
254
+ return [Time.now, pos] if pos + 4 > data.bytesize
255
+
256
+ mtime = parse_dos_time(data[pos, 4].unpack1("V"))
257
+ pos += 4
258
+
259
+ # Skip ctime, atime, arctime if present
260
+ begin
261
+ data[pos, 1].unpack1("C")
262
+ rescue StandardError
263
+ 0
264
+ end
265
+ pos += 1
266
+
267
+ # For now, just skip the remaining extended time data
268
+ # The format is complex and varies
269
+
270
+ [mtime, pos]
271
+ rescue ArgumentError
272
+ [Time.now, pos]
273
+ end
274
+
275
+ # Decode filename based on encoding flags
276
+ #
277
+ # @param bytes [String] The filename bytes
278
+ # @param flags [Integer] The file flags
279
+ # @return [String] The decoded filename
280
+ def decode_filename(bytes, flags)
281
+ if flags.nobits?(spec.format.file_flags[:unicode])
282
+ # Legacy encoding - assume CP437 or system encoding
283
+ bytes.force_encoding("CP437").encode("UTF-8", invalid: :replace)
284
+ else
285
+ # Unicode filename - decode UTF-8
286
+ bytes.force_encoding("UTF-8")
287
+ end
288
+ end
289
+
290
+ # Parse DOS date/time to Ruby Time
291
+ #
292
+ # @param dos_time [Integer] The DOS timestamp
293
+ # @return [Time] The parsed time
294
+ def parse_dos_time(dos_time)
295
+ second = (dos_time & 0x1F) * 2
296
+ minute = (dos_time >> 5) & 0x3F
297
+ hour = (dos_time >> 11) & 0x1F
298
+ day = (dos_time >> 16) & 0x1F
299
+ month = (dos_time >> 21) & 0x0F
300
+ year = ((dos_time >> 25) & 0x7F) + 1980
301
+
302
+ Time.new(year, month, day, hour, minute, second)
303
+ rescue ArgumentError
304
+ Time.now
305
+ end
306
+
307
+ # Read extended time information
308
+ #
309
+ # @param io [IO] The input stream
310
+ # @return [Time] The extended time
311
+ def read_extended_time(io)
312
+ io.read(2)&.unpack1("v")
313
+ # Extended time format varies, simplified implementation
314
+ Time.now
315
+ end
316
+
317
+ # Read comment block
318
+ #
319
+ # @param io [IO] The input stream
320
+ # @param block [BlockHeader] The block header
321
+ # @return [String] The comment text
322
+ def read_comment_block(io, block)
323
+ current_pos = io.pos
324
+ header_end = block.header_start + block.header_size
325
+ data_size = block.size - block.header_size
326
+ remaining = data_size - (current_pos - header_end)
327
+ comment = io.read(remaining) if remaining.positive?
328
+ comment&.force_encoding("UTF-8")
329
+ end
330
+
331
+ # Skip block data
332
+ #
333
+ # @param io [IO] The input stream
334
+ # @param block [BlockHeader] The block header
335
+ # @return [void]
336
+ def skip_block_data(io, block)
337
+ # block.size is the total size, calculate remaining bytes
338
+ current_pos = io.pos
339
+ header_end = block.header_start + block.header_size
340
+ data_size = block.size - block.header_size
341
+ remaining = data_size - (current_pos - header_end)
342
+ io.seek(remaining, ::IO::SEEK_CUR) if remaining.positive?
343
+ end
344
+ end
345
+
346
+ # RAR v3 block header model
347
+ class BlockHeader
348
+ attr_accessor :crc, :type, :flags, :size, :header_start, :header_size
349
+
350
+ def initialize(crc: nil, type: nil, flags: nil, size: nil,
351
+ header_start: nil, header_size: 7)
352
+ @crc = crc
353
+ @type = type
354
+ @flags = flags
355
+ @size = size
356
+ @header_start = header_start
357
+ @header_size = header_size
358
+ end
359
+ end
360
+
361
+ # RAR archive entry model
362
+ class Entry
363
+ attr_accessor :name, :compressed_size, :uncompressed_size, :crc32,
364
+ :compression_method, :modified_time, :attributes,
365
+ :encrypted, :data_offset, :host_os, :salt
366
+
367
+ def initialize(name: nil, compressed_size: nil, uncompressed_size: nil,
368
+ crc32: nil, compression_method: nil, modified_time: nil,
369
+ attributes: nil, encrypted: nil, data_offset: nil,
370
+ host_os: nil, salt: nil)
371
+ @name = name
372
+ @compressed_size = compressed_size
373
+ @uncompressed_size = uncompressed_size
374
+ @crc32 = crc32
375
+ @compression_method = compression_method
376
+ @modified_time = modified_time
377
+ @attributes = attributes
378
+ @encrypted = encrypted
379
+ @data_offset = data_offset
380
+ @host_os = host_os
381
+ @salt = salt
382
+ end
383
+ end
384
+ end
385
+ end
386
+ end
@@ -0,0 +1,219 @@
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 Rar3
16
+ # RAR v3 archive writer
17
+ #
18
+ # Writes RAR 3.x format archives, creating headers and compressing file
19
+ # data according to the RAR v3 specification.
20
+ #
21
+ # @example Writing a RAR3 archive
22
+ # writer = Rar3::Writer.new
23
+ # File.open("archive.rar", "wb") do |file|
24
+ # entries = [
25
+ # {name: "file.txt", data: "content", time: Time.now}
26
+ # ]
27
+ # writer.write_archive(file, entries)
28
+ # end
29
+ class Writer < Rar::RarFormatBase
30
+ # Initialize a RAR v3 writer
31
+ def initialize
32
+ super("rar3")
33
+ @compressor = Compressor.new
34
+ end
35
+
36
+ # Write a RAR v3 archive
37
+ #
38
+ # @param io [IO] The output stream
39
+ # @param entries [Array<Hash>] The entries to write
40
+ # @return [void]
41
+ def write_archive(io, entries)
42
+ # Write marker block
43
+ write_marker_block(io)
44
+
45
+ # Write archive header
46
+ write_archive_header(io)
47
+
48
+ # Write file entries
49
+ entries.each do |entry|
50
+ write_file_entry(io, entry)
51
+ end
52
+
53
+ # Write terminator block
54
+ write_terminator_block(io)
55
+ end
56
+
57
+ private
58
+
59
+ # Write marker block (RAR signature)
60
+ #
61
+ # @param io [IO] The output stream
62
+ # @return [void]
63
+ def write_marker_block(io)
64
+ io.write(spec.magic_bytes.pack("C*"))
65
+ end
66
+
67
+ # Write archive header block
68
+ #
69
+ # @param io [IO] The output stream
70
+ # @return [void]
71
+ def write_archive_header(io)
72
+ flags = 0
73
+ # Set default flags if needed
74
+
75
+ header_data = StringIO.new
76
+ # Reserved fields
77
+ header_data.write([0, 0].pack("vv"))
78
+
79
+ header = create_block_header(
80
+ type: block_type_code(:archive),
81
+ flags: flags,
82
+ data: header_data.string,
83
+ )
84
+
85
+ write_block_header(io, header)
86
+ io.write(header_data.string)
87
+ end
88
+
89
+ # Write a file entry
90
+ #
91
+ # @param io [IO] The output stream
92
+ # @param entry [Hash] The entry data
93
+ # @return [void]
94
+ def write_file_entry(io, entry)
95
+ name = entry[:name] || entry["name"]
96
+ data = entry[:data] || entry["data"]
97
+ mtime = entry[:time] || entry["time"] || Time.now
98
+ method = entry[:method] || entry["method"] || :normal
99
+
100
+ # Compress data
101
+ compressed_data = @compressor.compress(data, method: method)
102
+
103
+ # Calculate sizes and CRC
104
+ packed_size = compressed_data.bytesize
105
+ unpacked_size = data.bytesize
106
+ file_crc = calculate_crc32(data)
107
+
108
+ # Encode filename
109
+ name_bytes = name.encode("UTF-8")
110
+ name_size = name_bytes.bytesize
111
+
112
+ # Set flags
113
+ flags = spec.format.file_flags[:unicode]
114
+ if unpacked_size > 0xFFFFFFFF
115
+ flags |= spec.format.file_flags[:large_file]
116
+ end
117
+
118
+ # Build file header data
119
+ header_data = StringIO.new
120
+ header_data.write([packed_size & 0xFFFFFFFF].pack("V"))
121
+ header_data.write([unpacked_size & 0xFFFFFFFF].pack("V"))
122
+ header_data.write([spec.format.host_os[:win32]].pack("C"))
123
+ header_data.write([file_crc].pack("V"))
124
+ header_data.write([encode_dos_time(mtime)].pack("V"))
125
+ header_data.write([0x14].pack("C")) # Version needed
126
+ header_data.write([compression_method_code(method)].pack("C"))
127
+ header_data.write([name_size].pack("v"))
128
+ header_data.write([0x20].pack("V")) # File attributes
129
+ header_data.write(name_bytes)
130
+
131
+ # Add high size fields if needed
132
+ if flags & spec.format.file_flags[:large_file] != 0
133
+ header_data.write([(packed_size >> 32) & 0xFFFFFFFF].pack("V"))
134
+ header_data.write([(unpacked_size >> 32) & 0xFFFFFFFF].pack("V"))
135
+ end
136
+
137
+ # Create and write block header
138
+ header = create_block_header(
139
+ type: block_type_code(:file),
140
+ flags: flags,
141
+ data: header_data.string,
142
+ )
143
+
144
+ write_block_header(io, header)
145
+ io.write(header_data.string)
146
+ io.write(compressed_data)
147
+ end
148
+
149
+ # Write terminator block
150
+ #
151
+ # @param io [IO] The output stream
152
+ # @return [void]
153
+ def write_terminator_block(io)
154
+ header = create_block_header(
155
+ type: block_type_code(:terminator),
156
+ flags: 0x4000, # Archive end flag
157
+ data: "",
158
+ )
159
+
160
+ write_block_header(io, header)
161
+ end
162
+
163
+ # Create a block header
164
+ #
165
+ # @param type [Integer] The block type
166
+ # @param flags [Integer] The block flags
167
+ # @param data [String] The block data
168
+ # @return [Hash] The block header
169
+ def create_block_header(type:, flags:, data:)
170
+ size = 7 + data.bytesize # Header size + data size
171
+
172
+ # Calculate header CRC
173
+ temp = StringIO.new
174
+ temp.write([type].pack("C"))
175
+ temp.write([flags].pack("v"))
176
+ temp.write([size].pack("v"))
177
+ temp.write(data) unless data.empty?
178
+
179
+ header_crc = calculate_crc32(temp.string) & 0xFFFF
180
+
181
+ {
182
+ crc: header_crc,
183
+ type: type,
184
+ flags: flags,
185
+ size: size,
186
+ }
187
+ end
188
+
189
+ # Write a block header to stream
190
+ #
191
+ # @param io [IO] The output stream
192
+ # @param header [Hash] The header data
193
+ # @return [void]
194
+ def write_block_header(io, header)
195
+ io.write([header[:crc]].pack("v"))
196
+ io.write([header[:type]].pack("C"))
197
+ io.write([header[:flags]].pack("v"))
198
+ io.write([header[:size]].pack("v"))
199
+ end
200
+
201
+ # Encode Ruby Time to DOS date/time format
202
+ #
203
+ # @param time [Time] The time to encode
204
+ # @return [Integer] The DOS timestamp
205
+ def encode_dos_time(time)
206
+ second = time.sec / 2
207
+ minute = time.min
208
+ hour = time.hour
209
+ day = time.day
210
+ month = time.month
211
+ year = time.year - 1980
212
+
213
+ (year << 25) | (month << 21) | (day << 16) |
214
+ (hour << 11) | (minute << 5) | second
215
+ end
216
+ end
217
+ end
218
+ end
219
+ end
@@ -0,0 +1,73 @@
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 compressor
11
+ #
12
+ # Compresses data using RAR v5 compression algorithms.
13
+ # This implementation uses DEFLATE as a compatible fallback
14
+ # for demonstration purposes. Full RAR5 compression requires
15
+ # proprietary algorithms.
16
+ #
17
+ # @example Compressing data
18
+ # compressor = Rar5::Compressor.new
19
+ # compressed = compressor.compress("Hello, World!", method: :normal)
20
+ class Compressor < Rar::RarFormatBase
21
+ # Initialize a RAR v5 compressor
22
+ def initialize
23
+ super("rar5")
24
+ end
25
+
26
+ # Compress data using RAR v5 methods
27
+ #
28
+ # @param data [String] The data to compress
29
+ # @param method [Symbol] The compression method
30
+ # @param options [Hash] Compression options
31
+ # @return [String] The compressed data
32
+ def compress(data, method: :normal, **_options)
33
+ case method
34
+ when :store
35
+ compress_store(data)
36
+ when :fastest
37
+ compress_deflate(data, level: Zlib::BEST_SPEED)
38
+ when :fast
39
+ compress_deflate(data, level: 3)
40
+ when :normal
41
+ compress_deflate(data, level: Zlib::DEFAULT_COMPRESSION)
42
+ when :good
43
+ compress_deflate(data, level: 7)
44
+ when :best
45
+ compress_deflate(data, level: Zlib::BEST_COMPRESSION)
46
+ else
47
+ raise FormatError,
48
+ "Unsupported compression method: #{method}"
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ # Store data without compression
55
+ #
56
+ # @param data [String] The data to store
57
+ # @return [String] The uncompressed data
58
+ def compress_store(data)
59
+ data
60
+ end
61
+
62
+ # Compress using DEFLATE (compatible fallback)
63
+ #
64
+ # @param data [String] The data to compress
65
+ # @param level [Integer] The compression level
66
+ # @return [String] The compressed data
67
+ def compress_deflate(data, level:)
68
+ Zlib::Deflate.deflate(data, level)
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end