#pragma once #include #include #include #include #include #include #include "sidebar.h" #include "viewframe.h" #include "filter.h" #include "progresstask.h" #include "typeeditor.h" enum BINARYNINJAUIAPI TypeBrowserFilterMode { NamesOnly = 0, NamesAndMembers = 1, FullDefinitions = 2 }; class BINARYNINJAUIAPI TypeBrowserTreeNode : public std::enable_shared_from_this { public: struct UpdateData { enum UpdateType { NodeInserted, NodeUpdated, NodeRemoved, UpdatesFinished, }; UpdateType type; std::shared_ptr parent; std::shared_ptr node; std::function commit; }; typedef std::function UpdateNodeCallback; protected: class TypeBrowserModelData* m_model; std::optional> m_parent; std::vector> m_children; std::unordered_map m_childIndices; bool m_hasGeneratedChildren; TypeBrowserTreeNode(class TypeBrowserModelData* model, std::optional> parent); virtual ~TypeBrowserTreeNode() = default; virtual void generateChildren() = 0; void updateChildIndices(); void removeChild(std::shared_ptr child); void addChild(std::shared_ptr child); public: class TypeBrowserModelData* model() const { return m_model; } std::optional> parent() const; const std::vector>& children(); int indexOfChild(std::shared_ptr child) const; virtual std::string text(int column) const = 0; virtual bool lessThan(const TypeBrowserTreeNode& other, int column) const = 0; virtual bool filter(const std::string& filter, TypeBrowserFilterMode mode, bool caseSensitive) const = 0; virtual void updateChildren(bool recursive, UpdateNodeCallback update); }; class BINARYNINJAUIAPI EmptyTreeNode : public TypeBrowserTreeNode { public: EmptyTreeNode(class TypeBrowserModelData* model, std::optional> parent); virtual ~EmptyTreeNode() = default; virtual std::string text(int column) const override; virtual bool lessThan(const TypeBrowserTreeNode& other, int column) const override; virtual bool filter(const std::string& filter, TypeBrowserFilterMode mode, bool caseSensitive) const override; protected: virtual void generateChildren() override; virtual void updateChildren(bool recursive, UpdateNodeCallback update) override; }; class BINARYNINJAUIAPI RootTreeNode : public TypeBrowserTreeNode { std::map> m_containerNodes; public: RootTreeNode(class TypeBrowserModelData* model, std::optional> parent); virtual ~RootTreeNode() = default; virtual std::string text(int column) const override; virtual bool lessThan(const TypeBrowserTreeNode& other, int column) const override; virtual bool filter(const std::string& filter, TypeBrowserFilterMode mode, bool caseSensitive) const override; protected: virtual void generateChildren() override; virtual void updateChildren(bool recursive, UpdateNodeCallback update) override; }; class BINARYNINJAUIAPI TypeTreeNode : public TypeBrowserTreeNode { public: enum SourceType { None, TypeLibrary, TypeArchive, DebugInfo, Platform, Other }; private: std::string m_id; BinaryNinja::QualifiedName m_name; TypeRef m_type; std::string m_sortName; SourceType m_sourceType; std::optional m_sourceLibrary; std::optional m_sourceArchive; std::optional m_sourceDebugInfoParser; std::optional m_sourcePlatform; std::optional m_sourceOtherName; std::optional m_sourceOriginalName; public: TypeTreeNode(class TypeBrowserModelData* model, std::optional> parent, const std::string& id, BinaryNinja::QualifiedName name, TypeRef type); virtual ~TypeTreeNode() = default; const std::string& id() const { return m_id; } const BinaryNinja::QualifiedName& name() const { return m_name; } const TypeRef& type() const { return m_type; } void setType(const std::string& id, const BinaryNinja::QualifiedName& name, const TypeRef& type); const std::string& sortName() const { return m_sortName; } const SourceType& sourceType() const { return m_sourceType; } std::optional typeContainer() const; std::optional sourceTypeContainer() const; PlatformRef sourcePlatform() const; virtual std::string text(int column) const override; virtual bool lessThan(const TypeBrowserTreeNode& other, int column) const override; virtual bool filter(const std::string& filter, TypeBrowserFilterMode mode, bool caseSensitive) const override; protected: virtual void generateChildren() override; }; class BINARYNINJAUIAPI TypeContainerTreeNode : public TypeBrowserTreeNode { std::string m_containerId; // TODO: Gross std::map, std::shared_ptr>> m_typeNodes; public: TypeContainerTreeNode(class TypeBrowserModelData* model, std::optional> parent, const std::string& m_containerId); virtual ~TypeContainerTreeNode(); virtual std::string text(int column) const override; virtual bool filter(const std::string& filter, TypeBrowserFilterMode mode, bool caseSensitive) const override; virtual bool lessThan(const TypeBrowserTreeNode& other, int column) const override; const std::string& containerId() const { return m_containerId; } std::optional platform() const; std::optional typeContainer() const; std::optional containerType() const; virtual void updateChildren(bool recursive, UpdateNodeCallback update) override; protected: virtual void generateChildren() override; }; //----------------------------------------------------------------------------- /*! Cursed data struct behind a shared_ptr so Qt stops deleting our model while the background updates run */ class TypeBrowserModelData: public std::enable_shared_from_this { BinaryViewRef m_data; mutable std::recursive_mutex m_rootNodeMutex; // Controls m_rootNode std::shared_ptr m_rootNode; std::recursive_mutex m_stateMutex; // Controls m_needsUpdate, m_updating bool m_needsUpdate; bool m_updating; std::mutex m_backgroundTaskMutex; std::vector m_containerIds; std::map m_containerNames; std::map m_containerTypes; std::map m_containers; std::map m_containerViews; std::map m_containerArchives; std::map m_containerArchiveIds; std::map m_containerLibraries; std::map m_containerDebugInfos; std::map m_containerPlatforms; void addContainer(BinaryNinja::TypeContainer cont); friend class TypeBrowserModel; public: explicit TypeBrowserModelData(BinaryViewRef data); ~TypeBrowserModelData(); TypeBrowserModelData(const TypeBrowserModelData&) = delete; TypeBrowserModelData(TypeBrowserModelData&&) = delete; TypeBrowserModelData& operator=(const TypeBrowserModelData&) = delete; TypeBrowserModelData& operator=(TypeBrowserModelData&&) = delete; BinaryViewRef getData(); std::shared_ptr getRootNode(); std::vector containerIds() const; std::string nameForContainerId(const std::string& id) const; std::optional> containerForContainerId(const std::string& id); std::optional> containerForContainerId(const std::string& id) const; std::optional viewForContainerId(const std::string& id) const; std::optional archiveForContainerId(const std::string& id) const; std::optional archiveIdForContainerId(const std::string& id) const; std::optional libraryForContainerId(const std::string& id) const; std::optional debugInfoForContainerId(const std::string& id) const; std::optional platformForContainerId(const std::string& id) const; void addAllContainersForView(BinaryViewRef view); void addContainerForView(BinaryViewRef view); void addUserContainerForView(BinaryViewRef view); void addAutoContainerForView(BinaryViewRef view); void addContainerForArchive(TypeArchiveRef archive); void addContainerForArchiveId(const std::string& archiveId, const std::string& path); void addContainerForLibrary(TypeLibraryRef library); void addContainerForDebugInfo(DebugInfoRef debugInfo, const std::string& parser); void addContainerForPlatform(PlatformRef platform); void clearContainers(); std::vector> containerNodes() const; }; //----------------------------------------------------------------------------- class BINARYNINJAUIAPI TypeBrowserModel : public QAbstractItemModel, public BinaryNinja::BinaryDataNotification, public BinaryNinja::TypeArchiveNotification { Q_OBJECT BinaryViewRef m_data; std::shared_ptr m_modelData; void commitUpdate(const TypeBrowserTreeNode::UpdateData& update); void commitUpdates(const std::vector& updates); public: TypeBrowserModel(BinaryViewRef data, QObject* parent); virtual ~TypeBrowserModel(); BinaryViewRef getData(); std::shared_ptr getRootNode(); std::vector containerIds() const; std::vector> containerNodes() const; std::string nameForContainerId(const std::string& id) const; std::optional> containerForContainerId(const std::string& id); std::optional> containerForContainerId(const std::string& id) const; std::optional viewForContainerId(const std::string& id) const; std::optional archiveForContainerId(const std::string& id) const; std::optional archiveIdForContainerId(const std::string& id) const; std::optional libraryForContainerId(const std::string& id) const; std::optional debugInfoForContainerId(const std::string& id) const; std::optional platformForContainerId(const std::string& id) const; void updateFonts(); void runAfterUpdate(std::function callback); int columnCount(const QModelIndex& parent = QModelIndex()) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QModelIndex parent(const QModelIndex& child) const override; QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; std::shared_ptr nodeForIndex(const QModelIndex& index) const; QModelIndex indexForNode(std::shared_ptr node, int column = 0) const; bool filter(const QModelIndex& index, const std::string& filter, TypeBrowserFilterMode mode, bool caseSensitive) const; bool lessThan(const QModelIndex& left, const QModelIndex& right) const; void OnTypeDefined(BinaryNinja::BinaryView* data, const BinaryNinja::QualifiedName& name, BinaryNinja::Type* type) override; void OnTypeUndefined(BinaryNinja::BinaryView* data, const BinaryNinja::QualifiedName& name, BinaryNinja::Type* type) override; void OnTypeReferenceChanged(BinaryNinja::BinaryView* data, const BinaryNinja::QualifiedName& name, BinaryNinja::Type* type) override; void OnTypeFieldReferenceChanged(BinaryNinja::BinaryView* data, const BinaryNinja::QualifiedName& name, uint64_t offset) override; void OnTypeAdded(TypeArchiveRef archive, const std::string& id, TypeRef definition) override; void OnTypeUpdated(TypeArchiveRef archive, const std::string& id, TypeRef oldDefinition, TypeRef newDefinition) override; void OnTypeRenamed(TypeArchiveRef archive, const std::string& id, const BinaryNinja::QualifiedName& oldName, const BinaryNinja::QualifiedName& newName) override; void OnTypeDeleted(TypeArchiveRef archive, const std::string& id, TypeRef definition) override; void OnTypeArchiveAttached(BinaryNinja::BinaryView* data, const std::string& id, const std::string& path) override; void OnTypeArchiveDetached(BinaryNinja::BinaryView* data, const std::string& id, const std::string& path) override; void OnTypeArchiveConnected(BinaryNinja::BinaryView* data, BinaryNinja::TypeArchive* archive) override; void OnTypeArchiveDisconnected(BinaryNinja::BinaryView* data, BinaryNinja::TypeArchive* archive) override; Q_SIGNALS: void updatesAboutToHappen(); void updateComplete(bool didAnyHappen); public Q_SLOTS: void markDirty(); void notifyRefresh(); }; class BINARYNINJAUIAPI TypeBrowserFilterModel : public QSortFilterProxyModel { Q_OBJECT BinaryViewRef m_data; TypeBrowserModel* m_model; std::string m_originalFilter; std::string m_filter; bool m_filterCaseSensitive = false; TypeBrowserFilterMode m_filterMode; bool m_exactOnTop; protected: bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override; bool lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const override; public: TypeBrowserFilterModel(BinaryViewRef data, TypeBrowserModel* model, QObject* parent); void setFilter(const std::string& filter, FilterOptions options); TypeBrowserFilterMode filterMode() const { return m_filterMode; } void setFilterMode(TypeBrowserFilterMode newMode) { m_filterMode = newMode; } Q_SIGNALS: void filterAboutToBeChanged(); void filterChanged(); }; class BINARYNINJAUIAPI TypeBrowserItemDelegate : public QItemDelegate { QFont m_font; QFont m_monospaceFont; float m_charWidth, m_charHeight, m_charOffset; float m_baseline; class TypeBrowserView* m_view; void initFont(); public: TypeBrowserItemDelegate(class TypeBrowserView* view); int lineHeight() const; void updateFonts(); virtual QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override; virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; }; class BINARYNINJAUIAPI TypeBrowserTreeView : public QTreeView { Q_OBJECT UIActionHandler m_actionHandler; public: explicit TypeBrowserTreeView(class TypeBrowserView* parent); }; struct BINARYNINJAUIAPI TypeReference { std::string containerId; BinaryNinja::QualifiedName typeName; TypeReference() = default; TypeReference(std::string containerId, BinaryNinja::QualifiedName typeName); }; class BINARYNINJAUIAPI TypeBrowserView : public QFrame, public View, public FilterTarget { Q_OBJECT BinaryViewRef m_data; class TypeBrowserContainer* m_container; ContextMenuManager* m_contextMenuManager; QSplitter* m_splitter; TypeBrowserModel* m_model; TypeBrowserFilterModel* m_filterModel; QStandardItemModel* m_loadingModel; QTreeView* m_tree; TypeBrowserItemDelegate* m_delegate; bool m_updatedWidths; bool m_navigateToNextInsert; QModelIndex m_lastPosition; QModelIndex m_lastInsert; TypeEditor::SavedCursorPosition m_editorPosition; TypeEditor* m_typeEditor; QTextEdit* m_debugText; void updateInTransaction(std::function transaction); std::string dumpTypeDefinition(BinaryNinja::TypeContainer container, TypeRef type, std::string name); public: TypeBrowserView(BinaryViewRef data, TypeBrowserContainer* container); TypeBrowserContainer* getContainer() { return m_container; } TypeBrowserModel* getModel() { return m_model; } TypeBrowserFilterModel* getFilterModel() { return m_filterModel; } QTreeView* getTreeView() { return m_tree; } TypeEditor* getTypeEditor() { return m_typeEditor; } virtual BinaryViewRef getData() override { return m_data; } virtual uint64_t getCurrentOffset() override; virtual void setSelectionOffsets(BNAddressRange range) override; virtual bool navigate(uint64_t offset) override; virtual SelectionInfoForXref getSelectionForXref() override; virtual QFont getFont() override; virtual void updateFonts() override; virtual void showEvent(QShowEvent* event) override; virtual void resizeEvent(QResizeEvent* event) override; virtual StatusBarWidget* getStatusBarWidget() override; virtual QWidget* getHeaderOptionsWidget() override; virtual void setFilter(const std::string& filter, FilterOptions options) override; virtual void scrollToFirstItem() override; virtual void scrollToCurrentItem() override; virtual void ensureSelection() override; virtual void activateSelection() override; virtual void notifyRefresh() override; void showSelectedTypes(); void showTypes(const std::vector& types); void selectTypeByName(const std::string& name, bool newSelection); bool navigateToType(const std::string& typeName, uint64_t offset); void scrollToIndexWithContext(const QModelIndex& index, int context = 1); void setPrimaryOrientation(Qt::Orientation orientation); // Selection helpers // All nodes std::vector> selectedNodes() const; // BV selected or BV relevant to selected types, only if JUST bv stuff is selected std::optional selectedBV() const; // If selectedBV exists, names of selected types std::optional> selectedBVTypeNames() const; std::optional> selectedTypeNameAndContainer() const; // All selected type names, grouped by type container std::vector>> selectedTypeNamesByContainers() const; // Selected type reference std::optional selectedType() const; // Selected type references std::vector selectedTypes() const; // Selected type container, or container of selected type // makeSureItHasPlatform: if the type container is a BV with no platform (raw), ask for one and return nullopt if rejected // preferView: if the type container is a BV and the user/auto-only container, switch to the whole-view container for that BV instead std::optional selectedTypeContainer(bool makeSureItHasPlatform = true, bool preferView = false) const; // Which (selection preferred) container should be used for creating new types std::optional typeContainerForCreating(bool makeSureItHasPlatform = true, bool preferView = false) const; // Selected type container ids, or containers of selected types std::unordered_set selectedTypeContainerIds() const; // TA selected or TA relevant to selected types, only if JUST ta stuff is selected and only 1 TA std::optional selectedTA() const; // Id of TA selected or TA relevant to selected types, only if JUST ta stuff is selected and only 1 TA std::optional selectedTAId() const; // TAs selected or TAs relevant to selected types, only if JUST ta stuff is selected std::optional> selectedTAs() const; // Ids of TAs selected or TAs relevant to selected types, only if JUST ta stuff is selected std::optional> selectedTAIds() const; // If selectedTAs exist, map of ta ids to ids of selected types from that ta std::optional>> selectedTATypeIds() const; // All type archives that are attached and connected std::vector connectedTAs(BinaryViewRef view) const; // Names -> Ids, if any don't exist then nullopt static std::optional> typeIdsFromNames(BinaryViewRef view, const std::unordered_set& names); // Ids -> Option static std::unordered_map, std::unordered_set> associatedTypeArchivesForTypeIds(BinaryViewRef view, const std::unordered_set& typeIds); std::optional containerForId(const std::string& containerId, bool makeSureItHasPlatform = false, bool preferView = false) const; // Menu actions static void registerActions(); void bindActions(); void showContextMenu(); bool canConnectTypeArchive(); void connectTypeArchive(); bool canCreateTypeArchive(); void createTypeArchive(); bool canAttachTypeArchive(); void attachTypeArchive(); bool canDetachTypeArchive(); void detachTypeArchive(); bool canSyncSelectedTypes(); void syncSelectedTypes(); bool pushTypesFromView(BinaryViewRef view, const std::unordered_set& names); bool canPushSelectedTypes(); void pushSelectedTypes(); bool canPushAllTypes(); void pushAllTypes(); bool pullTypesFromView(BinaryViewRef view, const std::unordered_set& viewTypeNames); bool pullTypesFromArchives(BinaryViewRef view, const std::unordered_map>& archiveTypeIdSelection); bool canPullSelectedTypes(); void pullSelectedTypes(); bool canPullAllTypes(); void pullAllTypes(); bool canDisassociateSelectedTypes(); void disassociateSelectedTypes(); bool canCreateNewTypes(); void createNewTypes(); bool canCreateNewStructure(); void createNewStructure(); bool canCreateNewEnumeration(); void createNewEnumeration(); bool canCreateNewUnion(); void createNewUnion(); bool canRenameTypes(); void renameTypes(); bool canCopyTypeNames(); void copyTypeNames(); bool canCopyTypeDefinitions(); void copyTypeDefinitions(); bool canDeleteTypes(); void deleteTypes(); bool canChangeTypes(); void changeTypes(); bool canCreateStructureMemberAtOffset(); void createStructureMemberAtOffset(); bool canImportType(); void importType(); bool canImportTypeByGUID(BinaryViewRef view); void importTypeByGUID(); bool canAddTypeLibrary(); void addTypeLibrary(); bool canExpandAll(); void expandAll(); bool canCollapseAll(); void collapseAll(); bool canSwitchLayout(); void switchLayout(); Q_SIGNALS: void typeNameNavigated(const std::string& typeName, bool newSelection); protected: void itemSelected(const QItemSelection& selected, const QItemSelection& deselected); void itemDoubleClicked(const QModelIndex& index); virtual void contextMenuEvent(QContextMenuEvent* event) override; virtual bool event(QEvent* event) override; virtual bool eventFilter(QObject* obj, QEvent* event) override; virtual void focusInEvent(QFocusEvent* event) override; void ensureTypeEditorHasSelection(); }; class BINARYNINJAUIAPI TypeBrowserOptionsIconWidget : public QWidget { public: TypeBrowserOptionsIconWidget(TypeBrowserView* parent); private: TypeBrowserView* m_view; void showMenu(); }; class BINARYNINJAUIAPI TypeBrowserContainer : public QWidget, public ViewContainer { Q_OBJECT BinaryViewRef m_data; TypeBrowserView* m_view; FilteredView* m_filter; FilterEdit* m_separateEdit; class TypeBrowserSidebarWidget* m_sidebarWidget; UIActionHandler m_actionHandler; private: void bindNavigationShortcuts(); void navigateBack(); void navigateForward(); public: TypeBrowserContainer(BinaryViewRef data, class TypeBrowserSidebarWidget* parent); virtual View* getView() override { return m_view; } BinaryViewRef getData() { return m_data; } TypeBrowserView* getTypeBrowserView() { return m_view; } FilteredView* getFilter() { return m_filter; } FilterEdit* getSeparateFilterEdit() { return m_separateEdit; } class TypeBrowserSidebarWidget* getSidebarWidget() { return m_sidebarWidget; } void showContextMenu(); protected: virtual void focusInEvent(QFocusEvent* event) override; virtual bool event(QEvent* event) override; }; class BINARYNINJAUIAPI TypeBrowserViewType : public ViewType { static TypeBrowserViewType* g_instance; public: TypeBrowserViewType(); virtual int getPriority(BinaryViewRef data, const QString& filename) override; virtual QWidget* create(BinaryViewRef data, ViewFrame* frame) override; static void init(); }; class BINARYNINJAUIAPI TypeBrowserSidebarWidget : public SidebarWidget { Q_OBJECT QWidget* m_header; TypeBrowserContainer* m_container; public: TypeBrowserSidebarWidget(BinaryViewRef data); TypeBrowserContainer* container() { return m_container; } virtual void notifyRefresh() override; virtual QWidget* headerWidget() override { return m_header; } virtual void focus() override; virtual void setPrimaryOrientation(Qt::Orientation orientation) override; protected: virtual void contextMenuEvent(QContextMenuEvent* event) override; private Q_SLOTS: void showContextMenu(); }; class BINARYNINJAUIAPI TypeBrowserSidebarWidgetType : public SidebarWidgetType { public: TypeBrowserSidebarWidgetType(); virtual SidebarWidget* createWidget(ViewFrame* frame, BinaryViewRef data) override; SidebarWidgetLocation defaultLocation() const override { return SidebarWidgetLocation::LeftContent; } SidebarContextSensitivity contextSensitivity() const override { return PerViewTypeSidebarContext; } virtual bool canUseAsPane(SplitPaneWidget*, BinaryViewRef) const override { return true; } virtual Pane* createPane(SplitPaneWidget* panes, BinaryViewRef data) override; };