diff options
| author | Brian Potchik <brian@vector35.com> | 2026-05-14 07:51:48 -0400 |
|---|---|---|
| committer | Brian Potchik <brian@vector35.com> | 2026-05-14 07:51:48 -0400 |
| commit | 759f0eb73239dd747cd0b8eab55af01fa21837a4 (patch) | |
| tree | 1cef76f74e7415bc43c5e50208f0ca884e288849 | |
| parent | 92a7b85b7a0868f62db224f8b213027d3658f912 (diff) | |
Refine FileMetadata display name APIs and update UI consumers.
| -rw-r--r-- | binaryninjaapi.h | 42 | ||||
| -rw-r--r-- | filemetadata.cpp | 2 | ||||
| -rw-r--r-- | python/filemetadata.py | 37 | ||||
| -rw-r--r-- | rust/src/file_metadata.rs | 38 | ||||
| -rw-r--r-- | ui/filecontext.h | 8 |
5 files changed, 99 insertions, 28 deletions
diff --git a/binaryninjaapi.h b/binaryninjaapi.h index d9e1efc0..c48a5a6c 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -3809,9 +3809,21 @@ namespace BinaryNinja { */ void SetFilename(const std::string& name); - /*! Get the path to the container file if the current file is inside a container (e.g. ZIP, TAR, etc.) + /*! Get the transform-chain identity for this file in the current session There are three meaningful states: - \return The path to the container file if the current file is inside a container, otherwise an empty string + * Empty - not yet processed by the transform system. + * Equal to GetFilename() - processed, no transform chain applied (plain file, + database, or container system disabled via ``files.container.mode``). + * Non-empty and different from GetFilename() - derived container entry. + + Session-scoped: save-as does not persist the chain. Reopening the saved artifact + yields whatever chain that session's access path produces. + + Use this for cache keys, identity-sensitive operations, or testing whether a file + has been processed. Use GetFilename() for the physical path, GetDisplayName() for + UI display. + + \return The transform chain, or empty string if not yet processed. */ std::string GetVirtualPath() const; @@ -3821,10 +3833,30 @@ namespace BinaryNinja { */ void SetVirtualPath(const std::string& path); - /*! Get the display name for the file. For container entries, this returns the synthesized name - representing the extracted artifact. For normal files, this returns the filename. + /*! True if this file was produced by the container transform system (e.g. an entry + extracted from a Zip). False for plain files, databases, and FileMetadata that + has not yet been processed by the transform system (virtual_path empty). + + \return Whether this FileMetadata represents a derived container entry. + */ + bool IsContainerEntry() const + { + std::string virtualPath = GetVirtualPath(); + return !virtualPath.empty() && GetFilename() != virtualPath; + } + + /*! A leaf-shaped human-readable name for UI presentation. Never contains a directory + path. Resolution order: + + * An explicitly set display name (project-assigned, transform-synthesized for + container entries, or set by a plugin or user). + * Otherwise the leaf of GetFilename(). + + Use this for tab titles, save-dialog default leaf names, logs, and any UI surface + where you'd refer to the file by name. Use GetFilename() for the physical path that + can be reopened. - \return The display name for UI purposes (tab titles, save dialogs, etc.) + \return The display name for UI purposes. */ std::string GetDisplayName() const; diff --git a/filemetadata.cpp b/filemetadata.cpp index 18c30591..28384250 100644 --- a/filemetadata.cpp +++ b/filemetadata.cpp @@ -73,8 +73,8 @@ FileMetadata::FileMetadata(const string& filename) FileMetadata::FileMetadata(Ref<ProjectFile> projectFile) { m_object = BNCreateFileMetadata(); - BNSetProjectFile(m_object, projectFile->m_object); BNSetFilename(m_object, projectFile->GetPathOnDisk().c_str()); + BNSetProjectFile(m_object, projectFile->m_object); } diff --git a/python/filemetadata.py b/python/filemetadata.py index bfce4f98..de3472e3 100644 --- a/python/filemetadata.py +++ b/python/filemetadata.py @@ -211,11 +211,18 @@ class FileMetadata: @property def virtual_path(self) -> str: """ - ``virtual_path`` is a logical (non-filesystem) path that describes how this file was derived from container transform system. + ``virtual_path`` is a logical (non-filesystem) path describing how this file was derived from the + container transform system in the current session. There are three meaningful states: - This path records provenance for files extracted from the transform system. It may include a sequence of transform steps and selection names. + * Empty - not yet processed by the transform system. + * Equal to ``filename`` - processed, no transform chain applied (plain file, database, or container + system disabled via ``files.container.mode``). + * Non-empty and different from ``filename`` - derived container entry. - .. note:: An empty `virtual_path` indicates the file has not yet been processed by the transform system. If `virtual_path` matches `filename`, the file is not the result of an extraction or transform. + Session-scoped: save-as does not persist the chain. Reopening the saved artifact yields whatever chain + that session's access path produces. + + Use this for cache keys or identity-sensitive operations. Use ``filename`` for the physical path and ``display_name`` for UI display. """ return core.BNGetVirtualPath(self.handle) @@ -224,17 +231,27 @@ class FileMetadata: core.BNSetVirtualPath(self.handle, str(value)) @property - def display_name(self) -> str: + def is_container_entry(self) -> bool: + """ + ``True`` if this file was produced by the container transform system (e.g. an entry extracted from a Zip + archive). ``False`` for plain files, databases, and FileMetadata that has not yet been processed by the + transform system. """ - ``display_name`` is the synthesized name for UI display purposes. + virtual = self.virtual_path + return bool(virtual) and virtual != self.filename - For container entries, this contains a virtual filename representing the extracted artifact (e.g., "/path/to/entry"). - For normal files, this equals ``filename``. + @property + def display_name(self) -> str: + """ + ``display_name`` is a leaf-shaped human-readable name for UI presentation. It never contains a directory + path. Resolution order: - Use this property for tab titles, save dialog defaults, and other UI display purposes. - Use ``filename`` for the actual physical file path that can be reopened. + * An explicitly set display name (project-assigned, transform-synthesized for container entries, or set + by a plugin or user). + * Otherwise the leaf of ``filename``. - .. note:: For normal files, ``filename`` == ``virtual_path`` == ``display_name``. For container files, ``filename`` is the container path, ``virtual_path`` is the transform chain, and ``display_name`` is the extracted entry name. + Use this for tab titles, save-dialog default leaf names, logs, and any UI surface where you'd refer to + the file by name. Use ``filename`` for the physical path that can be reopened. """ return core.BNGetDisplayName(self.handle) diff --git a/rust/src/file_metadata.rs b/rust/src/file_metadata.rs index 7b8a60fd..064d1fe9 100644 --- a/rust/src/file_metadata.rs +++ b/rust/src/file_metadata.rs @@ -176,8 +176,15 @@ impl FileMetadata { } } - /// The display name of the file. Useful for presenting to the user. Can differ from the original - /// name of the file and can be overridden with [`FileMetadata::set_display_name`]. + /// A leaf-shaped human-readable name for UI presentation. Never contains a directory path. + /// Resolution order: + /// * An explicitly set display name (project-assigned, transform-synthesized for container + /// entries, or set via [`FileMetadata::set_display_name`]). + /// * Otherwise the leaf of [`FileMetadata::file_path`]. + /// + /// Use this for tab titles, save-dialog default leaf names, logs, and any UI surface where + /// you'd refer to the file by name. Use [`FileMetadata::file_path`] for the physical path + /// that can be reopened. pub fn display_name(&self) -> String { let raw_name = unsafe { let raw = BNGetDisplayName(self.handle); @@ -243,20 +250,23 @@ impl FileMetadata { } } - /// The non-filesystem path that describes how this file was derived from the container - /// transform system, detailing the sequence of transform steps and selection names. + /// The non-filesystem path describing how this file was derived from the container transform + /// system in the current session. There are three meaningful states: + /// * `None` - not yet processed by the transform system. + /// * `Some(p)` where `p == file_path()` - processed, no transform chain applied (plain file, + /// database, or the container system was disabled via `files.container.mode`). + /// * `Some(p)` where `p != file_path()` - derived container entry. /// - /// NOTE: Returns `None` if this [`FileMetadata`] was not processed by the transform system and - /// does not differ from that of the "physical" file path reported by [`FileMetadata::file_path`]. + /// Session-scoped: save-as does not persist the chain. Reopening the saved artifact yields + /// whatever chain that session's access path produces. pub fn virtual_path(&self) -> Option<String> { unsafe { let raw = BNGetVirtualPath(self.handle); let path = BnString::into_string(raw); - // For whatever reason the core may report there being a virtual path as the file path. - // In the case where that occurs, we wish not to report there being one to the user. - match path.is_empty() || path == self.file_path() { - true => None, - false => Some(path), + if path.is_empty() { + None + } else { + Some(path) } } } @@ -270,6 +280,12 @@ impl FileMetadata { } } + /// `true` if this file was produced by the container transform system, `false` for plain files, + /// databases, and FileMetadata that has not yet been processed by the transform system. + pub fn is_container_entry(&self) -> bool { + matches!(self.virtual_path(), Some(p) if p != self.file_path()) + } + /// Whether the file is currently flagged as modified. /// /// When this returns `true`, the UI will prompt to save the database on close, as well as display diff --git a/ui/filecontext.h b/ui/filecontext.h index 6a8c1d51..47581b53 100644 --- a/ui/filecontext.h +++ b/ui/filecontext.h @@ -65,7 +65,13 @@ class BINARYNINJAUIAPI FileContext : public FileContextBase, public BinaryNinja: FileMetadataRef getMetadata() const { return m_file; } QString getFilename() const { return m_filename; } void setFilename(QString newName) { m_filename = newName; } - QString getDisplayName() const; + + // Returns the virtual path if the FileMetadata has one (transform session set it), otherwise + // the physical filename. Use this anywhere you need a stable per-file identity. + // Empty virtual_path carries real meaning ("this FileMetadata has not been processed by the + // transform system"), so callers must not collapse that state at the core layer; this helper + // is the intended fallback at the use site. + static QString getVirtualPathOrFilename(FileMetadataRef file); ViewFrame* getCurrentViewFrame() const { return m_currentViewFrame; } QString getTabName(QWidget* widget); QString getShortFileName(QWidget* widget); |
