From 1cdb31de3252640eddabd7fc667ae77d97e9d683 Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Fri, 6 Dec 2019 14:49:35 -0800 Subject: [PATCH] Visualizer improvements Various changes: - Generally treat visualization sets like long comments and notes when it comes to defining data region boundaries. (We were doing this for selections; now we're also doing it for format-as-word and in the data analyzer when scanning for strings/fill.) - Clear the visualization cache when the address map is altered. This is necessary for visualizers that dereference addresses. - Read the Apple II screen image from a series of addresses rather than a series of offsets. This allows it to work when the image is contiguous in memory but split into chunks in the file. - Put 1 pixel of padding around the images in the main code list, so they don't blend into the background. - Remember the last visualizer used, so we can re-use it the next time the user selects "new". - Move min-size hack from Loaded to ContentRendered, as it apparently spoils CenterOwner placement. --- SourceGen/DataAnalysis.cs | 2 +- SourceGen/DisasmProject.cs | 32 ++++++++++++------- SourceGen/MainController.cs | 9 ++---- SourceGen/RuntimeData/Apple/VisHiRes.cs | 32 ++++++++++++------- SourceGen/RuntimeData/Help/index.html | 14 ++++---- SourceGen/RuntimeData/Help/visualization.html | 18 ++++++++--- .../apple2-bitmap-test#061000.dis65 | 2 +- SourceGen/WpfGui/CodeListItemStyle.xaml | 2 +- SourceGen/WpfGui/EditVisualization.xaml | 2 +- SourceGen/WpfGui/EditVisualization.xaml.cs | 20 ++++++++++-- 10 files changed, 87 insertions(+), 46 deletions(-) diff --git a/SourceGen/DataAnalysis.cs b/SourceGen/DataAnalysis.cs index 5174822..3c72cc3 100644 --- a/SourceGen/DataAnalysis.cs +++ b/SourceGen/DataAnalysis.cs @@ -552,7 +552,7 @@ namespace SourceGen { Debug.Assert(attr.Length > 0); offset += attr.Length; } - } else if (attr.Symbol != null || mProject.HasCommentOrNote(offset)) { + } else if (attr.Symbol != null || mProject.HasCommentNoteOrVis(offset)) { // In an uncategorized area, but we want to break at this byte // so the user or auto label doesn't get buried in the middle of // a large chunk. diff --git a/SourceGen/DisasmProject.cs b/SourceGen/DisasmProject.cs index 91ff6dd..cf33d83 100644 --- a/SourceGen/DisasmProject.cs +++ b/SourceGen/DisasmProject.cs @@ -1798,14 +1798,16 @@ namespace SourceGen { } /// - /// Returns true if the offset has a long comment or note. Used for determining how to - /// split up a data area. Currently not returning true for an end-of-line comment. + /// Returns true if the offset has a long comment, note, or visualization. Used for + /// determining how to split up a data area. + /// Currently not returning true for an end-of-line comment. /// /// Offset of interest. - /// True if a comment or note was found. - public bool HasCommentOrNote(int offset) { + /// True if a comment, note, or visualization was found. + public bool HasCommentNoteOrVis(int offset) { return (LongComments.ContainsKey(offset) || - Notes.ContainsKey(offset)); + Notes.ContainsKey(offset) || + VisualizationSets.ContainsKey(offset)); } /// @@ -1986,6 +1988,10 @@ namespace SourceGen { Debug.WriteLine("Map offset +" + offset.ToString("x6") + " to $" + ((int)newValue).ToString("x6")); + // Visualization generators in extension scripts could be chasing + // addresses. Give them a chance to update. + ClearVisualizationCache(); + // ignore affectedOffsets Debug.Assert(uc.ReanalysisRequired == UndoableChange.ReanalysisScope.CodeAndData); @@ -2238,12 +2244,7 @@ namespace SourceGen { // 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(); - } + ClearVisualizationCache(); } } // ignore affectedOffsets @@ -2292,6 +2293,15 @@ namespace SourceGen { return needReanalysis; } + /// + /// Clears all visualization cached images. + /// + private void ClearVisualizationCache() { + foreach (KeyValuePair kvp in VisualizationSets) { + kvp.Value.RefreshNeeded(); + } + } + /// /// Updates all symbolic references to the old label. Call this after replacing /// mAnattribs[labelOffset].Symbol. diff --git a/SourceGen/MainController.cs b/SourceGen/MainController.cs index f67eed3..dc2b0fa 100644 --- a/SourceGen/MainController.cs +++ b/SourceGen/MainController.cs @@ -2344,7 +2344,7 @@ namespace SourceGen { Anattrib nextAttr = mProject.GetAnattrib(nextOffset); // This must match what GroupedOffsetSetFromSelected() does. if (!mProject.UserLabels.ContainsKey(nextOffset) && - !mProject.HasCommentOrNote(nextOffset) && + !mProject.HasCommentNoteOrVis(nextOffset) && thisAttr.Address == nextAttr.Address - 1) { // Good to go. Debug.WriteLine("Grabbing second byte from +" + nextOffset.ToString("x6")); @@ -3329,11 +3329,8 @@ namespace SourceGen { // for the uncategorized data analyzer, but very annoying if you want to // slap a 16-bit numeric format on all entries in a table. groupNum++; - } else if (mProject.HasCommentOrNote(offset)) { - // Don't carry across a long comment or note. - groupNum++; - } else if (mProject.VisualizationSets.ContainsKey(offset)) { - // Don't carry across a visualization. + } else if (mProject.HasCommentNoteOrVis(offset)) { + // Don't carry across a long comment, note, or visualization. groupNum++; } diff --git a/SourceGen/RuntimeData/Apple/VisHiRes.cs b/SourceGen/RuntimeData/Apple/VisHiRes.cs index 51b5b05..8a7f6a7 100644 --- a/SourceGen/RuntimeData/Apple/VisHiRes.cs +++ b/SourceGen/RuntimeData/Apple/VisHiRes.cs @@ -247,7 +247,7 @@ namespace RuntimeData.Apple { } private IVisualization2d GenerateScreen(ReadOnlyDictionary parms) { - const int RAW_IMAGE_SIZE = 0x1ff8; + //const int RAW_IMAGE_SIZE = 0x1ff8; const int HR_WIDTH = 280; const int HR_BYTE_WIDTH = HR_WIDTH / 7; const int HR_HEIGHT = 192; @@ -261,24 +261,34 @@ namespace RuntimeData.Apple { return null; } - int lastOffset = offset + RAW_IMAGE_SIZE - 1; - if (lastOffset >= mFileData.Length) { - mAppRef.ReportError("Bitmap runs off end of file (last offset +" + - lastOffset.ToString("x6") + ")"); - return null; - } + //int lastOffset = offset + RAW_IMAGE_SIZE - 1; + //if (lastOffset >= mFileData.Length) { + // mAppRef.ReportError("Bitmap runs off end of file (last offset +" + + // lastOffset.ToString("x6") + ")"); + // return null; + //} - // Linearize the data. + // Linearize the data. To handle programs that move themselves around before + // executing we use the address translator (e.g. the title screen in Space Eggs + // is contiguous in memory but split in half in the file). This is slower, but + // mAddrTrans is a local (not proxy) object, so it's not too bad. byte[] buf = new byte[HR_BYTE_WIDTH * HR_HEIGHT]; int outIdx = 0; + int baseAddr = mAddrTrans.OffsetToAddress(offset); for (int row = 0; row < HR_HEIGHT; row++) { - // If row is ABCDEFGH, we want pppFGHCD EABAB000 (where p is zero for us). + // If row is ABCDEFGH, we want pppFGHCD EABAB000 (where p would be $20/$40). int low = ((row & 0xc0) >> 1) | ((row & 0xc0) >> 3) | ((row & 0x08) << 4); int high = ((row & 0x07) << 2) | ((row & 0x30) >> 4); - int addr = (high << 8) | low; + int rowAddr = baseAddr + ((high << 8) | low); for (int col = 0; col < HR_BYTE_WIDTH; col++) { - buf[outIdx++] = mFileData[offset + addr + col]; + int srcOffset = mAddrTrans.AddressToOffset(offset, rowAddr + col); + if (srcOffset < 0) { + mAppRef.ReportError("Address $" + (rowAddr + col).ToString("x4") + + " is outside of file"); + return null; + } + buf[outIdx++] = mFileData[srcOffset]; } } diff --git a/SourceGen/RuntimeData/Help/index.html b/SourceGen/RuntimeData/Help/index.html index d226572..16980bd 100644 --- a/SourceGen/RuntimeData/Help/index.html +++ b/SourceGen/RuntimeData/Help/index.html @@ -69,13 +69,6 @@ and 65816 code. The official web site is -
  • Visualizations -
  • -
  • Editors
  • +
  • Visualizations +
  • +
  • Code Generation & Assembly
    • Supported Assemblers diff --git a/SourceGen/RuntimeData/Help/visualization.html b/SourceGen/RuntimeData/Help/visualization.html index 0eadb5e..541e62b 100644 --- a/SourceGen/RuntimeData/Help/visualization.html +++ b/SourceGen/RuntimeData/Help/visualization.html @@ -30,10 +30,14 @@ include them in your project.

      The project file doesn't store the converted graphics. Instead, the project file holds a string that identifies the converter, and a list of parameters that are passed to the converter. Images are generated when -the project is first opened, and updated if the set of loaded extension -scripts changes.

      +the project is first opened, and updated when certain things change in +the project.

      Visualizations are not included in generated assembly output. They may be included in HTML exports.

      +

      Because visualizations are associated with a specific file offset, +they will become "hidden" if the offset isn't at the start of a line, +e.g. it's in the middle of a multi-byte instruction or data item. The +editors will try to prevent you from doing this.

      Visualizations and Visualization Sets

      @@ -43,7 +47,9 @@ assembled output, and do not change how code is analyzed. They are contained in sets that are placed at arbitrary offsets. Each set can contain multiple items. For example, if a file has data for 10 bitmaps, you can place a visualization near each, or create a single -visualization set with all 10 items and put it at the start of the file.

      +visualization set with all 10 items and put it at the start of the file. +You can display a visualization near the instructions that perform the +drawing.

      To create a visualization set, select a code or data line, and use Actions > Create/Edit Visualization Set. To edit a visualization set, @@ -115,7 +121,11 @@ arrangements are common. The script defines three generators:

      converted as monochrome, and have a 1-pixel transparent gap between elements.
    • Hi-Res Screen Image - used for 8KiB screen images. The - data is linearized and converted to a 280x192 bitmap.
    • + data is linearized and converted to a 280x192 bitmap. Because + images are relatively large, the generator does not require them + to be contiguous in the file, i.e. two halves of the image can be + in different parts of the file so long as they end up contiguous + in memory.

    Widths are specified in bytes, not pixels. Each byte represents 7 diff --git a/SourceGen/SGTestData/Visualization/apple2-bitmap-test#061000.dis65 b/SourceGen/SGTestData/Visualization/apple2-bitmap-test#061000.dis65 index 00fd089..a064771 100644 --- a/SourceGen/SGTestData/Visualization/apple2-bitmap-test#061000.dis65 +++ b/SourceGen/SGTestData/Visualization/apple2-bitmap-test#061000.dis65 @@ -21,7 +21,7 @@ "LongComments":{ }, "Notes":{ -"213":{ +"222":{ "Text":"Bitmaps here","BoxMode":false,"MaxWidth":80,"BackgroundColor":-256}}, "UserLabels":{ }, diff --git a/SourceGen/WpfGui/CodeListItemStyle.xaml b/SourceGen/WpfGui/CodeListItemStyle.xaml index 4185a6c..c62d4ea 100644 --- a/SourceGen/WpfGui/CodeListItemStyle.xaml +++ b/SourceGen/WpfGui/CodeListItemStyle.xaml @@ -106,7 +106,7 @@ See also https://github.com/fadden/DisasmUiTest - diff --git a/SourceGen/WpfGui/EditVisualization.xaml b/SourceGen/WpfGui/EditVisualization.xaml index 00e201a..c2f2616 100644 --- a/SourceGen/WpfGui/EditVisualization.xaml +++ b/SourceGen/WpfGui/EditVisualization.xaml @@ -25,7 +25,7 @@ limitations under the License. Title="Edit Visualization" SizeToContent="WidthAndHeight" ResizeMode="CanResizeWithGrip" ShowInTaskbar="False" WindowStartupLocation="CenterOwner" - Loaded="Window_Loaded" + ContentRendered="Window_ContentRendered" Closed="Window_Closed"> diff --git a/SourceGen/WpfGui/EditVisualization.xaml.cs b/SourceGen/WpfGui/EditVisualization.xaml.cs index 3a1841b..0172cc1 100644 --- a/SourceGen/WpfGui/EditVisualization.xaml.cs +++ b/SourceGen/WpfGui/EditVisualization.xaml.cs @@ -42,6 +42,11 @@ namespace SourceGen.WpfGui { private Visualization mOrigVis; private string mOrigTag; + ///

    + /// Identifier for last visualizer we used, for the benefit of "new". + /// + private static string sLastVisIdent = string.Empty; + private Brush mDefaultLabelColor = SystemColors.WindowTextBrush; private Brush mErrorLabelColor = Brushes.Red; @@ -168,7 +173,7 @@ namespace SourceGen.WpfGui { mFormatter = formatter; mSetOffset = setOffset; mOrigVis = vis; - mOrigTag = vis.Tag; + mOrigTag = (vis == null) ? string.Empty : vis.Tag; mScriptSupport = new ScriptSupport(this); mProject.PrepareScripts(mScriptSupport); @@ -180,7 +185,7 @@ namespace SourceGen.WpfGui { TagString = "vis" + mSetOffset.ToString("x6"); } - int visSelection = 0; + int visSelection = -1; VisualizationList = new List(); List plugins = proj.GetActivePlugins(); foreach (IPlugin chkPlug in plugins) { @@ -190,6 +195,10 @@ namespace SourceGen.WpfGui { IPlugin_Visualizer vplug = (IPlugin_Visualizer)chkPlug; foreach (VisDescr descr in vplug.GetVisGenDescrs()) { if (vis != null && vis.VisGenIdent == descr.Ident) { + // found matching descriptor, set selection to this + visSelection = VisualizationList.Count; + } else if (visSelection < 0 && descr.Ident == sLastVisIdent) { + // we used this one last time, use it if nothing better comes along visSelection = VisualizationList.Count; } VisualizationList.Add(new VisualizationItem(vplug, descr)); @@ -197,6 +206,9 @@ namespace SourceGen.WpfGui { } // Set the selection. This should cause the sel change event to fire. + if (visSelection < 0) { + visSelection = 0; + } visComboBox.SelectedIndex = visSelection; } @@ -253,7 +265,7 @@ namespace SourceGen.WpfGui { } } - private void Window_Loaded(object sender, RoutedEventArgs e) { + private void Window_ContentRendered(object sender, EventArgs e) { // https://stackoverflow.com/a/31407415/294248 // After the window's size has been established to minimally wrap the elements, // we set the minimum width to the current width, and un-freeze the preview image @@ -284,6 +296,8 @@ namespace SourceGen.WpfGui { NewVis = new Visualization(trimTag, item.VisDescriptor.Ident, valueDict); NewVis.CachedImage = (BitmapSource)previewImage.Source; + sLastVisIdent = NewVis.VisGenIdent; + DialogResult = true; }