#include <idc.idc>
//Microsoft C++ RTTI support for IDA
//Version 3.0 2006.01.20 Igor Skochinsky <skochinsky@mail.ru>

//#define DEBUG

//////////////////////////////////////
// Unknown(long ea, long length)
//////////////////////////////////////
// Mark the ea as unknown for a length
// of length, but don't propagate.
static Unknown( ea, length )
{
  auto i;
  if (ea==BADADDR)
    return;
//  Message("Unknown(%x,%d)\n",ea, length);
  for(i=0; i < length; i++)
     {
       MakeUnkn(ea+i,0);
     }
}

static ForceQword( x ) {  //Make dword, undefine as needed
  if (x==BADADDR || x==0)
    return;
 if (!MakeQword( x ))
 {
   Unknown(x,8);
   MakeQword(x);
 }
}

static ForceDword( x ) {  //Make dword, undefine as needed
 if (x==BADADDR || x==0)
   return;
 if (!MakeDword( x ))
 {
   Unknown(x,4);
   MakeDword(x);
 }
}

static ForceWord( x ) {  //Make word, undefine as needed
  if (x==BADADDR || x==0)
    return;
 if (!MakeWord( x ))
 {
   Unknown(x,2);
   MakeWord( x );
 }
}

static ForceByte( x ) {  //Make byte, undefine as needed
  if (x==BADADDR || x==0)
    return;
 if (!MakeByte( x ))
 {
   MakeUnkn(x,0);
   MakeByte( x );
 }
}

static SoftOff ( x ) { //Make offset if !=0
  if (x==BADADDR || x==0)
    return;
 ForceDword(x);
 if (Dword(x)>0 && Dword(x)<=MaxEA()) OpOff(x,0,0);
}

static GetAsciizStr(x)
{
  auto s,c;
  if (x==BADADDR || x==0)
    return "";
  s = "";
  while (c=Byte(x))
  {
    s = form("%s%c",s,c);
    x = x+1;
  }
  return s;
}

//check if Dword(vtbl-4) points to typeinfo record and extract the type name from it
static GetTypeName(vtbl)
{
  auto x, s, c;
  if (vtbl==BADADDR)
    return;
  x = Dword(vtbl-4);
  if ((!x) || (x==BADADDR)) return "";
//  if (Dword(x)||Dword(x+4)||Dword(x+8)) return "";
  x = Dword(x+12);
  if ((!x) || (x==BADADDR)) return "";
  s = "";
  x = x+8;
  while (c=Byte(x))
  {
    s = form("%s%c",s,c);
    x = x+1;
  }
  return s;
}

static DwordCmt(x, cmt)
{
  if (x==BADADDR || x==0)
    return;
  ForceDword(x);
  MakeComm(x, cmt);
}
static OffCmt(x, cmt)
{
  if (x==BADADDR || x==0)
    return;
  SoftOff(x);
  MakeComm(x, cmt);
}
static StrCmt(x, cmt)
{
  auto save_str;
  if (x==BADADDR || x==0)
    return;
  MakeUnkn(x, 0);
  save_str = GetLongPrm(INF_STRTYPE);
  SetLongPrm(INF_STRTYPE,0);
  MakeStr(x, BADADDR);
  MakeName(x, "");
  MakeComm(x, cmt);
  SetLongPrm(INF_STRTYPE,save_str);
}
static DwordArrayCmt(x, n, cmt)
{
  if (x==BADADDR || x==0)
    return;
  Unknown(x,4*n);
  ForceDword(x);
  MakeArray(x,n);
  MakeComm(x, cmt);
}

//check if values match a pattern
static matchBytes(addr,match)
{
  auto i,len,s;
  len = strlen(match);
  if (len%2) 
  { 
    Warning("Bad match string in matchBytes: %s",match);
    return 0;
  }
  i=0;
  while (i<len)
  {
    s = substr(match,i,i+2);
    if (s!="??" && form("%02X",Byte(addr))!=s)
      return 0;//mismatch
    i = i+2;
    addr++;
  }
  return 1;
}

static ForceDWMember(id, offset, name)
{
  if (0!=AddStrucMember(id, name,offset, FF_DWRD, -1, 4))
    SetMemberName(id, offset, name);
}

static ForceStrucMember(id, offset, sub_id, name)
{
  auto a,i;
  i = GetStrucSize(sub_id);
  if (0!=AddStrucMember(id,name,offset,FF_DATA|FF_STRU,sub_id,i))
  {
    for (a=offset;a<offset+i;a++)
      DelStrucMember(id,a);
    AddStrucMember(id,name,offset,FF_DATA|FF_STRU,sub_id,i);
    //SetMemberName(id, offset, name);
  }
}

//add (or rename) a stack variable named name at frame offset offset (i.e. bp-based)
//struc_id = structure variable
//if struc_id == -1, then add a dword
static CommentStack(start, offset, name, struc_id)
{
  auto id,l,bp;
  id = GetFrame(start);
  l = GetFrameLvarSize(start);
  if ( (GetFunctionFlags(start) & FUNC_FRAME) == 0)
    l = l + GetFrameRegsSize(start);
  l = l+offset;
  //Message("%a: ebp offset = %02Xh\n",start,l);
  if (l<0)
  {    
    //Message("growing the frame to locals=%d, regs=4, args=%d.\n",-offset, GetFrameArgsSize(start));
    //we need to grow the locals
    MakeFrame(start, -offset, GetFrameRegsSize(start), GetFrameArgsSize(start));
    l = 0;
  }
  if (struc_id==-1)
    ForceDWMember(id, l, name);
  else
    ForceStrucMember(id, l, struc_id, name);
}

static getRelJmpTarget(a)
{
  auto b;
  b = Byte(a);
  if (b == 0xEB)
  {
    b = Byte(a+1);
    if (b&0x80)
      return a+2-((~b&0xFF)+1);
    else
      return a+2+b;
  }
  else if (b==0xE9)
  {
    b = Dword(a+1);
    if (b&0x80000000)
      return a+5-(~b+1);
    else
      return a+5+b;
  }
  else
    return BADADDR;
}

static getRelCallTarget(a)
{
  auto b;
  b = Byte(a);
  if (b==0xE8)
  {
    b = Dword(a+1);
    if (b&0x80000000)
      return a+5-(~b+1);
    else
      return a+5+b;
  }
  else
    return BADADDR;
}

static MangleNumber(x)
{
//
// 0 = A@
// X = X-1 (1<=X<=10)
// -X = ?(X-1)
// 0x0..0xF = 'A'..'P'

  auto s, sign;
  s=""; sign=0;
  if (x<0)
  {
    sign = 1;
    x = -x;
  }  
  if (x==0)
    return "A@";
  else if (x<=10)
    return form("%s%d",sign?"?":"",x-1);
  else
  {
    while (x>0)
    {
      s = form("%c%s",'A'+x%16,s);
      x = x / 16;
    }
    return sign?"?":""+s+"@";
  }
}
static Parse_BCD(x, indent)
{
  auto indent_str,i,a,s;
  if (x==BADADDR || x==0)
    return;
  indent_str="";i=0;
  while(i<indent)
  {
    indent_str=indent_str+"    ";
    i++;
  }
/*
struct _s_RTTIBaseClassDescriptor
{
    struct TypeDescriptor* pTypeDescriptor; //type descriptor of the class
    DWORD numContainedBases; //number of nested classes following in the array
    struct PMD where;        //some displacement info
    DWORD attributes;        //usually 0, sometimes 10h
    struct _s_RTTIClassHierarchyDescriptor *pClassHierarchyDescriptor; //of this base class
};

struct PMD
{
    int mdisp;  //member displacement
    int pdisp;  //vbtable displacement
    int vdisp;  //displacement inside vbtable
};
*/
#ifdef DEBUG
  Message(indent_str+"0x%08.8X: RTTIBaseClassDescriptor\n", x);
  Message(indent_str+"    pTypeDescriptor:   %08.8Xh (%s)\n", Dword(x), GetAsciizStr(Dword(x)+8));
  Message(indent_str+"    numContainedBases: %08.8Xh\n", Dword(x+4));
  Message(indent_str+"    PMD where:         (%d,%d,%d)\n", Dword(x+8), Dword(x+12), Dword(x+16));
  Message(indent_str+"    attributes:        %08.8Xh\n", Dword(x+20));
#endif

  OffCmt(x, "pTypeDescriptor");
  DwordCmt(x+4, "numContainedBases");
  DwordArrayCmt(x+8, 3, "PMD where");
  DwordCmt(x+20, "attributes");
  OffCmt(x+24, "pClassHierarchyDescriptor");

  if(substr(Name(Dword(x+24)),0,5) != "??_R3")
  {
    // assign dummy name to prevent infinite recursion
    MakeName(Dword(x+24),"??_R3"+form("%06x",x)+"@@8");
    // a proper name will be assigned shortly
    Parse_CHD(Dword(x+24),indent-1);
  }

  s = Parse_TD(Dword(x), indent+1);
  //??_R1A@?0A@A@B@@8 = B::`RTTI Base Class Descriptor at (0,-1,0,0)'
  MakeName(x,"??_R1"+MangleNumber(Dword(x+8))+MangleNumber(Dword(x+12))+
           MangleNumber(Dword(x+16))+MangleNumber(Dword(x+20))+substr(s,4,-1)+'8');
  return s;
}

static GetClassName(p)
{
  /*auto s;
  s = GetAsciizStr(Dword(p)+8);
  Message("demangling %s\n",s);
  return DemangleTIName(s);*/
  auto s,s2;
  s = "??_7"+GetAsciizStr(Dword(p)+12)+"6B@";
  //Message("demangling %s\n",s);
  s2 = Demangle(s,8);
  if (s2!=0)
    //CObject::`vftable'
    return substr(s2,0,strlen(s2)-11);
  else
    return s;
}

static DumpNestedClass(x, indent, contained)
{
  auto indent_str,i,a,n,p,s,off;
  indent_str="";i=0;
  while(i<indent)
  {
    indent_str=indent_str+"    ";
    i++;
  }
  i=0;
  //indent=indent+1;
  a = x;
  while(i<contained)
  {
    p = Dword(a);
    off = Dword(p+8);
    s = form("%.4X: ",off);
    Message("%s%s%s\n", s, indent_str, GetClassName(p));
    //fprintf(f, form("%s%s%s\n",s,indent_str,GetClassName(p)));
    n = Dword(p+4);
    if (n>0) //check numContainedBases
      DumpNestedClass(a+4, indent+1, n); //nested classes following
    a=a+4*(n+1);
    i=i+n+1;
  }
}

static Parse_CHD(x, indent)
{
  auto indent_str,i,a,n,p,s;
  if (x==BADADDR || x==0)
    return;
  indent_str="";i=0;
  while(i<indent)
  {
    indent_str=indent_str+"    ";
    i++;
  }
  //Message(indent_str+"0x%08.8X: RTTIClassHierarchyDescriptor\n", x);
  /*
  struct _s_RTTIClassHierarchyDescriptor
  {
      DWORD signature;      //always zero?
      DWORD attributes;     //bit 0 = multiple inheritance, bit 1 = virtual inheritance
      DWORD numBaseClasses; //number of classes in pBaseClassArray
      struct _s_RTTIBaseClassArray* pBaseClassArray;
  };
  */
  a = Dword(x+4);
  if ((a&3)==1)
    p = "(MI)";
  else if ((a&3)==2)
    p = "(VI)";
  else if ((a&3)==3)
    p = "(MI VI)";
  else
    p="(SI)";

#ifdef DEBUG
  Message(indent_str+"    signature:         %08.8Xh\n", Dword(x));
  Message(indent_str+"    attributes:        %08.8Xh %s\n", a, p);
  Message(indent_str+"    numBaseClasses:    %08.8Xh\n", n);
  Message(indent_str+"    pBaseClassArray:   %08.8Xh\n", a);
#endif

  DwordCmt(x, "signature");
  DwordCmt(x+4, "attributes");
  DwordCmt(x+8, "numBaseClasses");
  OffCmt(x+12, "pBaseClassArray");

  a=Dword(x+12);
  n=Dword(x+8);
  i=0;
  DumpNestedClass(a, indent, n);
  indent=indent+1;
  while(i<=n)
  {
    p = Dword(a);
    if (i==n && p!=0)
      break;
    //Message(indent_str+"    BaseClass[%02d]:  %08.8Xh\n", i, p);
    OffCmt(a, form("BaseClass[%02d]", i));
    if (i==0)
    {
      s = Parse_BCD(p,indent);
      //??_R2A@@8 = A::`RTTI Base Class Array'
      MakeName(a,"??_R2"+substr(s,4,-1)+'8');
      //??_R3A@@8 = A::`RTTI Class Hierarchy Descriptor'
      MakeName(x,"??_R3"+substr(s,4,-1)+'8');
    }
    else
      Parse_BCD(p,indent);
    i=i+1;
    a=a+4;
  }
  return s;
}

static Parse_TD(x, indent)
{
  auto indent_str,i,a;
  if (x==BADADDR || x==0)
    return;
  indent_str="";i=0;
  while(i<indent)
  {
    indent_str=indent_str+"    ";
    i++;
  }
  //Message(indent_str+"0x%08.8X: TypeDescriptor\n", x);
/*
struct TypeDescriptor
{
    void* pVFTable;  //always pointer to type_info::vftable ?
    void* spare;     //seems to be zero for most classes, and default constructor for exceptions
    char name[0];    //mangled name, starting with .?A (.?AV=classes, .?AU=structs)
};
*/
  a = GetAsciizStr(x+8);
#ifdef DEBUG
  Message(indent_str+"    pVFTable:          %08.8Xh\n", Dword(x));
  Message(indent_str+"    spare:             %08.8Xh\n", Dword(x+4));
  Message(indent_str+"    name:              '%s'\n", a);
#endif

  OffCmt(x, "pVFTable");
  OffCmt(x+4, "spare");
  StrCmt(x+8, "name");

  //??_R0?AVA@@@8 = A `RTTI Type Descriptor'
  MakeName(x,"??_R0"+substr(a,1,-1)+"@8");
  return a;
}


static Parse_COL(x, indent)
{
/*
struct _s_RTTICompleteObjectLocator
{
    DWORD signature; //always zero ?
    DWORD offset;    //offset of this vtable in the class ?
    DWORD cdOffset;  //no idea
    struct TypeDescriptor* pTypeDescriptor; //TypeDescriptor of the class
    struct _s_RTTIClassHierarchyDescriptor* pClassDescriptor; //inheritance hierarchy
};*/
  auto indent_str,i,a,s;
  if (x==BADADDR || x==0)
    return;
  indent_str="";i=0;
  while(i<indent)
  {
    indent_str=indent_str+"    ";
    i++;
  }
  s = GetAsciizStr(Dword(x+12)+8);
  //Message(indent_str+"0x%08.8X: RTTICompleteObjectLocator\n", x);
#ifdef DEBUG
  Message(indent_str+"    signature:         %08.8Xh\n", Dword(x));
  Message(indent_str+"    offset:            %08.8Xh\n", Dword(x+4));
  Message(indent_str+"    cdOffset:          %08.8Xh\n", Dword(x+8));
  Message(indent_str+"    pTypeDescriptor:   %08.8Xh (%s)\n", Dword(x+12), DemangleTIName(s));
  Message(indent_str+"    pClassDescriptor:  %08.8Xh\n", Dword(x+16));
#endif

  DwordCmt(x, "signature");
  DwordCmt(x+4, "offset");
  DwordCmt(x+8, "cdOffset");
  OffCmt(x+12, "pTypeDescriptor");
  OffCmt(x+16, "pClassDescriptor");

  //
  Parse_CHD(Dword(x+16),indent+1);
}

static Parse_CT(x, indent)
{
  auto indent_str,i,a,s;
  if (x==BADADDR || x==0)
    return;
  indent_str="";i=0;
  while(i<indent)
  {
    indent_str=indent_str+"    ";
    i++;
  }
/*
typedef const struct _s__CatchableType {
  unsigned int properties;
  _TypeDescriptor *pType;
  _PMD thisDisplacement;
  int sizeOrOffset;
  _PMFN copyFunction;
} _CatchableType;

struct PMD
{
    int mdisp;  //members displacement ???
    int pdisp;  //
    int vdisp;  //vtable displacement ???
};
*/

  s = GetAsciizStr(Dword(x+4)+8);

#ifdef DEBUG
  Message(indent_str+"0x%08.8X: CatchableType\n", x);
  Message(indent_str+"    properties:        %08.8Xh\n", Dword(x));
  Message(indent_str+"    pType:             %08.8Xh (%s)\n", Dword(x+4), DemangleTIName(s));
  Message(indent_str+"    thisDisplacement:  (%d,%d,%d)\n", Dword(x+8), Dword(x+12), Dword(x+16));
  Message(indent_str+"    sizeOrOffset:      %08.8Xh\n", Dword(x+20));
  Message(indent_str+"    copyFunction:      %08.8Xh\n", Dword(x+24));
#endif

  a = "properties";
  i = Dword(x);
  if (i!=0) a = a+":";
  if (i&1) a = a+" simple type";
  if (i&2) a = a+" byref only";
  if (i&4) a = a+" has vbases";

  DwordCmt(x, a);
  OffCmt(x+4, "pType");
  DwordArrayCmt(x+8, 3, "thisDisplacement");
  DwordCmt(x+20, "sizeOrOffset");
  OffCmt(x+24, "copyFunction");
  ForceDword(x+28);

  //__CT??_R0 ?AVCTest@@ @81 = CTest::`catchable type'
  MakeName(x,"__CT??_R0?"+substr(s,1,-1)+"@81");

  if (Dword(x+24)) //we have a copy constructor
  //.?AVexception@@ -> ??0exception@@QAE@ABV0@@Z = exception::exception(exception const &)
     MakeName(Dword(x+24),"??0"+substr(s,4,-1)+"QAE@ABV0@@Z");
  return s;
}

static Parse_CTA(x, indent)
{
/*
typedef const struct _s__CatchableTypeArray {
  int nCatchableTypes;
  _CatchableType *arrayOfCatchableTypes[];
} _CatchableTypeArray;
*/  

  auto indent_str,i,a,n,p,s;
  if (x==BADADDR || x==0)
    return;
  indent_str="";i=0;
  while(i<indent)
  {
    indent_str=indent_str+"    ";
    i++;
  }

#ifdef DEBUG
  Message(indent_str+"    nCatchableTypes:         %08.8Xh\n", Dword(x));
  Message(indent_str+"    arrayOfCatchableTypes:   %08.8Xh\n", Dword(x+4));
#endif
  DwordCmt(x, "nCatchableTypes");
  //OffCmt(x+4, "arrayOfCatchableTypes");

  a=x+4;
  n=Dword(x);
  i=0;
  indent=indent+1;
  while(i<n)
  {
    p = Dword(a);
    //Message(indent_str+"    BaseClass[%02d]:  %08.8Xh\n", i, p);
    OffCmt(a, form("CatchableType[%02d]", i));
    if (i==0)
    {
      s = Parse_CT(p,indent);
      //__CTA1 ?AVCTest@@ = CTest::`catchable type array'
      MakeName(x,"__CTA1?"+substr(s,1,-1));
    }
    else
      Parse_CT(p,indent);
    i=i+1;
    a=a+4;
  }
  return s;
}

//demangle names like .?AVxxx, .PAD, .H etc
static DemangleTIName(s)
{
  auto i;
  if (substr(s,0,1)!=".")
    return "";
  s = Demangle("??_R0"+substr(s,1,-1)+"@8",8);
  i = strstr(s,"`RTTI Type Descriptor'");
  if (i==-1)
    return "";
  else
  {
    s = substr(s,0,i-1);
    //Message("throw %s;\n",s);
    return s;
  }
}

static Parse_ThrowInfo(x, indent)
{
/*
typedef const struct _s__ThrowInfo {
  unsigned int attributes;
  _PMFN pmfnUnwind;
  int (__cdecl*pForwardCompat)(...);
  _CatchableTypeArray *pCatchableTypeArray;
} _ThrowInfo;
*/
  auto indent_str,i,a,s;
  if (x==BADADDR || x==0)
    return;
  indent_str="";i=0;
  while(i<indent)
  {
    indent_str=indent_str+"    ";
    i++;
  }
#ifdef DEBUG
  Message(indent_str+"0x%08.8X: ThrowInfo\n", x);
  Message(indent_str+"    attributes:            %08.8Xh\n", Dword(x));
  Message(indent_str+"    pmfnUnwind:            %08.8Xh\n", Dword(x+4));
  Message(indent_str+"    pForwardCompat:        %08.8Xh\n", Dword(x+8));
  Message(indent_str+"    pCatchableTypeArray:   %08.8Xh\n", Dword(x+12));
#endif

  a = "attributes";
  i = Dword(x);
  if (i!=0) a = a+":";
  if (i&1) a = a+" const";
  if (i&2) a = a+" volatile";

  DwordCmt(x, a);
  OffCmt(x+4, "pmfnUnwind");
  OffCmt(x+8, "pForwardCompat");
  OffCmt(x+12, "pCatchableTypeArray");
  s = Parse_CTA(Dword(x+12), indent+1);
  if (s!="")
  {
    MakeName(x,"__TI1?"+substr(s,1,-1));
    if (Dword(x+4)) //we have a destructor
      //.?AVexception@@ -> ??1exception@@UAE@XZ = exception::~exception(void)
      MakeName(Dword(x+4),"??1"+substr(s,4,-1)+"UAE@XZ");
    i = Dword(x); //attributes
    a = DemangleTIName(s);
    if (i&1) a = "const "+a;
    if (i&2) a = "volatile "+a;
    a = "throw "+a;
    Message("%s\n",a);
    MakeRptCmt(x, a);
  }
  return s;
}

static Parse_TryBlock(x, indent)
{
/*
typedef const struct _s_TryBlockMapEntry {
  int tryLow;                                  //00
  int tryHigh;                                 //04
  int catchHigh;                               //08
  int nCatches;                                //0C
  const struct _s_HandlerType * pHandlerArray; //10
} TryBlockMapEntry;

typedef const struct _s_HandlerType {
  unsigned int adjectives;          //00
  struct TypeDescriptor * pType;    //04
  int dispCatchObj;                 //08
  void * addressOfHandler;          //0C
}
*/  
  auto indent_str,i,a,n,p,s;
  if (x==BADADDR || x==0)
    return;
  indent_str="";i=0;
  while(i<indent)
  {
    indent_str=indent_str+"    ";
    i++;
  }

#ifdef DEBUG
  Message(indent_str+"    tryLow:          %d\n", Dword(x));
  Message(indent_str+"    tryHigh:         %d\n", Dword(x+4));
  Message(indent_str+"    catchHigh:       %d\n", Dword(x+8));
  Message(indent_str+"    nCatches:        %d\n", Dword(x+12));
  Message(indent_str+"    pHandlerArray:   %08.8Xh\n", Dword(x+16));
#endif

  DwordCmt(x, "tryLow");
  DwordCmt(x+4, "tryHigh");
  DwordCmt(x+8, "catchHigh");
  DwordCmt(x+12, "nCatches");
  OffCmt(x+16, "pHandlerArray");

  a=Dword(x+16);
  n=Dword(x+12);
  if (a==BADADDR || a==0 || n==0)
    return;
  i=0;
  indent=indent+1;
  while(i<n)
  {
#ifdef DEBUG
    Message(indent_str+"        adjectives:       %08.8Xh\n", Dword(a));
    Message(indent_str+"        pType:            %08.8Xh\n", Dword(a+4));
    Message(indent_str+"        dispCatchObj:     %08.8Xh\n", Dword(a+8));
    Message(indent_str+"        addressOfHandler: %08.8Xh\n", Dword(a+12));
#endif

    DwordCmt(a, "adjectives");
    OffCmt(a+4,"pType");
    DwordCmt(a+8, "dispCatchObj");
    OffCmt(a+12,"addressOfHandler");

    p = Dword(a+4);
    if (p)
    {
      s = DemangleTIName(Parse_TD(p, indent+1));
      if (Dword(a)&8) //reference
        s = s+"&";
      if (Dword(a)&2) //volatile
        s = "volatile "+s;
      if (Dword(a)&1) //const
        s = "const "+s;
      s = s+" e";
    }
    else
      s = "...";

    p = Dword(a+12);
    if (p!=0 && p!=BADADDR)
    {
      ExtLinA(Dword(a+12),0,form("; catch (%s)",s));
      ExtLinA(Dword(a+12),1,form("; states %d..%d",Dword(x),Dword(x+4)));
      p = Dword(a+8);
      if (p)
      {
        if (p&0x80000000)
          s = form("; e = [epb-%Xh]",-p);
        else
          s = form("; e = [epb+%Xh]",p);
        ExtLinA(Dword(a+12),2,s);
      }      
    }
    i=i+1;
    a=a+16;
  }
  return s;
}

static Parse_FuncInfo(x, indent)
{
/*
typedef const struct _s_FuncInfo {
  unsigned int magicNumber:29;                      //0
  unsigned int bbtFlags:3;
  int maxState;                                     //4
  const struct _s_UnwindMapEntry * pUnwindMap;      //8
  unsigned int nTryBlocks;                          //C
  const struct _s_TryBlockMapEntry * pTryBlockMap;  //10
  unsigned int nIPMapEntries;                       //14
  void * pIPtoStateMap;                             //18
  const struct _s_ESTypeList * pESTypeList;         //1C
  int EHFlags; //present only in vc8?               //20
} FuncInfo;
typedef const struct _s_UnwindMapEntry {
  int toState;          //0
  function  * action;   //4
} UnwindMapEntry;
*/  
  auto indent_str,i,a,s,n;
  if (x==BADADDR || x==0)
    return;
  if ((Dword(x)^0x19930520)>0xF) {
    Message("Magic is not 1993052Xh!\n");
    return;
  }
  indent_str="";i=0;
  while(i<indent)
  {
    indent_str=indent_str+"    ";
    i++;
  }
#ifdef DEBUG
  Message(indent_str+"0x%08.8X: FuncInfo\n", x);
  Message(indent_str+"    magicNumber:            %08.8Xh\n", Dword(x));
  Message(indent_str+"    maxState:               %d\n", Dword(x+4));
  Message(indent_str+"    pUnwindMap:             %08.8Xh\n", Dword(x+8));
#endif
  n = Dword(x+4);
  i = 0; a = Dword(x+8);
  while (i<n)
  {
#ifdef DEBUG
    Message(indent_str+"        toState:              %d\n", Dword(a));
    Message(indent_str+"        action:               %08.8Xh\n", Dword(a+4));
#endif
    DwordCmt(a, "toState");
    OffCmt(a+4, "action");
    a = a+8;
    i = i+1;
  }
#ifdef DEBUG
  Message(indent_str+"    nTryBlocks:             %d\n", Dword(x+12));
  Message(indent_str+"    pTryBlockMap:           %08.8Xh\n", Dword(x+16));
#endif
  n = Dword(x+12);
  i = 0; a = Dword(x+16);
  while (i<n)
  {
    Parse_TryBlock(a, indent+1);
    a = a+20;
    i = i+1;
  }
#ifdef DEBUG
  Message(indent_str+"    nIPMapEntries:          %d\n", Dword(x+20));
  Message(indent_str+"    pIPtoStateMap:          %08.8Xh\n", Dword(x+24));
#endif

  DwordCmt(x, "magicNumber");
  DwordCmt(x+4, "maxState");
  OffCmt(x+8, "pUnwindMap");
  //Parse_UnwindMap(Dword(x+8), indent+1);
  DwordCmt(x+12, "nTryBlocks");
  OffCmt(x+16, "pTryBlockMap");
  DwordCmt(x+20, "nIPMapEntries");
  OffCmt(x+24, "pIPtoStateMap");
  if ((Dword(x+8)-x)>=32 || Dword(x)>0x19930520)
  {
#ifdef DEBUG
    Message(indent_str+"    pESTypeList:            %08.8Xh\n", Dword(x+28));
#endif
    OffCmt(x+28, "pESTypeList");
  }
  if ((Dword(x+8)-x)>=36 || Dword(x)>0x19930521)
  {
#ifdef DEBUG
    Message(indent_str+"    EHFlags:                %08.8Xh\n", Dword(x+32));
#endif
    OffCmt(x+32, "EHFlags");
  }
  return s;
}

//get class name for this vtable instance
static GetVtableClass(x)
{
  auto offset, n, i, s, a, p;
  offset = Dword(x+4);
  x = Dword(x+16); //Class Hierarchy Descriptor

  a=Dword(x+12); //pBaseClassArray
  n=Dword(x+8);  //numBaseClasses
  i = 0;
  s = "";
  while(i<n)
  {
    p = Dword(a);
    //Message(indent_str+"    BaseClass[%02d]:  %08.8Xh\n", i, p);
    if (Dword(p+8)==offset)
    {
      //found it
      s = GetAsciizStr(Dword(p)+8);
      return s;
    }
    i=i+1;
    a=a+4;
  }
  //didn't find matching one, let's get the first vbase
  i=0;
  a=Dword(x+12);
  while(i<n)
  {
    p = Dword(a);
    if (Dword(p+12)!=-1)
    {
      s = GetAsciizStr(Dword(p)+8);
      return s;
    }
    i=i+1;
    a=a+4;
  }
  return s;
}

static Parse_Vtable(a)
{
  auto i,s,s2;
  s=GetTypeName(a);
  if (substr(s,0,3)==".?A")
  {
#ifdef DEBUG
    Message("RTTICompleteObjectLocator:      %08.8Xh\n", Dword(a-4));
#endif
    Parse_COL(Dword(a-4),0);
    Unknown(a-4,4);
    SoftOff(a-4);
    i = Dword(a-4);  //COL
    s2 = Dword(i+4); //offset
    i = Dword(i+16); //CHD
    i = Dword(i+4);  //Attributes
    if ((i&3)==0  && s2==0)
    { //Single inheritance, so we don't need to worry about duplicate names (several vtables)
      s=substr(s,4,-1);
      MakeName(a,"??_7"+s+"6B@");
      MakeName(Dword(a-4),"??_R4"+s+"6B@");
    }
    else// if ((i&3)==1)
    { 
      //Message("Multiple inheritance\n");
      s2 = GetVtableClass(Dword(a-4));
      s2 = substr(s2,4,-1);
      s  = substr(s,4,-1);
      s = s+"6B"+s2+"@";
      MakeName(a,"??_7"+s);
      MakeName(Dword(a-4),"??_R4"+s);
    }
  }
}

static ParseVtbl()
{
  Parse_Vtable(ScreenEA());
}
static ParseExc()
{
  Parse_ThrowInfo(ScreenEA(), 0);
}

static ParseFI()
{
  Parse_FuncInfo(ScreenEA(), 0);
}

static AddHotkeys()
{
  AddHotkey("Alt-F7","ParseFI");
  AddHotkey("Alt-F8","ParseVtbl");
  AddHotkey("Alt-F9","ParseExc");
  Message("Use Alt-F7 to parse FuncInfo\n");
  Message("Use Alt-F8 to parse vtable\n");
  Message("Use Alt-F9 to parse throw info\n");
}
#ifndef __INCLUDED
static main(void)
{
  AddHotkeys();
}
#endif