/* * Copyright 2019 faddenSoft * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.ComponentModel; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Windows; using System.Windows.Controls; using Asm65; using CharConvMode = Asm65.Formatter.FormatConfig.CharConvMode; namespace SourceGen.Tools.WpfGui { /// /// Hex dump viewer. /// public partial class HexDumpViewer : Window, INotifyPropertyChanged { /// /// Maximum length of data we will display. /// public const int MAX_LENGTH = 1 << 24; /// /// ItemsSource for list. /// public VirtualHexDump HexDumpLines { get; private set; } /// /// Formatter that handles the actual string formatting. /// /// There's currently no way to update this after the dialog is opened, which means /// we won't track changes to hex case preference if the app settings are updated. /// I'm okay with that. /// private Formatter mFormatter; /// /// If true, don't include non-ASCII characters in text area. (Without this we might /// use Unicode bullets or other glyphs for unprintable text.) Bound to a CheckBox. /// public bool AsciiOnlyDump { get { return mAsciiOnlyDump; } set { mAsciiOnlyDump = value; OnPropertyChanged(); ReplaceFormatter(); } } private bool mAsciiOnlyDump; // INotifyPropertyChanged implementation public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged([CallerMemberName] string propertyName = "") { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } /// /// Character conversion combo box item. /// public class CharConvItem { public string Name { get; private set; } public CharConvMode Mode { get; private set; } public CharConvItem(string name, CharConvMode mode) { Name = name; Mode = mode; } } public CharConvItem[] CharConvItems { get; private set; } public HexDumpViewer(Window owner, byte[] data, Formatter formatter) { InitializeComponent(); Owner = owner; DataContext = this; Debug.Assert(data.Length <= MAX_LENGTH); HexDumpLines = new VirtualHexDump(data, formatter); mFormatter = formatter; CharConvItems = new CharConvItem[] { new CharConvItem((string)FindResource("str_AsciiOnly"), CharConvMode.Ascii), new CharConvItem((string)FindResource("str_LowHighAscii"), CharConvMode.LowHighAscii), new CharConvItem((string)FindResource("str_C64Petscii"), CharConvMode.C64Petscii), new CharConvItem((string)FindResource("str_C64ScreenCode"), CharConvMode.C64ScreenCode), }; } private void Window_Loaded(object sender, RoutedEventArgs e) { // Restore ASCII-only setting. AsciiOnlyDump = AppSettings.Global.GetBool(AppSettings.HEXD_ASCII_ONLY, false); // Restore conv mode setting. CharConvMode mode = (CharConvMode)AppSettings.Global.GetEnum( AppSettings.HEXD_CHAR_CONV, typeof(CharConvMode), (int)CharConvMode.Ascii); int index = 0; for (int i = 0; i < CharConvItems.Length; i++) { if (CharConvItems[i].Mode == mode) { index = i; break; } } charConvComboBox.SelectedIndex = index; } /// /// Sets the filename associated with the data. This is for display purposes only. /// public void SetFileName(string fileName) { Title = fileName; } private void CharConvComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { ReplaceFormatter(); } private void ReplaceFormatter() { Formatter.FormatConfig config = mFormatter.Config; CharConvItem item = (CharConvItem)charConvComboBox.SelectedItem; if (item == null) { // initializing return; } config.mHexDumpCharConvMode = item.Mode; config.mHexDumpAsciiOnly = AsciiOnlyDump; // Keep app settings up to date. AppSettings.Global.SetBool(AppSettings.HEXD_ASCII_ONLY, mAsciiOnlyDump); AppSettings.Global.SetEnum(AppSettings.HEXD_CHAR_CONV, typeof(CharConvMode), (int)item.Mode); mFormatter = new Formatter(config); HexDumpLines.Reformat(mFormatter); } /// /// Sets the scroll position and selection to show the specified range. /// /// First offset to show. /// Last offset to show. public void ShowOffsetRange(int startOffset, int endOffset) { Debug.WriteLine("HexDumpViewer: show +" + startOffset.ToString("x6") + " - +" + endOffset.ToString("x6")); int startLine = startOffset / 16; int endLine = endOffset / 16; hexDumpData.SelectedItems.Clear(); for (int i = startLine; i <= endLine; i++) { hexDumpData.SelectedItems.Add(HexDumpLines[i]); } // Make sure it's visible. hexDumpData.ScrollIntoView(HexDumpLines[endLine]); hexDumpData.ScrollIntoView(HexDumpLines[startLine]); hexDumpData.Focus(); } #if false // DataGrid provides this automatically /// /// Generates a string for every selected line, then copies the full thing to the /// clipboard. /// private void CopySelectionToClipboard() { ListView.SelectedIndexCollection indices = hexDumpListView.SelectedIndices; if (indices.Count == 0) { Debug.WriteLine("Nothing selected"); return; } // Try to make the initial allocation big enough to hold the full thing. // Each line is currently 73 bytes, plus we throw in a CRLF. Doesn't have to // be exact. With a 16MB max file size we're creating a ~75MB string for the // clipboard, which .NET and Win10-64 seem to be able to handle. StringBuilder sb = new StringBuilder(indices.Count * (73 + 2)); try { Application.UseWaitCursor = true; Cursor.Current = Cursors.WaitCursor; foreach (int index in indices) { mFormatter.FormatHexDump(mData, index * 16, sb); sb.Append("\r\n"); } } finally { Application.UseWaitCursor = false; Cursor.Current = Cursors.Arrow; } Clipboard.SetText(sb.ToString(), TextDataFormat.Text); } #endif } }