bool disassembleExport(DisassembleState &state, latte::cf::inst id, latte::cf::Instruction &cf)
{
   auto eid = static_cast<latte::exp::inst>(cf.expWord1.inst);
   auto name = latte::exp::name[id];
   auto type = static_cast<latte::exp::Type::Type>(cf.expWord0.type);

   state.out
      << name
      << ": ";

   switch (type) {
   case latte::exp::Type::Pixel:
      state.out << "PIX";
      break;
   case latte::exp::Type::Position:
      state.out << "POS";
      break;
   case latte::exp::Type::Parameter:
      state.out << "PARAM";
      break;
   }

   state.out
      << cf.expWord0.dstReg;

   state.out
      << ", R"
      << cf.expWord0.srcReg
      << ".";

   writeSelectName(state, cf.expWord1.srcSelX);
   writeSelectName(state, cf.expWord1.srcSelY);
   writeSelectName(state, cf.expWord1.srcSelZ);
   writeSelectName(state, cf.expWord1.srcSelW);

   endLine(state);
   return true;
}
bool Disassembler::cfTEX(fmt::MemoryWriter &out, latte::cf::inst cfID, latte::cf::Instruction &cf)
{
   uint32_t *ptr = mWords + (wordsPerCF * cf.word0.addr);

   out
      << latte::cf::name[cfID] << ": "
      << "ADDR(" << cf.word0.addr << ") CNT(" << (cf.word1.count + 1) << ")\n";

   increaseIndent();

   for (auto slot = 0u; slot <= cf.word1.count; ) {
      auto tex = *reinterpret_cast<latte::tex::Instruction*>(ptr);
      auto name = latte::tex::name[tex.word0.inst];
      auto id = tex.word0.inst;

      if (id == latte::tex::VTX_FETCH || id == latte::tex::VTX_SEMANTIC || id == latte::tex::GET_BUFFER_RESINFO) {
         assert(false);
         ptr += wordsPerVTX;
      } else if (id == latte::tex::MEM) {
         assert(false);
         ptr += wordsPerMEM;
      } else {
         out
            << mIndent.c_str()
            << fmt::pad(mGroupCounter, groupCounterSize, '0')
            << ' '
            << name
            << ' ';

         // Write dst
         if (tex.word1.dstRel != 0) {
            // TODO: relative address
            out << "__UNK_REL(" << tex.word1.dstRel << ")__";
         }

         writeRegisterName(out, tex.word1.dstReg);
         out << '.';
         writeSelectName(out, tex.word1.dstSelX);
         writeSelectName(out, tex.word1.dstSelY);
         writeSelectName(out, tex.word1.dstSelZ);
         writeSelectName(out, tex.word1.dstSelW);

         // Write src
         out << ", ";

         if (tex.word0.srcRel != 0) {
            // TODO: relative address
            out << "__UNK_REL(" << tex.word0.srcRel << ")__";
         }

         writeRegisterName(out, tex.word0.srcReg);
         out << '.';
         writeSelectName(out, tex.word2.srcSelX);
         writeSelectName(out, tex.word2.srcSelY);
         writeSelectName(out, tex.word2.srcSelZ);
         writeSelectName(out, tex.word2.srcSelW);

         out
            << ", t" << tex.word0.resourceID
            << ", s" << tex.word2.samplerID;

         if (tex.word2.offsetX || tex.word2.offsetY || tex.word2.offsetZ) {
            out
               << " OFFSET("
               << tex.word2.offsetX
               << ", "
               << tex.word2.offsetY
               << ", "
               << tex.word2.offsetZ
               << ")";
         }

         if (tex.word1.lodBias) {
            out << " LOD_BIAS(" << tex.word1.lodBias << ")";
         }

         if (!tex.word1.coordTypeX) {
            out << " CTX_UNORM";
         }

         if (!tex.word1.coordTypeY) {
            out << " CTY_UNORM";
         }

         if (!tex.word1.coordTypeZ) {
            out << " CTZ_UNORM";
         }

         if (!tex.word1.coordTypeW) {
            out << " CTW_UNORM";
         }

         if (tex.word0.bcFracMode) {
            out << " BFM";
         }

         if (tex.word0.fetchWholeQuad) {
            out << " FWQ";
         }

         out << '\n';
         ptr += wordsPerTEX;
      }

      mGroupCounter++;
      slot++;
   }

   decreaseIndent();
   return true;
}