From 5bb1052d9659d41b04278bf582550fc6e15fe10e Mon Sep 17 00:00:00 2001 From: tinysec Date: Thu, 9 Jul 2026 00:30:00 +0800 Subject: [PATCH] feat: add StringReference.GetValue/GetRaw (Python parity) Closes HIGH convenience gap #4. Python's StringReference exposes .value (decoded text) and .raw (bytes) (binaryview.py:510/:514); the C# StringReference is a handle-less Type/Start/Length carrier, so callers had to separately ReadData(Start, Length) and hand-decode per StringType. Add durable GetValue(this StringReference, BinaryView) and GetRaw(this StringReference, BinaryView) extension methods with a StringType -> Encoding table mirroring Python's _decodings (Ascii->ASCII, Utf8->UTF8, Utf16->Unicode/UTF-16LE, Utf32->UTF32LE). GetValue decodes with .NET's default replacement fallback, the equivalent of Python errors='replace'. The view is a parameter because StringReference does not own one (unlike Python); extending its shape would force a FromNative signature change threaded through the TakeStructArray pipelines, so composition-only extensions are cleaner. --- NavigationExtensions.cs | 51 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/NavigationExtensions.cs b/NavigationExtensions.cs index 563792e..4563a28 100644 --- a/NavigationExtensions.cs +++ b/NavigationExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Runtime.InteropServices; +using System.Text; namespace BinaryNinja { @@ -635,6 +636,56 @@ public static bool Contains(this BinaryView view, ulong address) }; } + // Maps each StringType to the .NET encoding Python's StringReference._decodings uses + // (binaryview.py:491): Ascii->ascii, Utf8->utf-8, Utf16->utf-16, Utf32->utf-32. UTF-16 LE + // is Encoding.Unicode and UTF-32 LE is Encoding.UTF32 on all platforms Binary Ninja runs on. + private static readonly Dictionary s_stringDecodings = + new Dictionary + { + { StringType.AsciiString, Encoding.ASCII }, + { StringType.Utf8String, Encoding.UTF8 }, + { StringType.Utf16String, Encoding.Unicode }, + { StringType.Utf32String, Encoding.UTF32 }, + }; + + /// + /// The raw bytes of this string reference, mirroring Python StringReference.raw + /// (binaryview.py:514): reads bytes at + /// from . The view is taken as a + /// parameter because is a handle-less value carrier and does + /// not own one (unlike Python, where the reference stores the view). + /// + public static byte[] GetRaw(this StringReference stringReference, BinaryView view) + { + if (null == view) + { + throw new ArgumentNullException(nameof(view)); + } + + return view.ReadData(stringReference.Start, stringReference.Length); + } + + /// + /// The decoded text of this string reference, mirroring Python StringReference.value + /// (binaryview.py:510): reads the bytes via and decodes them according + /// to . Undecodable bytes are replaced rather than + /// thrown, matching Python errors='replace'. + /// + public static string GetValue(this StringReference stringReference, BinaryView view) + { + byte[] raw = GetRaw(stringReference, view); + + // Fall back to ASCII for an unexpected StringType, matching Python's dict lookup + // semantics as closely as possible (Python would KeyError; the binding degrades + // gracefully because the type is an open enum). + s_stringDecodings.TryGetValue(stringReference.Type, out Encoding? foundEncoding); + Encoding encoding = foundEncoding ?? Encoding.ASCII; + + // .NET's default decoders use replacement fallback (U+FFFD / '?'), which is the + // equivalent of Python errors='replace'. + return encoding.GetString(raw); + } + /// /// Every disassembly instruction line across the whole view, in /// view-block-then-instruction order. Mirrors Python BinaryView.instructions