mirror of
https://github.com/SourMesen/Mesen.git
synced 2025-04-02 10:52:48 -04:00
Debugger: Replaced custom hex editor with HexBox
This commit is contained in:
parent
b95c18b729
commit
9c2f6995c2
24 changed files with 7025 additions and 151 deletions
214
GUI.NET/Debugger/Controls/ctrlHexViewer.Designer.cs
generated
214
GUI.NET/Debugger/Controls/ctrlHexViewer.Designer.cs
generated
|
@ -29,51 +29,55 @@
|
|||
{
|
||||
this.components = new System.ComponentModel.Container();
|
||||
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
|
||||
this.ctrlDataViewer = new Mesen.GUI.Debugger.ctrlScrollableTextbox();
|
||||
this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel();
|
||||
this.lblNumberOfColumns = new System.Windows.Forms.Label();
|
||||
this.cboNumberColumns = new System.Windows.Forms.ComboBox();
|
||||
this.panelSearch = new System.Windows.Forms.Panel();
|
||||
this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel();
|
||||
this.picCloseSearch = new System.Windows.Forms.PictureBox();
|
||||
this.picSearchNext = new System.Windows.Forms.PictureBox();
|
||||
this.picSearchPrevious = new System.Windows.Forms.PictureBox();
|
||||
this.cboSearch = new System.Windows.Forms.ComboBox();
|
||||
this.lblSearchWarning = new System.Windows.Forms.Label();
|
||||
this.flowLayoutPanel2 = new System.Windows.Forms.FlowLayoutPanel();
|
||||
this.chkTextSearch = new System.Windows.Forms.CheckBox();
|
||||
this.chkMatchCase = new System.Windows.Forms.CheckBox();
|
||||
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
||||
this.ctrlHexBox = new Be.Windows.Forms.HexBox();
|
||||
this.tableLayoutPanel1.SuspendLayout();
|
||||
this.flowLayoutPanel1.SuspendLayout();
|
||||
this.panelSearch.SuspendLayout();
|
||||
this.tableLayoutPanel2.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.picCloseSearch)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.picSearchNext)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.picSearchPrevious)).BeginInit();
|
||||
this.flowLayoutPanel2.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// tableLayoutPanel1
|
||||
//
|
||||
this.tableLayoutPanel1.ColumnCount = 1;
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||
this.tableLayoutPanel1.Controls.Add(this.ctrlDataViewer, 0, 1);
|
||||
this.tableLayoutPanel1.Controls.Add(this.flowLayoutPanel1, 0, 0);
|
||||
this.tableLayoutPanel1.Controls.Add(this.panelSearch, 0, 2);
|
||||
this.tableLayoutPanel1.Controls.Add(this.ctrlHexBox, 0, 1);
|
||||
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
|
||||
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
|
||||
this.tableLayoutPanel1.RowCount = 2;
|
||||
this.tableLayoutPanel1.RowCount = 3;
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||
this.tableLayoutPanel1.Size = new System.Drawing.Size(191, 109);
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||
this.tableLayoutPanel1.Size = new System.Drawing.Size(543, 309);
|
||||
this.tableLayoutPanel1.TabIndex = 0;
|
||||
//
|
||||
// ctrlDataViewer
|
||||
//
|
||||
this.ctrlDataViewer.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||
this.ctrlDataViewer.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.ctrlDataViewer.FontSize = 13F;
|
||||
this.ctrlDataViewer.Location = new System.Drawing.Point(3, 30);
|
||||
this.ctrlDataViewer.Name = "ctrlDataViewer";
|
||||
this.ctrlDataViewer.ShowContentNotes = false;
|
||||
this.ctrlDataViewer.ShowLineNumberNotes = false;
|
||||
this.ctrlDataViewer.Size = new System.Drawing.Size(185, 76);
|
||||
this.ctrlDataViewer.TabIndex = 0;
|
||||
this.ctrlDataViewer.MouseMove += new System.Windows.Forms.MouseEventHandler(this.ctrlDataViewer_MouseMove);
|
||||
this.ctrlDataViewer.FontSizeChanged += new System.EventHandler(this.ctrlDataViewer_FontSizeChanged);
|
||||
//
|
||||
// flowLayoutPanel1
|
||||
//
|
||||
this.flowLayoutPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.flowLayoutPanel1.AutoSize = true;
|
||||
this.flowLayoutPanel1.Controls.Add(this.lblNumberOfColumns);
|
||||
this.flowLayoutPanel1.Controls.Add(this.cboNumberColumns);
|
||||
this.flowLayoutPanel1.Location = new System.Drawing.Point(27, 0);
|
||||
this.flowLayoutPanel1.Location = new System.Drawing.Point(379, 0);
|
||||
this.flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(0);
|
||||
this.flowLayoutPanel1.Name = "flowLayoutPanel1";
|
||||
this.flowLayoutPanel1.Size = new System.Drawing.Size(164, 27);
|
||||
|
@ -105,17 +109,175 @@
|
|||
this.cboNumberColumns.TabIndex = 1;
|
||||
this.cboNumberColumns.SelectedIndexChanged += new System.EventHandler(this.cboNumberColumns_SelectedIndexChanged);
|
||||
//
|
||||
// panelSearch
|
||||
//
|
||||
this.panelSearch.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||
this.panelSearch.Controls.Add(this.tableLayoutPanel2);
|
||||
this.panelSearch.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.panelSearch.Location = new System.Drawing.Point(3, 281);
|
||||
this.panelSearch.Margin = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||
this.panelSearch.Name = "panelSearch";
|
||||
this.panelSearch.Size = new System.Drawing.Size(537, 28);
|
||||
this.panelSearch.TabIndex = 3;
|
||||
this.panelSearch.Visible = false;
|
||||
//
|
||||
// tableLayoutPanel2
|
||||
//
|
||||
this.tableLayoutPanel2.ColumnCount = 6;
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 250F));
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||
this.tableLayoutPanel2.Controls.Add(this.picCloseSearch, 3, 0);
|
||||
this.tableLayoutPanel2.Controls.Add(this.picSearchNext, 2, 0);
|
||||
this.tableLayoutPanel2.Controls.Add(this.picSearchPrevious, 1, 0);
|
||||
this.tableLayoutPanel2.Controls.Add(this.cboSearch, 0, 0);
|
||||
this.tableLayoutPanel2.Controls.Add(this.lblSearchWarning, 4, 0);
|
||||
this.tableLayoutPanel2.Controls.Add(this.flowLayoutPanel2, 5, 0);
|
||||
this.tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.tableLayoutPanel2.Location = new System.Drawing.Point(0, 0);
|
||||
this.tableLayoutPanel2.Margin = new System.Windows.Forms.Padding(0);
|
||||
this.tableLayoutPanel2.Name = "tableLayoutPanel2";
|
||||
this.tableLayoutPanel2.RowCount = 2;
|
||||
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||
this.tableLayoutPanel2.Size = new System.Drawing.Size(535, 26);
|
||||
this.tableLayoutPanel2.TabIndex = 0;
|
||||
//
|
||||
// picCloseSearch
|
||||
//
|
||||
this.picCloseSearch.Anchor = System.Windows.Forms.AnchorStyles.None;
|
||||
this.picCloseSearch.Cursor = System.Windows.Forms.Cursors.Hand;
|
||||
this.picCloseSearch.Image = global::Mesen.GUI.Properties.Resources.Close;
|
||||
this.picCloseSearch.Location = new System.Drawing.Point(297, 5);
|
||||
this.picCloseSearch.Name = "picCloseSearch";
|
||||
this.picCloseSearch.Size = new System.Drawing.Size(16, 16);
|
||||
this.picCloseSearch.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
|
||||
this.picCloseSearch.TabIndex = 3;
|
||||
this.picCloseSearch.TabStop = false;
|
||||
this.picCloseSearch.Click += new System.EventHandler(this.picCloseSearch_Click);
|
||||
//
|
||||
// picSearchNext
|
||||
//
|
||||
this.picSearchNext.Anchor = System.Windows.Forms.AnchorStyles.None;
|
||||
this.picSearchNext.Cursor = System.Windows.Forms.Cursors.Hand;
|
||||
this.picSearchNext.Image = global::Mesen.GUI.Properties.Resources.NextArrow;
|
||||
this.picSearchNext.Location = new System.Drawing.Point(275, 5);
|
||||
this.picSearchNext.Name = "picSearchNext";
|
||||
this.picSearchNext.Size = new System.Drawing.Size(16, 16);
|
||||
this.picSearchNext.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
|
||||
this.picSearchNext.TabIndex = 2;
|
||||
this.picSearchNext.TabStop = false;
|
||||
this.picSearchNext.MouseUp += new System.Windows.Forms.MouseEventHandler(this.picSearchNext_MouseUp);
|
||||
//
|
||||
// picSearchPrevious
|
||||
//
|
||||
this.picSearchPrevious.Anchor = System.Windows.Forms.AnchorStyles.None;
|
||||
this.picSearchPrevious.Cursor = System.Windows.Forms.Cursors.Hand;
|
||||
this.picSearchPrevious.Image = global::Mesen.GUI.Properties.Resources.PreviousArrow;
|
||||
this.picSearchPrevious.Location = new System.Drawing.Point(253, 5);
|
||||
this.picSearchPrevious.Name = "picSearchPrevious";
|
||||
this.picSearchPrevious.Size = new System.Drawing.Size(16, 16);
|
||||
this.picSearchPrevious.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
|
||||
this.picSearchPrevious.TabIndex = 1;
|
||||
this.picSearchPrevious.TabStop = false;
|
||||
this.picSearchPrevious.MouseUp += new System.Windows.Forms.MouseEventHandler(this.picSearchPrevious_MouseUp);
|
||||
//
|
||||
// cboSearch
|
||||
//
|
||||
this.cboSearch.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.cboSearch.FormattingEnabled = true;
|
||||
this.cboSearch.Location = new System.Drawing.Point(3, 3);
|
||||
this.cboSearch.Name = "cboSearch";
|
||||
this.cboSearch.Size = new System.Drawing.Size(244, 21);
|
||||
this.cboSearch.TabIndex = 4;
|
||||
this.cboSearch.TextUpdate += new System.EventHandler(this.cboSearch_TextUpdate);
|
||||
this.cboSearch.KeyDown += new System.Windows.Forms.KeyEventHandler(this.cboSearch_KeyDown);
|
||||
//
|
||||
// lblSearchWarning
|
||||
//
|
||||
this.lblSearchWarning.Anchor = System.Windows.Forms.AnchorStyles.Left;
|
||||
this.lblSearchWarning.AutoSize = true;
|
||||
this.lblSearchWarning.ForeColor = System.Drawing.Color.Red;
|
||||
this.lblSearchWarning.Location = new System.Drawing.Point(319, 7);
|
||||
this.lblSearchWarning.Name = "lblSearchWarning";
|
||||
this.lblSearchWarning.Size = new System.Drawing.Size(0, 13);
|
||||
this.lblSearchWarning.TabIndex = 6;
|
||||
//
|
||||
// flowLayoutPanel2
|
||||
//
|
||||
this.flowLayoutPanel2.Controls.Add(this.chkTextSearch);
|
||||
this.flowLayoutPanel2.Controls.Add(this.chkMatchCase);
|
||||
this.flowLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.flowLayoutPanel2.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft;
|
||||
this.flowLayoutPanel2.Location = new System.Drawing.Point(322, 2);
|
||||
this.flowLayoutPanel2.Margin = new System.Windows.Forms.Padding(0, 2, 0, 0);
|
||||
this.flowLayoutPanel2.Name = "flowLayoutPanel2";
|
||||
this.flowLayoutPanel2.Size = new System.Drawing.Size(213, 25);
|
||||
this.flowLayoutPanel2.TabIndex = 7;
|
||||
//
|
||||
// chkTextSearch
|
||||
//
|
||||
this.chkTextSearch.Anchor = System.Windows.Forms.AnchorStyles.Right;
|
||||
this.chkTextSearch.AutoSize = true;
|
||||
this.chkTextSearch.Location = new System.Drawing.Point(128, 3);
|
||||
this.chkTextSearch.Name = "chkTextSearch";
|
||||
this.chkTextSearch.Size = new System.Drawing.Size(82, 17);
|
||||
this.chkTextSearch.TabIndex = 5;
|
||||
this.chkTextSearch.Text = "Text search";
|
||||
this.chkTextSearch.UseVisualStyleBackColor = true;
|
||||
this.chkTextSearch.CheckedChanged += new System.EventHandler(this.chkTextSearch_CheckedChanged);
|
||||
//
|
||||
// chkMatchCase
|
||||
//
|
||||
this.chkMatchCase.Anchor = System.Windows.Forms.AnchorStyles.Right;
|
||||
this.chkMatchCase.AutoSize = true;
|
||||
this.chkMatchCase.Location = new System.Drawing.Point(39, 3);
|
||||
this.chkMatchCase.Name = "chkMatchCase";
|
||||
this.chkMatchCase.Size = new System.Drawing.Size(83, 17);
|
||||
this.chkMatchCase.TabIndex = 6;
|
||||
this.chkMatchCase.Text = "Match Case";
|
||||
this.chkMatchCase.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// ctrlDataViewer
|
||||
//
|
||||
this.ctrlHexBox.ColumnInfoVisible = true;
|
||||
this.ctrlHexBox.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.ctrlHexBox.Font = new System.Drawing.Font("Consolas", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.ctrlHexBox.InfoBackColor = System.Drawing.Color.DarkGray;
|
||||
this.ctrlHexBox.LineInfoVisible = true;
|
||||
this.ctrlHexBox.Location = new System.Drawing.Point(3, 30);
|
||||
this.ctrlHexBox.Name = "ctrlHexBox";
|
||||
this.ctrlHexBox.ReadOnly = true;
|
||||
this.ctrlHexBox.SelectionBackColor = System.Drawing.Color.RoyalBlue;
|
||||
this.ctrlHexBox.ShadowSelectionColor = System.Drawing.Color.Orange;
|
||||
this.ctrlHexBox.Size = new System.Drawing.Size(537, 248);
|
||||
this.ctrlHexBox.StringViewVisible = true;
|
||||
this.ctrlHexBox.TabIndex = 2;
|
||||
this.ctrlHexBox.UseFixedBytesPerLine = true;
|
||||
this.ctrlHexBox.VScrollBarVisible = true;
|
||||
//
|
||||
// ctrlHexViewer
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.Controls.Add(this.tableLayoutPanel1);
|
||||
this.Name = "ctrlHexViewer";
|
||||
this.Size = new System.Drawing.Size(191, 109);
|
||||
this.Size = new System.Drawing.Size(543, 309);
|
||||
this.tableLayoutPanel1.ResumeLayout(false);
|
||||
this.tableLayoutPanel1.PerformLayout();
|
||||
this.flowLayoutPanel1.ResumeLayout(false);
|
||||
this.flowLayoutPanel1.PerformLayout();
|
||||
this.panelSearch.ResumeLayout(false);
|
||||
this.tableLayoutPanel2.ResumeLayout(false);
|
||||
this.tableLayoutPanel2.PerformLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.picCloseSearch)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.picSearchNext)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.picSearchPrevious)).EndInit();
|
||||
this.flowLayoutPanel2.ResumeLayout(false);
|
||||
this.flowLayoutPanel2.PerformLayout();
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
@ -123,10 +285,20 @@
|
|||
#endregion
|
||||
|
||||
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
|
||||
private ctrlScrollableTextbox ctrlDataViewer;
|
||||
private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1;
|
||||
private System.Windows.Forms.Label lblNumberOfColumns;
|
||||
private System.Windows.Forms.ComboBox cboNumberColumns;
|
||||
private System.Windows.Forms.ToolTip toolTip;
|
||||
private Be.Windows.Forms.HexBox ctrlHexBox;
|
||||
private System.Windows.Forms.Panel panelSearch;
|
||||
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2;
|
||||
private System.Windows.Forms.PictureBox picCloseSearch;
|
||||
private System.Windows.Forms.PictureBox picSearchNext;
|
||||
private System.Windows.Forms.PictureBox picSearchPrevious;
|
||||
private System.Windows.Forms.ComboBox cboSearch;
|
||||
private System.Windows.Forms.CheckBox chkTextSearch;
|
||||
private System.Windows.Forms.Label lblSearchWarning;
|
||||
private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel2;
|
||||
private System.Windows.Forms.CheckBox chkMatchCase;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,61 +8,58 @@ using System.Text;
|
|||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Mesen.GUI.Config;
|
||||
using Be.Windows.Forms;
|
||||
using Mesen.GUI.Controls;
|
||||
|
||||
namespace Mesen.GUI.Debugger.Controls
|
||||
{
|
||||
public partial class ctrlHexViewer : BaseScrollableTextboxUserControl
|
||||
public partial class ctrlHexViewer : UserControl
|
||||
{
|
||||
public event EventHandler ColumnCountChanged;
|
||||
|
||||
private int _currentColumnCount;
|
||||
private byte[] _data;
|
||||
private FindOptions _findOptions;
|
||||
private StaticByteProvider _byteProvider;
|
||||
|
||||
public ctrlHexViewer()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
this.ctrlHexBox.Font = new Font(BaseControl.MonospaceFontFamily, 10, FontStyle.Regular);
|
||||
this.ctrlHexBox.SelectionForeColor = Color.White;
|
||||
this.ctrlHexBox.SelectionBackColor = Color.FromArgb(31, 123, 205);
|
||||
this.ctrlHexBox.ChangeColor = Color.Red;
|
||||
this.ctrlHexBox.SelectionChangeColor = Color.FromArgb(255, 125, 125);
|
||||
this.ctrlHexBox.ShadowSelectionColor = Color.FromArgb(100, 60, 128, 200);
|
||||
this.ctrlHexBox.InfoBackColor = Color.FromArgb(235, 235, 235);
|
||||
this.ctrlHexBox.InfoForeColor = Color.Gray;
|
||||
|
||||
if(LicenseManager.UsageMode != LicenseUsageMode.Designtime) {
|
||||
this.cboNumberColumns.SelectedIndex = ConfigManager.Config.DebugInfo.RamColumnCount;
|
||||
}
|
||||
}
|
||||
|
||||
protected override ctrlScrollableTextbox ScrollableTextbox
|
||||
public byte[] GetData()
|
||||
{
|
||||
get { return this.ctrlDataViewer; }
|
||||
return this._byteProvider != null ? this._byteProvider.Bytes.ToArray() : new byte[0];
|
||||
}
|
||||
|
||||
[Browsable(false)]
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
public byte[] Data
|
||||
|
||||
public void SetData(byte[] data, bool clearHistory)
|
||||
{
|
||||
get { return this._data; }
|
||||
set
|
||||
{
|
||||
if(value != null) {
|
||||
if(_currentColumnCount != this.ColumnCount) {
|
||||
_currentColumnCount = this.ColumnCount;
|
||||
if(data != null) {
|
||||
bool changed = true;
|
||||
if(this._byteProvider != null && data.Length == this._byteProvider.Bytes.Count) {
|
||||
changed = false;
|
||||
for(int i = 0; i < this._byteProvider.Bytes.Count; i++) {
|
||||
if(this._byteProvider.Bytes[i] != data[i]) {
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string[] hexContent, previousHexContent = null;
|
||||
int[] lineNumbers, previousLineNumbers;
|
||||
if(this._data != null && this._data.Length == value.Length) {
|
||||
this.ArrayToHex(this._data, out previousHexContent, out previousLineNumbers);
|
||||
}
|
||||
this.ArrayToHex(value, out hexContent, out lineNumbers);
|
||||
this.ctrlDataViewer.CompareLines = previousHexContent;
|
||||
this.ctrlDataViewer.TextLines = hexContent;
|
||||
|
||||
this._data = value;
|
||||
this.ctrlDataViewer.LineNumbers = lineNumbers;
|
||||
if(this.ColumnCount == 16) {
|
||||
this.ctrlDataViewer.Header = "00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F";
|
||||
} else if(this.ColumnCount == 32) {
|
||||
this.ctrlDataViewer.Header = "00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F";
|
||||
} else if(this.ColumnCount == 64) {
|
||||
this.ctrlDataViewer.Header = "00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F";
|
||||
} else {
|
||||
this.ctrlDataViewer.Header = null;
|
||||
if(changed) {
|
||||
_byteProvider = new StaticByteProvider(data);
|
||||
this.ctrlHexBox.ByteProvider = _byteProvider;
|
||||
if(clearHistory) {
|
||||
this.ctrlHexBox.ClearHistory();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -73,101 +70,215 @@ namespace Mesen.GUI.Debugger.Controls
|
|||
get { return Int32.Parse(this.cboNumberColumns.Text); }
|
||||
}
|
||||
|
||||
public int IdealWidth
|
||||
public int RequiredWidth
|
||||
{
|
||||
get
|
||||
{
|
||||
return 145 + this.ColumnCount * 30;
|
||||
}
|
||||
get { return this.ctrlHexBox.RequiredWidth; }
|
||||
}
|
||||
|
||||
private string[] _lookupTable = new string[] {
|
||||
"00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0A", "0B", "0C", "0D", "0E", "0F",
|
||||
"10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1A", "1B", "1C", "1D", "1E", "1F",
|
||||
"20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2A", "2B", "2C", "2D", "2E", "2F",
|
||||
"30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3A", "3B", "3C", "3D", "3E", "3F",
|
||||
"40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4A", "4B", "4C", "4D", "4E", "4F",
|
||||
"50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5A", "5B", "5C", "5D", "5E", "5F",
|
||||
"60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6A", "6B", "6C", "6D", "6E", "6F",
|
||||
"70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7A", "7B", "7C", "7D", "7E", "7F",
|
||||
"80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8A", "8B", "8C", "8D", "8E", "8F",
|
||||
"90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9A", "9B", "9C", "9D", "9E", "9F",
|
||||
"A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "AA", "AB", "AC", "AD", "AE", "AF",
|
||||
"B0", "B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF",
|
||||
"C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CA", "CB", "CC", "CD", "CE", "CF",
|
||||
"D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF",
|
||||
"E0", "E1", "E2", "E3", "E4", "E5", "E6", "E7", "E8", "E9", "EA", "EB", "EC", "ED", "EE", "EF",
|
||||
"F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB", "FC", "FD", "FE", "FF",
|
||||
};
|
||||
|
||||
private void ArrayToHex(byte[] data, out string[] hexContent, out int[] lineNumbers)
|
||||
{
|
||||
int columnCount = this.ColumnCount;
|
||||
int lineNumber = 0;
|
||||
|
||||
int rowCount = data.Length / columnCount;
|
||||
if(data.Length % columnCount != 0) {
|
||||
rowCount++;
|
||||
}
|
||||
|
||||
hexContent = new string[rowCount];
|
||||
lineNumbers = new int[rowCount];
|
||||
|
||||
StringBuilder sb = new StringBuilder(columnCount * 4);
|
||||
int columnCounter = 0;
|
||||
int lineCounter = 0;
|
||||
foreach(byte b in data) {
|
||||
sb.Append(_lookupTable[b]);
|
||||
sb.Append(" ");
|
||||
|
||||
columnCounter++;
|
||||
if(columnCounter == columnCount) {
|
||||
hexContent[lineCounter] = sb.ToString();
|
||||
lineNumbers[lineCounter] = lineNumber;
|
||||
sb.Clear();
|
||||
columnCounter = 0;
|
||||
lineCounter++;
|
||||
lineNumber += columnCount;
|
||||
}
|
||||
}
|
||||
if(sb.Length > 0) {
|
||||
hexContent[lineCounter] = sb.ToString();
|
||||
lineNumbers[lineCounter] = lineNumber;
|
||||
}
|
||||
}
|
||||
|
||||
private void cboNumberColumns_SelectedIndexChanged(object sender, EventArgs e)
|
||||
{
|
||||
if(this.ColumnCountChanged != null) {
|
||||
this.ColumnCountChanged(sender, e);
|
||||
}
|
||||
this.Data = _data;
|
||||
this.ctrlDataViewer.Focus();
|
||||
this.ctrlHexBox.Focus();
|
||||
|
||||
this.ctrlHexBox.BytesPerLine = this.ColumnCount;
|
||||
this.ctrlHexBox.UseFixedBytesPerLine = true;
|
||||
|
||||
ConfigManager.Config.DebugInfo.RamColumnCount = this.cboNumberColumns.SelectedIndex;
|
||||
ConfigManager.ApplyChanges();
|
||||
}
|
||||
|
||||
Point _previousLocation;
|
||||
private void ctrlDataViewer_MouseMove(object sender, MouseEventArgs e)
|
||||
public Font HexFont
|
||||
{
|
||||
if(_previousLocation != e.Location) {
|
||||
string currentWord = this.GetWordUnderLocation(e.Location, false);
|
||||
string originalWord = this.GetWordUnderLocation(e.Location, true);
|
||||
get { return this.ctrlHexBox.Font; }
|
||||
}
|
||||
|
||||
if(currentWord != originalWord) {
|
||||
this.toolTip.Show("Previous Value: $" + originalWord + Environment.NewLine + "Current Value: $" + currentWord, this.ctrlDataViewer, e.Location.X + 5, e.Location.Y - 40, 3000);
|
||||
} else {
|
||||
this.toolTip.Hide(this.ctrlDataViewer);
|
||||
}
|
||||
_previousLocation = e.Location;
|
||||
public void IncreaseFontSize()
|
||||
{
|
||||
this.SetFontSize(Math.Min(40, this.ctrlHexBox.Font.Size + 1));
|
||||
}
|
||||
|
||||
public void DecreaseFontSize()
|
||||
{
|
||||
this.SetFontSize(Math.Max(6, this.ctrlHexBox.Font.Size - 1));
|
||||
}
|
||||
|
||||
public void SetFontSize(float size)
|
||||
{
|
||||
this.ctrlHexBox.Font = new Font(BaseControl.MonospaceFontFamily, size);
|
||||
}
|
||||
|
||||
public void ResetFontSize()
|
||||
{
|
||||
this.SetFontSize(BaseControl.DefaultFontSize);
|
||||
}
|
||||
|
||||
public void GoToAddress()
|
||||
{
|
||||
GoToAddress address = new GoToAddress();
|
||||
|
||||
int currentAddr = (int)(this.ctrlHexBox.CurrentLine - 1) * this.ctrlHexBox.BytesPerLine;
|
||||
address.Address = (UInt32)currentAddr;
|
||||
|
||||
frmGoToLine frm = new frmGoToLine(address);
|
||||
frm.StartPosition = FormStartPosition.Manual;
|
||||
Point topLeft = this.PointToScreen(new Point(0, 0));
|
||||
frm.Location = new Point(topLeft.X + (this.Width - frm.Width) / 2, topLeft.Y + (this.Height - frm.Height) / 2);
|
||||
if(frm.ShowDialog() == DialogResult.OK) {
|
||||
this.ctrlHexBox.ScrollByteIntoView((int)address.Address);
|
||||
this.ctrlHexBox.Focus();
|
||||
}
|
||||
}
|
||||
|
||||
private void ctrlDataViewer_FontSizeChanged(object sender, EventArgs e)
|
||||
public void OpenSearchBox(bool forceFocus = false)
|
||||
{
|
||||
ConfigManager.Config.DebugInfo.RamFontSize = this.ctrlDataViewer.FontSize;
|
||||
ConfigManager.ApplyChanges();
|
||||
this._findOptions = new Be.Windows.Forms.FindOptions();
|
||||
this._findOptions.Type = chkTextSearch.Checked ? FindType.Text : FindType.Hex;
|
||||
this._findOptions.MatchCase = false;
|
||||
this._findOptions.Text = this.cboSearch.Text;
|
||||
this._findOptions.WrapSearch = true;
|
||||
|
||||
bool focus = !this.panelSearch.Visible;
|
||||
this.panelSearch.Visible = true;
|
||||
if(focus || forceFocus) {
|
||||
this.cboSearch.Focus();
|
||||
this.cboSearch.SelectAll();
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseSearchBox()
|
||||
{
|
||||
this.panelSearch.Visible = false;
|
||||
this.Focus();
|
||||
}
|
||||
|
||||
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
|
||||
{
|
||||
switch(keyData) {
|
||||
case Keys.Control | Keys.F: this.OpenSearchBox(true); return true;
|
||||
case Keys.Escape: this.CloseSearchBox(); return true;
|
||||
case Keys.Control | Keys.Oemplus: this.IncreaseFontSize(); return true;
|
||||
case Keys.Control | Keys.OemMinus: this.DecreaseFontSize(); return true;
|
||||
case Keys.Control | Keys.D0: this.ResetFontSize(); return true;
|
||||
}
|
||||
|
||||
return base.ProcessCmdKey(ref msg, keyData);
|
||||
}
|
||||
|
||||
public void FindNext()
|
||||
{
|
||||
this.OpenSearchBox();
|
||||
if(this.UpdateSearchOptions()) {
|
||||
if(this.ctrlHexBox.Find(this._findOptions, HexBox.eSearchDirection.Next) == -1) {
|
||||
this.lblSearchWarning.Text = "No matches found!";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void FindPrevious()
|
||||
{
|
||||
this.OpenSearchBox();
|
||||
if(this.UpdateSearchOptions()) {
|
||||
if(this.ctrlHexBox.Find(this._findOptions, HexBox.eSearchDirection.Previous) == -1) {
|
||||
this.lblSearchWarning.Text = "No matches found!";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void picCloseSearch_Click(object sender, EventArgs e)
|
||||
{
|
||||
this.CloseSearchBox();
|
||||
}
|
||||
|
||||
private void picSearchPrevious_MouseUp(object sender, MouseEventArgs e)
|
||||
{
|
||||
this.FindPrevious();
|
||||
}
|
||||
|
||||
private void picSearchNext_MouseUp(object sender, MouseEventArgs e)
|
||||
{
|
||||
this.FindNext();
|
||||
}
|
||||
|
||||
private byte[] GetByteArray(string hexText, ref bool hasWildcard)
|
||||
{
|
||||
hexText = hexText.Replace(" ", "");
|
||||
|
||||
try {
|
||||
List<byte> bytes = new List<byte>(hexText.Length/2);
|
||||
for(int i = 0; i < hexText.Length; i+=2) {
|
||||
if(i == hexText.Length - 1) {
|
||||
bytes.Add((byte)(Convert.ToByte(hexText.Substring(i, 1), 16) << 4));
|
||||
hasWildcard = true;
|
||||
} else {
|
||||
bytes.Add(Convert.ToByte(hexText.Substring(i, 2), 16));
|
||||
}
|
||||
}
|
||||
return bytes.ToArray();
|
||||
} catch {
|
||||
return new byte[0];
|
||||
}
|
||||
}
|
||||
|
||||
private bool UpdateSearchOptions()
|
||||
{
|
||||
bool invalidSearchString = false;
|
||||
|
||||
this._findOptions.MatchCase = this.chkMatchCase.Checked;
|
||||
|
||||
if(this.chkTextSearch.Checked) {
|
||||
this._findOptions.Type = FindType.Text;
|
||||
this._findOptions.Text = this.cboSearch.Text;
|
||||
this._findOptions.HasWildcard = false;
|
||||
} else {
|
||||
this._findOptions.Type = FindType.Hex;
|
||||
bool hasWildcard = false;
|
||||
this._findOptions.Hex = this.GetByteArray(this.cboSearch.Text, ref hasWildcard);
|
||||
this._findOptions.HasWildcard = hasWildcard;
|
||||
invalidSearchString = this._findOptions.Hex.Length == 0 && this.cboSearch.Text.Trim().Length > 0;
|
||||
}
|
||||
|
||||
this.lblSearchWarning.Text = "";
|
||||
|
||||
bool emptySearch = this._findOptions.Text.Length == 0 || (!this.chkTextSearch.Checked && (this._findOptions.Hex == null || this._findOptions.Hex.Length == 0));
|
||||
if(invalidSearchString) {
|
||||
this.lblSearchWarning.Text = "Invalid search string";
|
||||
} else if(!emptySearch) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void cboSearch_TextUpdate(object sender, EventArgs e)
|
||||
{
|
||||
if(this.UpdateSearchOptions()) {
|
||||
if(this.ctrlHexBox.Find(this._findOptions, HexBox.eSearchDirection.Incremental) == -1) {
|
||||
this.lblSearchWarning.Text = "No matches found!";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void cboSearch_KeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if(e.KeyCode == Keys.Enter) {
|
||||
this.FindNext();
|
||||
if(this.cboSearch.Items.Contains(this.cboSearch.Text)) {
|
||||
this.cboSearch.Items.Remove(this.cboSearch.Text);
|
||||
}
|
||||
this.cboSearch.Items.Insert(0, this.cboSearch.Text);
|
||||
|
||||
e.Handled = true;
|
||||
e.SuppressKeyPress = true;
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler RequiredWidthChanged
|
||||
{
|
||||
add { this.ctrlHexBox.RequiredWidthChanged += value; }
|
||||
remove { this.ctrlHexBox.RequiredWidthChanged -= value; }
|
||||
}
|
||||
|
||||
private void chkTextSearch_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
this.UpdateSearchOptions();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
220
GUI.NET/Debugger/HexBox/BuiltInContextMenu.cs
Normal file
220
GUI.NET/Debugger/HexBox/BuiltInContextMenu.cs
Normal file
|
@ -0,0 +1,220 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Drawing;
|
||||
using System.ComponentModel;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Be.Windows.Forms
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a build-in ContextMenuStrip manager for HexBox control to show Copy, Cut, Paste menu in contextmenu of the control.
|
||||
/// </summary>
|
||||
[TypeConverterAttribute(typeof(ExpandableObjectConverter))]
|
||||
public sealed class BuiltInContextMenu : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains the HexBox control.
|
||||
/// </summary>
|
||||
HexBox _hexBox;
|
||||
/// <summary>
|
||||
/// Contains the ContextMenuStrip control.
|
||||
/// </summary>
|
||||
ContextMenuStrip _contextMenuStrip;
|
||||
/// <summary>
|
||||
/// Contains the "Cut"-ToolStripMenuItem object.
|
||||
/// </summary>
|
||||
ToolStripMenuItem _cutToolStripMenuItem;
|
||||
/// <summary>
|
||||
/// Contains the "Copy"-ToolStripMenuItem object.
|
||||
/// </summary>
|
||||
ToolStripMenuItem _copyToolStripMenuItem;
|
||||
/// <summary>
|
||||
/// Contains the "Paste"-ToolStripMenuItem object.
|
||||
/// </summary>
|
||||
ToolStripMenuItem _pasteToolStripMenuItem;
|
||||
/// <summary>
|
||||
/// Contains the "Select All"-ToolStripMenuItem object.
|
||||
/// </summary>
|
||||
ToolStripMenuItem _selectAllToolStripMenuItem;
|
||||
/// <summary>
|
||||
/// Initializes a new instance of BuildInContextMenu class.
|
||||
/// </summary>
|
||||
/// <param name="hexBox">the HexBox control</param>
|
||||
internal BuiltInContextMenu(HexBox hexBox)
|
||||
{
|
||||
_hexBox = hexBox;
|
||||
_hexBox.ByteProviderChanged += new EventHandler(HexBox_ByteProviderChanged);
|
||||
}
|
||||
/// <summary>
|
||||
/// If ByteProvider
|
||||
/// </summary>
|
||||
/// <param name="sender">the sender object</param>
|
||||
/// <param name="e">the event data</param>
|
||||
void HexBox_ByteProviderChanged(object sender, EventArgs e)
|
||||
{
|
||||
CheckBuiltInContextMenu();
|
||||
}
|
||||
/// <summary>
|
||||
/// Assigns the ContextMenuStrip control to the HexBox control.
|
||||
/// </summary>
|
||||
void CheckBuiltInContextMenu()
|
||||
{
|
||||
if (Util.DesignMode)
|
||||
return;
|
||||
|
||||
if (this._contextMenuStrip == null)
|
||||
{
|
||||
ContextMenuStrip cms = new ContextMenuStrip();
|
||||
_cutToolStripMenuItem = new ToolStripMenuItem(CutMenuItemTextInternal, CutMenuItemImage, new EventHandler(CutMenuItem_Click));
|
||||
cms.Items.Add(_cutToolStripMenuItem);
|
||||
_copyToolStripMenuItem = new ToolStripMenuItem(CopyMenuItemTextInternal, CopyMenuItemImage, new EventHandler(CopyMenuItem_Click));
|
||||
cms.Items.Add(_copyToolStripMenuItem);
|
||||
_pasteToolStripMenuItem = new ToolStripMenuItem(PasteMenuItemTextInternal, PasteMenuItemImage, new EventHandler(PasteMenuItem_Click));
|
||||
cms.Items.Add(_pasteToolStripMenuItem);
|
||||
|
||||
cms.Items.Add(new ToolStripSeparator());
|
||||
|
||||
_selectAllToolStripMenuItem = new ToolStripMenuItem(SelectAllMenuItemTextInternal, SelectAllMenuItemImage, new EventHandler(SelectAllMenuItem_Click));
|
||||
cms.Items.Add(_selectAllToolStripMenuItem);
|
||||
cms.Opening += new CancelEventHandler(BuildInContextMenuStrip_Opening);
|
||||
|
||||
_contextMenuStrip = cms;
|
||||
}
|
||||
|
||||
if (this._hexBox.ByteProvider == null && this._hexBox.ContextMenuStrip == this._contextMenuStrip)
|
||||
this._hexBox.ContextMenuStrip = null;
|
||||
else if (this._hexBox.ByteProvider != null && this._hexBox.ContextMenuStrip == null)
|
||||
this._hexBox.ContextMenuStrip = _contextMenuStrip;
|
||||
}
|
||||
/// <summary>
|
||||
/// Before opening the ContextMenuStrip, we manage the availability of the items.
|
||||
/// </summary>
|
||||
/// <param name="sender">the sender object</param>
|
||||
/// <param name="e">the event data</param>
|
||||
void BuildInContextMenuStrip_Opening(object sender, CancelEventArgs e)
|
||||
{
|
||||
_cutToolStripMenuItem.Enabled = this._hexBox.CanCut();
|
||||
_copyToolStripMenuItem.Enabled = this._hexBox.CanCopy();
|
||||
_pasteToolStripMenuItem.Enabled = this._hexBox.CanPaste();
|
||||
_selectAllToolStripMenuItem.Enabled = this._hexBox.CanSelectAll();
|
||||
}
|
||||
/// <summary>
|
||||
/// The handler for the "Cut"-Click event
|
||||
/// </summary>
|
||||
/// <param name="sender">the sender object</param>
|
||||
/// <param name="e">the event data</param>
|
||||
void CutMenuItem_Click(object sender, EventArgs e) { this._hexBox.Cut(); }
|
||||
/// <summary>
|
||||
/// The handler for the "Copy"-Click event
|
||||
/// </summary>
|
||||
/// <param name="sender">the sender object</param>
|
||||
/// <param name="e">the event data</param>
|
||||
void CopyMenuItem_Click(object sender, EventArgs e) { this._hexBox.Copy(); }
|
||||
/// <summary>
|
||||
/// The handler for the "Paste"-Click event
|
||||
/// </summary>
|
||||
/// <param name="sender">the sender object</param>
|
||||
/// <param name="e">the event data</param>
|
||||
void PasteMenuItem_Click(object sender, EventArgs e) { this._hexBox.Paste(); }
|
||||
/// <summary>
|
||||
/// The handler for the "Select All"-Click event
|
||||
/// </summary>
|
||||
/// <param name="sender">the sender object</param>
|
||||
/// <param name="e">the event data</param>
|
||||
void SelectAllMenuItem_Click(object sender, EventArgs e) { this._hexBox.SelectAll(); }
|
||||
/// <summary>
|
||||
/// Gets or sets the custom text of the "Copy" ContextMenuStrip item.
|
||||
/// </summary>
|
||||
[Category("BuiltIn-ContextMenu"), DefaultValue(null), Localizable(true)]
|
||||
public string CopyMenuItemText
|
||||
{
|
||||
get { return _copyMenuItemText; }
|
||||
set { _copyMenuItemText = value; }
|
||||
} string _copyMenuItemText;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the custom text of the "Cut" ContextMenuStrip item.
|
||||
/// </summary>
|
||||
[Category("BuiltIn-ContextMenu"), DefaultValue(null), Localizable(true)]
|
||||
public string CutMenuItemText
|
||||
{
|
||||
get { return _cutMenuItemText; }
|
||||
set { _cutMenuItemText = value; }
|
||||
} string _cutMenuItemText;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the custom text of the "Paste" ContextMenuStrip item.
|
||||
/// </summary>
|
||||
[Category("BuiltIn-ContextMenu"), DefaultValue(null), Localizable(true)]
|
||||
public string PasteMenuItemText
|
||||
{
|
||||
get { return _pasteMenuItemText; }
|
||||
set { _pasteMenuItemText = value; }
|
||||
} string _pasteMenuItemText;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the custom text of the "Select All" ContextMenuStrip item.
|
||||
/// </summary>
|
||||
[Category("BuiltIn-ContextMenu"), DefaultValue(null), Localizable(true)]
|
||||
public string SelectAllMenuItemText
|
||||
{
|
||||
get { return _selectAllMenuItemText; }
|
||||
set { _selectAllMenuItemText = value; }
|
||||
} string _selectAllMenuItemText = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the text of the "Cut" ContextMenuStrip item.
|
||||
/// </summary>
|
||||
internal string CutMenuItemTextInternal { get { return !string.IsNullOrEmpty(CutMenuItemText) ? CutMenuItemText : "Cut"; } }
|
||||
/// <summary>
|
||||
/// Gets the text of the "Copy" ContextMenuStrip item.
|
||||
/// </summary>
|
||||
internal string CopyMenuItemTextInternal { get { return !string.IsNullOrEmpty(CopyMenuItemText) ? CopyMenuItemText : "Copy"; } }
|
||||
/// <summary>
|
||||
/// Gets the text of the "Paste" ContextMenuStrip item.
|
||||
/// </summary>
|
||||
internal string PasteMenuItemTextInternal { get { return !string.IsNullOrEmpty(PasteMenuItemText) ? PasteMenuItemText : "Paste"; } }
|
||||
/// <summary>
|
||||
/// Gets the text of the "Select All" ContextMenuStrip item.
|
||||
/// </summary>
|
||||
internal string SelectAllMenuItemTextInternal { get { return !string.IsNullOrEmpty(SelectAllMenuItemText) ? SelectAllMenuItemText : "Select All"; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the image of the "Cut" ContextMenuStrip item.
|
||||
/// </summary>
|
||||
[Category("BuiltIn-ContextMenu"), DefaultValue(null)]
|
||||
public Image CutMenuItemImage
|
||||
{
|
||||
get { return _cutMenuItemImage; }
|
||||
set { _cutMenuItemImage = value; }
|
||||
} Image _cutMenuItemImage = null;
|
||||
/// <summary>
|
||||
/// Gets or sets the image of the "Copy" ContextMenuStrip item.
|
||||
/// </summary>
|
||||
[Category("BuiltIn-ContextMenu"), DefaultValue(null)]
|
||||
public Image CopyMenuItemImage
|
||||
{
|
||||
get { return _copyMenuItemImage; }
|
||||
set { _copyMenuItemImage = value; }
|
||||
} Image _copyMenuItemImage = null;
|
||||
/// <summary>
|
||||
/// Gets or sets the image of the "Paste" ContextMenuStrip item.
|
||||
/// </summary>
|
||||
[Category("BuiltIn-ContextMenu"), DefaultValue(null)]
|
||||
public Image PasteMenuItemImage
|
||||
{
|
||||
get { return _pasteMenuItemImage; }
|
||||
set { _pasteMenuItemImage = value; }
|
||||
} Image _pasteMenuItemImage = null;
|
||||
/// <summary>
|
||||
/// Gets or sets the image of the "Select All" ContextMenuStrip item.
|
||||
/// </summary>
|
||||
[Category("BuiltIn-ContextMenu"), DefaultValue(null)]
|
||||
public Image SelectAllMenuItemImage
|
||||
{
|
||||
get { return _selectAllMenuItemImage; }
|
||||
set { _selectAllMenuItemImage = value; }
|
||||
} Image _selectAllMenuItemImage = null;
|
||||
}
|
||||
}
|
104
GUI.NET/Debugger/HexBox/ByteCharConverters.cs
Normal file
104
GUI.NET/Debugger/HexBox/ByteCharConverters.cs
Normal file
|
@ -0,0 +1,104 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Be.Windows.Forms
|
||||
{
|
||||
/// <summary>
|
||||
/// The interface for objects that can translate between characters and bytes.
|
||||
/// </summary>
|
||||
public interface IByteCharConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the character to display for the byte passed across.
|
||||
/// </summary>
|
||||
/// <param name="b"></param>
|
||||
/// <returns></returns>
|
||||
char ToChar(byte b);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the byte to use when the character passed across is entered during editing.
|
||||
/// </summary>
|
||||
/// <param name="c"></param>
|
||||
/// <returns></returns>
|
||||
byte ToByte(char c);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The default <see cref="IByteCharConverter"/> implementation.
|
||||
/// </summary>
|
||||
public class DefaultByteCharConverter : IByteCharConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the character to display for the byte passed across.
|
||||
/// </summary>
|
||||
/// <param name="b"></param>
|
||||
/// <returns></returns>
|
||||
public virtual char ToChar(byte b)
|
||||
{
|
||||
return b > 0x1F && !(b > 0x7E && b < 0xA0) ? (char)b : '.';
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the byte to use for the character passed across.
|
||||
/// </summary>
|
||||
/// <param name="c"></param>
|
||||
/// <returns></returns>
|
||||
public virtual byte ToByte(char c)
|
||||
{
|
||||
return (byte)c;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a description of the byte char provider.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return "ANSI (Default)";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A byte char provider that can translate bytes encoded in codepage 500 EBCDIC
|
||||
/// </summary>
|
||||
public class EbcdicByteCharProvider : IByteCharConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// The IBM EBCDIC code page 500 encoding. Note that this is not always supported by .NET,
|
||||
/// the underlying platform has to provide support for it.
|
||||
/// </summary>
|
||||
private Encoding _ebcdicEncoding = Encoding.GetEncoding(500);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the EBCDIC character corresponding to the byte passed across.
|
||||
/// </summary>
|
||||
/// <param name="b"></param>
|
||||
/// <returns></returns>
|
||||
public virtual char ToChar(byte b)
|
||||
{
|
||||
string encoded = _ebcdicEncoding.GetString(new byte[] { b });
|
||||
return encoded.Length > 0 ? encoded[0] : '.';
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the byte corresponding to the EBCDIC character passed across.
|
||||
/// </summary>
|
||||
/// <param name="c"></param>
|
||||
/// <returns></returns>
|
||||
public virtual byte ToByte(char c)
|
||||
{
|
||||
byte[] decoded = _ebcdicEncoding.GetBytes(new char[] { c });
|
||||
return decoded.Length > 0 ? decoded[0] : (byte)0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a description of the byte char provider.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return "EBCDIC (Code Page 500)";
|
||||
}
|
||||
}
|
||||
}
|
127
GUI.NET/Debugger/HexBox/ByteCollection.cs
Normal file
127
GUI.NET/Debugger/HexBox/ByteCollection.cs
Normal file
|
@ -0,0 +1,127 @@
|
|||
using System;
|
||||
|
||||
using System.Collections;
|
||||
|
||||
namespace Be.Windows.Forms
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a collection of bytes.
|
||||
/// </summary>
|
||||
public class ByteCollection : CollectionBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of ByteCollection class.
|
||||
/// </summary>
|
||||
public ByteCollection() { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of ByteCollection class.
|
||||
/// </summary>
|
||||
/// <param name="bs">an array of bytes to add to collection</param>
|
||||
public ByteCollection(byte[] bs)
|
||||
{ AddRange(bs); }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value of a byte
|
||||
/// </summary>
|
||||
public byte this[int index]
|
||||
{
|
||||
get { return (byte)List[index]; }
|
||||
set { List[index] = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a byte into the collection.
|
||||
/// </summary>
|
||||
/// <param name="b">the byte to add</param>
|
||||
public void Add(byte b)
|
||||
{ List.Add(b); }
|
||||
|
||||
/// <summary>
|
||||
/// Adds a range of bytes to the collection.
|
||||
/// </summary>
|
||||
/// <param name="bs">the bytes to add</param>
|
||||
public void AddRange(byte[] bs)
|
||||
{ InnerList.AddRange(bs); }
|
||||
|
||||
/// <summary>
|
||||
/// Removes a byte from the collection.
|
||||
/// </summary>
|
||||
/// <param name="b">the byte to remove</param>
|
||||
public void Remove(byte b)
|
||||
{ List.Remove(b); }
|
||||
|
||||
/// <summary>
|
||||
/// Removes a range of bytes from the collection.
|
||||
/// </summary>
|
||||
/// <param name="index">the index of the start byte</param>
|
||||
/// <param name="count">the count of the bytes to remove</param>
|
||||
public void RemoveRange(int index, int count)
|
||||
{ InnerList.RemoveRange(index, count); }
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a range of bytes to the collection.
|
||||
/// </summary>
|
||||
/// <param name="index">the index of start byte</param>
|
||||
/// <param name="bs">an array of bytes to insert</param>
|
||||
public void InsertRange(int index, byte[] bs)
|
||||
{ InnerList.InsertRange(index, bs); }
|
||||
|
||||
/// <summary>
|
||||
/// Gets all bytes in the array
|
||||
/// </summary>
|
||||
/// <returns>an array of bytes.</returns>
|
||||
public byte[] GetBytes()
|
||||
{
|
||||
byte[] bytes = new byte[Count];
|
||||
InnerList.CopyTo(0, bytes, 0, bytes.Length);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a byte to the collection.
|
||||
/// </summary>
|
||||
/// <param name="index">the index</param>
|
||||
/// <param name="b">a byte to insert</param>
|
||||
public void Insert(int index, byte b)
|
||||
{
|
||||
InnerList.Insert(index, b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the index of the given byte.
|
||||
/// </summary>
|
||||
public int IndexOf(byte b)
|
||||
{
|
||||
return InnerList.IndexOf(b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true, if the byte exists in the collection.
|
||||
/// </summary>
|
||||
public bool Contains(byte b)
|
||||
{
|
||||
return InnerList.Contains(b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the content of the collection into the given array.
|
||||
/// </summary>
|
||||
public void CopyTo(byte[] bs, int index)
|
||||
{
|
||||
InnerList.CopyTo(bs, index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the content of the collection into an array.
|
||||
/// </summary>
|
||||
/// <returns>the array containing all bytes.</returns>
|
||||
public byte[] ToArray()
|
||||
{
|
||||
byte[] data = new byte[this.Count];
|
||||
this.CopyTo(data, 0);
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
28
GUI.NET/Debugger/HexBox/BytePositionInfo.cs
Normal file
28
GUI.NET/Debugger/HexBox/BytePositionInfo.cs
Normal file
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Be.Windows.Forms
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a position in the HexBox control
|
||||
/// </summary>
|
||||
struct BytePositionInfo
|
||||
{
|
||||
public BytePositionInfo(long index, int characterPosition)
|
||||
{
|
||||
_index = index;
|
||||
_characterPosition = characterPosition;
|
||||
}
|
||||
|
||||
public int CharacterPosition
|
||||
{
|
||||
get { return _characterPosition; }
|
||||
} int _characterPosition;
|
||||
|
||||
public long Index
|
||||
{
|
||||
get { return _index; }
|
||||
} long _index;
|
||||
}
|
||||
}
|
42
GUI.NET/Debugger/HexBox/DataBlock.cs
Normal file
42
GUI.NET/Debugger/HexBox/DataBlock.cs
Normal file
|
@ -0,0 +1,42 @@
|
|||
using System;
|
||||
|
||||
namespace Be.Windows.Forms
|
||||
{
|
||||
internal abstract class DataBlock
|
||||
{
|
||||
internal DataMap _map;
|
||||
internal DataBlock _nextBlock;
|
||||
internal DataBlock _previousBlock;
|
||||
|
||||
public abstract long Length
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public DataMap Map
|
||||
{
|
||||
get
|
||||
{
|
||||
return _map;
|
||||
}
|
||||
}
|
||||
|
||||
public DataBlock NextBlock
|
||||
{
|
||||
get
|
||||
{
|
||||
return _nextBlock;
|
||||
}
|
||||
}
|
||||
|
||||
public DataBlock PreviousBlock
|
||||
{
|
||||
get
|
||||
{
|
||||
return _previousBlock;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void RemoveBytes(long position, long count);
|
||||
}
|
||||
}
|
318
GUI.NET/Debugger/HexBox/DataMap.cs
Normal file
318
GUI.NET/Debugger/HexBox/DataMap.cs
Normal file
|
@ -0,0 +1,318 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Text;
|
||||
|
||||
namespace Be.Windows.Forms
|
||||
{
|
||||
internal class DataMap : ICollection, IEnumerable
|
||||
{
|
||||
readonly object _syncRoot = new object();
|
||||
internal int _count;
|
||||
internal DataBlock _firstBlock;
|
||||
internal int _version;
|
||||
|
||||
public DataMap()
|
||||
{
|
||||
}
|
||||
|
||||
public DataMap(IEnumerable collection)
|
||||
{
|
||||
if (collection == null)
|
||||
{
|
||||
throw new ArgumentNullException("collection");
|
||||
}
|
||||
|
||||
foreach (DataBlock item in collection)
|
||||
{
|
||||
AddLast(item);
|
||||
}
|
||||
}
|
||||
|
||||
public DataBlock FirstBlock
|
||||
{
|
||||
get
|
||||
{
|
||||
return _firstBlock;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddAfter(DataBlock block, DataBlock newBlock)
|
||||
{
|
||||
AddAfterInternal(block, newBlock);
|
||||
}
|
||||
|
||||
public void AddBefore(DataBlock block, DataBlock newBlock)
|
||||
{
|
||||
AddBeforeInternal(block, newBlock);
|
||||
}
|
||||
|
||||
public void AddFirst(DataBlock block)
|
||||
{
|
||||
if (_firstBlock == null)
|
||||
{
|
||||
AddBlockToEmptyMap(block);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddBeforeInternal(_firstBlock, block);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddLast(DataBlock block)
|
||||
{
|
||||
if (_firstBlock == null)
|
||||
{
|
||||
AddBlockToEmptyMap(block);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddAfterInternal(GetLastBlock(), block);
|
||||
}
|
||||
}
|
||||
|
||||
public void Remove(DataBlock block)
|
||||
{
|
||||
RemoveInternal(block);
|
||||
}
|
||||
|
||||
public void RemoveFirst()
|
||||
{
|
||||
if (_firstBlock == null)
|
||||
{
|
||||
throw new InvalidOperationException("The collection is empty.");
|
||||
}
|
||||
RemoveInternal(_firstBlock);
|
||||
}
|
||||
|
||||
public void RemoveLast()
|
||||
{
|
||||
if (_firstBlock == null)
|
||||
{
|
||||
throw new InvalidOperationException("The collection is empty.");
|
||||
}
|
||||
RemoveInternal(GetLastBlock());
|
||||
}
|
||||
|
||||
public DataBlock Replace(DataBlock block, DataBlock newBlock)
|
||||
{
|
||||
AddAfterInternal(block, newBlock);
|
||||
RemoveInternal(block);
|
||||
return newBlock;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
DataBlock block = FirstBlock;
|
||||
while (block != null)
|
||||
{
|
||||
DataBlock nextBlock = block.NextBlock;
|
||||
InvalidateBlock(block);
|
||||
block = nextBlock;
|
||||
}
|
||||
_firstBlock = null;
|
||||
_count = 0;
|
||||
_version++;
|
||||
}
|
||||
|
||||
void AddAfterInternal(DataBlock block, DataBlock newBlock)
|
||||
{
|
||||
newBlock._previousBlock = block;
|
||||
newBlock._nextBlock = block._nextBlock;
|
||||
newBlock._map = this;
|
||||
|
||||
if (block._nextBlock != null)
|
||||
{
|
||||
block._nextBlock._previousBlock = newBlock;
|
||||
}
|
||||
block._nextBlock = newBlock;
|
||||
|
||||
this._version++;
|
||||
this._count++;
|
||||
}
|
||||
|
||||
void AddBeforeInternal(DataBlock block, DataBlock newBlock)
|
||||
{
|
||||
newBlock._nextBlock = block;
|
||||
newBlock._previousBlock = block._previousBlock;
|
||||
newBlock._map = this;
|
||||
|
||||
if (block._previousBlock != null)
|
||||
{
|
||||
block._previousBlock._nextBlock = newBlock;
|
||||
}
|
||||
block._previousBlock = newBlock;
|
||||
|
||||
if (_firstBlock == block)
|
||||
{
|
||||
_firstBlock = newBlock;
|
||||
}
|
||||
this._version++;
|
||||
this._count++;
|
||||
}
|
||||
|
||||
void RemoveInternal(DataBlock block)
|
||||
{
|
||||
DataBlock previousBlock = block._previousBlock;
|
||||
DataBlock nextBlock = block._nextBlock;
|
||||
|
||||
if (previousBlock != null)
|
||||
{
|
||||
previousBlock._nextBlock = nextBlock;
|
||||
}
|
||||
|
||||
if (nextBlock != null)
|
||||
{
|
||||
nextBlock._previousBlock = previousBlock;
|
||||
}
|
||||
|
||||
if (_firstBlock == block)
|
||||
{
|
||||
_firstBlock = nextBlock;
|
||||
}
|
||||
|
||||
InvalidateBlock(block);
|
||||
|
||||
_count--;
|
||||
_version++;
|
||||
}
|
||||
|
||||
DataBlock GetLastBlock()
|
||||
{
|
||||
DataBlock lastBlock = null;
|
||||
for (DataBlock block = FirstBlock; block != null; block = block.NextBlock)
|
||||
{
|
||||
lastBlock = block;
|
||||
}
|
||||
return lastBlock;
|
||||
}
|
||||
|
||||
void InvalidateBlock(DataBlock block)
|
||||
{
|
||||
block._map = null;
|
||||
block._nextBlock = null;
|
||||
block._previousBlock = null;
|
||||
}
|
||||
|
||||
void AddBlockToEmptyMap(DataBlock block)
|
||||
{
|
||||
block._map = this;
|
||||
block._nextBlock = null;
|
||||
block._previousBlock = null;
|
||||
|
||||
_firstBlock = block;
|
||||
_version++;
|
||||
_count++;
|
||||
}
|
||||
|
||||
#region ICollection Members
|
||||
public void CopyTo(Array array, int index)
|
||||
{
|
||||
DataBlock[] blockArray = array as DataBlock[];
|
||||
for (DataBlock block = FirstBlock; block != null; block = block.NextBlock)
|
||||
{
|
||||
blockArray[index++] = block;
|
||||
}
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return _count;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSynchronized
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public object SyncRoot
|
||||
{
|
||||
get
|
||||
{
|
||||
return _syncRoot;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IEnumerable Members
|
||||
public IEnumerator GetEnumerator()
|
||||
{
|
||||
return new Enumerator(this);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Enumerator Nested Type
|
||||
internal class Enumerator : IEnumerator, IDisposable
|
||||
{
|
||||
DataMap _map;
|
||||
DataBlock _current;
|
||||
int _index;
|
||||
int _version;
|
||||
|
||||
internal Enumerator(DataMap map)
|
||||
{
|
||||
_map = map;
|
||||
_version = map._version;
|
||||
_current = null;
|
||||
_index = -1;
|
||||
}
|
||||
|
||||
object IEnumerator.Current
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_index < 0 || _index > _map.Count)
|
||||
{
|
||||
throw new InvalidOperationException("Enumerator is positioned before the first element or after the last element of the collection.");
|
||||
}
|
||||
return _current;
|
||||
}
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (this._version != _map._version)
|
||||
{
|
||||
throw new InvalidOperationException("Collection was modified after the enumerator was instantiated.");
|
||||
}
|
||||
|
||||
if (_index >= _map.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (++_index == 0)
|
||||
{
|
||||
_current = _map.FirstBlock;
|
||||
}
|
||||
else
|
||||
{
|
||||
_current = _current.NextBlock;
|
||||
}
|
||||
|
||||
return (_index < _map.Count);
|
||||
}
|
||||
|
||||
void IEnumerator.Reset()
|
||||
{
|
||||
if (this._version != this._map._version)
|
||||
{
|
||||
throw new InvalidOperationException("Collection was modified after the enumerator was instantiated.");
|
||||
}
|
||||
|
||||
this._index = -1;
|
||||
this._current = null;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
182
GUI.NET/Debugger/HexBox/DynamicByteProvider.cs
Normal file
182
GUI.NET/Debugger/HexBox/DynamicByteProvider.cs
Normal file
|
@ -0,0 +1,182 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Be.Windows.Forms
|
||||
{
|
||||
/// <summary>
|
||||
/// Byte provider for a small amount of data.
|
||||
/// </summary>
|
||||
public class DynamicByteProvider : IByteProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains information about changes.
|
||||
/// </summary>
|
||||
bool _hasChanges;
|
||||
/// <summary>
|
||||
/// Contains a byte collection.
|
||||
/// </summary>
|
||||
List<byte> _bytes;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the DynamicByteProvider class.
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
public DynamicByteProvider(byte[] data) : this(new List<Byte>(data))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the DynamicByteProvider class.
|
||||
/// </summary>
|
||||
/// <param name="bytes"></param>
|
||||
public DynamicByteProvider(List<Byte> bytes)
|
||||
{
|
||||
_bytes = bytes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the Changed event.
|
||||
/// </summary>
|
||||
void OnChanged(EventArgs e)
|
||||
{
|
||||
_hasChanges = true;
|
||||
|
||||
if(Changed != null)
|
||||
Changed(this, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the LengthChanged event.
|
||||
/// </summary>
|
||||
void OnLengthChanged(EventArgs e)
|
||||
{
|
||||
if(LengthChanged != null)
|
||||
LengthChanged(this, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the byte collection.
|
||||
/// </summary>
|
||||
public List<Byte> Bytes
|
||||
{
|
||||
get { return _bytes; }
|
||||
}
|
||||
|
||||
#region IByteProvider Members
|
||||
/// <summary>
|
||||
/// True, when changes are done.
|
||||
/// </summary>
|
||||
public bool HasChanges()
|
||||
{
|
||||
return _hasChanges;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies changes.
|
||||
/// </summary>
|
||||
public void ApplyChanges()
|
||||
{
|
||||
_hasChanges = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs, when the write buffer contains new changes.
|
||||
/// </summary>
|
||||
public event EventHandler Changed;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs, when InsertBytes or DeleteBytes method is called.
|
||||
/// </summary>
|
||||
public event EventHandler LengthChanged;
|
||||
|
||||
public event EventHandler ByteChanged;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Reads a byte from the byte collection.
|
||||
/// </summary>
|
||||
/// <param name="index">the index of the byte to read</param>
|
||||
/// <returns>the byte</returns>
|
||||
public byte ReadByte(long index)
|
||||
{ return _bytes[(int)index]; }
|
||||
|
||||
/// <summary>
|
||||
/// Write a byte into the byte collection.
|
||||
/// </summary>
|
||||
/// <param name="index">the index of the byte to write.</param>
|
||||
/// <param name="value">the byte</param>
|
||||
public void WriteByte(long index, byte value)
|
||||
{
|
||||
if(_bytes[(int)index] != value) {
|
||||
_bytes[(int)index] = value;
|
||||
ByteChanged?.Invoke((int)index, EventArgs.Empty);
|
||||
OnChanged(EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes bytes from the byte collection.
|
||||
/// </summary>
|
||||
/// <param name="index">the start index of the bytes to delete.</param>
|
||||
/// <param name="length">the length of bytes to delete.</param>
|
||||
public void DeleteBytes(long index, long length)
|
||||
{
|
||||
int internal_index = (int)Math.Max(0, index);
|
||||
int internal_length = (int)Math.Min((int)Length, length);
|
||||
_bytes.RemoveRange(internal_index, internal_length);
|
||||
|
||||
OnLengthChanged(EventArgs.Empty);
|
||||
OnChanged(EventArgs.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts byte into the byte collection.
|
||||
/// </summary>
|
||||
/// <param name="index">the start index of the bytes in the byte collection</param>
|
||||
/// <param name="bs">the byte array to insert</param>
|
||||
public void InsertBytes(long index, byte[] bs)
|
||||
{
|
||||
_bytes.InsertRange((int)index, bs);
|
||||
|
||||
OnLengthChanged(EventArgs.Empty);
|
||||
OnChanged(EventArgs.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the bytes in the byte collection.
|
||||
/// </summary>
|
||||
public long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return _bytes.Count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true
|
||||
/// </summary>
|
||||
public virtual bool SupportsWriteByte()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true
|
||||
/// </summary>
|
||||
public virtual bool SupportsInsertBytes()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true
|
||||
/// </summary>
|
||||
public virtual bool SupportsDeleteBytes()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
569
GUI.NET/Debugger/HexBox/DynamicFileByteProvider.cs
Normal file
569
GUI.NET/Debugger/HexBox/DynamicFileByteProvider.cs
Normal file
|
@ -0,0 +1,569 @@
|
|||
using System;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
|
||||
namespace Be.Windows.Forms
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements a fully editable byte provider for file data of any size.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Only changes to the file are stored in memory with reads from the
|
||||
/// original data occurring as required.
|
||||
/// </remarks>
|
||||
public sealed class DynamicFileByteProvider : IByteProvider, IDisposable
|
||||
{
|
||||
const int COPY_BLOCK_SIZE = 4096;
|
||||
|
||||
string _fileName;
|
||||
Stream _stream;
|
||||
DataMap _dataMap;
|
||||
long _totalLength;
|
||||
bool _readOnly;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="DynamicFileByteProvider" /> instance.
|
||||
/// </summary>
|
||||
/// <param name="fileName">The name of the file from which bytes should be provided.</param>
|
||||
public DynamicFileByteProvider(string fileName) : this(fileName, false) { }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="DynamicFileByteProvider" /> instance.
|
||||
/// </summary>
|
||||
/// <param name="fileName">The name of the file from which bytes should be provided.</param>
|
||||
/// <param name="readOnly">True, opens the file in read-only mode.</param>
|
||||
public DynamicFileByteProvider(string fileName, bool readOnly)
|
||||
{
|
||||
_fileName = fileName;
|
||||
|
||||
if (!readOnly)
|
||||
{
|
||||
_stream = File.Open(fileName, FileMode.Open, FileAccess.ReadWrite, FileShare.Read);
|
||||
}
|
||||
else
|
||||
{
|
||||
_stream = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
}
|
||||
|
||||
_readOnly = readOnly;
|
||||
|
||||
ReInitialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="DynamicFileByteProvider" /> instance.
|
||||
/// </summary>
|
||||
/// <param name="stream">the stream containing the data.</param>
|
||||
/// <remarks>
|
||||
/// The stream must supported seek operations.
|
||||
/// </remarks>
|
||||
public DynamicFileByteProvider(Stream stream)
|
||||
{
|
||||
if (stream == null)
|
||||
throw new ArgumentNullException("stream");
|
||||
if (!stream.CanSeek)
|
||||
throw new ArgumentException("stream must supported seek operations(CanSeek)");
|
||||
|
||||
_stream = stream;
|
||||
_readOnly = !stream.CanWrite;
|
||||
ReInitialize();
|
||||
}
|
||||
|
||||
#region IByteProvider Members
|
||||
/// <summary>
|
||||
/// See <see cref="IByteProvider.LengthChanged" /> for more information.
|
||||
/// </summary>
|
||||
public event EventHandler LengthChanged;
|
||||
|
||||
/// <summary>
|
||||
/// See <see cref="IByteProvider.Changed" /> for more information.
|
||||
/// </summary>
|
||||
public event EventHandler Changed;
|
||||
|
||||
/// <summary>
|
||||
/// See <see cref="IByteProvider.ReadByte" /> for more information.
|
||||
/// </summary>
|
||||
public byte ReadByte(long index)
|
||||
{
|
||||
long blockOffset;
|
||||
DataBlock block = GetDataBlock(index, out blockOffset);
|
||||
FileDataBlock fileBlock = block as FileDataBlock;
|
||||
if (fileBlock != null)
|
||||
{
|
||||
return ReadByteFromFile(fileBlock.FileOffset + index - blockOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
MemoryDataBlock memoryBlock = (MemoryDataBlock)block;
|
||||
return memoryBlock.Data[index - blockOffset];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See <see cref="IByteProvider.WriteByte" /> for more information.
|
||||
/// </summary>
|
||||
public void WriteByte(long index, byte value)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Find the block affected.
|
||||
long blockOffset;
|
||||
DataBlock block = GetDataBlock(index, out blockOffset);
|
||||
|
||||
// If the byte is already in a memory block, modify it.
|
||||
MemoryDataBlock memoryBlock = block as MemoryDataBlock;
|
||||
if (memoryBlock != null)
|
||||
{
|
||||
memoryBlock.Data[index - blockOffset] = value;
|
||||
return;
|
||||
}
|
||||
|
||||
FileDataBlock fileBlock = (FileDataBlock)block;
|
||||
|
||||
// If the byte changing is the first byte in the block and the previous block is a memory block, extend that.
|
||||
if (blockOffset == index && block.PreviousBlock != null)
|
||||
{
|
||||
MemoryDataBlock previousMemoryBlock = block.PreviousBlock as MemoryDataBlock;
|
||||
if (previousMemoryBlock != null)
|
||||
{
|
||||
previousMemoryBlock.AddByteToEnd(value);
|
||||
fileBlock.RemoveBytesFromStart(1);
|
||||
if (fileBlock.Length == 0)
|
||||
{
|
||||
_dataMap.Remove(fileBlock);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If the byte changing is the last byte in the block and the next block is a memory block, extend that.
|
||||
if (blockOffset + fileBlock.Length - 1 == index && block.NextBlock != null)
|
||||
{
|
||||
MemoryDataBlock nextMemoryBlock = block.NextBlock as MemoryDataBlock;
|
||||
if (nextMemoryBlock != null)
|
||||
{
|
||||
nextMemoryBlock.AddByteToStart(value);
|
||||
fileBlock.RemoveBytesFromEnd(1);
|
||||
if (fileBlock.Length == 0)
|
||||
{
|
||||
_dataMap.Remove(fileBlock);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Split the block into a prefix and a suffix and place a memory block in-between.
|
||||
FileDataBlock prefixBlock = null;
|
||||
if (index > blockOffset)
|
||||
{
|
||||
prefixBlock = new FileDataBlock(fileBlock.FileOffset, index - blockOffset);
|
||||
}
|
||||
|
||||
FileDataBlock suffixBlock = null;
|
||||
if (index < blockOffset + fileBlock.Length - 1)
|
||||
{
|
||||
suffixBlock = new FileDataBlock(
|
||||
fileBlock.FileOffset + index - blockOffset + 1,
|
||||
fileBlock.Length - (index - blockOffset + 1));
|
||||
}
|
||||
|
||||
block = _dataMap.Replace(block, new MemoryDataBlock(value));
|
||||
|
||||
if (prefixBlock != null)
|
||||
{
|
||||
_dataMap.AddBefore(block, prefixBlock);
|
||||
}
|
||||
|
||||
if (suffixBlock != null)
|
||||
{
|
||||
_dataMap.AddAfter(block, suffixBlock);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
OnChanged(EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See <see cref="IByteProvider.InsertBytes" /> for more information.
|
||||
/// </summary>
|
||||
public void InsertBytes(long index, byte[] bs)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Find the block affected.
|
||||
long blockOffset;
|
||||
DataBlock block = GetDataBlock(index, out blockOffset);
|
||||
|
||||
// If the insertion point is in a memory block, just insert it.
|
||||
MemoryDataBlock memoryBlock = block as MemoryDataBlock;
|
||||
if (memoryBlock != null)
|
||||
{
|
||||
memoryBlock.InsertBytes(index - blockOffset, bs);
|
||||
return;
|
||||
}
|
||||
|
||||
FileDataBlock fileBlock = (FileDataBlock)block;
|
||||
|
||||
// If the insertion point is at the start of a file block, and the previous block is a memory block, append it to that block.
|
||||
if (blockOffset == index && block.PreviousBlock != null)
|
||||
{
|
||||
MemoryDataBlock previousMemoryBlock = block.PreviousBlock as MemoryDataBlock;
|
||||
if (previousMemoryBlock != null)
|
||||
{
|
||||
previousMemoryBlock.InsertBytes(previousMemoryBlock.Length, bs);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Split the block into a prefix and a suffix and place a memory block in-between.
|
||||
FileDataBlock prefixBlock = null;
|
||||
if (index > blockOffset)
|
||||
{
|
||||
prefixBlock = new FileDataBlock(fileBlock.FileOffset, index - blockOffset);
|
||||
}
|
||||
|
||||
FileDataBlock suffixBlock = null;
|
||||
if (index < blockOffset + fileBlock.Length)
|
||||
{
|
||||
suffixBlock = new FileDataBlock(
|
||||
fileBlock.FileOffset + index - blockOffset,
|
||||
fileBlock.Length - (index - blockOffset));
|
||||
}
|
||||
|
||||
block = _dataMap.Replace(block, new MemoryDataBlock(bs));
|
||||
|
||||
if (prefixBlock != null)
|
||||
{
|
||||
_dataMap.AddBefore(block, prefixBlock);
|
||||
}
|
||||
|
||||
if (suffixBlock != null)
|
||||
{
|
||||
_dataMap.AddAfter(block, suffixBlock);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_totalLength += bs.Length;
|
||||
OnLengthChanged(EventArgs.Empty);
|
||||
OnChanged(EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See <see cref="IByteProvider.DeleteBytes" /> for more information.
|
||||
/// </summary>
|
||||
public void DeleteBytes(long index, long length)
|
||||
{
|
||||
try
|
||||
{
|
||||
long bytesToDelete = length;
|
||||
|
||||
// Find the first block affected.
|
||||
long blockOffset;
|
||||
DataBlock block = GetDataBlock(index, out blockOffset);
|
||||
|
||||
// Truncate or remove each block as necessary.
|
||||
while ((bytesToDelete > 0) && (block != null))
|
||||
{
|
||||
long blockLength = block.Length;
|
||||
DataBlock nextBlock = block.NextBlock;
|
||||
|
||||
// Delete the appropriate section from the block (this may result in two blocks or a zero length block).
|
||||
long count = Math.Min(bytesToDelete, blockLength - (index - blockOffset));
|
||||
block.RemoveBytes(index - blockOffset, count);
|
||||
|
||||
if (block.Length == 0)
|
||||
{
|
||||
_dataMap.Remove(block);
|
||||
if (_dataMap.FirstBlock == null)
|
||||
{
|
||||
_dataMap.AddFirst(new MemoryDataBlock(new byte[0]));
|
||||
}
|
||||
}
|
||||
|
||||
bytesToDelete -= count;
|
||||
blockOffset += block.Length;
|
||||
block = (bytesToDelete > 0) ? nextBlock : null;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_totalLength -= length;
|
||||
OnLengthChanged(EventArgs.Empty);
|
||||
OnChanged(EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See <see cref="IByteProvider.Length" /> for more information.
|
||||
/// </summary>
|
||||
public long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return _totalLength;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See <see cref="IByteProvider.HasChanges" /> for more information.
|
||||
/// </summary>
|
||||
public bool HasChanges()
|
||||
{
|
||||
if (_readOnly)
|
||||
return false;
|
||||
|
||||
if (_totalLength != _stream.Length)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
long offset = 0;
|
||||
for (DataBlock block = _dataMap.FirstBlock; block != null; block = block.NextBlock)
|
||||
{
|
||||
FileDataBlock fileBlock = block as FileDataBlock;
|
||||
if (fileBlock == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (fileBlock.FileOffset != offset)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
offset += fileBlock.Length;
|
||||
}
|
||||
return (offset != _stream.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See <see cref="IByteProvider.ApplyChanges" /> for more information.
|
||||
/// </summary>
|
||||
public void ApplyChanges()
|
||||
{
|
||||
if (_readOnly)
|
||||
throw new OperationCanceledException("File is in read-only mode");
|
||||
|
||||
// This method is implemented to efficiently save the changes to the same file stream opened for reading.
|
||||
// Saving to a separate file would be a much simpler implementation.
|
||||
|
||||
// Firstly, extend the file length (if necessary) to ensure that there is enough disk space.
|
||||
if (_totalLength > _stream.Length)
|
||||
{
|
||||
_stream.SetLength(_totalLength);
|
||||
}
|
||||
|
||||
// Secondly, shift around any file sections that have moved.
|
||||
long dataOffset = 0;
|
||||
for (DataBlock block = _dataMap.FirstBlock; block != null; block = block.NextBlock)
|
||||
{
|
||||
FileDataBlock fileBlock = block as FileDataBlock;
|
||||
if (fileBlock != null && fileBlock.FileOffset != dataOffset)
|
||||
{
|
||||
MoveFileBlock(fileBlock, dataOffset);
|
||||
}
|
||||
dataOffset += block.Length;
|
||||
}
|
||||
|
||||
// Next, write in-memory changes.
|
||||
dataOffset = 0;
|
||||
for (DataBlock block = _dataMap.FirstBlock; block != null; block = block.NextBlock)
|
||||
{
|
||||
MemoryDataBlock memoryBlock = block as MemoryDataBlock;
|
||||
if (memoryBlock != null)
|
||||
{
|
||||
_stream.Position = dataOffset;
|
||||
for (int memoryOffset = 0; memoryOffset < memoryBlock.Length; memoryOffset += COPY_BLOCK_SIZE)
|
||||
{
|
||||
_stream.Write(memoryBlock.Data, memoryOffset, (int)Math.Min(COPY_BLOCK_SIZE, memoryBlock.Length - memoryOffset));
|
||||
}
|
||||
}
|
||||
dataOffset += block.Length;
|
||||
}
|
||||
|
||||
// Finally, if the file has shortened, truncate the stream.
|
||||
_stream.SetLength(_totalLength);
|
||||
ReInitialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See <see cref="IByteProvider.SupportsWriteByte" /> for more information.
|
||||
/// </summary>
|
||||
public bool SupportsWriteByte()
|
||||
{
|
||||
return !_readOnly;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See <see cref="IByteProvider.SupportsInsertBytes" /> for more information.
|
||||
/// </summary>
|
||||
public bool SupportsInsertBytes()
|
||||
{
|
||||
return !_readOnly;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See <see cref="IByteProvider.SupportsDeleteBytes" /> for more information.
|
||||
/// </summary>
|
||||
public bool SupportsDeleteBytes()
|
||||
{
|
||||
return !_readOnly;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IDisposable Members
|
||||
/// <summary>
|
||||
/// See <see cref="Object.Finalize" /> for more information.
|
||||
/// </summary>
|
||||
~DynamicFileByteProvider()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See <see cref="IDisposable.Dispose" /> for more information.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_stream != null)
|
||||
{
|
||||
_stream.Close();
|
||||
_stream = null;
|
||||
}
|
||||
_fileName = null;
|
||||
_dataMap = null;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value, if the file is opened in read-only mode.
|
||||
/// </summary>
|
||||
public bool ReadOnly
|
||||
{
|
||||
get { return _readOnly; }
|
||||
}
|
||||
|
||||
void OnLengthChanged(EventArgs e)
|
||||
{
|
||||
if (LengthChanged != null)
|
||||
LengthChanged(this, e);
|
||||
}
|
||||
|
||||
void OnChanged(EventArgs e)
|
||||
{
|
||||
if (Changed != null)
|
||||
{
|
||||
Changed(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
DataBlock GetDataBlock(long findOffset, out long blockOffset)
|
||||
{
|
||||
if (findOffset < 0 || findOffset > _totalLength)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("findOffset");
|
||||
}
|
||||
|
||||
// Iterate over the blocks until the block containing the required offset is encountered.
|
||||
blockOffset = 0;
|
||||
for (DataBlock block = _dataMap.FirstBlock; block != null; block = block.NextBlock)
|
||||
{
|
||||
if ((blockOffset <= findOffset && blockOffset + block.Length > findOffset) || block.NextBlock == null)
|
||||
{
|
||||
return block;
|
||||
}
|
||||
blockOffset += block.Length;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
FileDataBlock GetNextFileDataBlock(DataBlock block, long dataOffset, out long nextDataOffset)
|
||||
{
|
||||
// Iterate over the remaining blocks until a file block is encountered.
|
||||
nextDataOffset = dataOffset + block.Length;
|
||||
block = block.NextBlock;
|
||||
while (block != null)
|
||||
{
|
||||
FileDataBlock fileBlock = block as FileDataBlock;
|
||||
if (fileBlock != null)
|
||||
{
|
||||
return fileBlock;
|
||||
}
|
||||
nextDataOffset += block.Length;
|
||||
block = block.NextBlock;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
byte ReadByteFromFile(long fileOffset)
|
||||
{
|
||||
// Move to the correct position and read the byte.
|
||||
if (_stream.Position != fileOffset)
|
||||
{
|
||||
_stream.Position = fileOffset;
|
||||
}
|
||||
return (byte)_stream.ReadByte();
|
||||
}
|
||||
|
||||
void MoveFileBlock(FileDataBlock fileBlock, long dataOffset)
|
||||
{
|
||||
// First, determine whether the next file block needs to move before this one.
|
||||
long nextDataOffset;
|
||||
FileDataBlock nextFileBlock = GetNextFileDataBlock(fileBlock, dataOffset, out nextDataOffset);
|
||||
if (nextFileBlock != null && dataOffset + fileBlock.Length > nextFileBlock.FileOffset)
|
||||
{
|
||||
// The next block needs to move first, so do that now.
|
||||
MoveFileBlock(nextFileBlock, nextDataOffset);
|
||||
}
|
||||
|
||||
// Now, move the block.
|
||||
if (fileBlock.FileOffset > dataOffset)
|
||||
{
|
||||
// Move the section to earlier in the file stream (done in chunks starting at the beginning of the section).
|
||||
byte[] buffer = new byte[COPY_BLOCK_SIZE];
|
||||
for (long relativeOffset = 0; relativeOffset < fileBlock.Length; relativeOffset += buffer.Length)
|
||||
{
|
||||
long readOffset = fileBlock.FileOffset + relativeOffset;
|
||||
int bytesToRead = (int)Math.Min(buffer.Length, fileBlock.Length - relativeOffset);
|
||||
_stream.Position = readOffset;
|
||||
_stream.Read(buffer, 0, bytesToRead);
|
||||
|
||||
long writeOffset = dataOffset + relativeOffset;
|
||||
_stream.Position = writeOffset;
|
||||
_stream.Write(buffer, 0, bytesToRead);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Move the section to later in the file stream (done in chunks starting at the end of the section).
|
||||
byte[] buffer = new byte[COPY_BLOCK_SIZE];
|
||||
for (long relativeOffset = 0; relativeOffset < fileBlock.Length; relativeOffset += buffer.Length)
|
||||
{
|
||||
int bytesToRead = (int)Math.Min(buffer.Length, fileBlock.Length - relativeOffset);
|
||||
long readOffset = fileBlock.FileOffset + fileBlock.Length - relativeOffset - bytesToRead;
|
||||
_stream.Position = readOffset;
|
||||
_stream.Read(buffer, 0, bytesToRead);
|
||||
|
||||
long writeOffset = dataOffset + fileBlock.Length - relativeOffset - bytesToRead;
|
||||
_stream.Position = writeOffset;
|
||||
_stream.Write(buffer, 0, bytesToRead);
|
||||
}
|
||||
}
|
||||
|
||||
// This block now points to a different position in the file.
|
||||
fileBlock.SetFileOffset(dataOffset);
|
||||
}
|
||||
|
||||
void ReInitialize()
|
||||
{
|
||||
_dataMap = new DataMap();
|
||||
_dataMap.AddFirst(new FileDataBlock(0, _stream.Length));
|
||||
_totalLength = _stream.Length;
|
||||
}
|
||||
}
|
||||
}
|
273
GUI.NET/Debugger/HexBox/FileByteProvider.cs
Normal file
273
GUI.NET/Debugger/HexBox/FileByteProvider.cs
Normal file
|
@ -0,0 +1,273 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Collections;
|
||||
|
||||
namespace Be.Windows.Forms
|
||||
{
|
||||
/// <summary>
|
||||
/// Byte provider for (big) files.
|
||||
/// </summary>
|
||||
public class FileByteProvider : IByteProvider, IDisposable
|
||||
{
|
||||
#region WriteCollection class
|
||||
/// <summary>
|
||||
/// Represents the write buffer class
|
||||
/// </summary>
|
||||
class WriteCollection : DictionaryBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a byte in the collection
|
||||
/// </summary>
|
||||
public byte this[long index]
|
||||
{
|
||||
get { return (byte)this.Dictionary[index]; }
|
||||
set { Dictionary[index] = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a byte into the collection
|
||||
/// </summary>
|
||||
/// <param name="index">the index of the byte</param>
|
||||
/// <param name="value">the value of the byte</param>
|
||||
public void Add(long index, byte value)
|
||||
{ Dictionary.Add(index, value); }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a byte with the given index exists.
|
||||
/// </summary>
|
||||
/// <param name="index">the index of the byte</param>
|
||||
/// <returns>true, if the is in the collection</returns>
|
||||
public bool Contains(long index)
|
||||
{ return Dictionary.Contains(index); }
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Occurs, when the write buffer contains new changes.
|
||||
/// </summary>
|
||||
public event EventHandler Changed;
|
||||
|
||||
/// <summary>
|
||||
/// Contains all changes
|
||||
/// </summary>
|
||||
WriteCollection _writes = new WriteCollection();
|
||||
|
||||
/// <summary>
|
||||
/// Contains the file name.
|
||||
/// </summary>
|
||||
string _fileName;
|
||||
/// <summary>
|
||||
/// Contains the file stream.
|
||||
/// </summary>
|
||||
FileStream _fileStream;
|
||||
/// <summary>
|
||||
/// Read-only access.
|
||||
/// </summary>
|
||||
bool _readOnly;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the FileByteProvider class.
|
||||
/// </summary>
|
||||
/// <param name="fileName"></param>
|
||||
public FileByteProvider(string fileName)
|
||||
{
|
||||
_fileName = fileName;
|
||||
|
||||
try
|
||||
{
|
||||
// try to open in write mode
|
||||
_fileStream = File.Open(fileName, FileMode.Open, FileAccess.ReadWrite, FileShare.Read);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// write mode failed, try to open in read-only and fileshare friendly mode.
|
||||
try
|
||||
{
|
||||
_fileStream = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
_readOnly = true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Terminates the instance of the FileByteProvider class.
|
||||
/// </summary>
|
||||
~FileByteProvider()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the Changed event.
|
||||
/// </summary>
|
||||
/// <remarks>Never used.</remarks>
|
||||
void OnChanged(EventArgs e)
|
||||
{
|
||||
if(Changed != null)
|
||||
Changed(this, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the file the byte provider is using.
|
||||
/// </summary>
|
||||
public string FileName
|
||||
{
|
||||
get { return _fileName; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value if there are some changes.
|
||||
/// </summary>
|
||||
/// <returns>true, if there are some changes</returns>
|
||||
public bool HasChanges()
|
||||
{
|
||||
return (_writes.Count > 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the file with all changes the write buffer contains.
|
||||
/// </summary>
|
||||
public void ApplyChanges()
|
||||
{
|
||||
if (this._readOnly)
|
||||
{
|
||||
throw new Exception("File is in read-only mode.");
|
||||
}
|
||||
|
||||
if(!HasChanges())
|
||||
return;
|
||||
|
||||
IDictionaryEnumerator en = _writes.GetEnumerator();
|
||||
while(en.MoveNext())
|
||||
{
|
||||
long index = (long)en.Key;
|
||||
byte value = (byte)en.Value;
|
||||
if(_fileStream.Position != index)
|
||||
_fileStream.Position = index;
|
||||
_fileStream.Write(new byte[] { value }, 0, 1);
|
||||
}
|
||||
_writes.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the write buffer and reject all changes made.
|
||||
/// </summary>
|
||||
public void RejectChanges()
|
||||
{
|
||||
_writes.Clear();
|
||||
}
|
||||
|
||||
#region IByteProvider Members
|
||||
/// <summary>
|
||||
/// Never used.
|
||||
/// </summary>
|
||||
#pragma warning disable 0067
|
||||
public event EventHandler LengthChanged;
|
||||
#pragma warning restore 0067
|
||||
|
||||
/// <summary>
|
||||
/// Reads a byte from the file.
|
||||
/// </summary>
|
||||
/// <param name="index">the index of the byte to read</param>
|
||||
/// <returns>the byte</returns>
|
||||
public byte ReadByte(long index)
|
||||
{
|
||||
if(_writes.Contains(index))
|
||||
return _writes[index];
|
||||
|
||||
if(_fileStream.Position != index)
|
||||
_fileStream.Position = index;
|
||||
|
||||
byte res = (byte)_fileStream.ReadByte();
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the file.
|
||||
/// </summary>
|
||||
public long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return _fileStream.Length;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a byte into write buffer
|
||||
/// </summary>
|
||||
public void WriteByte(long index, byte value)
|
||||
{
|
||||
if(_writes.Contains(index))
|
||||
_writes[index] = value;
|
||||
else
|
||||
_writes.Add(index, value);
|
||||
|
||||
OnChanged(EventArgs.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Not supported
|
||||
/// </summary>
|
||||
public void DeleteBytes(long index, long length)
|
||||
{
|
||||
throw new NotSupportedException("FileByteProvider.DeleteBytes");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Not supported
|
||||
/// </summary>
|
||||
public void InsertBytes(long index, byte[] bs)
|
||||
{
|
||||
throw new NotSupportedException("FileByteProvider.InsertBytes");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true
|
||||
/// </summary>
|
||||
public bool SupportsWriteByte()
|
||||
{
|
||||
return !_readOnly;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns false
|
||||
/// </summary>
|
||||
public bool SupportsInsertBytes()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns false
|
||||
/// </summary>
|
||||
public bool SupportsDeleteBytes()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IDisposable Members
|
||||
/// <summary>
|
||||
/// Releases the file handle used by the FileByteProvider.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if(_fileStream != null)
|
||||
{
|
||||
_fileName = null;
|
||||
|
||||
_fileStream.Close();
|
||||
_fileStream = null;
|
||||
}
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
96
GUI.NET/Debugger/HexBox/FileDataBlock.cs
Normal file
96
GUI.NET/Debugger/HexBox/FileDataBlock.cs
Normal file
|
@ -0,0 +1,96 @@
|
|||
using System;
|
||||
|
||||
namespace Be.Windows.Forms
|
||||
{
|
||||
internal sealed class FileDataBlock : DataBlock
|
||||
{
|
||||
long _length;
|
||||
long _fileOffset;
|
||||
|
||||
public FileDataBlock(long fileOffset, long length)
|
||||
{
|
||||
_fileOffset = fileOffset;
|
||||
_length = length;
|
||||
}
|
||||
|
||||
public long FileOffset
|
||||
{
|
||||
get
|
||||
{
|
||||
return _fileOffset;
|
||||
}
|
||||
}
|
||||
|
||||
public override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return _length;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetFileOffset(long value)
|
||||
{
|
||||
_fileOffset = value;
|
||||
}
|
||||
|
||||
public void RemoveBytesFromEnd(long count)
|
||||
{
|
||||
if (count > _length)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("count");
|
||||
}
|
||||
|
||||
_length -= count;
|
||||
}
|
||||
|
||||
public void RemoveBytesFromStart(long count)
|
||||
{
|
||||
if (count > _length)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("count");
|
||||
}
|
||||
|
||||
_fileOffset += count;
|
||||
_length -= count;
|
||||
}
|
||||
|
||||
public override void RemoveBytes(long position, long count)
|
||||
{
|
||||
if (position > _length)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("position");
|
||||
}
|
||||
|
||||
if (position + count > _length)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("count");
|
||||
}
|
||||
|
||||
long prefixLength = position;
|
||||
long prefixFileOffset = _fileOffset;
|
||||
|
||||
long suffixLength = _length - count - prefixLength;
|
||||
long suffixFileOffset = _fileOffset + position + count;
|
||||
|
||||
if (prefixLength > 0 && suffixLength > 0)
|
||||
{
|
||||
_fileOffset = prefixFileOffset;
|
||||
_length = prefixLength;
|
||||
_map.AddAfter(this, new FileDataBlock(suffixFileOffset, suffixLength));
|
||||
return;
|
||||
}
|
||||
|
||||
if (prefixLength > 0)
|
||||
{
|
||||
_fileOffset = prefixFileOffset;
|
||||
_length = prefixLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
_fileOffset = suffixFileOffset;
|
||||
_length = suffixLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
97
GUI.NET/Debugger/HexBox/FindOptions.cs
Normal file
97
GUI.NET/Debugger/HexBox/FindOptions.cs
Normal file
|
@ -0,0 +1,97 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Be.Windows.Forms
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the type of the Find operation.
|
||||
/// </summary>
|
||||
public enum FindType
|
||||
{
|
||||
/// <summary>
|
||||
/// Used for Text Find operations
|
||||
/// </summary>
|
||||
Text,
|
||||
/// <summary>
|
||||
/// Used for Hex Find operations
|
||||
/// </summary>
|
||||
Hex
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines all state information nee
|
||||
/// </summary>
|
||||
public class FindOptions
|
||||
{
|
||||
public bool WrapSearch { get; set; }
|
||||
public bool HasWildcard { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the Find options are valid
|
||||
/// </summary>
|
||||
public bool IsValid { get; set; }
|
||||
/// <summary>
|
||||
/// Gets the Find buffer used for case insensitive Find operations. This is the binary representation of Text.
|
||||
/// </summary>
|
||||
internal byte[] FindBuffer { get; private set; }
|
||||
/// <summary>
|
||||
/// Gets the Find buffer used for case sensitive Find operations. This is the binary representation of Text in lower case format.
|
||||
/// </summary>
|
||||
internal byte[] FindBufferLowerCase { get; private set; }
|
||||
/// <summary>
|
||||
/// Gets the Find buffer used for case sensitive Find operations. This is the binary representation of Text in upper case format.
|
||||
/// </summary>
|
||||
internal byte[] FindBufferUpperCase { get; private set; }
|
||||
/// <summary>
|
||||
/// Contains the MatchCase value
|
||||
/// </summary>
|
||||
bool _matchCase;
|
||||
/// <summary>
|
||||
/// Gets or sets the value, whether the Find operation is case sensitive or not.
|
||||
/// </summary>
|
||||
public bool MatchCase
|
||||
{
|
||||
get { return _matchCase; }
|
||||
set
|
||||
{
|
||||
_matchCase = value;
|
||||
UpdateFindBuffer();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Contains the text that should be found.
|
||||
/// </summary>
|
||||
string _text;
|
||||
/// <summary>
|
||||
/// Gets or sets the text that should be found. Only used, when Type is FindType.Hex.
|
||||
/// </summary>
|
||||
public string Text
|
||||
{
|
||||
get { return _text; }
|
||||
set
|
||||
{
|
||||
_text = value;
|
||||
UpdateFindBuffer();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets or sets the hex buffer that should be found. Only used, when Type is FindType.Hex.
|
||||
/// </summary>
|
||||
public byte[] Hex { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the type what should be searched.
|
||||
/// </summary>
|
||||
public FindType Type { get; set; }
|
||||
/// <summary>
|
||||
/// Updates the find buffer.
|
||||
/// </summary>
|
||||
void UpdateFindBuffer()
|
||||
{
|
||||
string text = this.Text != null ? this.Text : string.Empty;
|
||||
FindBuffer = ASCIIEncoding.ASCII.GetBytes(text);
|
||||
FindBufferLowerCase = ASCIIEncoding.ASCII.GetBytes(text.ToLower());
|
||||
FindBufferUpperCase = ASCIIEncoding.ASCII.GetBytes(text.ToUpper());
|
||||
}
|
||||
}
|
||||
}
|
4120
GUI.NET/Debugger/HexBox/HexBox.cs
Normal file
4120
GUI.NET/Debugger/HexBox/HexBox.cs
Normal file
File diff suppressed because it is too large
Load diff
126
GUI.NET/Debugger/HexBox/HexBox.resx
Normal file
126
GUI.NET/Debugger/HexBox/HexBox.resx
Normal file
|
@ -0,0 +1,126 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<metadata name="contextMenuStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
<metadata name="$this.TrayLargeIcon" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>False</value>
|
||||
</metadata>
|
||||
</root>
|
21
GUI.NET/Debugger/HexBox/HexCasing.cs
Normal file
21
GUI.NET/Debugger/HexBox/HexCasing.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Be.Windows.Forms
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the case of hex characters in the HexBox control
|
||||
/// </summary>
|
||||
public enum HexCasing
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts all characters to uppercase.
|
||||
/// </summary>
|
||||
Upper = 0,
|
||||
/// <summary>
|
||||
/// Converts all characters to lowercase.
|
||||
/// </summary>
|
||||
Lower = 1
|
||||
}
|
||||
}
|
75
GUI.NET/Debugger/HexBox/IByteProvider.cs
Normal file
75
GUI.NET/Debugger/HexBox/IByteProvider.cs
Normal file
|
@ -0,0 +1,75 @@
|
|||
using System;
|
||||
|
||||
namespace Be.Windows.Forms
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a byte provider for HexBox control
|
||||
/// </summary>
|
||||
public interface IByteProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads a byte from the provider
|
||||
/// </summary>
|
||||
/// <param name="index">the index of the byte to read</param>
|
||||
/// <returns>the byte to read</returns>
|
||||
byte ReadByte(long index);
|
||||
/// <summary>
|
||||
/// Writes a byte into the provider
|
||||
/// </summary>
|
||||
/// <param name="index">the index of the byte to write</param>
|
||||
/// <param name="value">the byte to write</param>
|
||||
void WriteByte(long index, byte value);
|
||||
/// <summary>
|
||||
/// Inserts bytes into the provider
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="bs"></param>
|
||||
/// <remarks>This method must raise the LengthChanged event.</remarks>
|
||||
void InsertBytes(long index, byte[] bs);
|
||||
/// <summary>
|
||||
/// Deletes bytes from the provider
|
||||
/// </summary>
|
||||
/// <param name="index">the start index of the bytes to delete</param>
|
||||
/// <param name="length">the length of the bytes to delete</param>
|
||||
/// <remarks>This method must raise the LengthChanged event.</remarks>
|
||||
void DeleteBytes(long index, long length);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the total length of bytes the byte provider is providing.
|
||||
/// </summary>
|
||||
long Length { get; }
|
||||
/// <summary>
|
||||
/// Occurs, when the Length property changed.
|
||||
/// </summary>
|
||||
event EventHandler LengthChanged;
|
||||
|
||||
/// <summary>
|
||||
/// True, when changes are done.
|
||||
/// </summary>
|
||||
bool HasChanges();
|
||||
/// <summary>
|
||||
/// Applies changes.
|
||||
/// </summary>
|
||||
void ApplyChanges();
|
||||
/// <summary>
|
||||
/// Occurs, when bytes are changed.
|
||||
/// </summary>
|
||||
event EventHandler Changed;
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value if the WriteByte methods is supported by the provider.
|
||||
/// </summary>
|
||||
/// <returns>True, when it´s supported.</returns>
|
||||
bool SupportsWriteByte();
|
||||
/// <summary>
|
||||
/// Returns a value if the InsertBytes methods is supported by the provider.
|
||||
/// </summary>
|
||||
/// <returns>True, when it´s supported.</returns>
|
||||
bool SupportsInsertBytes();
|
||||
/// <summary>
|
||||
/// Returns a value if the DeleteBytes methods is supported by the provider.
|
||||
/// </summary>
|
||||
/// <returns>True, when it´s supported.</returns>
|
||||
bool SupportsDeleteBytes();
|
||||
}
|
||||
}
|
87
GUI.NET/Debugger/HexBox/MemoryDataBlock.cs
Normal file
87
GUI.NET/Debugger/HexBox/MemoryDataBlock.cs
Normal file
|
@ -0,0 +1,87 @@
|
|||
using System;
|
||||
|
||||
namespace Be.Windows.Forms
|
||||
{
|
||||
internal sealed class MemoryDataBlock : DataBlock
|
||||
{
|
||||
byte[] _data;
|
||||
|
||||
public MemoryDataBlock(byte data)
|
||||
{
|
||||
_data = new byte[] { data };
|
||||
}
|
||||
|
||||
public MemoryDataBlock(byte[] data)
|
||||
{
|
||||
if (data == null)
|
||||
{
|
||||
throw new ArgumentNullException("data");
|
||||
}
|
||||
|
||||
_data = (byte[])data.Clone();
|
||||
}
|
||||
|
||||
public override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return _data.LongLength;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] Data
|
||||
{
|
||||
get
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddByteToEnd(byte value)
|
||||
{
|
||||
byte[] newData = new byte[_data.LongLength + 1];
|
||||
_data.CopyTo(newData, 0);
|
||||
newData[newData.LongLength - 1] = value;
|
||||
_data = newData;
|
||||
}
|
||||
|
||||
public void AddByteToStart(byte value)
|
||||
{
|
||||
byte[] newData = new byte[_data.LongLength + 1];
|
||||
newData[0] = value;
|
||||
_data.CopyTo(newData, 1);
|
||||
_data = newData;
|
||||
}
|
||||
|
||||
public void InsertBytes(long position, byte[] data)
|
||||
{
|
||||
byte[] newData = new byte[_data.LongLength + data.LongLength];
|
||||
if (position > 0)
|
||||
{
|
||||
Array.Copy(_data, 0, newData, 0, position);
|
||||
}
|
||||
Array.Copy(data, 0, newData, position, data.LongLength);
|
||||
if (position < _data.LongLength)
|
||||
{
|
||||
Array.Copy(_data, position, newData, position + data.LongLength, _data.LongLength - position);
|
||||
}
|
||||
_data = newData;
|
||||
}
|
||||
|
||||
public override void RemoveBytes(long position, long count)
|
||||
{
|
||||
byte[] newData = new byte[_data.LongLength - count];
|
||||
|
||||
if (position > 0)
|
||||
{
|
||||
Array.Copy(_data, 0, newData, 0, position);
|
||||
}
|
||||
if (position + count < _data.LongLength)
|
||||
{
|
||||
Array.Copy(_data, position + count, newData, position, newData.LongLength - position);
|
||||
}
|
||||
|
||||
_data = newData;
|
||||
}
|
||||
}
|
||||
}
|
14
GUI.NET/Debugger/HexBox/NativeMethods.cs
Normal file
14
GUI.NET/Debugger/HexBox/NativeMethods.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Be.Windows.Forms
|
||||
{
|
||||
internal static class NativeMethods
|
||||
{
|
||||
// Key definitions
|
||||
public const int WM_KEYDOWN = 0x100;
|
||||
public const int WM_KEYUP = 0x101;
|
||||
public const int WM_CHAR = 0x102;
|
||||
}
|
||||
}
|
25
GUI.NET/Debugger/HexBox/StaticByteProvider.cs
Normal file
25
GUI.NET/Debugger/HexBox/StaticByteProvider.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Be.Windows.Forms
|
||||
{
|
||||
public class StaticByteProvider : DynamicByteProvider
|
||||
{
|
||||
public StaticByteProvider(byte[] data) : base(data)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool SupportsInsertBytes()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool SupportsDeleteBytes()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
44
GUI.NET/Debugger/HexBox/Util.cs
Normal file
44
GUI.NET/Debugger/HexBox/Util.cs
Normal file
|
@ -0,0 +1,44 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Be.Windows.Forms
|
||||
{
|
||||
static class Util
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains true, if we are in design mode of Visual Studio
|
||||
/// </summary>
|
||||
private static bool _designMode;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes an instance of Util class
|
||||
/// </summary>
|
||||
static Util()
|
||||
{
|
||||
// design mode is true if host process is: Visual Studio, Visual Studio Express Versions (C#, VB, C++) or SharpDevelop
|
||||
var designerHosts = new List<string>() { "devenv", "vcsexpress", "vbexpress", "vcexpress", "sharpdevelop" };
|
||||
using (var process = System.Diagnostics.Process.GetCurrentProcess())
|
||||
{
|
||||
var processName = process.ProcessName.ToLower();
|
||||
_designMode = designerHosts.Contains(processName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets true, if we are in design mode of Visual Studio
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// In Visual Studio 2008 SP1 the designer is crashing sometimes on windows forms.
|
||||
/// The DesignMode property of Control class is buggy and cannot be used, so use our own implementation instead.
|
||||
/// </remarks>
|
||||
public static bool DesignMode
|
||||
{
|
||||
get
|
||||
{
|
||||
return _designMode;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
2
GUI.NET/Debugger/frmMemoryViewer.Designer.cs
generated
2
GUI.NET/Debugger/frmMemoryViewer.Designer.cs
generated
|
@ -81,7 +81,7 @@
|
|||
this.ctrlHexViewer.Name = "ctrlHexViewer";
|
||||
this.ctrlHexViewer.Size = new System.Drawing.Size(665, 346);
|
||||
this.ctrlHexViewer.TabIndex = 0;
|
||||
this.ctrlHexViewer.ColumnCountChanged += new System.EventHandler(this.ctrlHexViewer_ColumnCountChanged);
|
||||
this.ctrlHexViewer.RequiredWidthChanged += new System.EventHandler(this.ctrlHexViewer_RequiredWidthChanged);
|
||||
//
|
||||
// flowLayoutPanel1
|
||||
//
|
||||
|
|
|
@ -29,12 +29,11 @@ namespace Mesen.GUI.Debugger
|
|||
base.OnLoad(e);
|
||||
|
||||
this.mnuAutoRefresh.Checked = ConfigManager.Config.DebugInfo.RamAutoRefresh;
|
||||
this.ctrlHexViewer.FontSize = ConfigManager.Config.DebugInfo.RamFontSize;
|
||||
this.ctrlHexViewer.SetFontSize((int)ConfigManager.Config.DebugInfo.RamFontSize);
|
||||
|
||||
UpdateImportButton();
|
||||
|
||||
this.cboMemoryType.SelectedIndex = 0;
|
||||
this.Size = new Size(this.ctrlHexViewer.IdealWidth, this.Height);
|
||||
|
||||
_notifListener = new InteropEmu.NotificationListener();
|
||||
_notifListener.OnNotification += _notifListener_OnNotification;
|
||||
|
@ -52,7 +51,6 @@ namespace Mesen.GUI.Debugger
|
|||
this._memoryType = (DebugMemoryType)this.cboMemoryType.SelectedIndex;
|
||||
this.UpdateImportButton();
|
||||
this.RefreshData();
|
||||
this.ctrlHexViewer.ScrollToTop();
|
||||
}
|
||||
|
||||
private void mnuRefresh_Click(object sender, EventArgs e)
|
||||
|
@ -60,20 +58,17 @@ namespace Mesen.GUI.Debugger
|
|||
this.RefreshData();
|
||||
}
|
||||
|
||||
int _previousIndex = -1;
|
||||
private void RefreshData()
|
||||
{
|
||||
if(this.tabMain.SelectedTab == this.tpgAccessCounters) {
|
||||
this.ctrlMemoryAccessCounters.RefreshData();
|
||||
} else if(this.tabMain.SelectedTab == this.tpgMemoryViewer) {
|
||||
this.ctrlHexViewer.Data = InteropEmu.DebugGetMemoryState((DebugMemoryType)this.cboMemoryType.SelectedIndex);
|
||||
this.ctrlHexViewer.SetData(InteropEmu.DebugGetMemoryState((DebugMemoryType)this.cboMemoryType.SelectedIndex), this.cboMemoryType.SelectedIndex != _previousIndex);
|
||||
this._previousIndex = this.cboMemoryType.SelectedIndex;
|
||||
}
|
||||
}
|
||||
|
||||
private void ctrlHexViewer_ColumnCountChanged(object sender, EventArgs e)
|
||||
{
|
||||
this.Size = new Size(this.ctrlHexViewer.IdealWidth, this.Height);
|
||||
}
|
||||
|
||||
private void mnuFind_Click(object sender, EventArgs e)
|
||||
{
|
||||
this.ctrlHexViewer.OpenSearchBox();
|
||||
|
@ -96,19 +91,19 @@ namespace Mesen.GUI.Debugger
|
|||
|
||||
private void mnuIncreaseFontSize_Click(object sender, EventArgs e)
|
||||
{
|
||||
this.ctrlHexViewer.FontSize++;
|
||||
this.ctrlHexViewer.IncreaseFontSize();
|
||||
this.UpdateConfig();
|
||||
}
|
||||
|
||||
private void mnuDecreaseFontSize_Click(object sender, EventArgs e)
|
||||
{
|
||||
this.ctrlHexViewer.FontSize--;
|
||||
this.ctrlHexViewer.DecreaseFontSize();
|
||||
this.UpdateConfig();
|
||||
}
|
||||
|
||||
private void mnuResetFontSize_Click(object sender, EventArgs e)
|
||||
{
|
||||
this.ctrlHexViewer.FontSize = BaseControl.DefaultFontSize;
|
||||
this.ctrlHexViewer.ResetFontSize();
|
||||
this.UpdateConfig();
|
||||
}
|
||||
|
||||
|
@ -120,7 +115,7 @@ namespace Mesen.GUI.Debugger
|
|||
private void UpdateConfig()
|
||||
{
|
||||
ConfigManager.Config.DebugInfo.RamAutoRefresh = this.mnuAutoRefresh.Checked;
|
||||
ConfigManager.Config.DebugInfo.RamFontSize = this.ctrlHexViewer.FontSize;
|
||||
ConfigManager.Config.DebugInfo.RamFontSize = this.ctrlHexViewer.HexFont.Size;
|
||||
ConfigManager.ApplyChanges();
|
||||
}
|
||||
|
||||
|
@ -166,7 +161,7 @@ namespace Mesen.GUI.Debugger
|
|||
sfd.InitialDirectory = ConfigManager.DebuggerFolder;
|
||||
sfd.FileName = InteropEmu.GetRomInfo().GetRomName() + " - " + cboMemoryType.SelectedItem.ToString() + ".dmp";
|
||||
if(sfd.ShowDialog() == DialogResult.OK) {
|
||||
File.WriteAllBytes(sfd.FileName, this.ctrlHexViewer.Data);
|
||||
File.WriteAllBytes(sfd.FileName, this.ctrlHexViewer.GetData());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -185,5 +180,10 @@ namespace Mesen.GUI.Debugger
|
|||
this.ctrlProfiler.RefreshData();
|
||||
}
|
||||
}
|
||||
|
||||
private void ctrlHexViewer_RequiredWidthChanged(object sender, EventArgs e)
|
||||
{
|
||||
this.Size = new Size(this.ctrlHexViewer.RequiredWidth + this.Width - this.ctrlHexViewer.Width + 30, this.Height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -434,6 +434,28 @@
|
|||
<Compile Include="Debugger\frmTraceLogger.Designer.cs">
|
||||
<DependentUpon>frmTraceLogger.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Debugger\HexBox\BuiltInContextMenu.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Debugger\HexBox\ByteCharConverters.cs" />
|
||||
<Compile Include="Debugger\HexBox\ByteCollection.cs" />
|
||||
<Compile Include="Debugger\HexBox\BytePositionInfo.cs" />
|
||||
<Compile Include="Debugger\HexBox\DataBlock.cs" />
|
||||
<Compile Include="Debugger\HexBox\DataMap.cs" />
|
||||
<Compile Include="Debugger\HexBox\DynamicByteProvider.cs" />
|
||||
<Compile Include="Debugger\HexBox\DynamicFileByteProvider.cs" />
|
||||
<Compile Include="Debugger\HexBox\FileByteProvider.cs" />
|
||||
<Compile Include="Debugger\HexBox\FileDataBlock.cs" />
|
||||
<Compile Include="Debugger\HexBox\FindOptions.cs" />
|
||||
<Compile Include="Debugger\HexBox\HexBox.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Debugger\HexBox\HexCasing.cs" />
|
||||
<Compile Include="Debugger\HexBox\IByteProvider.cs" />
|
||||
<Compile Include="Debugger\HexBox\MemoryDataBlock.cs" />
|
||||
<Compile Include="Debugger\HexBox\NativeMethods.cs" />
|
||||
<Compile Include="Debugger\HexBox\StaticByteProvider.cs" />
|
||||
<Compile Include="Debugger\HexBox\Util.cs" />
|
||||
<Compile Include="Debugger\LabelManager.cs" />
|
||||
<Compile Include="Debugger\TextboxHistory.cs" />
|
||||
<Compile Include="Forms\BaseConfigForm.Designer.cs">
|
||||
|
@ -719,6 +741,7 @@
|
|||
<EmbeddedResource Include="Debugger\frmTraceLogger.resx">
|
||||
<DependentUpon>frmTraceLogger.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Debugger\HexBox\HexBox.resx" />
|
||||
<EmbeddedResource Include="Forms\BaseConfigForm.resx">
|
||||
<DependentUpon>BaseConfigForm.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
|
|
Loading…
Add table
Reference in a new issue