using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Globalization; using System.IO; using BizHawk.Client.Common; namespace BizHawk.MultiClient { public partial class HexEditor : Form { //TODO: //Increment/Decrement wrapping logic for 4 byte values is messed up //2 & 4 byte text area clicking is off private int defaultWidth; private int defaultHeight; private readonly List domainMenuItems = new List(); private int RowsVisible = 0; private int NumDigits = 4; private string NumDigitsStr = "{0:X4}"; private string DigitFormatString = "{0:X2}"; private readonly char[] nibbles = { 'G', 'G', 'G', 'G', 'G', 'G', 'G', 'G' }; //G = off 0-9 & A-F are acceptable values private int addressHighlighted = -1; private readonly List SecondaryHighlightedAddresses = new List(); private int addressOver = -1; private int maxRow = 0; private MemoryDomain Domain = new MemoryDomain("NULL", 1024, Endian.Little, addr => 0, (a, v) => { v = 0; }); private string info = ""; private int row; private int addr; private const int fontHeight = 14; private const int fontWidth = 7; //Width of 1 digits private string FindStr = ""; private bool loaded; private bool MouseIsDown; private byte[] ROM; private MemoryDomain ROMDomain; // Configurations private bool AutoLoad; private bool SaveWindowPosition; private int Wndx = -1; private int Wndy = -1; private int Width_ = -1; private int Height_ = -1; private bool BigEndian; private int DataSize; HexFind HexFind1 = new HexFind(); public HexEditor() { InitializeComponent(); AddressesLabel.BackColor = Color.Transparent; LoadConfigSettings(); SetHeader(); Closing += (o, e) => SaveConfigSettings(); Header.Font = new Font("Courier New", 8); AddressesLabel.Font = new Font("Courier New", 8); AddressLabel.Font = new Font("Courier New", 8); } private void LoadConfigSettings() { AutoLoad = Global.Config.AutoLoadHexEditor; SaveWindowPosition = Global.Config.SaveWindowPosition; Wndx = Global.Config.HexEditorWndx; Wndy = Global.Config.HexEditorWndy; Width_ = Global.Config.HexEditorWidth; Height_ = Global.Config.HexEditorHeight; BigEndian = Global.Config.HexEditorBigEndian; DataSize = Global.Config.HexEditorDataSize; //Colors menuStrip1.BackColor = Global.Config.HexMenubarColor; MemoryViewerBox.BackColor = Global.Config.HexBackgrndColor; MemoryViewerBox.ForeColor = Global.Config.HexForegrndColor; Header.BackColor = Global.Config.HexBackgrndColor; Header.ForeColor = Global.Config.HexForegrndColor; } public void SaveConfigSettings() { if (HexFind1.IsHandleCreated || !HexFind1.IsDisposed) { HexFind1.Close(); } Global.Config.AutoLoadHexEditor = AutoLoad; Global.Config.HexEditorSaveWindowPosition = SaveWindowPosition; if (SaveWindowPosition) { Global.Config.HexEditorWndx = loaded ? Location.X : Wndx; Global.Config.HexEditorWndy = loaded ? Location.Y : Wndy; Global.Config.HexEditorWidth = loaded ? Right - Left : Width_; Global.Config.HexEditorHeight = loaded ? Bottom - Top : Height_; } Global.Config.HexEditorBigEndian = BigEndian; Global.Config.HexEditorDataSize = DataSize; } private void HexEditor_Load(object sender, EventArgs e) { defaultWidth = Size.Width; //Save these first so that the user can restore to its original size defaultHeight = Size.Height; if (SaveWindowPosition) { if (Wndx >= 0 && Wndy >= 0) { Location = new Point(Wndx, Wndy); } if (Width_ >= 0 && Height_ >= 0) { Size = new Size(Width_, Height_); } } SetMemoryDomainMenu(); SetDataSize(DataSize); UpdateValues(); loaded = true; } private void exitToolStripMenuItem_Click(object sender, EventArgs e) { Close(); } public void UpdateValues() { if (!IsHandleCreated || IsDisposed) return; AddressesLabel.Text = GenerateMemoryViewString(); AddressLabel.Text = GenerateAddressString(); } public string GenerateAddressString() { StringBuilder addrStr = new StringBuilder(); for (int i = 0; i < RowsVisible; i++) { row = i + vScrollBar1.Value; addr = (row << 4); if (addr >= Domain.Size) break; if (NumDigits == 4) { addrStr.Append(" "); //Hack to line things up better between 4 and 6 } else if (NumDigits == 6) { addrStr.Append(" "); } addrStr.Append(String.Format("{0:X" + NumDigits + "}", addr)); addrStr.Append('\n'); } return addrStr.ToString(); } public string GenerateMemoryViewString() { StringBuilder rowStr = new StringBuilder(); for (int i = 0; i < RowsVisible; i++) { row = i + vScrollBar1.Value; addr = (row << 4); if (addr >= Domain.Size) break; for (int j = 0; j < 16; j += DataSize) { if (addr + j + DataSize <= Domain.Size) { rowStr.AppendFormat(DigitFormatString, MakeValue(addr + j)); } else { for (int t = 0; t < DataSize; t++) rowStr.Append(" "); rowStr.Append(' '); } } rowStr.Append(" | "); for (int k = 0; k < 16; k++) { if (addr + k < Domain.Size) { rowStr.Append(Remap(Domain.PeekByte(addr + k))); } } rowStr.AppendLine(); } return rowStr.ToString(); } static char Remap(byte val) { if (val < ' ') return '.'; else if (val >= 0x80) return '.'; else return (char)val; } private int MakeValue(int address) { switch (DataSize) { default: case 1: return Domain.PeekByte(address); case 2: if (BigEndian) { int value = 0; value |= Domain.PeekByte(address) << 8; value |= Domain.PeekByte(address + 1); return value; } else { int value = 0; value |= Domain.PeekByte(address); value |= Domain.PeekByte(address + 1) << 8; return value; } case 4: if (BigEndian) { int value = 0; value |= Domain.PeekByte(address) << 24; value |= Domain.PeekByte(address + 1) << 16; value |= Domain.PeekByte(address + 2) << 8; value |= Domain.PeekByte(address + 3) << 0; return value; } else { int value = 0; value |= Domain.PeekByte(address) << 0; value |= Domain.PeekByte(address + 1) << 8; value |= Domain.PeekByte(address + 2) << 16; value |= Domain.PeekByte(address + 3) << 24; return value; } } } private int? GetDomainInt(string name) { for (int i = 0; i < Global.Emulator.MemoryDomains.Count; i++) { if (Global.Emulator.MemoryDomains[i].Name == name) { return i; } } return null; } public void SetDomain(MemoryDomain domain) { Domain = domain; int? theDomain = GetDomainInt(Domain.Name); SetMemoryDomain(theDomain ?? 0); SetHeader(); } public void Restart() { if (!IsHandleCreated || IsDisposed) return; int? theDomain; if (Domain.Name.ToLower() == "rom file") { theDomain = 999; } else { theDomain = GetDomainInt(Domain.Name); } SetMemoryDomainMenu(); //Calls update routines if (theDomain != null) { SetMemoryDomain((int)theDomain); } SetHeader(); ResetScrollBar(); SetDataSize(DataSize); UpdateValues(); AddressLabel.Text = GenerateAddressString(); } private void restoreWindowSizeToolStripMenuItem_Click(object sender, EventArgs e) { Size = new Size(defaultWidth, defaultHeight); SetUpScrollBar(); } private void autoloadToolStripMenuItem_Click(object sender, EventArgs e) { AutoLoad ^= true; } private void optionsToolStripMenuItem_DropDownOpened(object sender, EventArgs e) { enToolStripMenuItem.Checked = BigEndian; switch (DataSize) { default: case 1: byteToolStripMenuItem.Checked = true; byteToolStripMenuItem1.Checked = false; byteToolStripMenuItem2.Checked = false; break; case 2: byteToolStripMenuItem.Checked = false; byteToolStripMenuItem1.Checked = true; byteToolStripMenuItem2.Checked = false; break; case 4: byteToolStripMenuItem.Checked = false; byteToolStripMenuItem1.Checked = false; byteToolStripMenuItem2.Checked = true; break; } if (HighlightedAddress.HasValue && IsFrozen(HighlightedAddress.Value)) { freezeAddressToolStripMenuItem.Image = Properties.Resources.Unfreeze; freezeAddressToolStripMenuItem.Text = "Un&freeze Address"; } else { freezeAddressToolStripMenuItem.Image = Properties.Resources.Freeze; freezeAddressToolStripMenuItem.Text = "&Freeze Address"; } if (HighlightedAddress.HasValue) { addToRamWatchToolStripMenuItem1.Enabled = true; freezeAddressToolStripMenuItem.Enabled = true; } else { addToRamWatchToolStripMenuItem1.Enabled = false; freezeAddressToolStripMenuItem.Enabled = false; } } public void SetMemoryDomain(MemoryDomain d) { Domain = d; if (d.Endian == Endian.Big) BigEndian = true; else BigEndian = false; maxRow = Domain.Size / 2; SetUpScrollBar(); if (0 >= vScrollBar1.Minimum && 0 <= vScrollBar1.Maximum) { vScrollBar1.Value = 0; } Refresh(); } private bool CurrentROMIsArchive() { string path = GlobalWinF.MainForm.CurrentlyOpenRom; if (path == null) { return false; } using (var file = new HawkFile()) { file.Open(path); if (!file.Exists) { return false; } if (file.IsArchive) { return true; } else { return false; } } } private byte[] GetRomBytes() { string path = GlobalWinF.MainForm.CurrentlyOpenRom; if (path == null) { return null; } using (var file = new HawkFile()) { file.Open(path); if (!file.Exists) { return null; } if (file.IsArchive) { var stream = file.GetStream(); return Util.ReadAllBytes(stream); } else { return File.ReadAllBytes(path); } } } private void SetMemoryDomain(int pos) { // THIS IS HORRIBLE. if (pos == 999) { // THIS IS HORRIBLE. ROM = GetRomBytes() ?? new byte[] { 0xFF }; // THIS IS HORRIBLE. ROMDomain = new MemoryDomain("ROM File", ROM.Length, Endian.Little, i => ROM[i], (i, value) => ROM[i] = value); // THIS IS HORRIBLE. Domain = ROMDomain; } else if (pos < Global.Emulator.MemoryDomains.Count) //Sanity check { SetMemoryDomain(Global.Emulator.MemoryDomains[pos]); } SetHeader(); UpdateGroupBoxTitle(); ResetScrollBar(); UpdateValues(); MemoryViewerBox.Refresh(); } private void UpdateGroupBoxTitle() { string memoryDomain = Domain.ToString(); string systemID = Global.Emulator.SystemId; int addresses = Domain.Size / DataSize; string addressesString = "0x" + string.Format("{0:X8}", addresses).TrimStart('0'); //if ((addresses & 0x3FF) == 0) // addressesString = (addresses >> 10).ToString() + "K"; //else addressesString = addresses.ToString(); MemoryViewerBox.Text = systemID + " " + memoryDomain + " - " + addressesString + " addresses"; } private void SetMemoryDomainMenu() { memoryDomainsToolStripMenuItem.DropDownItems.Clear(); for (int i = 0; i < Global.Emulator.MemoryDomains.Count; i++) { if (Global.Emulator.MemoryDomains[i].Size > 0) { string str = Global.Emulator.MemoryDomains[i].ToString(); var item = new ToolStripMenuItem { Text = str }; { int z = i; item.Click += (o, ev) => SetMemoryDomain(z); } if (i == 0) { SetMemoryDomain(i); } memoryDomainsToolStripMenuItem.DropDownItems.Add(item); domainMenuItems.Add(item); } } //Add ROM File memory domain // THIS IS HORRIBLE. var rom_item = new ToolStripMenuItem { Text = "ROM File" }; rom_item.Click += (o, ev) => SetMemoryDomain(999); //999 will denote ROM file memoryDomainsToolStripMenuItem.DropDownItems.Add(rom_item); domainMenuItems.Add(rom_item); } private void goToAddressToolStripMenuItem_Click(object sender, EventArgs e) { GoToSpecifiedAddress(); } private int GetNumDigits(Int32 i) { if (i <= 0x10000) return 4; if (i <= 0x1000000) return 6; else return 8; } private Point GetPromptPoint() { Point p = new Point(MemoryViewerBox.Location.X + 30, MemoryViewerBox.Location.Y + 30); return PointToScreen(p); } public void GoToSpecifiedAddress() { InputPrompt i = new InputPrompt { Text = "Go to Address" }; i._Location = GetPromptPoint(); i.SetMessage("Enter a hexadecimal value"); GlobalWinF.Sound.StopSound(); i.ShowDialog(); GlobalWinF.Sound.StartSound(); if (i.UserOK) { if (InputValidate.IsValidHexNumber(i.UserText)) { GoToAddress(int.Parse(i.UserText, NumberStyles.HexNumber)); } } AddressLabel.Text = GenerateAddressString(); } private void ClearNibbles() { for (int x = 0; x < 8; x++) { nibbles[x] = 'G'; } } private void GoToAddress(int address) { if (address < 0) { address = 0; } if (address >= Domain.Size) { address = Domain.Size - 1; } SetHighlighted(address); ClearNibbles(); UpdateValues(); MemoryViewerBox.Refresh(); AddressLabel.Text = GenerateAddressString(); } public void SetToAddresses(List addresses) { if (addresses.Any()) { SetHighlighted(addresses[0]); SecondaryHighlightedAddresses.Clear(); SecondaryHighlightedAddresses.AddRange(addresses.Where(x => x != addresses[0]).ToList()); ClearNibbles(); UpdateValues(); MemoryViewerBox.Refresh(); AddressLabel.Text = GenerateAddressString(); } } public void SetHighlighted(int address) { if (address < 0) { address = 0; } if (address >= Domain.Size) { address = Domain.Size - 1; } if (!IsVisible(address)) { int v = (address / 16) - RowsVisible + 1; if (v < 0) { v = 0; } vScrollBar1.Value = v; } addressHighlighted = address; addressOver = address; ClearNibbles(); info = String.Format(NumDigitsStr, addressOver); UpdateFormText(); } private void UpdateFormText() { if (addressHighlighted >= 0) Text = "Hex Editor - Editing Address 0x" + String.Format(NumDigitsStr, addressHighlighted); else Text = "Hex Editor"; } public bool IsVisible(int address) { int i = address >> 4; return i >= vScrollBar1.Value && i < (RowsVisible + vScrollBar1.Value); } private void HexEditor_Resize(object sender, EventArgs e) { SetUpScrollBar(); UpdateValues(); } private void SetHeader() { switch (DataSize) { case 1: Header.Text = " 0 1 2 3 4 5 6 7 8 9 A B C D E F"; break; case 2: Header.Text = " 0 2 4 6 8 A C E"; break; case 4: Header.Text = " 0 4 8 C"; break; } NumDigits = GetNumDigits(Domain.Size); NumDigitsStr = "{0:X" + NumDigits.ToString() + "} "; } public void SetDataSize(int size) { if (size == 1 || size == 2 || size == 4) DataSize = size; DigitFormatString = "{0:X" + (DataSize * 2).ToString() + "} "; SetHeader(); UpdateGroupBoxTitle(); UpdateValues(); } private void byteToolStripMenuItem_Click(object sender, EventArgs e) { SetDataSize(1); } private void byteToolStripMenuItem1_Click(object sender, EventArgs e) { SetDataSize(2); } private void byteToolStripMenuItem2_Click(object sender, EventArgs e) { SetDataSize(4); } private void enToolStripMenuItem_Click(object sender, EventArgs e) { BigEndian ^= true; UpdateValues(); } private Watch MakeWatch(int address) { switch (DataSize) { default: case 1: return new ByteWatch(Domain, address, Watch.DisplayType.Hex, BigEndian, String.Empty); case 2: return new WordWatch(Domain, address, Watch.DisplayType.Hex, BigEndian, String.Empty); case 4: return new DWordWatch(Domain, address, Watch.DisplayType.Hex, BigEndian, String.Empty); } } private void AddToRamWatch() { if (HighlightedAddress.HasValue || SecondaryHighlightedAddresses.Count > 0) { GlobalWinF.MainForm.LoadRamWatch(true); } if (HighlightedAddress.HasValue) { GlobalWinF.MainForm.RamWatch1.AddWatch(MakeWatch(HighlightedAddress.Value)); } foreach (int i in SecondaryHighlightedAddresses) { GlobalWinF.MainForm.RamWatch1.AddWatch(MakeWatch(i)); } } private void PokeAddress() { var addresses = new List(); if (HighlightedAddress.HasValue) { addresses.Add(HighlightedAddress.Value); } if (SecondaryHighlightedAddresses.Count > 0) { addresses.AddRange(SecondaryHighlightedAddresses); } if (addresses.Any()) { var poke = new RamPoke { InitialLocation = GetAddressCoordinates(addresses[0]) }; var Watches = new List(); foreach (var address in addresses) { Watches.Add(Watch.GenerateWatch( Domain, address, (Watch.WatchSize)DataSize, Watch.DisplayType.Hex, String.Empty, BigEndian)); } poke.SetWatch(Watches); GlobalWinF.Sound.StopSound(); var result = poke.ShowDialog(); UpdateValues(); GlobalWinF.Sound.StartSound(); } } public int GetPointedAddress() { if (addressOver >= 0) { return addressOver; } else { return -1; //Negative = no address pointed } } public void PokeHighlighted(int value) { //TODO: 4 byte if (addressHighlighted >= 0) { switch (DataSize) { default: case 1: Domain.PokeByte(addressHighlighted, (byte)value); break; case 2: PokeWord(addressHighlighted, (byte)(value % 256), (byte)value); break; case 4: break; } } } private void addToRamWatchToolStripMenuItem_Click(object sender, EventArgs e) { AddToRamWatch(); } private void addToRamWatchToolStripMenuItem1_Click(object sender, EventArgs e) { AddToRamWatch(); } private void saveWindowsSettingsToolStripMenuItem_Click(object sender, EventArgs e) { SaveWindowPosition ^= true; } private void settingsToolStripMenuItem_DropDownOpened(object sender, EventArgs e) { autoloadToolStripMenuItem.Checked = AutoLoad; saveWindowsSettingsToolStripMenuItem.Checked = SaveWindowPosition; } private void freezeToolStripMenuItem_Click(object sender, EventArgs e) { ToggleFreeze(); } public int? HighlightedAddress { get { if (addressHighlighted >= 0) { return addressHighlighted; } else { return null; //Negative = no address highlighted } } } private bool IsFrozen(int address) { return Global.CheatList.IsActive(Domain, address); } private void ToggleFreeze() { if (HighlightedAddress.HasValue && IsFrozen(HighlightedAddress.Value)) { UnFreezeAddress(HighlightedAddress.Value); } else { FreezeAddress(HighlightedAddress.Value); } foreach (int i in SecondaryHighlightedAddresses) { if (IsFrozen(i)) { UnFreezeAddress(i); } else { FreezeAddress(i); } } UpdateRelatedDialogs(); } private void UnFreezeAddress(int address) //TODO: refactor to int? { if (address >= 0) { var cheats = Global.CheatList.Where(x => x.Contains(address)).ToList(); Global.CheatList.RemoveRange(cheats); } MemoryViewerBox.Refresh(); ToolHelpers.UpdateCheatRelatedTools(); } private Watch.WatchSize WatchSize { get { switch (DataSize) { default: case 1: return Watch.WatchSize.Byte; case 2: return Watch.WatchSize.Word; case 4: return Watch.WatchSize.DWord; } } } private void UpdateRelatedDialogs() { GlobalWinF.MainForm.UpdateCheatStatus(); GlobalWinF.MainForm.RamSearch1.UpdateValues(); GlobalWinF.MainForm.RamWatch1.UpdateValues(); GlobalWinF.MainForm.Cheats_UpdateValues(); UpdateValues(); } private void FreezeAddress(int address) //TODO refactor to int? { if (address >= 0) { Watch watch = Watch.GenerateWatch( Domain, address, WatchSize, Watch.DisplayType.Hex, String.Empty, BigEndian); Global.CheatList.Add(new Cheat( watch, watch.Value.Value, compare: null, enabled: true)); MemoryViewerBox.Refresh(); UpdateRelatedDialogs(); } } private void freezeAddressToolStripMenuItem_Click(object sender, EventArgs e) { ToggleFreeze(); } private void CheckDomainMenuItems() { foreach (ToolStripMenuItem t in domainMenuItems) { if (Domain.Name == t.Text) { t.Checked = true; } else { t.Checked = false; } } } private void memoryDomainsToolStripMenuItem_DropDownOpened(object sender, EventArgs e) { CheckDomainMenuItems(); } private void dumpToFileToolStripMenuItem_Click(object sender, EventArgs e) { SaveAsText(); } private void SaveAsText() { var file = GetSaveFileFromUser(); if (file != null) { using (StreamWriter sw = new StreamWriter(file.FullName)) { string str = ""; for (int x = 0; x < Domain.Size / 16; x++) { for (int y = 0; y < 16; y++) { str += String.Format("{0:X2} ", Domain.PeekByte((x * 16) + y)); } str += "\r\n"; } sw.WriteLine(str); } } } private void SaveAsBinary() { SaveFileBinary(GetBinarySaveFileFromUser()); } private void SaveFileBinary(FileInfo file) { if (file != null) { using (BinaryWriter binWriter = new BinaryWriter(File.Open(file.FullName, FileMode.Create))) { for (int x = 0; x < Domain.Size; x++) { binWriter.Write(Domain.PeekByte(x)); } } } } private FileInfo GetSaveFileFromUser() { var sfd = new SaveFileDialog(); if (!(Global.Emulator is NullEmulator)) sfd.FileName = PathManager.FilesystemSafeName(Global.Game); else sfd.FileName = "MemoryDump"; sfd.InitialDirectory = PathManager.GetPlatformBase(Global.Emulator.SystemId); sfd.Filter = "Text (*.txt)|*.txt|All Files|*.*"; sfd.RestoreDirectory = true; GlobalWinF.Sound.StopSound(); var result = sfd.ShowDialog(); GlobalWinF.Sound.StartSound(); if (result != DialogResult.OK) return null; var file = new FileInfo(sfd.FileName); return file; } private string GetSaveFileFilter() { if (Domain.Name == "ROM File") { string extension = Path.GetExtension(GlobalWinF.MainForm.CurrentlyOpenRom); return "Binary (*" + extension + ")|*" + extension + "|All Files|*.*"; } else { return "Binary (*.bin)|*.bin|All Files|*.*"; } } private FileInfo GetBinarySaveFileFromUser() { var sfd = new SaveFileDialog(); if (!(Global.Emulator is NullEmulator)) sfd.FileName = PathManager.FilesystemSafeName(Global.Game); else sfd.FileName = "MemoryDump"; sfd.InitialDirectory = PathManager.GetPlatformBase(Global.Emulator.SystemId); sfd.Filter = GetSaveFileFilter(); sfd.RestoreDirectory = true; GlobalWinF.Sound.StopSound(); var result = sfd.ShowDialog(); GlobalWinF.Sound.StartSound(); if (result != DialogResult.OK) return null; var file = new FileInfo(sfd.FileName); return file; } public void ResetScrollBar() { vScrollBar1.Value = 0; SetUpScrollBar(); Refresh(); } public void SetUpScrollBar() { RowsVisible = ((MemoryViewerBox.Height - (fontHeight * 2) - (fontHeight / 2)) / fontHeight); int totalRows = Domain.Size / 16; vScrollBar1.Maximum = totalRows - 1; vScrollBar1.LargeChange = RowsVisible; vScrollBar1.Visible = totalRows > RowsVisible; AddressLabel.Text = GenerateAddressString(); } private int GetPointedAddress(int x, int y) { int address; //Scroll value determines the first row int i = vScrollBar1.Value; int rowoffset = y / fontHeight; i += rowoffset; int colWidth; switch (DataSize) { default: case 1: colWidth = 3; break; case 2: colWidth = 5; break; case 4: colWidth = 9; break; } int column = (x /*- 43*/) / (fontWidth * colWidth); int start = GetTextOffset() - 50; if (x > start) { column = (x - start) / (fontWidth / DataSize); } if (i >= 0 && i <= maxRow && column >= 0 && column < (16 / DataSize)) { address = i * 16 + (column * DataSize); info = String.Format(NumDigitsStr, addressOver); } else { address = -1; info = ""; } return address; } private void HexEditor_ResizeEnd(object sender, EventArgs e) { SetUpScrollBar(); } private void AddressesLabel_MouseMove(object sender, MouseEventArgs e) { addressOver = GetPointedAddress(e.X, e.Y); if (MouseIsDown) { DoShiftClick(); MemoryViewerBox.Refresh(); } } private void AddressesLabel_MouseClick(object sender, MouseEventArgs e) { } private void DoShiftClick() { if (addressOver >= 0) { SecondaryHighlightedAddresses.Clear(); if (addressOver < addressHighlighted) { for (int x = addressOver; x < addressHighlighted; x++) { SecondaryHighlightedAddresses.Add(x); } } else if (addressOver > addressHighlighted) { for (int x = addressHighlighted + DataSize; x <= addressOver; x++) { SecondaryHighlightedAddresses.Add(x); } } } } private void ClearHighlighted() { addressHighlighted = -1; UpdateFormText(); MemoryViewerBox.Refresh(); } private Point GetAddressCoordinates(int address) { switch (DataSize) { default: case 1: return new Point(((address % 16) * (fontWidth * 3)) + 67, (((address / 16) - vScrollBar1.Value) * fontHeight) + 30); case 2: return new Point((((address % 16) / DataSize) * (fontWidth * 5)) + 67, (((address / 16) - vScrollBar1.Value) * fontHeight) + 30); case 4: return new Point((((address % 16) / DataSize) * (fontWidth * 9)) + 67, (((address / 16) - vScrollBar1.Value) * fontHeight) + 30); } } private int GetTextOffset() { int start; switch (DataSize) { default: case 1: start = (16 * (fontWidth * 3)) + 67; break; case 2: start = ((16 / DataSize) * (fontWidth * 5)) + 67; break; case 4: start = ((16 / DataSize) * (fontWidth * 9)) + 67; break; } start += (fontWidth * 4); return start; } private int GetTextX(int address) { int start = GetTextOffset(); return start + ((address % 16) * fontWidth); } private void MemoryViewerBox_Paint(object sender, PaintEventArgs e) { var activeCheats = Global.CheatList.Where(x => x.Enabled); foreach (var cheat in activeCheats) { if (IsVisible(cheat.Address.Value)) { if (Domain.ToString() == cheat.Domain.Name) { Rectangle rect = new Rectangle(GetAddressCoordinates(cheat.Address.Value), new Size(15 * DataSize, fontHeight)); e.Graphics.DrawRectangle(new Pen(Brushes.Black), rect); e.Graphics.FillRectangle(new SolidBrush(Global.Config.HexFreezeColor), rect); } } } if (addressHighlighted >= 0 && IsVisible(addressHighlighted)) { Point point = GetAddressCoordinates(addressHighlighted); int textX = GetTextX(addressHighlighted); Point textpoint = new Point(textX, point.Y); Rectangle rect = new Rectangle(point, new Size(15 * DataSize, fontHeight)); e.Graphics.DrawRectangle(new Pen(Brushes.Black), rect); Rectangle textrect = new Rectangle(textpoint, new Size((8 * DataSize), fontHeight)); if (Global.CheatList.IsActive(Domain, addressHighlighted)) { e.Graphics.FillRectangle(new SolidBrush(Global.Config.HexHighlightFreezeColor), rect); e.Graphics.FillRectangle(new SolidBrush(Global.Config.HexHighlightFreezeColor), textrect); } else { e.Graphics.FillRectangle(new SolidBrush(Global.Config.HexHighlightColor), rect); e.Graphics.FillRectangle(new SolidBrush(Global.Config.HexHighlightColor), textrect); } } foreach (int address in SecondaryHighlightedAddresses) { Point point = GetAddressCoordinates(address); int textX = GetTextX(address); Point textpoint = new Point(textX, point.Y); Rectangle rect = new Rectangle(point, new Size(15 * DataSize, fontHeight)); e.Graphics.DrawRectangle(new Pen(Brushes.Black), rect); Rectangle textrect = new Rectangle(textpoint, new Size(8, fontHeight)); if (Global.CheatList.IsActive(Domain, address)) { e.Graphics.FillRectangle(new SolidBrush(Global.Config.HexHighlightFreezeColor), rect); e.Graphics.FillRectangle(new SolidBrush(Global.Config.HexHighlightFreezeColor), textrect); } else { e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(0x77FFD4D4)), rect); e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(0x77FFD4D4)), textrect); } } if (HasNibbles()) { e.Graphics.DrawString(MakeNibbles(), new Font("Courier New", 8, FontStyle.Italic), Brushes.Black, new Point(158, 4)); } } private bool HasNibbles() { for (int x = 0; x < (DataSize * 2); x++) { if (nibbles[x] != 'G') return true; } return false; } private string MakeNibbles() { string str = ""; for (int x = 0; x < (DataSize * 2); x++) { if (nibbles[x] != 'G') str += nibbles[x]; else break; } return str; } private void AddressesLabel_MouseLeave(object sender, EventArgs e) { addressOver = -1; MemoryViewerBox.Refresh(); } private void HexEditor_KeyDown(object sender, KeyEventArgs e) { if (e.Control && e.KeyCode == Keys.G) { GoToSpecifiedAddress(); return; } if (e.Control && e.KeyCode == Keys.P) { PokeAddress(); return; } int newHighlighted; switch (e.KeyCode) { case Keys.Up: newHighlighted = addressHighlighted - 16; if (e.Modifiers == Keys.Shift) { for (int i = newHighlighted + 1; i <= addressHighlighted; i++) { AddToSecondaryHighlights(i); } GoToAddress(newHighlighted); } else { SecondaryHighlightedAddresses.Clear(); GoToAddress(newHighlighted); } break; case Keys.Down: newHighlighted = addressHighlighted + 16; if (e.Modifiers == Keys.Shift) { for (int i = newHighlighted - 16; i < newHighlighted; i++) { AddToSecondaryHighlights(i); } GoToAddress(newHighlighted); } else { SecondaryHighlightedAddresses.Clear(); GoToAddress(newHighlighted); } break; case Keys.Left: newHighlighted = addressHighlighted - (1 * DataSize); if (e.Modifiers == Keys.Shift) { AddToSecondaryHighlights(addressHighlighted); GoToAddress(newHighlighted); } else { SecondaryHighlightedAddresses.Clear(); GoToAddress(newHighlighted); } break; case Keys.Right: newHighlighted = addressHighlighted + (1 * DataSize); if (e.Modifiers == Keys.Shift) { AddToSecondaryHighlights(addressHighlighted); GoToAddress(newHighlighted); } else { SecondaryHighlightedAddresses.Clear(); GoToAddress(newHighlighted); } break; case Keys.PageUp: newHighlighted = addressHighlighted - (RowsVisible * 16); if (e.Modifiers == Keys.Shift) { for (int i = newHighlighted + 1; i <= addressHighlighted; i++) { AddToSecondaryHighlights(i); } GoToAddress(newHighlighted); } else { SecondaryHighlightedAddresses.Clear(); GoToAddress(newHighlighted); } break; case Keys.PageDown: newHighlighted = addressHighlighted + (RowsVisible * 16); if (e.Modifiers == Keys.Shift) { for (int i = addressHighlighted + 1; i < newHighlighted; i++) { AddToSecondaryHighlights(i); } GoToAddress(newHighlighted); } else { SecondaryHighlightedAddresses.Clear(); GoToAddress(newHighlighted); } break; case Keys.Tab: SecondaryHighlightedAddresses.Clear(); if (e.Modifiers == Keys.Shift) GoToAddress(addressHighlighted - 8); else GoToAddress(addressHighlighted + 8); break; case Keys.Home: if (e.Modifiers == Keys.Shift) { for (int i = 1; i <= addressHighlighted; i++) { AddToSecondaryHighlights(i); } GoToAddress(0); } else { SecondaryHighlightedAddresses.Clear(); GoToAddress(0); } break; case Keys.End: newHighlighted = Domain.Size - (DataSize); if (e.Modifiers == Keys.Shift) { for (int i = addressHighlighted; i < newHighlighted; i++) { AddToSecondaryHighlights(i); } GoToAddress(newHighlighted); } else { SecondaryHighlightedAddresses.Clear(); GoToAddress(newHighlighted); } break; case Keys.Add: IncrementHighlighted(); UpdateValues(); break; case Keys.Subtract: DecrementHighlighted(); UpdateValues(); break; case Keys.Space: ToggleFreeze(); break; case Keys.Delete: if (e.Modifiers == Keys.Shift) { ToolHelpers.UnfreezeAll(); } else { if (HighlightedAddress.HasValue) { UnFreezeAddress(HighlightedAddress.Value); } } break; case Keys.W: if (e.Modifiers == Keys.Control) { AddToRamWatch(); } break; case Keys.Escape: SecondaryHighlightedAddresses.Clear(); ClearHighlighted(); break; } } private void AddToSecondaryHighlights(int address) { if (address >= 0 && address < Domain.Size) { SecondaryHighlightedAddresses.Add(address); } } private bool IsHexKeyCode(char key) { if (key >= 48 && key <= 57) //0-9 { return true; } else if (key >= 65 && key <= 70) //A-F { return true; } else if (key >= 96 && key <= 106) //0-9 Numpad { return true; } else { return false; } } //Winform key events suck at the numberpad, so this is necessary private char ForceCorrectKeyString(Keys keycode) { if ((int)keycode >= 96 && (int)keycode <= 106) { return (char)((int)keycode - 48); } else { return (char)keycode; } } private void HexEditor_KeyUp(object sender, KeyEventArgs e) { if (!IsHexKeyCode((char)e.KeyCode)) { e.Handled = true; return; } if (e.Control || e.Shift || e.Alt) //If user is pressing one of these, don't type into the hex editor { return; } switch (DataSize) { default: case 1: if (nibbles[0] == 'G') { nibbles[0] = ForceCorrectKeyString(e.KeyCode); info = nibbles[0].ToString(); } else { string temp = nibbles[0].ToString() + ForceCorrectKeyString(e.KeyCode).ToString(); byte x = byte.Parse(temp, NumberStyles.HexNumber); Domain.PokeByte(addressHighlighted, x); ClearNibbles(); SetHighlighted(addressHighlighted + 1); UpdateValues(); } break; case 2: if (nibbles[0] == 'G') { nibbles[0] = ForceCorrectKeyString(e.KeyCode); info = nibbles[0].ToString(); } else if (nibbles[1] == 'G') { nibbles[1] = ForceCorrectKeyString(e.KeyCode); info = nibbles[1].ToString(); } else if (nibbles[2] == 'G') { nibbles[2] = ForceCorrectKeyString(e.KeyCode); info = nibbles[2].ToString(); } else if (nibbles[3] == 'G') { string temp = nibbles[0].ToString() + nibbles[1].ToString(); byte x1 = byte.Parse(temp, NumberStyles.HexNumber); string temp2 = nibbles[2].ToString() + ((char)e.KeyCode).ToString(); byte x2 = byte.Parse(temp2, NumberStyles.HexNumber); PokeWord(addressHighlighted, x1, x2); ClearNibbles(); SetHighlighted(addressHighlighted + 2); UpdateValues(); } break; case 4: if (nibbles[0] == 'G') { nibbles[0] = ForceCorrectKeyString(e.KeyCode); info = nibbles[0].ToString(); } else if (nibbles[1] == 'G') { nibbles[1] = ForceCorrectKeyString(e.KeyCode); info = nibbles[1].ToString(); } else if (nibbles[2] == 'G') { nibbles[2] = ForceCorrectKeyString(e.KeyCode); info = nibbles[2].ToString(); } else if (nibbles[3] == 'G') { nibbles[3] = ForceCorrectKeyString(e.KeyCode); info = nibbles[3].ToString(); } else if (nibbles[4] == 'G') { nibbles[4] = ForceCorrectKeyString(e.KeyCode); info = nibbles[4].ToString(); } else if (nibbles[5] == 'G') { nibbles[5] = ForceCorrectKeyString(e.KeyCode); info = nibbles[5].ToString(); } else if (nibbles[6] == 'G') { nibbles[6] = ForceCorrectKeyString(e.KeyCode); info = nibbles[6].ToString(); } else if (nibbles[7] == 'G') { string temp = nibbles[0].ToString() + nibbles[1].ToString(); byte x1 = byte.Parse(temp, NumberStyles.HexNumber); string temp2 = nibbles[2].ToString() + nibbles[3].ToString(); byte x2 = byte.Parse(temp2, NumberStyles.HexNumber); string temp3 = nibbles[4].ToString() + nibbles[5].ToString(); byte x3 = byte.Parse(temp3, NumberStyles.HexNumber); string temp4 = nibbles[6].ToString() + ForceCorrectKeyString(e.KeyCode).ToString(); byte x4 = byte.Parse(temp4, NumberStyles.HexNumber); PokeWord(addressHighlighted, x1, x2); PokeWord(addressHighlighted + 2, x3, x4); ClearNibbles(); SetHighlighted(addressHighlighted + 4); UpdateValues(); } break; } MemoryViewerBox.Refresh(); } private void PokeWord(int address, byte _1, byte _2) { if (BigEndian) { Domain.PokeByte(address, _2); Domain.PokeByte(address + 1, _1); } else { Domain.PokeByte(address, _1); Domain.PokeByte(address + 1, _2); } } private void unfreezeAllToolStripMenuItem_Click(object sender, EventArgs e) { ToolHelpers.UnfreezeAll(); } private void unfreezeAllToolStripMenuItem1_Click(object sender, EventArgs e) { ToolHelpers.UnfreezeAll(); } private void HexEditor_MouseWheel(object sender, MouseEventArgs e) { int delta = 0; if (e.Delta > 0) delta = -1; else if (e.Delta < 0) delta = 1; int newValue = vScrollBar1.Value + delta; if (newValue < vScrollBar1.Minimum) newValue = vScrollBar1.Minimum; if (newValue > vScrollBar1.Maximum - vScrollBar1.LargeChange + 1) newValue = vScrollBar1.Maximum - vScrollBar1.LargeChange + 1; if (newValue != vScrollBar1.Value) { vScrollBar1.Value = newValue; MemoryViewerBox.Refresh(); } } private void IncrementAddress(int address) { if (Global.CheatList.IsActive(Domain, address)) { Global.CheatList.FirstOrDefault(x => x.Domain == Domain && x.Address == address).Increment(); Global.CheatList.FlagChanges(); } else { Endian endian = BigEndian ? Endian.Big : Endian.Little; switch (DataSize) { default: case 1: byte thebyte = Domain.PeekByte(address); Domain.PokeByte( address, (byte)(Domain.PeekByte(address) + 1) ); break; case 2: Domain.PokeWord( address, (ushort)(Domain.PeekWord(address, endian) + 1), endian ); break; case 4: Domain.PokeDWord( address, Domain.PeekDWord(address, endian) + 1, endian ); break; } } } private void DecrementAddress(int address) { if (Global.CheatList.IsActive(Domain, address)) { Global.CheatList.FirstOrDefault(x => x.Domain == Domain && x.Address == address).Decrement(); Global.CheatList.FlagChanges(); } else { Endian endian = BigEndian ? Endian.Big : Endian.Little; switch (DataSize) { default: case 1: byte thebyte = Domain.PeekByte(address); Domain.PokeByte( address, (byte)(Domain.PeekByte(address) - 1) ); break; case 2: Domain.PokeWord( address, (ushort)(Domain.PeekWord(address, endian) - 1), endian ); break; case 4: Domain.PokeDWord( address, Domain.PeekDWord(address, endian) - 1, endian ); break; } } } private void IncrementHighlighted() { if (HighlightedAddress.HasValue) { IncrementAddress(HighlightedAddress.Value); } if (SecondaryHighlightedAddresses.Any()) { foreach (int address in SecondaryHighlightedAddresses) { IncrementAddress(address); } } } private void DecrementHighlighted() { if (HighlightedAddress.HasValue) { DecrementAddress(HighlightedAddress.Value); } if (SecondaryHighlightedAddresses.Any()) { foreach (int address in SecondaryHighlightedAddresses) { DecrementAddress(address); } } } private void incrementToolStripMenuItem_Click(object sender, EventArgs e) { IncrementHighlighted(); } private void decrementToolStripMenuItem_Click(object sender, EventArgs e) { DecrementHighlighted(); } private void ViewerContextMenuStrip_Opening(object sender, CancelEventArgs e) { copyToolStripMenuItem1.Visible = HighlightedAddress.HasValue || SecondaryHighlightedAddresses.Any(); IDataObject iData = Clipboard.GetDataObject(); pasteToolStripMenuItem1.Visible = iData != null && iData.GetDataPresent(DataFormats.Text); if (HighlightedAddress.HasValue && IsFrozen(HighlightedAddress.Value)) { freezeToolStripMenuItem.Text = "Un&freeze"; freezeToolStripMenuItem.Image = Properties.Resources.Unfreeze; } else { freezeToolStripMenuItem.Text = "&Freeze"; freezeToolStripMenuItem.Image = Properties.Resources.Freeze; } } private void gotoAddressToolStripMenuItem1_Click(object sender, EventArgs e) { GoToSpecifiedAddress(); } private string ValueString(int address) { if (address != -1) { return String.Format(DigitFormatString, MakeValue(address)).Trim(); } else { return String.Empty; } } private void OpenFindBox() { FindStr = GetFindValues(); if (!HexFind1.IsHandleCreated || HexFind1.IsDisposed) { HexFind1 = new HexFind(); Point p = PointToScreen(AddressesLabel.Location); HexFind1.SetLocation(p); HexFind1.SetInitialValue(FindStr); HexFind1.Show(); } else { HexFind1.SetInitialValue(FindStr); HexFind1.Focus(); } } private string GetFindValues() { if (HighlightedAddress.HasValue) { string values = ValueString(HighlightedAddress.Value); return SecondaryHighlightedAddresses.Aggregate(values, (current, x) => current + ValueString(x)); } else { return ""; } } public void FindNext(string value, Boolean wrap) { int found = -1; string search = value.Replace(" ", "").ToUpper(); if (search.Length == 0) return; int numByte = search.Length / 2; int startByte; if (addressHighlighted == -1) { startByte = 0; } else if (addressHighlighted >= (Domain.Size - 1 - numByte)) { startByte = 0; } else { startByte = addressHighlighted + DataSize; } for (int i = startByte; i < (Domain.Size - numByte); i++) { StringBuilder ramblock = new StringBuilder(); for (int j = 0; j < numByte; j++) { ramblock.Append(String.Format("{0:X2}", (int)Domain.PeekByte(i + j))); } string block = ramblock.ToString().ToUpper(); if (search == block) { found = i; break; } } if (found > -1) { HighlightSecondaries(search, found); GoToAddress(found); FindStr = search; MemoryViewerBox.Focus(); } else if (wrap == false) // Search the opposite direction if not found { FindPrev(value, true); } } public void FindPrev(string value, Boolean wrap) { int found = -1; string search = value.Replace(" ", "").ToUpper(); if (search.Length == 0) return; int numByte = search.Length / 2; int startByte; if (addressHighlighted == -1) { startByte = Domain.Size - DataSize; } else { startByte = addressHighlighted - 1; } for (int i = startByte; i >= 0; i--) { StringBuilder ramblock = new StringBuilder(); for (int j = 0; j < numByte; j++) { ramblock.Append(String.Format("{0:X2}", (int)Domain.PeekByte(i + j))); } string block = ramblock.ToString().ToUpper(); if (search == block) { found = i; break; } } if (found > -1) { HighlightSecondaries(search, found); GoToAddress(found); FindStr = search; MemoryViewerBox.Focus(); } else if (wrap == false) // Search the opposite direction if not found { FindPrev(value, true); } } private void HighlightSecondaries(string value, int found) { //This function assumes that the primary highlighted value has been set and sets the remaining characters in this string SecondaryHighlightedAddresses.Clear(); int addrLength = DataSize * 2; if (value.Length <= addrLength) { return; } int numToHighlight = ((value.Length / addrLength)) - 1; for (int i = 0; i < numToHighlight; i++) { SecondaryHighlightedAddresses.Add(found + 1 + i); } } private void Copy() { string value = HighlightedAddress.HasValue ? ValueString(HighlightedAddress.Value) : String.Empty; value = SecondaryHighlightedAddresses.Aggregate(value, (current, x) => current + ValueString(x)); if (!String.IsNullOrWhiteSpace(value)) { Clipboard.SetDataObject(value); } } private void copyToolStripMenuItem_Click(object sender, EventArgs e) { Copy(); } private void Paste() { IDataObject iData = Clipboard.GetDataObject(); if (iData != null && iData.GetDataPresent(DataFormats.Text)) { string clipboardRaw = (String)iData.GetData(DataFormats.Text); string hex = InputValidate.DoHexString(clipboardRaw); int numBytes = hex.Length / 2; for (int i = 0; i < numBytes; i++) { int value = int.Parse(hex.Substring(i * 2, 2), NumberStyles.HexNumber); int address = addressHighlighted + i; Domain.PokeByte(address, (byte)value); } UpdateValues(); } else { //Do nothing } } private void pasteToolStripMenuItem_Click(object sender, EventArgs e) { Paste(); } private void findToolStripMenuItem1_Click(object sender, EventArgs e) { OpenFindBox(); //Find(); } private void saveAsBinaryToolStripMenuItem_Click(object sender, EventArgs e) { SaveAsBinary(); } private void setColorsToolStripMenuItem_Click(object sender, EventArgs e) { HexColors_Form h = new HexColors_Form(); h.Show(); } private void setColorsToolStripMenuItem1_Click(object sender, EventArgs e) { HexColors_Form h = new HexColors_Form(); GlobalWinF.Sound.StopSound(); h.ShowDialog(); GlobalWinF.Sound.StartSound(); } private void resetToDefaultToolStripMenuItem1_Click(object sender, EventArgs e) { MemoryViewerBox.BackColor = Color.FromName("Control"); MemoryViewerBox.ForeColor = Color.FromName("ControlText"); menuStrip1.BackColor = Color.FromName("Control"); Header.BackColor = Color.FromName("Control"); Header.ForeColor = Color.FromName("ControlText"); Global.Config.HexMenubarColor = Color.FromName("Control"); Global.Config.HexForegrndColor = Color.FromName("ControlText"); Global.Config.HexBackgrndColor = Color.FromName("Control"); Global.Config.HexFreezeColor = Color.LightBlue; Global.Config.HexHighlightColor = Color.Pink; Global.Config.HexHighlightFreezeColor = Color.Violet; } private void copyToolStripMenuItem1_Click(object sender, EventArgs e) { Copy(); } private void pasteToolStripMenuItem1_Click(object sender, EventArgs e) { Paste(); } private void findNextToolStripMenuItem_Click(object sender, EventArgs e) { FindNext(FindStr, false); } private void findPrevToolStripMenuItem_Click(object sender, EventArgs e) { FindPrev(FindStr, false); } private void editToolStripMenuItem_DropDownOpened(object sender, EventArgs e) { if (String.IsNullOrWhiteSpace(FindStr)) { findNextToolStripMenuItem.Enabled = false; findPrevToolStripMenuItem.Enabled = false; } else { findNextToolStripMenuItem.Enabled = true; findPrevToolStripMenuItem.Enabled = true; } } private void saveToolStripMenuItem_Click(object sender, EventArgs e) { if (CurrentROMIsArchive()) { return; } else { FileInfo file = new FileInfo(GlobalWinF.MainForm.CurrentlyOpenRom); SaveFileBinary(file); } } private void fileToolStripMenuItem_DropDownOpened(object sender, EventArgs e) { if (Domain.Name == "ROM File") { if (!CurrentROMIsArchive()) { saveToolStripMenuItem.Visible = true; } else { saveToolStripMenuItem.Visible = false; } saveAsBinaryToolStripMenuItem.Text = "Save as ROM..."; } else { saveAsBinaryToolStripMenuItem.Text = "Save as binary..."; } } private void pokeAddressToolStripMenuItem_Click(object sender, EventArgs e) { PokeAddress(); } private void pokeAddressToolStripMenuItem1_Click(object sender, EventArgs e) { PokeAddress(); } private void vScrollBar1_ValueChanged(object sender, EventArgs e) { UpdateValues(); } private void AddressesLabel_MouseDown(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { int pointed_address = GetPointedAddress(e.X, e.Y); if (pointed_address >= 0) { if ((ModifierKeys & Keys.Control) == Keys.Control) { if (pointed_address == addressHighlighted) { ClearHighlighted(); } else if (SecondaryHighlightedAddresses.Contains(pointed_address)) { SecondaryHighlightedAddresses.Remove(pointed_address); } else { SecondaryHighlightedAddresses.Add(pointed_address); } } else if ((ModifierKeys & Keys.Shift) == Keys.Shift) { DoShiftClick(); } //else if (addressOver == addressHighlighted) //{ // ClearHighlighted(); //} else { SetHighlighted(pointed_address); SecondaryHighlightedAddresses.Clear(); FindStr = ""; } MemoryViewerBox.Refresh(); } MouseIsDown = true; } } private void AddressesLabel_MouseUp(object sender, MouseEventArgs e) { MouseIsDown = false; } private void AddressesLabel_Click(object sender, EventArgs e) { } private void MemoryViewerBox_Enter(object sender, EventArgs e) { } private void alwaysOnTopToolStripMenuItem_Click(object sender, EventArgs e) { alwaysOnTopToolStripMenuItem.Checked = alwaysOnTopToolStripMenuItem.Checked == false; this.TopMost = alwaysOnTopToolStripMenuItem.Checked; } } }