Added an option to run commands before and after resources are reloaded, and before testing a map. This option can be accessed from the map options dialog.

This commit is contained in:
biwa 2021-09-27 16:33:35 +02:00
parent a53198f44e
commit b0cfc8ef26
18 changed files with 1756 additions and 46 deletions

View file

@ -143,6 +143,7 @@
<Compile Include="Compilers\AccCompiler.cs" />
<Compile Include="Compilers\NodesCompiler.cs" />
<Compile Include="Config\ArgumentInfo.cs" />
<Compile Include="Config\ExternalCommandSettings.cs" />
<Compile Include="Config\StaticLimits.cs" />
<Compile Include="Config\MapLumpInfo.cs" />
<Compile Include="Config\ScriptConfiguration.cs" />
@ -179,6 +180,12 @@
<Compile Include="Controls\ArgumentBox.Designer.cs">
<DependentUpon>ArgumentBox.cs</DependentUpon>
</Compile>
<Compile Include="Controls\ExternalCommandControl.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Controls\ExternalCommandControl.Designer.cs">
<DependentUpon>ExternalCommandControl.cs</DependentUpon>
</Compile>
<Compile Include="Controls\FolderSelectDialog.cs" />
<Compile Include="Controls\Scripting\TextEditorControl.cs" />
<Compile Include="Controls\TransparentTrackBar.cs">
@ -262,6 +269,18 @@
<Compile Include="Rendering\VertexBuffer.cs" />
<Compile Include="Rendering\VisualSlopeHandle.cs" />
<Compile Include="VisualModes\VisualSlope.cs" />
<Compile Include="Windows\PreAndPostCommandsForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Windows\PreAndPostCommandsForm.Designer.cs">
<DependentUpon>PreAndPostCommandsForm.cs</DependentUpon>
</Compile>
<Compile Include="Windows\RunExternalCommandForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Windows\RunExternalCommandForm.Designer.cs">
<DependentUpon>RunExternalCommandForm.cs</DependentUpon>
</Compile>
<Compile Include="Windows\ThingStatisticsForm.cs">
<SubType>Form</SubType>
</Compile>
@ -637,9 +656,18 @@
</Reference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Controls\ExternalCommandControl.resx">
<DependentUpon>ExternalCommandControl.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Controls\PairedFloatControl.resx">
<DependentUpon>PairedFloatControl.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Windows\PreAndPostCommandsForm.resx">
<DependentUpon>PreAndPostCommandsForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Windows\RunExternalCommandForm.resx">
<DependentUpon>RunExternalCommandForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Windows\ThingStatisticsForm.resx">
<DependentUpon>ThingStatisticsForm.cs</DependentUpon>
</EmbeddedResource>

View file

@ -140,6 +140,7 @@
<Compile Include="Compilers\AccCompiler.cs" />
<Compile Include="Compilers\NodesCompiler.cs" />
<Compile Include="Config\ArgumentInfo.cs" />
<Compile Include="Config\ExternalCommandSettings.cs" />
<Compile Include="Config\MapLumpInfo.cs" />
<Compile Include="Config\ScriptConfiguration.cs" />
<Compile Include="Config\DefinedTextureSet.cs" />
@ -176,6 +177,12 @@
<Compile Include="Controls\ArgumentBox.Designer.cs">
<DependentUpon>ArgumentBox.cs</DependentUpon>
</Compile>
<Compile Include="Controls\ExternalCommandControl.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Controls\ExternalCommandControl.Designer.cs">
<DependentUpon>ExternalCommandControl.cs</DependentUpon>
</Compile>
<Compile Include="Controls\FolderSelectDialog.cs" />
<Compile Include="Controls\Scripting\TextEditorControl.cs" />
<Compile Include="Controls\TransparentTrackBar.cs">
@ -254,6 +261,18 @@
<Compile Include="Rendering\VertexBuffer.cs" />
<Compile Include="Rendering\VisualSlopeHandle.cs" />
<Compile Include="VisualModes\VisualSlope.cs" />
<Compile Include="Windows\PreAndPostCommandsForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Windows\PreAndPostCommandsForm.Designer.cs">
<DependentUpon>PreAndPostCommandsForm.cs</DependentUpon>
</Compile>
<Compile Include="Windows\RunExternalCommandForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Windows\RunExternalCommandForm.Designer.cs">
<DependentUpon>RunExternalCommandForm.cs</DependentUpon>
</Compile>
<Compile Include="Windows\ThingStatisticsForm.cs">
<SubType>Form</SubType>
</Compile>
@ -629,9 +648,18 @@
</Reference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Controls\ExternalCommandControl.resx">
<DependentUpon>ExternalCommandControl.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Controls\PairedFloatControl.resx">
<DependentUpon>PairedFloatControl.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Windows\PreAndPostCommandsForm.resx">
<DependentUpon>PreAndPostCommandsForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Windows\RunExternalCommandForm.resx">
<DependentUpon>RunExternalCommandForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Windows\ThingStatisticsForm.resx">
<DependentUpon>ThingStatisticsForm.cs</DependentUpon>
</EmbeddedResource>
@ -941,7 +969,7 @@
<Compile Include="Dehacked\DehackedData.cs" />
<Compile Include="Dehacked\DehackedFrame.cs" />
<Compile Include="Dehacked\DehackedParser.cs" />
<Compile Include="Dehacked\DehackedThing.cs" />
<Compile Include="Dehacked\DehackedThing.cs" />
<Compile Include="Editing\CustomThingsFilter.cs" />
<Compile Include="General\CRC.cs" />
<Compile Include="General\ErrorItem.cs" />

View file

@ -0,0 +1,122 @@
#region ================== Copyright (c) 2021 Boris Iwanski
/*
* This program is free software: you can redistribute it and/or modify
*
* it under the terms of the GNU General Public License as published by
*
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
*
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.If not, see<http://www.gnu.org/licenses/>.
*/
#endregion
#region ================== Namespaces
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CodeImp.DoomBuilder.IO;
#endregion
namespace CodeImp.DoomBuilder.Config
{
public class ExternalCommandSettings
{
#region ================== Variables
private string workingdirectory;
private string commands;
private bool autocloseonsuccess;
private bool exitcodeiserror;
private bool stderriserror;
#endregion
#region ================== Properties
public string WorkingDirectory { get { return workingdirectory; } set { workingdirectory = value; } }
public string Commands { get { return commands; } set { commands = value; } }
public bool AutoCloseOnSuccess { get { return autocloseonsuccess; } set { autocloseonsuccess = value; } }
public bool ExitCodeIsError { get { return exitcodeiserror; } set { exitcodeiserror = value; } }
public bool StdErrIsError { get { return stderriserror; } set { stderriserror = value; } }
#endregion
#region ================== Constructors
public ExternalCommandSettings()
{
WorkingDirectory = string.Empty;
Commands = string.Empty;
AutoCloseOnSuccess = true;
ExitCodeIsError = true;
StdErrIsError = true;
}
/// <summary>
/// Initialize with the settings loaded from a given section in a configuration.
/// </summary>
/// <param name="cfg">The configuration</param>
/// <param name="section">The section to load the settings from</param>
public ExternalCommandSettings(Configuration cfg, string section)
{
LoadSettings(cfg, section);
}
#endregion
#region ================== Methods
/// <summary>
/// Loads the settings from a given section in a configuration.
/// </summary>
/// <param name="cfg">The configuration</param>
/// <param name="section">The section to load the settings from</param>
public void LoadSettings(Configuration cfg, string section)
{
WorkingDirectory = cfg.ReadSetting(section + ".workingdirectory", string.Empty);
Commands = cfg.ReadSetting(section + ".commands", string.Empty);
AutoCloseOnSuccess = cfg.ReadSetting(section + ".autocloseonsuccess", true);
ExitCodeIsError = cfg.ReadSetting(section + ".exitcodeiserror", true);
StdErrIsError = cfg.ReadSetting(section + ".stderriserror", true);
}
/// <summary>
/// Writes the settings to a given section in a configuration.
/// </summary>
/// <param name="cfg">The configuration</param>
/// <param name="section">The section to write the settings to</param>
public void WriteSettings(Configuration cfg, string section)
{
if (!string.IsNullOrWhiteSpace(Commands))
cfg.WriteSetting(section + ".commands", Commands);
else
cfg.DeleteSetting(section + ".commands");
if (!string.IsNullOrWhiteSpace(WorkingDirectory))
cfg.WriteSetting(section + ".workingdirectory", WorkingDirectory);
else
cfg.DeleteSetting(section + ".workingdirectory");
cfg.WriteSetting(section + ".autocloseonsuccess", AutoCloseOnSuccess);
cfg.WriteSetting(section + ".exitcodeiserror", ExitCodeIsError);
cfg.WriteSetting(section + ".stderriserror", StdErrIsError);
}
#endregion
}
}

View file

@ -0,0 +1,147 @@
namespace CodeImp.DoomBuilder.Controls
{
partial class ExternalCommandControl
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.tbFolder = new System.Windows.Forms.TextBox();
this.tbCommand = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.btnBrowseFolder = new System.Windows.Forms.Button();
this.cbAutoclose = new System.Windows.Forms.CheckBox();
this.cbExitCode = new System.Windows.Forms.CheckBox();
this.cbStdErr = new System.Windows.Forms.CheckBox();
this.SuspendLayout();
//
// tbFolder
//
this.tbFolder.Location = new System.Drawing.Point(101, 5);
this.tbFolder.Name = "tbFolder";
this.tbFolder.Size = new System.Drawing.Size(498, 20);
this.tbFolder.TabIndex = 0;
//
// tbCommand
//
this.tbCommand.AcceptsReturn = true;
this.tbCommand.Location = new System.Drawing.Point(101, 31);
this.tbCommand.Multiline = true;
this.tbCommand.Name = "tbCommand";
this.tbCommand.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.tbCommand.Size = new System.Drawing.Size(498, 160);
this.tbCommand.TabIndex = 1;
this.tbCommand.WordWrap = false;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(33, 31);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(62, 13);
this.label1.TabIndex = 2;
this.label1.Text = "Commands:";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(2, 8);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(93, 13);
this.label2.TabIndex = 2;
this.label2.Text = "Working directory:";
//
// btnBrowseFolder
//
this.btnBrowseFolder.Image = global::CodeImp.DoomBuilder.Properties.Resources.Folder;
this.btnBrowseFolder.Location = new System.Drawing.Point(605, 4);
this.btnBrowseFolder.Name = "btnBrowseFolder";
this.btnBrowseFolder.Size = new System.Drawing.Size(23, 23);
this.btnBrowseFolder.TabIndex = 3;
this.btnBrowseFolder.UseVisualStyleBackColor = true;
this.btnBrowseFolder.Click += new System.EventHandler(this.button1_Click);
//
// cbAutoclose
//
this.cbAutoclose.AutoSize = true;
this.cbAutoclose.Location = new System.Drawing.Point(101, 197);
this.cbAutoclose.Name = "cbAutoclose";
this.cbAutoclose.Size = new System.Drawing.Size(243, 17);
this.cbAutoclose.TabIndex = 4;
this.cbAutoclose.Text = "Automatically close status window on success";
this.cbAutoclose.UseVisualStyleBackColor = true;
//
// cbExitCode
//
this.cbExitCode.AutoSize = true;
this.cbExitCode.Location = new System.Drawing.Point(101, 221);
this.cbExitCode.Name = "cbExitCode";
this.cbExitCode.Size = new System.Drawing.Size(175, 17);
this.cbExitCode.TabIndex = 5;
this.cbExitCode.Text = "Exit code not equal 0 is an error";
this.cbExitCode.UseVisualStyleBackColor = true;
//
// cbStdErr
//
this.cbStdErr.AutoSize = true;
this.cbStdErr.Location = new System.Drawing.Point(101, 244);
this.cbStdErr.Name = "cbStdErr";
this.cbStdErr.Size = new System.Drawing.Size(149, 17);
this.cbStdErr.TabIndex = 5;
this.cbStdErr.Text = "Writing to stderr is an error";
this.cbStdErr.UseVisualStyleBackColor = true;
//
// ExternalCommandControl
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.cbStdErr);
this.Controls.Add(this.cbExitCode);
this.Controls.Add(this.cbAutoclose);
this.Controls.Add(this.btnBrowseFolder);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);
this.Controls.Add(this.tbCommand);
this.Controls.Add(this.tbFolder);
this.Name = "ExternalCommandControl";
this.Size = new System.Drawing.Size(633, 268);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.TextBox tbFolder;
private System.Windows.Forms.TextBox tbCommand;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Button btnBrowseFolder;
private System.Windows.Forms.CheckBox cbAutoclose;
private System.Windows.Forms.CheckBox cbExitCode;
private System.Windows.Forms.CheckBox cbStdErr;
}
}

View file

@ -0,0 +1,101 @@
#region ================== Copyright (c) 2021 Boris Iwanski
/*
* This program is free software: you can redistribute it and/or modify
*
* it under the terms of the GNU General Public License as published by
*
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
*
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.If not, see<http://www.gnu.org/licenses/>.
*/
#endregion
#region ================== Namespaces
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using CodeImp.DoomBuilder.Config;
#endregion
namespace CodeImp.DoomBuilder.Controls
{
public partial class ExternalCommandControl : UserControl
{
#region ================== Constructors
public ExternalCommandControl()
{
InitializeComponent();
}
#endregion
#region ================== Methods
/// <summary>
/// Fills the controls with the settings from the ExternalCommandSettings.
/// </summary>
/// <param name="ecs">The settings</param>
public void Setup(ExternalCommandSettings ecs)
{
tbFolder.Text = ecs.WorkingDirectory;
tbCommand.Text = ecs.Commands;
cbAutoclose.Checked = ecs.AutoCloseOnSuccess;
cbExitCode.Checked = ecs.ExitCodeIsError;
cbStdErr.Checked = ecs.StdErrIsError;
}
/// <summary>
/// Returns the external command settings.
/// </summary>
/// <returns>The external command settings</returns>
public ExternalCommandSettings GetSettings()
{
ExternalCommandSettings ecs = new ExternalCommandSettings();
ecs.WorkingDirectory = tbFolder.Text.Trim();
ecs.Commands = tbCommand.Text.Trim();
ecs.AutoCloseOnSuccess = cbAutoclose.Checked;
ecs.ExitCodeIsError = cbExitCode.Checked;
ecs.StdErrIsError = cbStdErr.Checked;
return ecs;
}
#endregion
#region ================== Events
private void button1_Click(object sender, EventArgs e)
{
FolderSelectDialog dirdialog = new FolderSelectDialog();
dirdialog.Title = "Select base folder";
dirdialog.InitialDirectory = tbFolder.Text;
if (dirdialog.ShowDialog(this.Handle))
{
tbFolder.Text = dirdialog.FileName;
}
}
#endregion
}
}

View file

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View file

@ -327,40 +327,71 @@ namespace CodeImp.DoomBuilder
General.Plugins.OnMapSaveBegin(SavePurpose.Testing);
if(General.Map.SaveMap(tempwad, SavePurpose.Testing))
{
bool canceled = false;
// No compiler errors?
if(General.Map.Errors.Count == 0)
if (General.Map.Errors.Count == 0)
{
// Make arguments
string args = ConvertParameters(General.Map.ConfigSettings.TestParameters, skill, General.Map.ConfigSettings.TestShortPaths, General.Map.ConfigSettings.TestLinuxPaths);
// Setup process info
ProcessStartInfo processinfo = new ProcessStartInfo();
processinfo.Arguments = args;
processinfo.FileName = General.Map.ConfigSettings.TestProgram;
processinfo.CreateNoWindow = false;
processinfo.ErrorDialog = false;
processinfo.UseShellExecute = true;
processinfo.WindowStyle = ProcessWindowStyle.Normal;
processinfo.WorkingDirectory = Path.GetDirectoryName(processinfo.FileName);
// Output info
General.WriteLogLine("Running test program: " + processinfo.FileName);
General.WriteLogLine("Program parameters: " + processinfo.Arguments);
General.MainWindow.DisplayStatus(StatusType.Info, "Launching " + processinfo.FileName + "...");
try
// Check if there's a pre command to run, and try to execute it
if (!string.IsNullOrWhiteSpace(General.Map.Options.TestPreCommand.Commands))
{
// Start the program
Process process = Process.Start(processinfo);
process.EnableRaisingEvents = true; //mxd
process.Exited += ProcessOnExited; //mxd
processes.Add(process, tempwad); //mxd
Cursor.Current = oldcursor; //mxd
if (!General.Map.ExecuteExternalCommand(General.Map.Options.TestPreCommand, tempwad))
{
General.WriteLogLine("Testing was canceled when executing the testing pre command.");
// Reset status
General.MainWindow.DisplayStatus(StatusType.Warning, "Testing was canceled.");
canceled = true;
}
}
catch(Exception e)
if (!canceled)
{
// Unable to start the program
General.ShowErrorMessage("Unable to start the test program, " + e.GetType().Name + ": " + e.Message, MessageBoxButtons.OK);
// Make arguments
string args = ConvertParameters(General.Map.ConfigSettings.TestParameters, skill, General.Map.ConfigSettings.TestShortPaths, General.Map.ConfigSettings.TestLinuxPaths);
// Setup process info
ProcessStartInfo processinfo = new ProcessStartInfo();
processinfo.Arguments = args;
processinfo.FileName = General.Map.ConfigSettings.TestProgram;
processinfo.CreateNoWindow = false;
processinfo.ErrorDialog = false;
processinfo.UseShellExecute = true;
processinfo.WindowStyle = ProcessWindowStyle.Normal;
processinfo.WorkingDirectory = Path.GetDirectoryName(processinfo.FileName);
// Output info
General.WriteLogLine("Running test program: " + processinfo.FileName);
General.WriteLogLine("Program parameters: " + processinfo.Arguments);
General.MainWindow.DisplayStatus(StatusType.Info, "Launching " + processinfo.FileName + "...");
try
{
// Start the program
Process process = Process.Start(processinfo);
process.EnableRaisingEvents = true; //mxd
process.Exited += ProcessOnExited; //mxd
processes.Add(process, tempwad); //mxd
Cursor.Current = oldcursor; //mxd
}
catch (Exception e)
{
// Unable to start the program
General.ShowErrorMessage("Unable to start the test program, " + e.GetType().Name + ": " + e.Message, MessageBoxButtons.OK);
}
// Check if there's a post command to run, and try to execute it
// TODO: currently modifying the commands is disabled since it'd have to be executed after the test program ends, which is not
// the case in the current situation, since this code here is reached immediately after launching the test program
/*
if (!string.IsNullOrWhiteSpace(General.Map.Options.TestPostCommand.Commands))
{
if (!General.Map.ExecuteExternalCommand(General.Map.Options.TestPostCommand, tempwad))
{
General.WriteLogLine("Failed to execute the test post command successfully.");
}
}
*/
}
}
else

View file

@ -1053,7 +1053,7 @@ namespace CodeImp.DoomBuilder
filetitle = Path.GetFileName(filepathname);
// Reload resources
ReloadResources(true);
ReloadResources(true, false);
}
try
@ -2300,7 +2300,7 @@ namespace CodeImp.DoomBuilder
DebugConsole.Clear();
#endif
ReloadResources(true);
ReloadResources(true, true);
if(General.ErrorLogger.IsErrorAdded)
{
@ -2315,12 +2315,27 @@ namespace CodeImp.DoomBuilder
}
internal void ReloadResources(bool clearerrors) //mxd. clearerrors flag
internal void ReloadResources(bool clearerrors, bool runprepostcommands) //mxd. clearerrors flag
{
// Keep old display info
StatusInfo oldstatus = General.MainWindow.Status;
Cursor oldcursor = Cursor.Current;
// Check if there's a pre command to run, and try to execute it
if (runprepostcommands && !string.IsNullOrWhiteSpace(options.ReloadResourcePreCommand.Commands))
{
if(!ExecuteExternalCommand(options.ReloadResourcePreCommand))
{
General.WriteLogLine("Reloading resources was canceled when executing the reload resource pre command.");
// Reset status
General.MainWindow.DisplayStatus(StatusType.Warning, "Reloading resourcess was canceled.");
Cursor.Current = oldcursor;
return;
}
}
// Show status
General.MainWindow.DisplayStatus(StatusType.Busy, "Reloading data resources...");
Cursor.Current = Cursors.WaitCursor;
@ -2377,11 +2392,46 @@ namespace CodeImp.DoomBuilder
//mxd. Update script names
LoadACS();
// Check if there's a post command to run, and try to execute it
if (runprepostcommands && !string.IsNullOrWhiteSpace(options.ReloadResourcePostCommand.Commands))
{
if (!ExecuteExternalCommand(options.ReloadResourcePostCommand))
{
General.WriteLogLine("Failed to execute the reload resource post command successfully.");
}
}
// Reset status
General.MainWindow.DisplayStatus(oldstatus);
Cursor.Current = oldcursor;
}
public bool ExecuteExternalCommand(ExternalCommandSettings cmdsettings, string arguments = "")
{
string filename;
do
{
filename = Path.ChangeExtension(Path.GetTempFileName(), ".cmd");
}
while (File.Exists(filename));
File.WriteAllText(filename, cmdsettings.Commands);
ProcessStartInfo startinfo = new ProcessStartInfo();
startinfo.FileName = "cmd.exe";
startinfo.Arguments = "/C " + filename + " " + arguments;
if(!string.IsNullOrWhiteSpace(cmdsettings.WorkingDirectory))
startinfo.WorkingDirectory = cmdsettings.WorkingDirectory;
RunExternalCommandForm f = new RunExternalCommandForm(startinfo, cmdsettings);
f.ShowDialog();
File.Delete(filename);
return f.DialogResult == DialogResult.OK;
}
// Game Configuration action
[BeginAction("mapoptions")]
internal void ShowMapOptions()
@ -2475,7 +2525,7 @@ namespace CodeImp.DoomBuilder
map.UpdateCustomLinedefColors();
// Reload resources
ReloadResources(false);
ReloadResources(false, false);
// Update interface
General.MainWindow.SetupInterface();

View file

@ -89,6 +89,11 @@ namespace CodeImp.DoomBuilder.Map
//mxd. Position and scale
private readonly Vector2D viewposition;
private readonly float viewscale;
private ExternalCommandSettings reloadresourceprecommand;
private ExternalCommandSettings reloadresourcepostcommand;
private ExternalCommandSettings testprecommand;
private ExternalCommandSettings testpostcommand;
#endregion
@ -147,6 +152,11 @@ namespace CodeImp.DoomBuilder.Map
public Vector2D ViewPosition { get { return viewposition; } }
public float ViewScale { get { return viewscale; } }
public ExternalCommandSettings ReloadResourcePreCommand { get { return reloadresourceprecommand; } internal set { reloadresourceprecommand = value; } }
public ExternalCommandSettings ReloadResourcePostCommand { get { return reloadresourcepostcommand; } internal set { reloadresourcepostcommand = value; } }
public ExternalCommandSettings TestPreCommand { get { return testprecommand; } internal set { testprecommand = value; } }
public ExternalCommandSettings TestPostCommand { get { return testpostcommand; } internal set { testpostcommand = value; } }
#endregion
#region ================== Constructor / Disposer
@ -167,6 +177,11 @@ namespace CodeImp.DoomBuilder.Map
this.viewposition = new Vector2D(float.NaN, float.NaN); //mxd
this.viewscale = float.NaN; //mxd
reloadresourceprecommand = new ExternalCommandSettings();
reloadresourcepostcommand = new ExternalCommandSettings();
testprecommand = new ExternalCommandSettings();
testpostcommand = new ExternalCommandSettings();
//mxd. Sector drawing options
this.custombrightness = 196;
this.customceilheight = 128;
@ -235,6 +250,12 @@ namespace CodeImp.DoomBuilder.Map
//mxd
uselongtexturenames = longtexturenamessupported && this.mapconfig.ReadSetting("uselongtexturenames", false);
// Load the pre and post commands
reloadresourceprecommand = new ExternalCommandSettings(mapconfig, "reloadresourceprecommand");
reloadresourcepostcommand= new ExternalCommandSettings(mapconfig, "reloadresourcepostcommand");
testprecommand = new ExternalCommandSettings(mapconfig, "testprecommand");
testpostcommand = new ExternalCommandSettings(mapconfig, "testpostcommand");
//mxd. Position and scale
float vpx = this.mapconfig.ReadSetting("viewpositionx", float.NaN);
float vpy = this.mapconfig.ReadSetting("viewpositiony", float.NaN);
@ -386,8 +407,14 @@ namespace CodeImp.DoomBuilder.Map
foreach(ScriptDocumentSettings settings in scriptsettings.Values)
WriteScriptDocumentSettings(mapconfig, "scriptdocuments.document" + (sdcounter++), settings);
// Write pre and post commands
reloadresourceprecommand.WriteSettings(mapconfig, "reloadresourceprecommand");
reloadresourcepostcommand.WriteSettings(mapconfig, "reloadresourcepostcommand");
testprecommand.WriteSettings(mapconfig, "testprecommand");
testpostcommand.WriteSettings(mapconfig, "testpostcommand");
// Load the file or make a new file
if(File.Exists(settingsfile))
if (File.Exists(settingsfile))
wadcfg = new Configuration(settingsfile, true);
else
wadcfg = new Configuration(true);

View file

@ -47,6 +47,8 @@ namespace CodeImp.DoomBuilder.Windows
this.strictpatches = new System.Windows.Forms.CheckBox();
this.datalocations = new CodeImp.DoomBuilder.Controls.ResourceListEditor();
this.tooltip = new System.Windows.Forms.ToolTip(this.components);
this.label5 = new System.Windows.Forms.Label();
this.prepostcommands = new System.Windows.Forms.Button();
label3 = new System.Windows.Forms.Label();
label2 = new System.Windows.Forms.Label();
label1 = new System.Windows.Forms.Label();
@ -165,7 +167,7 @@ namespace CodeImp.DoomBuilder.Windows
// apply
//
this.apply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.apply.Location = new System.Drawing.Point(179, 399);
this.apply.Location = new System.Drawing.Point(179, 429);
this.apply.Name = "apply";
this.apply.Size = new System.Drawing.Size(112, 25);
this.apply.TabIndex = 2;
@ -177,7 +179,7 @@ namespace CodeImp.DoomBuilder.Windows
//
this.cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.cancel.Location = new System.Drawing.Point(297, 399);
this.cancel.Location = new System.Drawing.Point(297, 429);
this.cancel.Name = "cancel";
this.cancel.Size = new System.Drawing.Size(112, 25);
this.cancel.TabIndex = 3;
@ -229,13 +231,33 @@ namespace CodeImp.DoomBuilder.Windows
this.datalocations.Size = new System.Drawing.Size(368, 127);
this.datalocations.TabIndex = 0;
//
// label5
//
this.label5.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
this.label5.Location = new System.Drawing.Point(13, 423);
this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(396, 2);
this.label5.TabIndex = 21;
//
// prepostcommands
//
this.prepostcommands.Location = new System.Drawing.Point(247, 395);
this.prepostcommands.Name = "prepostcommands";
this.prepostcommands.Size = new System.Drawing.Size(162, 25);
this.prepostcommands.TabIndex = 20;
this.prepostcommands.Text = "Edit pre and post commands";
this.prepostcommands.UseVisualStyleBackColor = true;
this.prepostcommands.Click += new System.EventHandler(this.prepostcommands_Click);
//
// MapOptionsForm
//
this.AcceptButton = this.apply;
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.CancelButton = this.cancel;
this.ClientSize = new System.Drawing.Size(421, 436);
this.ClientSize = new System.Drawing.Size(421, 466);
this.Controls.Add(this.label5);
this.Controls.Add(this.prepostcommands);
this.Controls.Add(this.panelres);
this.Controls.Add(this.cancel);
this.Controls.Add(this.apply);
@ -272,7 +294,7 @@ namespace CodeImp.DoomBuilder.Windows
private System.Windows.Forms.Label examplelabel;
private System.Windows.Forms.CheckBox longtexturenames;
private System.Windows.Forms.ToolTip tooltip;
private System.Windows.Forms.Label label5;
private System.Windows.Forms.Button prepostcommands;
}
}

View file

@ -34,6 +34,11 @@ namespace CodeImp.DoomBuilder.Windows
// Variables
private readonly MapOptions options;
private readonly bool newmap;
private ExternalCommandSettings reloadresourceprecommand;
private ExternalCommandSettings reloadresourcepostcommand;
private ExternalCommandSettings testprecommand;
private ExternalCommandSettings testpostcommand;
private bool prepostcommandsmodified;
// Properties
public MapOptions Options { get { return options; } }
@ -49,8 +54,14 @@ namespace CodeImp.DoomBuilder.Windows
// Keep settings
this.options = options;
prepostcommandsmodified = false;
reloadresourceprecommand = options.ReloadResourcePreCommand ;
reloadresourcepostcommand = options.ReloadResourcePostCommand ;
testprecommand = options.TestPreCommand ;
testpostcommand = options.TestPostCommand;
//mxd. Add script compilers
foreach(KeyValuePair<string, ScriptConfiguration> group in General.CompiledScriptConfigs)
foreach (KeyValuePair<string, ScriptConfiguration> group in General.CompiledScriptConfigs)
{
scriptcompiler.Items.Add(group.Value);
}
@ -110,6 +121,8 @@ namespace CodeImp.DoomBuilder.Windows
// Fill the resources list
datalocations.EditResourceLocationList(options.Resources);
//reloadresourceprecmd.Text = options.ReloadResourcePreCommand;
}
// OK clicked
@ -247,6 +260,15 @@ namespace CodeImp.DoomBuilder.Windows
options.StrictPatches = strictpatches.Checked;
options.CopyResources(datalocations.GetResources());
// Only store the pre and post commands in the map options if they were actually changed (i.e. the user pressed the OK button the the dialog)
if (prepostcommandsmodified)
{
options.ReloadResourcePreCommand = reloadresourceprecommand;
options.ReloadResourcePostCommand = reloadresourcepostcommand;
options.TestPreCommand = testprecommand;
options.TestPostCommand = testpostcommand;
}
//mxd. Store script compiler
if(scriptcompiler.Enabled)
{
@ -264,6 +286,8 @@ namespace CodeImp.DoomBuilder.Windows
//mxd. Use long texture names?
if(longtexturenames.Enabled) options.UseLongTextureNames = longtexturenames.Checked;
//options.ReloadResourcePreCommand = reloadresourceprecmd.Text;
// Hide window
this.DialogResult = DialogResult.OK;
this.Close();
@ -347,5 +371,22 @@ namespace CodeImp.DoomBuilder.Windows
General.ShowHelp("w_mapoptions.html");
hlpevent.Handled = true;
}
private void prepostcommands_Click(object sender, EventArgs e)
{
PreAndPostCommandsForm papcf = new PreAndPostCommandsForm(reloadresourceprecommand, reloadresourcepostcommand, testprecommand, testpostcommand);
papcf.ShowDialog();
if (papcf.DialogResult == DialogResult.OK)
{
reloadresourceprecommand = papcf.GetReloadResourcePreCommand();
reloadresourcepostcommand = papcf.GetReloadResourcePostCommand();
testprecommand = papcf.GetTestPreCommand();
testpostcommand = papcf.GetTestPostCommand();
prepostcommandsmodified = true;
}
}
}
}

View file

@ -132,7 +132,7 @@ namespace CodeImp.DoomBuilder.Windows
// apply
//
this.apply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.apply.Location = new System.Drawing.Point(178, 513);
this.apply.Location = new System.Drawing.Point(178, 510);
this.apply.Name = "apply";
this.apply.Size = new System.Drawing.Size(112, 25);
this.apply.TabIndex = 3;
@ -144,7 +144,7 @@ namespace CodeImp.DoomBuilder.Windows
//
this.cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.cancel.Location = new System.Drawing.Point(296, 513);
this.cancel.Location = new System.Drawing.Point(296, 510);
this.cancel.Name = "cancel";
this.cancel.Size = new System.Drawing.Size(112, 25);
this.cancel.TabIndex = 4;
@ -214,7 +214,7 @@ namespace CodeImp.DoomBuilder.Windows
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.CancelButton = this.cancel;
this.ClientSize = new System.Drawing.Size(420, 544);
this.ClientSize = new System.Drawing.Size(420, 541);
this.Controls.Add(this.scriptcompiler);
this.Controls.Add(this.scriptcompilerlabel);
this.Controls.Add(this.mapslist);
@ -255,7 +255,5 @@ namespace CodeImp.DoomBuilder.Windows
private System.Windows.Forms.Label scriptcompilerlabel;
private System.Windows.Forms.CheckBox longtexturenames;
private System.Windows.Forms.ToolTip tooltip;
}
}

View file

@ -0,0 +1,248 @@
namespace CodeImp.DoomBuilder.Windows
{
partial class PreAndPostCommandsForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.btnOK = new System.Windows.Forms.Button();
this.btnCancel = new System.Windows.Forms.Button();
this.tabControl1 = new System.Windows.Forms.TabControl();
this.tabPage1 = new System.Windows.Forms.TabPage();
this.label2 = new System.Windows.Forms.Label();
this.groupBox2 = new System.Windows.Forms.GroupBox();
this.reloadpost = new CodeImp.DoomBuilder.Controls.ExternalCommandControl();
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.reloadpre = new CodeImp.DoomBuilder.Controls.ExternalCommandControl();
this.tabPage2 = new System.Windows.Forms.TabPage();
this.label1 = new System.Windows.Forms.Label();
this.groupBox3 = new System.Windows.Forms.GroupBox();
this.testpost = new CodeImp.DoomBuilder.Controls.ExternalCommandControl();
this.groupBox4 = new System.Windows.Forms.GroupBox();
this.testpre = new CodeImp.DoomBuilder.Controls.ExternalCommandControl();
this.tabControl1.SuspendLayout();
this.tabPage1.SuspendLayout();
this.groupBox2.SuspendLayout();
this.groupBox1.SuspendLayout();
this.tabPage2.SuspendLayout();
this.groupBox3.SuspendLayout();
this.groupBox4.SuspendLayout();
this.SuspendLayout();
//
// btnOK
//
this.btnOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnOK.Location = new System.Drawing.Point(524, 678);
this.btnOK.Name = "btnOK";
this.btnOK.Size = new System.Drawing.Size(75, 23);
this.btnOK.TabIndex = 3;
this.btnOK.Text = "OK";
this.btnOK.UseVisualStyleBackColor = true;
this.btnOK.Click += new System.EventHandler(this.btnOK_Click);
//
// btnCancel
//
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.btnCancel.Location = new System.Drawing.Point(605, 678);
this.btnCancel.Name = "btnCancel";
this.btnCancel.Size = new System.Drawing.Size(75, 23);
this.btnCancel.TabIndex = 3;
this.btnCancel.Text = "Cancel";
this.btnCancel.UseVisualStyleBackColor = true;
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
//
// tabControl1
//
this.tabControl1.Controls.Add(this.tabPage1);
this.tabControl1.Controls.Add(this.tabPage2);
this.tabControl1.Location = new System.Drawing.Point(12, 12);
this.tabControl1.Name = "tabControl1";
this.tabControl1.SelectedIndex = 0;
this.tabControl1.Size = new System.Drawing.Size(670, 657);
this.tabControl1.TabIndex = 4;
//
// tabPage1
//
this.tabPage1.Controls.Add(this.label2);
this.tabPage1.Controls.Add(this.groupBox2);
this.tabPage1.Controls.Add(this.groupBox1);
this.tabPage1.Location = new System.Drawing.Point(4, 22);
this.tabPage1.Name = "tabPage1";
this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
this.tabPage1.Size = new System.Drawing.Size(662, 631);
this.tabPage1.TabIndex = 0;
this.tabPage1.Text = "Reload resources commands";
this.tabPage1.UseVisualStyleBackColor = true;
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(6, 15);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(431, 13);
this.label2.TabIndex = 8;
this.label2.Text = "The commands will be automatically stored in a temporary CMD file and executed as" +
" such.";
//
// groupBox2
//
this.groupBox2.Controls.Add(this.reloadpost);
this.groupBox2.Location = new System.Drawing.Point(6, 336);
this.groupBox2.Name = "groupBox2";
this.groupBox2.Size = new System.Drawing.Size(649, 289);
this.groupBox2.TabIndex = 4;
this.groupBox2.TabStop = false;
this.groupBox2.Text = "Post commands";
//
// reloadpost
//
this.reloadpost.Location = new System.Drawing.Point(6, 19);
this.reloadpost.Name = "reloadpost";
this.reloadpost.Size = new System.Drawing.Size(633, 268);
this.reloadpost.TabIndex = 1;
//
// groupBox1
//
this.groupBox1.Controls.Add(this.reloadpre);
this.groupBox1.Location = new System.Drawing.Point(6, 41);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(649, 289);
this.groupBox1.TabIndex = 3;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "Pre commands";
//
// reloadpre
//
this.reloadpre.Location = new System.Drawing.Point(7, 19);
this.reloadpre.Name = "reloadpre";
this.reloadpre.Size = new System.Drawing.Size(633, 268);
this.reloadpre.TabIndex = 1;
//
// tabPage2
//
this.tabPage2.Controls.Add(this.label1);
this.tabPage2.Controls.Add(this.groupBox3);
this.tabPage2.Controls.Add(this.groupBox4);
this.tabPage2.Location = new System.Drawing.Point(4, 22);
this.tabPage2.Name = "tabPage2";
this.tabPage2.Padding = new System.Windows.Forms.Padding(3);
this.tabPage2.Size = new System.Drawing.Size(662, 631);
this.tabPage2.TabIndex = 1;
this.tabPage2.Text = "Test map commands";
this.tabPage2.UseVisualStyleBackColor = true;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(6, 15);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(637, 13);
this.label1.TabIndex = 7;
this.label1.Text = "The commands will be automatically stored in a temporary CMD file and executed as" +
" such. %1 is the full path to the temporary map file.";
//
// groupBox3
//
this.groupBox3.Controls.Add(this.testpost);
this.groupBox3.Enabled = false;
this.groupBox3.Location = new System.Drawing.Point(6, 336);
this.groupBox3.Name = "groupBox3";
this.groupBox3.Size = new System.Drawing.Size(649, 289);
this.groupBox3.TabIndex = 6;
this.groupBox3.TabStop = false;
this.groupBox3.Text = "Post commands";
//
// testpost
//
this.testpost.Location = new System.Drawing.Point(6, 19);
this.testpost.Name = "testpost";
this.testpost.Size = new System.Drawing.Size(633, 268);
this.testpost.TabIndex = 1;
//
// groupBox4
//
this.groupBox4.Controls.Add(this.testpre);
this.groupBox4.Location = new System.Drawing.Point(6, 41);
this.groupBox4.Name = "groupBox4";
this.groupBox4.Size = new System.Drawing.Size(649, 289);
this.groupBox4.TabIndex = 5;
this.groupBox4.TabStop = false;
this.groupBox4.Text = "Pre commands";
//
// testpre
//
this.testpre.Location = new System.Drawing.Point(7, 19);
this.testpre.Name = "testpre";
this.testpre.Size = new System.Drawing.Size(633, 268);
this.testpre.TabIndex = 1;
//
// PreAndPostCommandsForm
//
this.AcceptButton = this.btnOK;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.btnCancel;
this.ClientSize = new System.Drawing.Size(692, 713);
this.Controls.Add(this.tabControl1);
this.Controls.Add(this.btnOK);
this.Controls.Add(this.btnCancel);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "PreAndPostCommandsForm";
this.Text = "Pre and post commands";
this.tabControl1.ResumeLayout(false);
this.tabPage1.ResumeLayout(false);
this.tabPage1.PerformLayout();
this.groupBox2.ResumeLayout(false);
this.groupBox1.ResumeLayout(false);
this.tabPage2.ResumeLayout(false);
this.tabPage2.PerformLayout();
this.groupBox3.ResumeLayout(false);
this.groupBox4.ResumeLayout(false);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Button btnCancel;
private System.Windows.Forms.Button btnOK;
private System.Windows.Forms.TabControl tabControl1;
private System.Windows.Forms.TabPage tabPage1;
private System.Windows.Forms.GroupBox groupBox2;
private Controls.ExternalCommandControl reloadpost;
private System.Windows.Forms.GroupBox groupBox1;
private Controls.ExternalCommandControl reloadpre;
private System.Windows.Forms.TabPage tabPage2;
private System.Windows.Forms.GroupBox groupBox3;
private Controls.ExternalCommandControl testpost;
private System.Windows.Forms.GroupBox groupBox4;
private Controls.ExternalCommandControl testpre;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label label1;
}
}

View file

@ -0,0 +1,97 @@
#region ================== Copyright (c) 2021 Boris Iwanski
/*
* This program is free software: you can redistribute it and/or modify
*
* it under the terms of the GNU General Public License as published by
*
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
*
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.If not, see<http://www.gnu.org/licenses/>.
*/
#endregion
#region ================== Namespaces
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using CodeImp.DoomBuilder.Config;
#endregion
namespace CodeImp.DoomBuilder.Windows
{
public partial class PreAndPostCommandsForm : DelayedForm
{
#region ================== Constructors
public PreAndPostCommandsForm(ExternalCommandSettings reloadresourceprecommand, ExternalCommandSettings reloadresourcepostcommand, ExternalCommandSettings testprecommand, ExternalCommandSettings testpostcommand)
{
InitializeComponent();
reloadpre.Setup(reloadresourceprecommand);
reloadpost.Setup(reloadresourcepostcommand);
testpre.Setup(testprecommand);
testpost.Setup(testpostcommand);
}
#endregion
#region ================== Methods
public ExternalCommandSettings GetReloadResourcePreCommand()
{
return reloadpre.GetSettings();
}
public ExternalCommandSettings GetReloadResourcePostCommand()
{
return reloadpost.GetSettings();
}
public ExternalCommandSettings GetTestPreCommand()
{
return testpre.GetSettings();
}
public ExternalCommandSettings GetTestPostCommand()
{
return testpost.GetSettings();
}
#endregion
#region ================== Events
private void btnCancel_Click(object sender, EventArgs e)
{
DialogResult = DialogResult.Cancel;
Close();
}
private void btnOK_Click(object sender, EventArgs e)
{
DialogResult = DialogResult.OK;
Close();
}
#endregion
}
}

View file

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View file

@ -0,0 +1,114 @@
namespace CodeImp.DoomBuilder.Windows
{
partial class RunExternalCommandForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.btnContinue = new System.Windows.Forms.Button();
this.btnCancel = new System.Windows.Forms.Button();
this.rtbOutput = new System.Windows.Forms.RichTextBox();
this.btnRetry = new System.Windows.Forms.Button();
this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
this.SuspendLayout();
//
// btnContinue
//
this.btnContinue.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnContinue.Enabled = false;
this.btnContinue.Location = new System.Drawing.Point(596, 459);
this.btnContinue.Name = "btnContinue";
this.btnContinue.Size = new System.Drawing.Size(75, 23);
this.btnContinue.TabIndex = 1;
this.btnContinue.Text = "Continue";
this.toolTip1.SetToolTip(this.btnContinue, "Continues loading the resources");
this.btnContinue.UseVisualStyleBackColor = true;
this.btnContinue.Click += new System.EventHandler(this.btnContinue_Click);
//
// btnCancel
//
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnCancel.Location = new System.Drawing.Point(677, 459);
this.btnCancel.Name = "btnCancel";
this.btnCancel.Size = new System.Drawing.Size(75, 23);
this.btnCancel.TabIndex = 1;
this.btnCancel.Text = "Cancel";
this.toolTip1.SetToolTip(this.btnCancel, "Cancels the running command, or cancels loading the resources");
this.btnCancel.UseVisualStyleBackColor = true;
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
//
// rtbOutput
//
this.rtbOutput.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.rtbOutput.Location = new System.Drawing.Point(12, 12);
this.rtbOutput.Name = "rtbOutput";
this.rtbOutput.ReadOnly = true;
this.rtbOutput.Size = new System.Drawing.Size(740, 441);
this.rtbOutput.TabIndex = 2;
this.rtbOutput.Text = "";
//
// btnRetry
//
this.btnRetry.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnRetry.Enabled = false;
this.btnRetry.Location = new System.Drawing.Point(515, 459);
this.btnRetry.Name = "btnRetry";
this.btnRetry.Size = new System.Drawing.Size(75, 23);
this.btnRetry.TabIndex = 1;
this.btnRetry.Text = "Run again";
this.toolTip1.SetToolTip(this.btnRetry, "Runs the command again");
this.btnRetry.UseVisualStyleBackColor = true;
this.btnRetry.Click += new System.EventHandler(this.btnRetry_Click);
//
// RunExternalCommandForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(766, 494);
this.Controls.Add(this.rtbOutput);
this.Controls.Add(this.btnRetry);
this.Controls.Add(this.btnContinue);
this.Controls.Add(this.btnCancel);
this.Name = "RunExternalCommandForm";
this.ShowIcon = false;
this.Text = "Running external command";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.RunExternalCommandForm_FormClosing);
this.Shown += new System.EventHandler(this.RunExternalCommandForm_Shown);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Button btnCancel;
private System.Windows.Forms.Button btnContinue;
private System.Windows.Forms.RichTextBox rtbOutput;
private System.Windows.Forms.Button btnRetry;
private System.Windows.Forms.ToolTip toolTip1;
}
}

View file

@ -0,0 +1,290 @@
#region ================== Copyright (c) 2021 Boris Iwanski
/*
* This program is free software: you can redistribute it and/or modify
*
* it under the terms of the GNU General Public License as published by
*
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
*
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.If not, see<http://www.gnu.org/licenses/>.
*/
#endregion
#region ================== Namespaces
using System;
using System.Diagnostics;
using System.Drawing;
using System.Management;
using System.Threading;
using System.Windows.Forms;
using CodeImp.DoomBuilder.Config;
#endregion
namespace CodeImp.DoomBuilder.Windows
{
public partial class RunExternalCommandForm : DelayedForm
{
#region ================== Variables
private Process process;
private ProcessStartInfo startinfo;
private Thread runthread;
private object lockobj;
private bool haserrors;
private ExternalCommandSettings settings;
#endregion
#region ================== Delegates
private delegate void CallStringBoolMethodDeletage(string s, bool iserror);
private delegate void CallVoidMethodDeletage();
#endregion
#region ================== Constructors
public RunExternalCommandForm(ProcessStartInfo startinfo, ExternalCommandSettings settings)
{
InitializeComponent();
lockobj = new object();
haserrors = false;
rtbOutput.Font = new Font(FontFamily.GenericMonospace, rtbOutput.Font.Size);
this.startinfo = startinfo;
this.settings = settings;
}
#endregion
#region ================== Methods
/// <summary>
/// Starts execution of the external command
/// </summary>
private void Start()
{
rtbOutput.Clear();
haserrors = false;
btnContinue.Enabled = false;
btnRetry.Enabled = false;
runthread = new Thread(() => Run());
runthread.Name = "Run external command";
runthread.Priority = ThreadPriority.Normal;
runthread.Start();
}
/// <summary>
/// Stops execution of the external command
/// </summary>
private void Stop()
{
haserrors = true;
process.CancelOutputRead();
process.CancelErrorRead();
KillProcessAndChildren(process.Id);
FinishRun();
}
/// <summary>
/// Runs the external command
/// </summary>
private void Run()
{
process = new Process();
process.StartInfo = startinfo;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo.CreateNoWindow = true;
process.OutputDataReceived += new DataReceivedEventHandler(OutputHandler);
process.ErrorDataReceived += new DataReceivedEventHandler(ErrorHandler);
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
if (process.ExitCode != 0 && settings.ExitCodeIsError)
haserrors = true;
FinishRun();
}
/// <summary>
/// Finishes after running the external command. Closes the window if there were no errors
/// </summary>
private void FinishRun()
{
if(InvokeRequired)
{
CallVoidMethodDeletage d = FinishRun;
Invoke(d);
}
else
{
btnContinue.Enabled = true;
btnRetry.Enabled = true;
if(haserrors)
{
AppendText(Environment.NewLine + Environment.NewLine + "There were errors during the execution of the external commands.", true);
AppendText(Environment.NewLine + "Exit code: " + process.ExitCode, true);
}
if(!haserrors && settings.AutoCloseOnSuccess)
{
DialogResult = DialogResult.OK;
Close();
}
}
}
/// <summary>
/// Handles lines going to stdout
/// </summary>
/// <param name="sendingprocess">Process the line is coming from</param>
/// <param name="outline">The received line event</param>
private void OutputHandler(object sendingprocess, DataReceivedEventArgs outline)
{
if(outline.Data != null)
AppendText(outline.Data + Environment.NewLine, false);
}
/// <summary>
/// handles lines going to stderr
/// </summary>
/// <param name="sendingprocess">Process the line is coming from</param>
/// <param name="outline">The received line event</param>
private void ErrorHandler(object sendingprocess, DataReceivedEventArgs outline)
{
if (outline.Data != null)
{
AppendText(outline.Data + Environment.NewLine, true);
if(settings.StdErrIsError)
haserrors = true;
}
}
/// <summary>
/// Adds a line to the output text box and scrolls to the bottom. Colors the text red if it's an error
/// </summary>
/// <param name="text">Text to append</param>
/// <param name="iserror">If it's an error or not</param>
private void AppendText(string text, bool iserror)
{
if(InvokeRequired)
{
CallStringBoolMethodDeletage d = AppendText;
Invoke(d, text, iserror);
}
else
{
lock (lockobj)
{
rtbOutput.AppendText(text);
if(iserror)
{
rtbOutput.Select(rtbOutput.Text.Length - text.Length, text.Length);
rtbOutput.SelectionColor = Color.Red;
//haserrors = true;
}
rtbOutput.Select(rtbOutput.Text.Length, 0);
rtbOutput.ScrollToCaret();
}
}
}
/// <summary>
/// Recursively kill a process and its childern. Taken from https://stackoverflow.com/a/40265540
/// </summary>
/// <param name="pid">Process id</param>
private static void KillProcessAndChildren(int pid)
{
ManagementObjectSearcher processSearcher = new ManagementObjectSearcher("Select * From Win32_Process Where ParentProcessID=" + pid);
ManagementObjectCollection processCollection = processSearcher.Get();
// We must kill child processes first!
if (processCollection != null)
{
foreach (ManagementObject mo in processCollection)
{
KillProcessAndChildren(Convert.ToInt32(mo["ProcessID"])); //kill child processes(also kills childrens of childrens etc.)
}
}
// Then kill parents.
try
{
Process proc = Process.GetProcessById(pid);
if (!proc.HasExited) proc.Kill();
}
catch (ArgumentException)
{
// Process already exited.
}
}
#endregion
#region ================== Events
private void RunExternalCommandForm_Shown(object sender, EventArgs e)
{
Start();
}
private void btnCancel_Click(object sender, EventArgs e)
{
if (process.HasExited)
{
DialogResult = DialogResult.Cancel;
Close();
}
else
{
Stop();
}
}
private void btnRetry_Click(object sender, EventArgs e)
{
Start();
}
private void btnContinue_Click(object sender, EventArgs e)
{
DialogResult = DialogResult.OK;
Close();
}
private void RunExternalCommandForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (!process.HasExited)
e.Cancel = true;
}
#endregion
}
}

View file

@ -0,0 +1,126 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="toolTip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="toolTip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>