neptools

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

instruction.cpp (13374B)


      1 #include "instruction.hpp"
      2 #include "data.hpp"
      3 #include "expansion.hpp"
      4 #include "../context.hpp"
      5 #include "../raw_item.hpp"
      6 #include "../../sink.hpp"
      7 
      8 #include <libshit/except.hpp>
      9 #include <libshit/container/vector.lua.hpp>
     10 
     11 #include <set>
     12 #include <iostream>
     13 
     14 namespace Neptools::Stcm
     15 {
     16 
     17 #define IP InstructionItem::Parameter
     18 
     19   static void Param48Validate(uint32_t param, FilePosition file_size)
     20   {
     21 #define VALIDATE(x) LIBSHIT_VALIDATE_FIELD("Stcm Param48", x)
     22     switch (IP::TypeTag(param))
     23     {
     24     case IP::Type48::MEM_OFFSET:
     25       VALIDATE((IP::Value(param) + 16) < file_size);
     26       return;
     27     case IP::Type48::IMMEDIATE:
     28       return;
     29     case IP::Type48::INDIRECT:
     30       return; // ??
     31     default:
     32       VALIDATE((param >= IP::Type48Special::READ_STACK_MIN &&
     33                 param <= IP::Type48Special::READ_STACK_MAX) ||
     34                (param >= IP::Type48Special::READ_4AC_MIN &&
     35                 param <= IP::Type48Special::READ_4AC_MAX));
     36       return;
     37     }
     38 #undef VALIDATE
     39   }
     40 
     41   void InstructionItem::Parameter::Validate(FilePosition file_size) const
     42   {
     43 #define VALIDATE(x) LIBSHIT_VALIDATE_FIELD("Stcm::InstructionItem::Parameter", x)
     44     switch (TypeTag(param_0))
     45     {
     46     case Type0::MEM_OFFSET:
     47       VALIDATE(Value(param_0) < file_size);
     48       Param48Validate(param_4, file_size);
     49       Param48Validate(param_8, file_size);
     50       return;
     51 
     52       //case Type0::UNK: todo
     53     case Type0::INDIRECT:
     54       VALIDATE(Value(param_0) < 256 && param_4 == 0x40000000);
     55       Param48Validate(param_8, file_size);
     56       return;
     57 
     58     case Type0::SPECIAL:
     59       if ((param_0 >= Type0Special::READ_STACK_MIN && param_0 <= Type0Special::READ_STACK_MAX) ||
     60           (param_0 >= Type0Special::READ_4AC_MIN && param_0 <= Type0Special::READ_4AC_MAX))
     61       {
     62         VALIDATE(param_4 == 0x40000000);
     63         VALIDATE(param_8 == 0x40000000);
     64       }
     65       else if (param_0 == Type0Special::INSTR_PTR0 ||
     66                param_0 == Type0Special::INSTR_PTR1)
     67       {
     68         VALIDATE(param_4 < file_size);
     69         VALIDATE(param_8 == 0x40000000);
     70       }
     71       else if (param_0 == Type0Special::COLL_LINK)
     72       {
     73         VALIDATE(param_4 + 8 < file_size);
     74         VALIDATE(param_8 == 0);
     75       }
     76       else if (param_0 == Type0Special::EXPANSION)
     77       {
     78         VALIDATE(param_4 + 80 < file_size);
     79         VALIDATE(param_8 == 0);
     80       }
     81       else
     82         VALIDATE(!"Unknown special");
     83       return;
     84     default:
     85       VALIDATE(!"Unknown type0");
     86     }
     87 #undef VALIDATE
     88   }
     89 
     90   void InstructionItem::Header::Validate(FilePosition file_size) const
     91   {
     92 #define VALIDATE(x) LIBSHIT_VALIDATE_FIELD("Stcm::InstructionItem::Header", x)
     93     VALIDATE(is_call == 0 || is_call == 1);
     94     VALIDATE(param_count < 16);
     95     VALIDATE(size >= sizeof(Header) + param_count*sizeof(Parameter));
     96 
     97     if (is_call)
     98       VALIDATE(opcode < file_size);
     99     else
    100       VALIDATE(
    101         opcode < USER_OPCODES ||
    102         (opcode >= SYSTEM_OPCODES_BEGIN && opcode <= SYSTEM_OPCODES_END));
    103 #undef VALIDATE
    104   }
    105 
    106   InstructionItem::InstructionItem(Key k, Context& ctx, Source src)
    107     : ItemWithChildren{k, ctx}
    108   {
    109     ADD_SOURCE(Parse_(ctx, src), src);
    110   }
    111 
    112   void InstructionItem::Parse_(Context& ctx, Source& src)
    113   {
    114     auto instr = src.ReadGen<Header>();
    115     instr.Validate(ctx.GetSize());
    116 
    117     if (instr.is_call)
    118       SetTarget(ctx.GetLabelTo(instr.opcode));
    119     else
    120       SetOpcode(instr.opcode);
    121 
    122     params.reserve(instr.param_count);
    123     for (size_t i = 0; i < instr.param_count; ++i)
    124     {
    125       auto p = src.ReadGen<Parameter>();
    126       params.emplace_back(ctx, p);
    127     }
    128   }
    129 
    130   auto InstructionItem::Param::GetVariant(Context& ctx, const Parameter& in)
    131     -> Variant
    132   {
    133     in.Validate(ctx.GetSize());
    134 
    135     switch (Parameter::TypeTag(in.param_0))
    136     {
    137     case Parameter::Type0::MEM_OFFSET:
    138       return MemOffset{
    139         ctx.GetLabelTo(Parameter::Value(in.param_0)),
    140           Param48{ctx, in.param_4},
    141           Param48{ctx, in.param_8}};
    142 
    143     case Parameter::Type0::INDIRECT:
    144       return Indirect{
    145         Parameter::Value(in.param_0),
    146           Param48{ctx, in.param_8}};
    147 
    148 #define RETVAR(type, val) return Variant{ \
    149         std::in_place_index<static_cast<size_t>(Type::type)>, val}
    150     case Parameter::Type0::SPECIAL:
    151       if (in.param_0 >= Parameter::Type0Special::READ_STACK_MIN &&
    152           in.param_0 <= Parameter::Type0Special::READ_STACK_MAX)
    153         RETVAR(READ_STACK, in.param_0 - Parameter::Type0Special::READ_STACK_MIN);
    154       else if (in.param_0 >= Parameter::Type0Special::READ_4AC_MIN &&
    155                in.param_0 <= Parameter::Type0Special::READ_4AC_MAX)
    156         RETVAR(READ_4AC, in.param_0 - Parameter::Type0Special::READ_4AC_MIN);
    157       else if (in.param_0 == Parameter::Type0Special::INSTR_PTR0)
    158         RETVAR(INSTR_PTR0, ctx.GetLabelTo(in.param_4));
    159       else if (in.param_0 == Parameter::Type0Special::INSTR_PTR1)
    160         RETVAR(INSTR_PTR1, ctx.GetLabelTo(in.param_4));
    161       else if (in.param_0 == Parameter::Type0Special::COLL_LINK)
    162         RETVAR(COLL_LINK, ctx.GetLabelTo(in.param_4));
    163       else if (in.param_0 == Parameter::Type0Special::EXPANSION)
    164         RETVAR(EXPANSION, ctx.GetLabelTo(in.param_4));
    165       else
    166         LIBSHIT_UNREACHABLE("Invalid special parameter type");
    167     }
    168 
    169     LIBSHIT_UNREACHABLE("Invalid parameter type");
    170   }
    171 
    172   void InstructionItem::Param::Dump(Sink& sink) const
    173   {
    174     Parameter pp;
    175     switch (GetType())
    176     {
    177     case Type::MEM_OFFSET:
    178     {
    179       const auto& o = Get<Type::MEM_OFFSET>();
    180       pp.param_0 = Parameter::Tag(
    181         Parameter::Type0::MEM_OFFSET, ToFilePos(o.target->GetPtr()));
    182       pp.param_4 = o.param_4.Dump();
    183       pp.param_8 = o.param_8.Dump();
    184       break;
    185     }
    186 
    187     case Type::INDIRECT:
    188     {
    189       const auto& i = Get<Type::INDIRECT>();
    190       pp.param_0 = Parameter::Tag(Parameter::Type0::INDIRECT, i.param_0);
    191       pp.param_4 = 0x40000000;
    192       pp.param_8 = i.param_8.Dump();
    193       break;
    194     }
    195 
    196     case Type::READ_STACK:
    197       pp.param_0 = Parameter::Type0Special::READ_STACK_MIN +
    198         Get<Type::READ_STACK>();
    199       pp.param_4 = 0x40000000;
    200       pp.param_8 = 0x40000000;
    201       break;
    202 
    203     case Type::READ_4AC:
    204       pp.param_0 = Parameter::Type0Special::READ_4AC_MIN +
    205         Get<Type::READ_4AC>();
    206       pp.param_4 = 0x40000000;
    207       pp.param_8 = 0x40000000;
    208       break;
    209 
    210     case Type::INSTR_PTR0:
    211       pp.param_0 = Parameter::Type0Special::INSTR_PTR0;
    212       pp.param_4 = ToFilePos(Get<Type::INSTR_PTR0>()->GetPtr());
    213       pp.param_8 = 0x40000000;
    214       break;
    215 
    216     case Type::INSTR_PTR1:
    217       pp.param_0 = Parameter::Type0Special::INSTR_PTR1;
    218       pp.param_4 = ToFilePos(Get<Type::INSTR_PTR1>()->GetPtr());
    219       pp.param_8 = 0x40000000;
    220       break;
    221 
    222     case Type::COLL_LINK:
    223       pp.param_0 = Parameter::Type0Special::COLL_LINK;
    224       pp.param_4 = ToFilePos(Get<Type::COLL_LINK>()->GetPtr());
    225       pp.param_8 = 0;
    226       break;
    227 
    228     case Type::EXPANSION:
    229       pp.param_0 = Parameter::Type0Special::EXPANSION;
    230       pp.param_4 = ToFilePos(Get<Type::EXPANSION>()->GetPtr());
    231       pp.param_8 = 0;
    232       break;
    233     }
    234     sink.WriteGen(pp);
    235   }
    236 
    237   std::ostream& operator<<(std::ostream& os, const InstructionItem::Param& p)
    238   {
    239     using T = InstructionItem::Param::Type;
    240     switch (p.GetType())
    241     {
    242     case T::MEM_OFFSET:
    243     {
    244       const auto& o = p.Get<T::MEM_OFFSET>();
    245       return os << "{'mem_offset', " << PrintLabel(o.target) << ", "
    246                 << o.param_4 << ", " << o.param_8 << '}';
    247     }
    248     case T::INDIRECT:
    249     {
    250       const auto& i = p.Get<T::INDIRECT>();
    251       return os << "{'indirect', " << i.param_0 << ", " << i.param_8 << '}';
    252     }
    253     case T::READ_STACK:
    254       return os << "{'read_stack', " << p.Get<T::READ_STACK>() << "}";
    255     case T::READ_4AC:
    256       return os << "{'read_4ac', " << p.Get<T::READ_4AC>() << "}";
    257     case T::INSTR_PTR0:
    258       return os << "{'instr_ptr0', " << PrintLabel(p.Get<T::INSTR_PTR0>()) << '}';
    259     case T::INSTR_PTR1:
    260       return os << "{'instr_ptr1', " << PrintLabel(p.Get<T::INSTR_PTR1>()) << '}';
    261     case T::COLL_LINK:
    262       return os << "{'coll_link', " << PrintLabel(p.Get<T::COLL_LINK>()) << '}';
    263     case T::EXPANSION:
    264       return os << "{'expansion', " << PrintLabel(p.Get<T::EXPANSION>()) << '}';
    265     }
    266     LIBSHIT_UNREACHABLE("Invalid type");
    267   }
    268 
    269 
    270   auto InstructionItem::Param48::GetVariant(Context& ctx, uint32_t in) -> Variant
    271   {
    272     switch (Parameter::TypeTag(in))
    273     {
    274     case Parameter::Type48::MEM_OFFSET:
    275       RETVAR(MEM_OFFSET, ctx.GetLabelTo(Parameter::Value(in)));
    276 
    277     case Parameter::Type48::IMMEDIATE:
    278       RETVAR(IMMEDIATE, Parameter::Value(in));
    279 
    280     case Parameter::Type48::INDIRECT:
    281       RETVAR(INDIRECT, Parameter::Value(in));
    282 
    283     case Parameter::Type48::SPECIAL:
    284       if (in >= Parameter::Type48Special::READ_STACK_MIN &&
    285           in <= Parameter::Type48Special::READ_STACK_MAX)
    286       {
    287         RETVAR(READ_STACK, in - Parameter::Type48Special::READ_STACK_MIN);
    288       }
    289       else if (in >= Parameter::Type48Special::READ_4AC_MIN &&
    290                in <= Parameter::Type48Special::READ_4AC_MAX)
    291       {
    292         RETVAR(READ_4AC, in - Parameter::Type48Special::READ_4AC_MIN);
    293       }
    294       else
    295         LIBSHIT_UNREACHABLE("Invalid 48Special param");
    296     }
    297 #undef RETVAR
    298 
    299     LIBSHIT_UNREACHABLE("Invalid 48 param");
    300   }
    301 
    302   std::ostream& operator<<(std::ostream& os, const InstructionItem::Param48& p)
    303   {
    304     using T = InstructionItem::Param48::Type;
    305     switch (p.GetType())
    306     {
    307     case T::MEM_OFFSET:
    308       return os << "{'mem_offset', " << PrintLabel(p.Get<T::MEM_OFFSET>()) << '}';
    309     case T::IMMEDIATE:
    310       return os << "{'immediate', " << p.Get<T::IMMEDIATE>() << '}';
    311     case T::INDIRECT:
    312       return os << "{'indirect', " << p.Get<T::INDIRECT>() << '}';
    313     case T::READ_STACK:
    314       return os << "{'read_stack', " << p.Get<T::READ_STACK>() << '}';
    315     case T::READ_4AC:
    316       return os << "{'read_4ac', " << p.Get<T::READ_4AC>() << '}';
    317     }
    318 
    319     LIBSHIT_UNREACHABLE("Invalid 48 param");
    320   }
    321 
    322   static const std::set<uint32_t> no_returns{0, 6};
    323 
    324   InstructionItem& InstructionItem::CreateAndInsert(ItemPointer ptr)
    325   {
    326     auto x = RawItem::GetSource(ptr, -1);
    327 
    328     x.src.CheckSize(sizeof(Header));
    329     auto inst = x.src.PreadGen<Header>(0);
    330     x.src.CheckSize(inst.size);
    331 
    332     auto& ret = x.ritem.SplitCreate<InstructionItem>(ptr.offset, x.src);
    333 
    334     auto rem_data = inst.size - sizeof(Header) -
    335       sizeof(Parameter) * inst.param_count;
    336     if (rem_data)
    337       ret.MoveNextToChild(rem_data);
    338 
    339     LIBSHIT_ASSERT(ret.GetSize() == inst.size);
    340 
    341     // recursive parse
    342     if (ret.IsCall())
    343       MaybeCreate<InstructionItem>(ret.GetTarget()->GetPtr());
    344     if (ret.IsCall() || !no_returns.count(ret.GetOpcode()))
    345       MaybeCreate<InstructionItem>({&*++ret.Iterator(), 0});
    346     for (const auto& p : ret.params)
    347     {
    348       using T = Param::Type;
    349       switch (p.GetType())
    350       {
    351       case T::MEM_OFFSET:
    352         MaybeCreate<DataItem>(p.Get<T::MEM_OFFSET>().target->GetPtr());
    353         break;
    354       case T::INSTR_PTR0:
    355         MaybeCreate<InstructionItem>(p.Get<T::INSTR_PTR0>()->GetPtr());
    356         break;
    357       case T::INSTR_PTR1:
    358         MaybeCreate<InstructionItem>(p.Get<T::INSTR_PTR1>()->GetPtr());
    359         break;
    360       default:;
    361       }
    362     }
    363 
    364     return ret;
    365   }
    366 
    367 
    368   FilePosition InstructionItem::GetSize() const noexcept
    369   {
    370     return sizeof(Header) + params.size() * sizeof(Parameter) +
    371       ItemWithChildren::GetSize();
    372   }
    373 
    374   uint32_t InstructionItem::Param48::Dump() const noexcept
    375   {
    376     switch (GetType())
    377     {
    378     case Type::MEM_OFFSET:
    379       return Parameter::Tag(Parameter::Type48::MEM_OFFSET,
    380                             ToFilePos(Get<Type::MEM_OFFSET>()->GetPtr()));
    381     case Type::IMMEDIATE:
    382       return Parameter::Tag(Parameter::Type48::IMMEDIATE, Get<Type::IMMEDIATE>());
    383     case Type::INDIRECT:
    384       return Parameter::Tag(Parameter::Type48::INDIRECT, Get<Type::INDIRECT>());
    385     case Type::READ_STACK:
    386       return Parameter::Type48Special::READ_STACK_MIN + Get<Type::READ_STACK>();
    387     case Type::READ_4AC:
    388       return Parameter::Type48Special::READ_4AC_MIN + Get<Type::READ_4AC>();
    389     }
    390     LIBSHIT_UNREACHABLE("Invalid Param48 Type stored");
    391   }
    392 
    393   void InstructionItem::Fixup()
    394   {
    395     ItemWithChildren::Fixup_(sizeof(Header) + params.size() * sizeof(Parameter));
    396   }
    397 
    398   void InstructionItem::Dispose() noexcept
    399   {
    400     params.clear();
    401     ItemWithChildren::Dispose();
    402   }
    403 
    404   void InstructionItem::Dump_(Sink& sink) const
    405   {
    406     Header hdr;
    407     hdr.is_call = IsCall();
    408 
    409     if (IsCall())
    410       hdr.opcode = ToFilePos(GetTarget()->GetPtr());
    411     else
    412       hdr.opcode = GetOpcode();
    413     hdr.param_count = params.size();
    414     hdr.size = GetSize();
    415     sink.WriteGen(hdr);
    416 
    417     for (const auto& p : params)
    418       p.Dump(sink);
    419 
    420     ItemWithChildren::Dump_(sink);
    421   }
    422 
    423   void InstructionItem::Inspect_(std::ostream &os, unsigned indent) const
    424   {
    425     Item::Inspect_(os, indent);
    426 
    427     if (IsCall())
    428       os << "call(" << PrintLabel(GetTarget());
    429     else
    430       os << "instruction(" << GetOpcode();
    431     if (!params.empty())
    432     {
    433       os << ", {\n";
    434       size_t i = 0;
    435       for (const auto& p : params)
    436         Indent(os, indent+1) << '[' << i++ << "] = " << p << ",\n";
    437       Indent(os, indent) << '}';
    438     }
    439     os << ')';
    440     InspectChildren(os, indent);
    441   }
    442 
    443 }
    444 
    445 LIBSHIT_STD_VECTOR_LUAGEN(
    446   stcm_instruction_param, Neptools::Stcm::InstructionItem::Param);
    447 #include "instruction.binding.hpp"