neptools

Modding tools to Neptunia games
git clone https://git.neptards.moe/neptards/neptools.git
Log | Files | Refs | Submodules | README | LICENSE

instruction.cpp (20949B)


      1 #include "instruction.hpp"
      2 
      3 #include "../cstring_item.hpp"
      4 #include "../raw_item.hpp"
      5 #include "../../sink.hpp"
      6 
      7 #include <libshit/lua/static_class.hpp>
      8 #include <libshit/lua/user_type.hpp>
      9 
     10 #include <boost/mp11/algorithm.hpp>
     11 #include <boost/mp11/bind.hpp>
     12 #include <boost/mp11/list.hpp>
     13 
     14 #include <iomanip>
     15 
     16 namespace Neptools::Stsc
     17 {
     18 
     19   // helpers for generic CreateAndInsert
     20   namespace
     21   {
     22     using CreateType = Libshit::NotNull<Libshit::SmartPtr<InstructionBase>>
     23       (*)(Context&, const Source&);
     24 
     25     template <Flavor F, uint8_t I>
     26     Libshit::NotNull<Libshit::SmartPtr<InstructionBase>>
     27     CreateAdapt(Context& ctx, const Source& src)
     28     { return ctx.Create<InstructionItem<F, I>>(I, src); }
     29 
     30 
     31     namespace mp = boost::mp11;
     32 #define NEPTOOLS_GEN(x,y) , Flavor::x
     33     using Flavors = mp::mp_list_c<
     34       Flavor NEPTOOLS_GEN_STSC_FLAVOR(NEPTOOLS_GEN,)>;
     35 #undef NEPTOOLS_GEN
     36 
     37     template <typename F>
     38     using MakeMap = mp::mp_transform_q<
     39       mp::mp_bind<std::pair, F, mp::_1>,
     40       mp::mp_from_sequence<std::make_integer_sequence<std::uint32_t, 256>>>;
     41     using AllOpcodes = mp::mp_flatten<
     42       mp::mp_transform_q<mp::mp_bind<MakeMap, mp::_1>, Flavors>>;
     43 
     44     template <typename List> struct CreateMapImpl;
     45     template <typename... X>
     46     struct CreateMapImpl<mp::mp_list<X...>>
     47     {
     48 #pragma GCC diagnostic push
     49 #pragma GCC diagnostic ignored "-Wmissing-braces"
     50       static inline const constexpr CreateType
     51       MAP[mp::mp_size<Flavors>::value][256] =
     52       { CreateAdapt<X::first_type::value, X::second_type::value>... };
     53 #pragma GCC diagnostic pop
     54     };
     55 
     56     using CreateMap = CreateMapImpl<AllOpcodes>;
     57   }
     58 
     59   // base
     60   InstructionBase& InstructionBase::CreateAndInsert(ItemPointer ptr, Flavor f)
     61   {
     62     auto x = RawItem::GetSource(ptr, -1);
     63     x.src.CheckSize(1);
     64     uint8_t opcode = x.src.ReadLittleUint8();
     65     auto ctx = x.ritem.GetContext();
     66     auto& ret = x.ritem.Split(
     67       ptr.offset, CreateMap::MAP[static_cast<size_t>(f)][opcode](*ctx, x.src));
     68 
     69     ret.PostInsert(f);
     70     return ret;
     71   }
     72 
     73   void InstructionBase::InstrDump(Sink& sink) const
     74   {
     75     sink.WriteLittleUint8(opcode);
     76   }
     77 
     78   std::ostream& InstructionBase::InstrInspect(
     79     std::ostream& os, unsigned indent) const
     80   {
     81     Item::Inspect_(os, indent);
     82     auto flags = os.flags();
     83     os << "instruction(0x" << std::setw(2) << std::setfill('0') << std::hex
     84        << unsigned(opcode);
     85     os.flags(flags);
     86     return os;
     87   }
     88 
     89 
     90   // generic implementation
     91   namespace
     92   {
     93 
     94     template <typename... Args> struct PODTuple;
     95 
     96     template <typename Head, typename... Args>
     97     struct PODTuple<Head, Args...>
     98     {
     99       Head head;
    100       PODTuple<Args...> tail;
    101     };
    102 
    103     template <typename T> struct PODTuple<T> { T head; };
    104     template<> struct PODTuple<> {};
    105 
    106     template <size_t I> struct PODTupleGet
    107     {
    108       template <typename T> static auto& Get(T& tuple)
    109       { return PODTupleGet<I-1>::Get(tuple.tail); }
    110     };
    111     template<> struct PODTupleGet<0>
    112     {
    113       template <typename T> static auto& Get(T& tuple)
    114       { return tuple.head; }
    115     };
    116 
    117     template <size_t I, typename T> inline auto& Get(T& tuple)
    118     { return PODTupleGet<I>::Get(tuple); }
    119 
    120 
    121     template <typename T> struct EndianMap;
    122     template<> struct EndianMap<uint8_t>
    123     { using Type = boost::endian::little_uint8_t; };
    124     template<> struct EndianMap<uint16_t>
    125     { using Type = boost::endian::little_uint16_t; };
    126     template<> struct EndianMap<uint32_t>
    127     { using Type = boost::endian::little_uint32_t; };
    128 
    129     template <typename T> using ToVoid = void;
    130 
    131 
    132     template <typename T, typename Enable = void> struct Traits;
    133 
    134     template <typename T>
    135     struct Traits<T, ToVoid<typename EndianMap<T>::Type>>
    136     {
    137       using RawType = typename EndianMap<T>::Type;
    138       static constexpr const size_t SIZE = sizeof(T);
    139 
    140       static void Validate(RawType, FilePosition) {}
    141       static T Parse(RawType r, Context&) { return r; }
    142       static RawType Dump(T r) { return r; }
    143       static void Inspect(std::ostream& os, T t) { os << uint32_t(t); }
    144       static void PostInsert(T, Flavor) {}
    145     };
    146 
    147     template<> struct Traits<float>
    148     {
    149       using RawType = boost::endian::little_uint32_t;
    150       static constexpr const size_t SIZE = 4;
    151 
    152       static void Validate(RawType, FilePosition) {}
    153       static float Parse(RawType r, Context&)
    154       {
    155         uint32_t num = r;
    156         float ret;
    157         memcpy(&ret, &num, sizeof(ret));
    158         return ret;
    159       }
    160 
    161       static RawType Dump(float f)
    162       {
    163         uint32_t ret;
    164         memcpy(&ret, &f, sizeof(ret));
    165         return ret;
    166       }
    167 
    168       static void Inspect(std::ostream& os, float v) { os << v; }
    169       static void PostInsert(float, Flavor) {}
    170     };
    171 
    172     template<> struct Traits<void*>
    173     {
    174       using RawType = boost::endian::little_uint32_t;
    175       static constexpr const size_t SIZE = 4;
    176 
    177       static void Validate(uint32_t r, FilePosition size)
    178       { LIBSHIT_VALIDATE_FIELD("Stsc::Instruction", r <= size); }
    179 
    180       static LabelPtr Parse(uint32_t r, Context& ctx)
    181       { return r ? ctx.GetLabelTo(r) : LabelPtr{}; }
    182 
    183       static RawType Dump(const LabelPtr& l)
    184       { return l ? ToFilePos(l->GetPtr()) : 0; }
    185 
    186       static void Inspect(std::ostream& os, const LabelPtr& l)
    187       { os << PrintLabel(l); }
    188 
    189       static void PostInsert(const LabelPtr&, Flavor) {}
    190     };
    191 
    192     template<> struct Traits<std::string> : public Traits<void*>
    193     {
    194       static LabelPtr Parse(uint32_t r, Context& ctx)
    195       {
    196         if (!r) return {};
    197         if (auto ptr = ctx.GetPointer(r); ptr.Maybe<RawItem>())
    198         {
    199           auto x = RawItem::GetSource(ptr, -1);
    200           return ctx.GetLabelTo(
    201             r, CStringItem::GetLabelName(x.src.PreadCString(0)));
    202         }
    203         else
    204           return ctx.GetLabelTo(r);
    205       }
    206 
    207       static void PostInsert(const LabelPtr& lbl, Flavor)
    208       { if (lbl) MaybeCreate<CStringItem>(lbl->GetPtr()); }
    209     };
    210 
    211     template<> struct Traits<Code*> : public Traits<void*>
    212     {
    213       static void PostInsert(const LabelPtr& lbl, Flavor f)
    214       { if (lbl) MaybeCreateUnchecked<InstructionBase>(lbl->GetPtr(), f); }
    215     };
    216 
    217     template <typename T, typename... Args> struct OperationsImpl;
    218     template <typename... T, size_t... I>
    219     struct OperationsImpl<std::index_sequence<I...>, T...>
    220     {
    221 #define FORALL(...) ((__VA_ARGS__), ...)
    222 
    223       template <typename Tuple>
    224       static void Validate(const Tuple& tuple, FilePosition size)
    225       {
    226         (void) size; // shut up, retarded gcc
    227         FORALL(Traits<T>::Validate(Get<I>(tuple), size));
    228       }
    229 
    230       template <typename Dst, typename Src>
    231       static void Parse(Dst& dst, const Src& src, Context& ctx)
    232       {
    233         (void) ctx; // shut up, retarded gcc
    234         FORALL(std::get<I>(dst) = Traits<T>::Parse(Get<I>(src), ctx));
    235       }
    236 
    237       template <typename Dst, typename Src>
    238       static void Dump(Dst& dst, const Src& src)
    239       { FORALL(Get<I>(dst) = Traits<T>::Dump(std::get<I>(src))); }
    240 
    241       template <typename Tuple>
    242       static void Inspect(std::ostream& os, const Tuple& tuple)
    243       {
    244         FORALL(
    245           os << ", ",
    246           Traits<T>::Inspect(os, std::get<I>(tuple)));
    247       }
    248 
    249       template <typename Tuple>
    250       static void PostInsert(const Tuple& tuple, Flavor f)
    251       {
    252         (void) f; // shut up, retarded gcc
    253         FORALL(Traits<T>::PostInsert(std::get<I>(tuple), f));
    254       }
    255 
    256       static constexpr size_t Size()
    257       {
    258         size_t sum = 0;
    259         FORALL(sum += Traits<T>::SIZE);
    260         return sum;
    261       }
    262 #undef FORALL
    263     };
    264 
    265     template <typename... Args>
    266     using Operations = OperationsImpl<std::index_sequence_for<Args...>, Args...>;
    267 
    268   }
    269 
    270   template <bool NoReturn, typename... Args>
    271   const FilePosition SimpleInstruction<NoReturn, Args...>::SIZE =
    272     Operations<Args...>::Size() + 1;
    273 
    274   template <bool NoReturn, typename... Args>
    275   SimpleInstruction<NoReturn, Args...>::SimpleInstruction(
    276     Key k, Context& ctx, uint8_t opcode, Source src)
    277     : InstructionBase{k, ctx, opcode}
    278   {
    279     ADD_SOURCE(Parse_(ctx, src), src);
    280   }
    281 
    282   template <bool NoReturn, typename... Args>
    283   void SimpleInstruction<NoReturn, Args...>::Parse_(Context& ctx, Source& src)
    284   {
    285     src.CheckSize(SIZE);
    286     using Tuple = PODTuple<typename Traits<Args>::RawType...>;
    287     static_assert(std::is_pod_v<Tuple>);
    288     static_assert(Libshit::EmptySizeof<Tuple> == Operations<Args...>::Size());
    289 
    290     auto raw = src.ReadGen<Tuple>();
    291 
    292     Operations<Args...>::Validate(raw, ctx.GetSize());
    293     Operations<Args...>::Parse(args, raw, ctx);
    294   }
    295 
    296   template <bool NoReturn, typename... Args>
    297   void SimpleInstruction<NoReturn, Args...>::Dump_(Sink& sink) const
    298   {
    299     InstrDump(sink);
    300 
    301     using Tuple = PODTuple<typename Traits<Args>::RawType...>;
    302     Tuple t;
    303     Operations<Args...>::Dump(t, args);
    304     sink.WriteGen(t);
    305   }
    306 
    307   template <bool NoReturn, typename... Args>
    308   void SimpleInstruction<NoReturn, Args...>::Inspect_(
    309     std::ostream& os, unsigned indent) const
    310   {
    311     InstrInspect(os, indent);
    312     Operations<Args...>::Inspect(os, args);
    313     os << ')';
    314   }
    315 
    316   template <bool NoReturn, typename... Args>
    317   void SimpleInstruction<NoReturn, Args...>::PostInsert(Flavor f)
    318   {
    319     Operations<Args...>::PostInsert(args, f);
    320     if (!NoReturn) MaybeCreateUnchecked<InstructionBase>(&*++Iterator(), f);
    321   }
    322 
    323   // ------------------------------------------------------------------------
    324   // specific instruction implementations
    325   InstructionRndJumpItem::InstructionRndJumpItem(
    326     Key k, Context& ctx, uint8_t opcode, Source src)
    327     : InstructionBase{k, ctx, opcode}
    328   {
    329     ADD_SOURCE(Parse_(ctx, src), src);
    330   }
    331 
    332   void InstructionRndJumpItem::Parse_(Context& ctx, Source& src)
    333   {
    334     src.CheckRemainingSize(1);
    335     uint8_t n = src.ReadLittleUint8();
    336     src.CheckRemainingSize(4*n);
    337 
    338     tgts.reserve(n);
    339 
    340     for (size_t i = 0; i < n; ++i)
    341     {
    342       uint32_t t = src.ReadLittleUint32();
    343       LIBSHIT_VALIDATE_FIELD(
    344         "Stsc::InstructionRndJumpItem", t < ctx.GetSize());
    345       tgts.push_back(ctx.GetLabelTo(t));
    346     }
    347   }
    348 
    349   void InstructionRndJumpItem::Dump_(Sink& sink) const
    350   {
    351     InstrDump(sink);
    352     sink.WriteLittleUint8(tgts.size());
    353     for (const auto& l : tgts)
    354       sink.WriteLittleUint32(ToFilePos(l->GetPtr()));
    355   }
    356 
    357   void InstructionRndJumpItem::Inspect_(std::ostream& os, unsigned indent) const
    358   {
    359     InstrInspect(os, indent);
    360     bool first = true;
    361     for (const auto& l : tgts)
    362     {
    363       if (!first) os << ", ";
    364       first = false;
    365       os << PrintLabel(l);
    366     }
    367     os << ')';
    368   }
    369 
    370   void InstructionRndJumpItem::PostInsert(Flavor f)
    371   {
    372     for (const auto& l : tgts)
    373       MaybeCreateUnchecked<InstructionBase>(l->GetPtr(), f);
    374     MaybeCreateUnchecked<InstructionBase>(&*++Iterator(), f);
    375   }
    376 
    377   // ------------------------------------------------------------------------
    378 
    379   void InstructionJumpIfItem::FixParams::Validate(
    380     FilePosition rem_size, FilePosition size)
    381   {
    382 #define VALIDATE(x) \
    383     LIBSHIT_VALIDATE_FIELD("Stsc::InstructionJumpIfItem::FixParams", x)
    384     VALIDATE(this->size * sizeof(NodeParams) <= rem_size);
    385     VALIDATE(tgt < size);
    386 #undef VALIDATE
    387   }
    388 
    389   void InstructionJumpIfItem::NodeParams::Validate(uint16_t size)
    390   {
    391 #define VALIDATE(x) \
    392     LIBSHIT_VALIDATE_FIELD("Stsc::InstructionJumpIfItem::NodeParams", x)
    393     VALIDATE(left <= size);
    394     VALIDATE(right <= size);
    395 #undef VALIDATE
    396   }
    397 
    398   InstructionJumpIfItem::InstructionJumpIfItem(
    399     Key k, Context& ctx, uint8_t opcode, Source src)
    400     : InstructionBase{k, ctx, opcode}, tgt{Libshit::EmptyNotNull{}}
    401   {
    402     ADD_SOURCE(Parse_(ctx, src), src);
    403   }
    404 
    405 #if LIBSHIT_WITH_LUA
    406   static size_t ParseTree(
    407     Libshit::Lua::StateRef vm, std::vector<InstructionJumpIfItem::Node>& tree,
    408     Libshit::Lua::Any lua_tree, int type)
    409   {
    410     if (type == LUA_TNIL) return 0;
    411 
    412     lua_checkstack(vm, 5);
    413 
    414     auto i = tree.size();
    415     tree.emplace_back();
    416 
    417     lua_rawgeti(vm, lua_tree, 1); // +1
    418     tree[i].operation = vm.Check<uint8_t>(-1);
    419     lua_rawgeti(vm, lua_tree, 2); // +2
    420     tree[i].value = vm.Check<uint32_t>(-1);
    421     lua_pop(vm, 2); // 0
    422 
    423     type = lua_rawgeti(vm, lua_tree, 3); // +1
    424     tree[i].left = ParseTree(vm, tree, lua_gettop(vm), type);
    425     lua_pop(vm, 1); // 0
    426 
    427     type = lua_rawgeti(vm, lua_tree, 4); // +1
    428     tree[i].right = ParseTree(vm, tree, lua_gettop(vm), type);
    429     lua_pop(vm, 1); // 0
    430     return i+1;
    431   }
    432 
    433   InstructionJumpIfItem::InstructionJumpIfItem(
    434     Key k, Context& ctx, Libshit::Lua::StateRef vm, uint8_t opcode,
    435     Libshit::NotNull<LabelPtr> tgt, Libshit::Lua::RawTable lua_tree)
    436     : InstructionBase{k, ctx, opcode}, tgt{tgt}
    437   { ParseTree(vm, tree, lua_tree, LUA_TTABLE); }
    438 #endif
    439 
    440   void InstructionJumpIfItem::Dispose() noexcept
    441   {
    442     tree.clear();
    443     InstructionBase::Dispose();
    444   }
    445 
    446   void InstructionJumpIfItem::Parse_(Context& ctx, Source& src)
    447   {
    448     src.CheckRemainingSize(sizeof(FixParams));
    449     auto fp = src.ReadGen<FixParams>();
    450     fp.Validate(src.GetRemainingSize(), ctx.GetSize());
    451     tgt = ctx.GetLabelTo(fp.tgt);
    452 
    453     uint16_t n = fp.size;
    454     src.CheckRemainingSize(n * sizeof(NodeParams));
    455     tree.reserve(n);
    456     for (uint16_t i = 0; i < n; ++i)
    457     {
    458       auto nd = src.ReadGen<NodeParams>();
    459       nd.Validate(n);
    460       tree.push_back({nd.operation, nd.value, nd.left, nd.right});
    461     }
    462   }
    463 
    464   void InstructionJumpIfItem::Dump_(Sink& sink) const
    465   {
    466     InstrDump(sink);
    467     sink.WriteGen(FixParams{tree.size(), ToFilePos(tgt->GetPtr())});
    468     for (auto& n : tree)
    469       sink.WriteGen(NodeParams{n.operation, n.value, n.left, n.right});
    470   }
    471 
    472   void InstructionJumpIfItem::Inspect_(std::ostream& os, unsigned indent) const
    473   {
    474     InstrInspect(os, indent) << ", " << PrintLabel(tgt) << ", ";
    475     InspectNode(os, 0);
    476     os << ')';
    477   }
    478 
    479   void InstructionJumpIfItem::InspectNode(std::ostream& os, size_t i) const
    480   {
    481     if (i >= tree.size())
    482     {
    483       os << "nil";
    484       return;
    485     }
    486 
    487     auto& n = tree[i];
    488     os << '{' << unsigned(n.operation) << ", " << n.value << ", ";
    489     InspectNode(os, n.left - 1);
    490     os << ", ";
    491     InspectNode(os, n.right - 1);
    492     os << '}';
    493   }
    494 
    495   void InstructionJumpIfItem::PostInsert(Flavor f)
    496   {
    497     MaybeCreateUnchecked<InstructionBase>(tgt->GetPtr(), f);
    498     MaybeCreateUnchecked<InstructionBase>(&*++Iterator(), f);
    499   }
    500 
    501   // ------------------------------------------------------------------------
    502 
    503   void InstructionJumpSwitchItemNoire::FixParams::Validate(FilePosition rem_size)
    504   {
    505     LIBSHIT_VALIDATE_FIELD("Stsc::InstructionJumpSwitchItemNoire::FixParams",
    506                            size * sizeof(ExpressionParams) <= rem_size);
    507   }
    508 
    509   void InstructionJumpSwitchItemNoire::ExpressionParams::Validate(
    510     FilePosition size)
    511   {
    512     LIBSHIT_VALIDATE_FIELD(
    513       "Stsc::InstructionJumpSwitchItemNoire::ExpressionParams", tgt < size);
    514   }
    515 
    516   InstructionJumpSwitchItemNoire::InstructionJumpSwitchItemNoire(
    517     Key k, Context& ctx, uint8_t opcode, Source src)
    518     : InstructionBase{k, ctx, opcode}
    519   {
    520     ADD_SOURCE(Parse_(ctx, src), src);
    521   }
    522 
    523   InstructionJumpSwitchItemPotbb::InstructionJumpSwitchItemPotbb(
    524     Key k, Context& ctx, uint8_t opcode, Source src)
    525     : InstructionJumpSwitchItemNoire{k, ctx, opcode}
    526   {
    527     ADD_SOURCE(Parse_(ctx, src), src);
    528   }
    529 
    530   void InstructionJumpSwitchItemNoire::Dispose() noexcept
    531   {
    532     expressions.clear();
    533     InstructionBase::Dispose();
    534   }
    535 
    536   void InstructionJumpSwitchItemNoire::Parse_(Context& ctx, Source& src)
    537   {
    538     src.CheckRemainingSize(sizeof(FixParams));
    539     auto fp = src.ReadGen<FixParams>();
    540     fp.Validate(src.GetRemainingSize());
    541 
    542     expected_val = fp.expected_val;
    543     last_is_default = fp.size & 0x8000;
    544     auto size = last_is_default ? fp.size & 0x7ff : uint16_t(fp.size);
    545 
    546     expressions.reserve(size);
    547     for (uint16_t i = 0; i < size; ++i)
    548     {
    549       auto exp = src.ReadGen<ExpressionParams>();
    550       exp.Validate(ctx.GetSize());
    551       expressions.emplace_back(
    552         exp.expression, ctx.GetLabelTo(exp.tgt));
    553     }
    554   }
    555 
    556   void InstructionJumpSwitchItemPotbb::Parse_(Context& ctx, Source& src)
    557   {
    558     InstructionJumpSwitchItemNoire::Parse_(ctx, src);
    559     src.CheckRemainingSize(1);
    560     trailing_byte = src.ReadLittleUint8();
    561   }
    562 
    563   void InstructionJumpSwitchItemNoire::Dump_(Sink& sink) const
    564   {
    565     InstrDump(sink);
    566     sink.WriteGen(FixParams{expected_val,
    567                             (last_is_default << 15) | expressions.size()});
    568     for (auto& e : expressions)
    569       sink.WriteGen(ExpressionParams{
    570           e.expression, ToFilePos(e.target->GetPtr())});
    571   }
    572 
    573   void InstructionJumpSwitchItemPotbb::Dump_(Sink& sink) const
    574   {
    575     InstructionJumpSwitchItemNoire::Dump_(sink);
    576     sink.WriteLittleUint8(trailing_byte);
    577   }
    578 
    579   void InstructionJumpSwitchItemNoire::InspectBase(
    580     std::ostream& os, unsigned indent) const
    581   {
    582     InstrInspect(os, indent) << ", " << expected_val << ", "
    583                              << last_is_default << ", {";
    584     bool first = true;
    585     for (auto& e : expressions)
    586     {
    587       if (!first) os << ", ";
    588       first = false;
    589       os << '{' << e.expression << ", " << PrintLabel(e.target) << '}';
    590     }
    591     os << '}';
    592   }
    593 
    594   void InstructionJumpSwitchItemNoire::Inspect_(
    595     std::ostream& os, unsigned indent) const
    596   {
    597     InspectBase(os, indent);
    598     os << ')';
    599   }
    600 
    601   void InstructionJumpSwitchItemPotbb::Inspect_(
    602     std::ostream& os, unsigned indent) const
    603   {
    604     InspectBase(os, indent);
    605     os << ", " << int(trailing_byte) << ')';
    606   }
    607 
    608   void InstructionJumpSwitchItemNoire::PostInsert(Flavor f)
    609   {
    610     for (const auto& e : expressions)
    611       MaybeCreate<InstructionBase>(e.target->GetPtr(), f);
    612     if (!last_is_default)
    613       MaybeCreateUnchecked<InstructionBase>(&*++Iterator(), f);
    614   }
    615 
    616 }
    617 
    618 #if LIBSHIT_WITH_LUA
    619 namespace Libshit::Lua
    620 {
    621 
    622   template <bool NoReturn, typename... Args>
    623   struct TypeRegisterTraits<Neptools::Stsc::SimpleInstruction<NoReturn, Args...>>
    624   {
    625     using T = Neptools::Stsc::SimpleInstruction<NoReturn, Args...>;
    626 
    627     template <size_t I>
    628     static RetNum Get0(StateRef vm, T& instr, int idx)
    629     {
    630       if constexpr (I == sizeof...(Args))
    631         lua_pushnil(vm);
    632       else if (idx == I)
    633         vm.Push(std::get<I>(instr.args));
    634       else
    635         return Get0<I+1>(vm, instr, idx);
    636       return 1;
    637     }
    638     static void Get1(const T&, Libshit::Lua::VarArg) noexcept {}
    639 
    640 
    641     template <size_t I>
    642     static void Set(StateRef vm, T& instr, int idx, Skip val)
    643     {
    644       (void) val;
    645       if constexpr (I == sizeof...(Args))
    646         luaL_error(vm, "trying to set invalid index");
    647       else if (idx == I)
    648         std::get<I>(instr.args) = vm.Check<std::tuple_element_t<
    649           I, typename T::ArgsT>>(3);
    650       else
    651         Set<I+1>(vm, instr, idx, {});
    652     }
    653 
    654     static void Register(TypeBuilder& bld)
    655     {
    656       bld.Inherit<T, Neptools::Stsc::InstructionBase>();
    657 
    658       // that tuple constructors can blow up exponentially, disable overload
    659       // check (tuple constructors can't take source, so it should be ok)
    660       bld.AddFunction<
    661         TypeTraits<T>::template Make<
    662           Neptools::Context::Key, Neptools::Context&, uint8_t, Neptools::Source&>,
    663         TypeTraits<T>::template Make<
    664           Neptools::Context::Key, Neptools::Context&, uint8_t,
    665           LuaGetRef<Neptools::Stsc::TupleTypeMapT<Args>>...>
    666       >("new");
    667 
    668       bld.AddFunction<&Get0<0>, &Get1>("get");
    669       bld.AddFunction<&Set<0>>("set");
    670     }
    671   };
    672 
    673   namespace
    674   {
    675     struct LIBSHIT_NOLUA InstructionItem : StaticClass
    676     {
    677       constexpr static char TYPE_NAME[] = "neptools.stsc.instruction_item";
    678     };
    679 
    680     template <typename T> struct InstructionReg;
    681     template <typename... X> struct InstructionReg<boost::mp11::mp_list<X...>>
    682     {
    683       static void GetTab(Lua::StateRef vm, int i, int& prev)
    684       {
    685         if (i == prev) return;
    686         if (prev != -1) lua_pop(vm, 1);
    687         prev = i;
    688         lua_createtable(vm, 255, 0);
    689         lua_pushvalue(vm, -1);
    690         lua_rawseti(vm, -4, i);
    691       }
    692 
    693       static void Register(TypeBuilder& bld)
    694       {
    695         static_assert(sizeof...(X) > 0);
    696         auto vm = bld.GetVm();
    697         int prev = -1;
    698         ((GetTab(vm, static_cast<int>(X::first_type::value), prev),
    699           TypeRegister::Register<Neptools::Stsc::InstructionItem<
    700           X::first_type::value, X::second_type::value>>(vm),
    701           lua_rawseti(vm, -2, X::second_type::value)), ...);
    702         lua_pop(vm, 1);
    703       }
    704     };
    705 
    706   }
    707 
    708   template<> struct TypeRegisterTraits<InstructionItem>
    709     : InstructionReg<Neptools::Stsc::AllOpcodes> {};
    710 
    711   static TypeRegister::StateRegister<InstructionItem> reg;
    712 
    713 }
    714 
    715 #include <libshit/container/vector.lua.hpp>
    716 LIBSHIT_STD_VECTOR_LUAGEN(
    717   label, Libshit::NotNull<Neptools::LabelPtr>);
    718 LIBSHIT_STD_VECTOR_LUAGEN(
    719   stsc_instruction_jump_if_item_node,
    720   Neptools::Stsc::InstructionJumpIfItem::Node);
    721 LIBSHIT_STD_VECTOR_LUAGEN(
    722   stsc_instruction_jump_switch_item_noire_expression,
    723   Neptools::Stsc::InstructionJumpSwitchItemNoire::Expression);
    724 #include "instruction.binding.hpp"
    725 
    726 #endif