diff --git a/CMakeLists.txt b/CMakeLists.txt
index da78078..94106c7 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -25,4 +25,7 @@ set(BLOCKY_CXX_FLAGS
 )
 
 add_subdirectory(thirdparty)
+
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${BLKY_CXX_FLAGS} -I ${PROJECT_SOURCE_DIR}")
 add_subdirectory(conv)
+add_subdirectory(gen)
diff --git a/conv/CMakeLists.txt b/conv/CMakeLists.txt
index cac4797..ccbfe60 100644
--- a/conv/CMakeLists.txt
+++ b/conv/CMakeLists.txt
@@ -1,5 +1,3 @@
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${BLKY_CXX_FLAGS} -I ${PROJECT_SOURCE_DIR}")
-
 add_executable(fidx_video common.hh fidx_video.cc)
 target_link_libraries(fidx_video PRIVATE args minimp4 openh264)
 
diff --git a/conv/fcprob_fcode.cc b/conv/fcprob_fcode.cc
index 277f18e..f344405 100644
--- a/conv/fcprob_fcode.cc
+++ b/conv/fcprob_fcode.cc
@@ -24,24 +24,8 @@ HelpFlag help {
   parser, "help", "display this menu", {'h', "help"},
 };
 
-enum StepAlgo {
-  kIncrement,
-};
-const std::unordered_map<std::string, StepAlgo> kStepAlgo = {
-  {"inc", kIncrement},
-};
-MapFlag<std::string, StepAlgo> algo {
-  parser, "inc", "step algorithm (inc)", {"algorithm", "algo"}, kStepAlgo,
-};
-
-Group inc {
-  parser, "increment algorithm parameters"
-};
-ValueFlag<uint32_t> inc_min {
-  inc, "1", "max stride of increment algorithm", {"inc-min"}, 1,
-};
-ValueFlag<uint32_t> inc_max {
-  inc, "1", "max stride of increment algorithm", {"inc-max"}, 1,
+ValueFlag<std::string> smap {
+  parser, "path", "step map file path", {"smap"},
 };
 
 Flag output_prob {
@@ -51,31 +35,6 @@ Flag output_prob {
 }  // namespace param
 
 
-static auto GenerateLegalStepMap(size_t dur, size_t n) {
-  const auto inc_max = args::get(param::inc_max);
-  const auto inc_min = args::get(param::inc_min);
-  Enforce(0 <= inc_min && inc_min <= inc_max, "invalid increment stride");
-
-  std::vector<std::vector<int32_t>> ret;
-  ret.resize(dur*n);
-  auto legals = &ret[0];
-
-  for (size_t t = 0; t < dur; ++t) {
-    for (size_t i = 0; i < n; ++i) {
-      switch (args::get(param::algo)) {
-      case param::kIncrement:
-        legals->reserve(inc_max-inc_min+1);
-        for (uint32_t j = inc_min; j <= inc_max; ++j) {
-          legals->push_back(static_cast<int32_t>((i+j)%n));
-        }
-        break;
-      }
-      ++legals;
-    }
-  }
-  return ret;
-}
-
 static void Exec() {
   const auto cprobs = ReadMatrix<double>(std::cin);
   Enforce(cprobs.size() > 0 && cprobs[0].size() > 0, "empty matrix");
@@ -83,7 +42,10 @@ static void Exec() {
   const auto dur = cprobs.size();
   const auto n   = cprobs[0].size();
 
-  const auto lmap = GenerateLegalStepMap(dur, n);
+  std::ifstream smap_st {args::get(param::smap)};
+  Enforce(!!smap_st, "smap path is invalid");
+  const auto smap = ReadMatrix<uint32_t>(smap_st);
+  Enforce(smap.size() >= dur*n, "smap row shortage");
 
   struct Step {
     double prob = -1;
@@ -97,7 +59,7 @@ static void Exec() {
     Enforce(cprobs[t].size() == n, "ill-formed matrix");
     for (size_t i = 0; i < n; ++i) {
       const auto& cur = steps[(t-1)*n + i];
-      for (auto j : lmap[t*n+i]) {
+      for (auto j : smap[(t-1)*n+i]) {
         auto& next = steps[t*n + j];
 
         const auto sum = cur.prob + cprobs[t][j];
diff --git a/gen/CMakeLists.txt b/gen/CMakeLists.txt
new file mode 100644
index 0000000..ba9679f
--- /dev/null
+++ b/gen/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_executable(smap smap.cc)
+target_link_libraries(smap PRIVATE args)
diff --git a/gen/smap.cc b/gen/smap.cc
new file mode 100644
index 0000000..9ebaf88
--- /dev/null
+++ b/gen/smap.cc
@@ -0,0 +1,93 @@
+#include <cassert>
+#include <cstdlib>
+#include <iostream>
+#include <string>
+#include <unordered_map>
+
+#include <args.hxx>
+
+
+namespace param {
+using namespace ::args;
+
+ArgumentParser parser {
+  "converter: feature indices + host -> stego"
+};
+HelpFlag help {
+  parser, "help", "display this menu", {'h', "help"},
+};
+
+ValueFlag<size_t> dur {
+  parser, "100", "duration", {"dur"}, 100,
+};
+ValueFlag<size_t> fcnum {
+  parser, "50", "number of feature code alphabet", {"fc-num"}, 50,
+};
+ValueFlag<size_t> branch {
+  parser, "2", "number of branch", {"branch"}, 2,
+};
+
+enum Algo {
+  kIncrement,
+};
+const std::unordered_map<std::string, Algo> kAlgo = {
+  {"inc", kIncrement},
+};
+MapFlag<std::string, Algo> algo {
+  parser, "inc", "generator algorithm", {"algo", "algorithm"}, kAlgo,
+};
+
+Group inc {
+  parser, "increment algorithm parameters"
+};
+ValueFlag<size_t> inc_min {
+  inc, "0", "min stride of each move", {"inc-min"}, 0,
+};
+Flag inc_time {
+  inc, "inc-time", "add current time value", {"inc-time"},
+};
+
+}  // namespace param
+
+
+size_t Step(size_t t, size_t c, size_t b) {
+  const auto fcnum = args::get(param::fcnum);
+
+  size_t ret;
+  switch (args::get(param::algo)) {
+  case param::kIncrement:
+    ret = c+b+args::get(param::inc_min);
+    if (param::inc_time) {
+      ret += t;
+    }
+    return ret%fcnum;
+
+  default:
+    assert(false);
+    std::abort();
+  }
+}
+
+void Exec() {
+  for (size_t t = 0; t < args::get(param::dur); ++t) {
+    for (size_t c = 0; c < args::get(param::fcnum); ++c) {
+      for (size_t b = 0; b < args::get(param::branch); ++b) {
+        std::cout << Step(t, c, b) << ' ';
+      }
+      std::cout << '\n';
+    }
+  }
+}
+
+int main(int argc, char** argv)
+try {
+  param::parser.ParseCLI(argc, argv);
+  Exec();
+  return EXIT_SUCCESS;
+} catch (const args::Help&) {
+  std::cout << param::parser << std::endl;
+  return EXIT_SUCCESS;
+} catch (const std::exception& e) {
+  std::cerr << e.what() << std::endl;
+  return EXIT_FAILURE;
+}