REhints/HexRaysCodeXplorer

View on GitHub
src/HexRaysCodeXplorer/ReconstructableType.cpp

Summary

Maintainability
Test Coverage
/*    Copyright (c) 2013-2020
REhints <info@rehints.com>
All rights reserved.

==============================================================================

This file is part of HexRaysCodeXplorer

HexRaysCodeXplorer is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

==============================================================================
*/


#include "ReconstructableType.h"
#include "Common.h"
#include "Debug.h"
#include "Utility.h"
#include "reconstructed_place_t.h"

#define BAD_RETYPE_ID -1

SyncTypeInfoMethod syncTypeInfoMethod = SyncTypeInfo_Full;

std::map<std::string, ReconstructableType*> g_ReconstractedTypes;

bool inside_hook = false;
std::set<ReconstructableType*> g_ChangedTypes;
bool in_type_changing = false;

struct reconstructed_types_info_t
{
    TWidget *widget;
    TWidget *cv;
    TWidget *codeview;
    strvec_t sv;
    reconstructed_types_info_t(TWidget *f) : widget(f), cv(nullptr), codeview(nullptr) {}
};

ReconstructableMember::ReconstructableMember()
{

}

ReconstructableMember::ReconstructableMember(const ReconstructableMember & other) :
    name(other.name),
    offset(other.offset),
    memberType(other.memberType->clone())
{

}

ReconstructableMember::~ReconstructableMember()
{

}

ReconstructableType::ReconstructableType(const std::string &Name) : 
    name(Name), typeId(BAD_RETYPE_ID), maxSize((unsigned long)-1), id(0)
{
}

ReconstructableType::~ReconstructableType()
{
}

unsigned ReconstructableType::getSize()
{
    std::map<unsigned int, ReconstructableMember*>::reverse_iterator last;
    /* Any container should copy its members to ownMembers. */
    unsigned long result = 0;
    if (derivedMembers.size()) {
        result = derivedMembers.rbegin()->second->offset + derivedMembers.rbegin()->second->getSize();
    }
    
    if (ownMembers.size()) {
        last = ownMembers.rbegin();
        if (last->second->offset + last->second->getSize() > result)
            return last->second->offset + last->second->getSize();
    }
    return result;
}

ReconstructableMember* ReconstructableType::findMemberByOffset(unsigned long offset, bool isDerived)
{
    std::map<unsigned int, ReconstructableMember*> *members;
    if (isDerived)
        members = &derivedMembers;
    else
        members = &ownMembers;

    if (members->count(offset))
        return (*members)[offset];
    std::map<unsigned int, ReconstructableMember*>::iterator it = members->lower_bound(offset);
    if (it == members->end())
        return 0;

    if (offset >= it->second->offset && offset < it->second->offset + it->second->getSize()) 
        return it->second;

    return 0;
}

bool ReconstructableType::SetMemberName(unsigned long offset, const char * newName)
{
    ReconstructableMember *member = 0;

    bool cur = in_type_changing;

    if (in_type_changing) {
        if (g_ChangedTypes.count(this))
            return false;
    }
    else {
        in_type_changing = true;
        g_ChangedTypes.clear();
    }
    g_ChangedTypes.emplace(this);

    if (member = findMemberByOffset(offset, false))
    {
        member->name = newName;
        for (std::set<ReconstructableType*>::iterator it = childrenTypes.begin(); it != childrenTypes.end(); ++it) {
            
            (*it)->SetMemberNameUpcast(offset, this, newName);
        }
        // if (cur) // do sync only if we do upcast change
            SyncTypeInfo();
        in_type_changing = cur;
        return true;
    }

    in_type_changing = cur;
    return false;
}

bool ReconstructableType::SetMemberNameUpcast(unsigned long offset, ReconstructableType * base, const char * newName)
{
    assert(in_type_changing);
    if (g_ChangedTypes.count(this))
        return false;

    std::map<unsigned int, ReconstructableMember*>::iterator it = derivedMembers.begin();
    while (it != derivedMembers.end()) {
        assert(it->second->memberType->getKind() == MemberType_Reconsrtucted);
        ReconstructedMemberReType * mType = (ReconstructedMemberReType*)it->second->memberType;
        if (mType->reType == base) {
            offset += it->second->offset;
            SetMemberName(offset, newName);
            return true;
        }
        ++it;
    }
    // not found?
    return false;
}

/*

tinfo_t tinfo;
if (tinfo.get_named_type(get_idati(), "GCC_RTTI::type_info")) {
apply_tinfo(ea, tinfo, TINFO_DEFINITE);
}

void ReconstructableType::UndefMembers(unsigned long startOffset, unsigned long size, ReconstructableMemberChange modificator)
{
    assert(modificator == ReconstructableMemberChange_None);
    for (unsigned long offset = startOffset; offset < startOffset + size; ++offset)
    {
        ReconstructableMember *tmpMember = findMemberByOffset(offset, false);
        if (tmpMember) {
            ownMembers.erase(offset);
            delete tmpMember;
        }
    }

}
*/

void ReconstructableType::AddSubType(ReconstructableType * subType)
{
    childrenTypes.emplace(subType);
}

bool ReconstructableType::AddDerivedMember(ReconstructableMember* member)
{
    // sanity checks
    if (!member)
        return false;
    if (member->memberType->getKind() != MemberType_Reconsrtucted)
        return false;
    ReconstructedMemberReType *type = (ReconstructedMemberReType*)member->memberType;
    if (!type->reType)
        return false;
    ReconstructableType * parentType = type->reType;

    parentTypes.emplace(parentType);

    derivedMembers[member->offset] = member;
    parentType->CopyMembersToOther(this, member->offset, member->name);
    parentType->AddSubType(this);
}

void ReconstructableType::CopyMembersToOther(ReconstructableType *other, unsigned long offset, std::string &namePrefix)
{
    for (std::map<unsigned int, ReconstructableMember*>::iterator it = ownMembers.begin(); it != ownMembers.end(); ++it)
    {
        ReconstructableMember *newMember = new ReconstructableMember(*(it->second));

        newMember->offset += offset;
        newMember->name = namePrefix + newMember->name;
        other->AddMember(newMember);
        // assert();

    }
}

void ReconstructableType::UndefMembers(unsigned long startOffset, unsigned long size, bool ownMember)
{
    unsigned long offset = startOffset;
    while (offset < size + startOffset) {
        ReconstructableMember* member = findMemberByOffset(offset, false);
        if (!member) {
            offset++;
            continue;
        }
        delete member->memberType;
        ownMembers.erase(offset);
        delete member;

    }
}

bool ReconstructableType::SetMemberType(unsigned long offset, ReconstructedMemberType* newType)
{

    bool cur = in_type_changing;

    if (in_type_changing) {
        if (g_ChangedTypes.count(this))
            return false;
    }
    else {
        in_type_changing = true;
        g_ChangedTypes.clear();
    }
    g_ChangedTypes.emplace(this);

    ReconstructableMember* member = findMemberByOffset(offset, false);
    if (!member) {
        delete newType;
        in_type_changing = cur;
        return false;
    }
    member->memberType = newType;

    for (std::set<ReconstructableType*>::iterator it = childrenTypes.begin(); it != childrenTypes.end(); ++it) {
        newType = newType->clone();
        (*it)->SetMemberTypeUpcast(offset, this, newType);
    }
    // if (cur)
        SyncTypeInfo();
    in_type_changing = cur;
    return true;
}

bool ReconstructableType::SetMemberTypeUpcast(unsigned long offset, ReconstructableType * base, ReconstructedMemberType * newType)
{
    assert(in_type_changing);
    if (g_ChangedTypes.count(this))
        return false;

    std::map<unsigned int, ReconstructableMember*>::iterator it = derivedMembers.begin();
    while (it != derivedMembers.end()) {
        assert(it->second->memberType->getKind() == MemberType_Reconsrtucted);
        ReconstructedMemberReType * mType = (ReconstructedMemberReType*)it->second->memberType;
        if (mType->reType == base) {
            offset += it->second->offset;
            SetMemberType(offset, newType->clone());
            return true;
        }

        ++it;
    }
    // not found?
    delete newType;
    return false;
}
/*
bool ReconstructableType::SetMemberType(unsigned long offset, tinfo_t * info)
{
    ReconstructedMemberType *newMemberType = 0;
    ReconstructableMember* member = findMemberByOffset(offset, false);
    if (!member)
        return false;
    qstring type_string;
    if (!info->print(&type_string)) {
        // failed to print.
        return false;
    }
    if (g_ReconstractedTypes.count(type_string.c_str())) 
        newMemberType = new ReconstructedMemberReType(g_ReconstractedTypes[type_string.c_str()]);
    else 
        newMemberType = new MemberTypeIDATypeInfoGate(*info);
    
    return SetMemberType(offset, newMemberType);
}
*/
bool ReconstructableType::AddMember( ReconstructableMember* member) 
{
    ReconstructableMember *upcastMember = 0;
    ReconstructableMember *newMember = 0;

    bool cur = in_type_changing;

    if (in_type_changing) {
        if (g_ChangedTypes.count(this))
            return false;
    }
    else {
        in_type_changing = true;
        g_ChangedTypes.clear();
    }
    g_ChangedTypes.emplace(this);
    
    if (!findMemberByOffset(member->offset, false))
    {
        ownMembers[member->offset] = member;
        for (std::set<ReconstructableType*>::iterator it = childrenTypes.begin(); it != childrenTypes.end(); ++it) {
            newMember = new ReconstructableMember(*member);
            (*it)->AddMemberUpcast(newMember, this);
        }
        //if (cur) // if we go with children
            SyncTypeInfo();
    }
    
    in_type_changing = cur;
    return true;
}

bool ReconstructableType::AddMemberUpcast(ReconstructableMember * member, ReconstructableType * base)
{
    assert(in_type_changing);
    if (g_ChangedTypes.count(this))
        return false;

    std::map<unsigned int, ReconstructableMember*>::iterator it = derivedMembers.begin();
    while (it != derivedMembers.end()) {
        assert(it->second->memberType->getKind() == MemberType_Reconsrtucted);
        ReconstructedMemberReType * mType = (ReconstructedMemberReType*)it->second->memberType;
        if (mType->reType == base) {
            member->offset += it->second->offset;
            AddMember(member);
            return true;
        }


        ++it;
    }
    // not found?
    delete member;
    return false;
}

void ReconstructableType::SyncTypeInfo()
{
    struc_t* struc = 0; 
    bool cur = inside_hook;
    inside_hook = true;

    if (this->typeId != BAD_RETYPE_ID) {
        struc = get_struc(this->typeId);
        if (struc) {
            if (name != get_struc_name(this->typeId).c_str()) {
                // we hade id of other struc I guess
                struc = 0;
            }
        }
    }

    if (struc == 0) {
        tid_t id = get_struc_id(name.c_str());
        if (id == BADADDR)
            id = add_struc(BADADDR, name.c_str());
        this->typeId = id;
        struc = get_struc(id);
        if (!struc)
        {
            inside_hook = cur;
            return;
        }
    }

    if (syncTypeInfoMethod == SyncTypeInfo_Names)
        return;

    /* lets do it in a stupid way 
    if (struc->memqty)
    {
        member_t *tmpArray = new member_t[struc->memqty];
        memcpy(tmpArray, struc->members, sizeof(member_t) *struc->memqty);
        for (unsigned int i = 0; i < struc->memqty; ++i)
        {
            if (!del_struc_member(struc, tmpArray[i].soff))
                msg("failde to delete member of %s with index %d\n", name.c_str(), i);
        }
        delete[] tmpArray;
    }
    */
      for (std::map<unsigned int, ReconstructableMember*>::iterator it = ownMembers.begin(); it != ownMembers.end(); ++it)
    {

        opinfo_t info;
        ReconstructableMember * member = it->second;
        std::string typeString = member->memberType->getTypeString();
        const char * typeName = typeString.c_str();
        info.tid = get_struc_id(typeName);

        //flags_t flag = it->second->memberType->get_idaapi_flags();
        tinfo_t mt;
        it->second->memberType->get_idaapi_tinfo(&mt);

        struc_error_t error;
        flags_t flags = 0;
        switch (it->second->getSize()) {
        case 1:
            flags = byte_flag();
            break;
        case 2:
            flags = word_flag();
            break;
        case 4:
            flags = dword_flag();
            break;
        case 8:
            flags = qword_flag();
            break;
        default:
            break;
        }

        error = add_struc_member(struc, it->second->name.c_str(), it->second->offset, flags, 0, it->second->getSize());
        if (error != STRUC_ERROR_MEMBER_OK) {
            msg("Failed to add field %s::%s  cause %d\n", this->name.c_str(), it->second->name.c_str(), error);
        }
        member_t * mptr = get_member(struc, it->second->offset);
        if (!mptr) {
            msg("failed to get member  for  %s::%s . this IDA is so dead lol \n", this->name.c_str(), it->second->name.c_str());
            continue;
        }

        smt_code_t smt_code = set_member_tinfo(struc, mptr, it->second->offset, mt, 0     );
        if (smt_code != SMT_OK) {
            msg("failed to set type for  %s::%s cause %d\n", this->name.c_str(), it->second->name.c_str(), smt_code);
        }
        /*
         smt_code_t ida_export set_member_tinfo    (    struc_t *     sptr,
            member_t *     mptr,
            uval_t     memoff,
            const tinfo_t &     tif,
            int     flags 
            )    
        
        */
    }
    inside_hook = cur;
}

const std::set<ReconstructableType*> & ReconstructableType::getParents()
{
    return parentTypes;
}

const std::set<ReconstructableType*> & ReconstructableType::getChildren()
{
    return childrenTypes;
}

const std::map<unsigned int, ReconstructableMember*> & ReconstructableType::getDerivedMembers() {
    return derivedMembers;
}

const std::map<unsigned int, ReconstructableMember *> & ReconstructableType::getOwnMembers() {
    return ownMembers;
}

void ReconstructableType::SetMaxSize(unsigned long newSize)
{
    if (getSize() > newSize) {
        assert(false); // need to undef all who hier.
    }
    if (maxSize > newSize)
        maxSize = newSize;
}

ReconstructableType * ReconstructableType::getReconstructableType(const std::string & Name)
{
    ReconstructableType *reType = 0;
    if (g_ReconstractedTypes.count(Name))
        return g_ReconstractedTypes[Name];
    reType = new ReconstructableType(Name);
    g_ReconstractedTypes[Name] = reType;
    return reType;
}

// ------------------------------------------------ window handlers

static bool idaapi reconstructed_keydown(TWidget *cv, int vk_key, int shift, void *ud) {
    int x, y;
    qstring name;
    place_t *place = get_custom_viewer_place(cv, true, &x, &y);
    if (place == 0)
        return false;
    reconstructed_place_t *replace = (reconstructed_place_t *)place;
    ReconstructableType *reType = replace->getReType();
    if (reType == 0)
        return false;
    msg("current position: typename %s, index %d, pos %d, atOwnMembers %d offset %x\n", replace->typeName.c_str(),
        replace->index, replace->position, replace->atOwnMembers, replace->own_offset);
    switch (vk_key) {

    case 'S':
        /* Sync current type */
        reType->SyncTypeInfo();
        break;
    case 'N':
        /* Set name of struct member */
        if (replace->position != REPLACE_MEMBERS)
            break;
        
        if (ask_str(&name, HIST_IDENT, "Set member name")) {
            reType->SetMemberName(replace->own_offset, name.c_str());
        }
        refresh_custom_viewer(cv);
        break;
    

    case 'D':
        /* Add member with type to the end of struct */
        if (replace->position < REPLACE_MEMBERS)
            break;
        if (replace->own_offset == reType->getSize()) {

            if (ask_str(&name, HIST_TYPE, "Set member type")) {
                tinfo_t tinfo;
                std::string sname = name.c_str();
                if (!ends_with(sname, ";")) {
                    sname += ";";
                }
                if (!parse_decl(&tinfo, &name, (til_t *)get_idati(), sname.c_str(), PT_TYP)) {
                    msg("Failed to parse declaration %s \n", sname.c_str());
                    break;
                }
                ReconstructableMember *member = new ReconstructableMember();
                name.sprnt("field_%x", replace->own_offset);
                member->name = name.c_str();
                member->offset = replace->own_offset;
                member->memberType = new MemberTypeIDATypeInfoGate(tinfo);
                reType->AddMember(member);
                reType->SyncTypeInfo();

                place_t *plce = replace->makeplace((void *)replace->own_offset, reType->typeId, reType->typeId);
                return jumpto(cv, plce, 0, 0);
            }
            refresh_custom_viewer(cv); // this one doesn't seems to work?
            break;
        }
        break;

    case 'Y':
        if (replace->position != REPLACE_MEMBERS)
            break;

        if (ask_str(&name, HIST_TYPE, "Set member type")) {
            ReconstructedMemberType * newType;
            std::string sname = name.c_str();
            if (g_ReconstractedTypes.count(sname)) {
                newType  = new ReconstructedMemberReType(g_ReconstractedTypes[sname]);
            }
            else {
                tinfo_t tinfo;
                if (!ends_with(sname, ";")) {
                    sname += ";";
                }
                if (!parse_decl(&tinfo, &name, (til_t *)get_idati(), sname.c_str(), PT_TYP)) {
                    msg("Failed to parse declaration %s \n", sname.c_str());
                    break;
                }
                newType = new MemberTypeIDATypeInfoGate(tinfo);
            }
             
            reType->SetMemberType(replace->own_offset, newType);
            // force to sync typeinfo since it will not do it.
            // Yes, I know, bad code.
            reType->SyncTypeInfo();
        }
        refresh_custom_viewer(cv);
        break;

    case 'G':
        if (ask_str(&name, /*HIST_IDENT*/ 0, "Where to jump")) {
            std::string sname = name.c_str();
            if (g_ReconstractedTypes.count(sname)) {
                place_t *plce = replace->makeplace(0, g_ReconstractedTypes[sname]->typeId, g_ReconstractedTypes[sname]->typeId);
                return jumpto(cv, plce, 0, 0);
            }
            else {
                int offset = strtol(sname.c_str(), 0, 16);
                if (reType->getSize() >= offset) {
                    place_t *plce = replace->makeplace((void *)offset, reType->typeId, reType->typeId);
                    return jumpto(cv, plce, 0, 0);
                }
                msg("bad offset : %s\n", sname.c_str());
            }
        }


    }

    return true;
}

/// The user right clicked. See ::ui_populating_widget_popup, too.
static void idaapi reconstructed_popup(TWidget *cv, void *ud) {
    return;
}

static bool idaapi reconstructed_types_lines_linenum(TWidget *cv, const place_t *p, uval_t *num, void *ud) {
    reconstructed_place_t *replace = (reconstructed_place_t *)p;
    if (replace->position == REPLACE_CLASSNAME_TOP) {
        if (g_ReconstractedTypes.count(replace->typeName) == 0)
            return false;

        // for now i dont like how it works.
        ReconstructableType *reType = g_ReconstractedTypes[replace->typeName];
        *num = reType->id;
        return true;
    }

    return false;
}

static bool idaapi reconstructed_types_dblclick(TWidget *v, int shift, void *ud)
{
    int x, y;
    const auto place = get_custom_viewer_place(v, true, &x, &y);
    if (!place)
        return false;
    auto *replace = dynamic_cast<reconstructed_place_t*>(place);
    const auto ea = replace->toea();
    if (ea != BADADDR)
        return jumpto(ea);
    //return true;
    auto re_type = replace->getReType();
    ReconstructableTypeVtable *re_vtable = nullptr;
    if (re_type == nullptr)
        return false;

    auto members = re_type->getOwnMembers();
    auto derived_members = re_type->getDerivedMembers();
    switch (replace->position) {

    case REPLACE_SPLIT:
    case REPLACE_BLANK:
    case REPLACE_CLASSNAME_TOP:
    case REPLACE_BLANK_BOT:
    case REPLACE_CLASSNAME_BOT:
        if (re_vtable = dynamic_cast<ReconstructableTypeVtable*>(re_type)) {
            return re_vtable->vtable_address;
        }
        break;
    case REPLACE_PARENTS:
    case REPLACE_CHILDREN:
        break; // TODO
    case REPLACE_MEMBERS:
        if (replace->atOwnMembers == false) {
            const auto type_name = derived_members[replace->own_offset]->memberType->getTypeString();
            if (g_ReconstractedTypes.count(type_name)) {
                const auto plce = replace->makeplace(nullptr, g_ReconstractedTypes[type_name]->typeId, g_ReconstractedTypes[type_name]->typeId);
                return jumpto(v, plce, 0,0);
            }
        }
    }
    return false;
}

static void idaapi reconstructed_adjust_place(TWidget *v, lochist_entry_t *loc, void *ud) {
    auto *re_place = dynamic_cast<reconstructed_place_t*>(loc->plce);
    int x, y;
    const auto place = get_custom_viewer_place(v, true, &x, &y);
    auto *replace = dynamic_cast<reconstructed_place_t*>(place);
}

ssize_t hook_idb_events(void *user_data, int notification_code, va_list va) {
    ssize_t result = 0;
    ReconstructableType *re_type;
    tid_t tid{};
    const char *oldname;
    const char *newname;
    struc_t *struc;
    ea_t ea;
    adiff_t diff;
    member_t *member;
    flags_t flags;
    const opinfo_t *info;
    asize_t size;
    tinfo_t tinfo;
    qstring q_string;
    ReconstructableMember *reMember;
    ReconstructedMemberType * reMemberType;
    func_t * func;
    std::map<unsigned int, ReconstructableMember *> members;
    if (inside_hook)
        /// just pass it as is
        return 0;

    inside_hook = true;
    switch (notification_code) {
    case idb_event::struc_created:
        tid = va_arg(va, tid_t);
        struc = get_struc(tid);
        if (!struc)
            // should not happend.
        {
            result = BADADDR;
            break;
        }
        oldname = get_struc_name(tid).c_str();
        // probably we not interested on it
        if (g_ReconstractedTypes.count(oldname) == 0)
        {
            result = 0;
            break;
        }
        g_ReconstractedTypes[oldname]->typeId = tid;
        break;
    case idb_event::deleting_struc:
        struc = va_arg(va, struc_t*);
        oldname = get_struc_name(struc->id).c_str();
        if (g_ReconstractedTypes.count(oldname) == 0)
        {
            result = 0;
            break;
        }
        g_ReconstractedTypes[oldname]->typeId = BADADDR;
        break;
    case idb_event::renaming_struc:
        tid = va_arg(va, tid_t);
        oldname = va_arg(va, const char *);
        newname = va_arg(va, const char *);
        if (g_ReconstractedTypes.count(oldname) == 0)
        {
            result = 0;
            break;
        }
        // deny renaming.
        result = BADADDR;
        break;
    case idb_event::expanding_struc:
        struc = va_arg(va, struc_t*);
        ea = va_arg(va, ea_t);
        diff = va_arg(va, adiff_t);
        oldname = get_struc_name(tid).c_str();
        if (g_ReconstractedTypes.count(oldname) == 0)
            break;
        
        re_type = g_ReconstractedTypes[oldname];
        if (re_type->maxSize < ea + diff)
            // it will corrupt what we have
        {
            result = BADADDR;
            break;
        }
        
        break;
    case idb_event::struc_member_created:
        struc = va_arg(va, struc_t*);
        member = va_arg(va, member_t*);
        q_string = get_struc_name(struc->id);
        oldname = q_string.c_str();
        if (g_ReconstractedTypes.count(oldname) == 0)
            break;
        re_type = g_ReconstractedTypes[oldname];
    
        if (re_type->maxSize < member->eoff)
            // it will corrupt what we have
        {
            result = BADADDR;
            break;
        }
        if (!get_member_tinfo(&tinfo, member))
        {
            if (!get_or_guess_member_tinfo(&tinfo, member)) {
                opinfo_t opinfo{};
                if (retrieve_member_info(&opinfo, member)) {
                    result = BADADDR;
                    break;
                }
                result = BADADDR;
                break;
            }
        }
        reMember = new ReconstructableMember();
        //get_member_name(member->id)
        get_member_name(&q_string, member->id);
        reMember->name = q_string.c_str();
        reMember->offset = member->soff;

        if (tinfo.get_type_name(&q_string)) {

            if (tinfo.is_ptr()) {
                if (g_ReconstractedTypes.count(q_string.c_str()) != 0)
                {
                    reMemberType = new MemberTypePointer(g_ReconstractedTypes[q_string.c_str()]->name);

                }
                else {
                    reMemberType = new MemberTypeIDATypeInfoGate(tinfo);
                }

                reMember->memberType = reMemberType;
                re_type->AddMember(reMember);
                break;
            }
            if (g_ReconstractedTypes.count(q_string.c_str()) != 0) {
                reMemberType = new ReconstructedMemberReType(g_ReconstractedTypes[q_string.c_str()]);
                reMember->memberType = reMemberType;
                re_type->AddMember(reMember);
                break;
            }

        }
        reMemberType = new MemberTypeIDATypeInfoGate(tinfo);
        reMember->memberType = reMemberType;
        re_type->AddMember(reMember);

        break;

    case idb_event::deleting_struc_member:
        struc = va_arg(va, struc_t*);
        member = va_arg(va, member_t*);
        q_string = get_struc_name(struc->id);
        oldname = q_string.c_str();
        if (g_ReconstractedTypes.count(oldname) == 0)
            break;
        re_type = g_ReconstractedTypes[oldname];
        re_type->UndefMembers(member->soff, member->eoff - member->soff, true);
        break;

    case idb_event::renaming_struc_member:
        struc = va_arg(va, struc_t*);
        member = va_arg(va, member_t*);
        newname = va_arg(va, const char *);
        q_string = get_struc_name(struc->id);
        oldname = q_string.c_str();
        if (g_ReconstractedTypes.count(oldname) == 0)
            break;
        re_type = g_ReconstractedTypes[oldname];
        re_type->SetMemberName(member->soff, newname);
        break;

    case idb_event::changing_struc_member:
        ///< \param sptr    (::struc_t *)
        ///< \param mptr    (::member_t *)
        ///< \param flag    (::flags_t)
        ///< \param ti      (const ::opinfo_t *)
        ///< \param nbytes  (::asize_t)
        struc = va_arg(va, struc_t*);
        member = va_arg(va, member_t*);
        flags = va_arg(va, flags_t);
        info = va_arg(va, const opinfo_t *);
        size = va_arg(va, asize_t);
        //msg("Setting member %s.%s to type %s\n", reType->name.c_str(), reMember->name.c_str(), q_string.c_str());
        
        break;
    case idb_event::struc_member_changed:
        struc = va_arg(va, struc_t*);
        member = va_arg(va, member_t*);
        q_string = get_struc_name(struc->id);
        oldname = q_string.c_str();
        if (g_ReconstractedTypes.count(oldname) == 0)
            break;
        re_type = g_ReconstractedTypes[oldname];

        if (!get_member_tinfo(&tinfo, member))
        {
            if (!get_or_guess_member_tinfo(&tinfo, member))
            {
                opinfo_t opinfo;
                if (retrieve_member_info(&opinfo, member)) {
                    // placeholder
                    result = BADADDR;
                    break;
                }
                result = BADADDR;
                break;
            }
        }
    
        members = re_type->getOwnMembers();
        if (members.count(member->soff) == 0)
            assert(false);
        reMember = members[member->soff];
        size = tinfo.get_size();
        if (reMember->getSize() < size) {
            re_type->UndefMembers(reMember->getSize() + reMember->offset, size - reMember->getSize(), true);
        }

        if (!tinfo.print(&q_string)) {
            // failed to print.
            return false;
        }
        msg("Setting member %s.%s to type %s\n", re_type->name.c_str(), reMember->name.c_str(), q_string.c_str());
        //break; // debug
        if (g_ReconstractedTypes.count(q_string.c_str()))
            reMemberType = new ReconstructedMemberReType(g_ReconstractedTypes[q_string.c_str()]);
        else
            reMemberType = new MemberTypeIDATypeInfoGate(tinfo);
        re_type->SetMemberType(member->soff, reMemberType);
        break;
    case idb_event::func_updated:
        func = va_arg(va, func_t *);
        break;
    default:
        break;
    }
    inside_hook = false;
    return result;
}

void re_types_form_init()
{
    hook_to_notification_point(
        HT_IDB,
        hook_idb_events,
        NULL);
    /*if (vtbl_list.empty() || vtbl_t_list.empty())
    {
        warning("ObjectExplorer not found any virtual tables here ...\n");
        logmsg(DEBUG, "ObjectExplorer not found any virtual tables here ...\n");
        return;
    }
    */
    auto widget = find_widget("Reconstructed Types");
    if (widget)
    {
        warning("Reconstructed Types window already open. Switching to it.\n");
        logmsg(DEBUG, "Reconstructed Types window already open. Switching to it.\n");
        activate_widget(widget, true);
        return;
    }

    widget = create_empty_widget("Reconstructed Types");
    static auto actions_initialized = false;
    if (!actions_initialized)
    {
        actions_initialized = true;
        //register_action(kMakeVTBLStrucActionDesc);
        //register_action(kShowVTBLXrefsWindowActionDesc);
    }

    auto*si = new reconstructed_types_info_t(widget);
    if (!g_ReconstractedTypes.empty()) {
        auto type_name = g_ReconstractedTypes.begin()->first; // bug if no any types found 
        reconstructed_place_t start(type_name);
        type_name = g_ReconstractedTypes.rbegin()->first;
        reconstructed_place_t end(type_name);
        end.lnnum = g_ReconstractedTypes.rbegin()->second->id;
        end.index = g_ReconstractedTypes.rbegin()->second->id;
        // simpleline_place_t s2(static_cast<int>(si->sv.size()) - 1);
        si->cv = create_custom_viewer("", static_cast<place_t*>(&start), static_cast<place_t*>(&end), &start, nullptr, nullptr, nullptr, nullptr, widget);
    }
    else {
        reconstructed_place_t start;
        si->cv = create_custom_viewer("", static_cast<place_t*>(&start), static_cast<place_t*>(&start), &start, nullptr, nullptr, nullptr, nullptr, widget);
    }

    si->codeview = create_code_viewer(si->cv, CDVF_STATUSBAR, widget);
    // ct_object_explorer_keyboard;
    auto cvh = custom_viewer_handlers_t();
    cvh.keyboard = reconstructed_keydown;
    cvh.popup = reconstructed_popup;
    cvh.adjust_place = reconstructed_adjust_place;
    //cvh.get_place_xcoord = reconstructed_get_place_xcoord;

    cvh.dblclick = reconstructed_types_dblclick;
    set_custom_viewer_handlers(si->cv, &cvh, si);
    set_code_viewer_line_handlers(si->codeview, nullptr, nullptr, nullptr, nullptr, reconstructed_types_lines_linenum);
    //hook_to_notification_point(HT_UI, ui_object_explorer_callback, si);
    display_widget(widget, WOPN_DP_TAB | WOPN_RESTORE);
}

void re_types_form_fini() {
    unhook_from_notification_point(HT_IDB,
        hook_idb_events,
        nullptr);
    const auto widget = find_widget("Reconstructed Types");
    // * no widget, no need to handle a thing
    if (!widget)
        return;
}

ReconstructedMemberType::ReconstructedMemberType(MemberTypeKind k) : kind(k) 
{
}

ReconstructedMemberType::ReconstructedMemberType(const ReconstructedMemberType &other) : kind(other.kind)
{
}

ReconstructedMemberType::~ReconstructedMemberType()
{
}

MemberTypeKind ReconstructedMemberType::getKind() 
{ 
    return kind; 
}


ReconstructedMemberReType::ReconstructedMemberReType(ReconstructableType * type) :
    ReconstructedMemberType(MemberType_Reconsrtucted), reType(type)
{
}

ReconstructedMemberReType::ReconstructedMemberReType(const ReconstructedMemberReType &other) :
    ReconstructedMemberType(other), reType(other.reType)
{
}

ReconstructedMemberReType::~ReconstructedMemberReType()
= default;


unsigned long ReconstructedMemberReType::getSize()
{
    return reType->getSize();
}

std::string ReconstructedMemberReType::getTypeString()
{
    return  reType->name;
}

void ReconstructedMemberReType::get_idaapi_tinfo(tinfo_t * out)
{
    tinfo_t result;
    result.get_named_type(get_idati(), reType->name.c_str());
    *out = result;
}

ReconstructedMemberType * ReconstructedMemberReType::clone()
{
    const auto result = new ReconstructedMemberReType(*this);
    return result;
}

MemberTypeIDATypeInfoGate::MemberTypeIDATypeInfoGate(tinfo_t t) : ReconstructedMemberType(MemberType_IDAGate), info(t)
{
}

MemberTypeIDATypeInfoGate::MemberTypeIDATypeInfoGate(const MemberTypeIDATypeInfoGate &other) : ReconstructedMemberType(other), info(other.info)
{
}

MemberTypeIDATypeInfoGate::~MemberTypeIDATypeInfoGate()
= default;


unsigned long MemberTypeIDATypeInfoGate::getSize()
{
    return info.get_size();
}

std::string MemberTypeIDATypeInfoGate::getTypeString()
{
    qstring out;
    info.print(&out);
    const auto name = out.c_str();
    return name;
}

void MemberTypeIDATypeInfoGate::get_idaapi_tinfo(tinfo_t * out)
{
    *out = info;
}

ReconstructedMemberType * MemberTypeIDATypeInfoGate::clone()
{
    return new MemberTypeIDATypeInfoGate(*this);
}

MemberTypePointer::MemberTypePointer(std::string t) :
    ReconstructedMemberType(MemberType_Pointer), pointedType(std::move(t))
{
}

MemberTypePointer::MemberTypePointer(const MemberTypePointer &other) :
    ReconstructedMemberType(other), pointedType(other.pointedType)
{
}

MemberTypePointer::~MemberTypePointer()
= default;

unsigned long MemberTypePointer::getSize()
{
    return sizeof(uval_t);
}

std::string MemberTypePointer::getTypeString()
{
    return pointedType + "*";
}

void MemberTypePointer::get_idaapi_tinfo(tinfo_t * out)
{
    tinfo_t info;
    info.get_named_type(0, pointedType.c_str());
    *out = make_pointer(info);
}

ReconstructedMemberType * MemberTypePointer::clone()
{
    return new MemberTypePointer(*this);
}

MemberTypeIDAFlagsTGate::MemberTypeIDAFlagsTGate(flags_t f, opinfo_t i) :
    ReconstructedMemberType(MemberType_IDAGate), flags(f), info(i)
{
}

MemberTypeIDAFlagsTGate::MemberTypeIDAFlagsTGate(const MemberTypeIDAFlagsTGate &other) :
    ReconstructedMemberType(other), flags(other.flags), info(other.info)
{

}

MemberTypeIDAFlagsTGate::~MemberTypeIDAFlagsTGate()
= default;


unsigned long MemberTypeIDAFlagsTGate::getSize()
{
    assert(false);
    return 0;
}

std::string MemberTypeIDAFlagsTGate::getTypeString()
{
    assert(false);
    return std::string();
}

void MemberTypeIDAFlagsTGate::get_idaapi_tinfo(tinfo_t * out)
{
    assert(false);
}

ReconstructedMemberType * MemberTypeIDAFlagsTGate::clone()
{
    return new MemberTypeIDAFlagsTGate(*this);
}

ReconstructableTypeVtable::~ReconstructableTypeVtable()
= default;

bool ReconstructableTypeVtable::SetMemberName(unsigned long offset, const char * newName)
{
    const auto func_addr = getEa(vtable_address + sizeof(ea_t) * 2 + sizeof(ea_t) * offset);
    auto funcname = newName;
    const auto class_name = name.substr(0, name.length() - sizeof(VTBL_CLSNAME_POSTFIX));
    // name.substr(0, name.length() - sizeof(VTBL_CLSNAME_POSTFIX))
    const auto member = findMemberByOffset(offset, false);
    if (!member)
        return false;
    char *ptr;
    if ((ptr = std::strstr(const_cast<char*>(newName), class_name.c_str()))) {
        ptr += class_name.size();
        if (ptr[0] == ':' && ptr[1] == ':')
        {
            ptr += 2;
        }
        funcname = ptr;
    }
    const auto func = get_func(func_addr);
    if (func) {
        auto long_name = class_name;
        long_name += "::";
        long_name += funcname;
        set_name(func_addr, long_name.c_str(), SN_NON_AUTO | SN_NOWARN | SN_FORCE);
    }
    return ReconstructableType::SetMemberName(offset, newName);
}

bool ReconstructableTypeVtable::SetMemberType(const unsigned long offset, ReconstructedMemberType * new_type)
{
    return ReconstructableType::SetMemberType(offset, new_type);
}

bool ReconstructableTypeVtable::AddMember(ReconstructableMember * member)
{
    //msg("Error: Called ReconstructableTypeVtable::AddMember for %s what usually shouldnt happend\n", name.c_str());
    //return false;
    return ReconstructableType::AddMember(member);
}

ea_t ReconstructableTypeVtable::to_ea(const unsigned long offset) const
{
    const auto res = getEa(vtable_address + sizeof(ea_t) * 2 + offset);

    return res;
}

ReconstructableTypeVtable * ReconstructableTypeVtable::get_reconstructable_type_vtable(const std::string & Name, ea_t addr)
{
    ReconstructableTypeVtable *re_type = nullptr;
    if (g_ReconstractedTypes.count(Name))
        return dynamic_cast<ReconstructableTypeVtable *>(g_ReconstractedTypes[Name]);
    re_type = new ReconstructableTypeVtable(Name, addr);
    g_ReconstractedTypes[Name] = re_type;
    return re_type;
}