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,195 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "recovery_record"
4
+ require_relative "parity_handler"
5
+
6
+ module Omnizip
7
+ module Formats
8
+ module Rar
9
+ # RAR archive verification
10
+ # Verifies archive integrity using CRCs and recovery records
11
+ class ArchiveVerifier
12
+ attr_reader :archive_path, :recovery_record, :parity_handler
13
+
14
+ # Verification result
15
+ class VerificationResult
16
+ attr_accessor :valid, :files_total, :files_ok, :files_corrupted,
17
+ :corrupted_files, :recoverable, :corrupt_blocks,
18
+ :recovery_available, :errors
19
+
20
+ def initialize
21
+ @valid = true
22
+ @files_total = 0
23
+ @files_ok = 0
24
+ @files_corrupted = 0
25
+ @corrupted_files = []
26
+ @recoverable = false
27
+ @corrupt_blocks = []
28
+ @recovery_available = false
29
+ @errors = []
30
+ end
31
+
32
+ # Check if archive is valid
33
+ #
34
+ # @return [Boolean] true if valid
35
+ def valid?
36
+ @valid && @files_corrupted.zero?
37
+ end
38
+
39
+ # Check if corruption can be repaired
40
+ #
41
+ # @return [Boolean] true if repairable
42
+ def can_repair?
43
+ @recovery_available && @recoverable
44
+ end
45
+
46
+ # Get summary string
47
+ #
48
+ # @return [String] Verification summary
49
+ def summary
50
+ if valid?
51
+ "Archive OK: #{@files_total} files verified"
52
+ else
53
+ msg = "Archive corrupted: #{@files_corrupted}/#{@files_total} files damaged"
54
+ msg += " (repairable)" if can_repair?
55
+ msg
56
+ end
57
+ end
58
+ end
59
+
60
+ # Initialize verifier
61
+ #
62
+ # @param archive_path [String] Path to RAR archive
63
+ def initialize(archive_path)
64
+ @archive_path = archive_path
65
+ @recovery_record = nil
66
+ @parity_handler = nil
67
+ end
68
+
69
+ # Verify archive integrity
70
+ #
71
+ # @param use_recovery [Boolean] Use recovery records for verification
72
+ # @param verbose [Boolean] Enable verbose output
73
+ # @return [VerificationResult] Verification results
74
+ def verify(use_recovery: true, verbose: false)
75
+ result = VerificationResult.new
76
+
77
+ begin
78
+ # Open and parse archive
79
+ reader = Reader.new(@archive_path)
80
+ reader.open
81
+
82
+ result.files_total = reader.entries.size
83
+
84
+ # Detect recovery records
85
+ detect_recovery_records(reader, result) if use_recovery
86
+
87
+ # Verify each file
88
+ reader.entries.each do |entry|
89
+ file_valid = verify_entry(entry, verbose)
90
+
91
+ if file_valid
92
+ result.files_ok += 1
93
+ else
94
+ result.files_corrupted += 1
95
+ result.corrupted_files << entry.name
96
+ result.valid = false
97
+ end
98
+ end
99
+
100
+ # Check if corruption is recoverable
101
+ if result.files_corrupted.positive? && result.recovery_available
102
+ result.recoverable = check_recoverability(result.corrupt_blocks)
103
+ end
104
+ rescue StandardError => e
105
+ result.valid = false
106
+ result.errors << "Verification failed: #{e.message}"
107
+ end
108
+
109
+ result
110
+ end
111
+
112
+ # Verify single file entry
113
+ #
114
+ # @param entry [Models::RarEntry] Entry to verify
115
+ # @param verbose [Boolean] Print verbose output
116
+ # @return [Boolean] true if valid
117
+ def verify_entry(entry, verbose = false)
118
+ return true if entry.directory?
119
+
120
+ # For now, we rely on the decompressor to verify
121
+ # In a full implementation, we would check CRC32
122
+ valid = verify_entry_crc(entry)
123
+
124
+ puts "#{entry.name}: #{valid ? 'OK' : 'FAILED'}" if verbose
125
+
126
+ valid
127
+ rescue StandardError => e
128
+ puts "#{entry.name}: ERROR (#{e.message})" if verbose
129
+ false
130
+ end
131
+
132
+ # Quick test of archive
133
+ #
134
+ # @return [Boolean] true if archive can be opened
135
+ def quick_test
136
+ Reader.new(@archive_path).open
137
+ true
138
+ rescue StandardError
139
+ false
140
+ end
141
+
142
+ private
143
+
144
+ # Detect recovery records in archive
145
+ #
146
+ # @param reader [Reader] Archive reader
147
+ # @param result [VerificationResult] Result object to update
148
+ def detect_recovery_records(reader, result)
149
+ version = reader.header&.version || 4
150
+
151
+ @recovery_record = RecoveryRecord.new(version)
152
+
153
+ # Check for integrated recovery records
154
+ File.open(@archive_path, "rb") do |io|
155
+ @recovery_record.parse_from_archive(io, reader.archive_info.flags)
156
+ end
157
+
158
+ # Check for external .rev files
159
+ rev_files = @recovery_record.detect_external_files(@archive_path)
160
+ @recovery_record.load_external_files(rev_files) if rev_files.any?
161
+
162
+ result.recovery_available = @recovery_record.available?
163
+
164
+ # Initialize parity handler if recovery available
165
+ return unless result.recovery_available
166
+
167
+ @parity_handler = ParityHandler.new(@recovery_record)
168
+ @parity_handler.load_parity_data(@recovery_record.external_files)
169
+ end
170
+
171
+ # Verify entry CRC
172
+ #
173
+ # @param entry [Models::RarEntry] Entry to verify
174
+ # @return [Boolean] true if CRC matches
175
+ def verify_entry_crc(_entry)
176
+ # This is a placeholder - actual CRC verification would require
177
+ # extracting and checking the file
178
+ # For now, assume OK unless we have specific corruption data
179
+ true
180
+ end
181
+
182
+ # Check if corruption is recoverable
183
+ #
184
+ # @param corrupt_blocks [Array<Integer>] Corrupted block indices
185
+ # @return [Boolean] true if recoverable
186
+ def check_recoverability(corrupt_blocks)
187
+ return false unless @parity_handler
188
+
189
+ # Check if all corrupted blocks can be recovered
190
+ corrupt_blocks.all? { |idx| @parity_handler.can_recover?(idx) }
191
+ end
192
+ end
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,243 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "constants"
4
+ require_relative "models/rar_entry"
5
+
6
+ module Omnizip
7
+ module Formats
8
+ module Rar
9
+ # RAR block parser
10
+ # Parses different block types in RAR archives
11
+ class BlockParser
12
+ include Constants
13
+
14
+ attr_reader :version
15
+
16
+ # Initialize block parser
17
+ #
18
+ # @param version [Integer] RAR version (4 or 5)
19
+ def initialize(version)
20
+ @version = version
21
+ end
22
+
23
+ # Parse file block and create entry
24
+ #
25
+ # @param io [IO] Input stream
26
+ # @return [Models::RarEntry, nil] Parsed entry or nil
27
+ def parse_file_block(io)
28
+ if @version == 5
29
+ parse_rar5_file_block(io)
30
+ else
31
+ parse_rar4_file_block(io)
32
+ end
33
+ end
34
+
35
+ # Skip to next block
36
+ #
37
+ # @param io [IO] Input stream
38
+ # @param block_size [Integer] Block size to skip
39
+ def skip_block(io, block_size)
40
+ io.read(block_size) if block_size.positive?
41
+ end
42
+
43
+ private
44
+
45
+ # Parse RAR4 file block
46
+ #
47
+ # @param io [IO] Input stream
48
+ # @return [Models::RarEntry, nil] Parsed entry or nil
49
+ def parse_rar4_file_block(io)
50
+ entry = Models::RarEntry.new
51
+
52
+ # Read block header
53
+ read_uint16(io)
54
+ head_type = io.read(1)&.ord
55
+ return nil unless head_type == BLOCK_FILE
56
+
57
+ head_flags = read_uint16(io)
58
+ head_size = read_uint16(io)
59
+
60
+ # Read file header data
61
+ pack_size = read_uint32(io)
62
+ unpack_size = read_uint32(io)
63
+ host_os = io.read(1)&.ord
64
+ file_crc = read_uint32(io)
65
+ file_time = read_uint32(io)
66
+ unpack_ver = io.read(1)&.ord
67
+ method = io.read(1)&.ord
68
+ name_size = read_uint16(io)
69
+ attr = read_uint32(io)
70
+
71
+ # Read extended sizes if large file
72
+ if head_flags.anybits?(FILE_LARGE)
73
+ high_pack_size = read_uint32(io)
74
+ high_unpack_size = read_uint32(io)
75
+ pack_size |= (high_pack_size << 32)
76
+ unpack_size |= (high_unpack_size << 32)
77
+ end
78
+
79
+ # Read file name
80
+ name_bytes = io.read(name_size)
81
+ entry.name = decode_filename(name_bytes, head_flags)
82
+
83
+ # Set entry properties
84
+ entry.size = unpack_size
85
+ entry.compressed_size = pack_size
86
+ entry.crc = file_crc
87
+ entry.host_os = host_os
88
+ entry.method = method
89
+ entry.version = unpack_ver
90
+ entry.flags = head_flags
91
+ entry.attributes = attr
92
+ entry.mtime = dos_time_to_time(file_time)
93
+
94
+ # Set flags
95
+ entry.is_dir = head_flags.anybits?(FILE_DIRECTORY)
96
+ entry.encrypted = head_flags.anybits?(FILE_ENCRYPTED)
97
+ entry.split_before = head_flags.anybits?(FILE_SPLIT_BEFORE)
98
+ entry.split_after = head_flags.anybits?(FILE_SPLIT_AFTER)
99
+
100
+ # Skip remaining header data and file data
101
+ # Fixed fields: TYPE(1) + FLAGS(2) + SIZE(2) + PACK_SIZE(4) + UNPACK_SIZE(4) +
102
+ # HOST_OS(1) + FILE_CRC(4) + FILE_TIME(4) + VERSION(1) + METHOD(1) +
103
+ # NAME_SIZE(2) + ATTR(4) = 30 bytes
104
+ remaining = head_size - (name_size + 30)
105
+ remaining += 8 if head_flags.anybits?(FILE_LARGE)
106
+ io.read(remaining) if remaining.positive?
107
+ io.read(pack_size) # Skip compressed data
108
+
109
+ entry
110
+ end
111
+
112
+ # Parse RAR5 file block
113
+ #
114
+ # @param io [IO] Input stream
115
+ # @return [Models::RarEntry, nil] Parsed entry or nil
116
+ def parse_rar5_file_block(io)
117
+ entry = Models::RarEntry.new
118
+
119
+ # Read block header
120
+ read_uint32(io)
121
+ read_vint(io)
122
+ header_type = read_vint(io)
123
+ return nil unless header_type == RAR5_HEADER_FILE
124
+
125
+ header_flags = read_vint(io)
126
+
127
+ # Read file header
128
+ file_flags = read_vint(io)
129
+ unpack_size = read_vint(io)
130
+ attr = read_vint(io)
131
+
132
+ # Read modification time if present
133
+ mtime = nil
134
+ mtime = read_uint32(io) if file_flags.anybits?(0x02)
135
+
136
+ # Read CRC if present
137
+ crc = nil
138
+ crc = read_uint32(io) if file_flags.anybits?(0x04)
139
+
140
+ # Read compression info
141
+ read_vint(io)
142
+ host_os = read_vint(io)
143
+ name_length = read_vint(io)
144
+
145
+ # Read file name
146
+ name_bytes = io.read(name_length)
147
+ entry.name = name_bytes.force_encoding("UTF-8")
148
+
149
+ # Set entry properties
150
+ entry.size = unpack_size
151
+ entry.compressed_size = 0 # Not directly available in header
152
+ entry.crc = crc
153
+ entry.host_os = host_os
154
+ entry.flags = file_flags
155
+ entry.attributes = attr
156
+ entry.mtime = Time.at(mtime) if mtime
157
+ entry.is_dir = file_flags.anybits?(RAR5_FLAG_IS_DIR)
158
+ entry.version = 5
159
+
160
+ # Read extra area if present
161
+ if header_flags.anybits?(RAR5_FLAG_EXTRA_AREA)
162
+ extra_size = read_vint(io)
163
+ io.read(extra_size) if extra_size.positive?
164
+ end
165
+
166
+ # Read data area if present
167
+ if header_flags.anybits?(RAR5_FLAG_DATA_AREA)
168
+ data_size = read_vint(io)
169
+ io.read(data_size) if data_size.positive?
170
+ end
171
+
172
+ entry
173
+ end
174
+
175
+ # Decode filename from bytes
176
+ #
177
+ # @param bytes [String] Raw filename bytes
178
+ # @param flags [Integer] Block flags
179
+ # @return [String] Decoded filename
180
+ def decode_filename(bytes, flags)
181
+ if flags.nobits?(FILE_UNICODE)
182
+ # ASCII filename
183
+ bytes.force_encoding("ASCII-8BIT")
184
+ else
185
+ # Unicode filename
186
+ bytes.force_encoding("UTF-8")
187
+ end
188
+ end
189
+
190
+ # Convert DOS time to Ruby Time
191
+ #
192
+ # @param dos_time [Integer] DOS time value
193
+ # @return [Time] Ruby time object
194
+ def dos_time_to_time(dos_time)
195
+ sec = (dos_time & 0x1F) * 2
196
+ min = (dos_time >> 5) & 0x3F
197
+ hour = (dos_time >> 11) & 0x1F
198
+ day = (dos_time >> 16) & 0x1F
199
+ month = (dos_time >> 21) & 0x0F
200
+ year = ((dos_time >> 25) & 0x7F) + 1980
201
+
202
+ Time.new(year, month, day, hour, min, sec)
203
+ rescue ArgumentError
204
+ Time.now
205
+ end
206
+
207
+ # Read 16-bit unsigned integer (little-endian)
208
+ def read_uint16(io)
209
+ bytes = io.read(2)
210
+ return 0 unless bytes&.size == 2
211
+
212
+ bytes.unpack1("v")
213
+ end
214
+
215
+ # Read 32-bit unsigned integer (little-endian)
216
+ def read_uint32(io)
217
+ bytes = io.read(4)
218
+ return 0 unless bytes&.size == 4
219
+
220
+ bytes.unpack1("V")
221
+ end
222
+
223
+ # Read variable-length integer (RAR5)
224
+ def read_vint(io)
225
+ result = 0
226
+ shift = 0
227
+
228
+ loop do
229
+ byte = io.read(1)&.ord
230
+ return result unless byte
231
+
232
+ result |= (byte & 0x7F) << shift
233
+ break if byte.nobits?(0x80)
234
+
235
+ shift += 7
236
+ end
237
+
238
+ result
239
+ end
240
+ end
241
+ end
242
+ end
243
+ end
@@ -0,0 +1,180 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2025 Ribose Inc.
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a
6
+ # copy of this software and associated documentation files (the "Software"),
7
+ # to deal in the Software without restriction, including without limitation
8
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
9
+ # and/or sell copies of the Software, and to permit persons to whom the
10
+ # Software is furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21
+ # DEALINGS IN THE SOFTWARE.
22
+
23
+ module Omnizip
24
+ module Formats
25
+ module Rar
26
+ module Compression
27
+ # Bit-level I/O stream for RAR compression algorithms
28
+ #
29
+ # Provides methods to read and write individual bits from byte streams.
30
+ # This is a shared utility used by PPMd, LZ77+Huffman, and other RAR
31
+ # compression algorithms that need bit-level access.
32
+ #
33
+ # Responsibilities:
34
+ # - ONE responsibility: Bit-level I/O operations
35
+ # - Read bits from byte stream
36
+ # - Write bits to byte stream
37
+ # - Manage bit buffer and byte alignment
38
+ class BitStream
39
+ # Initialize a new bit stream
40
+ #
41
+ # @param io [IO] The underlying byte stream
42
+ # @param mode [Symbol] :read or :write
43
+ def initialize(io, mode = :read)
44
+ @io = io
45
+ @mode = mode
46
+ @buffer = 0
47
+ @bits_in_buffer = 0
48
+ end
49
+
50
+ # Read specified number of bits
51
+ #
52
+ # @param count [Integer] Number of bits to read (1-32)
53
+ # @return [Integer] The bits read as an integer
54
+ def read_bits(count)
55
+ unless @mode == :read
56
+ raise ArgumentError,
57
+ "Can only read in read mode"
58
+ end
59
+ raise ArgumentError, "Count must be 1-32" unless count.between?(1,
60
+ 32)
61
+
62
+ result = 0
63
+
64
+ count.times do
65
+ result = (result << 1) | read_bit
66
+ end
67
+
68
+ result
69
+ end
70
+
71
+ # Read a single bit
72
+ #
73
+ # @return [Integer] 0 or 1
74
+ def read_bit
75
+ unless @mode == :read
76
+ raise ArgumentError,
77
+ "Can only read in read mode"
78
+ end
79
+
80
+ if @bits_in_buffer.zero?
81
+ fill_buffer
82
+ end
83
+
84
+ @bits_in_buffer -= 1
85
+ (@buffer >> @bits_in_buffer) & 1
86
+ end
87
+
88
+ # Write specified number of bits
89
+ #
90
+ # @param value [Integer] The value to write
91
+ # @param count [Integer] Number of bits to write (1-32)
92
+ # @return [void]
93
+ def write_bits(value, count)
94
+ unless @mode == :write
95
+ raise ArgumentError,
96
+ "Can only write in write mode"
97
+ end
98
+ raise ArgumentError, "Count must be 1-32" unless count.between?(1,
99
+ 32)
100
+
101
+ count.times do |i|
102
+ bit = (value >> (count - 1 - i)) & 1
103
+ write_bit(bit)
104
+ end
105
+ end
106
+
107
+ # Write a single bit
108
+ #
109
+ # @param bit [Integer] 0 or 1
110
+ # @return [void]
111
+ def write_bit(bit)
112
+ unless @mode == :write
113
+ raise ArgumentError,
114
+ "Can only write in write mode"
115
+ end
116
+
117
+ @buffer = (@buffer << 1) | (bit & 1)
118
+ @bits_in_buffer += 1
119
+
120
+ flush_buffer if @bits_in_buffer == 8
121
+ end
122
+
123
+ # Align to byte boundary (read mode)
124
+ #
125
+ # @return [void]
126
+ def align_to_byte
127
+ return unless @mode == :read
128
+
129
+ @bits_in_buffer = 0
130
+ @buffer = 0
131
+ end
132
+
133
+ # Flush any remaining bits (write mode)
134
+ #
135
+ # @return [void]
136
+ def flush
137
+ return unless @mode == :write
138
+ return if @bits_in_buffer.zero?
139
+
140
+ # Pad with zeros to complete byte
141
+ padding = 8 - @bits_in_buffer
142
+ @buffer <<= padding
143
+ @io.write([@buffer].pack("C"))
144
+ @buffer = 0
145
+ @bits_in_buffer = 0
146
+ end
147
+
148
+ # Check if at end of stream
149
+ #
150
+ # @return [Boolean] True if no more data available
151
+ def eof?
152
+ @mode == :read && @bits_in_buffer.zero? && @io.eof?
153
+ end
154
+
155
+ private
156
+
157
+ # Fill buffer with next byte from stream
158
+ #
159
+ # @return [void]
160
+ def fill_buffer
161
+ byte = @io.read(1)
162
+ raise EOFError, "Unexpected end of stream" if byte.nil?
163
+
164
+ @buffer = byte.unpack1("C")
165
+ @bits_in_buffer = 8
166
+ end
167
+
168
+ # Flush full buffer byte to stream
169
+ #
170
+ # @return [void]
171
+ def flush_buffer
172
+ @io.write([@buffer].pack("C"))
173
+ @buffer = 0
174
+ @bits_in_buffer = 0
175
+ end
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end