diff --git a/ReClass.NET/Constants.cs b/ReClass.NET/Constants.cs
index 4e54782a..bd918dbe 100644
--- a/ReClass.NET/Constants.cs
+++ b/ReClass.NET/Constants.cs
@@ -1,4 +1,4 @@
-namespace ReClassNET
+namespace ReClassNET
{
public class Constants
{
@@ -39,5 +39,14 @@ public static class CommandLineOptions
public const string FileExtRegister = "registerfileext";
public const string FileExtUnregister = "unregisterfileext";
}
+
+ ///
+ /// Change type for commandified members in classes which is used to signal what change occurred exactly. As we don't use this feature of the commandified
+ /// class, this enum is defined to simply signal 'no specific change other than it changed' happened.
+ ///
+ public enum GeneralPurposeChangeType
+ {
+ None
+ }
}
}
diff --git a/ReClass.NET/Controls/MemoryViewControl.cs b/ReClass.NET/Controls/MemoryViewControl.cs
index 64b8dfa4..d323903b 100644
--- a/ReClass.NET/Controls/MemoryViewControl.cs
+++ b/ReClass.NET/Controls/MemoryViewControl.cs
@@ -704,5 +704,23 @@ public void Reset()
VerticalScroll.Value = VerticalScroll.Minimum;
}
+
+ public void InitCurrentClassFromRTTI(ClassNode classNode)
+ {
+ var args = new DrawContextRequestEventArgs { Node = classNode };
+
+ var requestHandler = DrawContextRequested;
+ requestHandler?.Invoke(this, args);
+ var view = new DrawContext
+ {
+ Settings = args.Settings,
+ Process = args.Process,
+ Memory = args.Memory,
+ CurrentTime = args.CurrentTime,
+ Address = args.BaseAddress,
+ Level = 0,
+ };
+ classNode.InitFromRTTI(view);
+ }
}
}
diff --git a/ReClass.NET/Forms/MainForm.Designer.cs b/ReClass.NET/Forms/MainForm.Designer.cs
index e2b2a199..20b2c907 100644
--- a/ReClass.NET/Forms/MainForm.Designer.cs
+++ b/ReClass.NET/Forms/MainForm.Designer.cs
@@ -77,6 +77,7 @@ private void InitializeComponent()
this.toolStripSeparator8 = new System.Windows.Forms.ToolStripSeparator();
this.createClassFromNodesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator13 = new System.Windows.Forms.ToolStripSeparator();
+ this.initClassToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.dissectNodesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator9 = new System.Windows.Forms.ToolStripSeparator();
this.searchForEqualValuesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
@@ -106,6 +107,7 @@ private void InitializeComponent()
this.saveToolStripButton = new System.Windows.Forms.ToolStripButton();
this.toolStripSeparator7 = new System.Windows.Forms.ToolStripSeparator();
this.newClassToolStripButton = new System.Windows.Forms.ToolStripButton();
+ this.initClassFromRTTIToolStripBarMenuItem = new ReClassNET.Controls.TypeToolStripMenuItem();
this.addBytesToolStripDropDownButton = new System.Windows.Forms.ToolStripDropDownButton();
this.add4BytesToolStripMenuItem = new ReClassNET.Controls.IntegerToolStripMenuItem();
this.add8BytesToolStripMenuItem = new ReClassNET.Controls.IntegerToolStripMenuItem();
@@ -167,6 +169,8 @@ private void InitializeComponent()
this.generateCSharpCodeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.helpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.undoToolbarMenuItem = new ReClassNET.Controls.TypeToolStripMenuItem();
+ this.redoToolbarMenuItem = new ReClassNET.Controls.TypeToolStripMenuItem();
((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit();
this.splitContainer.Panel1.SuspendLayout();
this.splitContainer.Panel2.SuspendLayout();
@@ -202,7 +206,7 @@ private void InitializeComponent()
//
this.splitContainer.Panel2.BackColor = System.Drawing.SystemColors.Control;
this.splitContainer.Panel2.Controls.Add(this.memoryViewControl);
- this.splitContainer.Size = new System.Drawing.Size(1141, 524);
+ this.splitContainer.Size = new System.Drawing.Size(1103, 524);
this.splitContainer.SplitterDistance = 201;
this.splitContainer.TabIndex = 4;
//
@@ -364,7 +368,7 @@ private void InitializeComponent()
this.memoryViewControl.Location = new System.Drawing.Point(0, 0);
this.memoryViewControl.Name = "memoryViewControl";
this.memoryViewControl.NodeContextMenuStrip = this.selectedNodeContextMenuStrip;
- this.memoryViewControl.Size = new System.Drawing.Size(936, 524);
+ this.memoryViewControl.Size = new System.Drawing.Size(898, 524);
this.memoryViewControl.TabIndex = 0;
this.memoryViewControl.DrawContextRequested += new ReClassNET.Controls.DrawContextRequestEventHandler(this.memoryViewControl_DrawContextRequested);
this.memoryViewControl.SelectionChanged += new System.EventHandler(this.memoryViewControl_SelectionChanged);
@@ -382,6 +386,7 @@ private void InitializeComponent()
this.toolStripSeparator8,
this.createClassFromNodesToolStripMenuItem,
this.toolStripSeparator13,
+ this.initClassToolStripMenuItem,
this.dissectNodesToolStripMenuItem,
this.toolStripSeparator9,
this.searchForEqualValuesToolStripMenuItem,
@@ -402,7 +407,7 @@ private void InitializeComponent()
this.showCodeOfClassToolStripMenuItem,
this.shrinkClassToolStripMenuItem});
this.selectedNodeContextMenuStrip.Name = "selectedNodeContextMenuStrip";
- this.selectedNodeContextMenuStrip.Size = new System.Drawing.Size(270, 410);
+ this.selectedNodeContextMenuStrip.Size = new System.Drawing.Size(270, 432);
this.selectedNodeContextMenuStrip.Opening += new System.ComponentModel.CancelEventHandler(this.selectedNodeContextMenuStrip_Opening);
//
// changeTypeToolStripMenuItem
@@ -604,6 +609,16 @@ private void InitializeComponent()
this.toolStripSeparator13.Name = "toolStripSeparator13";
this.toolStripSeparator13.Size = new System.Drawing.Size(266, 6);
//
+ // initClassToolStripMenuItem
+ //
+ this.initClassToolStripMenuItem.Image = global::ReClassNET.Properties.Resources.B16x16_Button_AutoName;
+ this.initClassToolStripMenuItem.Name = "initClassToolStripMenuItem";
+ this.initClassToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)(((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Shift)
+ | System.Windows.Forms.Keys.N)));
+ this.initClassToolStripMenuItem.Size = new System.Drawing.Size(269, 22);
+ this.initClassToolStripMenuItem.Text = "Init Class from RTTI";
+ this.initClassToolStripMenuItem.Click += new System.EventHandler(this.initClassToolStripMenuItem_Click);
+ //
// dissectNodesToolStripMenuItem
//
this.dissectNodesToolStripMenuItem.Image = global::ReClassNET.Properties.Resources.B16x16_Camera;
@@ -771,12 +786,15 @@ private void InitializeComponent()
this.saveToolStripButton,
this.toolStripSeparator7,
this.newClassToolStripButton,
+ this.initClassFromRTTIToolStripBarMenuItem,
this.addBytesToolStripDropDownButton,
this.insertBytesToolStripDropDownButton,
- this.nodeTypesToolStripSeparator});
+ this.nodeTypesToolStripSeparator,
+ this.undoToolbarMenuItem,
+ this.redoToolbarMenuItem});
this.toolStrip.Location = new System.Drawing.Point(0, 24);
this.toolStrip.Name = "toolStrip";
- this.toolStrip.Size = new System.Drawing.Size(1141, 25);
+ this.toolStrip.Size = new System.Drawing.Size(1103, 25);
this.toolStrip.TabIndex = 3;
//
// attachToProcessToolStripSplitButton
@@ -832,6 +850,19 @@ private void InitializeComponent()
this.newClassToolStripButton.ToolTipText = "Add a new class to this project";
this.newClassToolStripButton.Click += new System.EventHandler(this.newClassToolStripButton_Click);
//
+ // initClassFromRTTIToolStripBarMenuItem
+ //
+ this.initClassFromRTTIToolStripBarMenuItem.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+ this.initClassFromRTTIToolStripBarMenuItem.Image = global::ReClassNET.Properties.Resources.B16x16_Button_AutoName;
+ this.initClassFromRTTIToolStripBarMenuItem.Name = "initClassFromRTTIToolStripBarMenuItem";
+ this.initClassFromRTTIToolStripBarMenuItem.Overflow = System.Windows.Forms.ToolStripItemOverflow.AsNeeded;
+ this.initClassFromRTTIToolStripBarMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)(((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Shift)
+ | System.Windows.Forms.Keys.N)));
+ this.initClassFromRTTIToolStripBarMenuItem.Size = new System.Drawing.Size(28, 25);
+ this.initClassFromRTTIToolStripBarMenuItem.ToolTipText = "Init selected class from RTTI info";
+ this.initClassFromRTTIToolStripBarMenuItem.Value = null;
+ this.initClassFromRTTIToolStripBarMenuItem.Click += new System.EventHandler(this.initClassToolStripMenuItem_Click);
+ //
// addBytesToolStripDropDownButton
//
this.addBytesToolStripDropDownButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
@@ -1023,7 +1054,7 @@ private void InitializeComponent()
this.infoToolStripStatusLabel});
this.statusStrip.Location = new System.Drawing.Point(0, 573);
this.statusStrip.Name = "statusStrip";
- this.statusStrip.Size = new System.Drawing.Size(1141, 22);
+ this.statusStrip.Size = new System.Drawing.Size(1103, 22);
this.statusStrip.TabIndex = 1;
//
// processInfoToolStripStatusLabel
@@ -1048,7 +1079,7 @@ private void InitializeComponent()
this.helpToolStripMenuItem});
this.mainMenuStrip.Location = new System.Drawing.Point(0, 0);
this.mainMenuStrip.Name = "mainMenuStrip";
- this.mainMenuStrip.Size = new System.Drawing.Size(1141, 24);
+ this.mainMenuStrip.Size = new System.Drawing.Size(1103, 24);
this.mainMenuStrip.TabIndex = 2;
//
// fileToolStripMenuItem
@@ -1367,16 +1398,37 @@ private void InitializeComponent()
this.aboutToolStripMenuItem.Text = "About...";
this.aboutToolStripMenuItem.Click += new System.EventHandler(this.aboutToolStripMenuItem_Click);
//
+ // undoToolbarMenuItem
+ //
+ this.undoToolbarMenuItem.Image = global::ReClassNET.Properties.Resources.B16x16_Undo;
+ this.undoToolbarMenuItem.Name = "undoToolbarMenuItem";
+ this.undoToolbarMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Z)));
+ this.undoToolbarMenuItem.Size = new System.Drawing.Size(28, 25);
+ this.undoToolbarMenuItem.ToolTipText = "Undo the latest change";
+ this.undoToolbarMenuItem.Value = null;
+ this.undoToolbarMenuItem.Click += new System.EventHandler(this.undoToolbarMenuItem_Click);
+ //
+ // redoToolbarMenuItem
+ //
+ this.redoToolbarMenuItem.Image = global::ReClassNET.Properties.Resources.B16x16_Redo;
+ this.redoToolbarMenuItem.Name = "redoToolbarMenuItem";
+ this.redoToolbarMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Y)));
+ this.redoToolbarMenuItem.Size = new System.Drawing.Size(28, 25);
+ this.redoToolbarMenuItem.ToolTipText = "Redo the latest undone change";
+ this.redoToolbarMenuItem.Value = null;
+ this.redoToolbarMenuItem.Click += new System.EventHandler(this.redoToolbarMenuItem_Click);
+ //
// MainForm
//
this.AllowDrop = true;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
- this.ClientSize = new System.Drawing.Size(1141, 595);
+ this.ClientSize = new System.Drawing.Size(1103, 595);
this.Controls.Add(this.splitContainer);
this.Controls.Add(this.toolStrip);
this.Controls.Add(this.statusStrip);
this.Controls.Add(this.mainMenuStrip);
+ this.KeyPreview = true;
this.MainMenuStrip = this.mainMenuStrip;
this.MinimumSize = new System.Drawing.Size(200, 100);
this.Name = "MainForm";
@@ -1542,6 +1594,10 @@ private void InitializeComponent()
private System.Windows.Forms.ToolStripMenuItem showEnumsToolStripMenuItem;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator23;
private System.Windows.Forms.ToolStripMenuItem isLittleEndianToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem initClassToolStripMenuItem;
+ private TypeToolStripMenuItem initClassFromRTTIToolStripBarMenuItem;
+ private TypeToolStripMenuItem undoToolbarMenuItem;
+ private TypeToolStripMenuItem redoToolbarMenuItem;
}
}
diff --git a/ReClass.NET/Forms/MainForm.Functions.cs b/ReClass.NET/Forms/MainForm.Functions.cs
index 84f1b53a..d5eaeeab 100644
--- a/ReClass.NET/Forms/MainForm.Functions.cs
+++ b/ReClass.NET/Forms/MainForm.Functions.cs
@@ -16,6 +16,7 @@
using ReClassNET.Nodes;
using ReClassNET.Project;
using ReClassNET.UI;
+using SD.Tools.Algorithmia.Commands;
namespace ReClassNET.Forms
{
@@ -213,6 +214,10 @@ public void LoadProjectFromPath(string path)
{
Contract.Requires(path != null);
+ CommandQueueManagerSingleton.GetInstance().ResetActiveCommandQueue();
+ CommandQueueManagerSingleton.GetInstance().BeginNonUndoablePeriod(); // we don't want to trigger undo/redo activity while loading
+ CommandQueueManagerSingleton.GetInstance().RaiseEvents = false;
+
var project = new ReClassNetProject();
LoadProjectFromPath(path, ref project);
@@ -224,6 +229,10 @@ public void LoadProjectFromPath(string path)
}
SetProject(project);
+
+ // Done loading, resume undo/redo activity
+ CommandQueueManagerSingleton.GetInstance().RaiseEvents = true;
+ CommandQueueManagerSingleton.GetInstance().EndNonUndoablePeriod();
}
/// Loads the file into the given project.
@@ -310,6 +319,9 @@ public void ReplaceSelectedNodesWithType(Type type)
{
var selected = hotSpotsToReplace.Dequeue();
+ // Use a single command here to wrap all state changes into one single undoable object, so everything gets undone/redone in 1 go
+ var cmd = new UndoablePeriodCommand("Replace node");
+ CommandQueueManagerSingleton.GetInstance().BeginUndoablePeriod(cmd);
var node = BaseNode.CreateInstanceFromType(type);
var createdNodes = new List();
@@ -329,6 +341,8 @@ public void ReplaceSelectedNodesWithType(Type type)
hotSpotsToReplace.Enqueue(new MemoryViewControl.SelectedNodeInfo(createdNode, selected.Process, selected.Memory, selected.Address + createdNode.Offset - node.Offset, selected.Level));
}
}
+ // Mark the end of the activities that have to be tracked with this single command
+ CommandQueueManagerSingleton.GetInstance().EndUndoablePeriod(cmd);
}
}
diff --git a/ReClass.NET/Forms/MainForm.cs b/ReClass.NET/Forms/MainForm.cs
index e771a10d..53b1c8db 100644
--- a/ReClass.NET/Forms/MainForm.cs
+++ b/ReClass.NET/Forms/MainForm.cs
@@ -22,6 +22,7 @@
using ReClassNET.UI;
using ReClassNET.Util;
using ReClassNET.Util.Conversion;
+using SD.Tools.Algorithmia.Commands;
namespace ReClassNET.Forms
{
@@ -95,8 +96,11 @@ public MainForm()
};
pluginManager = new PluginManager(new DefaultPluginHost(this, Program.RemoteProcess, Program.Logger));
+
+ CommandQueueManagerSingleton.GetInstance().CommandQueueActionPerformed += OnCommandQueueActionPerformed;
}
+
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
@@ -135,6 +139,8 @@ protected override void OnLoad(EventArgs e)
{
AttachToProcess(Program.CommandLineArgs[Constants.CommandLineOptions.AttachTo]);
}
+
+ SetStateOfUndoRedoButtons();
}
protected override void OnFormClosed(FormClosedEventArgs e)
@@ -835,6 +841,8 @@ private void memoryViewControl_SelectionChanged(object sender, EventArgs e)
addBytesToolStripDropDownButton.Enabled = parentContainer != null || isContainerNode;
insertBytesToolStripDropDownButton.Enabled = selectedNodes.Count == 1 && parentContainer != null && !isContainerNode;
+ initClassToolStripMenuItem.Enabled = nodeIsClass;
+ initClassFromRTTIToolStripBarMenuItem.Enabled = nodeIsClass;
var enabled = selectedNodes.Count > 0 && !nodeIsClass;
toolStrip.Items.OfType().ForEach(b => b.Enabled = enabled);
@@ -1027,7 +1035,7 @@ private void memoryViewControl_DrawContextRequested(object sender, DrawContextRe
{
var process = Program.RemoteProcess;
- var classNode = CurrentClassNode;
+ var classNode = (args.Node as ClassNode) ?? CurrentClassNode;
if (classNode != null)
{
memoryViewBuffer.Size = classNode.MemorySize;
@@ -1051,5 +1059,45 @@ private void memoryViewControl_DrawContextRequested(object sender, DrawContextRe
args.BaseAddress = address;
}
}
+
+ private void initClassToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ var selectedNodes = memoryViewControl.GetSelectedNodes();
+ var node = selectedNodes.FirstOrDefault()?.Node;
+ if (node == null || !(node is ClassNode))
+ {
+ return;
+ }
+
+ var cmd = new UndoablePeriodCommand("InitClassFromRTTI");
+ CommandQueueManagerSingleton.GetInstance().BeginUndoablePeriod(cmd);
+ memoryViewControl.InitCurrentClassFromRTTI(node as ClassNode);
+ CommandQueueManagerSingleton.GetInstance().EndUndoablePeriod(cmd);
+ }
+
+
+ private void SetStateOfUndoRedoButtons()
+ {
+ undoToolbarMenuItem.Enabled = CommandQueueManagerSingleton.GetInstance().CanUndo(Program.CommandQueueID);
+ redoToolbarMenuItem.Enabled = CommandQueueManagerSingleton.GetInstance().CanDo(Program.CommandQueueID);
+ }
+
+
+ private void OnCommandQueueActionPerformed(object sender, CommandQueueActionPerformedEventArgs e)
+ {
+ SetStateOfUndoRedoButtons();
+ }
+
+
+ private void undoToolbarMenuItem_Click(object sender, EventArgs e)
+ {
+ CommandQueueManagerSingleton.GetInstance().UndoLastCommand();
+ }
+
+
+ private void redoToolbarMenuItem_Click(object sender, EventArgs e)
+ {
+ CommandQueueManagerSingleton.GetInstance().RedoLastCommand();
+ }
}
}
diff --git a/ReClass.NET/Forms/MainForm.resx b/ReClass.NET/Forms/MainForm.resx
index c430dab3..0e88c503 100644
--- a/ReClass.NET/Forms/MainForm.resx
+++ b/ReClass.NET/Forms/MainForm.resx
@@ -206,6 +206,6 @@
- 42
+ 104
\ No newline at end of file
diff --git a/ReClass.NET/Memory/MemoryBuffer.cs b/ReClass.NET/Memory/MemoryBuffer.cs
index e1b515a4..cb7361ec 100644
--- a/ReClass.NET/Memory/MemoryBuffer.cs
+++ b/ReClass.NET/Memory/MemoryBuffer.cs
@@ -366,5 +366,17 @@ public bool HasChanged(int offset, int length)
return false;
}
+
+ public UInt64FloatDoubleData InterpretData64(int offset) => new UInt64FloatDoubleData
+ {
+ Raw1 = ReadInt32(offset),
+ Raw2 = ReadInt32(offset + sizeof(int))
+ };
+
+
+ public UInt32FloatData InterpretData32(int offset) => new UInt32FloatData
+ {
+ Raw = ReadInt32(offset)
+ };
}
}
diff --git a/ReClass.NET/Nodes/BaseContainerNode.cs b/ReClass.NET/Nodes/BaseContainerNode.cs
index 6926111f..1d3ec5e8 100644
--- a/ReClass.NET/Nodes/BaseContainerNode.cs
+++ b/ReClass.NET/Nodes/BaseContainerNode.cs
@@ -1,12 +1,16 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
+using ReClassNET.Extensions;
+using SD.Tools.Algorithmia.Commands;
+using SD.Tools.Algorithmia.GeneralDataStructures;
namespace ReClassNET.Nodes
{
public abstract class BaseContainerNode : BaseNode
{
- private readonly List nodes = new List();
+ //private readonly List nodes = new List();
+ private readonly CommandifiedList nodes = new CommandifiedList();
private int updateCount;
@@ -183,8 +187,8 @@ public void ReplaceChildNode(BaseNode oldNode, BaseNode newNode, ref List createdNodes)
{
return;
}
-
+ // Mark the actions that follow as actions that have to be ignored so they're not ending up in a command's command queue
+ CommandQueueManagerSingleton.GetInstance().BeginNonUndoablePeriod();
while (size > 0)
{
var node = CreateDefaultNodeForSize(size);
@@ -281,6 +286,8 @@ protected void InsertBytes(int index, int size, ref List createdNodes)
index++;
}
+ // Mark the end of the actions that have to be ignored for undo/redo
+ CommandQueueManagerSingleton.GetInstance().EndNonUndoablePeriod();
OnNodesUpdated();
}
diff --git a/ReClass.NET/Nodes/BaseHexCommentNode.cs b/ReClass.NET/Nodes/BaseHexCommentNode.cs
index f2a4053f..98305d8c 100644
--- a/ReClass.NET/Nodes/BaseHexCommentNode.cs
+++ b/ReClass.NET/Nodes/BaseHexCommentNode.cs
@@ -44,7 +44,7 @@ protected int AddComment(DrawContext view, int x, int y, float fvalue, IntPtr iv
if (view.Settings.ShowCommentRtti)
{
- var rtti = view.Process.ReadRemoteRuntimeTypeInformation(ivalue);
+ var rtti = GetAssociatedRemoteRuntimeTypeInformation(view, ivalue);
if (!string.IsNullOrEmpty(rtti))
{
x = AddText(view, x, y, view.Settings.OffsetColor, HotSpot.ReadOnlyId, rtti) + view.Font.Width;
@@ -110,5 +110,10 @@ protected int AddComment(DrawContext view, int x, int y, float fvalue, IntPtr iv
return x;
}
+
+ public string GetAssociatedRemoteRuntimeTypeInformation(DrawContext context, IntPtr ivalue)
+ {
+ return context.Process.ReadRemoteRuntimeTypeInformation(ivalue);
+ }
}
}
diff --git a/ReClass.NET/Nodes/BaseNode.cs b/ReClass.NET/Nodes/BaseNode.cs
index adef39e5..edac2e11 100644
--- a/ReClass.NET/Nodes/BaseNode.cs
+++ b/ReClass.NET/Nodes/BaseNode.cs
@@ -7,6 +7,8 @@
using ReClassNET.Extensions;
using ReClassNET.UI;
using ReClassNET.Util;
+using SD.Tools.Algorithmia.GeneralDataStructures;
+using SD.Tools.Algorithmia.GeneralDataStructures.EventArguments;
namespace ReClassNET.Nodes
{
@@ -24,14 +26,25 @@ public abstract class BaseNode
private static int nodeIndex = 0;
- private string name = string.Empty;
+ private CommandifiedMember name;
private string comment = string.Empty;
/// Gets or sets the offset of the node.
public int Offset { get; set; }
/// Gets or sets the name of the node. If a new name was set the property changed event gets fired.
- public virtual string Name { get => name; set { if (value != null && name != value) { name = value; NameChanged?.Invoke(this); } } }
+ public virtual string Name
+ {
+ get => name.MemberValue;
+ set
+ {
+ if (value == null)
+ {
+ return;
+ }
+ name.MemberValue = value;
+ }
+ }
/// Gets or sets the comment of the node.
public string Comment { get => comment; set { if (value != null && comment != value) { comment = value; CommentChanged?.Invoke(this); } } }
@@ -39,9 +52,12 @@ public abstract class BaseNode
/// Gets or sets the parent node.
public BaseNode ParentNode { get; internal set; }
- /// Gets a value indicating whether this node is wrapped into an other node.
+ /// Gets a value indicating whether this node is wrapped into an other node.
public bool IsWrapped => ParentNode is BaseWrapperNode;
+ /// All nodes that are wrapped can't be selected except classnodes because they have a context menu
+ public bool CanBeSelected => !IsWrapped || (this is ClassNode);
+
/// Gets or sets a value indicating whether this node is hidden.
public bool IsHidden { get; set; }
@@ -97,12 +113,15 @@ protected BaseNode()
Contract.Ensures(name != null);
Contract.Ensures(comment != null);
- Name = $"N{nodeIndex++:X08}";
+ name = new CommandifiedMember("Name", Constants.GeneralPurposeChangeType.None, $"N{nodeIndex++:X08}");
+ name.ValueChanged += Name_ValueChanged;
Comment = string.Empty;
LevelsOpen[0] = true;
}
+ private void Name_ValueChanged(object sender, MemberChangedEventArgs e) => NameChanged?.Invoke(this);
+
public abstract void GetUserInterfaceInfo(out string name, out Image icon);
public virtual bool UseMemoryPreviewToolTip(HotSpot spot, out IntPtr address)
@@ -236,6 +255,15 @@ public virtual void ClearSelection()
/// The calculated height.
public abstract int CalculateDrawnHeight(DrawContext context);
+ ///
+ /// Called when this node has been created, initialized and the parent node has been assigned. For some nodes
+ /// Additional work has to be performed, this work can be done in a derived method of this method.
+ ///
+ public virtual void PerformPostInitWork()
+ {
+ // nop
+ }
+
/// Updates the node from the given . Sets the and of the node.
/// The spot.
public virtual void Update(HotSpot spot)
@@ -367,7 +395,7 @@ protected void AddSelection(DrawContext context, int x, int y, int height)
Contract.Requires(context != null);
Contract.Requires(context.Graphics != null);
- if (y > context.ClientArea.Bottom || y + height < 0 || IsWrapped)
+ if (y > context.ClientArea.Bottom || y + height < 0 || !CanBeSelected)
{
return;
}
diff --git a/ReClass.NET/Nodes/ClassNode.cs b/ReClass.NET/Nodes/ClassNode.cs
index 9b144061..d5eba916 100644
--- a/ReClass.NET/Nodes/ClassNode.cs
+++ b/ReClass.NET/Nodes/ClassNode.cs
@@ -1,9 +1,12 @@
using System;
+using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Drawing;
using System.Linq;
using ReClassNET.Controls;
using ReClassNET.UI;
+using SD.Tools.Algorithmia.GeneralDataStructures;
+using SD.Tools.Algorithmia.GeneralDataStructures.EventArguments;
namespace ReClassNET.Nodes
{
@@ -27,7 +30,12 @@ public class ClassNode : BaseContainerNode
public Guid Uuid { get; set; }
- public string AddressFormula { get; set; } = DefaultAddressFormula;
+ private CommandifiedMember addressFormula;
+ public string AddressFormula
+ {
+ get => addressFormula.MemberValue;
+ set => addressFormula.MemberValue = value;
+ }
public event NodeEventHandler NodesChanged;
@@ -35,6 +43,7 @@ internal ClassNode(bool notifyClassCreated)
{
Contract.Ensures(AddressFormula != null);
+ addressFormula = new CommandifiedMember("AddressFormula", Constants.GeneralPurposeChangeType.None, DefaultAddressFormula);
LevelsOpen.DefaultValue = true;
Uuid = Guid.NewGuid();
@@ -51,7 +60,48 @@ public static ClassNode Create()
return new ClassNode(true);
}
+
+ ///
+ /// Initializes the class' name and vtable node from RTTI information, if it's not set already
+ ///
+ ///
+ public void InitFromRTTI(DrawContext context)
+ {
+ // first node should be a VTable node or a hex64/32 node
+ if (Nodes.Count <= 0)
+ {
+ return;
+ }
+
+ var rttiInfoFromFirstNode = string.Empty;
+ var firstNode = Nodes[0];
+ if (firstNode is VirtualMethodTableNode vtableNode)
+ {
+ rttiInfoFromFirstNode = vtableNode.GetAssociatedRemoteRuntimeTypeInformation(context);
+ }
+ else if (firstNode is BaseHexCommentNode baseHexCommentNode)
+ {
+ // ask it as if it might point to a vtable
+ var value = context.Memory.InterpretData64(Offset);
+ rttiInfoFromFirstNode = baseHexCommentNode.GetAssociatedRemoteRuntimeTypeInformation(context, value.IntPtr);
+ if (!string.IsNullOrEmpty(rttiInfoFromFirstNode))
+ {
+ // convert first node to vtable node
+ var newVTableNode = BaseNode.CreateInstanceFromType(typeof(VirtualMethodTableNode));
+ var createdNodes = new List();
+ this.ReplaceChildNode(firstNode, newVTableNode, ref createdNodes);
+ }
+ }
+ if (string.IsNullOrEmpty(rttiInfoFromFirstNode))
+ {
+ return;
+ }
+
+ var fragments = rttiInfoFromFirstNode.Split(':');
+ this.Name = fragments[0];
+ }
+
public override void GetUserInterfaceInfo(out string name, out Image icon)
{
throw new InvalidOperationException($"The '{nameof(ClassNode)}' node should not be accessible from the ui.");
diff --git a/ReClass.NET/Nodes/Hex32Node.cs b/ReClass.NET/Nodes/Hex32Node.cs
index c7b54027..2329c954 100644
--- a/ReClass.NET/Nodes/Hex32Node.cs
+++ b/ReClass.NET/Nodes/Hex32Node.cs
@@ -18,7 +18,7 @@ public override void GetUserInterfaceInfo(out string name, out Image icon)
public override bool UseMemoryPreviewToolTip(HotSpot spot, out IntPtr address)
{
- var value = ReadFromBuffer(spot.Memory, Offset);
+ var value = spot.Memory.InterpretData32(Offset);
address = value.IntPtr;
@@ -27,7 +27,7 @@ public override bool UseMemoryPreviewToolTip(HotSpot spot, out IntPtr address)
public override string GetToolTipText(HotSpot spot)
{
- var value = ReadFromBuffer(spot.Memory, Offset);
+ var value = spot.Memory.InterpretData32(Offset);
return $"Int32: {value.IntValue}\nUInt32: 0x{value.UIntValue:X08}\nFloat: {value.FloatValue:0.000}";
}
@@ -46,16 +46,11 @@ protected override int AddComment(DrawContext context, int x, int y)
{
x = base.AddComment(context, x, y);
- var value = ReadFromBuffer(context.Memory, Offset);
+ var value = context.Memory.InterpretData32(Offset);
x = AddComment(context, x, y, value.FloatValue, value.IntPtr, value.UIntPtr);
return x;
}
-
- private static UInt32FloatData ReadFromBuffer(MemoryBuffer memory, int offset) => new UInt32FloatData
- {
- Raw = memory.ReadInt32(offset)
- };
}
}
diff --git a/ReClass.NET/Nodes/Hex64Node.cs b/ReClass.NET/Nodes/Hex64Node.cs
index d54f1e71..61749e46 100644
--- a/ReClass.NET/Nodes/Hex64Node.cs
+++ b/ReClass.NET/Nodes/Hex64Node.cs
@@ -18,7 +18,7 @@ public override void GetUserInterfaceInfo(out string name, out Image icon)
public override bool UseMemoryPreviewToolTip(HotSpot spot, out IntPtr address)
{
- var value = ReadFromBuffer(spot.Memory, Offset);
+ var value = spot.Memory.InterpretData64(Offset);
address = value.IntPtr;
@@ -27,7 +27,7 @@ public override bool UseMemoryPreviewToolTip(HotSpot spot, out IntPtr address)
public override string GetToolTipText(HotSpot spot)
{
- var value = ReadFromBuffer(spot.Memory, Offset);
+ var value = spot.Memory.InterpretData64(Offset);
return $"Int64: {value.LongValue}\nUInt64: 0x{value.ULongValue:X016}\nFloat: {value.FloatValue:0.000}\nDouble: {value.DoubleValue:0.000}";
}
@@ -46,17 +46,11 @@ protected override int AddComment(DrawContext context, int x, int y)
{
x = base.AddComment(context, x, y);
- var value = ReadFromBuffer(context.Memory, Offset);
+ var value = context.Memory.InterpretData64(Offset);
x = AddComment(context, x, y, value.FloatValue, value.IntPtr, value.UIntPtr);
return x;
}
-
- private static UInt64FloatDoubleData ReadFromBuffer(MemoryBuffer memory, int offset) => new UInt64FloatDoubleData
- {
- Raw1 = memory.ReadInt32(offset),
- Raw2 = memory.ReadInt32(offset + sizeof(int))
- };
}
}
diff --git a/ReClass.NET/Nodes/PointerNode.cs b/ReClass.NET/Nodes/PointerNode.cs
index 027b0d28..9df3b8b0 100644
--- a/ReClass.NET/Nodes/PointerNode.cs
+++ b/ReClass.NET/Nodes/PointerNode.cs
@@ -1,5 +1,6 @@
using System;
using System.Drawing;
+using ReClassNET.AddressParser;
using ReClassNET.Controls;
using ReClassNET.Memory;
using ReClassNET.UI;
@@ -134,5 +135,39 @@ public override int CalculateDrawnHeight(DrawContext context)
}
return height;
}
+
+ public override void PerformPostInitWork()
+ {
+ base.PerformPostInitWork();
+
+ var parentClass = ParentNode as ClassNode;
+ if (parentClass == null)
+ {
+ return;
+ }
+
+ var process = Program.RemoteProcess;
+ IntPtr address;
+ try
+ {
+ address = process.ParseAddress(parentClass.AddressFormula);
+ }
+ catch (ParseException)
+ {
+ address = IntPtr.Zero;
+ }
+
+ var memoryBuffer = new MemoryBuffer() { Size = parentClass.MemorySize};
+ memoryBuffer.UpdateFrom(process, address);
+ var ptr = memoryBuffer.ReadIntPtr(Offset);
+
+ var classNode = ((ClassInstanceNode)InnerNode)?.InnerNode as ClassNode;
+ if (classNode == null)
+ {
+ return;
+ }
+
+ classNode.AddressFormula = ptr.ToString(Constants.AddressHexFormat);
+ }
}
}
diff --git a/ReClass.NET/Nodes/VirtualMethodTableNode.cs b/ReClass.NET/Nodes/VirtualMethodTableNode.cs
index 9e82ab40..2c9099a0 100644
--- a/ReClass.NET/Nodes/VirtualMethodTableNode.cs
+++ b/ReClass.NET/Nodes/VirtualMethodTableNode.cs
@@ -33,7 +33,33 @@ public override void Initialize()
AddNode(CreateDefaultNodeForSize(IntPtr.Size));
}
}
+
+ protected override int AddComment(DrawContext context, int x, int y)
+ {
+ x = base.AddComment(context, x, y);
+
+ if (context.Settings.ShowCommentRtti)
+ {
+ var rtti = GetAssociatedRemoteRuntimeTypeInformation(context);
+ if (!string.IsNullOrEmpty(rtti))
+ {
+ x = AddText(context, x, y, context.Settings.OffsetColor, HotSpot.ReadOnlyId, rtti) + context.Font.Width;
+ }
+ }
+ return x;
+ }
+
+ public string GetAssociatedRemoteRuntimeTypeInformation(DrawContext context)
+ {
+ var addressFirstVTableFunction = context.Memory.InterpretData64(Offset).IntPtr;
+ if (addressFirstVTableFunction != IntPtr.Zero)
+ {
+ return context.Process.ReadRemoteRuntimeTypeInformation(addressFirstVTableFunction);
+ }
+ return string.Empty;
+ }
+
public override Size Draw(DrawContext context, int x, int y)
{
if (IsHidden && !IsWrapped)
diff --git a/ReClass.NET/Program.cs b/ReClass.NET/Program.cs
index f28cb0bd..aba0f3f1 100644
--- a/ReClass.NET/Program.cs
+++ b/ReClass.NET/Program.cs
@@ -2,6 +2,7 @@
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
+using System.Threading;
using System.Windows.Forms;
using Microsoft.SqlServer.MessageBox;
using ReClassNET.Core;
@@ -11,6 +12,7 @@
using ReClassNET.Native;
using ReClassNET.UI;
using ReClassNET.Util;
+using SD.Tools.Algorithmia.Commands;
namespace ReClassNET
{
@@ -34,10 +36,18 @@ public static class Program
public static FontEx MonoSpaceFont { get; private set; }
+ public static Guid CommandQueueID { get; private set; }
+
[STAThread]
static void Main(string[] args)
{
DesignMode = false; // The designer doesn't call Main()
+ CommandQueueID = Guid.NewGuid();
+
+ // wire event handlers for unhandled exceptions, so these will be shown using our own method.
+ Application.SetUnhandledExceptionMode(UnhandledExceptionMode.Automatic, true);
+ Application.ThreadException += new ThreadExceptionEventHandler(Program.Application_ThreadException);
+ AppDomain.CurrentDomain.UnhandledException+=new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
CommandLineArgs = new CommandLineArgs(args);
@@ -63,6 +73,11 @@ static void Main(string[] args)
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
+ // switch is set to false, so Do actions during Undo actions are ignored.
+ CommandQueueManager.ThrowExceptionOnDoDuringUndo = false;
+ // activate our command queue stack. We're only changing things from the main thread so we don't need multiple stacks.
+ CommandQueueManagerSingleton.GetInstance().ActivateCommandQueueStack(CommandQueueID);
+
CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture;
Settings = SettingsSerializer.Load();
@@ -98,7 +113,17 @@ static void Main(string[] args)
SettingsSerializer.Save(Settings);
}
-
+
+ private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
+ {
+ ShowException(e.Exception);
+ }
+
+ private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
+ {
+ ShowException(e.ExceptionObject as Exception);
+ }
+
/// Shows the exception in a special form.
/// The exception.
public static void ShowException(Exception ex)
diff --git a/ReClass.NET/Project/ReClassNetProject.cs b/ReClass.NET/Project/ReClassNetProject.cs
index 90f4b5ca..bd2be84b 100644
--- a/ReClass.NET/Project/ReClassNetProject.cs
+++ b/ReClass.NET/Project/ReClassNetProject.cs
@@ -1,9 +1,13 @@
using System;
using System.Collections.Generic;
+using System.ComponentModel;
using System.Diagnostics.Contracts;
using System.Linq;
+using ReClassNET.Extensions;
using ReClassNET.Nodes;
using ReClassNET.Util;
+using SD.Tools.Algorithmia.GeneralDataStructures;
+using SD.Tools.Algorithmia.GeneralDataStructures.EventArguments;
namespace ReClassNET.Project
{
@@ -18,7 +22,7 @@ public class ReClassNetProject : IDisposable
public event EnumsChangedEvent EnumRemoved;
private readonly List enums = new List();
- private readonly List classes = new List();
+ private readonly CommandifiedList classes = new CommandifiedList(); // use a commandified list for the set of classes so we get auto undo/redo tracking
public IReadOnlyList Enums => enums;
@@ -36,6 +40,25 @@ public class ReClassNetProject : IDisposable
/// List of data types to use while generating C++ code for nodes.
///
public CppTypeMapping TypeMapping { get; } = new CppTypeMapping();
+
+ public ReClassNetProject()
+ {
+ // We're using ListChanged instead of ElementAdding here because ListChanged is also raised when 'Redo' is executed on the list re-adding the already created element.
+ classes.ListChanged += Classes_ListChanged;
+ classes.ElementRemoved += Classes_ElementRemoved;
+ }
+
+ private void Classes_ListChanged(object sender, System.ComponentModel.ListChangedEventArgs e)
+ {
+ // nothing. The removed event is handled separately because ListChangedType.ItemRemoved doesn't give access to the removed element and ElementRemoved does.
+ if (e.ListChangedType == ListChangedType.ItemAdded)
+ {
+ ClassAdded?.Invoke(classes[e.NewIndex]);
+ }
+ }
+
+ private void Classes_ElementRemoved(object sender, CollectionElementRemovedEventArgs e) => ClassRemoved?.Invoke(e.InvolvedElement);
+ private void Enums_ElementRemoved(object sender, CollectionElementRemovedEventArgs e) => EnumRemoved?.Invoke(e.InvolvedElement);
public void Dispose()
{
@@ -56,7 +79,7 @@ public void AddClass(ClassNode node)
node.NodesChanged += NodesChanged_Handler;
- ClassAdded?.Invoke(node);
+ // No need to invoke the ClassAdded event here, as it's automatically raised when the class is added to the commandified list.
}
public bool ContainsClass(Guid uuid)
diff --git a/ReClass.NET/Properties/Resources.Designer.cs b/ReClass.NET/Properties/Resources.Designer.cs
index 3d412074..3738ffbc 100644
--- a/ReClass.NET/Properties/Resources.Designer.cs
+++ b/ReClass.NET/Properties/Resources.Designer.cs
@@ -19,7 +19,7 @@ namespace ReClassNET.Properties {
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
@@ -190,6 +190,16 @@ internal static System.Drawing.Bitmap B16x16_Button_Array {
}
}
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap B16x16_Button_AutoName {
+ get {
+ object obj = ResourceManager.GetObject("B16x16_Button_AutoName", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
///
/// Looks up a localized resource of type System.Drawing.Bitmap.
///
@@ -1371,7 +1381,7 @@ internal static System.Drawing.Bitmap B32x32_Plugin {
}
///
- /// Looks up a localized string similar to 2020/10/17 09:45:04
+ /// Looks up a localized string similar to 2023/07/03 12:55:32
///.
///
internal static string BuildDate {
diff --git a/ReClass.NET/Properties/Resources.resx b/ReClass.NET/Properties/Resources.resx
index 48c2c826..3cee6c55 100644
--- a/ReClass.NET/Properties/Resources.resx
+++ b/ReClass.NET/Properties/Resources.resx
@@ -517,4 +517,7 @@
..\Resources\Images\B16x16_Button_NUInt.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+ ..\Resources\Images\B16x16_Button_AutoName.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
\ No newline at end of file
diff --git a/ReClass.NET/ReClass.NET.csproj b/ReClass.NET/ReClass.NET.csproj
index 0c990cc2..3a98a45a 100644
--- a/ReClass.NET/ReClass.NET.csproj
+++ b/ReClass.NET/ReClass.NET.csproj
@@ -122,6 +122,12 @@
False
..\Dependencies\Microsoft.ExceptionMessageBox.dll
+
+ ..\packages\SD.Tools.Algorithmia.1.4.0\lib\net452\SD.Tools.Algorithmia.dll
+
+
+ ..\packages\SD.Tools.BCLExtensions.1.2.2\lib\net452\SD.Tools.BCLExtensions.dll
+
@@ -608,6 +614,7 @@
Resources.Designer.cs
+
SettingsSingleFileGenerator
Settings.Designer.cs
@@ -1023,6 +1030,9 @@
+
+
+
powershell -Command "((Get-Date).ToUniversalTime()).ToString(\"yyyy\/MM\/dd HH:mm:ss\") | Out-File '$(ProjectDir)Resources\BuildDate.txt'"
diff --git a/ReClass.NET/Resources/Images/B16x16_Button_AutoName.png b/ReClass.NET/Resources/Images/B16x16_Button_AutoName.png
new file mode 100644
index 00000000..bfdd7e64
Binary files /dev/null and b/ReClass.NET/Resources/Images/B16x16_Button_AutoName.png differ
diff --git a/ReClass.NET/Settings.cs b/ReClass.NET/Settings.cs
index b5d9268b..32eafc3d 100644
--- a/ReClass.NET/Settings.cs
+++ b/ReClass.NET/Settings.cs
@@ -1,11 +1,36 @@
-using System.Drawing;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
using System.Text;
+using System.Windows.Forms;
+using ReClassNET.Nodes;
using ReClassNET.Util;
namespace ReClassNET
{
public class Settings
{
+ private readonly Dictionary _shortcutKeyPerNode;
+
+ public Settings()
+ {
+ _shortcutKeyPerNode = new Dictionary
+ {
+ { typeof(Hex64Node), Keys.Control | Keys.Shift | Keys.D6 },
+ { typeof(ClassInstanceNode), Keys.Control | Keys.Shift | Keys.C },
+ { typeof(FloatNode), Keys.Control | Keys.Shift | Keys.F },
+ { typeof(Hex8Node), Keys.Control | Keys.Shift | Keys.B },
+ { typeof(PointerNode), Keys.Control | Keys.Shift | Keys.P },
+ { typeof(Vector2Node), Keys.Control | Keys.Shift | Keys.D2 },
+ { typeof(Vector3Node), Keys.Control | Keys.Shift | Keys.D3 },
+ { typeof(Vector4Node), Keys.Control | Keys.Shift | Keys.D4 },
+ { typeof(VirtualMethodTableNode), Keys.Control | Keys.Shift | Keys.V },
+ { typeof(BoolNode), Keys.Control | Keys.Shift | Keys.O },
+ { typeof(EnumNode), Keys.Control | Keys.Shift | Keys.E },
+ { typeof(Int32Node), Keys.Control | Keys.Shift | Keys.I }
+ };
+ }
+
// Application Settings
public string LastProcess { get; set; } = string.Empty;
@@ -75,6 +100,11 @@ public class Settings
public Color PluginColor { get; set; } = Color.FromArgb(255, 0, 255);
public CustomDataMap CustomData { get; } = new CustomDataMap();
+
+ public Keys GetShortcutKeyForNodeType(Type nodeType)
+ {
+ return !_shortcutKeyPerNode.TryGetValue(nodeType, out var shortcutKeys) ? Keys.None : shortcutKeys;
+ }
public Settings Clone() => MemberwiseClone() as Settings;
}
diff --git a/ReClass.NET/UI/HotSpot.cs b/ReClass.NET/UI/HotSpot.cs
index 3a0d4bc0..27b949d0 100644
--- a/ReClass.NET/UI/HotSpot.cs
+++ b/ReClass.NET/UI/HotSpot.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Drawing;
using ReClassNET.Memory;
using ReClassNET.Nodes;
diff --git a/ReClass.NET/UI/NodeTypesBuilder.cs b/ReClass.NET/UI/NodeTypesBuilder.cs
index 8d519022..79d4bfbe 100644
--- a/ReClass.NET/UI/NodeTypesBuilder.cs
+++ b/ReClass.NET/UI/NodeTypesBuilder.cs
@@ -14,6 +14,7 @@ internal static class NodeTypesBuilder
{
private static readonly List defaultNodeTypeGroupList = new List();
private static readonly Dictionary> pluginNodeTypes = new Dictionary>();
+ private static readonly HashSet nodeTypesWhichCanOverflowInToolbar;
static NodeTypesBuilder()
{
@@ -27,6 +28,9 @@ static NodeTypesBuilder()
defaultNodeTypeGroupList.Add(new[] { typeof(PointerNode), typeof(ArrayNode), typeof(UnionNode) });
defaultNodeTypeGroupList.Add(new[] { typeof(ClassInstanceNode) });
defaultNodeTypeGroupList.Add(new[] { typeof(VirtualMethodTableNode), typeof(FunctionNode), typeof(FunctionPtrNode) });
+
+ // define the node types which can overflow in the toolbar if the window is too narrow. Add types here which aren't used that much
+ nodeTypesWhichCanOverflowInToolbar = new HashSet { typeof(NIntNode), typeof(NUIntNode), typeof(BitFieldNode), typeof(Utf16TextNode), typeof(Utf16TextPtrNode) } ;
}
public static void AddPluginNodeGroup(Plugin plugin, IReadOnlyList nodeTypes)
@@ -57,14 +61,16 @@ public static IEnumerable CreateToolStripButtons(Action han
return CreateToolStripItems(t =>
{
- GetNodeInfoFromType(t, out var label, out var icon);
+ GetNodeInfoFromType(t, out var label, out var icon, out var shortcutKeys);
- var item = new TypeToolStripButton
+ var item = new TypeToolStripMenuItem
{
Value = t,
ToolTipText = label,
DisplayStyle = ToolStripItemDisplayStyle.Image,
- Image = icon
+ Image = icon,
+ ShortcutKeys = shortcutKeys,
+ Overflow = nodeTypesWhichCanOverflowInToolbar.Contains(t) ? ToolStripItemOverflow.AsNeeded : ToolStripItemOverflow.Never,
};
item.Click += clickHandler;
return item;
@@ -74,7 +80,7 @@ public static IEnumerable CreateToolStripButtons(Action han
Image = p.Icon
}, t =>
{
- GetNodeInfoFromType(t, out var label, out var icon);
+ GetNodeInfoFromType(t, out var label, out var icon, out var shortcutKeys);
var item = new TypeToolStripMenuItem
{
@@ -95,13 +101,14 @@ public static IEnumerable CreateToolStripMenuItems(Action h
var items = CreateToolStripItems(t =>
{
- GetNodeInfoFromType(t, out var label, out var icon);
+ GetNodeInfoFromType(t, out var label, out var icon, out var shortcutKeys);
var item = new TypeToolStripMenuItem
{
Value = t,
Text = label,
- Image = icon
+ Image = icon,
+ ShortcutKeys = shortcutKeys,
};
item.Click += clickHandler;
return item;
@@ -166,10 +173,12 @@ private static IEnumerable CreateToolStripItems(Func
+
+
+
+
\ No newline at end of file