diff --git a/CommonUtil/Container.cs b/CommonUtil/Container.cs
index 8370112..d297690 100644
--- a/CommonUtil/Container.cs
+++ b/CommonUtil/Container.cs
@@ -39,10 +39,13 @@ namespace CommonUtil {
///
/// Compares two Dictionaries to see if their contents are equal. Key and value types
- /// must have correctly-implemented equality checks.
+ /// must have correctly-implemented equality checks. (I contend this works incorrectly
+ /// for float -- 5.0f is equal to the integer 5.)
///
///
/// https://stackoverflow.com/q/3804367/294248
+ ///
+ /// TODO: make this work right for float/int comparisons
///
/// Dictionary key type.
/// Dictionary value type.
diff --git a/PluginCommon/Interfaces.cs b/PluginCommon/Interfaces.cs
index 3f9f655..9a2a3a4 100644
--- a/PluginCommon/Interfaces.cs
+++ b/PluginCommon/Interfaces.cs
@@ -29,15 +29,20 @@ namespace PluginCommon {
string Identifier { get; }
///
- /// Prepares the plugin for action. Called at the start of every code analysis pass.
- ///
+ /// Prepares the plugin for action. Called at the start of every code analysis pass
+ /// and when generating visualization images.
+ ///
/// In the current implementation, the file data will be the same every time,
/// because it doesn't change after the project is opened. However, this could
- /// change if we add a descramble feature.
+ /// change if we add a descramble feature. The IApplication and AddressTranslate
+ /// references will likely change between invocations.
+ ///
+ /// This may be called even when the plugin won't be asked to do anything. Avoid
+ /// performing expensive operations here.
///
/// Reference to application interface.
/// 65xx code and data.
- /// Mapping between offsets and addresses.
+ /// Mapping between offsets and addresses.
void Prepare(IApplication appRef, byte[] fileData, AddressTranslate addrTrans);
///
@@ -236,12 +241,13 @@ namespace PluginCommon {
///
/// Interfaces provided by the application for use by plugins. An IApplication instance
- /// is passed to the plugin as an argument Prepare().
+ /// is passed to the plugin as an argument to Prepare().
///
public interface IApplication {
///
/// Sends a debug message to the application. This can be useful when debugging scripts.
- /// (Use DEBUG > Show Analyzer Output to view it.)
+ /// (For example, DEBUG > Show Analyzer Output shows output generated while performing
+ /// code analysis.)
///
/// Message to send.
void DebugLog(string msg);
diff --git a/SourceGen/App.xaml b/SourceGen/App.xaml
index debd7bd..0066d93 100644
--- a/SourceGen/App.xaml
+++ b/SourceGen/App.xaml
@@ -22,6 +22,18 @@ limitations under the License.
Consolas
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SourceGen/DisasmProject.cs b/SourceGen/DisasmProject.cs
index 77c065c..91ff6dd 100644
--- a/SourceGen/DisasmProject.cs
+++ b/SourceGen/DisasmProject.cs
@@ -2204,11 +2204,11 @@ namespace SourceGen {
}
break;
case UndoableChange.ChangeType.SetProjectProperties: {
- bool needExternalFileReload = !CommonUtil.Container.StringListEquals(
+ bool needPlatformSymReload = !CommonUtil.Container.StringListEquals(
((ProjectProperties)oldValue).PlatformSymbolFileIdentifiers,
((ProjectProperties)newValue).PlatformSymbolFileIdentifiers,
null /*StringComparer.InvariantCulture*/);
- needExternalFileReload |= !CommonUtil.Container.StringListEquals(
+ bool needExtScriptReload = !CommonUtil.Container.StringListEquals(
((ProjectProperties)oldValue).ExtensionScriptFileIdentifiers,
((ProjectProperties)newValue).ExtensionScriptFileIdentifiers,
null);
@@ -2226,7 +2226,7 @@ namespace SourceGen {
Debug.WriteLine("Replacing CPU def object");
UpdateCpuDef();
- if (needExternalFileReload) {
+ if (needPlatformSymReload || needExtScriptReload) {
string errMsgs = LoadExternalFiles();
// TODO(someday): if the plugin failed to compile, we will have
@@ -2237,6 +2237,14 @@ namespace SourceGen {
// "reload all external files and plugins" command, which might
// run through here.)
}
+ if (needExtScriptReload) {
+ // Clear all the Visualization thumbnails. We don't do GUI
+ // stuff in here, so this just sets a flag.
+ foreach (KeyValuePair kvp
+ in VisualizationSets) {
+ kvp.Value.RefreshNeeded();
+ }
+ }
}
// ignore affectedOffsets
Debug.Assert(uc.ReanalysisRequired ==
diff --git a/SourceGen/DisplayList.cs b/SourceGen/DisplayList.cs
index d56c96e..06eb3da 100644
--- a/SourceGen/DisplayList.cs
+++ b/SourceGen/DisplayList.cs
@@ -20,6 +20,7 @@ using System.Collections.Specialized;
using System.Diagnostics;
using System.ComponentModel;
using System.Windows.Media;
+using System.Windows.Media.Imaging;
namespace SourceGen {
///
@@ -354,10 +355,13 @@ namespace SourceGen {
public string Operand { get; private set; }
public string Comment { get; private set; }
public bool IsLongComment { get; private set; }
- public bool IsVisualizationSet { get; private set; }
+
public bool HasBackgroundColor { get; private set; }
public Brush BackgroundBrush { get; private set; }
+ public bool IsVisualizationSet { get; private set; }
+ public Visualization[] VisualizationSet { get; private set; }
+
// Set to true if we want to highlight the address and label fields.
public bool HasAddrLabelHighlight { get; private set; }
@@ -452,8 +456,9 @@ namespace SourceGen {
public static FormattedParts CreateVisualizationSet(VisualizationSet visSet) {
FormattedParts parts = new FormattedParts();
if (visSet.Count == 0) {
- // not expected
- parts.Comment = "!EMPTY!";
+ // should not happen
+ parts.Comment = "!EMPTY VSET!";
+ parts.IsLongComment = true;
} else {
string fmt;
if (visSet.Count == 1) {
@@ -461,10 +466,10 @@ namespace SourceGen {
} else {
fmt = Res.Strings.VIS_SET_MULTIPLE_FMT;
}
- parts.Comment = string.Format(fmt, visSet[0].Tag, visSet.Count - 1);
+ parts.Comment = string.Format(fmt, "Bitmap", visSet[0].Tag, visSet.Count - 1);
+ parts.VisualizationSet = visSet.ToArray();
+ parts.IsVisualizationSet = true;
}
- // TODO(xyzzy): show image thumbnails
- parts.IsVisualizationSet = true;
return parts;
}
diff --git a/SourceGen/MainController.cs b/SourceGen/MainController.cs
index ca43677..f1bcab3 100644
--- a/SourceGen/MainController.cs
+++ b/SourceGen/MainController.cs
@@ -983,6 +983,10 @@ namespace SourceGen {
mReanalysisTimer.StartTask("Generate DisplayList");
CodeLineList.GenerateAll();
mReanalysisTimer.EndTask("Generate DisplayList");
+
+ mReanalysisTimer.StartTask("Refresh Visualization thumbnails");
+ VisualizationSet.RefreshAllThumbnails(mProject);
+ mReanalysisTimer.EndTask("Refresh Visualization thumbnails");
}
#endregion Project management
diff --git a/SourceGen/Res/Strings.xaml b/SourceGen/Res/Strings.xaml
index bffbc17..ff33f3a 100644
--- a/SourceGen/Res/Strings.xaml
+++ b/SourceGen/Res/Strings.xaml
@@ -175,6 +175,6 @@ limitations under the License.
[new project]
*READ-ONLY*
[unset]
- Vis: {0} (+{1} more)
- Vis: {0}
+ {0}: {1} (+{2} more)
+ {0}: {1}
\ No newline at end of file
diff --git a/SourceGen/RuntimeData/Apple/VisHiRes.cs b/SourceGen/RuntimeData/Apple/VisHiRes.cs
index 6b01b49..b9434d8 100644
--- a/SourceGen/RuntimeData/Apple/VisHiRes.cs
+++ b/SourceGen/RuntimeData/Apple/VisHiRes.cs
@@ -121,8 +121,8 @@ namespace RuntimeData.Apple {
bool isColor, isFirstOdd;
offset = Util.GetFromObjDict(parms, P_OFFSET, 0);
- byteWidth = Util.GetFromObjDict(parms, P_BYTE_WIDTH, 0); // width ignoring colStride
- height = Util.GetFromObjDict(parms, P_HEIGHT, 0);
+ byteWidth = Util.GetFromObjDict(parms, P_BYTE_WIDTH, 1); // width ignoring colStride
+ height = Util.GetFromObjDict(parms, P_HEIGHT, 1);
colStride = Util.GetFromObjDict(parms, P_COL_STRIDE, 0);
rowStride = Util.GetFromObjDict(parms, P_ROW_STRIDE, 0);
isColor = Util.GetFromObjDict(parms, P_IS_COLOR, true);
diff --git a/SourceGen/Visualization.cs b/SourceGen/Visualization.cs
index 2ba1693..1653a37 100644
--- a/SourceGen/Visualization.cs
+++ b/SourceGen/Visualization.cs
@@ -18,10 +18,17 @@ using System.Collections.Generic;
using System.Text;
using System.Windows.Media;
using System.Windows.Media.Imaging;
+
using CommonUtil;
using PluginCommon;
namespace SourceGen {
+ ///
+ /// Graphical visualization object. Useful for displaying 2D bitmaps and 3D objects.
+ ///
+ /// Treat this as generally immutable, i.e. don't modify VisGenParams. The CachedImage
+ /// field is mutable.
+ ///
public class Visualization {
///
/// Unique user-specified tag. Contents are arbitrary, but may not be empty.
@@ -39,13 +46,19 @@ namespace SourceGen {
public Dictionary VisGenParams { get; private set; }
///
- /// Cached reference to thumbnail.
+ /// Cached reference to 2D image, useful for thumbnails. Not serialized.
///
///
/// Because the underlying data never changes, we only need to regenerate the
- /// thumbnail if the set of active plugins changes.
+ /// image if the set of active plugins changes.
///
- private BitmapSource Thumbnail { get; set; } // TODO - 64x64(?) bitmap
+ public BitmapSource CachedImage { get; set; }
+
+ ///
+ /// Image to show when things are broken.
+ ///
+ public static readonly BitmapImage BROKEN_IMAGE =
+ new BitmapImage(new Uri("pack://application:,,,/Res/RedX.png"));
///
@@ -59,6 +72,19 @@ namespace SourceGen {
Tag = tag;
VisGenIdent = visGenIdent;
VisGenParams = visGenParams;
+ CachedImage = BROKEN_IMAGE;
+ }
+
+ ///
+ /// Updates the cached thumbnail image.
+ ///
+ /// Visualization, or null to clear the thumbnail.
+ public void SetThumbnail(IVisualization2d vis2d) {
+ if (vis2d == null) {
+ CachedImage = BROKEN_IMAGE;
+ } else {
+ CachedImage = ConvertToBitmapSource(vis2d);
+ }
}
///
@@ -110,31 +136,6 @@ namespace SourceGen {
return image;
}
- ///
- /// Finds a plugin that provides the named visualization generator.
- ///
- /// Project with script manager.
- /// Visualization generator identifier.
- /// A plugin that matches, or null if none found.
- public static IPlugin_Visualizer FindPluginByVisGenIdent(DisasmProject proj,
- string visGenIdent, out VisDescr visDescr) {
- List plugins = proj.GetActivePlugins();
- foreach (IPlugin chkPlug in plugins) {
- if (!(chkPlug is IPlugin_Visualizer)) {
- continue;
- }
- IPlugin_Visualizer vplug = (IPlugin_Visualizer)chkPlug;
- foreach (VisDescr descr in vplug.GetVisGenDescrs()) {
- if (descr.Ident == visGenIdent) {
- visDescr = descr;
- return vplug;
- }
- }
- }
- visDescr = null;
- return null;
- }
-
public override string ToString() {
return "[Vis: " + Tag + " (" + VisGenIdent + ") count=" + VisGenParams.Count + "]";
@@ -147,8 +148,8 @@ namespace SourceGen {
if (ReferenceEquals(a, null) || ReferenceEquals(b, null)) {
return false; // one is null
}
- // All fields must be equal.
- if (a.Tag != b.Tag || a.VisGenIdent != b.VisGenIdent || a.Thumbnail != b.Thumbnail) {
+ // All fields must be equal (but we ignore CachedImage).
+ if (a.Tag != b.Tag || a.VisGenIdent != b.VisGenIdent) {
return false;
}
// Compare the vis gen parameter lists.
@@ -167,7 +168,7 @@ namespace SourceGen {
return obj is Visualization && this == (Visualization)obj;
}
public override int GetHashCode() {
- // TODO(maybe): hash code should include up VisGenParams items
+ // TODO(maybe): hash code should factor in VisGenParams items
return Tag.GetHashCode() ^ VisGenIdent.GetHashCode() ^ VisGenParams.Count;
}
}
diff --git a/SourceGen/VisualizationSet.cs b/SourceGen/VisualizationSet.cs
index 34068bc..0dfeaf1 100644
--- a/SourceGen/VisualizationSet.cs
+++ b/SourceGen/VisualizationSet.cs
@@ -16,13 +16,16 @@
using System;
using System.Collections;
using System.Collections.Generic;
+using System.Diagnostics;
+
+using PluginCommon;
namespace SourceGen {
///
/// Ordered list of visualization objects.
///
///
- /// Right now the only thing separating this from a plain List<> is the operator== stuff.
+ /// There's not much separating this from a plain List<>, except perhaps the operator== stuff.
///
public class VisualizationSet : IEnumerable {
///
@@ -74,6 +77,122 @@ namespace SourceGen {
mList.Remove(vis);
}
+ public Visualization[] ToArray() {
+ Visualization[] arr = new Visualization[mList.Count];
+ for (int i = 0; i < mList.Count; i++) {
+ arr[i] = mList[i];
+ }
+ return arr;
+ }
+
+ #region Image generation
+
+ private class ScriptSupport : MarshalByRefObject, PluginCommon.IApplication {
+ public ScriptSupport() { }
+
+ public void DebugLog(string msg) {
+ Debug.WriteLine("Vis plugin: " + msg);
+ }
+
+ public bool SetOperandFormat(int offset, DataSubType subType, string label) {
+ throw new InvalidOperationException();
+ }
+ public bool SetInlineDataFormat(int offset, int length, DataType type,
+ DataSubType subType, string label) {
+ throw new InvalidOperationException();
+ }
+ }
+
+ ///
+ /// Informs all list elements that a refresh is needed. Call this when the set of active
+ /// plugins changes. The actual refresh will happen later.
+ ///
+ public void RefreshNeeded() {
+ foreach (Visualization vis in mList) {
+ vis.SetThumbnail(null);
+ }
+ }
+
+ ///
+ /// Attempts to refresh broken thumbnails across all visualization sets in the project.
+ ///
+ /// Project reference.
+ public static void RefreshAllThumbnails(DisasmProject project) {
+ ScriptSupport iapp = null;
+ List plugins = null;
+
+ foreach (KeyValuePair kvp in project.VisualizationSets) {
+ VisualizationSet visSet = kvp.Value;
+ foreach (Visualization vis in visSet) {
+ if (vis.CachedImage != Visualization.BROKEN_IMAGE) {
+ continue;
+ }
+ Debug.WriteLine("Vis needs refresh: " + vis.Tag);
+
+ if (plugins == null) {
+ plugins = project.GetActivePlugins();
+ }
+ IPlugin_Visualizer vplug = FindPluginByVisGenIdent(plugins,
+ vis.VisGenIdent, out VisDescr visDescr);
+ if (vplug == null) {
+ Debug.WriteLine("Unable to referesh " + vis.Tag + ": plugin not found");
+ continue;
+ }
+
+ if (iapp == null) {
+ // Prep the plugins on first need.
+ iapp = new ScriptSupport();
+ project.PrepareScripts(iapp);
+ }
+
+ IVisualization2d vis2d;
+ try {
+ vis2d = vplug.Generate2d(visDescr, vis.VisGenParams);
+ if (vis2d == null) {
+ Debug.WriteLine("Vis generator returned null");
+ }
+ } catch (Exception ex) {
+ Debug.WriteLine("Vis generation failed: " + ex);
+ vis2d = null;
+ }
+ if (vis2d != null) {
+ Debug.WriteLine(" Rendered thumbnail: " + vis.Tag);
+ vis.SetThumbnail(vis2d);
+ }
+ }
+ }
+
+ if (iapp != null) {
+ project.UnprepareScripts();
+ }
+ }
+
+ ///
+ /// Finds a plugin that provides the named visualization generator.
+ ///
+ /// List of plugins, from project ScriptManager.
+ /// Visualization generator identifier.
+ /// A plugin that matches, or null if none found.
+ private static IPlugin_Visualizer FindPluginByVisGenIdent(List plugins,
+ string visGenIdent, out VisDescr visDescr) {
+ foreach (IPlugin chkPlug in plugins) {
+ if (!(chkPlug is IPlugin_Visualizer)) {
+ continue;
+ }
+ IPlugin_Visualizer vplug = (IPlugin_Visualizer)chkPlug;
+ foreach (VisDescr descr in vplug.GetVisGenDescrs()) {
+ if (descr.Ident == visGenIdent) {
+ visDescr = descr;
+ return vplug;
+ }
+ }
+ }
+ visDescr = null;
+ return null;
+ }
+
+ #endregion Image generation
+
public override string ToString() {
return "[VS: " + mList.Count + " items]";
diff --git a/SourceGen/WpfGui/CodeListItemStyle.xaml b/SourceGen/WpfGui/CodeListItemStyle.xaml
index cd32ada..4185a6c 100644
--- a/SourceGen/WpfGui/CodeListItemStyle.xaml
+++ b/SourceGen/WpfGui/CodeListItemStyle.xaml
@@ -92,16 +92,28 @@ See also https://github.com/fadden/DisasmUiTest
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SourceGen/WpfGui/EditVisualization.xaml b/SourceGen/WpfGui/EditVisualization.xaml
index 56c448f..1041a11 100644
--- a/SourceGen/WpfGui/EditVisualization.xaml
+++ b/SourceGen/WpfGui/EditVisualization.xaml
@@ -127,7 +127,7 @@ limitations under the License.
+ Background="{StaticResource CheckerBackground}">
diff --git a/SourceGen/WpfGui/EditVisualization.xaml.cs b/SourceGen/WpfGui/EditVisualization.xaml.cs
index 270b66f..8642929 100644
--- a/SourceGen/WpfGui/EditVisualization.xaml.cs
+++ b/SourceGen/WpfGui/EditVisualization.xaml.cs
@@ -141,10 +141,9 @@ namespace SourceGen.WpfGui {
private ScriptSupport mScriptSupport;
///
- /// URI for the image we show when we fail to generate a visualization.
+ /// Image we show when we fail to generate a visualization.
///
- private static BitmapImage sBadParamsImage =
- new BitmapImage(new Uri("pack://application:,,,/Res/RedX.png"));
+ private static BitmapImage sBadParamsImage = Visualization.BROKEN_IMAGE;
///
@@ -259,6 +258,7 @@ namespace SourceGen.WpfGui {
string trimTag = Visualization.TrimAndValidateTag(TagString, out bool isTagValid);
Debug.Assert(isTagValid);
NewVis = new Visualization(trimTag, item.VisDescriptor.Ident, valueDict);
+ NewVis.CachedImage = (BitmapSource)previewImage.Source;
DialogResult = true;
}
diff --git a/SourceGen/WpfGui/EditVisualizationSet.xaml b/SourceGen/WpfGui/EditVisualizationSet.xaml
index f412b5e..25613c0 100644
--- a/SourceGen/WpfGui/EditVisualizationSet.xaml
+++ b/SourceGen/WpfGui/EditVisualizationSet.xaml
@@ -23,7 +23,7 @@ limitations under the License.
xmlns:local="clr-namespace:SourceGen.WpfGui"
mc:Ignorable="d"
Title="Edit Visualization Set"
- Width="500" Height="400" ResizeMode="NoResize"
+ Width="560" Height="400" ResizeMode="NoResize"
ShowInTaskbar="False" WindowStartupLocation="CenterOwner"
Closing="Window_Closing">
@@ -47,7 +47,7 @@ limitations under the License.
IsReadOnly="True"
FontFamily="{StaticResource GeneralMonoFont}"
SnapsToDevicePixels="True"
- GridLinesVisibility="Vertical"
+ HorizontalGridLinesBrush="#FF7F7F7F"
VerticalGridLinesBrush="#FF7F7F7F"
AutoGenerateColumns="False"
HeadersVisibility="Column"
@@ -64,7 +64,18 @@ limitations under the License.
+
+
+
+
+
+
+
+
+
+
diff --git a/SourceGen/WpfGui/EditVisualizationSet.xaml.cs b/SourceGen/WpfGui/EditVisualizationSet.xaml.cs
index 0520b1f..f4490bf 100644
--- a/SourceGen/WpfGui/EditVisualizationSet.xaml.cs
+++ b/SourceGen/WpfGui/EditVisualizationSet.xaml.cs
@@ -22,12 +22,9 @@ using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
-using System.Windows.Media;
-using System.Windows.Media.Imaging;
using Asm65;
using PluginCommon;
-using SourceGen.Sandbox;
namespace SourceGen.WpfGui {
///