[
  {
    "path": ".gitignore",
    "content": "# editor\n.vscode\n\n# build\nbuild\n\n# cl with debug option\n*.ilk\n*.obj\n*.pdb\n\n# sample program\n*.exe\n\n# python cache\n__pycache__"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"external/Catch2\"]\n\tpath = external/Catch2\n\turl = https://github.com/catchorg/Catch2\n"
  },
  {
    "path": "3RD-PARTY.md",
    "content": "cpp-obfuscator uses Catch2 for testing obfuscator modules\n\nBoost Software License - Version 1.0 - August 17th, 2003\n\nPermission is hereby granted, free of charge, to any person or organization\nobtaining a copy of the software and accompanying documentation covered by\nthis license (the \"Software\") to use, reproduce, display, distribute,\nexecute, and transmit the Software, and to prepare derivative works of the\nSoftware, and to permit third-parties to whom the Software is furnished to\ndo so, all subject to the following:\n\nThe copyright notices in the Software and this entire statement, including\nthe above license grant, this restriction and the following disclaimer,\nmust be included in all copies of the Software, in whole or in part, and\nall derivative works of the Software, unless such copies or derivative\nworks are solely in the form of machine-executable object code generated by\na source language processor.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT\nSHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE\nFOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,\nARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n\n---"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019 YoungJoong Kim\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# cpp-obfuscator\n[![License](https://img.shields.io/badge/Licence-MIT-blue.svg)](https://github.com/revsic/cpp-obfuscator/blob/master/LICENSE)\n[![Build Status](https://dev.azure.com/revsic99/cpp-obfuscator/_apis/build/status/revsic.cpp-obfuscator?branchName=master)](https://dev.azure.com/revsic99/cpp-obfuscator/_build/latest?definitionId=3&branchName=master)\n\nC++ implementation of compile time obfuscator\n\n## Compiler Support\n| Compiler | Version |\n| -- | -- |\n| Visual Studio | 2019 |\n| Clang++ | 8.0 |\n| g++ | 7.3 |\n| Apple Clang | Mojave |\n\n## Usage\n\nUse existing single header [obfuscator.hpp](./obfuscator.hpp) or run [script](./script/merge.py) to merge multiple headers in directory [obfuscator](./obfuscator).\n\n```bash\npython -m script.merge\n```\n\nInclude [obfuscator.hpp](./obfuscator.hpp) to use it.\n```C++\n#include \"../obfuscator.hpp\"\n\ntemplate <char key>\nconstexpr char xor_(char c) {\n    return c ^ key;\n}\n\nstd::cout << obfs::make_string<xor_<0x50>, xor_<0x50>>(\"Hello World !\\n\").decode();\n```\n\n## String\n\nCompile time string obfuscator. No more raw string in binary, sample [string_obfuscator](./sample/string_obfs.cpp).\n\n0. Sample encoder, decoder\n```C++\ntemplate <char key>\nconstexpr char xor_(char c) {\n    return c ^ key;\n}\n\ntemplate <char Key>\nconstexpr char add(char c) {\n    return c + Key;\n}\n\ntemplate <char(*f)(char), char(*g)(char)>\nconstexpr char comp(char c) {\n    return f(g(c));\n}\n```\n\n1. Single encoder, decoder.\n```C++\nstd::cout << obfs::make_string<xor_<0x50>, xor_<0x50>>(\"Hello World !\\n\").decode();\n```\n\n2. Multiple encoder, decoder and random selection.\n```C++\nusing table = obfs::make_table<\n    obfs::encoder_seq<xor_<0x50>, add<10>, comp<xor_<0x50>, add<10>>>,\n    obfs::decoder_seq<xor_<0x50>, add<-10>, comp<add<-10>, xor_<0x50>>>>;\n\nstd::cout << obfs::make_string<table>(\"Hello World !\\n\").decode();\n```\n\n3. Multiple encoder, decoder as pair.\n```C++\nusing table = obfs::make_pair_table<\n    obfs::encoder_pair<xor_<0x50>, xor_<0x50>>,\n    obfs::encoder_pair<add<10>, add<-10>>,\n    obfs::encoder_pair<comp<xor_<0x50>, add<10>>, comp<add<-10>, xor_<0x50>>>\n>;\n\nstd::cout << obfs::make_string<table>(\"Hello World !\\n\").decode();\n```\n\n4. String obfuscator macro.\n```C++\nMAKE_STRING(str, \"Hello World !\\n\", table);\nstd::cout << str.decode();\n```\n\n## Finite state machine\n\nCompile time finite state machine based routine obfuscator, sample [state_machine](./sample/state_machine.cpp).\n\n0. Make state table.\n```C++\nusing namespace obfs;\nusing machine = StateMachine<\n    Stage<state1, Next<event5 , state2, Dummy::dummy1>,\n                  Next<event1 , state3, Dummy::dummy3>>,\n    Stage<state2, Next<event2 , state4>>,\n    Stage<state3, Next<None   , state3>>,\n    Stage<state4, Next<event4 , state1>,\n                  Next<event3 , state5, Dummy::dummy2>>,\n    Stage<state5, Next<Trigger, Final, Action::action>>>;\n```\n\n1. Run state machine, each execution returns next state.\n```C++\nauto next1 = machine::run(state1{}, event5{}); // dummy1 executed\nauto next2 = machine::run(next1, event2{});\nauto next3 = machine::run(next2, event3{});    // dummy2 executed\nauto next4 = machine::run(next3, Trigger{});   // action executed\n```"
  },
  {
    "path": "azure-pipelines.yml",
    "content": "jobs:\n- template: script/azure-pipelines-template-mac.yml\n  parameters:\n    name: macOS\n    vmImage: macOS-10.14\n\n- template: script/azure-pipelines-template-unix.yml\n  parameters:\n    name: Linux\n    vmImage: ubuntu-16.04\n\n- template: script/azure-pipelines-template-win.yml\n  parameters:\n    name: Windows\n    vmImage: windows-2019\n"
  },
  {
    "path": "obfuscator/obfs/fsm.hpp",
    "content": "#ifndef OBFS_FINITE_STATE_MACHINE\n#define OBFS_FINITE_STATE_MACHINE\n\n#include \"sequence.hpp\"\n\n#include <type_traits>\n\nnamespace obfs {\n    void FreeAction() {}\n\n    template <typename Event, typename State, void(*Action)() = FreeAction>\n    struct Next {\n        using event = Event;\n        using state = State;\n        constexpr static void(*action)() = Action;\n    };\n\n    struct None {};\n\n    template <typename State, typename... Nexts>\n    struct Stage {\n        using state = State;\n\n        template <typename NextInfo, typename Event>\n        using act = std::conditional_t<\n            std::is_same_v<typename NextInfo::event, Event>, NextInfo, Pass>;\n\n        template <typename Event>\n        using next = typename First<None, act<Nexts, Event>...>::type;\n    };\n\n    template <typename Stage_, typename Event>\n    struct next_stage {\n        using type = typename Stage_::template next<Event>;\n    };\n\n    template <typename Event>\n    struct next_stage<None, Event> {\n        using type = None;\n    };\n\n    template <typename State>\n    struct action_invoker {\n        static auto action() {\n            State::action();\n            return typename State::state{};\n        }\n    };\n\n    template <>\n    struct action_invoker<None> {\n        static auto action() {\n            return None{};\n        }\n    };\n\n    template <typename... Specs>\n    struct StateMachine {\n        template <typename State, typename StageT>\n        using filter = std::conditional_t<\n            std::is_same_v<typename StageT::state, State>, StageT, Pass>;\n\n        template <typename State>\n        using find = typename First<None, filter<State, Specs>...>::type;\n\n        template <typename State, typename Event>\n        using next_t = typename next_stage<find<State>, Event>::type;\n\n        template <typename State, typename Event>\n        static auto run(State state, Event event) {\n            using next_state = next_t<std::decay_t<State>, std::decay_t<Event>>;\n            return action_invoker<next_state>::action();\n        }\n    };\n}\n\n#endif"
  },
  {
    "path": "obfuscator/obfs/random.hpp",
    "content": "#ifndef COMPILE_TIME_RANDOM\n#define COMPILE_TIME_RANDOM\n\nnamespace obfs {\n    using size_t = decltype(sizeof(void*));\n\n    constexpr char TIME[] = __TIME__;\n    constexpr int digit(char c) {\n        return c - '0';\n    }\n    constexpr size_t SEED = digit(TIME[7]) +\n                         digit(TIME[6]) * 10 +\n                         digit(TIME[4]) * 60 +\n                         digit(TIME[3]) * 600 +\n                         digit(TIME[1]) * 3600 +\n                         digit(TIME[0]) * 36000;\n\n    template <size_t Seed, size_t Idx>\n    struct Xorshiftplus {\n        using prev = Xorshiftplus<Seed, Idx - 1>;\n\n        constexpr static size_t update() {\n            constexpr size_t x = prev::state0 ^ (prev::state0 << 23);\n            constexpr size_t y = prev::state1;\n            return x ^ y ^ (x >> 17) ^ (y >> 26);\n        }\n\n        constexpr static size_t state0 = prev::state1;\n        constexpr static size_t state1 = update();\n\n        constexpr static size_t value = state0 + state1;\n    };\n\n    template <size_t Seed>\n    struct Xorshiftplus<Seed, 0> {\n        constexpr static size_t state0 = Seed;\n        constexpr static size_t state1 = Seed << 1;\n        constexpr static size_t value = state0 + state1;\n    };\n\n    template <size_t Idx, size_t Mod>\n    constexpr size_t RAND_VAL = Xorshiftplus<SEED, Idx>::value % Mod;\n}\n\n#define MAKE_RAND_VAL(MOD) obfs::RAND_VAL<__LINE__, MOD>\n\n#endif"
  },
  {
    "path": "obfuscator/obfs/sequence.hpp",
    "content": "#ifndef COMPILE_TIME_SEQUENCE\n#define COMPILE_TIME_SEQUENCE\n\n#include <type_traits>\n\nnamespace obfs {\n    template <typename T, T Val>\n    struct TypeVal {\n        using value_type = T;\n        constexpr static T value = Val;\n    };\n\n    struct Nothing {};\n\n    template <typename T, T Val, T... Others>\n    struct Sequence {\n        using value = TypeVal<T, Val>;\n        using next = Sequence<T, Others...>;\n\n        constexpr static std::size_t size = 1 + sizeof...(Others);\n\n        template <std::size_t Idx>\n        using index = std::conditional_t<Idx == 0, value, typename next::template index<Idx - 1>>;\n    };\n\n    template <typename T, T Val>\n    struct Sequence<T, Val> {\n        using value = TypeVal<T, Val>;\n\n        constexpr static std::size_t size = 1;\n\n        template <std::size_t Idx>\n        using index = std::conditional_t<Idx == 0, value, Nothing>;\n    };\n\n    template <typename T, typename... Ts>\n    struct TypeSeq {\n        using type = T;\n        using next = TypeSeq<Ts...>;\n\n        constexpr static std::size_t size = 1 + sizeof...(Ts);\n\n        template <std::size_t Idx>\n        using index = std::conditional_t<Idx == 0, type, typename next::template index<Idx - 1>>;\n    };\n\n    template <typename T>\n    struct TypeSeq<T> {\n        using type = T;\n\n        constexpr static std::size_t size = 1;\n\n        template <std::size_t Idx>\n        using index = std::conditional_t<Idx == 0, type, Nothing>;\n    };\n\n    template <std::size_t Val, std::size_t... Others>\n    struct MinVal {\n        constexpr static std::size_t value = \n            Val < MinVal<Others...>::value ? Val : MinVal<Others...>::value;\n    };\n\n    template <std::size_t Val>\n    struct MinVal<Val> {\n        constexpr static std::size_t value = Val;\n    };\n\n    template <typename... T>\n    struct SeqPack {\n        constexpr static std::size_t size = MinVal<T::size...>::value;\n\n        template <std::size_t Idx, typename U>\n        using getter = typename U::template index<Idx>;\n\n        template <std::size_t Idx>\n        using index = TypeSeq<getter<Idx, T>...>;\n    };\n\n    struct Pass {};\n\n    template <typename IfAllPass, typename T, typename... Ts>\n    struct First {\n        using type = std::conditional_t<\n            std::is_same_v<T, Pass>,\n            typename First<IfAllPass, Ts...>::type,\n            T>;\n    };\n\n    template <typename IfAllPass, typename T>\n    struct First<IfAllPass, T> {\n        using type = std::conditional_t<\n            std::is_same_v<T, Pass>,\n            IfAllPass,\n            T>;\n    };\n}\n\n#endif"
  },
  {
    "path": "obfuscator/obfs/string.hpp",
    "content": "#ifndef OBFS_STRING\n#define OBFS_STRING\n\n#include \"random.hpp\"\n#include \"sequence.hpp\"\n\n#include <utility>\n\nnamespace obfs {\n    using Encoder = char(*)(char);\n    using Decoder = char(*)(char);\n\n    template <std::size_t size, Encoder encoder, Decoder decoder>\n    class String {\n    public:\n        template <std::size_t... Idx>\n        constexpr String(char const* str,\n                         std::index_sequence<Idx...>):\n            str{ encoder(str[Idx])... } {\n            // Do Nothing\n        }\n\n        inline char const* decode() const {\n            for (char& chr : str) {\n                chr = decoder(chr);\n            }\n            return str;\n        }\n\n    private:\n        mutable char str[size];\n    };\n\n    template <Encoder encoder, Decoder decoder, std::size_t size>\n    constexpr auto make_string(char const (&str)[size]) {\n        return String<size, encoder, decoder>(str, std::make_index_sequence<size>());\n    }\n\n    template <Encoder... encoders>\n    using encoder_seq = Sequence<Encoder, encoders...>;\n\n    template <Decoder... decoders>\n    using decoder_seq = Sequence<Decoder, decoders...>;\n\n    template <typename EncoderSeq, typename DecoderSeq>\n    using make_table = SeqPack<EncoderSeq, DecoderSeq>;\n\n    template <Encoder encoder, Decoder decoder>\n    using encoder_pair = Sequence<Encoder, encoder, decoder>;\n\n    template <typename... Seq>\n    using make_pair_table = TypeSeq<Seq...>;\n\n    template <typename Table, std::size_t size>\n    constexpr auto make_string(char const(&str)[size]) {\n        using pair = typename Table::template index<MAKE_RAND_VAL(Table::size)>;\n        constexpr Encoder encoder = pair::template index<0>::value;\n        constexpr Decoder decoder = pair::template index<1>::value;\n\n        return make_string<encoder, decoder>(str);\n    }\n}\n\n#define MAKE_STRING(Var, String, ...) constexpr auto Var = obfs::make_string<__VA_ARGS__>(String);\n\n#endif"
  },
  {
    "path": "obfuscator/obfuscator.hpp",
    "content": "#ifndef OBFUSCATOR\n#define OBFUSCATOR\n\n#include \"obfs/fsm.hpp\"\n#include \"obfs/random.hpp\"\n#include \"obfs/sequence.hpp\"\n#include \"obfs/string.hpp\"\n\n#endif"
  },
  {
    "path": "obfuscator.hpp",
    "content": "#ifndef OBFUSCATOR_HPP\n#define OBFUSCATOR_HPP\n\n#include <type_traits>\n#include <utility>\n\n#define COMPILE_TIME_SEQUENCE\n#define OBFS_FINITE_STATE_MACHINE\n#define COMPILE_TIME_RANDOM\n#define MAKE_RAND_VAL(MOD) obfs::RAND_VAL<__LINE__, MOD>\n#define OBFS_STRING\n#define MAKE_STRING(Var, String, ...) constexpr auto Var = obfs::make_string<__VA_ARGS__>(String);\n\n\nnamespace obfs {\n    template <typename T, T Val>\n    struct TypeVal {\n        using value_type = T;\n        constexpr static T value = Val;\n    };\n\n    struct Nothing {};\n\n    template <typename T, T Val, T... Others>\n    struct Sequence {\n        using value = TypeVal<T, Val>;\n        using next = Sequence<T, Others...>;\n\n        constexpr static std::size_t size = 1 + sizeof...(Others);\n\n        template <std::size_t Idx>\n        using index = std::conditional_t<Idx == 0, value, typename next::template index<Idx - 1>>;\n    };\n\n    template <typename T, T Val>\n    struct Sequence<T, Val> {\n        using value = TypeVal<T, Val>;\n\n        constexpr static std::size_t size = 1;\n\n        template <std::size_t Idx>\n        using index = std::conditional_t<Idx == 0, value, Nothing>;\n    };\n\n    template <typename T, typename... Ts>\n    struct TypeSeq {\n        using type = T;\n        using next = TypeSeq<Ts...>;\n\n        constexpr static std::size_t size = 1 + sizeof...(Ts);\n\n        template <std::size_t Idx>\n        using index = std::conditional_t<Idx == 0, type, typename next::template index<Idx - 1>>;\n    };\n\n    template <typename T>\n    struct TypeSeq<T> {\n        using type = T;\n\n        constexpr static std::size_t size = 1;\n\n        template <std::size_t Idx>\n        using index = std::conditional_t<Idx == 0, type, Nothing>;\n    };\n\n    template <std::size_t Val, std::size_t... Others>\n    struct MinVal {\n        constexpr static std::size_t value = \n            Val < MinVal<Others...>::value ? Val : MinVal<Others...>::value;\n    };\n\n    template <std::size_t Val>\n    struct MinVal<Val> {\n        constexpr static std::size_t value = Val;\n    };\n\n    template <typename... T>\n    struct SeqPack {\n        constexpr static std::size_t size = MinVal<T::size...>::value;\n\n        template <std::size_t Idx, typename U>\n        using getter = typename U::template index<Idx>;\n\n        template <std::size_t Idx>\n        using index = TypeSeq<getter<Idx, T>...>;\n    };\n\n    struct Pass {};\n\n    template <typename IfAllPass, typename T, typename... Ts>\n    struct First {\n        using type = std::conditional_t<\n            std::is_same_v<T, Pass>,\n            typename First<IfAllPass, Ts...>::type,\n            T>;\n    };\n\n    template <typename IfAllPass, typename T>\n    struct First<IfAllPass, T> {\n        using type = std::conditional_t<\n            std::is_same_v<T, Pass>,\n            IfAllPass,\n            T>;\n    };\n}\n\n\nnamespace obfs {\n    void FreeAction() {}\n\n    template <typename Event, typename State, void(*Action)() = FreeAction>\n    struct Next {\n        using event = Event;\n        using state = State;\n        constexpr static void(*action)() = Action;\n    };\n\n    struct None {};\n\n    template <typename State, typename... Nexts>\n    struct Stage {\n        using state = State;\n\n        template <typename NextInfo, typename Event>\n        using act = std::conditional_t<\n            std::is_same_v<typename NextInfo::event, Event>, NextInfo, Pass>;\n\n        template <typename Event>\n        using next = typename First<None, act<Nexts, Event>...>::type;\n    };\n\n    template <typename Stage_, typename Event>\n    struct next_stage {\n        using type = typename Stage_::template next<Event>;\n    };\n\n    template <typename Event>\n    struct next_stage<None, Event> {\n        using type = None;\n    };\n\n    template <typename State>\n    struct action_invoker {\n        static auto action() {\n            State::action();\n            return typename State::state{};\n        }\n    };\n\n    template <>\n    struct action_invoker<None> {\n        static auto action() {\n            return None{};\n        }\n    };\n\n    template <typename... Specs>\n    struct StateMachine {\n        template <typename State, typename StageT>\n        using filter = std::conditional_t<\n            std::is_same_v<typename StageT::state, State>, StageT, Pass>;\n\n        template <typename State>\n        using find = typename First<None, filter<State, Specs>...>::type;\n\n        template <typename State, typename Event>\n        using next_t = typename next_stage<find<State>, Event>::type;\n\n        template <typename State, typename Event>\n        static auto run(State state, Event event) {\n            using next_state = next_t<std::decay_t<State>, std::decay_t<Event>>;\n            return action_invoker<next_state>::action();\n        }\n    };\n}\n\n\nnamespace obfs {\n    using size_t = decltype(sizeof(void*));\n\n    constexpr char TIME[] = __TIME__;\n    constexpr int digit(char c) {\n        return c - '0';\n    }\n    constexpr size_t SEED = digit(TIME[7]) +\n                         digit(TIME[6]) * 10 +\n                         digit(TIME[4]) * 60 +\n                         digit(TIME[3]) * 600 +\n                         digit(TIME[1]) * 3600 +\n                         digit(TIME[0]) * 36000;\n\n    template <size_t Seed, size_t Idx>\n    struct Xorshiftplus {\n        using prev = Xorshiftplus<Seed, Idx - 1>;\n\n        constexpr static size_t update() {\n            constexpr size_t x = prev::state0 ^ (prev::state0 << 23);\n            constexpr size_t y = prev::state1;\n            return x ^ y ^ (x >> 17) ^ (y >> 26);\n        }\n\n        constexpr static size_t state0 = prev::state1;\n        constexpr static size_t state1 = update();\n\n        constexpr static size_t value = state0 + state1;\n    };\n\n    template <size_t Seed>\n    struct Xorshiftplus<Seed, 0> {\n        constexpr static size_t state0 = Seed;\n        constexpr static size_t state1 = Seed << 1;\n        constexpr static size_t value = state0 + state1;\n    };\n\n    template <size_t Idx, size_t Mod>\n    constexpr size_t RAND_VAL = Xorshiftplus<SEED, Idx>::value % Mod;\n}\n\n\nnamespace obfs {\n    using Encoder = char(*)(char);\n    using Decoder = char(*)(char);\n\n    template <std::size_t size, Encoder encoder, Decoder decoder>\n    class String {\n    public:\n        template <std::size_t... Idx>\n        constexpr String(char const* str,\n                         std::index_sequence<Idx...>):\n            str{ encoder(str[Idx])... } {\n            // Do Nothing\n        }\n\n        inline char const* decode() const {\n            for (char& chr : str) {\n                chr = decoder(chr);\n            }\n            return str;\n        }\n\n    private:\n        mutable char str[size];\n    };\n\n    template <Encoder encoder, Decoder decoder, std::size_t size>\n    constexpr auto make_string(char const (&str)[size]) {\n        return String<size, encoder, decoder>(str, std::make_index_sequence<size>());\n    }\n\n    template <Encoder... encoders>\n    using encoder_seq = Sequence<Encoder, encoders...>;\n\n    template <Decoder... decoders>\n    using decoder_seq = Sequence<Decoder, decoders...>;\n\n    template <typename EncoderSeq, typename DecoderSeq>\n    using make_table = SeqPack<EncoderSeq, DecoderSeq>;\n\n    template <Encoder encoder, Decoder decoder>\n    using encoder_pair = Sequence<Encoder, encoder, decoder>;\n\n    template <typename... Seq>\n    using make_pair_table = TypeSeq<Seq...>;\n\n    template <typename Table, std::size_t size>\n    constexpr auto make_string(char const(&str)[size]) {\n        using pair = typename Table::template index<MAKE_RAND_VAL(Table::size)>;\n        constexpr Encoder encoder = pair::template index<0>::value;\n        constexpr Decoder decoder = pair::template index<1>::value;\n\n        return make_string<encoder, decoder>(str);\n    }\n}\n\n\n#endif\n"
  },
  {
    "path": "sample/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.10)\nproject(sample)\n\nset(CMAKE_CXX_STANDARD 17)\nadd_executable(string_obfs string_obfs.cpp)\nadd_executable(state_machine state_machine.cpp)\nadd_executable(random random.cpp)\n"
  },
  {
    "path": "sample/random.cpp",
    "content": "#include \"../obfuscator.hpp\"\n\n#include <iostream>\n\nint main() {\n    std::cout << MAKE_RAND_VAL(100) << std::endl;\n    return 0;\n}"
  },
  {
    "path": "sample/state_machine.cpp",
    "content": "#include \"../obfuscator.hpp\"\n\n#include <iostream>\n\n#define STATE(Name) struct Name {};\n#define EVENT(Name) struct Name {};\n\nSTATE(Final);\nEVENT(Trigger);\n\nSTATE(state1); STATE(state2); STATE(state3); STATE(state4); STATE(state5);\nEVENT(event1); EVENT(event2); EVENT(event3); EVENT(event4); EVENT(event5);\n\nstruct Action {\n    static bool trigged;\n    static void action() {\n        trigged = true;\n        std::cout << \"Trigged\" << std::endl;\n    }\n};\nbool Action::trigged = true;\n\nstruct Dummy {\n    static int dummy;\n    static void dummy1() {\n        dummy += 10;\n        std::cout << \"Dummy1\" << std::endl;\n    }\n    static void dummy2() {\n        dummy *= 20;\n        std::cout << \"Dummy2\" << std::endl;\n    }\n    static void dummy3() {\n        std::exit(1);\n    }\n};\nint Dummy::dummy = 0;\n\nint main() {\n    using namespace obfs;\n    using machine = StateMachine<\n        Stage<state1, Next<event5 , state2, Dummy::dummy1>,\n                      Next<event1 , state3, Dummy::dummy3>>,\n        Stage<state2, Next<event2 , state4>>,\n        Stage<state3, Next<None   , state3>>,\n        Stage<state4, Next<event4 , state1>,\n                      Next<event3 , state5, Dummy::dummy2>>,\n        Stage<state5, Next<Trigger, Final, Action::action>>>;\n\n    auto next1 = machine::run(state1{}, event5{});\n    auto next2 = machine::run(next1, event2{});\n    auto next3 = machine::run(next2, event3{});\n    auto next4 = machine::run(next3, Trigger{});\n\n    return 0;\n}"
  },
  {
    "path": "sample/string_obfs.cpp",
    "content": "#include \"../obfuscator.hpp\"\n\n#include <iostream>\n\ntemplate <char key>\nconstexpr char xor_(char c) {\n    return c ^ key;\n}\n\ntemplate <char Key>\nconstexpr char add(char c) {\n    return c + Key;\n}\n\ntemplate <char(*f)(char), char(*g)(char)>\nconstexpr char comp(char c) {\n    return f(g(c));\n}\n\nint main() {\n    using table = obfs::make_table<\n        obfs::encoder_seq<xor_<0x50>, add<10>, comp<xor_<0x50>, add<10>>>,\n        obfs::decoder_seq<xor_<0x50>, add<-10>, comp<add<-10>, xor_<0x50>>>>;\n\n    MAKE_STRING(str, \"Hello World !\", table);\n    std::cout << str.decode() << std::endl;\n}"
  },
  {
    "path": "script/Dockerfile.bionic",
    "content": "FROM ubuntu:18.04\n\nRUN apt-get update -yq && apt-get install -yq build-essential cmake python3.6 python3.6-dev python3-pip python3-setuptools python3-wheel\nRUN echo `g++ --version`\n\nADD . /app\n\n# Run UnitTest\nWORKDIR /app/test/build\nRUN cmake .. && \\\n    make -j `nproc` && \\\n    ./unittest\n\n# Run Additional Test\nWORKDIR /app/sample/build\nRUN cmake .. && \\\n    make -j `nproc`\n\nWORKDIR /app\nRUN python3 -m script.merge && \\\n    python3 -m script.string_obfs_tester ./sample/build/string_obfs \"Hello World !\"\n"
  },
  {
    "path": "script/__init__.py",
    "content": ""
  },
  {
    "path": "script/azure-pipelines-template-mac.yml",
    "content": "jobs:\n- job: ${{ parameters.name }}\n  pool:\n    vmImage: ${{ parameters.vmImage }}\n  steps:\n# Initialize\n  - script: git submodule update --init\n    displayName: Initialize submodule\n# UnitTest\n  - task: CMake@1\n    inputs:\n      workingDirectory: ./test/build\n      cmakeArgs: ../\n    displayName: CMake unittest\n\n  - script: cd ./test/build && make;\n    displayName: GNU Make unittest\n\n  - script: ./test/build/unittest\n    displayName: Run unittest\n# Additional tests\n  - task: UsePythonVersion@0\n    inputs:\n      versionSpec: 3.6\n      architecture: 'x64'\n\n  - script: python -m script.merge\n    displayName: Remerge obfuscator.hpp\n\n  - task: CMake@1\n    inputs:\n      workingDirectory: ./sample/build\n      cmakeArgs: ../\n    displayName: CMake sample\n\n  - script: cd ./sample/build && make;\n    displayName: GNU Make sample\n\n  - script: python -m script.string_obfs_tester ./sample/build/string_obfs \"Hello World !\"\n    displayName: String OBFS test\n"
  },
  {
    "path": "script/azure-pipelines-template-unix.yml",
    "content": "jobs:\n- job: ${{ parameters.name }}\n  pool:\n    vmImage: ${{ parameters.vmImage }}\n  steps:\n  - script: git submodule update --init\n    displayName: initialize submodule\n\n  - script: docker build -f script/Dockerfile.bionic -t cpp_obfuscator .\n    displayName: run docker-bionic\n"
  },
  {
    "path": "script/azure-pipelines-template-win.yml",
    "content": "jobs:\n- job: ${{ parameters.name }}\n  pool:\n    vmImage: ${{ parameters.vmImage }}\n  steps:\n# Initialize\n  - script: git submodule update --init\n    displayName: Initialize submodule\n# UnitTest\n  - task: CMake@1\n    inputs:\n      workingDirectory: .\\test\\build\n      cmakeArgs: ..\\\n    displayName: CMake unittest\n\n  - task: MSBuild@1\n    inputs: \n      solution: .\\test\\build\\unittest.sln\n    displayName: MSBuild unittest\n\n  - script: .\\test\\build\\Debug\\unittest.exe\n    displayName: Start unittest\n# Additional tests\n  - task: UsePythonVersion@0\n    inputs:\n      versionSpec: 3.6\n      architecture: 'x64'\n  \n  - script: python -m script.merge\n    displayName: Remerge obfuscator.hpp\n\n  - task: CMake@1\n    inputs:\n      workingDirectory: .\\sample\\build\n      cmakeArgs: ..\\\n    displayName: CMake sample\n\n  - task: MSBuild@1\n    inputs:\n      solution: .\\sample\\build\\sample.sln\n    displayName: MSBuild sample\n  \n  - script: python -m script.string_obfs_tester .\\sample\\build\\Debug\\string_obfs.exe \"Hello World !\"\n    displayName: String OBFS test\n  "
  },
  {
    "path": "script/merge.py",
    "content": "import os\nimport re\nimport sys\n\n\nclass Format(object):\n    hpp = '''\\\n#ifndef {0}\n#define {0}\n\n{1}\n{2}\n{3}\n#endif'''\n\n    @classmethod\n    def hpp_beutifier(cls, source):\n        out = ''\n        n_blank = 0\n        for line in source.split('\\n'):\n            if line == '' or line.isspace():\n                n_blank += 1\n            else:\n                if n_blank > 2:\n                    n_blank = 2\n\n                out += '\\n' * n_blank\n                n_blank = 0\n\n                out += line + '\\n'\n        return out\n\n\nclass ReSupport(object):\n    r_guard = re.compile(r'(#ifndef|#endif)')\n    r_include_dep = re.compile(r'#include \"(.+?)\"')\n    r_include = re.compile(r'#include <(.+?)>')\n    r_define = re.compile(r'#define (.+)')\n\n    @classmethod\n    def guard(cls, inp):\n        return cls.r_guard.findall(inp)\n\n    @classmethod\n    def include_dep(cls, inp):\n        return cls.r_include_dep.findall(inp)\n\n    @classmethod\n    def include(cls, inp):\n        return cls.r_include.findall(inp)\n\n    @classmethod\n    def define(cls, inp):\n        return cls.r_define.findall(inp)\n\n\nclass SourceInfo(object):\n    def __init__(self):\n        self.out = ''\n        self.deps = []\n        self.includes = []\n        self.defines = []\n\n    @classmethod\n    def set_with(cls, out, deps, includes, defines):\n        obj = cls()\n        obj.out = out\n        obj.deps = deps\n        obj.includes = includes\n        obj.defines = defines\n        return obj\n\n    @classmethod\n    def read_file(cls, path):\n        obj = cls()\n        with open(path) as f:\n            for line in f.readlines():\n                if len(ReSupport.guard(line)) > 0:\n                    continue\n\n                include_name = ReSupport.include_dep(line)\n                if len(include_name) > 0:\n                    obj.deps.append(include_name[0])\n                    continue\n\n                include_name = ReSupport.include(line)\n                if len(include_name) > 0:\n                    obj.includes.append(include_name[0])\n                    continue\n\n                define_name = ReSupport.define(line)\n                if len(define_name) > 0:\n                    obj.defines.append(define_name[0])\n                    continue\n\n                obj.out += line\n        return obj\n\n    def __add__(self, other):\n        out = self.out + other.out\n        deps = self.deps + other.deps\n        includes = self.includes + other.includes\n        defines = self.defines + other.defines\n        return SourceInfo.set_with(out, deps, includes, defines)\n\n\ndef file_list(dirname, ban_dir=[]):\n    files = []\n    if isinstance(ban_dir, str):\n        ban_dir = [ban_dir]\n\n    for name in os.listdir(dirname):\n        full_path = os.path.join(dirname, name)\n        if os.path.isfile(full_path):\n            files.append(full_path)\n        elif os.path.isdir(full_path) and name not in ban_dir:\n            files += file_list(full_path)\n    return files\n\n\ndef in_endswith(name, files):\n    for f in files:\n        if f.endswith(name):\n            return f\n    return None\n\n\ndef order_dep(deps, files, done):\n    info = SourceInfo()\n    for dep in deps:\n        if in_endswith(dep, done) is None:\n            path = in_endswith(dep.split('/')[-1], files)\n\n            if path is not None:\n                new = SourceInfo.read_file(path)\n                dep_new = order_dep(new.deps, files, done)\n\n                info += dep_new + new\n                done.append(path)\n\n    return info\n\n\ndef none_preproc(dirname):\n    if not os.path.exists(dirname):\n        return [], ''\n    \n    dep = []\n    out = ''\n    includes = ''\n    for files in os.listdir(dirname):\n        with open(os.path.join(dirname, files)) as f:\n            lines = f.readlines()\n        \n        if lines[0].startswith('#ifndef') \\\n            and lines[1].startswith('#define') \\\n            and lines[-1].startswith('#endif'):\n            lines = lines[2:-1]\n        \n        data = ''.join(lines)\n\n        include_idx = data.find('// merge:np_include')\n        if include_idx != -1:\n            start_idx = include_idx + len('// merge:np_include')\n            end_idx = data.find('// merge:end', include_idx)\n            \n            includes += data[start_idx:end_idx]\n            data = data[end_idx + len('// merge:end'):]\n\n        include_idx = data.find('// merge:include')\n        if include_idx != -1:\n            start_idx = include_idx + len('// merge:include')\n            end_idx = data.find('// merge:end', include_idx)\n\n            for line in data[start_idx:end_idx].split('\\n'):\n                res = ReSupport.include(line)\n                if len(res) > 0:\n                    dep += res\n                    includes += line + '\\n'\n\n            data = data[end_idx + len('// merge:end'):]\n\n        dep.append(files)\n        out += data\n\n    return dep, includes + out\n\n\ndef merge(dirname):\n    done = []\n    info = SourceInfo()\n    files = file_list(dirname, 'platform')\n\n    preproc_dep, info.out = \\\n        none_preproc(os.path.join(dirname, 'platform'))\n\n    for full_path in files:\n        if full_path not in done:\n            source = SourceInfo.read_file(full_path)\n            dep = order_dep(source.deps, files, done)\n\n            info += dep + source\n            done.append(full_path)\n\n    return info, preproc_dep\n\n\ndef write_hpp(outfile, merged):\n    info, preproc_dep = merged\n    dep_check = lambda file: not any(file.endswith(dep) for dep in preproc_dep)\n\n    idx = outfile.rfind('/')\n    if idx > -1:\n        outfile = outfile[idx+1:]\n    guard_name = outfile.upper().replace('.', '_')\n\n    unique_include = []\n\n    for include in info.includes:\n        if include not in unique_include and dep_check(include):\n            unique_include.append(include)\n\n    includes = '\\n'.join(\n        '#include <{}>'.format(x) for x in sorted(unique_include)) + '\\n'\n    defines = '\\n'.join(\n        '#define {}'.format(x) for x in info.defines) + '\\n'\n\n    out = Format.hpp.format(guard_name, includes, defines, info.out)\n    out = Format.hpp_beutifier(out)\n\n    with open(outfile, 'w') as f:\n        f.write(out)\n\nif __name__ == \"__main__\":\n    if len(sys.argv) > 1:\n        outfile = sys.argv[1]\n    else:\n        outfile = './obfuscator.hpp'\n\n    if len(sys.argv) > 2:\n        dirname = sys.argv[2]\n    else:\n        dirname = './obfuscator/obfs'\n\n    merged = merge(dirname)\n    write_hpp(outfile, merged)\n"
  },
  {
    "path": "script/string_obfs_tester.py",
    "content": "import sys\n\ndef main(argc, argv):\n    if argc < 3:\n        print('USAGE: python string_obfs_tester.py [STRING_OBFS.EXE] [TARGET_STRING]')\n        return 1\n    \n    path = argv[1]\n    with open(path, 'rb') as f:\n        data = f.read()\n    \n    if data.find(str.encode(argv[2])) == -1:\n        print('Cannot find string \"{}\" from \"{}\"'.format(argv[2], argv[1]))\n        return 0\n    else:\n        print('Find string \"{}\" from \"{}\"'.format(argv[2], argv[1]))\n        return 1\n\nif __name__ == '__main__':\n    retn = main(len(sys.argv), sys.argv)\n    sys.exit(retn)\n"
  },
  {
    "path": "test/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.10)\nproject(unittest)\n\nset(CMAKE_CXX_STANDARD 17)\n\nfile(GLOB test_files \n        \"impl/*.cpp\"\n)\n\nadd_executable(unittest main.cpp ${test_files})\ntarget_include_directories(unittest PRIVATE ../obfuscator)\n\nadd_subdirectory(../external/Catch2 external)\ntarget_link_libraries(unittest Catch2::Catch2)\n"
  },
  {
    "path": "test/impl/fsm.cpp",
    "content": "#include <obfs/fsm.hpp>\n#include <catch2/catch.hpp>\n\n#define STATE(Name) struct Name {};\n#define EVENT(Name) struct Name {};\n\nSTATE(Final);\nEVENT(Trigger);\n\nSTATE(state1); STATE(state2); STATE(state3); STATE(state4); STATE(state5);\nEVENT(event1); EVENT(event2); EVENT(event3); EVENT(event4); EVENT(event5);\n\nstruct Action {\n    static bool trigged;\n    static void action() {\n        trigged = true;\n    }\n};\nbool Action::trigged = true;\n\nstruct Dummy {\n    static int dummy;\n    static void dummy1() {\n        dummy += 10;\n    }\n    static void dummy2() {\n        dummy *= 20;\n    }\n};\nint Dummy::dummy = 0;\n\nTEST_CASE(\"Next\", \"[obfs::StateMachine]\") {\n    using next = obfs::Next<event1, state1>;\n    static_assert(std::is_same_v<event1, typename next::event>);\n    static_assert(std::is_same_v<state1, typename next::state>);\n    static_assert(next::action == obfs::FreeAction);\n\n    using next2 = obfs::Next<event2, state2, Action::action>;\n    static_assert(next2::action == Action::action);\n}\n\nTEST_CASE(\"Stage\", \"[obfs::StateMachine]\") {\n    using stage = obfs::Stage<\n        state1,\n        obfs::Next<event5, state2, Dummy::dummy1>,\n        obfs::Next<event1, state3>>;\n    \n    static_assert(std::is_same_v<\n        obfs::Next<event5, state2, Dummy::dummy1>,\n        typename stage::template next<event5>>);\n\n    static_assert(std::is_same_v<\n        obfs::Next<event5, state2, Dummy::dummy1>,\n        typename obfs::next_stage<stage, event5>::type>);\n\n    static_assert(std::is_same_v<\n        obfs::Next<event1, state3>,\n        typename stage::template next<event1>>);\n\n    static_assert(std::is_same_v<\n        obfs::Next<event1, state3>,\n        typename obfs::next_stage<stage, event1>::type>);\n\n    static_assert(std::is_same_v<obfs::None, typename stage::template next<event3>>);\n\n    static_assert(std::is_same_v<obfs::None, typename obfs::next_stage<stage, event3>::type>);\n\n    static_assert(std::is_same_v<obfs::None, typename obfs::next_stage<obfs::None, event1>::type>);\n}\n\nTEST_CASE(\"StateMachine\", \"[obfs::StateMachine]\") {\n    using namespace obfs;\n    using machine = StateMachine<\n        Stage<state1, Next<event5 , state2, Dummy::dummy1>,\n                      Next<event1 , state3>>,\n        Stage<state2, Next<event2 , state4>>,\n        Stage<state3, Next<None   , state3>>,\n        Stage<state4, Next<event4 , state1>,\n                      Next<event3 , state5, Dummy::dummy2>>,\n        Stage<state5, Next<Trigger, Final, Action::action>>>;\n\n    auto failure = machine::run(obfs::None{}, event5{});\n    static_assert(std::is_same_v<decltype(failure), obfs::None>);\n\n    auto next1 = machine::run(state1{}, event5{});\n    static_assert(std::is_same_v<decltype(next1), state2>);\n    REQUIRE(Dummy::dummy == 10);\n\n    auto next2 = machine::run(next1, event2{});\n    static_assert(std::is_same_v<decltype(next2), state4>);\n\n    auto failure2 = machine::run(next2, event1{});\n    static_assert(std::is_same_v<decltype(failure2), obfs::None>);\n    \n    auto next3 = machine::run(next2, event3{});\n    static_assert(std::is_same_v<decltype(next3), state5>);\n    REQUIRE(Dummy::dummy == 200);\n\n    auto next4 = machine::run(next3, Trigger{});\n    static_assert(std::is_same_v<decltype(next4), Final>);\n    REQUIRE(Action::trigged);\n}\n"
  },
  {
    "path": "test/impl/random.cpp",
    "content": "#include <obfs/random.hpp>\n#include <catch2/catch.hpp>\n\nusing ull = unsigned long long;\n\n// https://en.wikipedia.org/wiki/Xorshift\null xorshift128plus(ull state[2]) {\n\tull t = state[0];\n\tull const s = state[1];\n\tstate[0] = s;\n\tt ^= t << 23;\t\t// a\n\tt ^= t >> 17;\t\t// b\n\tt ^= s ^ (s >> 26);\t// c\n\tstate[1] = t;\n\treturn t + s;\n}\n\nTEST_CASE(\"Random case\", \"[obfs::Xorshift+]\") {\n    constexpr size_t val = MAKE_RAND_VAL(100);\n    ull state[2] = { val, val << 1 };\n\n    REQUIRE(obfs::Xorshiftplus<val, 1>::value == xorshift128plus(state));\n    REQUIRE(obfs::Xorshiftplus<val, 2>::value == xorshift128plus(state));\n    REQUIRE(obfs::Xorshiftplus<val, 3>::value == xorshift128plus(state));\n}\n"
  },
  {
    "path": "test/impl/sequence.cpp",
    "content": "#include <obfs/sequence.hpp>\n#include <catch2/catch.hpp>\n\nTEST_CASE(\"TypeVal\", \"[obfs::Sequence]\") {\n    using val = obfs::TypeVal<int, 10>;\n    \n    static_assert(std::is_same_v<int, typename val::value_type>);\n    static_assert(val::value == 10);\n}\n\nTEST_CASE(\"Sequence\", \"[obfs::Sequence]\") {\n    using seq = obfs::Sequence<int, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9>;\n\n    static_assert(std::is_same_v<int, typename seq::value::value_type>);\n    static_assert(seq::size == 10);\n    static_assert(seq::index<0>::value == 0);\n    static_assert(seq::index<5>::value == 5);\n    static_assert(seq::index<9>::value == 9);\n    static_assert(std::is_same_v<seq::index<10>, obfs::Nothing>);\n}\n\nTEST_CASE(\"TypeSeq\", \"[obfs::Sequence]\") {\n    using namespace obfs;\n    using seq = TypeSeq<TypeVal<int, 0>, TypeVal<int, 1>, TypeVal<int, 2>, TypeVal<int, 3>>;\n    \n    static_assert(seq::size == 4);\n    static_assert(seq::index<0>::value == 0);\n    static_assert(seq::index<2>::value == 2);\n    static_assert(seq::index<3>::value == 3);\n    static_assert(std::is_same_v<seq::index<4>, Nothing>);\n}\n\nTEST_CASE(\"MinVal\", \"[obfs::Sequence]\") {\n    using min = obfs::MinVal<1, 3, 2, 4, 9, 5, 6, 0, 7, 8>;\n    static_assert(min::value == 0);\n}\n\nTEST_CASE(\"SeqPack\", \"[obfs::Sequence]\") {\n    using pack = obfs::SeqPack<\n        obfs::Sequence<int, 0, 1, 2, 3, 4>,\n        obfs::Sequence<int, 1, 2, 3, 4, 5, 6>,\n        obfs::Sequence<int, 2, 3, 4, 5>\n    >;\n\n    static_assert(pack::size == 4);\n\n    using item = pack::index<2>;\n    static_assert(item::size == 3);\n    static_assert(item::index<0>::value == 2);\n    static_assert(item::index<1>::value == 3);\n    static_assert(item::index<2>::value == 4);\n}\n\ntemplate <typename Val>\nusing condition = std::conditional_t<(Val::value > 3), Val, obfs::Pass>;\n\ntemplate <typename... Values>\nusing first = typename obfs::First<obfs::Nothing, condition<Values>...>::type;\n\nTEST_CASE(\"First\", \"[obfs::Sequence]\") {\n    using result = first<\n        obfs::TypeVal<int, 0>,\n        obfs::TypeVal<int, 3>,\n        obfs::TypeVal<int, 2>,\n        obfs::TypeVal<int, 5>,\n        obfs::TypeVal<int, 1>,\n        obfs::TypeVal<int, 4>>;\n\n    static_assert(result::value == 5);\n}"
  },
  {
    "path": "test/impl/string.cpp",
    "content": "#include <obfs/string.hpp>\n#include <catch2/catch.hpp>\n\ntemplate <char Key>\nconstexpr char enc_xor(char value) {\n    return value ^ Key;\n}\n\ntemplate <char Key>\nconstexpr char add(char c) {\n    return c + Key;\n}\n\ntemplate <char(*f)(char), char(*g)(char)>\nconstexpr char comp(char c) {\n    return f(g(c));\n}\n\nTEST_CASE(\"Single encoder, decoder\", \"[obfs::String]\") {\n    REQUIRE(\n        obfs::make_string<enc_xor<0x50>, enc_xor<0x50>>(\"Hello World !\").decode()\n        == std::string_view(\"Hello World !\"));\n}\n\nTEST_CASE(\"Multiple encoder, decoder\", \"[obfs::String]\") {\n    using table = obfs::make_table<\n        obfs::encoder_seq<enc_xor<0x50>, add<10>, comp<enc_xor<0x50>, add<10>>>,\n        obfs::decoder_seq<enc_xor<0x50>, add<-10>, comp<add<-10>, enc_xor<0x50>>>>;\n\n    MAKE_STRING(str, \"Hello World !\", table);\n    REQUIRE(str.decode() == std::string_view(\"Hello World !\"));\n}\n\nTEST_CASE(\"Multiple encoder, decoder pair\", \"[obfs::String]\") {\n    using table = obfs::make_pair_table<\n        obfs::encoder_pair<enc_xor<0x50>, enc_xor<0x50>>,\n        obfs::encoder_pair<add<10>, add<-10>>,\n        obfs::encoder_pair<comp<enc_xor<0x50>, add<10>>, comp<add<-10>, enc_xor<0x50>>>\n    >;\n\n    MAKE_STRING(str, \"Hello World !\", table);\n    REQUIRE(str.decode() == std::string_view(\"Hello World !\"));\n}\n"
  },
  {
    "path": "test/main.cpp",
    "content": "#define CATCH_CONFIG_MAIN\n#include <catch2/catch.hpp>"
  }
]