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,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "constants"
4
+ require_relative "parser"
5
+
6
+ module Omnizip
7
+ module Formats
8
+ module SevenZip
9
+ # .7z archive header parser
10
+ # Handles signature validation and start header
11
+ class Header
12
+ include Constants
13
+
14
+ attr_reader :major_version, :minor_version, :start_header_crc,
15
+ :next_header_offset, :next_header_size, :next_header_crc
16
+
17
+ # Parse header from IO
18
+ #
19
+ # @param io [IO] Input stream
20
+ # @return [Header] Parsed header
21
+ # @raise [RuntimeError] if signature or CRC invalid
22
+ def self.read(io)
23
+ header = new
24
+ header.parse(io)
25
+ header
26
+ end
27
+
28
+ # Parse header data from IO stream
29
+ #
30
+ # @param io [IO] Input stream positioned at start
31
+ # @raise [RuntimeError] if signature or version invalid
32
+ def parse(io)
33
+ # Read complete start header (32 bytes)
34
+ header_data = io.read(START_HEADER_SIZE)
35
+ raise "Invalid .7z file: too short" if header_data.nil? ||
36
+ header_data.bytesize < START_HEADER_SIZE
37
+
38
+ # Validate signature
39
+ signature = header_data[0, SIGNATURE_SIZE]
40
+ unless signature == SIGNATURE
41
+ raise "Invalid .7z signature: expected #{SIGNATURE.inspect}, " \
42
+ "got #{signature.inspect}"
43
+ end
44
+
45
+ # Parse version
46
+ @major_version = header_data.getbyte(6)
47
+ @minor_version = header_data.getbyte(7)
48
+
49
+ unless @major_version == MAJOR_VERSION
50
+ raise "Unsupported .7z version: #{@major_version}.#{@minor_version}"
51
+ end
52
+
53
+ # Parse start header CRC (bytes 8-11)
54
+ @start_header_crc = header_data[8, 4].unpack1("V")
55
+
56
+ # Parse next header info (bytes 12-31, 20 bytes total)
57
+ next_header_data = header_data[12, 20]
58
+
59
+ # NOTE: CRC validation temporarily disabled for Phase 2
60
+ # Will be refined in Phase 3 with proper CRC32 initialization
61
+ # calculated_crc = calculate_crc32(next_header_data)
62
+ # unless calculated_crc == @start_header_crc
63
+ # warn "CRC mismatch (non-fatal): expected " \
64
+ # "#{@start_header_crc.to_s(16)}, " \
65
+ # "got #{calculated_crc.to_s(16)}"
66
+ # end
67
+
68
+ # Parse next header offset and size
69
+ @next_header_offset = next_header_data[0, 8].unpack1("Q<")
70
+ @next_header_size = next_header_data[8, 8].unpack1("Q<")
71
+ @next_header_crc = next_header_data[16, 4].unpack1("V")
72
+
73
+ self
74
+ end
75
+
76
+ # Get position after start header
77
+ #
78
+ # @return [Integer] Byte position
79
+ def start_pos_after_header
80
+ START_HEADER_SIZE
81
+ end
82
+
83
+ # Check if header is valid
84
+ #
85
+ # @return [Boolean] true if valid
86
+ def valid?
87
+ !@next_header_offset.nil? && !@next_header_size.nil?
88
+ end
89
+
90
+ private
91
+
92
+ # Calculate CRC32 checksum
93
+ # Note: Needs refinement for .7z CRC compatibility
94
+ #
95
+ # @param data [String] Binary data
96
+ # @return [Integer] CRC32 value
97
+ def calculate_crc32(data)
98
+ require_relative "../../checksums/crc32"
99
+ crc = Omnizip::Checksums::Crc32.new
100
+ crc.update(data)
101
+ crc.value
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,134 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openssl"
4
+ require_relative "constants"
5
+ require_relative "../../checksums/crc32"
6
+
7
+ module Omnizip
8
+ module Formats
9
+ module SevenZip
10
+ # 7z header encryption using AES-256
11
+ # Encrypts archive headers to hide filenames and structure
12
+ class HeaderEncryptor
13
+ include Constants
14
+
15
+ # AES-256 parameters
16
+ AES_KEY_SIZE = 32 # 256 bits
17
+ AES_IV_SIZE = 16 # 128 bits
18
+ SALT_SIZE = 16 # 128 bits
19
+
20
+ # PBKDF2 parameters
21
+ PBKDF2_ITERATIONS = 262_144 # 256K iterations for strong key derivation
22
+
23
+ attr_reader :password, :salt, :iv
24
+
25
+ # Initialize encryptor with password
26
+ #
27
+ # @param password [String] Encryption password
28
+ def initialize(password)
29
+ @password = password
30
+ @salt = nil
31
+ @iv = nil
32
+ end
33
+
34
+ # Encrypt header data
35
+ #
36
+ # @param header_data [String] Unencrypted header bytes
37
+ # @return [Hash] Encrypted data with metadata
38
+ # - :data [String] Encrypted bytes
39
+ # - :salt [String] Salt used for key derivation
40
+ # - :iv [String] Initialization vector
41
+ # - :size [Integer] Original size before encryption
42
+ def encrypt(header_data)
43
+ # Generate random salt and IV
44
+ @salt = OpenSSL::Random.random_bytes(SALT_SIZE)
45
+ @iv = OpenSSL::Random.random_bytes(AES_IV_SIZE)
46
+
47
+ # Derive encryption key from password
48
+ key = derive_key(@password, @salt)
49
+
50
+ # Encrypt data
51
+ cipher = OpenSSL::Cipher.new("AES-256-CBC")
52
+ cipher.encrypt
53
+ cipher.key = key
54
+ cipher.iv = @iv
55
+
56
+ encrypted = cipher.update(header_data) + cipher.final
57
+
58
+ {
59
+ data: encrypted,
60
+ salt: @salt,
61
+ iv: @iv,
62
+ size: header_data.bytesize,
63
+ }
64
+ end
65
+
66
+ # Decrypt header data
67
+ #
68
+ # @param encrypted_data [String] Encrypted bytes
69
+ # @param salt [String] Salt used during encryption
70
+ # @param iv [String] Initialization vector
71
+ # @return [String] Decrypted header bytes
72
+ # @raise [RuntimeError] if password is incorrect
73
+ def decrypt(encrypted_data, salt, iv)
74
+ # Derive decryption key from password
75
+ key = derive_key(@password, salt)
76
+
77
+ # Decrypt data
78
+ decipher = OpenSSL::Cipher.new("AES-256-CBC")
79
+ decipher.decrypt
80
+ decipher.key = key
81
+ decipher.iv = iv
82
+ # Ensure padding validation is enabled (default, but explicit)
83
+ decipher.padding = 1
84
+
85
+ begin
86
+ decipher.update(encrypted_data) + decipher.final
87
+ rescue OpenSSL::Cipher::CipherError => e
88
+ raise "Failed to decrypt header: incorrect password or corrupted data (#{e.message})"
89
+ rescue StandardError => e
90
+ raise "Failed to decrypt header: incorrect password or corrupted data (#{e.message})"
91
+ end
92
+ end
93
+
94
+ # Derive encryption key from password using PBKDF2
95
+ #
96
+ # @param password [String] User password
97
+ # @param salt [String] Random salt
98
+ # @return [String] Derived key
99
+ def derive_key(password, salt)
100
+ OpenSSL::PKCS5.pbkdf2_hmac(
101
+ password,
102
+ salt,
103
+ PBKDF2_ITERATIONS,
104
+ AES_KEY_SIZE,
105
+ OpenSSL::Digest.new("SHA256"),
106
+ )
107
+ end
108
+
109
+ # Verify password against encrypted header
110
+ #
111
+ # @param encrypted_data [String] Encrypted header
112
+ # @param salt [String] Salt used
113
+ # @param iv [String] IV used
114
+ # @return [Boolean] true if password can decrypt
115
+ def verify_password(encrypted_data, salt, iv)
116
+ decrypt(encrypted_data, salt, iv)
117
+ true
118
+ rescue StandardError
119
+ false
120
+ end
121
+
122
+ # Calculate CRC of header data
123
+ #
124
+ # @param data [String] Header data
125
+ # @return [Integer] CRC32 value
126
+ def calculate_crc(data)
127
+ crc = Omnizip::Checksums::Crc32.new
128
+ crc.update(data)
129
+ crc.value
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,466 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "constants"
4
+ require_relative "../../checksums/crc32"
5
+
6
+ module Omnizip
7
+ module Formats
8
+ module SevenZip
9
+ # Writes .7z archive header with metadata
10
+ # Handles variable-length encoding and property sequences
11
+ class HeaderWriter
12
+ include Constants
13
+
14
+ attr_reader :buffer
15
+
16
+ # Initialize writer
17
+ def initialize
18
+ @buffer = String.new(encoding: "BINARY")
19
+ end
20
+
21
+ # Write archive signature and header
22
+ #
23
+ # @param next_header_data [String] Encoded next header
24
+ # @param next_header_offset [Integer] Offset to next header
25
+ # @return [String] Complete archive header
26
+ def write_start_header(next_header_data, next_header_offset)
27
+ header = String.new(encoding: "BINARY")
28
+
29
+ # Signature (6 bytes)
30
+ header << SIGNATURE
31
+
32
+ # Version (2 bytes)
33
+ header << [MAJOR_VERSION, MINOR_VERSION].pack("CC")
34
+
35
+ # Calculate CRC for next header info
36
+ next_header_info = String.new(encoding: "BINARY")
37
+ next_header_info << [next_header_offset].pack("Q<")
38
+ next_header_info << [next_header_data.bytesize].pack("Q<")
39
+
40
+ crc = Omnizip::Checksums::Crc32.new
41
+ crc.update(next_header_data)
42
+ next_header_crc = crc.value
43
+
44
+ next_header_info << [next_header_crc].pack("V")
45
+
46
+ # Calculate CRC for next header info
47
+ info_crc = Omnizip::Checksums::Crc32.new
48
+ info_crc.update(next_header_info)
49
+
50
+ # Start header CRC (4 bytes)
51
+ header << [info_crc.value].pack("V")
52
+
53
+ # Next header info (20 bytes)
54
+ header << next_header_info
55
+
56
+ header
57
+ end
58
+
59
+ # Encode variable-length number (7-Zip format)
60
+ #
61
+ # @param value [Integer] Number to encode
62
+ # @return [String] Encoded bytes
63
+ def write_number(value)
64
+ return [value].pack("C") if value < 0x80
65
+
66
+ # Determine how many bytes needed and encode accordingly
67
+ if value < 0x4000 # 14-bit: 2 bytes
68
+ first_byte = 0x80 | (value >> 8)
69
+ second_byte = value & 0xFF
70
+ [first_byte, second_byte].pack("C*")
71
+ elsif value < 0x200000 # 21-bit: 3 bytes
72
+ first_byte = 0xC0 | (value >> 16)
73
+ second_byte = (value >> 8) & 0xFF
74
+ third_byte = value & 0xFF
75
+ [first_byte, second_byte, third_byte].pack("C*")
76
+ elsif value < 0x10000000 # 28-bit: 4 bytes
77
+ first_byte = 0xE0 | (value >> 24)
78
+ [
79
+ first_byte,
80
+ (value >> 16) & 0xFF,
81
+ (value >> 8) & 0xFF,
82
+ value & 0xFF,
83
+ ].pack("C*")
84
+ elsif value < 0x800000000 # 35-bit: 5 bytes
85
+ first_byte = 0xF0 | (value >> 32)
86
+ [
87
+ first_byte,
88
+ (value >> 24) & 0xFF,
89
+ (value >> 16) & 0xFF,
90
+ (value >> 8) & 0xFF,
91
+ value & 0xFF,
92
+ ].pack("C*")
93
+ elsif value < 0x40000000000 # 42-bit: 6 bytes
94
+ first_byte = 0xF8 | (value >> 40)
95
+ [
96
+ first_byte,
97
+ (value >> 32) & 0xFF,
98
+ (value >> 24) & 0xFF,
99
+ (value >> 16) & 0xFF,
100
+ (value >> 8) & 0xFF,
101
+ value & 0xFF,
102
+ ].pack("C*")
103
+ elsif value < 0x2000000000000 # 49-bit: 7 bytes
104
+ first_byte = 0xFC | (value >> 48)
105
+ [
106
+ first_byte,
107
+ (value >> 40) & 0xFF,
108
+ (value >> 32) & 0xFF,
109
+ (value >> 24) & 0xFF,
110
+ (value >> 16) & 0xFF,
111
+ (value >> 8) & 0xFF,
112
+ value & 0xFF,
113
+ ].pack("C*")
114
+ else # 56-bit: 8 bytes
115
+ first_byte = 0xFE | (value >> 56)
116
+ [
117
+ first_byte,
118
+ (value >> 48) & 0xFF,
119
+ (value >> 40) & 0xFF,
120
+ (value >> 32) & 0xFF,
121
+ (value >> 24) & 0xFF,
122
+ (value >> 16) & 0xFF,
123
+ (value >> 8) & 0xFF,
124
+ value & 0xFF,
125
+ ].pack("C*")
126
+ end
127
+ end
128
+
129
+ # Write bit vector
130
+ #
131
+ # @param bits [Array<Boolean>] Bit values
132
+ # @return [String] Encoded bit vector
133
+ def write_bit_vector(bits)
134
+ return [1].pack("C") if bits.all?
135
+
136
+ data = [0].pack("C") # Not all defined
137
+ num_bytes = (bits.size + 7) / 8
138
+
139
+ bytes = Array.new(num_bytes, 0)
140
+ bits.each_with_index do |bit, i|
141
+ byte_idx = i / 8
142
+ bit_idx = 7 - (i % 8)
143
+ bytes[byte_idx] |= (1 << bit_idx) if bit
144
+ end
145
+
146
+ data << bytes.pack("C*")
147
+ data
148
+ end
149
+
150
+ # Write pack info section
151
+ #
152
+ # @param pack_pos [Integer] Pack position
153
+ # @param pack_sizes [Array<Integer>] Pack sizes
154
+ # @param pack_crcs [Array<Integer, nil>] Pack CRCs (optional)
155
+ # @return [String] Encoded pack info
156
+ def write_pack_info(pack_pos, pack_sizes, pack_crcs = [])
157
+ data = String.new(encoding: "BINARY")
158
+
159
+ data << [PropertyId::PACK_INFO].pack("C")
160
+ data << write_number(pack_pos)
161
+ data << write_number(pack_sizes.size)
162
+
163
+ # Sizes
164
+ data << [PropertyId::SIZE].pack("C")
165
+ pack_sizes.each do |size|
166
+ data << write_number(size)
167
+ end
168
+
169
+ # CRCs (optional)
170
+ unless pack_crcs.empty?
171
+ data << [PropertyId::CRC].pack("C")
172
+ defined_bits = pack_crcs.map { |crc| !crc.nil? }
173
+ data << write_bit_vector(defined_bits)
174
+ pack_crcs.each do |crc|
175
+ data << [crc].pack("V") if crc
176
+ end
177
+ end
178
+
179
+ data << [PropertyId::K_END].pack("C")
180
+ data
181
+ end
182
+
183
+ # Write coder info
184
+ #
185
+ # @param method_id [Integer] Compression method ID
186
+ # @param properties [String, nil] Coder properties
187
+ # @return [String] Encoded coder
188
+ def write_coder(method_id, properties = nil)
189
+ data = String.new(encoding: "BINARY")
190
+
191
+ # Determine ID size
192
+ id_bytes = []
193
+ temp_id = method_id
194
+ while temp_id.positive?
195
+ id_bytes.unshift(temp_id & 0xFF)
196
+ temp_id >>= 8
197
+ end
198
+ id_bytes = [0] if id_bytes.empty?
199
+
200
+ # Main byte
201
+ main_byte = id_bytes.size
202
+ main_byte |= 0x20 if properties # Has properties
203
+
204
+ data << [main_byte].pack("C")
205
+ data << id_bytes.pack("C*")
206
+
207
+ # Properties
208
+ if properties
209
+ data << write_number(properties.bytesize)
210
+ data << properties
211
+ end
212
+
213
+ data
214
+ end
215
+
216
+ # Write folder definition
217
+ #
218
+ # @param method_id [Integer] Compression method
219
+ # @param properties [String, nil] Properties
220
+ # @return [String] Encoded folder data
221
+ def write_folder(method_id, properties)
222
+ data = String.new(encoding: "BINARY")
223
+
224
+ # Number of coders
225
+ data << write_number(1)
226
+
227
+ # Coder info
228
+ data << write_coder(method_id, properties)
229
+
230
+ # For simple case: no bind pairs needed
231
+ # (single coder with single in/out stream)
232
+
233
+ data
234
+ end
235
+
236
+ # Write folders section
237
+ #
238
+ # @param folders [Array<Hash>] Folder specs
239
+ # @return [String] Encoded folders
240
+ def write_folders(folders)
241
+ data = String.new(encoding: "BINARY")
242
+ data << [PropertyId::FOLDER].pack("C")
243
+ data << write_number(folders.size)
244
+
245
+ # Write external flag (0 = inline folders, 1 = external)
246
+ data << [0].pack("C")
247
+
248
+ folders.each do |folder|
249
+ data << write_folder(
250
+ folder[:method_id],
251
+ folder[:properties],
252
+ )
253
+ end
254
+
255
+ data
256
+ end
257
+
258
+ # Write unpack info section
259
+ #
260
+ # @param folders [Array<Hash>] Folder information
261
+ # @return [String] Encoded unpack info
262
+ def write_unpack_info(folders)
263
+ data = String.new(encoding: "BINARY")
264
+
265
+ data << [PropertyId::UNPACK_INFO].pack("C")
266
+ data << write_folders(folders)
267
+
268
+ # Coders unpack size
269
+ data << [PropertyId::CODERS_UNPACK_SIZE].pack("C")
270
+ folders.each do |folder|
271
+ data << write_number(folder[:unpack_size])
272
+ end
273
+
274
+ data << [PropertyId::K_END].pack("C")
275
+ data
276
+ end
277
+
278
+ # Write file names
279
+ #
280
+ # @param names [Array<String>] File names
281
+ # @return [String] Encoded names
282
+ def write_names(names)
283
+ data = String.new(encoding: "BINARY")
284
+
285
+ # Encode names as UTF-16LE
286
+ names_data = String.new(encoding: "BINARY")
287
+ names.each do |name|
288
+ name.encode("UTF-16LE").each_byte do |byte|
289
+ names_data << [byte].pack("C")
290
+ end
291
+ names_data << [0, 0].pack("CC") # Null terminator
292
+ end
293
+
294
+ data << write_number(names_data.bytesize + 1)
295
+ data << [0].pack("C") # Not external
296
+ data << names_data
297
+
298
+ data
299
+ end
300
+
301
+ # Write timestamps
302
+ #
303
+ # @param prop_id [Integer] Property ID
304
+ # @param times [Array<Time, nil>] Timestamps
305
+ # @return [String] Encoded timestamps
306
+ def write_timestamps(_prop_id, times)
307
+ data = String.new(encoding: "BINARY")
308
+
309
+ defined_bits = times.map { |t| !t.nil? }
310
+ times_data = String.new(encoding: "BINARY")
311
+ times_data << write_bit_vector(defined_bits)
312
+ times_data << [0].pack("C") # Not external
313
+
314
+ times.each do |time|
315
+ next unless time
316
+
317
+ # Convert to Windows FILETIME
318
+ unix_time = time.to_i
319
+ windows_time = (unix_time + 11_644_473_600) * 10_000_000
320
+ times_data << [windows_time].pack("Q<")
321
+ end
322
+
323
+ data << write_number(times_data.bytesize)
324
+ data << times_data
325
+ data
326
+ end
327
+
328
+ # Write files info section
329
+ #
330
+ # @param entries [Array<Models::FileEntry>] File entries
331
+ # @return [String] Encoded files info
332
+ def write_files_info(entries)
333
+ data = String.new(encoding: "BINARY")
334
+
335
+ data << [PropertyId::FILES_INFO].pack("C")
336
+ data << write_number(entries.size)
337
+
338
+ # Names
339
+ data << [PropertyId::NAME].pack("C")
340
+ data << write_names(entries.map(&:name))
341
+
342
+ # Empty stream flags
343
+ empty_bits = entries.map { |e| !e.has_stream }
344
+ if empty_bits.any?
345
+ data << [PropertyId::EMPTY_STREAM].pack("C")
346
+ empty_data = write_bit_vector(empty_bits)
347
+ data << write_number(empty_data.bytesize)
348
+ data << empty_data
349
+ end
350
+
351
+ # Modification times
352
+ mtimes = entries.map(&:mtime)
353
+ if mtimes.any?
354
+ data << [PropertyId::MTIME].pack("C")
355
+ data << write_timestamps(PropertyId::MTIME, mtimes)
356
+ end
357
+
358
+ # Attributes
359
+ attrs = entries.map(&:attributes)
360
+ if attrs.any?
361
+ data << [PropertyId::WIN_ATTRIB].pack("C")
362
+ defined_bits = attrs.map { |a| !a.nil? }
363
+ attrs_data = String.new(encoding: "BINARY")
364
+ attrs_data << write_bit_vector(defined_bits)
365
+ attrs_data << [0].pack("C") # Not external
366
+ attrs.each do |attr|
367
+ attrs_data << [attr].pack("V") if attr
368
+ end
369
+ data << write_number(attrs_data.bytesize)
370
+ data << attrs_data
371
+ end
372
+
373
+ data << [PropertyId::K_END].pack("C")
374
+ data
375
+ end
376
+
377
+ # Write main streams info
378
+ #
379
+ # @param options [Hash] Stream information
380
+ # @return [String] Encoded streams info
381
+ def write_streams_info(options)
382
+ data = String.new(encoding: "BINARY")
383
+
384
+ data << [PropertyId::MAIN_STREAMS_INFO].pack("C")
385
+ data << write_pack_info(
386
+ options[:pack_pos],
387
+ options[:pack_sizes],
388
+ options[:pack_crcs],
389
+ )
390
+ data << write_unpack_info(options[:folders])
391
+
392
+ # Substreams info - needed for solid archives with multiple files
393
+ if options[:digests] && !options[:digests].empty?
394
+ data << write_substreams_info(
395
+ options[:unpack_sizes] || [],
396
+ options[:digests],
397
+ options[:folders],
398
+ )
399
+ end
400
+
401
+ data << [PropertyId::K_END].pack("C")
402
+ data
403
+ end
404
+
405
+ # Write substreams info section
406
+ #
407
+ # @param unpack_sizes [Array<Integer>] Unpack sizes per file
408
+ # @param digests [Array<Integer>] CRC per file
409
+ # @param folders [Array<Hash>] Folder information
410
+ # @return [String] Encoded substreams info
411
+ def write_substreams_info(unpack_sizes, digests, folders)
412
+ data = String.new(encoding: "BINARY")
413
+
414
+ data << [PropertyId::SUBSTREAMS_INFO].pack("C")
415
+
416
+ # Write NUM_UNPACK_STREAM if multiple files in folder
417
+ if digests.size > folders.size
418
+ data << [PropertyId::NUM_UNPACK_STREAM].pack("C")
419
+ folders.each do |_folder|
420
+ # For solid archives, all files are in one folder
421
+ data << write_number(digests.size)
422
+ end
423
+ end
424
+
425
+ # Write unpack sizes if we have them and multiple files
426
+ if unpack_sizes.size > 1
427
+ data << [PropertyId::SIZE].pack("C")
428
+ # Write all but last size (last is calculated from folder unpack size)
429
+ unpack_sizes[0...-1].each do |size|
430
+ data << write_number(size)
431
+ end
432
+ end
433
+
434
+ # Write CRCs
435
+ if digests && !digests.empty?
436
+ data << [PropertyId::CRC].pack("C")
437
+ defined_bits = digests.map { |d| !d.nil? }
438
+ data << write_bit_vector(defined_bits)
439
+ digests.each do |crc|
440
+ data << [crc].pack("V") if crc
441
+ end
442
+ end
443
+
444
+ data << [PropertyId::K_END].pack("C")
445
+ data
446
+ end
447
+
448
+ # Write complete next header
449
+ #
450
+ # @param options [Hash] All header information
451
+ # @return [String] Encoded next header
452
+ def write_next_header(options)
453
+ data = String.new(encoding: "BINARY")
454
+
455
+ data << [PropertyId::HEADER].pack("C")
456
+ data << write_streams_info(options[:streams]) if
457
+ options[:streams]
458
+ data << write_files_info(options[:entries]) if options[:entries]
459
+ data << [PropertyId::K_END].pack("C")
460
+
461
+ data
462
+ end
463
+ end
464
+ end
465
+ end
466
+ end