#include "generator.hpp" #include "../include/hash.hpp" #include #include #include #include #include #include "xxhash.hpp" namespace fs = std::filesystem; static std::string sanitize(const std::string& name) { std::string out = name; for (char& c : out) if (!isalnum(c)) c = '_'; return out; } void generate_file_code(const std::string& source, const std::string& destfolder, bool silent) { fs::path src(source); fs::path dest(destfolder); std::string ns = "recreate_" + sanitize(src.stem().string()); std::string cppname = "_" + src.stem().string() + ".cpp"; std::string hppname = "_" + src.stem().string() + ".hpp"; fs::create_directories(dest); std::ifstream in(source, std::ios::binary); std::ostringstream oss; oss << in.rdbuf(); std::string filedata = oss.str(); std::string hash = hash_data(filedata); // Write HPP std::ofstream hpp(dest / hppname); hpp << "#pragma once\n#include \nnamespace " << ns << " {\nbool recreate_file(std::string destination_folder);\n}\n"; // Write CPP std::ofstream cpp(dest / cppname); cpp << R"cpp(#include #include #include #include #include // Tiny dependency-free FNV-1a 64-bit hash static uint64_t fnv1a_64(const void* data, size_t len) { const uint8_t* p = static_cast(data); uint64_t h = 0xcbf29ce484222325ULL; for (size_t i = 0; i < len; ++i) h = (h ^ p[i]) * 0x100000001b3ULL; return h; } )cpp"; cpp << "#include \"" << hppname << "\"\n"; cpp << "namespace " << ns << " {\n"; // Embed file data cpp << "static const unsigned char filedata[] = {"; for (size_t i = 0; i < filedata.size(); ++i) { if (i % 16 == 0) cpp << "\n "; cpp << "0x" << std::hex << std::setw(2) << std::setfill('0') << (int)(unsigned char)filedata[i]; if (i + 1 != filedata.size()) cpp << ", "; } cpp << "\n};\n"; cpp << "static const size_t filedata_len = " << filedata.size() << ";\n"; cpp << "static const char* file_hash = \"" << hash << "\";\n"; cpp << R"cpp( bool recreate_file(std::string destination_folder) { namespace fs = std::filesystem; fs::path outpath = fs::path(destination_folder) / ")cpp" << src.filename().string() << R"cpp("; std::string existing_hash; if (fs::exists(outpath)) { std::ifstream in(outpath, std::ios::binary); std::ostringstream oss; oss << in.rdbuf(); std::string data = oss.str(); uint64_t h = fnv1a_64(data.data(), data.size()); std::ostringstream hex; hex << std::hex << std::setw(16) << std::setfill('0') << h; existing_hash = hex.str(); } bool needs_write = !fs::exists(outpath) || existing_hash != file_hash; if (needs_write) { std::ofstream out(outpath, std::ios::binary); out.write(reinterpret_cast(filedata), filedata_len); } std::cout << "[dehydrate] " << outpath << ": "; if (!fs::exists(outpath)) { std::cout << "created\n"; } else if (needs_write) { std::cout << "updated (hash changed)\n"; } else { std::cout << "unchanged (hash match)\n"; } return needs_write; } )cpp"; cpp << "}\n"; if (!silent) { std::cout << "[dehydrate] Generated: " << (dest / cppname) << ", " << (dest / hppname) << std::endl; } } // Helper to recursively collect all files in a directory template void walk_dir(const fs::path& dir, F&& f) { for (auto& p : fs::recursive_directory_iterator(dir)) { if (fs::is_regular_file(p)) f(p.path()); } } void generate_folder_code(const std::string& source, const std::string& destfolder, bool silent) { fs::path src(source); fs::path dest(destfolder); std::string ns = "recreate_" + sanitize(src.stem().string()); std::string cppname = "_" + src.stem().string() + ".cpp"; std::string hppname = "_" + src.stem().string() + ".hpp"; fs::create_directories(dest); // Collect all files std::vector files; walk_dir(src, [&](const fs::path& p) { files.push_back(p); }); // Write HPP std::ofstream hpp(dest / hppname); hpp << "#pragma once\n#include \nnamespace " << ns << " {\nbool recreate_tree(std::string destination_folder);\n}\n"; // Write CPP std::ofstream cpp(dest / cppname); cpp << R"cpp(#include #include #include #include #include #include "xxhash.hpp" )cpp"; cpp << "#include \"" << hppname << "\"\n"; cpp << "namespace " << ns << " {\n"; // Embed all files for (const auto& file : files) { std::ifstream in(file, std::ios::binary); std::ostringstream oss; oss << in.rdbuf(); std::string filedata = oss.str(); std::string hash = hash_data(filedata); std::string rel = fs::relative(file, src).string(); std::string var = sanitize(rel); cpp << "static const unsigned char data_" << var << "[] = {"; for (size_t i = 0; i < filedata.size(); ++i) { if (i % 16 == 0) cpp << "\n "; cpp << "0x" << std::hex << std::setw(2) << std::setfill('0') << (int)(unsigned char)filedata[i]; if (i + 1 != filedata.size()) cpp << ", "; } cpp << "\n};\n"; cpp << "static const size_t len_" << var << " = " << filedata.size() << ";\n"; cpp << "static const char* hash_" << var << " = \"" << hash << "\";\n"; cpp << "static const char* rel_" << var << " = \"" << rel << "\";\n"; } // Write recreate_tree using heredoc style cpp << R"cpp( bool recreate_tree(std::string destination_folder) { namespace fs = std::filesystem; bool any_written = false; )cpp"; for (const auto& file : files) { std::string rel = fs::relative(file, src).string(); std::string var = sanitize(rel); cpp << R"cpp( { fs::path outpath = fs::path(destination_folder) / )cpp" << "rel_" << var << R"cpp(; fs::create_directories(outpath.parent_path()); std::string existing_hash; if (fs::exists(outpath)) { std::ifstream in(outpath, std::ios::binary); std::ostringstream oss; oss << in.rdbuf(); std::string data = oss.str(); uint64_t h = XXH3_64bits(data.data(), data.size()); std::ostringstream hex; hex << std::hex << std::setw(16) << std::setfill('0') << h; existing_hash = hex.str(); } bool needs_write = !fs::exists(outpath) || existing_hash != )cpp" << "hash_" << var << R"cpp(; if (needs_write) { std::ofstream out(outpath, std::ios::binary); out.write(reinterpret_cast()cpp" << "data_" << var << R"cpp(), )cpp" << "len_" << var << R"cpp(); } std::cout << "[dehydrate] " << outpath << ": "; if (!fs::exists(outpath)) { std::cout << "created\n"; } else if (needs_write) { std::cout << "updated (hash changed)\n"; } else { std::cout << "unchanged (hash match)\n"; } any_written = any_written || needs_write; } )cpp"; } cpp << R"cpp( return any_written; } )cpp"; cpp << "}\n"; if (!silent) { std::cout << "[dehydrate] Generated: " << (dest / cppname) << ", " << (dest / hppname) << std::endl; } }