using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Globalization; using System.IO; using System.Linq; using System.Text; using System.Windows.Forms; using BizHawk.Common; using BizHawk.Common.NumberExtensions; using BizHawk.Common.StringExtensions; using BizHawk.Common.IOExtensions; using BizHawk.Emulation.Common; using BizHawk.Emulation.Common.IEmulatorExtensions; using BizHawk.Client.Common; using BizHawk.Client.EmuHawk.WinFormExtensions; using BizHawk.Client.EmuHawk.ToolExtensions; namespace BizHawk.Client.EmuHawk { // int to long TODO: 32 bit domains have more digits than the hex editor can account for and the address covers up the 0 column public partial class HexEditor : ToolFormBase, IToolFormAutoConfig { private class NullMemoryDomain : MemoryDomain { public override byte PeekByte(long addr) { return 0; } public override void PokeByte(long addr, byte val) { } public NullMemoryDomain() { EndianType = Endian.Unknown; Name = "Null"; Size = 1024; Writable = true; WordSize = 1; } } [RequiredService] private IMemoryDomains MemoryDomains { get; set; } [RequiredService] private IEmulator Emulator { get; set; } private int fontWidth; private int fontHeight; private readonly List _domainMenuItems = new List(); private readonly char[] _nibbles = { 'G', 'G', 'G', 'G', 'G', 'G', 'G', 'G' }; // G = off 0-9 & A-F are acceptable values private readonly List _secondaryHighlightedAddresses = new List(); private readonly Dictionary _textTable = new Dictionary(); private int _rowsVisible; private int _numDigits = 4; private string _numDigitsStr = "{0:X4}"; private string _digitFormatString = "{0:X2}"; private long _addressHighlighted = -1; private long _addressOver = -1; private long _maxRow; private MemoryDomain _domain = new NullMemoryDomain(); private long _row; private long _addr; private string _findStr = string.Empty; private bool _mouseIsDown; private byte[] _rom; private MemoryDomain _romDomain; private HexFind _hexFind = new HexFind(); [ConfigPersist] private bool BigEndian { get; set; } [ConfigPersist] private int DataSize { get; set; } [ConfigPersist] private RecentFiles RecentTables { get; set; } public HexEditor() { RecentTables = new RecentFiles(8); DataSize = 1; var font = new Font("Courier New", 8); // Measure the font. There seems to be some extra horizontal padding on the first // character so we'll see how much the width increases on the second character. var fontSize1 = TextRenderer.MeasureText("0", font); var fontSize2 = TextRenderer.MeasureText("00", font); fontWidth = fontSize2.Width - fontSize1.Width; fontHeight = fontSize1.Height; InitializeComponent(); AddressesLabel.BackColor = Color.Transparent; LoadConfigSettings(); SetHeader(); Closing += (o, e) => SaveConfigSettings(); Header.Font = font; AddressesLabel.Font = font; AddressLabel.Font = font; } private long? HighlightedAddress { get { if (_addressHighlighted >= 0) { return _addressHighlighted; } return null; // Negative = no address highlighted } } private WatchSize WatchSize { get { return (WatchSize)DataSize; } } #region API public bool UpdateBefore { get { return false; } } public bool AskSaveChanges() { return true; } public void NewUpdate(ToolFormUpdateType type) { } public void UpdateValues() { AddressesLabel.Text = GenerateMemoryViewString(true); AddressLabel.Text = GenerateAddressString(); } public void FastUpdate() { // Do nothing } private string _lastRom = string.Empty; public void Restart() { _rom = GetRomBytes(); _romDomain = MemoryDomain.FromByteArray("File on Disk", MemoryDomain.Endian.Little, _rom); if (_domain.Name == _romDomain.Name) { _domain = _romDomain; } else if (MemoryDomains.Any(x => x.Name == _domain.Name)) { _domain = MemoryDomains[_domain.Name]; } else { _domain = MemoryDomains.MainMemory; } BigEndian = _domain.EndianType == MemoryDomain.Endian.Big; _maxRow = _domain.Size / 2; // Don't reset scroll bar if restarting the same rom if (_lastRom != GlobalWin.MainForm.CurrentlyOpenRom) { _lastRom = GlobalWin.MainForm.CurrentlyOpenRom; ResetScrollBar(); } SetDataSize(DataSize); SetHeader(); UpdateValues(); AddressLabel.Text = GenerateAddressString(); } public void SetToAddresses(IEnumerable addresses, MemoryDomain domain, WatchSize size) { DataSize = (int)size; SetDataSize(DataSize); var addrList = addresses.ToList(); if (addrList.Any()) { SetDomain(domain); SetHighlighted(addrList[0]); _secondaryHighlightedAddresses.Clear(); _secondaryHighlightedAddresses.AddRange(addrList.Where(addr => addr != addrList[0]).ToList()); ClearNibbles(); UpdateValues(); MemoryViewerBox.Refresh(); AddressLabel.Text = GenerateAddressString(); } } public byte[] ConvertTextToBytes(string str) { if (_textTable.Any()) { var byteArr = new List(); foreach (var chr in str) { byteArr.Add((byte)_textTable.FirstOrDefault(kvp => kvp.Value == chr).Key); } return byteArr.ToArray(); } return str.Select(Convert.ToByte).ToArray(); } public void FindNext(string value, bool wrap) { long found = -1; var search = value.Replace(" ", string.Empty).ToUpper(); if (string.IsNullOrEmpty(search)) { return; } var numByte = search.Length / 2; long startByte; if (_addressHighlighted == -1) { startByte = 0; } else if (_addressHighlighted >= (_domain.Size - 1 - numByte)) { startByte = 0; } else { startByte = _addressHighlighted + DataSize; } for (var i = startByte; i < (_domain.Size - numByte); i++) { var ramblock = new StringBuilder(); for (var j = 0; j < numByte; j++) { ramblock.Append(string.Format("{0:X2}", (int)_domain.PeekByte(i + j))); } var block = ramblock.ToString().ToUpper(); if (search == block) { found = i; break; } } if (found > -1) { HighlightSecondaries(search, found); GoToAddress(found); _findStr = search; } else if (wrap == false) { FindPrev(value, true); // Search the opposite direction if not found } _hexFind.Close(); } public void FindPrev(string value, bool wrap) { long found = -1; var search = value.Replace(" ", string.Empty).ToUpper(); if (string.IsNullOrEmpty(search)) { return; } var numByte = search.Length / 2; long startByte; if (_addressHighlighted == -1) { startByte = _domain.Size - DataSize; } else { startByte = _addressHighlighted - 1; } for (var i = startByte; i >= 0; i--) { var ramblock = new StringBuilder(); for (var j = 0; j < numByte; j++) { ramblock.Append(string.Format("{0:X2}", (int)_domain.PeekByte(i + j))); } var block = ramblock.ToString().ToUpper(); if (search == block) { found = i; break; } } if (found > -1) { HighlightSecondaries(search, found); GoToAddress(found); _findStr = search; } else if (wrap == false) { FindPrev(value, true); // Search the opposite direction if not found } _hexFind.Close(); } #endregion private char Remap(byte val) { if (_textTable.Any()) { if (_textTable.ContainsKey(val)) { return _textTable[val]; } return '?'; } else { if (val < ' ') { return '.'; } if (val >= 0x80) { return '.'; } return (char)val; } } private static bool CurrentRomIsArchive() { var path = GlobalWin.MainForm.CurrentlyOpenRom; if (path == null) { return false; } using (var file = new HawkFile()) { file.Open(path); if (!file.Exists) { return false; } return file.IsArchive; } } private static byte[] GetRomBytes() { var path = GlobalWin.MainForm.CurrentlyOpenRomArgs.OpenAdvanced.SimplePath; if (string.IsNullOrEmpty(path)) { return new byte[] { 0xFF }; } using (var file = new HawkFile()) { file.Open(path); if (!file.Exists) { return null; } if (file.IsArchive) { var stream = file.GetStream(); return stream.ReadAllBytes(); } return File.ReadAllBytes(path); } } private static int GetNumDigits(long i) { if (i <= 0x10000) { return 4; } return i <= 0x1000000 ? 6 : 8; } private static char ForceCorrectKeyString(char keycode) { return (char)keycode; } private static bool IsHexKeyCode(char key) { if (key >= '0' && key <= '9') // 0-9 { return true; } if (key >= 'a' && key <= 'f') // A-F { return true; } if (key >= 'A' && key <= 'F') // A-F { return true; } return false; } private void HexEditor_Load(object sender, EventArgs e) { DataSize = _domain.WordSize; SetDataSize(DataSize); if (RecentTables.AutoLoad) { LoadFileFromRecent(RecentTables[0]); } UpdateValues(); } private void LoadConfigSettings() { HexMenuStrip.BackColor = Global.Config.HexMenubarColor; MemoryViewerBox.BackColor = Global.Config.HexBackgrndColor; MemoryViewerBox.ForeColor = Global.Config.HexForegrndColor; Header.BackColor = Global.Config.HexBackgrndColor; Header.ForeColor = Global.Config.HexForegrndColor; } // TODO: rename me private void SaveConfigSettings() { if (_hexFind.IsHandleCreated || !_hexFind.IsDisposed) { _hexFind.Close(); } } private string GenerateAddressString() { var addrStr = new StringBuilder(); for (var i = 0; i < _rowsVisible; i++) { _row = i + HexScrollBar.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.AppendLine(_addr.ToHexString(_numDigits) + " |"); } return addrStr.ToString(); } private string GenerateMemoryViewString(bool forWindow) { var rowStr = new StringBuilder(); for (var i = 0; i < _rowsVisible; i++) { _row = i + HexScrollBar.Value; _addr = _row << 4; if (_addr >= _domain.Size) { break; } for (var j = 0; j < 16; j += DataSize) { if (_addr + j + DataSize <= _domain.Size) { rowStr.AppendFormat(_digitFormatString, MakeValue(_addr + j)); } else { for (var t = 0; t < DataSize; t++) { rowStr.Append(" "); } rowStr.Append(' '); } } rowStr.Append("| "); for (var k = 0; k < 16; k++) { if (_addr + k < _domain.Size) { byte b = MakeByte(_addr + k); char c = Remap(b); rowStr.Append(c); //winforms will be using these as escape codes for hotkeys if (forWindow) if (c == '&') rowStr.Append('&'); } } rowStr.AppendLine(); } return rowStr.ToString(); } private byte MakeByte(long address) { return Global.CheatList.IsActive(_domain, address) ? Global.CheatList.GetByteValue(_domain, address).Value : _domain.PeekByte(address); } private int MakeValue(int dataSize, long address) { if (Global.CheatList.IsActive(_domain, address)) { return Global.CheatList.GetCheatValue(_domain, address, (WatchSize)DataSize ).Value; } switch (dataSize) { default: case 1: return _domain.PeekByte(address); case 2: return _domain.PeekUshort(address, BigEndian); case 4: return (int)_domain.PeekUint(address, BigEndian); } } private int MakeValue(long address) { return MakeValue(DataSize, address); } private void SetMemoryDomain(string name) { if (name == _romDomain.Name) { _domain = _romDomain; } else { _domain = MemoryDomains[name]; } BigEndian = _domain.EndianType == MemoryDomain.Endian.Big; _maxRow = _domain.Size / 2; SetUpScrollBar(); if (0 >= HexScrollBar.Minimum && 0 <= HexScrollBar.Maximum) { HexScrollBar.Value = 0; } if (_domain.CanPoke()) { AddressesLabel.ForeColor = SystemColors.ControlText; } else { AddressesLabel.ForeColor = SystemColors.ControlDarkDark; } if (HighlightedAddress >= _domain.Size || (_secondaryHighlightedAddresses.Any() && _secondaryHighlightedAddresses.Max() >= _domain.Size)) { _addressHighlighted = -1; _secondaryHighlightedAddresses.Clear(); } UpdateGroupBoxTitle(); SetHeader(); UpdateValues(); } private void SetDomain(MemoryDomain domain) { SetMemoryDomain(domain.Name); } private void UpdateGroupBoxTitle() { var addressesString = "0x" + string.Format("{0:X8}", _domain.Size / DataSize).TrimStart('0'); MemoryViewerBox.Text = Emulator.SystemId + " " + _domain + (_domain.CanPoke() ? "" : " (READ-ONLY)") + " - " + addressesString + " addresses"; } private void ClearNibbles() { for (var i = 0; i < 8; i++) { _nibbles[i] = 'G'; } } private void GoToAddress(long address) { if (address < 0) { address = 0; } if (address >= _domain.Size) { address = _domain.Size - DataSize; } SetHighlighted(address); ClearNibbles(); UpdateValues(); MemoryViewerBox.Refresh(); AddressLabel.Text = GenerateAddressString(); } private void SetHighlighted(long address) { if (address < 0) { address = 0; } if (address >= _domain.Size) { address = _domain.Size - DataSize; } if (!IsVisible(address)) { var value = (address / 16) - _rowsVisible + 1; if (value < 0) { value = 0; } HexScrollBar.Value = (int)value; // This will fail on a sufficiently large domain } _addressHighlighted = address; _addressOver = address; ClearNibbles(); UpdateFormText(); } private void UpdateFormText() { Text = "Hex Editor"; if (_addressHighlighted >= 0) { Text += " - Editing Address 0x" + string.Format(_numDigitsStr, _addressHighlighted); if (_secondaryHighlightedAddresses.Any()) { Text += string.Format(" (Selected 0x{0:X})", _secondaryHighlightedAddresses.Count() + (_secondaryHighlightedAddresses.Contains(_addressHighlighted) ? 0 : 1)); } } } private bool IsVisible(long address) { var i = address >> 4; return i >= HexScrollBar.Value && i < (_rowsVisible + HexScrollBar.Value); } 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 + "} "; } private void SetDataSize(int size) { if (size == 1 || size == 2 || size == 4) { DataSize = size; _digitFormatString = "{0:X" + (DataSize * 2) + "} "; SetHeader(); UpdateGroupBoxTitle(); UpdateValues(); _secondaryHighlightedAddresses.Clear(); } } private Watch MakeWatch(long address) { switch (DataSize) { default: case 1: return Watch.GenerateWatch(_domain, address, WatchSize.Byte, Client.Common.DisplayType.Hex, BigEndian, string.Empty); case 2: return Watch.GenerateWatch(_domain, address, WatchSize.Word, Client.Common.DisplayType.Hex, BigEndian, string.Empty); case 4: return Watch.GenerateWatch(_domain, address, WatchSize.DWord, Client.Common.DisplayType.Hex, BigEndian, string.Empty); } } private bool IsFrozen(long address) { return Global.CheatList.IsActive(_domain, address); } private void UnFreezeAddress(long address) { if (address >= 0) { // TODO: can't unfreeze address 0?? Global.CheatList.RemoveRange( Global.CheatList.Where(x => x.Contains(address)).ToList()); } MemoryViewerBox.Refresh(); } // TODO refactor to int? private void FreezeAddress(long address) { if (address >= 0) { var watch = Watch.GenerateWatch( _domain, address, WatchSize, Client.Common.DisplayType.Hex, BigEndian); Global.CheatList.Add(new Cheat( watch, watch.Value)); } } private void FreezeSecondaries() { var cheats = new List(); foreach (var address in _secondaryHighlightedAddresses) { var watch = Watch.GenerateWatch( _domain, address, WatchSize, Client.Common.DisplayType.Hex, BigEndian); cheats.Add(new Cheat( watch, watch.Value)); } Global.CheatList.AddRange(cheats); } private void UnfreezeSecondaries() { Global.CheatList.RemoveRange( Global.CheatList.Where( cheat => !cheat.IsSeparator && cheat.Domain == _domain && _secondaryHighlightedAddresses.Contains(cheat.Address.Value))); } private void SaveFileBinary(string path) { var file = new FileInfo(path); using (var binWriter = new BinaryWriter(File.Open(file.FullName, FileMode.Create))) { for (var i = 0; i < _domain.Size; i++) { binWriter.Write(_domain.PeekByte(i)); } } } private string GetSaveFileFilter() { if (_domain.Name == "File on Disk") { var extension = Path.GetExtension(RomName); return "Binary (*" + extension + ")|*" + extension + "|All Files|*.*"; } return "Binary (*.bin)|*.bin|All Files|*.*"; } private string RomDirectory { get { string path = Global.Config.RecentRoms.MostRecent; if (string.IsNullOrWhiteSpace(path)) { return path; } if (path.Contains("|")) { path = path.Split('|').First(); } return Path.GetDirectoryName(path); } } private string RomName { get { string path = Global.Config.RecentRoms.MostRecent; if (string.IsNullOrWhiteSpace(path)) { return path; } if (path.Contains("|")) { path = path.Split('|').Last(); } return Path.GetFileName(path); } } private string GetBinarySaveFileFromUser() { var sfd = new SaveFileDialog { Filter = GetSaveFileFilter(), RestoreDirectory = true, InitialDirectory = RomDirectory }; if (_domain.Name == "File on Disk") { sfd.FileName = RomName; } else { sfd.FileName = PathManager.FilesystemSafeName(Global.Game); } var result = sfd.ShowHawkDialog(); return result == DialogResult.OK ? sfd.FileName : string.Empty; } private string GetSaveFileFromUser() { var sfd = new SaveFileDialog { Filter = "Text (*.txt)|*.txt|All Files|*.*", RestoreDirectory = true, InitialDirectory = RomDirectory }; if (_domain.Name == "File on Disk") { sfd.FileName = Path.GetFileNameWithoutExtension(RomName) + ".txt"; } else { sfd.FileName = PathManager.FilesystemSafeName(Global.Game); } var result = sfd.ShowHawkDialog(); return result == DialogResult.OK ? sfd.FileName : string.Empty; } private void ResetScrollBar() { HexScrollBar.Value = 0; SetUpScrollBar(); Refresh(); } private void SetUpScrollBar() { _rowsVisible = (MemoryViewerBox.Height - (fontHeight * 2) - (fontHeight / 2)) / fontHeight; var totalRows = (int)((_domain.Size + 15) / 16); if (totalRows < _rowsVisible) { _rowsVisible = totalRows; } HexScrollBar.Maximum = totalRows - 1; HexScrollBar.LargeChange = _rowsVisible; HexScrollBar.Visible = totalRows > _rowsVisible; AddressLabel.Text = GenerateAddressString(); } private long GetPointedAddress(int x, int y) { long address; // Scroll value determines the first row long i = HexScrollBar.Value; var rowoffset = y / fontHeight; i += rowoffset; int colWidth = DataSize * 2 + 1; var column = x / (fontWidth * colWidth); var innerOffset = AddressesLabel.Location.X - AddressLabel.Location.X + AddressesLabel.Margin.Left; var start = GetTextOffset() - innerOffset; if (x > start) { column = (x - start) / (fontWidth * DataSize); } if (i >= 0 && i <= _maxRow && column >= 0 && column < (16 / DataSize)) { address = (i * 16) + (column * DataSize); } else { address = -1; } return address; } private void DoShiftClick() { if (_addressOver >= 0 && _addressOver < _domain.Size) { _secondaryHighlightedAddresses.Clear(); if (_addressOver < _addressHighlighted) { for (var x = _addressOver; x < _addressHighlighted; x += DataSize) { _secondaryHighlightedAddresses.Add(x); } } else if (_addressOver > _addressHighlighted) { for (var x = _addressHighlighted + DataSize; x <= _addressOver; x += DataSize) { _secondaryHighlightedAddresses.Add(x); } } if (!IsVisible(_addressOver)) { var value = (_addressOver / 16) + 1 - ((_addressOver / 16) < HexScrollBar.Value ? 1 : _rowsVisible); if (value < 0) { value = 0; } HexScrollBar.Value = (int)value; // This will fail on a sufficiently large domain } } } private void ClearHighlighted() { _addressHighlighted = -1; UpdateFormText(); MemoryViewerBox.Refresh(); } private Point GetAddressCoordinates(long address) { var extra = (address % DataSize) * fontWidth * 2; var xOffset = AddressesLabel.Location.X + fontWidth / 2 - 2; var yOffset = AddressesLabel.Location.Y; return new Point( (int)((((address % 16) / DataSize) * (fontWidth * (DataSize * 2 + 1))) + xOffset + extra), (int)((((address / 16) - HexScrollBar.Value) * fontHeight) + yOffset) ); } // TODO: rename this, but it is a hack work around for highlighting misaligned addresses that result from highlighting on in a smaller data size and switching size private bool NeedsExtra(long val) { return val % DataSize > 0; } private int GetTextOffset() { int start = (16 / DataSize) * fontWidth * (DataSize * 2 + 1); start += AddressesLabel.Location.X + fontWidth / 2; start += fontWidth * 2; return start; } private long GetTextX(long address) { return GetTextOffset() + ((address % 16) * fontWidth); } private bool HasNibbles() { return _nibbles.Any(x => x != 'G'); } private string MakeNibbles() { var str = string.Empty; for (var x = 0; x < (DataSize * 2); x++) { if (_nibbles[x] != 'G') { str += _nibbles[x]; } else { break; } } return str; } private void AddToSecondaryHighlights(long address) { if (address >= 0 && address < _domain.Size && !_secondaryHighlightedAddresses.Contains(address)) { _secondaryHighlightedAddresses.Add(address); } } // TODO: obsolete me private void PokeWord(long 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 IncrementAddress(long address) { if (Global.CheatList.IsActive(_domain, address)) { // TODO: Increment should be intelligent since IsActive is. If this address is part of a multi-byte cheat it should intelligently increment just that byte Global.CheatList.FirstOrDefault(x => x.Domain == _domain && x.Address == address).Increment(); } else { switch (DataSize) { default: case 1: _domain.PokeByte( address, (byte)(_domain.PeekByte(address) + 1)); break; case 2: _domain.PokeUshort( address, (ushort)(_domain.PeekUshort(address, BigEndian) + 1), BigEndian); break; case 4: _domain.PokeUint( address, _domain.PeekUint(address, BigEndian) + 1, BigEndian); break; } } } private void DecrementAddress(long address) { if (Global.CheatList.IsActive(_domain, address)) { // TODO: Increment should be intelligent since IsActive is. If this address is part of a multi-byte cheat it should intelligently increment just that byte Global.CheatList.FirstOrDefault(x => x.Domain == _domain && x.Address == address).Decrement(); } else { switch (DataSize) { default: case 1: _domain.PokeByte( address, (byte)(_domain.PeekByte(address) - 1)); break; case 2: _domain.PokeUshort( address, (ushort)(_domain.PeekUshort(address, BigEndian) - 1), BigEndian); break; case 4: _domain.PokeUint( address, _domain.PeekUint(address, BigEndian) - 1, BigEndian); break; } } } private string ValueString(long address) { if (address != -1) { return string.Format(_digitFormatString, MakeValue(address)).Trim(); } return string.Empty; } private string GetFindValues() { if (HighlightedAddress.HasValue) { var values = ValueString(HighlightedAddress.Value); return _secondaryHighlightedAddresses.Aggregate(values, (current, x) => current + ValueString(x)); } return string.Empty; } private void HighlightSecondaries(string value, long found) { // This function assumes that the primary highlighted value has been set and sets the remaining characters in this string _secondaryHighlightedAddresses.Clear(); var addrLength = DataSize * 2; if (value.Length <= addrLength) { return; } var numToHighlight = (value.Length / addrLength) - 1; for (var i = 0; i < numToHighlight; i += DataSize) { _secondaryHighlightedAddresses.Add(found + DataSize + i); } } private bool LoadTable(string path) { var file = new FileInfo(path); if (!file.Exists) { return false; } using (var sr = file.OpenText()) { string line; while ((line = sr.ReadLine()) != null) { var parts = line.Split('='); _textTable.Add( int.Parse(parts[0], NumberStyles.HexNumber), parts[1].First()); } } return true; } #region Events #region File Menu private void FileSubMenu_DropDownOpened(object sender, EventArgs e) { if (_domain.Name == "File on Disk") { SaveMenuItem.Visible = !CurrentRomIsArchive(); SaveAsBinaryMenuItem.Text = "Save as ROM..."; } else { SaveAsBinaryMenuItem.Text = "Save as binary..."; } CloseTableFileMenuItem.Enabled = _textTable.Any(); } private void SaveMenuItem_Click(object sender, EventArgs e) { if (!CurrentRomIsArchive()) { SaveFileBinary(GlobalWin.MainForm.CurrentlyOpenRom); } } private void SaveAsBinaryMenuItem_Click(object sender, EventArgs e) { var path = GetBinarySaveFileFromUser(); if (!string.IsNullOrEmpty(path)) { SaveFileBinary(path); } } private void SaveAsTextMenuItem_Click(object sender, EventArgs e) { var path = GetSaveFileFromUser(); if (!string.IsNullOrWhiteSpace(path)) { var file = new FileInfo(path); using (var sw = new StreamWriter(file.FullName)) { var sb = new StringBuilder(); for (var i = 0; i < _domain.Size / 16; i++) { for (var j = 0; j < 16; j++) { sb.Append(string.Format("{0:X2} ", _domain.PeekByte((i * 16) + j))); } sb.AppendLine(); } sw.WriteLine(sb); } } } private void LoadTableFileMenuItem_Click(object sender, EventArgs e) { string romName; string intialDirectory = PathManager.MakeAbsolutePath(Global.Config.PathEntries.ToolsPathFragment, null); if (Global.Config.RecentRoms.MostRecent.Contains('|')) { romName = Global.Config.RecentRoms.MostRecent.Split('|').Last(); } else { romName = Global.Config.RecentRoms.MostRecent; } var ofd = new OpenFileDialog { FileName = Path.GetFileNameWithoutExtension(romName) + ".tbl", InitialDirectory = intialDirectory, Filter = "Text Table files (*.tbl)|*.tbl|All Files|*.*", RestoreDirectory = false }; var result = ofd.ShowHawkDialog(); if (result == DialogResult.OK) { LoadTable(ofd.FileName); RecentTables.Add(ofd.FileName); UpdateValues(); } } private void CloseTableFileMenuItem_Click(object sender, EventArgs e) { _textTable.Clear(); } public void LoadFileFromRecent(string path) { var result = LoadTable(path); if (!result) { RecentTables.HandleLoadError(path); } else { RecentTables.Add(path); UpdateValues(); } } private void RecentTablesSubMenu_DropDownOpened(object sender, EventArgs e) { RecentTablesSubMenu.DropDownItems.Clear(); RecentTablesSubMenu.DropDownItems.AddRange( RecentTables.RecentMenu(LoadFileFromRecent, true)); } private void ExitMenuItem_Click(object sender, EventArgs e) { Close(); } #endregion #region Edit private void EditMenuItem_DropDownOpened(object sender, EventArgs e) { var data = Clipboard.GetDataObject(); PasteMenuItem.Enabled = _domain.CanPoke() && (HighlightedAddress.HasValue || _secondaryHighlightedAddresses.Any()) && data != null && data.GetDataPresent(DataFormats.Text); FindNextMenuItem.Enabled = !string.IsNullOrWhiteSpace(_findStr); } string MakeCopyExportString(bool export) { //make room for an array with _secondaryHighlightedAddresses and optionally HighlightedAddress long[] addresses = new long[_secondaryHighlightedAddresses.Count + (HighlightedAddress.HasValue ? 1 : 0)]; //if there was actually nothing to do, return if (addresses.Length == 0) return null; //fill the array with _secondaryHighlightedAddresses for (int i = 0; i < _secondaryHighlightedAddresses.Count; i++) addresses[i] = _secondaryHighlightedAddresses[i]; //and add HighlightedAddress if present if (HighlightedAddress.HasValue) addresses[addresses.Length - 1] = HighlightedAddress.Value; //these need to be sorted. it's not just for HighlightedAddress, _secondaryHighlightedAddresses can even be jumbled Array.Sort(addresses); //find the maximum length of the exported string int maximumLength = addresses.Length * (export ? 3 : 2) + 8; StringBuilder sb = new StringBuilder(maximumLength); //generate it differently for export (as you see it) or copy (raw bytes) if (export) for (int i = 0; i < addresses.Length; i++) { sb.Append(ValueString(addresses[i])); if(i != addresses.Length-1) sb.Append(' '); } else { for (int i = 0; i < addresses.Length; i++) { long start = addresses[i]; long end = addresses[i] + DataSize -1 ; for(long a = start;a<=end;a++) sb.AppendFormat("{0:X2}", MakeValue(1,a)); } } return sb.ToString(); } private void ExportMenuItem_Click(object sender, EventArgs e) { var value = MakeCopyExportString(true); if (!string.IsNullOrEmpty(value)) Clipboard.SetDataObject(value); } private void CopyMenuItem_Click(object sender, EventArgs e) { var value = MakeCopyExportString(false); if (!string.IsNullOrEmpty(value)) Clipboard.SetDataObject(value); } private void PasteMenuItem_Click(object sender, EventArgs e) { var data = Clipboard.GetDataObject(); if (data != null && !data.GetDataPresent(DataFormats.Text)) return; var clipboardRaw = (string)data.GetData(DataFormats.Text); var hex = clipboardRaw.OnlyHex(); var numBytes = hex.Length / 2; for (var i = 0; i < numBytes; i++) { var value = int.Parse(hex.Substring(i * 2, 2), NumberStyles.HexNumber); var address = _addressHighlighted + i; if (address < _domain.Size) { _domain.PokeByte(address, (byte)value); } } UpdateValues(); } private void FindMenuItem_Click(object sender, EventArgs e) { _findStr = GetFindValues(); if (!_hexFind.IsHandleCreated || _hexFind.IsDisposed) { _hexFind = new HexFind { InitialLocation = PointToScreen(AddressesLabel.Location), InitialValue = _findStr }; _hexFind.Show(); } else { _hexFind.InitialValue = _findStr; _hexFind.Focus(); } } private void FindNextMenuItem_Click(object sender, EventArgs e) { FindNext(_findStr, false); } private void FindPrevMenuItem_Click(object sender, EventArgs e) { FindPrev(_findStr, false); } #endregion #region Options private void OptionsSubMenu_DropDownOpened(object sender, EventArgs e) { BigEndianMenuItem.Checked = BigEndian; DataSizeByteMenuItem.Checked = DataSize == 1; DataSizeWordMenuItem.Checked = DataSize == 2; DataSizeDWordMenuItem.Checked = DataSize == 4; if (HighlightedAddress.HasValue && IsFrozen(HighlightedAddress.Value)) { FreezeAddressMenuItem.Image = Properties.Resources.Unfreeze; FreezeAddressMenuItem.Text = "Un&freeze Address"; } else { FreezeAddressMenuItem.Image = Properties.Resources.Freeze; FreezeAddressMenuItem.Text = "&Freeze Address"; } AddToRamWatchMenuItem.Enabled = HighlightedAddress.HasValue; PokeAddressMenuItem.Enabled = FreezeAddressMenuItem.Enabled = HighlightedAddress.HasValue && _domain.CanPoke(); } private void MemoryDomainsMenuItem_DropDownOpened(object sender, EventArgs e) { MemoryDomainsMenuItem.DropDownItems.Clear(); MemoryDomainsMenuItem.DropDownItems.AddRange( MemoryDomains.MenuItems(SetMemoryDomain, _domain.Name) .ToArray()); var romMenuItem = new ToolStripMenuItem { Text = _romDomain.Name, Checked = _domain.Name == _romDomain.Name }; MemoryDomainsMenuItem.DropDownItems.Add(new ToolStripSeparator()); MemoryDomainsMenuItem.DropDownItems.Add(romMenuItem); romMenuItem.Click += (o, ev) => SetMemoryDomain(_romDomain.Name); } private void DataSizeByteMenuItem_Click(object sender, EventArgs e) { SetDataSize(1); } private void DataSizeWordMenuItem_Click(object sender, EventArgs e) { SetDataSize(2); } private void DataSizeDWordMenuItem_Click(object sender, EventArgs e) { SetDataSize(4); } private void BigEndianMenuItem_Click(object sender, EventArgs e) { BigEndian ^= true; UpdateValues(); } private void GoToAddressMenuItem_Click(object sender, EventArgs e) { var inputPrompt = new InputPrompt { Text = "Go to Address", StartLocation = this.ChildPointToScreen(MemoryViewerBox), Message = "Enter a hexadecimal value" }; var result = inputPrompt.ShowHawkDialog(); if (result == DialogResult.OK && inputPrompt.PromptText.IsHex()) { GoToAddress(long.Parse(inputPrompt.PromptText, NumberStyles.HexNumber)); } AddressLabel.Text = GenerateAddressString(); } private void AddToRamWatchMenuItem_Click(object sender, EventArgs e) { if (HighlightedAddress.HasValue || _secondaryHighlightedAddresses.Any()) { GlobalWin.Tools.LoadRamWatch(true); } if (HighlightedAddress.HasValue) { GlobalWin.Tools.RamWatch.AddWatch(MakeWatch(HighlightedAddress.Value)); } _secondaryHighlightedAddresses.ForEach(addr => GlobalWin.Tools.RamWatch.AddWatch(MakeWatch(addr))); } private void FreezeAddressMenuItem_Click(object sender, EventArgs e) { if (!_domain.CanPoke()) { return; } if (HighlightedAddress.HasValue) { if (IsFrozen(HighlightedAddress.Value)) { UnFreezeAddress(HighlightedAddress.Value); UnfreezeSecondaries(); } else { FreezeAddress(HighlightedAddress.Value); FreezeSecondaries(); } } UpdateCheatRelatedTools(null, null); MemoryViewerBox.Refresh(); } private void UnfreezeAllMenuItem_Click(object sender, EventArgs e) { Global.CheatList.RemoveAll(); } private void PokeAddressMenuItem_Click(object sender, EventArgs e) { if (!_domain.CanPoke()) { return; } var addresses = new List(); if (HighlightedAddress.HasValue) { addresses.Add(HighlightedAddress.Value); } if (_secondaryHighlightedAddresses.Any()) { addresses.AddRange(_secondaryHighlightedAddresses); } if (addresses.Any()) { var poke = new RamPoke { InitialLocation = this.ChildPointToScreen(AddressLabel), ParentTool = this }; var watches = addresses.Select( address => Watch.GenerateWatch( _domain, address, (WatchSize)DataSize, Client.Common.DisplayType.Hex, BigEndian)); poke.SetWatch(watches); poke.ShowHawkDialog(); UpdateValues(); } } #endregion #region Settings Menu private void SetColorsMenuItem_Click(object sender, EventArgs e) { new HexColorsForm().ShowHawkDialog(); } private void ResetColorsToDefaultMenuItem_Click(object sender, EventArgs e) { MemoryViewerBox.BackColor = Color.FromName("Control"); MemoryViewerBox.ForeColor = Color.FromName("ControlText"); this.HexMenuStrip.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; } #endregion #region Context Menu and Dialog Events private void HexEditor_Resize(object sender, EventArgs e) { SetUpScrollBar(); UpdateValues(); } private void HexEditor_ResizeEnd(object sender, EventArgs e) { SetUpScrollBar(); } private void HexEditor_KeyDown(object sender, KeyEventArgs e) { if (e.Control && e.KeyCode == Keys.G) { GoToAddressMenuItem_Click(sender, e); return; } if (e.Control && e.KeyCode == Keys.P) { PokeAddressMenuItem_Click(sender, e); return; } long newHighlighted; switch (e.KeyCode) { case Keys.Up: newHighlighted = _addressHighlighted - 16; if (e.Modifiers == Keys.Shift) { for (var i = newHighlighted + DataSize; i <= _addressHighlighted; i += DataSize) { AddToSecondaryHighlights(i); } GoToAddress(newHighlighted); } else { _secondaryHighlightedAddresses.Clear(); GoToAddress(newHighlighted); } break; case Keys.Down: newHighlighted = _addressHighlighted + 16; if (e.Modifiers == Keys.Shift) { for (var i = _addressHighlighted; i < newHighlighted; i += DataSize) { 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 (var i = newHighlighted + 1; i <= _addressHighlighted; i += DataSize) { AddToSecondaryHighlights(i); } GoToAddress(newHighlighted); } else { _secondaryHighlightedAddresses.Clear(); GoToAddress(newHighlighted); } break; case Keys.PageDown: newHighlighted = _addressHighlighted + (_rowsVisible * 16); if (e.Modifiers == Keys.Shift) { for (var i = _addressHighlighted + 1; i < newHighlighted; i += DataSize) { 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 (var i = 1; i <= _addressHighlighted; i += DataSize) { AddToSecondaryHighlights(i); } GoToAddress(0); } else { _secondaryHighlightedAddresses.Clear(); GoToAddress(0); } break; case Keys.End: newHighlighted = _domain.Size - DataSize; if (e.Modifiers == Keys.Shift) { for (var i = _addressHighlighted; i < newHighlighted; i += DataSize) { AddToSecondaryHighlights(i); } GoToAddress(newHighlighted); } else { _secondaryHighlightedAddresses.Clear(); GoToAddress(newHighlighted); } break; case Keys.Add: IncrementContextItem_Click(sender, e); break; case Keys.Subtract: DecrementContextItem_Click(sender, e); break; case Keys.Space: FreezeAddressMenuItem_Click(sender, e); break; case Keys.Delete: if (e.Modifiers == Keys.Shift) { Global.CheatList.RemoveAll(); } else { if (HighlightedAddress.HasValue) { UnFreezeAddress(HighlightedAddress.Value); } } break; case Keys.W: if (e.Modifiers == Keys.Control) { AddToRamWatchMenuItem_Click(sender, e); } break; case Keys.Escape: _secondaryHighlightedAddresses.Clear(); ClearHighlighted(); break; } } private void HexEditor_KeyPress(object sender, KeyPressEventArgs e) { if (!IsHexKeyCode(e.KeyChar)) { e.Handled = true; return; } if ((ModifierKeys & (Keys.Control | Keys.Shift | Keys.Alt)) != 0) { return; } if (!_domain.CanPoke()) { return; } switch (DataSize) { default: case 1: if (_nibbles[0] == 'G') { _nibbles[0] = ForceCorrectKeyString(e.KeyChar); } else { var temp = _nibbles[0].ToString() + ForceCorrectKeyString(e.KeyChar); var x = byte.Parse(temp, NumberStyles.HexNumber); _domain.PokeByte(_addressHighlighted, x); ClearNibbles(); SetHighlighted(_addressHighlighted + 1); UpdateValues(); Refresh(); } break; case 2: if (_nibbles[0] == 'G') { _nibbles[0] = ForceCorrectKeyString(e.KeyChar); } else if (_nibbles[1] == 'G') { _nibbles[1] = ForceCorrectKeyString(e.KeyChar); } else if (_nibbles[2] == 'G') { _nibbles[2] = ForceCorrectKeyString(e.KeyChar); } else if (_nibbles[3] == 'G') { var temp = _nibbles[0].ToString() + _nibbles[1]; var x1 = byte.Parse(temp, NumberStyles.HexNumber); var temp2 = _nibbles[2].ToString() + ((char)e.KeyChar); var x2 = byte.Parse(temp2, NumberStyles.HexNumber); PokeWord(_addressHighlighted, x1, x2); ClearNibbles(); SetHighlighted(_addressHighlighted + 2); UpdateValues(); Refresh(); } break; case 4: if (_nibbles[0] == 'G') { _nibbles[0] = ForceCorrectKeyString(e.KeyChar); } else if (_nibbles[1] == 'G') { _nibbles[1] = ForceCorrectKeyString(e.KeyChar); } else if (_nibbles[2] == 'G') { _nibbles[2] = ForceCorrectKeyString(e.KeyChar); } else if (_nibbles[3] == 'G') { _nibbles[3] = ForceCorrectKeyString(e.KeyChar); } else if (_nibbles[4] == 'G') { _nibbles[4] = ForceCorrectKeyString(e.KeyChar); } else if (_nibbles[5] == 'G') { _nibbles[5] = ForceCorrectKeyString(e.KeyChar); } else if (_nibbles[6] == 'G') { _nibbles[6] = ForceCorrectKeyString(e.KeyChar); } else if (_nibbles[7] == 'G') { var temp = _nibbles[0].ToString() + _nibbles[1]; var x1 = byte.Parse(temp, NumberStyles.HexNumber); var temp2 = _nibbles[2].ToString() + _nibbles[3]; var x2 = byte.Parse(temp2, NumberStyles.HexNumber); var temp3 = _nibbles[4].ToString() + _nibbles[5]; var x3 = byte.Parse(temp3, NumberStyles.HexNumber); var temp4 = _nibbles[6].ToString() + ForceCorrectKeyString(e.KeyChar); var x4 = byte.Parse(temp4, NumberStyles.HexNumber); PokeWord(_addressHighlighted, x1, x2); PokeWord(_addressHighlighted + 2, x3, x4); ClearNibbles(); SetHighlighted(_addressHighlighted + 4); UpdateValues(); Refresh(); } break; } UpdateValues(); } private void ViewerContextMenuStrip_Opening(object sender, CancelEventArgs e) { var data = Clipboard.GetDataObject(); CopyContextItem.Visible = AddToRamWatchContextItem.Visible = HighlightedAddress.HasValue || _secondaryHighlightedAddresses.Any(); FreezeContextItem.Visible = PokeContextItem.Visible = IncrementContextItem.Visible = DecrementContextItem.Visible = ContextSeparator2.Visible = (HighlightedAddress.HasValue || _secondaryHighlightedAddresses.Any()) && _domain.CanPoke(); UnfreezeAllContextItem.Visible = Global.CheatList.ActiveCount > 0; PasteContextItem.Visible = _domain.CanPoke() && data != null && data.GetDataPresent(DataFormats.Text); ContextSeparator1.Visible = HighlightedAddress.HasValue || _secondaryHighlightedAddresses.Any() || (data != null && data.GetDataPresent(DataFormats.Text)); if (HighlightedAddress.HasValue && IsFrozen(HighlightedAddress.Value)) { FreezeContextItem.Text = "Un&freeze"; FreezeContextItem.Image = Properties.Resources.Unfreeze; } else { FreezeContextItem.Text = "&Freeze"; FreezeContextItem.Image = Properties.Resources.Freeze; } toolStripMenuItem1.Visible = viewN64MatrixToolStripMenuItem.Visible = DataSize == 4; } private void IncrementContextItem_Click(object sender, EventArgs e) { if (!_domain.CanPoke()) { return; } if (HighlightedAddress.HasValue) { IncrementAddress(HighlightedAddress.Value); } _secondaryHighlightedAddresses.ForEach(IncrementAddress); UpdateValues(); } private void DecrementContextItem_Click(object sender, EventArgs e) { if (!_domain.CanPoke()) { return; } if (HighlightedAddress.HasValue) { DecrementAddress(HighlightedAddress.Value); } _secondaryHighlightedAddresses.ForEach(DecrementAddress); UpdateValues(); } #endregion #region MemoryViewer Events private void HexEditor_MouseWheel(object sender, MouseEventArgs e) { var delta = 0; if (e.Delta > 0) { delta = -1; } else if (e.Delta < 0) { delta = 1; } var newValue = HexScrollBar.Value + delta; if (newValue < HexScrollBar.Minimum) { newValue = HexScrollBar.Minimum; } if (newValue > HexScrollBar.Maximum - HexScrollBar.LargeChange + 1) { newValue = HexScrollBar.Maximum - HexScrollBar.LargeChange + 1; } if (newValue != HexScrollBar.Value) { HexScrollBar.Value = newValue; MemoryViewerBox.Refresh(); } } 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 ?? 0)) { if (_domain.ToString() == cheat.Domain.Name) { var gaps = (int)cheat.Size - (int)DataSize; if (cheat.Size == WatchSize.DWord && DataSize == 2) { gaps -= 1; } if (gaps < 0) { gaps = 0; } var width = (fontWidth * 2 * (int)cheat.Size) + (gaps * fontWidth); var rect = new Rectangle(GetAddressCoordinates(cheat.Address ?? 0), new Size(width, fontHeight)); e.Graphics.DrawRectangle(new Pen(Brushes.Black), rect); e.Graphics.FillRectangle(new SolidBrush(Global.Config.HexFreezeColor), rect); } } } if (_addressHighlighted >= 0 && IsVisible(_addressHighlighted)) { // Create a slight offset to increase rectangle sizes var point = GetAddressCoordinates(_addressHighlighted); var textX = (int)GetTextX(_addressHighlighted); var textpoint = new Point(textX, point.Y); var rect = new Rectangle(point, new Size(fontWidth * 2 * DataSize + (NeedsExtra(_addressHighlighted) ? fontWidth : 0) + 2, fontHeight)); e.Graphics.DrawRectangle(new Pen(Brushes.Black), rect); var textrect = new Rectangle(textpoint, new Size(fontWidth * 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 (var address in _secondaryHighlightedAddresses) { if (IsVisible(address)) { var point = GetAddressCoordinates(address); var textX = (int)GetTextX(address); var textpoint = new Point(textX, point.Y); var rect = new Rectangle(point, new Size(fontWidth * 2 * DataSize + 2, fontHeight)); e.Graphics.DrawRectangle(new Pen(Brushes.Black), rect); var textrect = new Rectangle(textpoint, new Size(fontWidth * DataSize, 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(0x44, Global.Config.HexHighlightColor)), rect); e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(0x44, Global.Config.HexHighlightColor)), textrect); } } } if (HasNibbles()) { e.Graphics.DrawString(MakeNibbles(), new Font("Courier New", 8, FontStyle.Italic), Brushes.Black, new Point(158, 4)); } } private void AddressesLabel_MouseUp(object sender, MouseEventArgs e) { _mouseIsDown = false; } private void AddressesLabel_MouseMove(object sender, MouseEventArgs e) { _addressOver = GetPointedAddress(e.X, e.Y); if (_mouseIsDown) { DoShiftClick(); UpdateFormText(); MemoryViewerBox.Refresh(); } } private void AddressesLabel_MouseLeave(object sender, EventArgs e) { _addressOver = -1; MemoryViewerBox.Refresh(); } private void AddressesLabel_MouseDown(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { var pointedAddress = GetPointedAddress(e.X, e.Y); if (pointedAddress >= 0) { if ((ModifierKeys & Keys.Control) == Keys.Control) { if (pointedAddress == _addressHighlighted) { ClearHighlighted(); } else if (_secondaryHighlightedAddresses.Contains(pointedAddress)) { _secondaryHighlightedAddresses.Remove(pointedAddress); } else { _secondaryHighlightedAddresses.Add(pointedAddress); } } else if ((ModifierKeys & Keys.Shift) == Keys.Shift) { DoShiftClick(); } else { _secondaryHighlightedAddresses.Clear(); _findStr = string.Empty; SetHighlighted(pointedAddress); } MemoryViewerBox.Refresh(); } _mouseIsDown = true; } } bool _programmaticallyChangingValue = false; private void HexScrollBar_ValueChanged(object sender, EventArgs e) { if (!_programmaticallyChangingValue) { if (HexScrollBar.Value < 0) { _programmaticallyChangingValue = true; HexScrollBar.Value = 0; _programmaticallyChangingValue = false; } UpdateValues(); } } #endregion private void HexMenuStrip_ItemClicked(object sender, ToolStripItemClickedEventArgs e) { } #endregion private void viewN64MatrixToolStripMenuItem_Click(object sender, EventArgs e) { if (!HighlightedAddress.HasValue) return; bool bigend = true; long addr = HighlightedAddress.Value; //ushort = _domain.PeekWord(addr,bigend); float[,] matVals = new float[4,4]; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { ushort hi = _domain.PeekUshort(((addr+(i<<3)+(j<<1) )^0x0),bigend); ushort lo = _domain.PeekUshort(((addr+(i<<3)+(j<<1) + 32)^0x0),bigend); matVals[i,j] = (int)(((hi << 16) | lo)) / 65536.0f; } } //if needed //var mat = new SlimDX.Matrix(); //mat.M11 = matVals[0, 0]; mat.M12 = matVals[0, 1]; mat.M13 = matVals[0, 2]; mat.M14 = matVals[0, 3]; //mat.M21 = matVals[1, 0]; mat.M22 = matVals[1, 1]; mat.M23 = matVals[1, 2]; mat.M24 = matVals[1, 3]; //mat.M31 = matVals[2, 0]; mat.M32 = matVals[2, 1]; mat.M33 = matVals[2, 2]; mat.M34 = matVals[2, 3]; //mat.M41 = matVals[3, 0]; mat.M42 = matVals[3, 1]; mat.M43 = matVals[3, 2]; mat.M44 = matVals[3, 3]; //MessageBox.Show(mat.ToString()); StringWriter sw = new StringWriter(); for(int i=0;i<4;i++) sw.WriteLine("{0,18:0.00000} {1,18:0.00000} {2,18:0.00000} {3,18:0.00000}", matVals[i, 0], matVals[i, 1], matVals[i, 2], matVals[i, 3]); var str = sw.ToString(); MessageBox.Show(str); } } }