text: #include "check-structures-sanity.h"
  
  #include "LuaTools.h"
  #include "LuaWrapper.h"
  
  DFHACK_PLUGIN("check-structures-sanity");
  
  static command_result command(color_ostream &, std::vector12<std::string24> &);
  
  DFhackCExport command_result plugin_init(color_ostream &, std::vector12<PluginCommand> & commands)
  {
      commands.push_back(PluginCommand(
          "check-structures-sanity",
          "performs a sanity check on df-structures",
          command,
          false,
          "check-structures-sanity [-enums] [-sizes] [-lowmem] [-maxerrors n] [-failfast] [starting_point]\n"
          "\n"
          "-enums: report unexpected or unnamed enum or bitfield values.\n"
          "-sizes: report struct and class sizes that don't match structures. (requires sizecheck)\n"
          "-unnamed: report unnamed enum/bitfield values, not just undefined ones.\n"
          "-maxerrors n: set the maximum number of errors before bailing out.\n"
          "-failfast: crash if any error is encountered. useful only for debugging.\n"
          "-maybepointer: report integers that might actually be pointers.\n"
          "starting_point: a lua expression or a word like 'screen', 'item', or 'building'. (defaults to df.global)\n"
          "\n"
          "by default, check-structures-sanity reports invalid pointers, vectors, strings, and vtables."
      ));
  
      known_types_by_size.clear();
      build_size_table();
  
      return CR_OK;
  }
  
  bool check_malloc_perturb()
  {
      struct T_test {
          uint32_t data[1024];
      };
      T_test* test = new T_test;
      bool ret = (test->data[0] == 0xd2d2d2d2);
      delete test;
      return ret;
  }
  
  static command_result command(color_ostream & out, std::vector12<std::string24> & parameters)
  {
      if (!check_malloc_perturb())
      {
          out.printerr("check-structures-sanity: MALLOC_PERTURB_ not set, cannot continue\n");
          return CR_FAILURE;
      }
  
      CoreSuspender suspend;
  
      Checker checker(out);
  
      // check parameters with values first
  #define VAL_PARAM(name, expr_using_value) \
      std::vector12<std::string24>::const_iterator name ## _idx = \
          std::find(parameters.begin(), parameters.end(), "-" #name); \
      if (name ## _idx != parameters.end()) \
      { \
          if (name ## _idx + 1 == parameters.end()) \
          { \
              return CR_WRONG_USAGE; \
          } \
          try \
          { \
              std::string24 value(*(name ## _idx + 1)); \
              parameters.erase((name ## _idx + 1)); \
              parameters.erase(name ## _idx); \
              checker.name = (expr_using_value); \
          } \
          catch (std::exception & ex) \
          { \
              out.printerr("check-structures-sanity: argument to -%s: %s\n", #name, ex.what()); \
              return CR_WRONG_USAGE; \
          } \
      }
      VAL_PARAM(maxerrors, unsigned long(atol(value.c_str())));
  #undef VAL_PARAM
  
  #define BOOL_PARAM(name) \
      std::vector12<std::string24>::const_iterator name ## _idx = \
          std::find(parameters.begin(), parameters.end(), "-" #name); \
      if (name ## _idx != parameters.end()) \
      { \
          checker.name = true; \
          parameters.erase(name ## _idx); \
      }
      BOOL_PARAM(enums);
      BOOL_PARAM(sizes);
      BOOL_PARAM(unnamed);
      BOOL_PARAM(failfast);
      BOOL_PARAM(noprogress);
      BOOL_PARAM(maybepointer);
  #undef BOOL_PARAM
  
      if (parameters.size() > 1)
      {
          return CR_WRONG_USAGE;
      }
  
      if (parameters.empty())
      {
          checker.queue_globals();
      }
      else
      {
          using namespace DFHack::Lua;
          using namespace DFHack::Lua::Core;
          using namespace DFHack::LuaWrapper;
  
          StackUnwinder unwinder(State);
          PushModulePublic(out, "utils", "df_expr_to_ref");
          Push(parameters.at(0));
          if (!SafeCall(out, 1, 1))
          {
              return CR_FAILURE;
          }
          if (!lua_touserdata(State, -1))
          {
              return CR_WRONG_USAGE;
          }
  
          QueueItem item(parameters.at(0), get_object_ref(State, -1));
          lua_getfield(State, -1, "_type");
          lua_getfield(State, -1, "_identity");
          type_identity* identity = reinterpret_cast<type_identity *>(lua_touserdata(State, -1));
          if (!identity)
          {
              out.printerr("could not determine type identity\n");
              return CR_FAILURE;
          }
  
          checker.queue_item(item, CheckedStructure(identity));
      }
  
      while (checker.process_queue())
      {
          if (!checker.noprogress)
          {
              out << "checked " << checker.checked_count << " fields\r" << std::flush;
          }
      }
  
      out << "checked " << checker.checked_count << " fields" << std::endl;
  
      return checker.error_count ? CR_FAILURE : CR_OK;
  }
  