Skip to content

Commit 759f0eb

Browse files
committed
Refine FileMetadata display name APIs and update UI consumers.
1 parent 92a7b85 commit 759f0eb

5 files changed

Lines changed: 99 additions & 28 deletions

File tree

binaryninjaapi.h

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3809,9 +3809,21 @@ namespace BinaryNinja {
38093809
*/
38103810
void SetFilename(const std::string& name);
38113811

3812-
/*! Get the path to the container file if the current file is inside a container (e.g. ZIP, TAR, etc.)
3812+
/*! Get the transform-chain identity for this file in the current session There are three meaningful states:
38133813

3814-
\return The path to the container file if the current file is inside a container, otherwise an empty string
3814+
* Empty - not yet processed by the transform system.
3815+
* Equal to GetFilename() - processed, no transform chain applied (plain file,
3816+
database, or container system disabled via ``files.container.mode``).
3817+
* Non-empty and different from GetFilename() - derived container entry.
3818+
3819+
Session-scoped: save-as does not persist the chain. Reopening the saved artifact
3820+
yields whatever chain that session's access path produces.
3821+
3822+
Use this for cache keys, identity-sensitive operations, or testing whether a file
3823+
has been processed. Use GetFilename() for the physical path, GetDisplayName() for
3824+
UI display.
3825+
3826+
\return The transform chain, or empty string if not yet processed.
38153827
*/
38163828
std::string GetVirtualPath() const;
38173829

@@ -3821,10 +3833,30 @@ namespace BinaryNinja {
38213833
*/
38223834
void SetVirtualPath(const std::string& path);
38233835

3824-
/*! Get the display name for the file. For container entries, this returns the synthesized name
3825-
representing the extracted artifact. For normal files, this returns the filename.
3836+
/*! True if this file was produced by the container transform system (e.g. an entry
3837+
extracted from a Zip). False for plain files, databases, and FileMetadata that
3838+
has not yet been processed by the transform system (virtual_path empty).
3839+
3840+
\return Whether this FileMetadata represents a derived container entry.
3841+
*/
3842+
bool IsContainerEntry() const
3843+
{
3844+
std::string virtualPath = GetVirtualPath();
3845+
return !virtualPath.empty() && GetFilename() != virtualPath;
3846+
}
3847+
3848+
/*! A leaf-shaped human-readable name for UI presentation. Never contains a directory
3849+
path. Resolution order:
3850+
3851+
* An explicitly set display name (project-assigned, transform-synthesized for
3852+
container entries, or set by a plugin or user).
3853+
* Otherwise the leaf of GetFilename().
3854+
3855+
Use this for tab titles, save-dialog default leaf names, logs, and any UI surface
3856+
where you'd refer to the file by name. Use GetFilename() for the physical path that
3857+
can be reopened.
38263858

3827-
\return The display name for UI purposes (tab titles, save dialogs, etc.)
3859+
\return The display name for UI purposes.
38283860
*/
38293861
std::string GetDisplayName() const;
38303862

filemetadata.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ FileMetadata::FileMetadata(const string& filename)
7373
FileMetadata::FileMetadata(Ref<ProjectFile> projectFile)
7474
{
7575
m_object = BNCreateFileMetadata();
76-
BNSetProjectFile(m_object, projectFile->m_object);
7776
BNSetFilename(m_object, projectFile->GetPathOnDisk().c_str());
77+
BNSetProjectFile(m_object, projectFile->m_object);
7878
}
7979

8080

python/filemetadata.py

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -211,11 +211,18 @@ def filename(self, value: str) -> None:
211211
@property
212212
def virtual_path(self) -> str:
213213
"""
214-
``virtual_path`` is a logical (non-filesystem) path that describes how this file was derived from container transform system.
214+
``virtual_path`` is a logical (non-filesystem) path describing how this file was derived from the
215+
container transform system in the current session. There are three meaningful states:
215216
216-
This path records provenance for files extracted from the transform system. It may include a sequence of transform steps and selection names.
217+
* Empty - not yet processed by the transform system.
218+
* Equal to ``filename`` - processed, no transform chain applied (plain file, database, or container
219+
system disabled via ``files.container.mode``).
220+
* Non-empty and different from ``filename`` - derived container entry.
217221
218-
.. 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.
222+
Session-scoped: save-as does not persist the chain. Reopening the saved artifact yields whatever chain
223+
that session's access path produces.
224+
225+
Use this for cache keys or identity-sensitive operations. Use ``filename`` for the physical path and ``display_name`` for UI display.
219226
"""
220227
return core.BNGetVirtualPath(self.handle)
221228

@@ -224,17 +231,27 @@ def virtual_path(self, value: str) -> None:
224231
core.BNSetVirtualPath(self.handle, str(value))
225232

226233
@property
227-
def display_name(self) -> str:
234+
def is_container_entry(self) -> bool:
235+
"""
236+
``True`` if this file was produced by the container transform system (e.g. an entry extracted from a Zip
237+
archive). ``False`` for plain files, databases, and FileMetadata that has not yet been processed by the
238+
transform system.
228239
"""
229-
``display_name`` is the synthesized name for UI display purposes.
240+
virtual = self.virtual_path
241+
return bool(virtual) and virtual != self.filename
230242

231-
For container entries, this contains a virtual filename representing the extracted artifact (e.g., "/path/to/entry").
232-
For normal files, this equals ``filename``.
243+
@property
244+
def display_name(self) -> str:
245+
"""
246+
``display_name`` is a leaf-shaped human-readable name for UI presentation. It never contains a directory
247+
path. Resolution order:
233248
234-
Use this property for tab titles, save dialog defaults, and other UI display purposes.
235-
Use ``filename`` for the actual physical file path that can be reopened.
249+
* An explicitly set display name (project-assigned, transform-synthesized for container entries, or set
250+
by a plugin or user).
251+
* Otherwise the leaf of ``filename``.
236252
237-
.. 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.
253+
Use this for tab titles, save-dialog default leaf names, logs, and any UI surface where you'd refer to
254+
the file by name. Use ``filename`` for the physical path that can be reopened.
238255
"""
239256
return core.BNGetDisplayName(self.handle)
240257

rust/src/file_metadata.rs

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,15 @@ impl FileMetadata {
176176
}
177177
}
178178

179-
/// The display name of the file. Useful for presenting to the user. Can differ from the original
180-
/// name of the file and can be overridden with [`FileMetadata::set_display_name`].
179+
/// A leaf-shaped human-readable name for UI presentation. Never contains a directory path.
180+
/// Resolution order:
181+
/// * An explicitly set display name (project-assigned, transform-synthesized for container
182+
/// entries, or set via [`FileMetadata::set_display_name`]).
183+
/// * Otherwise the leaf of [`FileMetadata::file_path`].
184+
///
185+
/// Use this for tab titles, save-dialog default leaf names, logs, and any UI surface where
186+
/// you'd refer to the file by name. Use [`FileMetadata::file_path`] for the physical path
187+
/// that can be reopened.
181188
pub fn display_name(&self) -> String {
182189
let raw_name = unsafe {
183190
let raw = BNGetDisplayName(self.handle);
@@ -243,20 +250,23 @@ impl FileMetadata {
243250
}
244251
}
245252

246-
/// The non-filesystem path that describes how this file was derived from the container
247-
/// transform system, detailing the sequence of transform steps and selection names.
253+
/// The non-filesystem path describing how this file was derived from the container transform
254+
/// system in the current session. There are three meaningful states:
255+
/// * `None` - not yet processed by the transform system.
256+
/// * `Some(p)` where `p == file_path()` - processed, no transform chain applied (plain file,
257+
/// database, or the container system was disabled via `files.container.mode`).
258+
/// * `Some(p)` where `p != file_path()` - derived container entry.
248259
///
249-
/// NOTE: Returns `None` if this [`FileMetadata`] was not processed by the transform system and
250-
/// does not differ from that of the "physical" file path reported by [`FileMetadata::file_path`].
260+
/// Session-scoped: save-as does not persist the chain. Reopening the saved artifact yields
261+
/// whatever chain that session's access path produces.
251262
pub fn virtual_path(&self) -> Option<String> {
252263
unsafe {
253264
let raw = BNGetVirtualPath(self.handle);
254265
let path = BnString::into_string(raw);
255-
// For whatever reason the core may report there being a virtual path as the file path.
256-
// In the case where that occurs, we wish not to report there being one to the user.
257-
match path.is_empty() || path == self.file_path() {
258-
true => None,
259-
false => Some(path),
266+
if path.is_empty() {
267+
None
268+
} else {
269+
Some(path)
260270
}
261271
}
262272
}
@@ -270,6 +280,12 @@ impl FileMetadata {
270280
}
271281
}
272282

283+
/// `true` if this file was produced by the container transform system, `false` for plain files,
284+
/// databases, and FileMetadata that has not yet been processed by the transform system.
285+
pub fn is_container_entry(&self) -> bool {
286+
matches!(self.virtual_path(), Some(p) if p != self.file_path())
287+
}
288+
273289
/// Whether the file is currently flagged as modified.
274290
///
275291
/// When this returns `true`, the UI will prompt to save the database on close, as well as display

ui/filecontext.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,13 @@ class BINARYNINJAUIAPI FileContext : public FileContextBase, public BinaryNinja:
6565
FileMetadataRef getMetadata() const { return m_file; }
6666
QString getFilename() const { return m_filename; }
6767
void setFilename(QString newName) { m_filename = newName; }
68-
QString getDisplayName() const;
68+
69+
// Returns the virtual path if the FileMetadata has one (transform session set it), otherwise
70+
// the physical filename. Use this anywhere you need a stable per-file identity.
71+
// Empty virtual_path carries real meaning ("this FileMetadata has not been processed by the
72+
// transform system"), so callers must not collapse that state at the core layer; this helper
73+
// is the intended fallback at the use site.
74+
static QString getVirtualPathOrFilename(FileMetadataRef file);
6975
ViewFrame* getCurrentViewFrame() const { return m_currentViewFrame; }
7076
QString getTabName(QWidget* widget);
7177
QString getShortFileName(QWidget* widget);

0 commit comments

Comments
 (0)