他人の空似自作物置場

touhouSE_th145BGMOnly.zip/cnut_converter/cnut.cpp


#include "stdafx.h"

namespace TouhouSE {

namespace Squirrel3 {

namespace {

enum Type {
  OT_NULL =           0x01000001,
  OT_INTEGER =        0x05000002,
  OT_FLOAT =          0x05000004,
  OT_BOOL =           0x01000008,
  OT_STRING =         0x08000010,
  OT_TABLE =          0x0a000020,
  OT_ARRAY =          0x08000040,
  OT_USERDATA =       0x0a000080,
  OT_CLOSURE =        0x08000100,
  OT_NATIVECLOSURE =  0x08000200,
  OT_GENERATOR =      0x08000400,
  OT_USERPOINTER =    0x00000800,
  OT_THREAD =         0x08001000,
  OT_FUNCPROTO =      0x08002000,
  OT_CLASS =          0x08004000,
  OT_INSTANCE =       0x0a008000,
  OT_WEAKREF =        0x08010000,
  OT_OUTER =          0x08020000,
};

constexpr unsigned int ObjectIndexNull = 0;
constexpr unsigned int ObjectIndexString = 1;
constexpr unsigned int ObjectIndexInteger = 2;

boost::optional<boost::variant<nullptr_t, std::string, unsigned int> > readObject(std::istream &in) {
  unsigned int type;
  in.read(reinterpret_cast<char *>(&type), 4);
  if (!in.good() || (type != OT_STRING && type != OT_NULL && type != OT_INTEGER)) {
    return{};
  }
  switch (type) {
  case OT_STRING: {
    unsigned int length;
    in.read(reinterpret_cast<char *>(&length), 4);
    if (!in.good()) {
      return{};
    }
    if (length == 0) {
      return std::string();
    }
    std::string result;
    result.resize(length);
    in.read(&result.front(), length);
    return std::move(result);
  }
  case OT_NULL:
    return boost::variant<nullptr_t, std::string>(nullptr);
  case OT_INTEGER: {
    unsigned int result;
    in.read(reinterpret_cast<char *>(&result), 4);
    if (!in.good()) {
      return{};
    }
    return result;
  }
  }
  return{};
}

bool isNull(const boost::variant<nullptr_t, std::string, unsigned int> &object) {
  return object.which() == ObjectIndexNull;
}

bool isString(const boost::variant<nullptr_t, std::string, unsigned int> &object) {
  return object.which() == ObjectIndexString;
}

bool isInteger(const boost::variant<nullptr_t, std::string, unsigned int> &object) {
  return object.which() == ObjectIndexInteger;
}

boost::optional<std::string> readString(std::istream &in) {
  const auto temp = readObject(in);
  if (!temp || !isString(*temp)) {
    return{};
  }
  return boost::get<std::string>(*temp);
}
boost::optional<std::string> readStringOrNull(std::istream &in) {
  const auto temp = readObject(in);
  if (!temp) {
    return{};
  }
  if (isString(*temp)) {
    return boost::get<std::string>(*temp);
  }
  if (isNull(*temp)) {
    return std::string();
  }
  return{};
}

boost::optional<unsigned int> readInteger(std::istream &in) {
  const auto temp = readObject(in);
  if (!temp) {
    return{};
  }
  if (!isInteger(*temp)) {
    return{};
  }
  return boost::get<unsigned int>(*temp);
}

boost::optional<unsigned int> readRawInteger(std::istream &in) {
  unsigned int data;
  in.read(reinterpret_cast<char *>(&data), 4);
  if (!in.good()) {
    return{};
  }
  return data;
}

boost::optional<unsigned char> readRawChar(std::istream &in) {
  unsigned char data;
  in.read(reinterpret_cast<char *>(&data), 1);
  if (!in.good()) {
    return{};
  }
  return data;
}

boost::optional<bool> readRawBoolean(std::istream &in) {
  unsigned char data;
  in.read(reinterpret_cast<char *>(&data), 1);
  if (!in.good()) {
    return{};
  }
  return data != 0;
}

template<typename T>
boost::optional<std::vector<T> > readVector(std::istream &in, const unsigned int count, boost::optional<T> (* const func)(std::istream &)) {
  std::vector<T> result;
  result.reserve(count);
  for (unsigned int i = 0; i < count; i++) {
    const boost::optional<T> item = func(in);
    if (!item) {
      return{};
    }
    result.push_back(*item);
  }
  return std::move(result);
}

auto readStringVector(std::istream &in, const unsigned int count) {
  return readVector(in, count, readString);
}

bool checkPart(std::istream &in) {
  char signature[4];
  in.read(signature, 4);
  return in.good() && std::equal(&signature[0], &signature[_countof(signature)], "TRAP");
}

enum OpCode {
  OP_LINE = 0x00,
  OP_LOAD = 0x01,
  OP_LOADINT = 0x02,
  OP_LOADFLOAT = 0x03,
  OP_DLOAD = 0x04,
  OP_TAILCALL = 0x05,
  OP_CALL = 0x06,
  OP_PREPCALL = 0x07,
  OP_PREPCALLK = 0x08,
  OP_GETK = 0x09,
  OP_MOVE = 0x0A,
  OP_NEWSLOT = 0x0B,
  OP_DELETE = 0x0C,
  OP_SET = 0x0D,
  OP_GET = 0x0E,
  OP_EQ = 0x0F,
  OP_NE = 0x10,
  OP_ADD = 0x11,
  OP_SUB = 0x12,
  OP_MUL = 0x13,
  OP_DIV = 0x14,
  OP_MOD = 0x15,
  OP_BITW = 0x16,
  OP_RETURN = 0x17,
  OP_LOADNULLS = 0x18,
  OP_LOADROOT = 0x19,
  OP_LOADBOOL = 0x1A,
  OP_DMOVE = 0x1B,
  OP_JMP = 0x1C,
  OP_JCMP = 0x1D,
  OP_JZ = 0x1E,
  OP_SETOUTER = 0x1F,
  OP_GETOUTER = 0x20,
  OP_NEWOBJ = 0x21,
  OP_APPENDARRAY = 0x22,
  OP_COMPARITH = 0x23,
  OP_INC = 0x24,
  OP_INCL = 0x25,
  OP_PINC = 0x26,
  OP_PINCL = 0x27,
  OP_CMP = 0x28,
  OP_EXISTS = 0x29,
  OP_INSTANCEOF = 0x2A,
  OP_AND = 0x2B,
  OP_OR = 0x2C,
  OP_NEG = 0x2D,
  OP_NOT = 0x2E,
  OP_BWNOT = 0x2F,
  OP_CLOSURE = 0x30,
  OP_YIELD = 0x31,
  OP_RESUME = 0x32,
  OP_FOREACH = 0x33,
  OP_POSTFOREACH = 0x34,
  OP_CLONE = 0x35,
  OP_TYPEOF = 0x36,
  OP_PUSHTRAP = 0x37,
  OP_POPTRAP = 0x38,
  OP_THROW = 0x39,
  OP_NEWSLOTA = 0x3A,
  OP_GETBASE = 0x3B,
  OP_CLOSE = 0x3C,
};

std::string indent(const unsigned int count) {
  std::string result;
  for (unsigned int i = 0; i < count; i++) {
    result += "  ";
  }
  return std::move(result);
}

} // anonymous

boost::optional<OuterValue> OuterValue::read(std::istream &in) {
  const boost::optional<unsigned int> type = readRawInteger(in);
  const boost::optional<unsigned int> src = readInteger(in);
  const boost::optional<std::string> name = readString(in);
  if (!type || (*type != 0 && *type != 1) || !src || !name || name->empty()) {
    return{};
  }
  return OuterValue{ *type, *src, *name };
}

boost::optional<LocalVarInfo> LocalVarInfo::read(std::istream &in) {
  const boost::optional<std::string> name = readString(in);
  const boost::optional<unsigned int> pos = readRawInteger(in);
  const boost::optional<unsigned int> start = readRawInteger(in);
  const boost::optional<unsigned int> end = readRawInteger(in);
  if (!name || !pos || !start || !end) {
    return{};
  }
  return LocalVarInfo{ *name, *pos, *start, *end };
}

boost::optional<LineInfo> LineInfo::read(std::istream &in) {
  const boost::optional<unsigned int> line = readRawInteger(in);
  const boost::optional<unsigned int> op = readRawInteger(in);
  if (!line || !op) {
    return{};
  }
  return LineInfo{ *line, *op };
}

boost::optional<DefaultParam> DefaultParam::read(std::istream &in) {
  const boost::optional<unsigned int> stackIndex = readRawInteger(in);
  if (!stackIndex) {
    return{};
  }
  return DefaultParam{ *stackIndex };
}

boost::optional<Instruction> Instruction::read(std::istream &in) {
  const boost::optional<unsigned int> arg1 = readRawInteger(in);
  const boost::optional<unsigned char> op = readRawChar(in);
  const boost::optional<unsigned char> arg0 = readRawChar(in);
  const boost::optional<unsigned char> arg2 = readRawChar(in);
  const boost::optional<unsigned char> arg3 = readRawChar(in);
  if (!arg1 || !op || !arg0 || !arg2 || !arg3) {
    return{};
  }
  return Instruction{ *arg1, *op, *arg0, *arg2, *arg3 };
}

boost::optional<Function> Function::read(std::istream &in) {
  const bool part1 = checkPart(in);
  const boost::optional<std::string> srcFileName = readStringOrNull(in);
  const boost::optional<std::string> name = readStringOrNull(in);
  const bool part2 = checkPart(in);
  const boost::optional<unsigned int> literalCount = readRawInteger(in);
  const boost::optional<unsigned int> parameterCount = readRawInteger(in);
  const boost::optional<unsigned int> outerValueCount = readRawInteger(in);
  const boost::optional<unsigned int> localVarInfoCount = readRawInteger(in);
  const boost::optional<unsigned int> lineInfoCount = readRawInteger(in);
  const boost::optional<unsigned int> defaultParamCount = readRawInteger(in);
  const boost::optional<unsigned int> instructionCount = readRawInteger(in);
  const boost::optional<unsigned int> founctionCount = readRawInteger(in);
  const bool part3 = checkPart(in);
  if (!part1 || !srcFileName || !name || !part2
    || !literalCount || !parameterCount || !outerValueCount || !localVarInfoCount
    || !lineInfoCount || !defaultParamCount || !instructionCount || !founctionCount
    || !part3)
  {
    return{};
  }
  const boost::optional<std::vector<std::string> > literalList = readStringVector(in, *literalCount);
  const bool part4 = checkPart(in);
  const boost::optional<std::vector<std::string> > parameterList = readStringVector(in, *parameterCount);
  const bool part5 = checkPart(in);
  const boost::optional<std::vector<OuterValue> > outerValueList = readVector(in, *outerValueCount, &OuterValue::read);
  const bool part6 = checkPart(in);
  const boost::optional<std::vector<LocalVarInfo> > localVarInfoList = readVector(in, *localVarInfoCount, &LocalVarInfo::read);
  const bool part7 = checkPart(in);
  const boost::optional<std::vector<LineInfo> > lineInfoList = readVector(in, *lineInfoCount, &LineInfo::read);
  const bool part8 = checkPart(in);
  const boost::optional<std::vector<DefaultParam> > defaultParamList = readVector(in, *defaultParamCount, &DefaultParam::read);
  const bool part9 = checkPart(in);
  const boost::optional<std::vector<Instruction> > instructionList = readVector(in, *instructionCount, &Instruction::read);
  const bool part10 = checkPart(in);
  if (!literalList || !part4 || !parameterCount || !part5 || !outerValueList || !part6 || !localVarInfoList || !part7
    || !lineInfoCount || !part8 || !defaultParamList || !part9 || !instructionList || !part10)
  {
    return{};
  }
  const boost::optional<std::vector<Function> > childList = readVector(in, *founctionCount, &Function::read);
  const boost::optional<unsigned int> stackSize = readRawInteger(in);
  const boost::optional<bool> isGenerator = readRawBoolean(in);
  const boost::optional<unsigned int> varParamCount = readRawInteger(in);
  if (!childList || !stackSize || !isGenerator || !varParamCount) {
    return{};
  }
  return Function(
    *srcFileName,
    *name,
    *literalList,
    *parameterList,
    *outerValueList,
    *localVarInfoList,
    *lineInfoList,
    *defaultParamList,
    *instructionList,
    *childList,
    *stackSize,
    *isGenerator,
    *varParamCount);
}

std::string Function::infoToString(const unsigned int indentWidth) const {
  std::stringstream result;
  if (!srcFileName.empty()) {
    result << indent(indentWidth) << boost::format("// original filename: %s\n") % srcFileName;
  }
  if (!name.empty()) {
    result << indent(indentWidth) << boost::format("// entry point name: %s\n") % name;
  }
  result << indent(indentWidth) << boost::format("// parameterList:\n");
  for (const std::string &parameter : parameterList) {
    result << indent(indentWidth) << boost::format("//   %s\n") % parameter;
  }
  result << indent(indentWidth) << boost::format("// localVarInfoList:\n");
  for (unsigned int i = localVarInfoList.size(); i > 0; i--) {
    const LocalVarInfo &localVar = localVarInfoList[i - 1];
    result << indent(indentWidth) << boost::format("//   stack[%d] : %s\n") % localVar.pos % localVar.name;
  }
  result << indent(indentWidth) << boost::format("// generator: %s\n") % (isGenerator ? "true" : "false");
  result << indent(indentWidth) << boost::format("// variable param count: %d\n") % varParamCount;
  return result.str();
}

std::string escape(const std::string &str) {
  std::string result = str;
  const std::initializer_list<std::array<std::string, 2> > list = {
    { "\\", "\\\\" },
    { "\"", "\\\"" },
    { "\b", "\\b" },
    { "\f", "\\f" },
    { "\n", "\\n" },
    { "\r", "\\r" },
    { "\t", "\\t" },
  };
  for (const std::array<std::string, 2> &item : list) {
    boost::algorithm::replace_all(result, item[0], item[1]);
  }
  return result;
}

std::string Function::getDisplayLiteralString(const unsigned int index) const {
  return (boost::format("\"%s\"") % escape(literalList[index])).str();
}

std::string Function::instructionToString(unsigned int indentWidth) const {
  std::vector<std::string> codeList;
  std::vector<unsigned int> labelList;
  unsigned int index = -1;
  for (const Instruction &in : instructionList) {
    std::stringstream out;
    index++;
    out << indent(indentWidth);
    switch (in.op) {
    case OP_LINE: /*out << boost::format("// line %d\n") % in.arg1;*/ break; // diffをとるときに邪魔なので撤去
    case OP_LOAD: out << boost::format("stack[%d] = %s;\n") % static_cast<unsigned int>(in.arg0) % getDisplayLiteralString(in.arg1); break;
    case OP_LOADINT: out << boost::format("stack[%d] = %d;\n") % static_cast<unsigned int>(in.arg0) % in.arg1; break;
    case OP_LOADFLOAT: out << boost::format("stack[%d] = %f;\n") % static_cast<unsigned int>(in.arg0) % *reinterpret_cast<const float *>(&in.arg1); break;
    case OP_DLOAD:
      out << boost::format("stack[%d] = %s;\n") % static_cast<unsigned int>(in.arg0) % getDisplayLiteralString(in.arg1);
      out << indent(indentWidth) << boost::format("stack[%d] = %s;\n") % static_cast<unsigned int>(in.arg2) % getDisplayLiteralString(in.arg3);
      break;
    case OP_TAILCALL:
    case OP_CALL: {
      std::vector<std::string> argList;
      for (unsigned int i = 0; i < in.arg3; i++) {
        argList.push_back((boost::format("stack[%d]") % (in.arg2 + i)).str());
      }
      if (in.arg0 != 0xFF) {
        out << boost::format("stack[%d] = ") % static_cast<unsigned int>(in.arg0);
      }
      out << boost::format("stack[%d](%s);\n") % in.arg1 % boost::algorithm::join(argList, ", ");
      break;
    }
    case OP_PREPCALL:
      out << boost::format("stack[%1$d] = stack[%2$d];\n%5$sstack[%3$d] = stack[%1$d][stack[%4$d]];\n")
        % static_cast<unsigned int>(in.arg3)
        % static_cast<unsigned int>(in.arg2)
        % static_cast<unsigned int>(in.arg0)
        % in.arg1
        % indent(indentWidth);
      break;
    case OP_PREPCALLK:
      out << boost::format("stack[%1$d] = stack[%2$d];\n%5$sstack[%3$d] = stack[%1$d][%4$s];\n")
        % static_cast<unsigned int>(in.arg3)
        % static_cast<unsigned int>(in.arg2)
        % static_cast<unsigned int>(in.arg0)
        % getDisplayLiteralString(in.arg1)
        % indent(indentWidth);
      break;
    case OP_GETK: out << boost::format("stack[%d] = stack[%d][%s];\n") % static_cast<unsigned int>(in.arg0) % static_cast<unsigned int>(in.arg2) % getDisplayLiteralString(in.arg1); break;
    case OP_MOVE: out << boost::format("stack[%d] = stack[%d];\n") % static_cast<unsigned int>(in.arg0) % in.arg1; break;
    case OP_NEWSLOT:
      out << boost::format("stack[%d].newSlot(stack[%d], stack[%d]);\n") % in.arg1 % static_cast<unsigned int>(in.arg2) % static_cast<unsigned int>(in.arg3);
      if (in.arg0 != 0xFF) {
        out << indent(indentWidth) << boost::format("stack[%d] = stack[%d][stack[%d]];\n") % static_cast<unsigned int>(in.arg0) % in.arg1 % static_cast<unsigned int>(in.arg2);
      }
      break;
    case OP_DELETE: out << boost::format("stack[%d].delete(stack[%d]);\n") % in.arg1 % static_cast<unsigned int>(in.arg2); break;
    case OP_SET:
      out << boost::format("stack[%d][stack[%d]] = stack[%d];\n") % in.arg1 % static_cast<unsigned int>(in.arg2) % static_cast<unsigned int>(in.arg3);
      if (in.arg0 != 0xFF) {
        out << indent(indentWidth) << boost::format("stack[%d] = stack[%d][stack[%d]];\n") % static_cast<unsigned int>(in.arg0) % in.arg1 % static_cast<unsigned int>(in.arg2);
      }
      break;
    case OP_GET: out << boost::format("stack[%d] = stack[%d][stack[%d]];\n") % static_cast<unsigned int>(in.arg0) % in.arg1 % static_cast<unsigned int>(in.arg2); break;
    case OP_EQ:
      out << boost::format("stack[%d] = (stack[%d] == ") % static_cast<unsigned int>(in.arg0) % static_cast<unsigned int>(in.arg2);
      if (in.arg3 != 0) {
        out << boost::format("%s") % getDisplayLiteralString(in.arg1);
      }
      else {
        out << boost::format("stack[%d]") % in.arg1;
      }
      out << ");\n";
      break;
    case OP_NE:
      out << boost::format("stack[%d] = (stack[%d] != ") % static_cast<unsigned int>(in.arg0) % static_cast<unsigned int>(in.arg2);
      if (in.arg3 != 0) {
        out << boost::format("%s") % getDisplayLiteralString(in.arg1);
      }
      else {
        out << boost::format("stack[%d]") % in.arg1;
      }
      out << ");\n";
      break;
    case OP_ADD: out << boost::format("stack[%d] = (stack[%d] + stack[%d]);\n") % static_cast<unsigned int>(in.arg0) % static_cast<unsigned int>(in.arg2) % in.arg1; break;
    case OP_SUB: out << boost::format("stack[%d] = (stack[%d] - stack[%d]);\n") % static_cast<unsigned int>(in.arg0) % static_cast<unsigned int>(in.arg2) % in.arg1; break;
    case OP_MUL: out << boost::format("stack[%d] = (stack[%d] * stack[%d]);\n") % static_cast<unsigned int>(in.arg0) % static_cast<unsigned int>(in.arg2) % in.arg1; break;
    case OP_DIV: out << boost::format("stack[%d] = (stack[%d] / stack[%d]);\n") % static_cast<unsigned int>(in.arg0) % static_cast<unsigned int>(in.arg2) % in.arg1; break;
    case OP_MOD: out << boost::format("stack[%d] = (stack[%d] %% stack[%d]);\n") % static_cast<unsigned int>(in.arg0) % static_cast<unsigned int>(in.arg2) % in.arg1; break;
    case OP_BITW: {
      std::string op;
      switch (in.arg3) {
      case 0: op = "&"; break;
      case 2: op = "|"; break;
      case 3: op = "^"; break;
      case 4: op = "<<"; break;
      case 5: op = ">>"; break;
      case 6: op = ">>>"; break;
      default: continue;
      }
      out << boost::format("stack[%d] = (stack[%d] %s stack[%d]);\n") % static_cast<unsigned int>(in.arg0) % static_cast<unsigned int>(in.arg2) % op % in.arg1;
      break;
    }
    case OP_RETURN:
      out << "return";
      if (in.arg0 != 0xFF) {
        out << boost::format(" stack[%d]") % in.arg1;
      }
      out << ";\n";
      break;
    case OP_LOADNULLS:
      for (unsigned int i = 0; i < in.arg1; i++) {
        if (i != 0) {
          out << indent(indentWidth);
        }
        out << boost::format("stack[%d] = null;\n") % static_cast<unsigned int>(in.arg0 + i);
      }
      break;
    case OP_LOADROOT: out << boost::format("stack[%d] = getroottable();\n") % static_cast<unsigned int>(in.arg0); break;
    case OP_LOADBOOL: out << boost::format("stack[%d] = %s;\n") % static_cast<unsigned int>(in.arg0) % (in.arg1 == 0 ? "false" : "true"); break;
    case OP_DMOVE:
      out << boost::format("stack[%d] = stack[%d];\n") % static_cast<unsigned int>(in.arg0) % in.arg1;
      out << indent(indentWidth) << boost::format("stack[%d] = stack[%d];\n") % static_cast<unsigned int>(in.arg2) % static_cast<unsigned int>(in.arg3);
      break;
    case OP_JMP: {
      const unsigned int labelIndex = index + in.arg1 + 1;
      out << boost::format("goto label%d;\n") % labelIndex;
      labelList.push_back(labelIndex);
      break;
    }
    case OP_JCMP: {
      std::string op;
      switch (in.arg3) {
      case 0: op = ">"; break;
      case 2: op = ">="; break;
      case 3: op = "<"; break;
      case 4: op = "<="; break;
      case 5: op = "<=>"; break;
      default:continue;
      }
      const unsigned int labelIndex = index + in.arg1 + 1;
      out << boost::format("if (!(stack[%d] %s stack[%d])) {\n") % static_cast<unsigned int>(in.arg2) % op % static_cast<unsigned int>(in.arg0);
      out << indent(indentWidth + 1) << boost::format("goto label%d;\n") % labelIndex;
      out << indent(indentWidth) << "}\n";
      labelList.push_back(labelIndex);
      break;
    }
    case OP_JZ: {
      const unsigned int labelIndex = index + in.arg1 + 1;
      out << boost::format("if (!stack[%d]) {\n") % static_cast<unsigned int>(in.arg0);
      out << indent(indentWidth + 1) << boost::format("goto label%d;\n") % labelIndex;
      out << indent(indentWidth) << "}\n";
      labelList.push_back(labelIndex);
      break;
    }
    case OP_GETOUTER: out << boost::format("stack[%d] = getOuter(\"%s\");\n") % static_cast<unsigned int>(in.arg0) % outerValueList[in.arg1].name; break;
    case OP_SETOUTER:
      if (in.arg0 != 0xFF) {
        out << boost::format("stack[%d] = setOuter(\"%s\", stack[%d]);\n")
          % static_cast<unsigned int>(in.arg0)
          % outerValueList[in.arg1].name
          % static_cast<unsigned int>(in.arg2);
      } else {
        out << boost::format("setOuter(\"%s\", stack[%d]);\n")
          % outerValueList[in.arg1].name
          % static_cast<unsigned int>(in.arg2);
      }
      break;
    case OP_NEWOBJ:
      out << boost::format("stack[%d] = ") % static_cast<unsigned int>(in.arg0);
      switch (in.arg3) {
      case 0:
        out << "{}";
        break;
      case 1:
        out << "[]";
        break;
      case 2:
        out << "class";
        if (in.arg1 != 0xFFFFFFFF) {
          out << boost::format(" extends stack[%d]") % in.arg1;
        }
        // attribute
        out << " {}";
      }
      out << ";\n";
      break;
    case OP_APPENDARRAY: {
      std::string value;
      switch (in.arg2) {
      case 0: value = (boost::format("stack[%d]") % in.arg1).str(); break;
      case 1: value = (boost::format("%s") % getDisplayLiteralString(in.arg1)).str(); break;
      case 2: value = (boost::format("%d") % in.arg1).str(); break;
      case 3: value = (boost::format("%f") % *reinterpret_cast<const float *>(&in.arg1)).str(); break;
      case 4: value = (boost::format("%s") % (in.arg1 == 0 ? "false" : "true")).str(); break;
      default: continue;
      }
      out << boost::format("stack[%d].append(%s);\n") % static_cast<unsigned int>(in.arg0) % value;
      break;
    }
    case OP_COMPARITH: {
      std::string op;
      switch (in.arg3) {
      case '+': op = "+"; break;
      case '-': op = "-"; break;
      case '*': op = "*"; break;
      case '/': op = "/"; break;
      case '%': op = "%"; break;
      }
      out << boost::format("stack[%1$d] = (stack[%2$d][stack[%3$d]] = (stack[%2$d][stack[%3$d]] %4$s stack[%5$d]));\n")
        % static_cast<unsigned int>(in.arg0)
        % (in.arg1 >> 16)
        % static_cast<unsigned int>(in.arg2)
        % op
        % (in.arg1 & 0x0000FFFF);
      break;
    }
    case OP_INC: {
      std::string op;
      if (in.arg3 == 1) {
        op = "++";
      } else if (in.arg3 == 255) {
        op = "--";
      } else {
        continue;
      }
      out << boost::format("stack[%d] = (%sstack[%d][stack[%d]]);\n") % static_cast<unsigned int>(in.arg0) % op % in.arg1 % static_cast<unsigned int>(in.arg2);
      break;
    }
    case OP_INCL: {
      std::string op;
      if (in.arg3 == 1) {
        op = "++";
      } else if (in.arg3 == 255) {
        op = "--";
      } else {
        continue;
      }
      out << boost::format("stack[%d] = (%sstack[%d]);\n") % static_cast<unsigned int>(in.arg0) % op % in.arg1;
      break;
    }
    case OP_PINC: {
      std::string op;
      if (in.arg3 == 1) {
        op = "++";
      } else if (in.arg3 == 255) {
        op = "--";
      } else {
        continue;
      }
      out << boost::format("stack[%d] = (stack[%d][stack[%d]]%s);\n") % static_cast<unsigned int>(in.arg0) % in.arg1 % static_cast<unsigned int>(in.arg2) % op;
      break;
    }
    case OP_PINCL: {
      std::string op;
      if (in.arg3 == 1) {
        op = "++";
      } else if (in.arg3 == 255) {
        op = "--";
      } else {
        continue;
      }
      out << boost::format("stack[%d] = (stack[%d]%s);\n") % static_cast<unsigned int>(in.arg0) % in.arg1 % op;
      break;
    }
    case OP_CMP: {
      std::string op;
      switch (in.arg3) {
      case 0: op = ">"; break;
      case 2: op = ">="; break;
      case 3: op = "<"; break;
      case 4: op = "<="; break;
      case 5: op = "<=>"; break;
      default: continue;
      }
      out << boost::format("stack[%d] = (stack[%d] %s stack[%d]);\n") % static_cast<unsigned int>(in.arg0) % static_cast<unsigned int>(in.arg2) % op % in.arg1;
      break;
    }
    case OP_EXISTS: out << boost::format("stack[%d] = (stack[%d] in stack[%d]);\n") % static_cast<unsigned int>(in.arg0) % static_cast<unsigned int>(in.arg2) % in.arg1; break;
    case OP_INSTANCEOF: out << boost::format("stack[%d] = (stack[%d] instanceof stack[%d]);\n") % static_cast<unsigned int>(in.arg0) % static_cast<unsigned int>(in.arg2) % in.arg1; break;
    case OP_AND: {
      const unsigned int labelIndex = index + in.arg1 + 1;
      out << boost::format("if (!stack[%d]) {\n") % static_cast<unsigned int>(in.arg2);
      out << indent(indentWidth + 1) << boost::format("stack[%d] = stack[%d];\n") % static_cast<unsigned int>(in.arg0) % static_cast<unsigned int>(in.arg2);
      out << indent(indentWidth + 1) << boost::format("goto label%d;\n") % labelIndex;
      out << indent(indentWidth) << "}\n";
      labelList.push_back(labelIndex);
      break;
    }
    case OP_OR: {
      const unsigned int labelIndex = index + in.arg1 + 1;
      out << boost::format("if (stack[%d]) {\n") % static_cast<unsigned int>(in.arg2);
      out << indent(indentWidth + 1) << boost::format("stack[%d] = stack[%d];\n") % static_cast<unsigned int>(in.arg0) % static_cast<unsigned int>(in.arg2);
      out << indent(indentWidth + 1) << boost::format("goto label%d;\n") % labelIndex;
      out << indent(indentWidth) << "}\n";
      labelList.push_back(labelIndex);
      break;
    }
    case OP_NEG: out << boost::format("stack[%d] = -stack[%d];\n") % static_cast<unsigned int>(in.arg0) % in.arg1; break;
    case OP_NOT: out << boost::format("stack[%d] = !stack[%d];\n") % static_cast<unsigned int>(in.arg0) % in.arg1; break;
    case OP_BWNOT: out << boost::format("stack[%d] = ~stack[%d];\n") % static_cast<unsigned int>(in.arg0) % in.arg1; break;
    case OP_CLOSURE: {
      const Function &child = childList[in.arg1];
      std::vector<std::string> argList;
      for (unsigned int i = 0; i < child.parameterList.size(); i++) {
        const std::string &param = child.parameterList[i];
        if (argList.size() == 0 && param == "this") {
          continue;
        }
        if (i < child.parameterList.size() - child.defaultParamList.size()) {
          argList.push_back(param);
          continue;
        }
        const unsigned int defaultParamIndex = i - (child.parameterList.size() - child.defaultParamList.size());
        argList.push_back((boost::format("%s = stack[%d]") % param % child.defaultParamList[defaultParamIndex].stackIndex).str());
      }
      const std::string argStr = boost::algorithm::join(argList, ", ");
      out << "// function info\n";
      out << child.infoToString(indentWidth);
      out << indent(indentWidth) << boost::format("stack[%d] = function (%s) {\n") % static_cast<unsigned int>(in.arg0) % argStr;
      out << child.instructionToString(indentWidth + 1);
      out << indent(indentWidth) << "}\n";
      break;
    }
    case OP_YIELD:
      out << boost::format("stackSave(%d);\n") % static_cast<unsigned int>(in.arg2);
      out << indent(indentWidth) << "yield";
      if (in.arg0 != 0xFF) {
        out << boost::format(" stack[%d]") % in.arg1;
      }
      out << ";\n";
      break;
    case OP_RESUME:
      out << boost::format("resume stack[%d];\n") % static_cast<unsigned int>(in.arg0);
      break;
    case OP_FOREACH: {
      const unsigned int errorLabelIndex = index + in.arg1 + 1;
      const unsigned int successLabelIndex = index + 1 + 1;
      out << boost::format("if (stack[%1$d], stack[%2$d], stack[%3$d] = next(stack[%4$d], stack[%3$d])) {\n")
        % static_cast<unsigned int>(in.arg2)
        % static_cast<unsigned int>(in.arg2 + 1)
        % static_cast<unsigned int>(in.arg2 + 2)
        % static_cast<unsigned int>(in.arg0);
      out << indent(indentWidth + 1) << boost::format("goto label%d;\n") % successLabelIndex;
      out << indent(indentWidth) << "} else {\n";
      out << indent(indentWidth + 1) << boost::format("goto label%d;\n") % errorLabelIndex;
      out << indent(indentWidth) << "}\n";
      labelList.push_back(successLabelIndex);
      labelList.push_back(errorLabelIndex);
      break;
    }
    case OP_POSTFOREACH: out << "// POST_FOREACH\n"; break;
    case OP_CLONE: out << boost::format("stack[%d] = clone(stack[%d]);\n") % static_cast<unsigned int>(in.arg0) % in.arg1; break;
    case OP_TYPEOF: out << boost::format("stack[%d] = typeof(stack[%d]);\n") % static_cast<unsigned int>(in.arg0) % in.arg1; break;
    case OP_PUSHTRAP: {
      const unsigned int labelIndex = index + in.arg1 + 1;
      out << boost::format("pushStoreExceptionStackIndex(%d);\n") % static_cast<unsigned int>(in.arg0);
      out << indent(indentWidth) << boost::format("pushExceptionCatchLabel(label%s);\n") % labelIndex;
      labelList.push_back(labelIndex);
      break;
    }
    case OP_POPTRAP:
      for (unsigned int i = 0; i < in.arg0; i++) {
        if (i != 0) {
          out << indent(indentWidth);
        }
        out << "popException;\n";
      }
      break;
    case OP_THROW: out << boost::format("throw(stack[%d]);\n") % static_cast<unsigned int>(in.arg0); break;
    case OP_NEWSLOTA: {
      constexpr unsigned int FLAG_ATTRIBUTE = 0x01;
      constexpr unsigned int FLAG_STATIC = 0x02;
      out << boost::format("stack[%d].newSlotA(stack[%d], stack[%d]);\n") % in.arg1 % static_cast<unsigned int>(in.arg2) % static_cast<unsigned int>(in.arg3);
      if ((in.arg0 & FLAG_ATTRIBUTE) != 0) {
        out << indent(indentWidth) << boost::format("stack[%d][stack[%d]].setAttribute(stack[%d]);\n")
          % in.arg1
          % static_cast<unsigned int>(in.arg2)
          % static_cast<unsigned int>(in.arg2 - 1);
      }
      out << indent(indentWidth) << boost::format("stack[%d][stack[%d]].setStatic(%s);\n")
        % in.arg1
        % static_cast<unsigned int>(in.arg2)
        % (((in.arg0 & FLAG_STATIC) != 0) ? "true" : "false");
      break;
    }
    case OP_GETBASE: out << boost::format("stack[%d] = base;\n") % static_cast<unsigned int>(in.arg0); break;
    case OP_CLOSE: out << boost::format("closeOuters(%d);\n") % in.arg1; break;
    default:
      std::wcout << boost::wformat(L"Error: squirrel reverse compile(op = %d).\n") % in.op;
      out << boost::format("// unknown op: %3d %3d %3d %3d %3d\n")
        % static_cast<unsigned int>(in.op)
        % static_cast<unsigned int>(in.arg0)
        % in.arg1
        % static_cast<unsigned int>(in.arg2)
        % static_cast<unsigned int>(in.arg3);
    }
    codeList.push_back(out.str());
  }
  std::sort(labelList.begin(), labelList.end());
  labelList.erase(std::unique(labelList.begin(), labelList.end()), labelList.end());
  std::stringstream result;
  for (unsigned int i = 0; i < codeList.size(); i++) {
    if (std::find(labelList.begin(), labelList.end(), i) != labelList.end()) {
      result << boost::format("label%d:\n") % i;
    }
    result << codeList[i];
  }
  return result.str();
}

std::string Function::toString(const unsigned int indentWidth) const {
  return infoToString(indentWidth) + instructionToString(indentWidth);
}

boost::optional<CNut> CNut::read(std::istream &in, const unsigned long long int fileSize) {
#pragma pack(push, 1)
  struct {
    unsigned char binarySignature[2];
    char signature[4];
    unsigned int charSize;
    unsigned int intSize;
    unsigned int floatSize;
  } header;
#pragma pack(pop)
  if (!in.good()) {
    return{};
  }
  in.read(reinterpret_cast<char *>(&header), sizeof(header));
  if (!in.good() || header.binarySignature[0] != 0xFA || header.binarySignature[1] != 0xFA
    || !std::equal(&header.signature[0], &header.signature[_countof(header.signature)], "RIQS")
    || header.charSize != 1 || header.intSize != 4 || header.floatSize != 4)
  {
    return{};
  }
  const boost::optional<Function> function = Function::read(in);
  if (!function) {
    return{};
  }
  char tailSignature[4];
  in.read(tailSignature, 4);
  if (!in.good() || !std::equal(&tailSignature[0], &tailSignature[_countof(tailSignature)], "LIAT")) {
    return{};
  }
  return CNut(*function);
}

std::string CNut::toString() const {
  return function.toString();
}

} // Squirrel3

} // TouhouSE