diff --git a/cordiceps.cpp b/cordiceps.cpp new file mode 100644 index 0000000..ce5a3e9 --- /dev/null +++ b/cordiceps.cpp @@ -0,0 +1,118 @@ +/* +cmake_minimum_required(VERSION 3.28) +project(codiceps) + +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(codiceps codiceps.cpp) +target_link_libraries(codiceps PUBLIC raylib_static) +*/ + +#include +#include +#include +#include +#include +#include +#include "raylib.h" + +std::string slurp(const char* filename) { + std::ifstream file{filename}; + 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; + for(auto composite : data | std::views::chunk(8)) { + char c = 0; + for(auto b : composite) { + c *= 2; + c += b; + } + d.push_back(c); + } + return d; +} + +void encode(const char* pic, const char* data_source) { + auto image = LoadImage(pic); + auto raw_data = slurp(data_source); + std::vector raw_bits = to_bits(raw_data); + assert(raw_bits.size() % 8 == 0); + assert(raw_bits.size() / 8 == raw_data.size()); + 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 < raw_bits.size() ? raw_bits[idx] : 0; idx++; + c.g += idx < raw_bits.size() ? raw_bits[idx] : 0; idx++; + c.b += idx < raw_bits.size() ? raw_bits[idx] : 0; idx++; + ImageDrawPixel(&image, i, j, c); + } + } + ExportImage(image, pic); + UnloadImage(image); + if(raw_bits.size() > idx) { + std::cout << "\x1b[1;31mWritten bits: " << raw_bits.size() << "/" << idx << std::endl; + } else { + std::cout << "\x1b[1;32mWritten bits: " << raw_bits.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); + std::cout << from_bits(raw_bits).c_str() << std::endl; +} + +int main(int argc, char** argv) { + 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; +}