├── .gitattributes
├── .gitignore
├── PM3UniversalGUI.sln
├── PM3UniversalGUI
├── App.config
├── ConsoleStreamReader.cs
├── MainForm.Designer.cs
├── MainForm.cs
├── MainForm.resx
├── PM3Client.cs
├── PM3UniversalGUI.csproj
├── Program.cs
├── Properties
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ ├── Resources.resx
│ ├── Settings.Designer.cs
│ └── Settings.settings
├── StringUtils.cs
├── bin
│ └── Release
│ │ ├── PM3UniversalGUI.exe
│ │ └── PM3UniversalGUI.exe.config
├── crab.png
└── pm3universalgui.png
└── README.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | #[Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | #[Bb]in/
22 | [Oo]bj/
23 | [Ll]og/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | project.fragment.lock.json
46 | artifacts/
47 |
48 | *_i.c
49 | *_p.c
50 | *_i.h
51 | *.ilk
52 | *.meta
53 | *.obj
54 | *.pch
55 | *.pdb
56 | *.pgc
57 | *.pgd
58 | *.rsp
59 | *.sbr
60 | *.tlb
61 | *.tli
62 | *.tlh
63 | *.tmp
64 | *.tmp_proj
65 | *.log
66 | *.vspscc
67 | *.vssscc
68 | .builds
69 | *.pidb
70 | *.svclog
71 | *.scc
72 |
73 | # Chutzpah Test files
74 | _Chutzpah*
75 |
76 | # Visual C++ cache files
77 | ipch/
78 | *.aps
79 | *.ncb
80 | *.opendb
81 | *.opensdf
82 | *.sdf
83 | *.cachefile
84 | *.VC.db
85 | *.VC.VC.opendb
86 |
87 | # Visual Studio profiler
88 | *.psess
89 | *.vsp
90 | *.vspx
91 | *.sap
92 |
93 | # TFS 2012 Local Workspace
94 | $tf/
95 |
96 | # Guidance Automation Toolkit
97 | *.gpState
98 |
99 | # ReSharper is a .NET coding add-in
100 | _ReSharper*/
101 | *.[Rr]e[Ss]harper
102 | *.DotSettings.user
103 |
104 | # JustCode is a .NET coding add-in
105 | .JustCode
106 |
107 | # TeamCity is a build add-in
108 | _TeamCity*
109 |
110 | # DotCover is a Code Coverage Tool
111 | *.dotCover
112 |
113 | # NCrunch
114 | _NCrunch_*
115 | .*crunch*.local.xml
116 | nCrunchTemp_*
117 |
118 | # MightyMoose
119 | *.mm.*
120 | AutoTest.Net/
121 |
122 | # Web workbench (sass)
123 | .sass-cache/
124 |
125 | # Installshield output folder
126 | [Ee]xpress/
127 |
128 | # DocProject is a documentation generator add-in
129 | DocProject/buildhelp/
130 | DocProject/Help/*.HxT
131 | DocProject/Help/*.HxC
132 | DocProject/Help/*.hhc
133 | DocProject/Help/*.hhk
134 | DocProject/Help/*.hhp
135 | DocProject/Help/Html2
136 | DocProject/Help/html
137 |
138 | # Click-Once directory
139 | publish/
140 |
141 | # Publish Web Output
142 | *.[Pp]ublish.xml
143 | *.azurePubxml
144 | # TODO: Comment the next line if you want to checkin your web deploy settings
145 | # but database connection strings (with potential passwords) will be unencrypted
146 | #*.pubxml
147 | *.publishproj
148 |
149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
150 | # checkin your Azure Web App publish settings, but sensitive information contained
151 | # in these scripts will be unencrypted
152 | PublishScripts/
153 |
154 | # NuGet Packages
155 | *.nupkg
156 | # The packages folder can be ignored because of Package Restore
157 | **/packages/*
158 | # except build/, which is used as an MSBuild target.
159 | !**/packages/build/
160 | # Uncomment if necessary however generally it will be regenerated when needed
161 | #!**/packages/repositories.config
162 | # NuGet v3's project.json files produces more ignoreable files
163 | *.nuget.props
164 | *.nuget.targets
165 |
166 | # Microsoft Azure Build Output
167 | csx/
168 | *.build.csdef
169 |
170 | # Microsoft Azure Emulator
171 | ecf/
172 | rcf/
173 |
174 | # Windows Store app package directories and files
175 | AppPackages/
176 | BundleArtifacts/
177 | Package.StoreAssociation.xml
178 | _pkginfo.txt
179 |
180 | # Visual Studio cache files
181 | # files ending in .cache can be ignored
182 | *.[Cc]ache
183 | # but keep track of directories ending in .cache
184 | !*.[Cc]ache/
185 |
186 | # Others
187 | ClientBin/
188 | ~$*
189 | *~
190 | *.dbmdl
191 | *.dbproj.schemaview
192 | *.jfm
193 | *.pfx
194 | *.publishsettings
195 | node_modules/
196 | orleans.codegen.cs
197 |
198 | # Since there are multiple workflows, uncomment next line to ignore bower_components
199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
200 | #bower_components/
201 |
202 | # RIA/Silverlight projects
203 | Generated_Code/
204 |
205 | # Backup & report files from converting an old project file
206 | # to a newer Visual Studio version. Backup files are not needed,
207 | # because we have git ;-)
208 | _UpgradeReport_Files/
209 | Backup*/
210 | UpgradeLog*.XML
211 | UpgradeLog*.htm
212 |
213 | # SQL Server files
214 | *.mdf
215 | *.ldf
216 |
217 | # Business Intelligence projects
218 | *.rdl.data
219 | *.bim.layout
220 | *.bim_*.settings
221 |
222 | # Microsoft Fakes
223 | FakesAssemblies/
224 |
225 | # GhostDoc plugin setting file
226 | *.GhostDoc.xml
227 |
228 | # Node.js Tools for Visual Studio
229 | .ntvs_analysis.dat
230 |
231 | # Visual Studio 6 build log
232 | *.plg
233 |
234 | # Visual Studio 6 workspace options file
235 | *.opt
236 |
237 | # Visual Studio LightSwitch build output
238 | **/*.HTMLClient/GeneratedArtifacts
239 | **/*.DesktopClient/GeneratedArtifacts
240 | **/*.DesktopClient/ModelManifest.xml
241 | **/*.Server/GeneratedArtifacts
242 | **/*.Server/ModelManifest.xml
243 | _Pvt_Extensions
244 |
245 | # Paket dependency manager
246 | .paket/paket.exe
247 | paket-files/
248 |
249 | # FAKE - F# Make
250 | .fake/
251 |
252 | # JetBrains Rider
253 | .idea/
254 | *.sln.iml
255 |
256 | # CodeRush
257 | .cr/
258 |
259 | # Python Tools for Visual Studio (PTVS)
260 | __pycache__/
261 | *.pyc
--------------------------------------------------------------------------------
/PM3UniversalGUI.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27428.2015
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PM3UniversalGUI", "PM3UniversalGUI\PM3UniversalGUI.csproj", "{E1D25E6A-0D03-42C3-96C2-CDA41B5F3735}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {E1D25E6A-0D03-42C3-96C2-CDA41B5F3735}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {E1D25E6A-0D03-42C3-96C2-CDA41B5F3735}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {E1D25E6A-0D03-42C3-96C2-CDA41B5F3735}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {E1D25E6A-0D03-42C3-96C2-CDA41B5F3735}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {2A281ACB-ACD7-45BB-B904-F85EE12CB761}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/PM3UniversalGUI/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/PM3UniversalGUI/ConsoleStreamReader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using System.IO;
7 |
8 | namespace PM3UniversalGUI
9 | {
10 | public class ConsoleInputReadEventArgs : EventArgs
11 | {
12 | public ConsoleInputReadEventArgs(string input)
13 | {
14 | this.Input = input;
15 | }
16 |
17 | public string Input { get; private set; }
18 | }
19 |
20 | public interface IConsoleAutomator
21 | {
22 | StreamWriter StandardInput { get; }
23 |
24 | event EventHandler StandardInputRead;
25 | }
26 |
27 | public abstract class ConsoleAutomatorBase : IConsoleAutomator
28 | {
29 | protected readonly StringBuilder inputAccumulator = new StringBuilder();
30 |
31 | protected readonly byte[] buffer = new byte[256];
32 |
33 | protected volatile bool stopAutomation;
34 |
35 | public StreamWriter StandardInput { get; protected set; }
36 |
37 | protected StreamReader StandardOutput { get; set; }
38 |
39 | protected StreamReader StandardError { get; set; }
40 |
41 | public event EventHandler StandardInputRead;
42 |
43 | protected void BeginReadAsync()
44 | {
45 | if (!this.stopAutomation)
46 | {
47 | this.StandardOutput.BaseStream.BeginRead(this.buffer, 0, this.buffer.Length, this.ReadHappened, null);
48 | }
49 | }
50 |
51 | protected virtual void OnAutomationStopped()
52 | {
53 | this.stopAutomation = true;
54 | this.StandardOutput.DiscardBufferedData();
55 | }
56 |
57 | private void ReadHappened(IAsyncResult asyncResult)
58 | {
59 | var bytesRead = this.StandardOutput.BaseStream.EndRead(asyncResult);
60 | if (bytesRead == 0)
61 | {
62 | this.OnAutomationStopped();
63 | return;
64 | }
65 |
66 | var input = this.StandardOutput.CurrentEncoding.GetString(this.buffer, 0, bytesRead);
67 | this.inputAccumulator.Append(input);
68 |
69 | if (bytesRead < this.buffer.Length)
70 | {
71 | this.OnInputRead(this.inputAccumulator.ToString());
72 | }
73 |
74 | this.BeginReadAsync();
75 | }
76 |
77 | private void OnInputRead(string input)
78 | {
79 | var handler = this.StandardInputRead;
80 | if (handler == null)
81 | {
82 | return;
83 | }
84 |
85 | handler(this, new ConsoleInputReadEventArgs(input));
86 | this.inputAccumulator.Clear();
87 | }
88 | }
89 |
90 | public class ConsoleAutomator : ConsoleAutomatorBase, IConsoleAutomator
91 | {
92 | public ConsoleAutomator(StreamWriter standardInput, StreamReader standardOutput)
93 | {
94 | this.StandardInput = standardInput;
95 | this.StandardOutput = standardOutput;
96 | }
97 |
98 | public void StartCapture()
99 | {
100 | this.stopAutomation = false;
101 | this.BeginReadAsync();
102 | }
103 |
104 | public void StopCapture()
105 | {
106 | this.OnAutomationStopped();
107 | }
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/PM3UniversalGUI/MainForm.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace PM3UniversalGUI
2 | {
3 | partial class MainForm
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.components = new System.ComponentModel.Container();
32 | this.defaultToolTip = new System.Windows.Forms.ToolTip(this.components);
33 | this.btnRun = new System.Windows.Forms.Button();
34 | this.splitContainer1 = new System.Windows.Forms.SplitContainer();
35 | this.btnReloadCommands = new System.Windows.Forms.Button();
36 | this.PM3CommandsTree = new System.Windows.Forms.TreeView();
37 | this.COMPortBox = new System.Windows.Forms.ComboBox();
38 | this.splitContainer2 = new System.Windows.Forms.SplitContainer();
39 | this.CommandDescriptionTextBox = new System.Windows.Forms.RichTextBox();
40 | this.CommandParamsContainer = new System.Windows.Forms.FlowLayoutPanel();
41 | this.groupBox1 = new System.Windows.Forms.GroupBox();
42 | this.radioButton1 = new System.Windows.Forms.RadioButton();
43 | this.textBox1 = new System.Windows.Forms.TextBox();
44 | this.checkBox1 = new System.Windows.Forms.CheckBox();
45 | this.btnCopy = new System.Windows.Forms.Button();
46 | this.commandComboBox = new System.Windows.Forms.ComboBox();
47 | this.ConsoleTextBox = new System.Windows.Forms.RichTextBox();
48 | ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
49 | this.splitContainer1.Panel1.SuspendLayout();
50 | this.splitContainer1.SuspendLayout();
51 | ((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).BeginInit();
52 | this.splitContainer2.Panel1.SuspendLayout();
53 | this.splitContainer2.Panel2.SuspendLayout();
54 | this.splitContainer2.SuspendLayout();
55 | this.CommandParamsContainer.SuspendLayout();
56 | this.groupBox1.SuspendLayout();
57 | this.SuspendLayout();
58 | //
59 | // btnRun
60 | //
61 | this.btnRun.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
62 | this.btnRun.Location = new System.Drawing.Point(738, 2);
63 | this.btnRun.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
64 | this.btnRun.Name = "btnRun";
65 | this.btnRun.Size = new System.Drawing.Size(75, 28);
66 | this.btnRun.TabIndex = 17;
67 | this.btnRun.Text = "Run";
68 | this.defaultToolTip.SetToolTip(this.btnRun, "No COM Port selected");
69 | this.btnRun.UseVisualStyleBackColor = true;
70 | this.btnRun.Click += new System.EventHandler(this.btnRun_Click);
71 | //
72 | // splitContainer1
73 | //
74 | this.splitContainer1.Location = new System.Drawing.Point(24, 10);
75 | this.splitContainer1.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
76 | this.splitContainer1.Name = "splitContainer1";
77 | //
78 | // splitContainer1.Panel1
79 | //
80 | this.splitContainer1.Panel1.Controls.Add(this.btnReloadCommands);
81 | this.splitContainer1.Panel1.Controls.Add(this.PM3CommandsTree);
82 | this.splitContainer1.Panel1.Controls.Add(this.COMPortBox);
83 | this.splitContainer1.Size = new System.Drawing.Size(340, 584);
84 | this.splitContainer1.SplitterDistance = 307;
85 | this.splitContainer1.TabIndex = 11;
86 | //
87 | // btnReloadCommands
88 | //
89 | this.btnReloadCommands.Location = new System.Drawing.Point(135, 311);
90 | this.btnReloadCommands.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
91 | this.btnReloadCommands.Name = "btnReloadCommands";
92 | this.btnReloadCommands.Size = new System.Drawing.Size(130, 29);
93 | this.btnReloadCommands.TabIndex = 11;
94 | this.btnReloadCommands.Text = "Reload Commands";
95 | this.btnReloadCommands.UseVisualStyleBackColor = true;
96 | this.btnReloadCommands.Visible = false;
97 | this.btnReloadCommands.Click += new System.EventHandler(this.btnReloadCommands_Click);
98 | //
99 | // PM3CommandsTree
100 | //
101 | this.PM3CommandsTree.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
102 | | System.Windows.Forms.AnchorStyles.Left)
103 | | System.Windows.Forms.AnchorStyles.Right)));
104 | this.PM3CommandsTree.HideSelection = false;
105 | this.PM3CommandsTree.Location = new System.Drawing.Point(10, 34);
106 | this.PM3CommandsTree.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
107 | this.PM3CommandsTree.Name = "PM3CommandsTree";
108 | this.PM3CommandsTree.ShowNodeToolTips = true;
109 | this.PM3CommandsTree.Size = new System.Drawing.Size(286, 539);
110 | this.PM3CommandsTree.TabIndex = 4;
111 | this.PM3CommandsTree.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.PM3CommandsTree_AfterSelect);
112 | this.PM3CommandsTree.DoubleClick += new System.EventHandler(this.btnRun_Click);
113 | //
114 | // COMPortBox
115 | //
116 | this.COMPortBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
117 | | System.Windows.Forms.AnchorStyles.Right)));
118 | this.COMPortBox.FormattingEnabled = true;
119 | this.COMPortBox.Location = new System.Drawing.Point(10, 2);
120 | this.COMPortBox.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
121 | this.COMPortBox.Name = "COMPortBox";
122 | this.COMPortBox.Size = new System.Drawing.Size(286, 28);
123 | this.COMPortBox.TabIndex = 3;
124 | this.COMPortBox.Text = "Select COM Port...";
125 | this.COMPortBox.DropDown += new System.EventHandler(this.COMPortBox_DropDown);
126 | this.COMPortBox.SelectedIndexChanged += new System.EventHandler(this.COMPortBox_SelectedIndexChanged);
127 | //
128 | // splitContainer2
129 | //
130 | this.splitContainer2.Location = new System.Drawing.Point(385, 12);
131 | this.splitContainer2.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
132 | this.splitContainer2.Name = "splitContainer2";
133 | this.splitContainer2.Orientation = System.Windows.Forms.Orientation.Horizontal;
134 | //
135 | // splitContainer2.Panel1
136 | //
137 | this.splitContainer2.Panel1.AutoScroll = true;
138 | this.splitContainer2.Panel1.Controls.Add(this.CommandDescriptionTextBox);
139 | this.splitContainer2.Panel1.Controls.Add(this.CommandParamsContainer);
140 | //
141 | // splitContainer2.Panel2
142 | //
143 | this.splitContainer2.Panel2.Controls.Add(this.btnRun);
144 | this.splitContainer2.Panel2.Controls.Add(this.btnCopy);
145 | this.splitContainer2.Panel2.Controls.Add(this.commandComboBox);
146 | this.splitContainer2.Panel2.Controls.Add(this.ConsoleTextBox);
147 | this.splitContainer2.Size = new System.Drawing.Size(825, 582);
148 | this.splitContainer2.SplitterDistance = 259;
149 | this.splitContainer2.TabIndex = 16;
150 | //
151 | // CommandDescriptionTextBox
152 | //
153 | this.CommandDescriptionTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
154 | | System.Windows.Forms.AnchorStyles.Right)));
155 | this.CommandDescriptionTextBox.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(32)))), ((int)(((byte)(32)))));
156 | this.CommandDescriptionTextBox.Font = new System.Drawing.Font("Trebuchet MS", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
157 | this.CommandDescriptionTextBox.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224)))));
158 | this.CommandDescriptionTextBox.Location = new System.Drawing.Point(0, 10);
159 | this.CommandDescriptionTextBox.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
160 | this.CommandDescriptionTextBox.Name = "CommandDescriptionTextBox";
161 | this.CommandDescriptionTextBox.Size = new System.Drawing.Size(796, 193);
162 | this.CommandDescriptionTextBox.TabIndex = 10;
163 | this.CommandDescriptionTextBox.Text = "";
164 | //
165 | // CommandParamsContainer
166 | //
167 | this.CommandParamsContainer.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
168 | | System.Windows.Forms.AnchorStyles.Right)));
169 | this.CommandParamsContainer.AutoScroll = true;
170 | this.CommandParamsContainer.AutoScrollMinSize = new System.Drawing.Size(100, 100);
171 | this.CommandParamsContainer.AutoSize = true;
172 | this.CommandParamsContainer.BackColor = System.Drawing.SystemColors.ControlDark;
173 | this.CommandParamsContainer.Controls.Add(this.groupBox1);
174 | this.CommandParamsContainer.Location = new System.Drawing.Point(0, 209);
175 | this.CommandParamsContainer.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
176 | this.CommandParamsContainer.Name = "CommandParamsContainer";
177 | this.CommandParamsContainer.Size = new System.Drawing.Size(796, 200);
178 | this.CommandParamsContainer.TabIndex = 9;
179 | //
180 | // groupBox1
181 | //
182 | this.groupBox1.Controls.Add(this.radioButton1);
183 | this.groupBox1.Controls.Add(this.textBox1);
184 | this.groupBox1.Controls.Add(this.checkBox1);
185 | this.groupBox1.Location = new System.Drawing.Point(3, 2);
186 | this.groupBox1.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
187 | this.groupBox1.Name = "groupBox1";
188 | this.groupBox1.Padding = new System.Windows.Forms.Padding(3, 2, 3, 2);
189 | this.groupBox1.Size = new System.Drawing.Size(267, 161);
190 | this.groupBox1.TabIndex = 0;
191 | this.groupBox1.TabStop = false;
192 | this.groupBox1.Text = "groupBox1";
193 | this.groupBox1.Visible = false;
194 | //
195 | // radioButton1
196 | //
197 | this.radioButton1.AutoSize = true;
198 | this.radioButton1.Location = new System.Drawing.Point(16, 58);
199 | this.radioButton1.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
200 | this.radioButton1.Name = "radioButton1";
201 | this.radioButton1.Size = new System.Drawing.Size(126, 24);
202 | this.radioButton1.TabIndex = 3;
203 | this.radioButton1.TabStop = true;
204 | this.radioButton1.Text = "radioButton1";
205 | this.radioButton1.UseVisualStyleBackColor = true;
206 | //
207 | // textBox1
208 | //
209 | this.textBox1.Location = new System.Drawing.Point(97, 88);
210 | this.textBox1.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
211 | this.textBox1.Name = "textBox1";
212 | this.textBox1.Size = new System.Drawing.Size(100, 26);
213 | this.textBox1.TabIndex = 2;
214 | this.textBox1.Text = "text";
215 | this.textBox1.TextChanged += new System.EventHandler(this.textBox1_TextChanged);
216 | this.textBox1.Enter += new System.EventHandler(this.textBox1_Enter);
217 | //
218 | // checkBox1
219 | //
220 | this.checkBox1.AutoSize = true;
221 | this.checkBox1.Location = new System.Drawing.Point(16, 0);
222 | this.checkBox1.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
223 | this.checkBox1.Name = "checkBox1";
224 | this.checkBox1.Size = new System.Drawing.Size(113, 24);
225 | this.checkBox1.TabIndex = 0;
226 | this.checkBox1.Text = "checkBox1";
227 | this.checkBox1.UseVisualStyleBackColor = true;
228 | this.checkBox1.CheckedChanged += new System.EventHandler(this.checkBox1_CheckedChanged);
229 | //
230 | // btnCopy
231 | //
232 | this.btnCopy.Location = new System.Drawing.Point(8, 2);
233 | this.btnCopy.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
234 | this.btnCopy.Name = "btnCopy";
235 | this.btnCopy.Size = new System.Drawing.Size(75, 28);
236 | this.btnCopy.TabIndex = 16;
237 | this.btnCopy.Text = "Copy";
238 | this.btnCopy.UseVisualStyleBackColor = true;
239 | //
240 | // commandComboBox
241 | //
242 | this.commandComboBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
243 | | System.Windows.Forms.AnchorStyles.Right)));
244 | this.commandComboBox.FormattingEnabled = true;
245 | this.commandComboBox.Location = new System.Drawing.Point(89, 2);
246 | this.commandComboBox.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
247 | this.commandComboBox.Name = "commandComboBox";
248 | this.commandComboBox.Size = new System.Drawing.Size(643, 28);
249 | this.commandComboBox.TabIndex = 15;
250 | this.commandComboBox.Enter += new System.EventHandler(this.commandComboBox_Enter);
251 | this.commandComboBox.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.commandComboBox_KeyPress);
252 | //
253 | // ConsoleTextBox
254 | //
255 | this.ConsoleTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
256 | | System.Windows.Forms.AnchorStyles.Left)
257 | | System.Windows.Forms.AnchorStyles.Right)));
258 | this.ConsoleTextBox.BackColor = System.Drawing.Color.Black;
259 | this.ConsoleTextBox.Font = new System.Drawing.Font("Consolas", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
260 | this.ConsoleTextBox.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224)))));
261 | this.ConsoleTextBox.Location = new System.Drawing.Point(0, 36);
262 | this.ConsoleTextBox.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
263 | this.ConsoleTextBox.Name = "ConsoleTextBox";
264 | this.ConsoleTextBox.Size = new System.Drawing.Size(813, 274);
265 | this.ConsoleTextBox.TabIndex = 11;
266 | this.ConsoleTextBox.Text = "";
267 | //
268 | // MainForm
269 | //
270 | this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F);
271 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
272 | this.ClientSize = new System.Drawing.Size(1222, 650);
273 | this.Controls.Add(this.splitContainer2);
274 | this.Controls.Add(this.splitContainer1);
275 | this.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
276 | this.Name = "MainForm";
277 | this.Text = "PM3 Universal GUI";
278 | this.Load += new System.EventHandler(this.MainForm_Load);
279 | this.splitContainer1.Panel1.ResumeLayout(false);
280 | ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
281 | this.splitContainer1.ResumeLayout(false);
282 | this.splitContainer2.Panel1.ResumeLayout(false);
283 | this.splitContainer2.Panel1.PerformLayout();
284 | this.splitContainer2.Panel2.ResumeLayout(false);
285 | ((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).EndInit();
286 | this.splitContainer2.ResumeLayout(false);
287 | this.CommandParamsContainer.ResumeLayout(false);
288 | this.groupBox1.ResumeLayout(false);
289 | this.groupBox1.PerformLayout();
290 | this.ResumeLayout(false);
291 |
292 | }
293 |
294 | #endregion
295 | private System.Windows.Forms.ToolTip defaultToolTip;
296 | private System.Windows.Forms.SplitContainer splitContainer1;
297 | private System.Windows.Forms.TreeView PM3CommandsTree;
298 | private System.Windows.Forms.ComboBox COMPortBox;
299 | private System.Windows.Forms.Button btnReloadCommands;
300 | private System.Windows.Forms.SplitContainer splitContainer2;
301 | private System.Windows.Forms.RichTextBox CommandDescriptionTextBox;
302 | private System.Windows.Forms.FlowLayoutPanel CommandParamsContainer;
303 | private System.Windows.Forms.Button btnRun;
304 | private System.Windows.Forms.Button btnCopy;
305 | private System.Windows.Forms.ComboBox commandComboBox;
306 | private System.Windows.Forms.RichTextBox ConsoleTextBox;
307 | private System.Windows.Forms.GroupBox groupBox1;
308 | private System.Windows.Forms.CheckBox checkBox1;
309 | private System.Windows.Forms.TextBox textBox1;
310 | private System.Windows.Forms.RadioButton radioButton1;
311 | }
312 | }
313 |
314 |
--------------------------------------------------------------------------------
/PM3UniversalGUI/MainForm.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Data;
5 | using System.Drawing;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 | using System.Windows.Forms;
10 | using System.IO.Ports;
11 | using System.Diagnostics;
12 |
13 | namespace PM3UniversalGUI
14 | {
15 | public partial class MainForm : Form
16 | {
17 |
18 | public MainForm()
19 | {
20 | InitializeComponent();
21 |
22 | splitContainer2.Parent = splitContainer1.Panel2;
23 | splitContainer1.Dock = DockStyle.Fill;
24 | splitContainer2.Dock = DockStyle.Fill;
25 | splitContainer1.SplitterDistance = splitContainer1.Width * 30 / 100;
26 |
27 | btnReloadCommands_Click(this, null);
28 | }
29 |
30 | public void ProcessConsoleOutput(string Output)
31 | {
32 | string[] Lines = Output.Split(new string[] { "\r\n" }, StringSplitOptions.None);
33 |
34 | foreach (string s in Lines)
35 | {
36 | int i = s.IndexOf("proxmark3>");
37 | if (i < 0) i = s.IndexOf("pm3 -->");
38 |
39 | if (i < 0)
40 | AppendText(ConsoleTextBox, ConsoleTextBox.ForeColor, s + "\r\n");
41 | else AppendText(ConsoleTextBox, Color.Goldenrod, s + "\r\n", true);
42 | }
43 | }
44 |
45 | public void OnAutomatorStandardInputRead(object sender, ConsoleInputReadEventArgs e)
46 | {
47 | if (String.IsNullOrEmpty(e.Input)) return;
48 |
49 | ProcessConsoleOutput(e.Input);
50 | }
51 |
52 | public void OnDataReceivedEventHandler(object sender, DataReceivedEventArgs e)
53 | {
54 | if (String.IsNullOrEmpty(e.Data)) return;
55 |
56 | ProcessConsoleOutput(e.Data + "\r\n");
57 | }
58 |
59 |
60 | private void COMPortBox_SelectedIndexChanged(object sender, EventArgs e)
61 | {
62 | if (COMPortBox.SelectedItem == null)
63 | {
64 | Program.PM3.PortName = "";
65 | Program.PM3.StopClient();
66 | return;
67 | }
68 |
69 | if (Program.PM3.PortName == COMPortBox.SelectedItem.ToString()) return;
70 |
71 | if (Program.PM3.IsRunning())
72 | {
73 | Program.PM3.StopClient();
74 | }
75 |
76 | Program.PM3.InitClient(COMPortBox.SelectedItem.ToString());
77 | Program.PM3.ClientProcess.OutputDataReceived += OnDataReceivedEventHandler;
78 | Program.PM3.StartClient();
79 |
80 | defaultToolTip.SetToolTip(btnRun, null);
81 |
82 | }
83 |
84 | private void COMPortBox_DropDown(object sender, EventArgs e)
85 | {
86 | COMPortBox.Items.Clear();
87 | COMPortBox.Items.AddRange(SerialPort.GetPortNames());
88 |
89 | }
90 |
91 | private void MainForm_Load(object sender, EventArgs e)
92 | {
93 |
94 |
95 | }
96 |
97 | private void AppendText(RichTextBox TargetTextBox, Color TextColor, string msg, bool Bold = false, bool Italic = false)
98 | {
99 | TargetTextBox.Invoke(new EventHandler(delegate
100 | {
101 | TargetTextBox.SelectedText = string.Empty;
102 | //ConsoleTextBox.SelectionFont = new Font(_displaywindow.SelectionFont, FontStyle.Bold);
103 | TargetTextBox.SelectionColor = TextColor;
104 | FontStyle fs = new FontStyle();
105 | if (Bold) fs = fs | FontStyle.Bold;
106 | if (Italic) fs = fs | FontStyle.Italic;
107 | TargetTextBox.SelectionFont = new Font(TargetTextBox.Font, fs);
108 | TargetTextBox.AppendText(msg);
109 | TargetTextBox.ScrollToCaret();
110 | }));
111 | }
112 |
113 | private void btnReloadCommands_Click(object sender, EventArgs e)
114 | {
115 | Program.PM3.LoadCommands(3);
116 | if (Program.PM3.Commands.Count == 0) Program.PM3.LoadCommands(4);
117 |
118 | PM3CommandsTree.Nodes.Clear();
119 | PM3CommandsTree.Nodes.Add("PM3");
120 |
121 | for (int cIndex = 0; cIndex < Program.PM3.Commands.Count; cIndex++)
122 | {
123 | PM3Command c = Program.PM3.Commands[cIndex];
124 | string[] path = c.Command.Split(' ');
125 | TreeNode n = PM3CommandsTree.Nodes[0];
126 |
127 | for (int i = 0; i < path.Count(); i++)
128 | {
129 | if (n.Nodes.ContainsKey(path[i])) n = n.Nodes[path[i]];
130 | else
131 | {
132 | TreeNode t = new TreeNode(path[i]);
133 | t.Tag = cIndex;
134 | t.Name = t.Text;
135 | t.Text += " (" + c.Description + ")";
136 | if (i == path.Count() - 1)
137 | t.ToolTipText = c.Description;
138 | n.Nodes.Add(t);
139 | n = t;
140 | }
141 | }
142 | }
143 |
144 | PM3CommandsTree.Nodes[0].Expand();
145 | }
146 |
147 | //enlarge the control width to fit its contents
148 | private void ResizeFit(Control c, int MaxWidthLimit)
149 | {
150 | int MaxWidth = TextRenderer.MeasureText(c.Text, c.Font).Width;
151 |
152 | if (c.GetType() == typeof(ComboBox))
153 | {
154 | foreach (Object o in ((ComboBox)c).Items)
155 | {
156 | MaxWidth = Math.Max(MaxWidth, TextRenderer.MeasureText(o.ToString(), c.Font).Width);
157 | }
158 | }
159 |
160 | foreach (Control child in c.Controls)
161 | {
162 | int PositionAdjustment = child.Left;
163 |
164 | if (c.GetType() == typeof(FlowLayoutPanel)) PositionAdjustment = 0;
165 |
166 | ResizeFit(child, MaxWidthLimit - 5 - PositionAdjustment);
167 | MaxWidth = Math.Max(MaxWidth, child.Width + PositionAdjustment);
168 | }
169 |
170 | if (c.GetType() == typeof(FlowLayoutPanel)) return;
171 |
172 | c.Width = Math.Min(MaxWidth, MaxWidthLimit) + 20;
173 |
174 | if (c.GetType() == typeof(TextBox))
175 | if (c.Width < 100) c.Width = 100;
176 | }
177 |
178 |
179 | private void PM3CommandsTree_AfterSelect(object sender, TreeViewEventArgs e)
180 | {
181 | if (e == null || e.Node.Tag == null) return;
182 | int ItemIndex = (int)e.Node.Tag; //156
183 | if (ItemIndex < 0 || ItemIndex >= Program.PM3.Commands.Count) return;
184 | PM3Command cmd = Program.PM3.Commands[ItemIndex];
185 |
186 | commandComboBox.Text = cmd.Command;
187 |
188 | if (cmd.DescriptionFull == null && cmd.Params.Count == 0 && cmd.Usage == null)
189 | {
190 | if (Program.PM3.Version >= 4)
191 | {
192 | if (!EnsurePM3isRunning()) return;
193 |
194 | Program.PM3.ClientProcessConsoleAutomator.StandardInputRead -= OnAutomatorStandardInputRead;
195 | /*
196 | Program.PM3.ClientProcess.OutputDataReceived -= OnDataReceivedEventHandler;
197 | Program.PM3.ClientProcess.CancelOutputRead();
198 | */
199 | }
200 |
201 | Program.PM3.ExtractCommandParams(cmd);
202 |
203 | if (Program.PM3.Version >= 4)
204 | {
205 | if (Program.PM3.IsRunning())
206 | {
207 | Program.PM3.ClientProcessConsoleAutomator.StandardInputRead += OnAutomatorStandardInputRead;
208 | /*
209 | Program.PM3.ClientProcess.OutputDataReceived += OnDataReceivedEventHandler;
210 | Program.PM3.ClientProcess.BeginOutputReadLine();
211 | */
212 | }
213 |
214 | if (!EnsurePM3isRunning()) return;
215 | }
216 | }
217 |
218 | CommandParamsContainer.Controls.Clear();
219 |
220 | CommandDescriptionTextBox.Text = "";
221 | if (cmd.DescriptionFull != null) AppendText(CommandDescriptionTextBox, Color.FromArgb(255, 224, 224, 224), cmd.DescriptionFull + "\r\n", false, true);
222 | if (cmd.Usage != null) AppendText(CommandDescriptionTextBox, Color.FromArgb(255, 192, 192, 255), cmd.Usage + "\r\n", true);
223 | if (cmd.Options != null) AppendText(CommandDescriptionTextBox, Color.FromArgb(255, 128, 128, 255), cmd.Options + "\r\n");
224 | if (cmd.Examples != null) AppendText(CommandDescriptionTextBox, Color.FromArgb(255, 255, 128, 0), cmd.Examples + "\r\n", false, true);
225 | CommandDescriptionTextBox.SelectionStart = 0;
226 | CommandDescriptionTextBox.SelectionLength = 0;
227 | CommandDescriptionTextBox.ScrollToCaret();
228 |
229 | Control PreviousControl = null;
230 | for (int i = 0; i < cmd.Params.Count; i++)
231 | {
232 | PM3CommandParam p = cmd.Params[i];
233 |
234 | Control Container = CommandParamsContainer; //by default adding new controls directly to the container. Otherwise - inside the checkbox or radio button
235 | Control c = null;
236 |
237 | //if the option should be placed into a group box
238 | if (p.GroupWithNext || (i > 0 && (cmd.Params[i - 1].GroupWithNext)))
239 | {
240 | GroupBox GroupContainer = null;
241 | int cTop = 0;
242 | //this is the first option in a group -> create a groupbox container
243 | if (i == 0 || (i > 0 && !(cmd.Params[i - 1].GroupWithNext)))
244 | {
245 | GroupContainer = new System.Windows.Forms.GroupBox();
246 | CommandParamsContainer.Controls.Add(GroupContainer);
247 | if (p.IsOptional)
248 | {
249 | CheckBox EnableGroup = new System.Windows.Forms.CheckBox();
250 | EnableGroup.Width = EnableGroup.Height;
251 | EnableGroup.CheckedChanged += textBox1_TextChanged;
252 | GroupContainer.Controls.Add(EnableGroup);
253 | }
254 | }
255 | else //continue adding options to the group box created before
256 | {
257 | Control PreviousRow = PreviousControl;
258 | if (PreviousRow.GetType() != typeof(RadioButton)
259 | && PreviousRow.GetType() != typeof(CheckBox)
260 | && PreviousRow.GetType() != typeof(Panel))
261 | PreviousRow = PreviousControl.Parent;
262 |
263 | Control PreviousGroupBox = PreviousRow.Parent;
264 | if (PreviousGroupBox.GetType() != typeof(GroupBox)) PreviousGroupBox = PreviousGroupBox.Parent;
265 |
266 | GroupContainer = (GroupBox)PreviousGroupBox;
267 | cTop = PreviousRow.Top + PreviousRow.Height;
268 | }
269 |
270 | if (p.OrWithNext || (i > 0 && (cmd.Params[i - 1].OrWithNext))) // [x|y]
271 | {
272 | Container = new System.Windows.Forms.RadioButton();
273 | }
274 | else //[x ]
275 | {
276 | Container = new System.Windows.Forms.Panel();
277 | Container.Height = TextRenderer.MeasureText("^_|", Container.Font).Height * 2;
278 | }
279 | Container.Top = cTop; // + Container.Height / 4;
280 | if (cTop == 0) Container.Top += Container.Height;
281 | Container.Width = GroupContainer.Width * 9 / 10;
282 | Container.Left = GroupContainer.Width * 1 / 20;
283 | GroupContainer.Controls.Add(Container);
284 | GroupContainer.Height = Container.Top + Container.Height + Container.Height / 4;
285 | }
286 |
287 |
288 |
289 | if (p.IsOptional)
290 | {
291 | if ((Container.GetType() == typeof(GroupBox))
292 | || (Container.GetType() == typeof(FlowLayoutPanel)))
293 | {
294 | Control CheckBoxContainer = new System.Windows.Forms.CheckBox();
295 | Container.Controls.Add(CheckBoxContainer);
296 | Container = CheckBoxContainer;
297 | }
298 | }
299 |
300 | if ((
301 | (p.ParamType == PM3CommandParam.EParamType.Fixed)
302 | ||
303 | (p.ParamType == PM3CommandParam.EParamType.Flag && p.GroupWithNext)
304 | )
305 | &&
306 | (
307 | (Container.GetType() == typeof(GroupBox))
308 | ||
309 | (Container.GetType() == typeof(FlowLayoutPanel))
310 | ||
311 | (Container.GetType() == typeof(Panel))
312 | ))
313 | {
314 | c = new System.Windows.Forms.Label();
315 | ((Label)c).TextAlign = ContentAlignment.MiddleLeft;
316 | ((Label)c).AutoEllipsis = true;
317 | Container.Controls.Add(c);
318 | }
319 | else
320 | if (p.AllowedValues.Count > 0)
321 | {
322 | c = new System.Windows.Forms.ComboBox();
323 | c.Enter += textBox1_Enter;
324 | Container.Controls.Add(c);
325 | }
326 | else
327 | if ((p.ParamType == PM3CommandParam.EParamType.Value) || (p.Description == null))
328 | {
329 | c = new System.Windows.Forms.TextBox();
330 | c.Enter += textBox1_Enter;
331 | Container.Controls.Add(c);
332 | }
333 | else
334 | if ((Container.GetType() != typeof(GroupBox))
335 | && (Container.GetType() != typeof(FlowLayoutPanel)))
336 | {
337 | c = Container;
338 | c.Enter += textBox1_Enter;
339 | }
340 |
341 | if (Container.GetType() == typeof(RadioButton) || Container.GetType() == typeof(CheckBox))
342 | if (c != Container)
343 | {
344 | c.Left += c.Height;
345 | c.Width = Container.Width - c.Left;
346 |
347 | Container.Enter += textBox1_Enter;
348 | if (Container.GetType() == typeof(RadioButton))
349 | ((RadioButton)Container).CheckedChanged += textBox1_TextChanged;
350 | if (Container.GetType() == typeof(CheckBox))
351 | ((CheckBox)Container).CheckedChanged += textBox1_TextChanged;
352 | }
353 |
354 | c.Text = p.Name;
355 | if (p.Description != null) c.Text += " (" + p.Description + ")";
356 | defaultToolTip.SetToolTip(c, c.Text);
357 | c.Tag = i;
358 | Container.Tag = i;
359 |
360 | foreach (PM3CommandParamAllowedValue av in p.AllowedValues)
361 | {
362 | if (c.GetType() == typeof(ComboBox))
363 | ((ComboBox)c).Items.Add(av);
364 | }
365 |
366 |
367 | PreviousControl = c;
368 |
369 | if (c.GetType() == typeof(RadioButton))
370 | ((RadioButton)c).CheckedChanged += textBox1_TextChanged;
371 | if (c.GetType() == typeof(CheckBox))
372 | ((CheckBox)c).CheckedChanged += textBox1_TextChanged;
373 | if (c.GetType() == typeof(TextBox) || c.GetType() == typeof(ComboBox))
374 | c.TextChanged += textBox1_TextChanged;
375 |
376 | }
377 |
378 | ResizeFit(CommandParamsContainer, 300);
379 |
380 | }
381 |
382 | private bool EnsurePM3isRunning()
383 | {
384 | if (!Program.PM3.IsRunning())
385 | {
386 | COMPortBox.Focus();
387 |
388 | if (COMPortBox.Items.Count == 0)
389 | {
390 | COMPortBox_DropDown(null, null);
391 | }
392 |
393 | if (COMPortBox.Items.Count == 1)
394 | {
395 | if (COMPortBox.SelectedIndex == 0) COMPortBox.SelectedIndex = -1;
396 | COMPortBox.SelectedIndex = 0;
397 | }
398 | }
399 |
400 | return Program.PM3.IsRunning();
401 | }
402 |
403 | private void btnRun_Click(object sender, EventArgs e)
404 | {
405 | if (!EnsurePM3isRunning()) return;
406 |
407 | Program.PM3.ClientProcess.StandardInput.WriteLine(commandComboBox.Text);
408 | if (commandComboBox.Items.IndexOf(commandComboBox.Text) < 0)
409 | commandComboBox.Items.Add(commandComboBox.Text);
410 | }
411 |
412 |
413 | private void button1_Click(object sender, EventArgs e)
414 | {
415 |
416 | }
417 |
418 | private void commandComboBox_Enter(object sender, EventArgs e)
419 | {
420 |
421 | }
422 |
423 | private void checkBox1_CheckedChanged(object sender, EventArgs e)
424 | {
425 |
426 | }
427 |
428 | // In case a control (e.g. textbox) is clicked, we want to make sure the checkboxes/radiobuttons related to it and the group it is in are selected as well
429 | private void SelectLineage(Control c)
430 | {
431 | if (c.GetType() == typeof(FlowLayoutPanel)) return;
432 |
433 | if (c.GetType() == typeof(TextBox))
434 | {
435 | if (((TextBox)c).SelectionLength == 0)
436 | ((TextBox)c).SelectAll();
437 | }
438 |
439 | if (c.Parent.GetType() == typeof(CheckBox))
440 | {
441 | ((CheckBox)(c.Parent)).Checked = true;
442 | }
443 |
444 | if (c.Parent.GetType() == typeof(RadioButton))
445 | {
446 | ((RadioButton)(c.Parent)).Checked = true;
447 | }
448 |
449 | if (c.GetType() == typeof(GroupBox))
450 | {
451 | Control c2 = c.Controls[0];
452 | if (c2.GetType() == typeof(CheckBox))
453 | {
454 | if (c2.Top == 0)
455 | ((CheckBox)c2).Checked = true;
456 | }
457 | }
458 |
459 | SelectLineage(c.Parent);
460 | }
461 |
462 | private string GenerateCommand(Control c, PM3Command cmd)
463 | {
464 | string Result = "";
465 |
466 | foreach (Control child in c.Controls)
467 | {
468 | if (child.GetType() == typeof(GroupBox))
469 | {
470 | Control c2 = child.Controls[0];
471 | if (c2.GetType() == typeof(CheckBox))
472 | {
473 | if (c2.Top == 0)
474 | if (!((CheckBox)c2).Checked) continue;
475 | }
476 | }
477 |
478 | if (child.GetType() == typeof(CheckBox))
479 | if (!((CheckBox)child).Checked) continue;
480 |
481 | if (child.GetType() == typeof(RadioButton))
482 | if (!((RadioButton)child).Checked) continue;
483 |
484 | Result += GenerateCommand(child, cmd);
485 | }
486 |
487 | if (c.Controls.Count > 0) return Result;
488 |
489 | if (c.GetType() == typeof(CheckBox))
490 | if (!((CheckBox)c).Checked) return Result;
491 |
492 | if (c.GetType() == typeof(RadioButton))
493 | if (!((RadioButton)c).Checked) return Result;
494 |
495 | if (c.Tag != null)
496 | {
497 | PM3CommandParam p = cmd.Params[(int)c.Tag];
498 |
499 | if (c.GetType() == typeof(TextBox))
500 | {
501 | Result += " " + c.Text;
502 | } else
503 | if (c.GetType() == typeof(ComboBox))
504 | {
505 | if (((ComboBox)c).SelectedItem != null)
506 | Result += " " + ((PM3CommandParamAllowedValue)((ComboBox)c).SelectedItem).Value;
507 | }
508 | else
509 | {
510 | Result += " " + p.Name;
511 | }
512 | }
513 | return Result;
514 | }
515 |
516 | private void textBox1_Click(object sender, EventArgs e)
517 | {
518 | Control c = (Control)sender;
519 |
520 | c.Click -= textBox1_Click;
521 |
522 | SelectLineage(c);
523 | }
524 |
525 | private void textBox1_Enter(object sender, EventArgs e)
526 | {
527 | Control c = (Control)sender;
528 |
529 | c.Click += textBox1_Click;
530 |
531 | SelectLineage(c);
532 |
533 | textBox1_TextChanged(sender, e);
534 | }
535 |
536 | private void textBox1_TextChanged(object sender, EventArgs e)
537 | {
538 | if (PM3CommandsTree.SelectedNode == null || PM3CommandsTree.SelectedNode.Tag == null) return;
539 | int ItemIndex = (int)PM3CommandsTree.SelectedNode.Tag; //156
540 | if (ItemIndex < 0 || ItemIndex >= Program.PM3.Commands.Count) return;
541 | PM3Command cmd = Program.PM3.Commands[ItemIndex];
542 |
543 | commandComboBox.Text = cmd.Command;
544 |
545 | commandComboBox.Text += GenerateCommand(CommandParamsContainer, cmd);
546 | }
547 |
548 | private void commandComboBox_KeyPress(object sender, KeyPressEventArgs e)
549 | {
550 | if (e.KeyChar == (char)13) btnRun_Click(sender, e);
551 | }
552 | }
553 | }
554 |
--------------------------------------------------------------------------------
/PM3UniversalGUI/MainForm.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 | 17, 17
122 |
123 |
--------------------------------------------------------------------------------
/PM3UniversalGUI/PM3Client.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 | using System.IO.Ports;
8 | using System.Diagnostics;
9 | using System.IO;
10 | using System.Runtime.InteropServices;
11 | using System.Configuration;
12 |
13 | namespace PM3UniversalGUI
14 | {
15 |
16 |
17 | class PM3CommandParamAllowedValue
18 | {
19 | public string Value, Description;
20 |
21 | public PM3CommandParamAllowedValue(string Value, string Description)
22 | {
23 | this.Value = Value;
24 | this.Description = Description;
25 | }
26 |
27 | public override string ToString()
28 | {
29 | string Result = Value;
30 |
31 | if (Description != null) Result += " - " + Description;
32 |
33 | return Result;
34 | }
35 | }
36 |
37 | class PM3CommandParam
38 | {
39 | public enum EParamType { Fixed, Flag, Value };
40 |
41 | public string Name, Description;
42 | public bool IsOptional = false; //e.g. [x]
43 | public bool OrWithNext = false; //e.g. [x|y]
44 | public bool GroupWithNext = false; //e.g. [x ]
45 | public EParamType ParamType = EParamType.Fixed;
46 |
47 | public List AllowedValues = new List();
48 | }
49 |
50 | class PM3Command
51 | {
52 | public string Command, Description, DescriptionFull;
53 | public string Usage, Options, Examples;
54 |
55 | public List Params = new List();
56 |
57 | public PM3Command(string Command, string Description)
58 | {
59 | this.Command = Command;
60 | this.Description = Description;
61 | }
62 |
63 | public void ParseUsage(string UsageString)
64 | {
65 | Params.Clear();
66 |
67 | bool TokenIsOptional = false;
68 | bool TokenIsValue = false;
69 | bool ReadingToken = false;
70 | bool FinishingToken = false;
71 | PM3CommandParam p = new PM3CommandParam();
72 |
73 | for (int i = 0; i < UsageString.Length; i++)
74 | {
75 | if (UsageString[i] == ' ' && !TokenIsValue && !TokenIsOptional) FinishingToken = true;
76 |
77 | if (UsageString[i] == '[' && !TokenIsValue)
78 | {
79 | FinishingToken = true;
80 | TokenIsOptional = true;
81 | }
82 |
83 | if (UsageString[i] == '<')
84 | {
85 | if ((ReadingToken && p.IsOptional) || p.ParamType == PM3CommandParam.EParamType.Fixed) p.GroupWithNext = true;
86 | FinishingToken = true;
87 | TokenIsValue = true;
88 | }
89 |
90 | if (UsageString[i] == '|' && !TokenIsValue)
91 | {
92 | p.OrWithNext = true;
93 | p.GroupWithNext = true;
94 | FinishingToken = true;
95 | }
96 |
97 | if (UsageString[i] == ']')
98 | {
99 | TokenIsOptional = false;
100 | FinishingToken = true;
101 | }
102 |
103 | if (UsageString[i] == '>')
104 | {
105 | TokenIsValue = false;
106 | FinishingToken = true;
107 | }
108 |
109 | if (FinishingToken)
110 | {
111 | if (ReadingToken && p.Name != null)
112 | {
113 | p.Name = p.Name.TrimEnd();
114 | if (p.Name != "") Params.Add(p);
115 | }
116 | ReadingToken = false;
117 | FinishingToken = false;
118 |
119 | continue;
120 | }
121 |
122 | if (!ReadingToken)
123 | {
124 | p = new PM3CommandParam();
125 |
126 | if (TokenIsValue) p.ParamType = PM3CommandParam.EParamType.Value;
127 |
128 | if (TokenIsOptional)
129 | {
130 | p.IsOptional = true;
131 | if (!TokenIsValue) p.ParamType = PM3CommandParam.EParamType.Flag;
132 | }
133 | else
134 | {
135 | if (!TokenIsValue) p.ParamType = PM3CommandParam.EParamType.Fixed;
136 | }
137 |
138 | ReadingToken = true;
139 | }
140 | if (ReadingToken)
141 | {
142 | if (p.Name != null || UsageString[i] != ' ') p.Name += UsageString[i];
143 | continue;
144 | }
145 | }
146 |
147 | if (ReadingToken && p.Name != null)
148 | {
149 | p.Name = p.Name.TrimEnd();
150 | if (p.Name != "") Params.Add(p);
151 | }
152 |
153 |
154 |
155 | foreach (PM3CommandParam pp in Params) //postprocessing to handle "" and "name (valueA/ValueB/valueC)" cases
156 | {
157 | string NameClean = pp.Name.Replace("w/o", "w\\o");
158 | string[] Values = StringUtils.SplitNearby(NameClean, new char[] { '|', '/' });
159 |
160 | if (Values != null)
161 | for (int i = 0; i < Values.Length; i++)
162 | {
163 | pp.AllowedValues.Add(new PM3CommandParamAllowedValue(Values[i], null));
164 | }
165 | }
166 | }
167 |
168 | public void ParseOptionDescription(string OptionDescription, string RelatedTo = null)
169 | {
170 | if (OptionDescription.IndexOf(' ') < 0) return;
171 | string RelatedToClean = RelatedTo;
172 |
173 | if (RelatedToClean != null)
174 | {
175 | RelatedToClean = StringUtils.ExtractBrackets(RelatedToClean, '[', ']');
176 | RelatedToClean = StringUtils.ExtractBrackets(RelatedToClean, '<', '>');
177 | if (RelatedToClean.IndexOf(':') > 0) RelatedToClean = RelatedToClean.Substring(0, RelatedToClean.IndexOf(':') - 1).Trim();
178 |
179 | if (RelatedTo.StartsWith("*"))
180 | {
181 | foreach (PM3CommandParam p in Params)
182 | if (p.Name[0] == '*')
183 | {
184 | RelatedToClean = p.Name;
185 | break;
186 | }
187 | }
188 | }
189 | string OptionName = OptionDescription.Trim();
190 |
191 | if (OptionName.StartsWith("[")) OptionName = StringUtils.ExtractBrackets(OptionName, '[', ']');
192 | else if (OptionName.StartsWith("<")) OptionName = StringUtils.ExtractBrackets(OptionName, '<', '>');
193 | else OptionName = OptionName.Substring(0, OptionDescription.IndexOfAny(new char[] { ' ', ':' }));
194 |
195 | for (int i = 0; i < Params.Count; i++)
196 | {
197 | PM3CommandParam p = Params[i];
198 |
199 | if (p.Name == OptionName && p.Description == null) //full match with expected parameter found -> whatever we see, it is less likely to be one of the allowed values for some other parameter
200 | {
201 | RelatedToClean = null;
202 | break;
203 | }
204 | }
205 | List ValueDescriptions = new List();
206 |
207 |
208 | //if there are too many '=' or '-' maybe it's not a description, but the list of allowed values instead
209 | if ((OptionDescription.Split(',').Length >= 2) &&
210 | (OptionDescription.Split(new string[] { " = " }, StringSplitOptions.RemoveEmptyEntries).Length >= 3
211 | || OptionDescription.Split(new string[] { " - " }, StringSplitOptions.RemoveEmptyEntries).Length >= 3
212 | || OptionDescription.Split(new string[] { " for " }, StringSplitOptions.RemoveEmptyEntries).Length >= 3)
213 | )
214 | {
215 | if (RelatedToClean == null) RelatedToClean = OptionName;
216 | string[] ValueDescriptionTmp = OptionDescription.Replace('=', '-').Replace(" for ", "-").Split(',');
217 |
218 | foreach(string s in ValueDescriptionTmp)
219 | {
220 | if (s.IndexOf('-') > 0)
221 | ValueDescriptions.Add(s.Split('-'));
222 | }
223 | } else
224 | if ((OptionDescription.Split('|').Length > 2))
225 | {
226 | if (RelatedToClean == null) RelatedToClean = OptionName;
227 | string[] ValueDescriptionTmp = StringUtils.SplitNearby(OptionDescription, new char[] { '|' });
228 |
229 | foreach (string s in ValueDescriptionTmp)
230 | {
231 | ValueDescriptions.Add(new string[] { s });
232 | }
233 | }
234 | else
235 | if (RelatedToClean != null && RelatedToClean != "")
236 | {
237 | int i1 = OptionDescription.IndexOf('=');
238 | int i2 = OptionDescription.IndexOf('-');
239 | if (i1 >=0 && (i2 <0 || i2 > i1))
240 | ValueDescriptions.Add(OptionDescription.Split('='));
241 | else
242 | ValueDescriptions.Add(OptionDescription.Split('-'));
243 | }
244 |
245 | int MatchingId = -1;
246 |
247 | //trying to find the exact match of description with parameter name mentioned in the usage
248 | if (MatchingId < 0)
249 | for (int i = 0; i < Params.Count; i++)
250 | {
251 | PM3CommandParam p = Params[i];
252 |
253 | if (p.Name == OptionName ||
254 | (RelatedToClean != null && p.Name == RelatedToClean)) //maybe it's not the parameter, but a description of allowed value of some parameter
255 | {
256 | MatchingId = i;
257 | break;
258 | }
259 | }
260 |
261 |
262 |
263 |
264 | //second chance by looking up partial match
265 | if (MatchingId < 0 && OptionName.IndexOf(' ') > 0)
266 | {
267 | string OptionNamePartial = OptionName.Substring(0, OptionName.IndexOf(' '));
268 |
269 | for (int i = 0; i < Params.Count; i++)
270 | {
271 | if (Params[i].Name == OptionNamePartial || (RelatedToClean != null && Params[i].Name == RelatedToClean))
272 | {
273 | MatchingId = i;
274 |
275 | string[] Values = StringUtils.SplitNearby(OptionName, new char[] { '|', '/' });
276 |
277 | if (Values != null)
278 | {
279 | for (int j = 0; j < Values.Length; j++)
280 | {
281 | ValueDescriptions.Add(new string[] { Values[j] });
282 | }
283 |
284 | if (RelatedToClean == null) RelatedToClean = OptionNamePartial;
285 | }
286 |
287 | break;
288 | }
289 | }
290 | }
291 |
292 |
293 | if (MatchingId >= 0)
294 | {
295 | /* to handle case:
296 | * usage: cmd p
297 | * options:
298 | * p : 1 = a
299 | * 2 = b
300 | * (should have been "", not "p"!!!)
301 | */
302 | if (ValueDescriptions.Count > 0)
303 | if (Params[MatchingId].ParamType != PM3CommandParam.EParamType.Value)
304 | {
305 | if (MatchingId + 1 < Params.Count)
306 | if (Params[MatchingId + 1].ParamType == PM3CommandParam.EParamType.Value)
307 | if (Params[MatchingId].Name[0] == Params[MatchingId + 1].Name[0])
308 | MatchingId++;
309 | }
310 |
311 | if (RelatedToClean != null && RelatedToClean != "" && ValueDescriptions.Count > 0)
312 | {
313 | foreach (string[] ValueDescription in ValueDescriptions)
314 | {
315 | string Value = ValueDescription[0].Trim();
316 |
317 | int i = Value.IndexOf(':');
318 |
319 | if (i > 0)
320 | {
321 | if (i < Value.Length - 1) Value = Value.Substring(Value.IndexOf(':') + 1).Trim();
322 | else Value = Value.Substring(0, i - 1);
323 | }
324 | Value = StringUtils.ExtractBrackets(Value, '\'', '\'');
325 | Value = StringUtils.ExtractBrackets(Value, '[', ']');
326 | Value = StringUtils.ExtractBrackets(Value, '<', '>');
327 | string Description = null;
328 | if (ValueDescription.Length > 1) Description = ValueDescription[1].Trim();
329 | PM3CommandParamAllowedValue v = new PM3CommandParamAllowedValue(Value, Description);
330 | if (Params[MatchingId].Name[0] == '*') v.Value = '*' + v.Value;
331 |
332 | Params[MatchingId].AllowedValues.Add(v);
333 | }
334 | }
335 | else
336 | {
337 | Params[MatchingId].Description = OptionDescription.Remove(0, OptionDescription.IndexOf(' ')).Trim();
338 |
339 | if (Params[MatchingId].Description.Length > 1)
340 | if (Params[MatchingId].Description[0] == '-')
341 | Params[MatchingId].Description = Params[MatchingId].Description.Substring(1).TrimStart();
342 |
343 | if (Params[MatchingId].Description.Length > 1)
344 | if (Params[MatchingId].Description[0] == ':')
345 | Params[MatchingId].Description = Params[MatchingId].Description.Substring(1).TrimStart();
346 |
347 | if (Params[MatchingId].Description.IndexOf("Optional", StringComparison.InvariantCultureIgnoreCase) > 0)
348 | {
349 | Params[MatchingId].IsOptional = true;
350 |
351 | if (Params[MatchingId].Description != null)
352 | if (Params[MatchingId].ParamType == PM3CommandParam.EParamType.Fixed)
353 | Params[MatchingId].ParamType = PM3CommandParam.EParamType.Flag;
354 | }
355 |
356 |
357 | }
358 | }
359 | }
360 | }
361 |
362 |
363 | class PM3Client
364 | {
365 | public ConsoleAutomator ClientProcessConsoleAutomator;
366 | public Process ClientProcess = new Process();
367 | public List Commands = new List();
368 | public string PM3FileName;
369 | public string PortName;
370 | public int Version = 3;
371 |
372 | private StringBuilder CMDBuffer = new StringBuilder();
373 | private ManualResetEvent EndOfOutputEvent = new ManualResetEvent(false);
374 |
375 | public PM3Client(string PM3FileName)
376 | {
377 | this.PM3FileName = PM3FileName;
378 | }
379 |
380 | public bool IsRunning()
381 | {
382 | try { Process.GetProcessById(ClientProcess.Id); }
383 | catch (InvalidOperationException) { return false; }
384 | catch (ArgumentException) { return false; }
385 | return true;
386 | }
387 |
388 | public void InitClient(string PortName)
389 | {
390 | this.PortName = PortName;
391 | ClientProcess.StartInfo.FileName = PM3FileName; // Specify exe name.
392 | ClientProcess.StartInfo.Arguments = PortName + " -f";
393 | ClientProcess.StartInfo.UseShellExecute = false;
394 | ClientProcess.StartInfo.RedirectStandardOutput = true;
395 | ClientProcess.StartInfo.RedirectStandardInput = true;
396 | ClientProcess.StartInfo.CreateNoWindow = true;
397 | }
398 |
399 |
400 | public void StartClient()
401 | {
402 | ClientProcess.Start();
403 |
404 | ClientProcessConsoleAutomator = new ConsoleAutomator(ClientProcess.StandardInput, ClientProcess.StandardOutput);
405 | ClientProcessConsoleAutomator.StartCapture();
406 |
407 | //ClientProcess.BeginOutputReadLine();
408 | }
409 |
410 | public void StopClient()
411 | {
412 | //ClientProcess.CancelOutputRead();
413 |
414 | ClientProcessConsoleAutomator.StopCapture();
415 | if (!ClientProcess.HasExited) ClientProcess.Kill();
416 | ClientProcess.Dispose();
417 |
418 | ClientProcess = new Process();
419 | }
420 |
421 |
422 | private void ProcessConsoleOutput (string Output)
423 | {
424 | CMDBuffer.Append(Output);
425 |
426 |
427 | if (Output.TrimEnd().EndsWith("proxmark3>") || Output.TrimEnd().EndsWith("pm3 -->"))
428 | EndOfOutputEvent.Set(); //in case command prompt returned terminate output reading
429 | }
430 |
431 |
432 |
433 | private void OnAutomatorStandardInputRead(object sender, ConsoleInputReadEventArgs e)
434 | {
435 | if (!String.IsNullOrEmpty(e.Input)) ProcessConsoleOutput(e.Input);
436 | else EndOfOutputEvent.Set();
437 | }
438 | private void OnDataReceivedEventHandler(object sender, DataReceivedEventArgs e)
439 | {
440 | if (!String.IsNullOrEmpty(e.Data)) ProcessConsoleOutput(e.Data + "\r\n");
441 | else EndOfOutputEvent.Set();
442 | }
443 |
444 | private string CaptureOutput(string cmd)
445 | {
446 | CMDBuffer.Clear();
447 | /*
448 | ClientProcess.OutputDataReceived += OnDataReceivedEventHandler;
449 | ClientProcess.BeginOutputReadLine();
450 | */
451 |
452 | ClientProcessConsoleAutomator.StandardInputRead += OnAutomatorStandardInputRead;
453 |
454 | ClientProcess.StandardInput.WriteLine(cmd);
455 |
456 | EndOfOutputEvent.Reset();
457 | EndOfOutputEvent.WaitOne(Int32.Parse(ConfigurationManager.AppSettings["ConsoleDumpTimeout"]));
458 |
459 | ClientProcessConsoleAutomator.StandardInputRead -= OnAutomatorStandardInputRead;
460 |
461 | /*
462 | ClientProcess.OutputDataReceived -= OnDataReceivedEventHandler;
463 | ClientProcess.CancelOutputRead();
464 | */
465 |
466 | return CMDBuffer.ToString();
467 | }
468 |
469 | //try to parse detailed help output of specific command
470 | public void ExtractCommandParams(PM3Command cmd)
471 | {
472 | string Output = "";
473 |
474 | if (Version < 4) //start a new process to fetch command parameters in PM3
475 | {
476 | Process TmpProcess = new Process();
477 | TmpProcess.StartInfo.FileName = PM3FileName;
478 | TmpProcess.StartInfo.Arguments = "-c \"" + cmd.Command + " h\"";
479 | TmpProcess.StartInfo.UseShellExecute = false;
480 | TmpProcess.StartInfo.RedirectStandardOutput = true;
481 | TmpProcess.StartInfo.CreateNoWindow = true;
482 |
483 | TmpProcess.Start();
484 |
485 | Task t = TmpProcess.StandardOutput.ReadToEndAsync();
486 | Task t2 = Task.WhenAny(t, Task.Delay(Int32.Parse(ConfigurationManager.AppSettings["ConsoleDumpTimeout"]))).GetAwaiter().GetResult();
487 | if (t2.Id == t.Id)
488 | {
489 | // task completed within timeout
490 | }
491 | else
492 | {
493 | try
494 | {
495 | TmpProcess.Kill();
496 | }
497 | catch (Exception e)
498 | {
499 |
500 | }
501 | }
502 | Output = t.Result;
503 | } else
504 | {
505 | Output = CaptureOutput(cmd.Command + " h"); //get help output for the command executed in already running client console
506 | }
507 |
508 | cmd.Usage = null;
509 | cmd.Examples = null;
510 | cmd.DescriptionFull = null;
511 | cmd.Options = null;
512 | bool OptionsBlockExpected = Output.IndexOf("Options:") > 0;
513 |
514 | using (StringReader sr = new StringReader(Output))
515 | {
516 | int Block = -1;
517 | string RelatedTo = null;
518 | int RelatedToIndent = 0;
519 | bool UsageParsed = false;
520 | string PrevDescription = null;
521 | string PrevRelatedTo = null;
522 | int PrevIndent = 0;
523 |
524 | string line;
525 | while ((line = sr.ReadLine()) != null)
526 | {
527 | if (line.IndexOf("[!]") == 0) continue;
528 | if (Block == -1 && line.TrimEnd().EndsWith("> " + cmd.Command + " h"))
529 | {
530 | Block = 3;
531 | continue;
532 | }
533 | if (line.TrimStart().IndexOf("Usage:", StringComparison.CurrentCultureIgnoreCase) == 0 ||
534 | line.TrimStart().IndexOf("Syntax:", StringComparison.CurrentCultureIgnoreCase) == 0)
535 | {
536 | Block = 0;
537 | }
538 |
539 | if (line.TrimStart().IndexOf("Options:", StringComparison.CurrentCultureIgnoreCase) == 0)
540 | {
541 | Block = 1;
542 | continue;
543 | }
544 |
545 | if (line.TrimStart().IndexOf("Example", StringComparison.CurrentCultureIgnoreCase) == 0)
546 | {
547 | Block = 2;
548 | }
549 |
550 | if (Block == 0) //usage
551 | {
552 | string UsageString = line.Replace("\t", "").Trim();
553 | foreach (string ReplaceKeyword in new string[] { "Usage:", "Syntax:" })
554 | {
555 | if (UsageString.IndexOf(ReplaceKeyword, StringComparison.CurrentCultureIgnoreCase) >= 0)
556 | UsageString = UsageString.Remove(UsageString.IndexOf(ReplaceKeyword, StringComparison.CurrentCultureIgnoreCase), ReplaceKeyword.Length);
557 | }
558 | if (UsageString.Length > 0)
559 | {
560 | cmd.Usage += UsageString + "\r\n";
561 | if (!OptionsBlockExpected) Block = 1; //if there is no explicit options block we do not expect Usage to be longer than 1 line
562 | }
563 | } else
564 | if (Block == 1) //options
565 | {
566 | cmd.Options += line + "\r\n";
567 | if (cmd.Usage != null && !UsageParsed)
568 | {
569 | string[] UsageParts = cmd.Usage.Trim().Replace("\r\n", " ").Split(new string[] { cmd.Command }, StringSplitOptions.RemoveEmptyEntries);
570 | string UsageString = null;
571 | if (UsageParts.Length > 0) UsageString = UsageParts[0].Trim(); else UsageString = cmd.Usage.Trim();
572 |
573 | cmd.ParseUsage(UsageString);
574 | UsageParsed = true;
575 | }
576 |
577 | string OptionDescription = line.Trim();
578 | int Indent = line.IndexOf(OptionDescription);
579 |
580 | if (Indent > PrevIndent && PrevDescription != null && PrevDescription != "" && RelatedTo == null)
581 | {
582 | RelatedTo = PrevDescription;
583 | RelatedToIndent = PrevIndent;
584 | }
585 |
586 | if (RelatedTo != null)
587 | {
588 | if (false//(line.IndexOf("\t\t\t") < 0)
589 | || (OptionDescription.Replace("\t", "") == "" || !line.StartsWith(" "))
590 | || (RelatedToIndent == Indent)
591 | || (Indent < PrevIndent))
592 | RelatedTo = null;
593 | }
594 |
595 | if (PrevRelatedTo == null && RelatedTo != null) //if we've missed the option in the first line
596 | {
597 | if (RelatedTo.Split('=').Length == 2 || RelatedTo.Split('-').Length == 2)
598 | cmd.ParseOptionDescription(RelatedTo, RelatedTo);
599 | }
600 |
601 | cmd.ParseOptionDescription(OptionDescription, RelatedTo);
602 |
603 | if (OptionDescription.Length > 1)
604 | if ((OptionDescription.IndexOf('*') == 0)
605 | || (OptionDescription.IndexOf(':') == OptionDescription.Length - 1)
606 | || (OptionDescription.IndexOf(" see ") > 0))
607 | {
608 | RelatedTo = OptionDescription;
609 | RelatedToIndent = Indent;
610 | }
611 |
612 | PrevDescription = OptionDescription;
613 | PrevIndent = Indent;
614 | PrevRelatedTo = RelatedTo;
615 | } else
616 | if (Block == 2) //examples
617 | {
618 | cmd.Examples += line.Trim() + "\r\n";
619 | }
620 | else
621 | if (Block == 3) //description full
622 | {
623 | cmd.DescriptionFull += line.Trim() + "\r\n";
624 | }
625 | }
626 |
627 | if (!UsageParsed && cmd.Usage != null)
628 | {
629 | string UsageString2 = cmd.Usage.Trim().Replace("\r\n", " ").Split(new string[] { cmd.Command }, StringSplitOptions.RemoveEmptyEntries)[0].Trim();
630 | cmd.ParseUsage(UsageString2);
631 |
632 | UsageParsed = true;
633 | }
634 | }
635 |
636 | //remove most likely wrongly detected fixed keywords in the beginning of the parameters block
637 | for (int i = 0; i < cmd.Params.Count; i++)
638 | {
639 | if (cmd.Params[i].ParamType != PM3CommandParam.EParamType.Fixed) break;
640 | if (cmd.Params[i].Description != null) break; //highly likely that's a real one
641 | cmd.Params.RemoveAt(i);
642 | i--;
643 | }
644 | //remove most likely wrongly detected fixed keywords in the end of the parameters block
645 | for (int i = cmd.Params.Count-1; i >= 0; i--)
646 | {
647 | if (cmd.Params[i].ParamType != PM3CommandParam.EParamType.Fixed) break;
648 | cmd.Params.RemoveAt(i);
649 | }
650 | }
651 |
652 |
653 | //load the commands list with basic descriptions
654 | public async void LoadCommands(int version = 3)
655 | {
656 | if (!File.Exists(PM3FileName))
657 | {
658 | throw new System.ArgumentException("Cannot find PM3 console client \"" + PM3FileName + "\". Please check that this file exists or provide an alternative location in PM3UniversalGUI.exe.config PM3ClientFilename setting.", "PM3ClientFilename");
659 | }
660 |
661 | Version = version;
662 | Process TmpProcess = new Process();
663 | TmpProcess.StartInfo.FileName = PM3FileName; // Specify exe name.
664 | if (version >= 4) { TmpProcess.StartInfo.Arguments = "-t"; } else TmpProcess.StartInfo.Arguments = "-h";
665 | TmpProcess.StartInfo.UseShellExecute = false;
666 | TmpProcess.StartInfo.RedirectStandardOutput = true;
667 |
668 |
669 | TmpProcess.Start();
670 |
671 |
672 | string CommandsList = TmpProcess.StandardOutput.ReadToEnd();
673 |
674 | Commands.Clear();
675 |
676 | using (StringReader sr = new StringReader(CommandsList))
677 | {
678 | bool StartParse = false;
679 |
680 | string line;
681 | while ((line = sr.ReadLine()) != null)
682 | {
683 | if (line.IndexOf("command") == 0) StartParse = true;
684 |
685 | if (!StartParse) continue;
686 |
687 | if (line.IndexOf("command") == 0 || line.IndexOf("-------") >= 0)
688 | {
689 | continue;
690 | }
691 |
692 | if (line.IndexOf("###") == 0)
693 | {
694 | Commands.Add(new PM3Command(line.Replace("###", "").Trim(), ""));
695 | }
696 | else if (line.IndexOf('|') > 0)
697 | {
698 | PM3Command cmd = new PM3Command((line.Split('|')[0]).Trim(), (line.Split('|')[2]).Trim());
699 |
700 | Commands.Add(cmd);
701 | }
702 | else if (line.IndexOf('{') > 0 && line.IndexOf('}') > 0)
703 | {
704 | Commands.Last().Description = line.Substring(line.IndexOf('{') + 1, line.IndexOf('}') - line.IndexOf('{') - 2).Trim();
705 | }
706 | }
707 | }
708 |
709 | }
710 | }
711 | }
712 |
--------------------------------------------------------------------------------
/PM3UniversalGUI/PM3UniversalGUI.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {E1D25E6A-0D03-42C3-96C2-CDA41B5F3735}
8 | WinExe
9 | PM3UniversalGUI
10 | PM3UniversalGUI
11 | v4.6.1
12 | 512
13 | true
14 | false
15 | publish\
16 | true
17 | Disk
18 | false
19 | Foreground
20 | 7
21 | Days
22 | false
23 | false
24 | true
25 | 0
26 | 1.1.0.%2a
27 | false
28 | true
29 |
30 |
31 | AnyCPU
32 | true
33 | full
34 | false
35 | bin\Debug\
36 | DEBUG;TRACE
37 | prompt
38 | 4
39 |
40 |
41 | AnyCPU
42 | pdbonly
43 | true
44 | bin\Release\
45 | TRACE
46 | prompt
47 | 4
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | Form
68 |
69 |
70 | MainForm.cs
71 |
72 |
73 |
74 |
75 |
76 |
77 | MainForm.cs
78 |
79 |
80 | ResXFileCodeGenerator
81 | Resources.Designer.cs
82 | Designer
83 |
84 |
85 | True
86 | Resources.resx
87 |
88 |
89 | SettingsSingleFileGenerator
90 | Settings.Designer.cs
91 |
92 |
93 | True
94 | Settings.settings
95 | True
96 |
97 |
98 |
99 |
100 | Designer
101 |
102 |
103 |
104 |
105 | False
106 | Microsoft .NET Framework 4.6.1 %28x86 and x64%29
107 | true
108 |
109 |
110 | False
111 | .NET Framework 3.5 SP1
112 | false
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/PM3UniversalGUI/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using System.Windows.Forms;
6 | using System.IO.Ports;
7 | using System.Configuration;
8 |
9 | namespace PM3UniversalGUI
10 | {
11 |
12 |
13 |
14 | static class Program
15 | {
16 | //@"C:\Dist\pm3-bin-v3-official\win32\proxmark3.exe";
17 | //@"C:\Dist\pm3-bin-v3_20180711\win32\proxmark3.exe"
18 | public static PM3Client PM3 = new PM3Client(ConfigurationManager.AppSettings["PM3ClientFilename"]);
19 | ///
20 | /// The main entry point for the application.
21 | ///
22 | [STAThread]
23 | static void Main()
24 | {
25 | Application.EnableVisualStyles();
26 | Application.SetCompatibleTextRenderingDefault(false);
27 | Application.Run(new MainForm());
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/PM3UniversalGUI/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("PM3 Universal GUI")]
9 | [assembly: AssemblyDescription("Automated GUI proxy for proxmark3.exe")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("https://github.com/burma69/PM3UniversalGUI/")]
12 | [assembly: AssemblyProduct("PM3UniversalGUI")]
13 | [assembly: AssemblyCopyright("Copyright © 2018, https://github.com/burma69/PM3UniversalGUI/")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("e1d25e6a-0d03-42c3-96c2-cda41b5f3735")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.1.4.0")]
36 | [assembly: AssemblyFileVersion("1.1.4.0")]
37 |
--------------------------------------------------------------------------------
/PM3UniversalGUI/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace PM3UniversalGUI.Properties
12 | {
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources
26 | {
27 |
28 | private static global::System.Resources.ResourceManager resourceMan;
29 |
30 | private static global::System.Globalization.CultureInfo resourceCulture;
31 |
32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
33 | internal Resources()
34 | {
35 | }
36 |
37 | ///
38 | /// Returns the cached ResourceManager instance used by this class.
39 | ///
40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
41 | internal static global::System.Resources.ResourceManager ResourceManager
42 | {
43 | get
44 | {
45 | if ((resourceMan == null))
46 | {
47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PM3UniversalGUI.Properties.Resources", typeof(Resources).Assembly);
48 | resourceMan = temp;
49 | }
50 | return resourceMan;
51 | }
52 | }
53 |
54 | ///
55 | /// Overrides the current thread's CurrentUICulture property for all
56 | /// resource lookups using this strongly typed resource class.
57 | ///
58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
59 | internal static global::System.Globalization.CultureInfo Culture
60 | {
61 | get
62 | {
63 | return resourceCulture;
64 | }
65 | set
66 | {
67 | resourceCulture = value;
68 | }
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/PM3UniversalGUI/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/PM3UniversalGUI/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace PM3UniversalGUI.Properties
12 | {
13 |
14 |
15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
18 | {
19 |
20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
21 |
22 | public static Settings Default
23 | {
24 | get
25 | {
26 | return defaultInstance;
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/PM3UniversalGUI/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/PM3UniversalGUI/StringUtils.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace PM3UniversalGUI
8 | {
9 | class StringUtils
10 | {
11 | public static string ExtractBrackets(string Text, char OpeningBracket, char ClosingBracket)
12 | {
13 | string Result = Text;
14 | int i1, i2;
15 |
16 | i1 = Text.IndexOf(OpeningBracket);
17 | if (i1 >= 0)
18 | {
19 | i1++;
20 | i2 = Text.Substring(i1).IndexOf(ClosingBracket) + i1;
21 | if (i2 > i1)
22 | {
23 | Result = Text.Substring(i1, i2 - i1).Trim();
24 | }
25 | }
26 |
27 | return Result;
28 | }
29 |
30 | // "aaa bb|c|de f h" -> {"bb", "c", "de"}
31 | public static string[] SplitNearby(string Text, char[] SplitChars)
32 | {
33 | foreach (char SplitChar in SplitChars)
34 | {
35 | int i1 = Text.IndexOf(SplitChar);
36 | if (i1 < 0) continue;
37 |
38 | string[] Values = Text.Split(SplitChar);
39 |
40 | for (int i = 0; i < Values.Length; i++)
41 | Values[i] = Values[i].Trim();
42 |
43 | int i2 = Values[0].LastIndexOf(' ');
44 | if (Values[0].LastIndexOf('(') > i2) i2 = Values[0].LastIndexOf('(');
45 | if (Values[0].LastIndexOf('<') > i2) i2 = Values[0].LastIndexOf('<');
46 |
47 | int i3 = Values[Values.Length - 1].IndexOf(' ');
48 | int i4 = Values[Values.Length - 1].IndexOf(')');
49 | if (i4 > 0 && (i4 < i3 || i3 < 0)) i3 = i4;
50 | i4 = Values[Values.Length - 1].IndexOf('>');
51 | if (i4 > 0 && (i4 < i3 || i3 < 0)) i3 = i4;
52 |
53 | if (i2 >= 0) Values[0] = Values[0].Substring(i2 + 1);
54 | if (i3 >= 0 && Values.Length > 1) Values[Values.Length - 1] = Values[Values.Length - 1].Substring(0, i3);
55 |
56 | return Values;
57 | }
58 |
59 | return null;
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/PM3UniversalGUI/bin/Release/PM3UniversalGUI.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/burma69/PM3UniversalGUI/57aaa86a19d519410decdea2b6f602018ac14a1d/PM3UniversalGUI/bin/Release/PM3UniversalGUI.exe
--------------------------------------------------------------------------------
/PM3UniversalGUI/bin/Release/PM3UniversalGUI.exe.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/PM3UniversalGUI/crab.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/burma69/PM3UniversalGUI/57aaa86a19d519410decdea2b6f602018ac14a1d/PM3UniversalGUI/crab.png
--------------------------------------------------------------------------------
/PM3UniversalGUI/pm3universalgui.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/burma69/PM3UniversalGUI/57aaa86a19d519410decdea2b6f602018ac14a1d/PM3UniversalGUI/pm3universalgui.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PM3UniversalGUI
2 | Proxmark3 automated GUI proxy tool
3 |
4 | PM3UniversalGUI works with proxmark3.exe console client to display help output in an easier-to-navigate view.
5 |
6 | 
7 |
8 | It tries to parse help outputs of each command reported by proxmark3.exe, which, however, are largely inconsistent, thereby making it impossible to interpret it correctly. Efforts are made to understand most common outputs and variations, covering ~70% of the commands
9 |
10 | ## Installation
11 |
12 | Place PM3UniversalGUI release files https://github.com/burma69/PM3UniversalGUI/tree/master/PM3UniversalGUI/bin/Release into the same folder with proxmark3.exe. Alternatively, you can modify PM3UniversalGUI.exe.config XML file and specify the exact location of proxmark3.exe
13 |
--------------------------------------------------------------------------------