Debugger: Replaced custom hex editor with HexBox

This commit is contained in:
Souryo 2017-02-26 13:46:57 -05:00
parent b95c18b729
commit 9c2f6995c2
24 changed files with 7025 additions and 151 deletions

View file

@ -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;
}
}

View file

@ -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();
}
}
}

View 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;
}
}

View 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)";
}
}
}

View 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;
}
}
}

View 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;
}
}

View 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);
}
}

View 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
}
}

View 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
}
}

View 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;
}
}
}

View 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
}
}

View 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;
}
}
}
}

View 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());
}
}
}

File diff suppressed because it is too large Load diff

View 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>

View 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
}
}

View 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();
}
}

View 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;
}
}
}

View 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;
}
}

View 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;
}
}
}

View 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;
}
}
}
}

View file

@ -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
//

View file

@ -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);
}
}
}

View file

@ -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>