197 lines
7.3 KiB
C++
197 lines
7.3 KiB
C++
#include "generator.hpp"
|
|
#include "../include/hash.hpp"
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <filesystem>
|
|
#include <sstream>
|
|
#include <vector>
|
|
#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 <string>\nnamespace " << ns << " {\nbool recreate_file(std::string destination_folder);\n}\n";
|
|
// Write CPP
|
|
std::ofstream cpp(dest / cppname);
|
|
cpp << R"cpp(#include <fstream>
|
|
#include <filesystem>
|
|
#include <string>
|
|
#include <iostream>
|
|
#include <iomanip>
|
|
|
|
// 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<const uint8_t*>(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<const char*>(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<typename F>
|
|
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<fs::path> files;
|
|
walk_dir(src, [&](const fs::path& p) { files.push_back(p); });
|
|
// Write HPP
|
|
std::ofstream hpp(dest / hppname);
|
|
hpp << "#pragma once\n#include <string>\nnamespace " << ns << " {\nbool recreate_tree(std::string destination_folder);\n}\n";
|
|
// Write CPP
|
|
std::ofstream cpp(dest / cppname);
|
|
cpp << R"cpp(#include <fstream>
|
|
#include <filesystem>
|
|
#include <string>
|
|
#include <iostream>
|
|
#include <iomanip>
|
|
#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<const char*>()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;
|
|
}
|
|
}
|