/* cmake_minimum_required(VERSION 3.27) project(cordiceps) set(CMAKE_CXX_STANDARD 23) include(FetchContent) FetchContent_Declare( raylib GIT_REPOSITORY https://github.com/raysan5/raylib.git GIT_TAG 5.0 ) FetchContent_MakeAvailable(raylib) add_executable(cordiceps cordiceps.cpp) target_link_libraries(cordiceps PUBLIC raylib_static) */ #include #include #include #include #include #include #include #include #include #include "raylib.h" std::string slurp(const char* filename) { std::ifstream file{filename, std::ios_base::binary}; std::stringstream data; file >> data.rdbuf(); return data.str(); } std::vector to_bits(std::string str) { std::vector d; for(char c : str) { d.push_back(c & 0b10000000); d.push_back(c & 0b1000000); d.push_back(c & 0b100000); d.push_back(c & 0b10000); d.push_back(c & 0b1000); d.push_back(c & 0b100); d.push_back(c & 0b10); d.push_back(c & 0b1); } return d; } std::string from_bits(std::vector data) { std::string d; std::bitset<32> size_bits; for(int idx = 0; idx < 32; ++idx) size_bits[idx] = data[idx]; uint32_t size = size_bits.to_ulong(); if(std::endian::native == std::endian::little) size = std::byteswap(size); std::cerr << "Decoded expected " << size << " bytes" << std::endl; for(auto composite : data | std::views::drop(32) | std::views::chunk(8)) { char c = 0; for(auto b : composite) { c *= 2; c += b; } d.push_back(c); size--; if(size == 0) return d; } return d; } void encode(const char* pic, const char* data_source) { auto image = LoadImage(pic); auto raw_data = slurp(data_source); uint32_t size = raw_data.size(); std::cerr << "Writing expected " << size << " bytes" << std::endl; if(std::endian::native == std::endian::little) size = std::byteswap(size); std::bitset<32> size_bits; size_bits = size; std::vector raw_bits = to_bits(raw_data); assert(raw_bits.size() % 8 == 0); assert(raw_bits.size() / 8 == raw_data.size()); std::vector data; for(int idx = 0; idx < 32; ++idx) data.push_back(size_bits[idx]); data.insert(data.end(), raw_bits.begin(), raw_bits.end()); size_t idx = 0; for(auto j : std::ranges::iota_view(0, image.height)) { for(auto i : std::ranges::iota_view(0, image.width)) { auto c = GetImageColor(image, i, j); c.r ^= c.r & 1; c.g ^= c.g & 1; c.b ^= c.b & 1; c.r += idx < data.size() ? data[idx] : 0; idx++; c.g += idx < data.size() ? data[idx] : 0; idx++; c.b += idx < data.size() ? data[idx] : 0; idx++; ImageDrawPixel(&image, i, j, c); } } ExportImage(image, pic); UnloadImage(image); if(raw_bits.size() > idx) { std::cout << "\x1b[1;31mWritten bits: " << data.size() << "/" << idx << std::endl; } else { std::cout << "\x1b[1;32mWritten bits: " << data.size() << "/" << idx << std::endl; } } void decode(const char* pic) { auto image = LoadImage(pic); std::vector raw_bits; size_t idx = 0; for(auto j : std::ranges::iota_view(0, image.height)) { for(auto i : std::ranges::iota_view(0, image.width)) { auto c = GetImageColor(image, i, j); raw_bits.push_back(c.r & 1); raw_bits.push_back(c.g & 1); raw_bits.push_back(c.b & 1); } } UnloadImage(image); auto data = from_bits(raw_bits); std::cerr << "Printing expected " << data.size() << " bytes" << std::endl; std::ofstream{"cordiceps.out", std::ios_base::binary} << data; } void CustomLog(int msgType, const char *text, va_list args) { fprintf(stderr, text, args); } int main(int argc, char** argv) { SetTraceLogLevel(LOG_ERROR); SetTraceLogCallback(CustomLog); if(argc <= 1) { perror("arguments missing, expected: picture then datafile for coding, picture for decoding"); } if(argc == 3) { encode(argv[1], argv[2]); } else if(argc == 2) { decode(argv[1]); } return 0; }