diff --git a/CMakeLists.txt b/CMakeLists.txt index d2c3f4f..0bed220 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,8 @@ cmake_minimum_required(VERSION 3.18) project(blocky C CXX) +option(BLOCKY_STATIC OFF) + set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) diff --git a/blocky/CMakeLists.txt b/blocky/CMakeLists.txt index 6bf5cfc..fc24274 100644 --- a/blocky/CMakeLists.txt +++ b/blocky/CMakeLists.txt @@ -4,8 +4,12 @@ target_compile_options(blocky PRIVATE ${BLOCKY_C_FLAGS}) target_sources(blocky PUBLIC main.cc + + bytes.hh + common.hh ) target_link_libraries(blocky PUBLIC + args liblocky ) diff --git a/blocky/bytes.hh b/blocky/bytes.hh new file mode 100644 index 0000000..d589f96 --- /dev/null +++ b/blocky/bytes.hh @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include + +#include "common.hh" + + +namespace blky { + +std::vector BytesEncoder( + const std::vector& bytes, + std::tuple& feats_num, + uint8_t feat_bits, + uint32_t first, + uint64_t seed) { + (void) bytes; + (void) feats_num; + (void) feat_bits; + (void) first; + (void) seed; + return {}; +} + +} // namespace blky diff --git a/blocky/common.hh b/blocky/common.hh new file mode 100644 index 0000000..fa17d6b --- /dev/null +++ b/blocky/common.hh @@ -0,0 +1,56 @@ +#pragma once + +extern "C" { +# include +} + +#include +#include +#include +#include +#include + + +namespace blky { + +enum DataFlow { + kBytes, + kFeatures, + kVideo, +}; + +static inline const std::string kDataFlowList = "bytes/features/video"; +static inline const std::unordered_map kDataFlowMap = { + {"bytes", kBytes}, + {"features", kFeatures}, + {"video", kVideo}, +}; + + +struct Feature final { + uint64_t index; + uint64_t begin; + uint64_t end; +}; + + +auto& operator<<(auto& ost, const Feature& feat) { + ost << feat.index << " " << feat.begin << " " << feat.end; + return ost; +} +auto& operator>>(auto& ist, Feature& feat) { + ist >> feat.index >> feat.begin >> feat.end; + return ist; +} + +} // namespace blky + + +namespace args { + +auto& operator>>(auto& ist, std::tuple& v) { + ist >> std::get<0>(v) >> std::get<1>(v); + return ist; +} + +} // namespace args diff --git a/blocky/main.cc b/blocky/main.cc index 452782a..ef9f31b 100644 --- a/blocky/main.cc +++ b/blocky/main.cc @@ -1,8 +1,180 @@ +#include +#include #include +#include +#include "common.hh" +#include "bytes.hh" + +#include + + +using namespace blky; int main(int argc, char** argv) { - (void) argc; - (void) argv; - std::cout << "helloworld" << std::endl; + args::ArgumentParser parser( + "liblocky command line tool", + "liblocky allow you to embed bits into video data secretly"); + + args::MapFlag from { + parser, kDataFlowList, "input layer specifier", {"from"}, kDataFlowMap, args::Options::Required}; + + args::MapFlag to { + parser, kDataFlowList, "output layer specifier", {"to"}, kDataFlowMap, args::Options::Required}; + + args::Group src_group { + parser, "source specifier", args::Group::Validators::Xor, args::Options::Required}; + args::Flag src_stdin {src_group, "src-stdin", "read from stdin", {"src-stdin", "stdin"}}; + + args::Group dst_group { + parser, "destination specifier", args::Group::Validators::Xor, args::Options::Required}; + args::Flag dst_stdout {dst_group, "dst-stdout", "write to stdout", {"dst-stdout", "stdout"}}; + + args::Group param_group { + parser, "processing parameters", args::Group::Validators::DontCare + }; + args::ValueFlag> param_feat_num { + param_group, + "int>0,int>0", + "number of features", + {"feature-num"}, + {16, 16} + }; + args::ValueFlag param_feat_bits { + param_group, + "int>0", + "number of bits that can be represented by a single feature", + {"feature-bits"}, + 1 + }; + args::ValueFlag param_feat_first { + param_group, + "int>=0", + "an index of first feature. used when encoding", + {"feature-first-index"}, + 0 + }; + args::ValueFlag param_feat_seed { + param_group, + "int>0", + "seed value for feature hopping", + {"feature-hopping-seed"}, + 1 + }; + + try { + parser.ParseCLI(argc, argv); + + std::vector bytes; + std::vector features; + + // read input + switch (args::get(from)) { + case kBytes: + if (src_stdin) { + std::string temp; + std::cin >> temp; + bytes = {temp.begin(), temp.end()}; + } else { + throw std::runtime_error {"invalid source format for bytes"}; + } + break; + + case kFeatures: + if (src_stdin) { + while (!std::cin.eof()) { + features.push_back({}); + std::cin >> features.back(); + } + } else { + throw std::runtime_error {"invalid source format for bytes"}; + } + break; + + case kVideo: + assert(false); + } + + if (args::get(from) < args::get(to)) { + // execute encoding + switch (args::get(from)) { + case kBytes: + if (args::get(to) == kBytes) break; + features = BytesEncoder( + bytes, + args::get(param_feat_num), + args::get(param_feat_bits), + args::get(param_feat_first), + args::get(param_feat_seed)); + /* fallthrough */ + + case kFeatures: + if (args::get(to) == kFeatures) break; + // TODO embed into video + assert(false); + + case kVideo: + if (args::get(to) == kVideo) break; + assert(false); + } + + } else if (args::get(from) > args::get(to)) { + // execute decoding + switch (args::get(from)) { + case kVideo: + if (args::get(to) == kVideo) break; + // TODO extract features // features = XX + assert(false); + + case kFeatures: + if (args::get(to) == kFeatures) break; + // TODO bytes = DataDecoder(features); + assert(false); + /* fallthrough */ + + case kBytes: + if (args::get(to) == kBytes) break; + assert(false); + } + } + + // output + switch (args::get(to)) { + case kBytes: + if (dst_stdout) { + std::cout << std::string {bytes.begin(), bytes.end()} << std::endl; + } else { + throw std::runtime_error {"invalid destination format for bytes"}; + } + break; + case kFeatures: + if (dst_stdout) { + for (auto& f : features) std::cout << f << "\n"; + } else { + throw std::runtime_error {"invalid destination format for features"}; + } + break; + case kVideo: + break; + } + + } catch (const args::Help&) { + std::cout << parser << std::endl; + return 0; + + } catch (const args::ParseError& e) { + std::cerr << e.what() << std::endl; + std::cerr << parser << std::endl; + return 1; + + } catch (const args::ValidationError& e) { + std::cerr << e.what() << std::endl; + std::cerr << parser << std::endl; + return 1; + + } catch (const std::runtime_error& e) { + std::cerr << "runtime error: " << e.what() << std::endl; + return 1; + } + return 0; } diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt index 3a40b56..2dd1a71 100644 --- a/thirdparty/CMakeLists.txt +++ b/thirdparty/CMakeLists.txt @@ -1,6 +1,20 @@ include(FetchContent) +# ---- args ---- +# repository: https://github.com/Taywee/args +# license : MIT + +FetchContent_Declare( + args + URL "https://github.com/Taywee/args/archive/refs/tags/6.3.0.zip" +) + +set(ARGS_BUILD_EXAMPLE OFF) +set(ARGS_BUILD_UNITTESTS OFF) +FetchContent_MakeAvailable(args) + + # ---- GLEW ---- # repository: https://github.com/Perlmint/glew-cmake # license : Modified BSD License, the Mesa 3-D License (MIT) and the Khronos License (MIT). @@ -11,7 +25,7 @@ FetchContent_Declare( ) FetchContent_MakeAvailable(glew) -if (KINGTAKER_STATIC) +if (BLOCKY_STATIC) add_library(glew ALIAS libglew_static) else() add_library(glew ALIAS libglew_shared)