1+ //
2+ // Created by Giuseppe Francione on 20/05/2026.
3+ //
4+
5+ #include " ../../include/kanzi_processor.hpp"
6+ #include " ../../include/logger.hpp"
7+ #include " ../../include/file_utils.hpp"
8+ #include " ../../include/random_utils.hpp"
9+ #include " ../../include/file_type.hpp"
10+ #include < Context.hpp>
11+ #include < array>
12+ #include " app/BlockCompressor.hpp"
13+ #include " app/BlockDecompressor.hpp"
14+
15+ namespace chisel {
16+
17+ namespace fs = std::filesystem;
18+
19+
20+ std::optional<ExtractedContent> KanziProcessor::prepare_extraction (const fs::path& input_path) {
21+ Logger::log (LogLevel::Debug, " Starting kanzi decompression" , get_name ());
22+
23+ ExtractedContent content;
24+ content.original_path = input_path;
25+ content.temp_dir = make_temp_dir_for (input_path, " kanzi" );
26+
27+ content.format = ContainerFormat::Kanzi;
28+
29+ fs::path raw_bin = content.temp_dir / " stream.raw" ;
30+
31+ try {
32+ kanzi::Context ctx;
33+ ctx.putString (" inputName" , input_path.string ());
34+ ctx.putString (" outputName" , raw_bin.string ());
35+ ctx.putInt (" jobs" , 1 );
36+ ctx.putInt (" overwrite" , 1 );
37+ ctx.putInt (" verbosity" , 0 );
38+
39+ kanzi::BlockDecompressor bd (ctx);
40+ uint64_t read = 0 ;
41+
42+ if (bd.decompress (read) != 0 ) {
43+ Logger::log (LogLevel::Error, " Kanzi decompression returned non-zero status" , get_name ());
44+ return std::nullopt ;
45+ }
46+ } catch (const std::exception& e) {
47+ Logger::log (LogLevel::Error, std::string (" Kanzi exception: " ) + e.what (), get_name ());
48+ return std::nullopt ;
49+ }
50+
51+ // expose the uncompressed stream to the processing pipeline
52+ content.extracted_files .push_back (raw_bin);
53+ return content;
54+ }
55+
56+ fs::path KanziProcessor::finalize_extraction (const ExtractedContent& content, const ProcessingOptions& /* options*/ ) {
57+ if (content.extracted_files .empty ()) {
58+ Logger::log (LogLevel::Error, " No raw stream found for kanzi repacking" , get_name ());
59+ throw std::runtime_error (" KanziProcessor: empty extracted files" );
60+ }
61+
62+ Logger::log (LogLevel::Debug, " Repacking kanzi" , get_name ());
63+
64+ const fs::path& processed_bin = content.extracted_files .front ();
65+ fs::path out_knz = fs::temp_directory_path () /
66+ (content.original_path .stem ().string () + " _tmp" + RandomUtils::random_suffix () + " .knz" );
67+
68+ try {
69+ kanzi::Context ctx;
70+ ctx.putString (" inputName" , processed_bin.string ());
71+ ctx.putString (" outputName" , out_knz.string ());
72+
73+ // force extreme compression (level 9 maps to EXE+RLT+TEXT+UTF+DNA&TPAQX)
74+ ctx.putInt (" level" , 9 );
75+ ctx.putInt (" autoBlock" , 1 );
76+ ctx.putInt (" jobs" , 1 );
77+ ctx.putInt (" overwrite" , 1 );
78+ ctx.putInt (" checksum" , 64 ); // enforce integrity
79+
80+ kanzi::BlockCompressor bc (ctx);
81+ uint64_t written = 0 ;
82+
83+ if (bc.compress (written) != 0 ) {
84+ Logger::log (LogLevel::Error, " Kanzi compression returned non-zero status" , get_name ());
85+ fs::remove (out_knz);
86+ throw std::runtime_error (" KanziProcessor: compression failed" );
87+ }
88+ } catch (const std::exception& e) {
89+ Logger::log (LogLevel::Error, std::string (" Kanzi exception: " ) + e.what (), get_name ());
90+ fs::remove (out_knz);
91+ throw ;
92+ }
93+
94+ cleanup_temp_dir (content.temp_dir , get_name ());
95+ return out_knz;
96+ }
97+
98+ std::string KanziProcessor::get_raw_checksum (const fs::path& /* file_path*/ ) const {
99+ return " " ;
100+ }
101+
102+ } // namespace chisel
0 commit comments