

#include "tilbuild.hpp"

// ---------------------------------------------------------------------------
void til_builder_t::remove_anonymous_namespaces(qstring &buf)
{
  char *p = buf.begin();
  while ( true )
  {             // 1234567890
    p = strstr(p, "`anonymous");
    if ( p == NULL )
      break;
    const char *q = p + 10;
    if ( *q != '-' && *q != ' ' )
      break;
    if ( strncmp(q+1, "namespace'::", 12) != 0 )
      break;      // 123456789012
    size_t idx = p - buf.begin();
    buf.remove(idx, 10+1+12);
    p = buf.begin() + idx;
  }
}


// ---------------------------------------------------------------------------
void til_builder_t::convert_to_idb_name(qstring &buf)
{
  // MAXNAMELEN is the maximum length of structname.fieldname.
  // Allocate half of it for structname and the other half for the fieldname
  if ( buf.length() >= MAXNAMELEN/2 )
  {
    til_builder_t::longnames_t::iterator q = longnames.find(buf);
    if ( q != longnames.end() )
    {
      buf.qclear();
      buf.append(q->second);
    }
    else
    {
      qstring lname = buf;
      buf.resize(MAXNAMELEN/2);
      while ( buf.last() == ' ' )
        buf.resize(buf.length()-1);
      for ( int i=0; longnames.find(buf) != longnames.end(); i++ )
      {
        char suffix[8];
        size_t suflen = qsnprintf(suffix, sizeof(suffix), "_%d", i);
        buf.resize(MAXNAMELEN/2-suflen);
        buf.append(suffix);
      }
      longnames[lname] = buf;
    }
  }
}


// ---------------------------------------------------------------------------
bool til_builder_t::get_symbol_name(pdb_sym_t &sym, qstring &buf)
{
  bool is_unnamed = false;
  sym.get_name(&buf);
  if ( buf.empty() )
  {
    is_unnamed = true;
  }
  else
  {
    //
    remove_anonymous_namespaces(buf);

    // <unnamed-tag>  => <unnamed_tag>
    // <unnamed-type-xxx> => <unnamed_type_xxx>
    char *p = buf.begin();
    while ( true )
    {
      //             012345678
      p = strstr(p, "<unnamed");
      if ( p == NULL )
        break;
      p += 8;
      while ( *p != '\0' )
      {
        if ( *p == '>' )
        {
          p++;
          break;
        }
        else if ( *p == '-' )
        {
          *p = '_';
        }
        p++;
      }
      is_unnamed = true;
    }
    if ( !is_unnamed && buf == "__unnamed" )
      is_unnamed = true;

    convert_to_idb_name(buf);
  }
  return is_unnamed;
}

// ---------------------------------------------------------------------------
bool til_builder_t::get_symbol_type(pdb_sym_t &sym, qtype &itp, qtype *fields, int *p_id)
{
  pdb_sym_t pType(*pdb_access);
  if ( p_id != NULL )
    *p_id = -1;
  if ( sym.get_type(&pType) != S_OK )
    return false;
  return retrieve_type(pType, NULL, itp, fields, p_id);
}

// ---------------------------------------------------------------------------
size_t til_builder_t::get_symbol_type_length(pdb_sym_t &sym) const
{
  DWORD64 size = 0;
  DWORD tag = 0;

  sym.get_symTag(&tag);
  if ( tag == SymTagData )
  {
    pdb_sym_t pType(*pdb_access);
    if ( sym.get_type(&pType) == S_OK )
      pType.get_length(&size);
  }
  else
  {
    sym.get_length(&size);
  }
  return size_t(size);
}

//----------------------------------------------------------------------
til_builder_t::cvt_code_t til_builder_t::append_basetype(DWORD baseType, int size, qtype &res) const
{
  type_t bt = BTF_TYPEDEF;
  const char *name = NULL;
  switch ( baseType )
  {
    default:
    case 0x12c304:                      // "impdir_entry" (guessed)
    case btNoType:
    case btBCD:
    case btBit:
      return cvt_failed;
    case btVoid:
      bt = BTF_VOID;
      break;
    case btChar:
      bt = BT_INT8|BTMT_CHAR;
      break;
    case btBool:
      bt = BT_BOOL;
      if ( size != inf.cc.size_b )
      {
        switch ( size )
        {
          case 1: bt |= BTMT_BOOL1; break;
          case 2: bt |= BTMT_BOOL2; break;
          case 4: bt |= BTMT_BOOL4; break;
          default:
            // can't make this bool size; make an int
            goto MAKE_INT;
        }
      }
      break;
MAKE_INT:
    case btInt:
    case btLong:     bt = get_int_type_bit(size);              break;
    case btUInt:
    case btULong:    bt = get_int_type_bit(size)|BTMT_USIGNED; break;
    case btFloat:
      if ( size == sizeof_ldbl() )
      {
        bt = BTMT_LNGDBL;
      }
      else
      {
        switch( size )
        {
          case 4:  bt = BTMT_FLOAT;   break;
          default:
          case 8:  bt = BTMT_DOUBLE;  break;
          case 10: bt = BTMT_SPECFLT; break;
        }
      }
      bt |= BT_FLOAT;
      break;
    case btWChar:    name = "wchar_t";                         break;
    case btBSTR:     name = "BSTR";                            break;
    case btHresult:  name = "HRESULT";                         break;
    case btCurrency: name = "CURRENCY";                        break;
    case btVariant:  name = "VARIANT";                         break;
    case btComplex:  name = "complex";                         break;
    case btDate:     name = "DATE";                            break;
  }
  res.append(bt);
  if ( name != NULL )
  {
    append_name(&res, name);
    return cvt_typedef;
  }
  return cvt_ok;
}

//----------------------------------------------------------------------
bool til_builder_t::retrieve_children_types_names(pdb_sym_t &sym, func_type_info_t &fi, pdb_sym_t *funcSym)
{
  struct type_name_collector_t : public pdb_access_t::children_visitor_t
  {
    func_type_info_t &fi;
    til_builder_t *tb;
    til_t *ti;
    HRESULT visit_child(pdb_sym_t &sym)
    {
      // check that it's a parameter
      DWORD dwDataKind;
      if ( sym.get_dataKind(&dwDataKind) == S_OK
        && !(dwDataKind == DataIsParam || dwDataKind == DataIsObjectPtr) )
        return S_OK;
      qtype type, fields;
      if ( tb->retrieve_type(sym, parent, type, &fields, NULL) )
      {
        funcarg_info_t &arg = fi.push_back();
        arg.type = type;
        arg.fields = fields;
        sym.get_name(&arg.name);
        arg.argloc = 0;
      }
      return S_OK;
    }
    type_name_collector_t(til_t *_ti, til_builder_t *_tb, func_type_info_t &_fi) : fi(_fi), tb(_tb), ti(_ti) {}
  };
  fi.clear();
  type_name_collector_t pp(ti, this, fi);
  HRESULT hr = pdb_access->iterate_children(sym, SymTagNull, pp);
  if ( SUCCEEDED(hr) && funcSym != NULL )
  {
    // get parameter names from the function symbol
    func_type_info_t args;
    args.flags = 0;
    enum_function_args(*funcSym, args);
//    QASSERT(497, args.empty() || args.size() == fi.size() );
    bool custom_cc = false;
    for ( int i = 0; i < args.size() && i < fi.size(); i++ )
    {
      if ( fi[i].name.empty() )
        fi[i].name = args[i].name;
      uint32 cur_argloc = args[i].argloc;
      fi[i].argloc = cur_argloc;
      if ( !custom_cc && is_reg_argloc(cur_argloc) )
      {
        if ( pdb_access->get_machine_type() == CV_CFL_80386 )
        {
          if ( fi.cc == CM_CC_FASTCALL
            && (get_argloc_r1(cur_argloc) == R_cx && i==0 || get_argloc_r1(cur_argloc) == R_dx && i==1) )
          {
            // ignore ecx and edx for fastcall
          }
          else if ( fi.cc == CM_CC_THISCALL && get_argloc_r1(cur_argloc) == R_cx && i==0 )
          {
            // ignore ecx for thiscall
          }
          else
          {
            custom_cc = true;
          }
        }
        //ask_for_feedback("pdb: register arguments are not supported for machine type %d", machine_type);
      }
    }
    if ( custom_cc )
    {
      // we have some register params; need to convert function to custom cc
      fi.cc = (is_purging_cc(fi.cc) || fi.cc == CM_CC_THISCALL || fi.cc == CM_CC_FASTCALL) ? CM_CC_SPECIALP : CM_CC_SPECIAL;
    }
    return S_OK;
  }
  return S_FALSE;
}

//----------------------------------------------------------------------
cm_t til_builder_t::convert_cc(DWORD cc0) const
{
  switch ( cc0 )
  {
    case CV_CALL_GENERIC    :
    case CV_CALL_NEAR_C     :
    case CV_CALL_FAR_C      : return CM_CC_CDECL;
    case CV_CALL_NEAR_PASCAL:
    case CV_CALL_FAR_PASCAL : return CM_CC_PASCAL;
    case CV_CALL_NEAR_FAST  :
    case CV_CALL_FAR_FAST   : return CM_CC_FASTCALL;
//    case CV_CALL_SKIPPED    :
    case CV_CALL_NEAR_STD   :
    case CV_CALL_FAR_STD    :
    case CV_CALL_ARMCALL    : return CM_CC_STDCALL;
    case CV_CALL_THISCALL   : return CM_CC_THISCALL;
//    case CV_CALL_NEAR_SYS   :
//    case CV_CALL_FAR_SYS    :
//    case CV_CALL_MIPSCALL   :
//    case CV_CALL_ALPHACALL  :
//    case CV_CALL_PPCCALL    :
//    case CV_CALL_SHCALL     :
//    case CV_CALL_ARMCALL    :
//    case CV_CALL_AM33CALL   :
//    case CV_CALL_TRICALL    :
//    case CV_CALL_SH5CALL    :
//    case CV_CALL_M32RCALL   :
  }
  return CM_CC_UNKNOWN;
}

//----------------------------------------------------------------------
uint32 til_builder_t::get_variant_long_value(pdb_sym_t &sym) const
{
  uint32 v = 0;
  VARIANT value;
  VariantInit(&value);
  if ( sym.get_value(&value) == S_OK )
  {
    VARIANT vlong;
    VariantInit(&vlong);
    HRESULT hr = VariantChangeType(&vlong, &value, 0, VT_UI4);
    if ( hr != S_OK )
      hr = VariantChangeType(&vlong, &value, 0, VT_I4);
    if ( hr == S_OK )
      v = vlong.ulVal;
    VariantClear(&vlong);
  }
  VariantClear(&value);
  return v;
}

//----------------------------------------------------------------------
// funcSym is Function, typeSym is FunctionType
bool til_builder_t::is_member_func(pdb_sym_t &typeSym, qtype &owner, qtype *fields, pdb_sym_t *funcSym)
{
  // make sure we retrieve class type first
  pdb_sym_t pParent(*pdb_access);
  if ( typeSym.get_classParent(&pParent) != S_OK || pParent.data == NULL )
    return false;

  bool has_parent = retrieve_type(pParent, NULL, owner, fields, NULL);

  // then check if it's static
  BOOL bIsStatic = false;
  if ( funcSym != NULL
    && pdb_access->get_dia_version() >= 800
    && funcSym->get_isStatic(&bIsStatic) == S_OK )
  {
    return !bIsStatic;
  }
  else
  {
    return has_parent;
  }
}

// ---------------------------------------------------------------------------
bool til_builder_t::is_arm(DWORD machine_type) const
{
  return machine_type == CV_CFL_ARM3
      || machine_type == CV_CFL_ARM4
      || machine_type == CV_CFL_ARM4T
      || machine_type == CV_CFL_ARM5
      || machine_type == CV_CFL_ARM5T
      || machine_type == CV_CFL_ARM6
      || machine_type == CV_CFL_ARM7
      || machine_type == CV_CFL_ARMNT
      || machine_type == CV_CFL_ARM_XMAC
      || machine_type == CV_CFL_ARM_WMMX
      || machine_type == CV_CFL_THUMB;
}

//----------------------------------------------------------------------
bool til_builder_t::is_frame_reg(int reg) const
{
  if ( pdb_access->get_machine_type() == CV_CFL_80386 )
    return reg == CV_REG_EBP;
  else if ( is_arm(pdb_access->get_machine_type()) )
    return reg == CV_ARM_R11 || reg == CV_ARM_SP;
  return false;
}


//----------------------------------------------------------------------
void til_builder_t::enum_function_args(pdb_sym_t &sym, func_type_info_t &args)
{
  // enumerate all function parameters and gather their names
  struct param_enumerator_t : public pdb_access_t::children_visitor_t
  {
    func_type_info_t& args;
    til_builder_t *tb;
    int stack_off;
    virtual HRESULT visit_child(pdb_sym_t &sym)
    {
      DWORD tag = 0;
      HRESULT hr = sym.get_symTag(&tag);
      if ( FAILED(hr) )
        return hr;

      switch ( tag )
      {
        case SymTagBlock: // nested blocks
          return tb->pdb_access->iterate_children(sym, SymTagNull, *this);
        case SymTagFuncDebugStart:
        case SymTagFuncDebugEnd:
          return S_OK;    // ignore these for the moment
      }

      DWORD dwDataKind, locType;
      if ( sym.get_dataKind(&dwDataKind) == S_OK && dwDataKind == DataIsParam
        && sym.get_locationType(&locType) == S_OK )
      {
        funcarg_info_t &fi = args.push_back();
        sym.get_name(&fi.name);
        tb->get_symbol_type(sym, fi.type, &fi.fields, NULL);
        if ( locType == LocIsEnregistered )
        {
          DWORD dwReg;
          if ( sym.get_registerId(&dwReg) == S_OK )
          {
            if ( tb->enregistered_bug && dwReg > 0 )
              dwReg--;
            const char *regname = print_pdb_register(tb->pdb_access->get_machine_type(), dwReg);
            fi.argloc = make_argloc(str2reg(regname), -1);
          }
        }
        else if ( locType == LocIsRegRel )
        {
          DWORD dwReg;
          LONG lOffset;
          if ( sym.get_registerId(&dwReg) == S_OK
            && sym.get_offset(&lOffset) == S_OK
            && tb->is_frame_reg(dwReg) )
          {
            fi.argloc = stack_off;
            size_t align;
            size_t argsz = get_type_size0(tb->ti, fi.type.c_str(), &align);
            if ( align > argsz )
              argsz = align;
            stack_off += argsz;
          }
        }
        else
        {
          ask_for_feedback("pdb: unsupported location type %d", locType);
        }
      }
      return S_OK; // continue enumeration
    }
    param_enumerator_t(func_type_info_t &_args, til_builder_t *_tb): args(_args), tb(_tb), stack_off(0) {}
  };
  param_enumerator_t pen(args, this);
  pdb_access->iterate_children(sym, SymTagData, pen);
}

//----------------------------------------------------------------------
// >0 - member alignment, <0 - gap filler array
int til_builder_t::handle_gap(size_t saved, size_t off) const
{
  // what if we introduce a sdacl byte?
  for ( int align=0; align <= MAX_DECL_ALIGN; align++ )
  {
    size_t copy = saved;
    align_size(copy, 1<<align);
    if ( copy == off )
      return align+1;      // found good member alignment
  }
  return int(saved - off);
}

// ---------------------------------------------------------------------------
// check if the specified alignment yields the same structure size and member
// offsets as in 'mems'. If maymodify is true, try to fix 'mems' by adding
// gap-members
bool til_builder_t::verify_alignment(
        members_t &mems,
        size_t total_size,
        int align,
        bool maymodify,
        intvec_t &fillers) const
{
  size_t cur = 0;
  fillers.qclear();
  uval_t bf_off = BADADDR;
  int maxal = 0;
  for ( members_t::iterator p=mems.begin(); p != mems.end(); ++p )
  {
    int gap = 0;
    meminfo_t &mem = *p;
    uval_t off = mem.offset;
    if ( !mem.is_bf() || bf_off != off )  // skip bitfields
    {
      size_t alsize = 0;
      const type_t *ptr = mem.type.c_str();
      size_t size = get_type_size(ti, ptr, &alsize);
      if ( size == BADSIZE ) // get_type_size() will fails on self-pointers
        size = mem.size;
      if ( alsize == 0 )
        alsize = size;
      if ( maxal < alsize )
        maxal = alsize;
      size_t saved = cur;
      align_size(cur, alsize, align);
      if ( off > cur )
      {
        if ( !maymodify )
          return false; // there is a gap but we may not fill it
        gap = handle_gap(saved, off);
        cur = off;
      }
      if ( off != cur )
        return false;
      cur += size;
    }
    fillers.push_back(gap);
    bf_off = mem.is_bf() ? mem.offset : BADADDR;
  }
  // align up the whole structure
  int gap = 0;
  size_t saved = cur;
  align_size(cur, maxal, align);
  if ( cur < total_size )
  {
    if ( !maymodify )
      return false;
    gap = handle_gap(saved, total_size);
    cur = total_size;
  }
  fillers.push_back(gap);
  return total_size == 0 || cur == total_size;
}

//----------------------------------------------------------------------
bool til_builder_t::create_gap_filler(qtype &itp, qtype *fields, size_t off, int size) const
{
  if ( size > 0 )
  {
    static const type_t t_byte[] = { BT_UNK_BYTE, 0 };
    qtype a2;
    if ( !build_array_type(&a2, t_byte, size) )
      return false;
    itp.append(a2);
    char name[32];
    qsnprintf(name, sizeof(name), "gap%x", (uint32) off-size);
    append_name(fields, name);
  }
  return true;
}

//----------------------------------------------------------------------
// verify unions that would be created out of [p1, p2) members.
// The [p1, p2) members are spoiled by the function.
// Create substructures if necessary. Returns the result in out (can be the same
// vector as [p1, p2)
til_builder_t::cvt_code_t til_builder_t::verify_union(
        members_t::iterator p1,
        members_t::const_iterator p2,
        members_t &out) const
{
  QASSERT(498, p2 > p1);
  int off = p1->offset;
  typedef qvector<members_t> stems_t;
  stems_t stems; // each stem is a member of the future union
  for ( members_t::iterator q=p1; q != p2; ++q )
  {
    members_t *best = NULL;
    q->offset -= off;
    if ( q->offset != 0 )
    { // find best suited stem: the one with end() closest to our offset
      uint64 bestend = uint64(-1);
      for ( stems_t::iterator s=stems.begin(); s != stems.end(); ++s )
      {
        members_t &sm = *s;
        uint64 smend = sm[sm.size()-1].end();
        if ( smend <= q->begin() && (best == NULL || bestend < smend) )
        {
          best = &sm;
          bestend = smend;
        }
      }
    }
    if ( best == NULL )
      best = &stems.push_back();
    qswap(best->push_back(), *q);
  }

  // all non-trivial stems must be converted to structures
  for ( stems_t::iterator s=stems.begin(); s != stems.end(); ++s )
  {
    if ( s->size() == 1 && s->begin()->offset == 0 )
      continue;
    qtype type;
    cvt_code_t code = create_udt_ref(*s, UdtStruct, type);
    if ( code != cvt_ok )
      return code;
    s->resize(1);
    meminfo_t &sm = *s->begin();
    sm.offset = 0;
    sm.size = get_type_size0(ti, type.c_str());
    sm.width = uint64(sm.size) * 8;
    sm.bitnum = 0;
    sm.name.sprnt("__s%d", (int32) (s-stems.begin()));
    sm.type.swap(type);
    sm.fields.clear();
  }

  // collect the results
  out.resize(stems.size());
  for ( int i=0; i < stems.size(); i++ )
  {
    QASSERT(499, stems[i].size() == 1);
    qswap(out[i], *stems[i].begin());
  }
  return cvt_ok;
}

//----------------------------------------------------------------------
// create a union out of [p1, p2) members. they are spoiled by the function.
// returns type of the new union and its fields
// this function also creates substructures if necessary
til_builder_t::cvt_code_t til_builder_t::create_union(members_t::iterator p1, members_t::const_iterator p2, qtype &itp) const
{
  members_t unimems;
  cvt_code_t code = verify_union(p1, p2, unimems);
  if ( code != cvt_ok )
    return code;
  return create_udt_ref(unimems, UdtUnion, itp);
}

//----------------------------------------------------------------------
// Since the kernel does not support bitfields, we have to replace them
// by simple data types
void til_builder_t::remove_bitfields(members_t &mems) const
{
  uval_t bfoff = BADADDR;
  int bfsize = 0;
  for ( members_t::iterator p=mems.begin(); ; )
  {
    if ( p != mems.end() )
    {
      if ( p->is_bf() )
      {
        bfoff = p->offset;
        bfsize = p->size;
        int idx = p - mems.begin();
        mems.erase(p);
        p = mems.begin() + idx;
        continue;
      }
    }
    if ( bfoff != BADADDR )
    {
      // do we have another element of this offset/size?
      bool found = false;
      for ( members_t::iterator q=mems.begin(); q != mems.end(); ++q )
      {
        if ( q->offset == bfoff && q->size == bfsize )
        {
          found = true;
          break;
        }
      }
      if ( !found )
      { // add a member to represent the bitfield
        meminfo_t mi;
        mi.offset = bfoff;
        mi.size = bfsize;
        mi.width = uint64(mi.size) * 8;
        mi.bitnum = 0;
        mi.name.sprnt("_bf%d", (int32) bfoff);
        mi.type.resize(1);
        mi.type[0] =  get_int_type_bit(mi.size);
        int idx = p - mems.begin();
        mems.insert(p, mi);
        p = mems.begin() + idx;
      }
      bfoff = BADADDR;
      continue;
    }
    if ( p == mems.end() )
      break;
    p++;
  }
  // array must stil be sorted
  int n = mems.size();
  QASSERT(500, n>0);
  for ( int i=1; i < n; i++ )
    QASSERT(501, mems[i-1].offset <= mems[i].offset);
}

//----------------------------------------------------------------------
// enumerate virtual functions of class sym and create a vtable structure
// with function pointers
til_builder_t::cvt_code_t til_builder_t::make_vtable_struct(pdb_sym_t &sym, qtype &itp, qtype *fields)
{
  struct virtual_func_visitor_t : public pdb_access_t::children_visitor_t
  {
    til_builder_t *tb;
    members_t &mems;
    qstring &classprefix;
    virtual HRESULT visit_child(pdb_sym_t &sym)
    {
      BOOL b;
      // skip non-virtual functions
      if ( sym.get_virtual(&b) != S_OK || !b )
        return S_OK;
      DWORD offset = -1;
      if ( sym.get_virtualBaseOffset(&offset) != S_OK )
        return S_OK; // skip

      // TODO: add RVA as a comment?
      // ULONGLONG dwRVA = -1;
      // tb->sym.get_virtualAddress(sym, &dwRVA);

      // if this offset was used before, replace the member
      // this often happens when virtual class::~class
      // is later redefined as __vecDelDtor()
      ssize_t memidx = -1;
      for ( size_t i=0; memidx == -1 && i < mems.size(); ++i )
      {
        if ( mems[i].offset == offset )
          memidx = i;
      }

      qstring name;
      sym.get_name(&name);
      // remove 'class_name::'
      if ( !classprefix.empty() )
      {
        size_t pos = name.find(classprefix);
        if ( pos != qstring::npos )
          name.remove(pos, classprefix.length());
      }

      qtype type, fields;
      // the field is a pointer to function
      type.append(BT_PTR);
      if ( tb->retrieve_type(sym, parent, type, &fields, NULL) )
      {
        asize_t size = get_type_size0(tb->ti, type.c_str());
        DWORD64 ulLen = DWORD64(size)*8;
        til_builder_t::meminfo_t &mem = memidx == -1 ? mems.push_back() : mems[memidx];
        mem.bitnum = 0;
        mem.width = ulLen;
        mem.offset = offset;
        mem.type.swap(type);
        mem.fields.swap(fields);
        mem.name.swap(name);
        mem.size = size;
      }
      return S_OK;
    }
    virtual_func_visitor_t(til_builder_t *_tb, members_t &m, qstring &cp) : tb(_tb), mems(m), classprefix(cp) {}
  };

  members_t mems;
  qstring classprefix;
  sym.get_name(&classprefix);
  if ( !classprefix.empty() )
    classprefix+="::";
  virtual_func_visitor_t pp(this, mems, classprefix);
  pdb_access->iterate_children(sym, SymTagFunction, pp);
  if ( mems.empty() )
  {
    return cvt_failed;
  }
  std::sort(mems.begin(), mems.end());
  return create_udt(mems, UdtStruct, 0, itp, fields);
}

//----------------------------------------------------------------------
static const type_t fake_vtable_type[] = "$vt";
til_builder_t::cvt_code_t til_builder_t::convert_udt(pdb_sym_t &sym, DWORD64 size, qtype &itp, qtype *fields)
{
  DWORD udtKind;
  if ( sym.get_udtKind(&udtKind) != S_OK )
    return cvt_failed;

  // retrieve member names, types, offsets
  struct type_name_collector_t : public pdb_access_t::children_visitor_t
  {
    til_builder_t *tb;
    members_t &mems;
    HRESULT visit_child(pdb_sym_t &sym)
    {
      DWORD dwLocType = LocIsNull;
      sym.get_locationType(&dwLocType); // may fail, must ignore

      LONG offset = 0;
      if ( sym.get_offset(&offset) != S_OK )
        return S_OK;

      qtype type, fields;
      asize_t size = tb->get_symbol_type_length(sym);
      if ( tb->retrieve_type(sym, parent, type, &fields, NULL) )
      {
        qstring name;
        DWORD tag = SymTagNull;
        sym.get_symTag(&tag);
        if ( tag == SymTagBaseClass )
        {
          name.sprnt("baseclass_%x", offset);
        }
        else
        {
          sym.get_name(&name);
          if ( tag == SymTagVTable )
          {
            if ( type == fake_vtable_type )
            {
              static const type_t t_pvoid[] = { BT_PTR, BTF_VOID, 0 };
              type = t_pvoid;
            }
            else
            {
              // type is a structure, while the field is a pointer to it
              type.insert(0, BT_PTR);
            }
            if ( name.empty() )
            {
              if ( offset == 0 )
                name = "vfptr";
              else
                name.sprnt("vfptr%x", offset);
            }
            size = get_type_size0(tb->ti, type.c_str());
          }
        }
        DWORD64 ulLen = DWORD64(size)*8;
        DWORD dwBitPos = 0;
        if ( dwLocType == LocIsBitField )
        {
          sym.get_bitPosition(&dwBitPos);
          sym.get_length(&ulLen);
        }
        til_builder_t::meminfo_t &mem = mems.push_back();
        mem.bitnum = dwBitPos;
        mem.width = ulLen;
        mem.offset = offset;
        mem.type.swap(type);
        mem.fields.swap(fields);
        mem.name.swap(name);
        mem.size = size;
      }
      return S_OK;
    }
    type_name_collector_t(til_builder_t *_tb, members_t &m) : tb(_tb), mems(m) {}
  };

  members_t mems;
  type_name_collector_t pp(this, mems);
  pdb_access->iterate_children(sym, SymTagNull, pp);
  if ( mems.empty() && size > 0 ) // add a dummy character array
  {
    static const type_t t_char[] = { BT_INT8|BTMT_CHAR, 0 };
    meminfo_t &m = mems.push_back();
    m.name = "dummy";
    build_array_type(&m.type, t_char, size);
    m.offset = 0;
    m.size = size;
    m.bitnum = 0;
    m.width = 8 * size;
  }
  if ( mems.empty() )
  {
    itp.append(udtKind == UdtUnion ? BTF_UNION : BTF_STRUCT);
    append_dt(&itp, 0);
    static qstring nm;
    nm.qclear();
    get_symbol_name(sym, nm);
    append_name(&itp, nm.c_str());
    return cvt_typedef;
  }

  std::stable_sort(mems.begin(), mems.end());
  remove_bitfields(mems);
  return create_udt(mems, udtKind, size, itp, fields);
}

//----------------------------------------------------------------------
void til_builder_t::append_member_type(qtype *itp, const meminfo_t &mem) const
{
  type_t typebuf[8];
  const type_t *mt;
  if ( mem.is_bf() )
  {
    int bit;
    switch ( mem.size )
    {
      default: ask_for_feedback("Unsupported bitfield size %d", mem.size);
      case 1: bit = BTMT_BFLDI8;  break;
      case 2: bit = BTMT_BFLDI16; break;
      case 4: bit = BTMT_BFLDI32; break;
      case 8: bit = BTMT_BFLDI64; break;
    }
    type_t *p = typebuf;
    *p++ = BT_BITFIELD|bit;
    p = set_dt(p, (mem.width<<1) | (is_type_unsigned(ti, mem.type.c_str()) ? 1 : 0) );
    *p = '\0';
    mt = typebuf;
  }
  else
  {
    mt = mem.type.c_str();
    // can not check it here because not all types are saved to the database
    // some referenced types are being built...
    //int size = get_type_size0(ti, mt);
    //QASSERT(502, size > 0 || size == 0 && is_restype_array(ti, mt));
  }
  itp->append(mt);
}

//----------------------------------------------------------------------
til_builder_t::cvt_code_t til_builder_t::create_udt(members_t &mems, int udtKind, uint64 size, qtype &itp, qtype *fields) const
{
  cvt_code_t code;
  if ( udtKind == UdtUnion )
  {
    code = verify_union(mems.begin(), mems.end(), mems);
    if ( code == cvt_ok )
    {
      itp.append(BTF_UNION);
      append_dt(&itp, mems.size() << 3);
      for ( members_t::iterator p=mems.begin(); p != mems.end(); ++p )
      {
        append_member_type(&itp, *p);
        if ( fields != NULL )
        {
          append_name(fields, p->name.c_str());
          fields->append(p->fields);
        }
      }
    }
    return code;
  }

  // find overlapping members and convert into subunions (anonymous union would be great)
  code = handle_overlapping_members(mems);
  if ( code != cvt_ok )
    return code;

  // verify gaps and add padding of alignment directives if necessary
  bool gaps_ok;
  intvec_t fillers; // >0:alignment:1<<(x-1); <0:gap size; 0-no filler
  int pragma_align = 0;
  for ( bool maymodify=false; ; maymodify=true )
  {
    int defal = (int)get_default_align(0);
    gaps_ok = verify_alignment(mems, (size_t)size, defal, maymodify, fillers);
    if ( !gaps_ok )
    {
      for ( int align=0; align < 7; align++ )
      {
        gaps_ok = verify_alignment(mems, (size_t)size, 1<<align, maymodify, fillers);
        if ( gaps_ok )
        {
          pragma_align = align + 1;
          break;
        }
      }
    }
    if ( gaps_ok || maymodify )
      break;
  }
  if ( !gaps_ok )
    return cvt_failed;

  // count number of filler arrays we will introduce
  int gaps = 0;
  for ( int i=0; i < fillers.size(); i++ )
    if ( fillers[i] < 0 )
      gaps++;

  itp.append(BTF_STRUCT);
  append_dt(&itp, int(((gaps+mems.size()) << 3) | pragma_align));

  // add sdacl byte for the whole structure
  int align = fillers.back();
  if ( align > 0 )
  {
    char sdacl = (char)sdacl_pack(align-1);
    itp.append(sdacl);
  }

  intvec_t::iterator q = fillers.begin();
  for ( members_t::iterator p=mems.begin(); p != mems.end(); ++p,++q )
  {
    meminfo_t &mem = *p;
    uint32 off = mem.offset;
    int align = *q;
    if ( align < 0 )  // we need a gap filler
    {
      if ( !create_gap_filler(itp, fields, off, -align) )
        return cvt_failed;
    }
    append_member_type(&itp, mem);
    if ( align > 0 )  // we need an alignment sdacl byte
    {
      char sdacl = (char)sdacl_pack(align-1);
      itp.append(sdacl);
    }
    if ( fields != NULL )
    {
      append_name(fields, mem.name.c_str());
      fields->append(mem.fields);
    }
  }
  // add final gap filler if necessary
  align = fillers.back();
  if ( align < 0 )
  {
    if ( !create_gap_filler(itp, fields, (size_t)size, -align) )
      return cvt_failed;
  }
  return cvt_ok;
}

//----------------------------------------------------------------------
// is the return type complex?
// if so, a pointer to return value will be passed as a hidden parameter
bool til_builder_t::is_complex_return(pdb_sym_t &sym) const
{
  pdb_sym_t pType(*pdb_access);
  bool complex = false;
  if ( sym.get_type(&pType) == S_OK )
  {
    DWORD tag = 0;
    complex = pType.get_symTag(&tag) == S_OK && tag == SymTagUDT;
    if ( complex )
    {
      ULONGLONG size;
      complex = pType.get_length(&size) == S_OK && size > 8;
    }
    if ( !complex && tag == SymTagUDT )
    {
      // we've got a small UDT which possibly fits into a register (or two)
      // but it has to be a POD for that, i.e. should have no constructor or assignment operators
      BOOL b;
      if ( (pType.get_constructor          (&b) == S_OK) && b
        || (pType.get_hasAssignmentOperator(&b) == S_OK) && b
        || (pType.get_hasCastOperator      (&b) == S_OK) && b )
        complex = true;
    }
  }
  return complex;
}


// ---------------------------------------------------------------------------
bool til_builder_t::is_unnamed_tag_typedef(const type_t *ptr) const
{
  char name[MAXNAMELEN];
  if ( !is_type_resolvable(ptr, name) )
    return false;

  if ( name[0] != '#' )
    return false;

  uint32 id;
  ptr = (const type_t *)name + 1;
  if ( !get_de(ptr, &id) )
    return false;

  return unnamed_types.find(id) != unnamed_types.end();
}


//----------------------------------------------------------------------
til_builder_t::cvt_code_t til_builder_t::really_convert_type(pdb_sym_t &sym, pdb_sym_t *parent, DWORD tag, qtype &itp, qtype *fields)
{
  // retrieve type modifiers
  size_t modidx = itp.length();
  type_t mods = 0;
  BOOL bSet;
  if ( sym.get_constType(&bSet) == S_OK && bSet )
    mods |= BTM_CONST;

  if ( sym.get_volatileType(&bSet) == S_OK && bSet )
    mods |= BTM_VOLATILE;

  DWORD64 size = 0;
  sym.get_length(&size);
  DWORD bt, count;
  cvt_code_t code = cvt_ok;
  switch ( tag )
  {
    default:
    case SymTagNull:
      deb(IDA_DEBUG_PLUGIN, "unsupported tag %s\n", symtag_to_string(tag));
      return cvt_failed;

    case SymTagBaseType:
      if ( sym.get_baseType(&bt) != S_OK )
        return cvt_failed;
      code = append_basetype(bt, int(size), itp);
      if ( code == cvt_failed )
        return cvt_failed;
      break;

    case SymTagPointerType:
      {
        int init = itp.length();
        itp.append(BT_PTR);
        if ( !get_symbol_type(sym, itp, fields, NULL) )
        {
          itp.resize(init);
          return cvt_failed;
        }
      }
      break;

    case SymTagArrayType:
      {
        qtype a2, t2;
        if ( !get_symbol_type(sym, t2, NULL, NULL) )
          return cvt_failed;
        t2[0] |= mods;  // propagate type modifiers to the element type

        if ( sym.get_count(&count) != S_OK )
          return cvt_failed;
        if ( !build_array_type(&a2, t2.c_str(), count) )
          return cvt_failed;
        itp.append(a2);
      }
      break;

    case SymTagFunctionType:
      {
        func_type_info_t fi;
        DWORD cc0;
        fi.cc = CM_CC_UNKNOWN;
        if ( sym.get_callingConvention(&cc0) == S_OK )
          fi.cc = convert_cc(cc0);
        if ( !get_symbol_type(sym, fi.rettype, &fi.retfields, NULL) ) // return type
          return cvt_failed;

        if ( get_cc(fi.cc) != CM_CC_VOIDARG )
        {
          retrieve_children_types_names(sym, fi, parent);
          // last arg unknown/invalid argument => convert to ellipsis
          if ( get_cc(fi.cc) == CM_CC_CDECL && !fi.empty() && fi.back().type.empty() )
          {
            fi.pop_back();
            fi.cc = CM_CC_ELLIPSIS;
          }
          // is there an implicit "result" pointer passed?
          if ( is_complex_return(sym) )
          {
            // complex return type: what's returned is actually a pointer
            fi.rettype.insert(0, BT_PTR);
            funcarg_info_t retarg;
            retarg.type = fi.rettype;
            retarg.fields = fi.retfields;
            retarg.name = "result";
            fi.insert(fi.begin(), retarg);
          }
          // is there an implicit "this" passed?
          // N.B.: 'this' is passed before the implicit result, if both are present
          qtype owner, ofields;
          if ( is_member_func(sym, owner, &ofields, parent) )
          {
            owner.insert(0, BT_PTR);
            funcarg_info_t thisarg;
            thisarg.type = owner;
            thisarg.fields = ofields;
            thisarg.name = "this";
            fi.insert(fi.begin(), thisarg);
          }
          if ( is_user_cc(fi.cc) )
          {
            // specify argloc for the return value
            size_t size = get_type_size0(ti, fi.rettype.c_str());
            if ( size <= 1 )
                fi.retloc = make_argloc(R_al, -1);
            else if ( size <= 4 )
                fi.retloc = make_argloc(R_ax, -1);
            else
                fi.retloc = make_argloc(R_ax, R_dx);
          }
          qtype ftype, ffields;
          if ( build_func_type2(ti, &ftype, &ffields, fi) )
          {
            itp.append(ftype);
            if ( fields != NULL )
              fields->append(ffields);
          }
          else
          {
            return cvt_failed;
          }
        }
      }
      break;

    case SymTagUDT:
    case SymTagBaseClass:
      code = convert_udt(sym, size, itp, fields);
      break;
    case SymTagEnum:
      {
        struct name_value_collector_t : public pdb_access_t::children_visitor_t
        {
          const type_t *idatype;
          qstrvec_t names;
          qvector<uval_t> values;
          HRESULT visit_child(pdb_sym_t &sym)
          {
            qstring &name = names.push_back();
            sym.get_name(&name);
            uint32 value = tb->get_variant_long_value(sym);
            values.push_back(value);
            if ( get_named_type(tb->ti, name.c_str(), NTF_SYMM, &idatype) )
              return E_FAIL;
            return S_OK;
          }
          name_value_collector_t(const til_builder_t *_tb) : tb(_tb) {}
          const til_builder_t *tb;
        };
        name_value_collector_t nvc(this);
        HRESULT hr = pdb_access->iterate_children(sym, SymTagNull, nvc);
        if ( FAILED(hr) )               // symbol already exists?
        {                               // just reuse the existing enum
          itp.append(nvc.idatype);      // this is not totally correct
          const type_t *p = nvc.idatype;// but ok for the current version
          if ( is_type_typedef(*p++) && get_dt(p) > 0 )
          {
            qstring nm;
            get_symbol_name(sym, nm);
            if ( nm == (char*)p )
              return cvt_typedef;         // avoid circular dependencies
          }
          return cvt_ok;
        }
        if ( nvc.values.empty() )
        { // ida does not support enum declarations without any members
          // create a dummy member
          qstring &name = nvc.names.push_back();
          sym.get_name(&name);
          name.append("_dummy");
          nvc.values.push_back(0);
        }
        itp.append(BTF_ENUM);
        append_dt(&itp, (int)nvc.values.size());
        itp.append(BTE_ALWAYS);
        uint32 v = 0;
        for ( int i=0; i < nvc.values.size(); i++ )
        {
          uint32 v2 = (uint32)nvc.values[i];
          append_de(&itp, v2-v);
          v = v2;
          if ( fields != NULL )
            append_name(fields, nvc.names[i].c_str());
        }
      }
      break;

    case SymTagTypedef:
    case SymTagFunctionArgType:
    case SymTagFunction:
    case SymTagData:
      {
        int idx = itp.length();
        if ( !get_symbol_type(sym, itp, fields, NULL) )
          return cvt_failed;
        if ( is_type_typedef(itp[idx]) )
          code = cvt_typedef; // signal that this is a typedef
      }
      break;
    case SymTagVTable:
      {
        int idx = itp.length();
        if ( parent == NULL || make_vtable_struct(*parent, itp, fields) != cvt_ok )
        {
          itp.resize(idx);
          itp.append(fake_vtable_type);
        }
      }
      break;
  }
  if ( mods != 0 )
    itp[modidx] |= mods;
  // todo: check that the type has the expected size
  return code;
}

//----------------------------------------------------------------------
til_builder_t::cvt_code_t til_builder_t::convert_type(pdb_sym_t &sym, pdb_sym_t *parent, DWORD type, DWORD tag, qtype &itp, qtype *fields)
{
  typemap_t::iterator p = typemap.find(type);
  if ( p == typemap.end() )
  {
    tpinfo_t tpi;
    tpi.cvt_code = really_convert_type(sym, parent, tag, tpi.type, &tpi.fields);
//    msg("cvt_type %d code %d: %s\n", type, ti.cvt_code, ti.dstr());
    p = typemap.insert(std::make_pair(type, tpi)).first;
  }
  tpinfo_t &tpi = p->second;
  itp.append(tpi.type);
  if ( fields != NULL )
    fields->append(tpi.fields);
  return tpi.cvt_code;
}

//----------------------------------------------------------------------
uint32 til_builder_t::begin_creation(DWORD tag, const qstring &name)
{
  if ( tag != SymTagFunction )
  {
    creating_t::iterator c = creating.find(name);
    if ( c != creating.end() ) // recursive call
    {
      if ( !c->second )        // allocated?
      {
        c->second = alloc_type_ordinal(ti); // have to create the type id immediately
        QASSERT(490, c->second != 0);
//        msg("%d %s: prematurely mapped to %d\n", type, name.c_str(), c->second);
      }
      return c->second;
    }
    creating.insert(std::make_pair(name, 0)); // add to the 'creating' list
  }
  return 0;
}

// ---------------------------------------------------------------------------
uint32 til_builder_t::end_creation(const qstring &name)
{
  uint32 id = 0;
  creating_t::iterator c = creating.find(name);
  if ( c != creating.end() )
  {
    id = c->second;
    creating.erase(c);
  }
  if ( id == 0 )
  {
    id = alloc_type_ordinal(ti); // have to create the type id immediately
    QASSERT(491, id != 0);
//    msg("%d %s: mapped to %d\n", type, name.c_str(), id);
  }
  return id;
}


// ---------------------------------------------------------------------------
til_builder_t::cvt_code_t til_builder_t::handle_overlapping_members(til_builder_t::members_t &mems) const
{
  qstack<qstring> union_names;
  members_t::iterator end = mems.end();
  members_t::iterator first = end; // !=end => collecting union members
  members_t::iterator last = end;  // member with highest ending offset so far
  for ( members_t::iterator p=mems.begin(); ; ++p )
  {
    if ( p != mems.end() )
    {
      if ( is_unnamed_tag_typedef(p->type.c_str()) )
        handle_unnamed_overlapping_member(mems, union_names, p->name);
      if ( last == end )
      {
        last = p;
        continue;
      }
      if ( last->end() > p->begin() )
      {
        if ( first == end )
          first = last;
        goto NEXT;
      }
    }
    if ( first != end )
    {
      int fidx = first - mems.begin();
      uval_t off = first->offset;
      // range [first, p) is overlapping, create a new type for it
      qtype unimem;
      cvt_code_t code = create_union(first, p, unimem);
      if ( code != cvt_ok )
        return code;
      mems.erase(first+1, p);
      end = mems.end();
      first = end;
      last = end;
      p = mems.begin() + fidx;
      p->offset = off;
      p->size = get_type_size0(ti, unimem.begin());
      p->width = p->size*8;
      p->bitnum = 0;
      if ( union_names.empty() )
        p->name.sprnt("___u%d", fidx);
      else
        p->name = union_names.pop();
      p->type.swap(unimem);
      p->fields.clear();
    }
    if ( p == end )
      break;
NEXT:
    if ( last->end() < p->end() )
      last = p;
  }
  return cvt_ok;
}


// ---------------------------------------------------------------------------
void til_builder_t::handle_function_type(pdb_sym_t &fun_sym, ea_t ea)
{
  struct local_data_creator_t : public pdb_access_t::children_visitor_t
  {
    virtual HRESULT visit_child(pdb_sym_t &sym)
    {
      DWORD tag = 0;
      HRESULT hr = sym.get_symTag(&tag);
      if ( FAILED(hr) )
        return hr;

      switch ( tag )
      {
        case SymTagBlock: // nested blocks
          return tb->pdb_access->iterate_children(sym, SymTagNull, *this);
        case SymTagFuncDebugStart:
        case SymTagFuncDebugEnd:
          return S_OK;    // ignore these for the moment
      }

      DWORD loc_type;
      if ( sym.get_locationType(&loc_type) != S_OK )
        return S_OK; // optimized away?

      return tb->handle_function_type_child(fun_sym, ea, sym, tag, loc_type);
    }
    local_data_creator_t(til_builder_t *_tb, pdb_sym_t &_fun_sym, ea_t _ea) :
      tb(_tb), fun_sym(_fun_sym), ea(_ea) {}
    til_builder_t *tb;
    pdb_sym_t &fun_sym;
    ea_t ea;
  };
  local_data_creator_t ldc(this, fun_sym, ea);
  pdb_access->iterate_children(fun_sym, SymTagNull, ldc);
}


// ---------------------------------------------------------------------------
void til_builder_t::type_created(ea_t /*ea*/, int /*id*/, const char * /*name*/, const type_t * /*ptr*/) const
{
}


// ---------------------------------------------------------------------------
HRESULT til_builder_t::handle_function_type_child(
        pdb_sym_t & /*fun_sym*/,
        ea_t ea,
        pdb_sym_t &child_sym,
        DWORD child_tag,
        DWORD child_loc_type)
{
  switch ( child_loc_type )
  {
    case LocIsConstant:
      break; // we ignore function level constants

    case LocIsStatic:
      handle_symbol(child_sym);
      break;

    case LocIsEnregistered:
    case LocIsRegRel:
      break;

    default:
      ask_for_feedback("pdb: unsupported location type %d, tag %d at %a", child_loc_type, child_tag, ea);
      break;
  }
  return S_OK;
}


// ---------------------------------------------------------------------------
til_builder_t::cvt_code_t til_builder_t::create_udt_ref(til_builder_t::members_t &mems, int udt_kind, qtype &itp) const
{
  qtype type, fields;
  cvt_code_t code = create_udt(mems, udt_kind, 0, type, &fields);
  if ( code != cvt_ok )
    return code;

  char name[MAXNAMELEN];
  build_anon_type_name(name, sizeof(name), type.begin(), fields.begin());
  uint32 id = get_type_ordinal(ti, name);
  if ( id == 0 )
  {
    id = alloc_type_ordinal(ti);
    if ( !set_numbered_type(ti, id, NTF_NOBASE, name, type.begin(), fields.begin()) )
      return cvt_failed;
    type_created(BADADDR, id, NULL, type.begin());
  }

  char buf[32];
  create_numbered_type_name(id, buf, sizeof(buf));
  itp.append(BTF_TYPEDEF);
  append_name(&itp, buf);
  return cvt_ok;
}


// ---------------------------------------------------------------------------
bool til_builder_t::retrieve_type(pdb_sym_t &sym, pdb_sym_t *parent, qtype &itp, qtype *fields, int *p_id)
{
  if ( p_id != NULL )
    *p_id = -1;

  // id -> unknown typedef?
  DWORD sym_id = 0;
  sym.get_symIndexId(&sym_id);
  tpdefs_t::iterator q = tpdefs.find(sym_id);
  if ( q != tpdefs.end() )
  {
    itp.append(BTF_TYPEDEF);
    append_name(&itp, q->second.c_str());
    return true;
  }

  uint32 id = idmap[sym_id];
  if ( id == 0 )
  {
    DWORD tag;
    HRESULT hr = sym.get_symTag(&tag);
    if ( FAILED(hr) )
      return false;

    qstring ns;
    bool is_unnamed = get_symbol_name(sym, ns);
    // msg("ID: 0x%x -> %s\n", (uint32) sym, ns.c_str());
    if ( tag == SymTagVTable && ns.empty() )
    {
      if ( parent != NULL )
        get_symbol_name(*parent, ns);

      if ( ns.empty() )
        ns.sprnt("vtable-%d", unnamed_idx++);
      else
        ns.append("Vtbl");

      is_unnamed = false;
    }

    // udt fields and simple types are converted without allocating
    // an ordinal number
    if ( tag == SymTagData || ns.empty() )
      return convert_type(sym, parent, sym_id, tag, itp, fields) != cvt_failed;

    // give a unique name to unnamed types so they can be told apart
    // this is a temporary name, it will be replaced by $hex..
    if ( is_unnamed )
      ns.sprnt("unnamed-%d", unnamed_idx++);

    // some types can be defined multiple times. check if the name is already defined
    id = get_type_ordinal(ti, ns.c_str());
    if ( id == 0 )
    {
      id = begin_creation(tag, ns);
      if ( id == 0 )
      {
        // now convert the type information, recursive types won't bomb
        qtype t2, f2;
        cvt_code_t cc = convert_type(sym, parent, sym_id, tag, t2, &f2);
        if ( cc != cvt_ok ) // failed or typedef
        {
          creating.erase(ns);
          if ( cc == cvt_failed )
            return false;
          // cvt_typedef
          tpdefs[sym_id] = ns; // reference to unknown typedef
RETT2:
          itp.append(t2);
          if ( fields != NULL )
            fields->append(f2);
          return true;
        }

        // Function types are saved as symbols
        if ( tag == SymTagFunction )
        {
          // the following may fail because of c++ overloaded functions
          // do not check the error code - we can not help it
          set_named_type(ti, ns.c_str(), NTF_SYMM, t2.begin(), f2.begin());
          type_created(BADADDR, 0, ns.c_str(), t2.begin());
          goto RETT2;
        }

        if ( is_unnamed ) // this type will be referenced, so create a name for it
        {
          ns.resize(MAXNAMELEN-1);
          build_anon_type_name(&ns[0], ns.size(), t2.begin(), f2.begin());
          id = get_type_ordinal(ti, ns.c_str());
          if ( id != 0 ) // this type already exists, just reuse it
            creating.erase(ns);
        }
        if ( id == 0 )
        {
          id = end_creation(ns);
          if ( !set_numbered_type(ti, id, NTF_NOBASE,
                                  ns.empty() ? NULL : ns.c_str(),
                                  t2.begin(),
                                  f2.begin()) )
          {
            return 0;
          }
          if ( is_unnamed )
            unnamed_types.insert(id);
          type_created(BADADDR, id, NULL, t2.begin());
        }
      }
    }
  }
  if ( p_id != NULL )
    *p_id = id;
  char buf[32];
  create_numbered_type_name(id, buf, sizeof(buf));
  itp.append(BTF_TYPEDEF);
  append_name(&itp, buf);
  return true;
}


// ---------------------------------------------------------------------------
bool til_builder_t::handle_symbol_at_ea(pdb_sym_t &/*sym*/, DWORD /*tag*/, DWORD /*id*/, ea_t /*ea*/, qstring & /*name*/)
{
  return true;
}


// ---------------------------------------------------------------------------
bool til_builder_t::handle_symbol(pdb_sym_t &sym)
{
  DWORD id;
  HRESULT hr = sym.get_symIndexId(&id);
  if ( FAILED(hr) )
    return hr;

  if ( handled.find(id) != handled.end() )
    return S_OK;
  handled.insert(id);

  DWORD tag;
  hr = sym.get_symTag(&tag);
  if ( FAILED(hr) )
    return hr;

  switch ( tag )
  {
    case SymTagNull:
    case SymTagExe:
    case SymTagCompiland:
    case SymTagCompilandEnv:
    case SymTagAnnotation:
    case SymTagCustom:
    case SymTagCustomType:
    case SymTagManagedType:
    case SymTagUDT:
    case SymTagEnum:
    case SymTagFunctionType:
    case SymTagPointerType:
    case SymTagArrayType:
    case SymTagBaseType:
    case SymTagTypedef:
    case SymTagBaseClass:
    case SymTagFunctionArgType:
    case SymTagUsingNamespace:
    case SymTagVTableShape:
    case SymTagDimension:
      return S_OK;
    case SymTagCompilandDetails:
      {
        DWORD backEndVer;
        if ( pdb_access->get_machine_type() == CV_CFL_80386 && sym.get_backEndMajor(&backEndVer) == S_OK )
          enregistered_bug = backEndVer <= 13;
      }
      return S_OK;
    default:
      break;
  }

  DWORD off = 0;
  hr = sym.get_relativeVirtualAddress(&off);
  if ( hr == S_OK )
  {
    ea_t ea = get_load_address() + off;
    if ( ea != 0 )
    {
      qstring name;
      sym.get_name(&name);
      handle_symbol_at_ea(sym, tag, id, ea, name);
    }
  }
  return S_OK;
}


//----------------------------------------------------------------------
struct symbol_handler_t : public pdb_access_t::children_visitor_t
{
  virtual HRESULT visit_child(pdb_sym_t &sym)
  {
    bool rc = tb->handle_symbol(sym);
    return rc ? S_OK : S_FALSE;
  }
  symbol_handler_t(til_builder_t *_tb) : tb(_tb) {}
  til_builder_t *tb;
};

HRESULT til_builder_t::handle_symbols(pdb_sym_t &global_sym)
{
  symbol_handler_t cp(this);
  return pdb_access->iterate_subtags(global_sym, SymTagNull, cp);
}

HRESULT til_builder_t::handle_publics(pdb_sym_t &global_sym)
{
  symbol_handler_t cp(this);
  return pdb_access->iterate_children(global_sym, SymTagPublicSymbol, cp);
}


//----------------------------------------------------------------------
HRESULT til_builder_t::handle_types(pdb_sym_t &global_sym)
{
  struct type_importer_t : public pdb_access_t::children_visitor_t
  {
    til_builder_t *tb;
    int counter;
    virtual HRESULT visit_child(pdb_sym_t &sym)
    {
      qtype type, fields;
      if ( tb->retrieve_type(sym, parent, type, &fields, NULL) )
        counter++;
      return S_OK;
    }
    type_importer_t(til_builder_t *_tb) : tb(_tb), counter(0) {}
  };
  type_importer_t timp(this);
  HRESULT hr = pdb_access->iterate_children(global_sym, SymTagEnum, timp);
  if ( SUCCEEDED(hr) )
    hr = pdb_access->iterate_children(global_sym, SymTagUDT, timp);
  if ( SUCCEEDED(hr) )
    hr = pdb_access->iterate_children(global_sym, SymTagTypedef, timp);
  msg("PDB: loaded %d type%s\n", timp.counter, timp.counter!=1 ? "s" : "");
  return hr;
}


// ---------------------------------------------------------------------------
int til_builder_t::before_iterating()
{
  return 0;
}


// ---------------------------------------------------------------------------
int til_builder_t::after_iterating()
{
  return 0;
}


// ---------------------------------------------------------------------------
HRESULT til_builder_t::build(pdb_sym_t &global_sym)
{
  int rc = before_iterating();
  if ( rc != 0 )
    return rc;

  HRESULT hr = handle_types(global_sym);
  if ( SUCCEEDED(hr) )
    hr = handle_symbols(global_sym);
  if ( SUCCEEDED(hr) )
    hr = handle_publics(global_sym);

  return hr;
}
