2009-04-19 18:07:22 +00:00
#region = = = = = = = = = = = = = = = = = = Copyright ( c ) 2007 Pascal vd Heiden
/ *
* Copyright ( c ) 2007 Pascal vd Heiden , www . codeimp . com
* This program is released under GNU General Public License
*
* 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 .
*
* /
#endregion
#region = = = = = = = = = = = = = = = = = = Namespaces
using System ;
using System.IO ;
using CodeImp.DoomBuilder.Data ;
using System.Diagnostics ;
using CodeImp.DoomBuilder.Actions ;
using System.Windows.Forms ;
using CodeImp.DoomBuilder.Windows ;
2013-12-18 09:11:04 +00:00
using CodeImp.DoomBuilder.IO ;
Model rendering (all modes): UDMF scale, pitch and roll are now displayed.
Thing Edit Form, UDMF: added controls for setting pitch, roll, scale, render style, fill color, alpha, health and score.
Visual mode, UDMF: UDMF scale is now applied when rendering sprites.
Added Thing Statistics form (Edit -> View Thing Types...), which shows all loaded thing types with some additional info.
Visual mode: sprites with negative ScaleX and positive ScaleY were not rendered properly.
Classic modes: display was not updated after loading a sprite.
Current testing engine change was not saved on closing the program when no other game configuration settings were changed.
2014-04-30 10:01:22 +00:00
using CodeImp.DoomBuilder.Editing ;
2009-04-19 18:07:22 +00:00
#endregion
namespace CodeImp.DoomBuilder
{
internal class Launcher : IDisposable
{
#region = = = = = = = = = = = = = = = = = = Constants
#endregion
#region = = = = = = = = = = = = = = = = = = Variables
private string tempwad ;
2014-03-23 09:08:51 +00:00
private Process process ; //mxd
2009-04-19 18:07:22 +00:00
private bool isdisposed ;
2014-03-23 09:08:51 +00:00
delegate void EngineExitedCallback ( ) ; //mxd
2009-04-19 18:07:22 +00:00
#endregion
#region = = = = = = = = = = = = = = = = = = Properties
public string TempWAD { get { return tempwad ; } }
2014-03-23 09:08:51 +00:00
2009-04-19 18:07:22 +00:00
#endregion
#region = = = = = = = = = = = = = = = = = = Constructor / Destructor
// Constructor
public Launcher ( MapManager manager )
{
// Initialize
2009-07-02 14:15:47 +00:00
CleanTempFile ( manager ) ;
2009-04-19 18:07:22 +00:00
// Bind actions
General . Actions . BindMethods ( this ) ;
}
// Disposer
public void Dispose ( )
{
// Not yet disposed?
if ( ! isdisposed )
{
// Unbind actions
General . Actions . UnbindMethods ( this ) ;
2014-03-23 09:08:51 +00:00
//mxd. Terminate process?
2015-10-26 11:58:33 +00:00
if ( process ! = null )
2014-12-03 23:15:26 +00:00
{
2014-03-23 09:08:51 +00:00
process . CloseMainWindow ( ) ;
process . Close ( ) ;
}
2009-04-19 18:07:22 +00:00
2009-07-02 14:15:47 +00:00
// Remove temporary file
try { File . Delete ( tempwad ) ; }
catch ( Exception ) { }
2009-04-19 18:07:22 +00:00
// Done
isdisposed = true ;
}
}
#endregion
#region = = = = = = = = = = = = = = = = = = Parameters
// This takes the unconverted parameters (with placeholders) and converts it
// to parameters with full paths, names and numbers where placeholders were put.
// The tempfile must be the full path and filename to the PWAD file to test.
public string ConvertParameters ( string parameters , int skill , bool shortpaths )
{
string outp = parameters ;
DataLocation iwadloc ;
string p_wp = "" , p_wf = "" ;
string p_ap = "" , p_apq = "" ;
string p_l1 = "" , p_l2 = "" ;
string p_nm = "" ;
string f = tempwad ;
// Make short path if needed
if ( shortpaths ) f = General . GetShortFilePath ( f ) ;
// Find the first IWAD file
if ( General . Map . Data . FindFirstIWAD ( out iwadloc ) )
{
// %WP and %WF result in IWAD file
p_wp = iwadloc . location ;
p_wf = Path . GetFileName ( p_wp ) ;
if ( shortpaths )
{
p_wp = General . GetShortFilePath ( p_wp ) ;
p_wf = General . GetShortFilePath ( p_wf ) ;
}
}
// Make a list of all data locations, including map location
2009-07-02 14:15:47 +00:00
DataLocation maplocation = new DataLocation ( DataLocation . RESOURCE_WAD , General . Map . FilePathName , false , false , false ) ;
2009-04-19 18:07:22 +00:00
DataLocationList locations = new DataLocationList ( ) ;
2009-07-02 14:15:47 +00:00
locations . AddRange ( General . Map . ConfigSettings . Resources ) ;
2009-04-19 18:07:22 +00:00
locations . AddRange ( General . Map . Options . Resources ) ;
2015-06-14 19:29:44 +00:00
if ( ! string . IsNullOrEmpty ( maplocation . location ) ) locations . Add ( maplocation ) ; //mxd. maplocation.location will be empty when a newly created map was not saved yet.
2009-04-19 18:07:22 +00:00
// Go for all data locations
foreach ( DataLocation dl in locations )
{
// Location not the IWAD file?
2009-07-02 14:15:47 +00:00
if ( ( dl . type ! = DataLocation . RESOURCE_WAD ) | | ( dl . location ! = iwadloc . location ) )
2009-04-19 18:07:22 +00:00
{
2009-07-02 14:15:47 +00:00
// Location not included?
if ( ! dl . notfortesting )
2009-04-19 18:07:22 +00:00
{
2009-07-02 14:15:47 +00:00
// Add to string of files
if ( shortpaths )
{
p_ap + = General . GetShortFilePath ( dl . location ) + " " ;
p_apq + = "\"" + General . GetShortFilePath ( dl . location ) + "\" " ;
}
else
{
p_ap + = dl . location + " " ;
p_apq + = "\"" + dl . location + "\" " ;
}
2009-04-19 18:07:22 +00:00
}
}
}
// Trim last space from resource file locations
p_ap = p_ap . TrimEnd ( ' ' ) ;
p_apq = p_apq . TrimEnd ( ' ' ) ;
// Try finding the L1 and L2 numbers from the map name
string numstr = "" ;
bool first = true ;
foreach ( char c in General . Map . Options . CurrentName )
{
// Character is a number?
2013-12-18 09:11:04 +00:00
if ( Configuration . NUMBERS . IndexOf ( c ) > - 1 )
2009-04-19 18:07:22 +00:00
{
// Include it
numstr + = c ;
}
else
{
// Store the number if we found one
if ( numstr . Length > 0 )
{
2014-02-21 14:42:12 +00:00
int num ;
2009-04-19 18:07:22 +00:00
int . TryParse ( numstr , out num ) ;
if ( first ) p_l1 = num . ToString ( ) ; else p_l2 = num . ToString ( ) ;
numstr = "" ;
first = false ;
}
}
}
// Store the number if we found one
if ( numstr . Length > 0 )
{
2014-02-21 14:42:12 +00:00
int num ;
2009-04-19 18:07:22 +00:00
int . TryParse ( numstr , out num ) ;
if ( first ) p_l1 = num . ToString ( ) ; else p_l2 = num . ToString ( ) ;
}
// No monsters?
if ( ! General . Settings . TestMonsters ) p_nm = "-nomonsters" ;
// Make sure all our placeholders are in uppercase
outp = outp . Replace ( "%f" , "%F" ) ;
outp = outp . Replace ( "%wp" , "%WP" ) ;
outp = outp . Replace ( "%wf" , "%WF" ) ;
outp = outp . Replace ( "%wP" , "%WP" ) ;
outp = outp . Replace ( "%wF" , "%WF" ) ;
outp = outp . Replace ( "%Wp" , "%WP" ) ;
outp = outp . Replace ( "%Wf" , "%WF" ) ;
outp = outp . Replace ( "%l1" , "%L1" ) ;
outp = outp . Replace ( "%l2" , "%L2" ) ;
outp = outp . Replace ( "%l" , "%L" ) ;
outp = outp . Replace ( "%ap" , "%AP" ) ;
outp = outp . Replace ( "%aP" , "%AP" ) ;
outp = outp . Replace ( "%Ap" , "%AP" ) ;
outp = outp . Replace ( "%s" , "%S" ) ;
outp = outp . Replace ( "%nM" , "%NM" ) ;
outp = outp . Replace ( "%Nm" , "%NM" ) ;
outp = outp . Replace ( "%nm" , "%NM" ) ;
// Replace placeholders with actual values
outp = outp . Replace ( "%F" , f ) ;
outp = outp . Replace ( "%WP" , p_wp ) ;
outp = outp . Replace ( "%WF" , p_wf ) ;
outp = outp . Replace ( "%L1" , p_l1 ) ;
outp = outp . Replace ( "%L2" , p_l2 ) ;
outp = outp . Replace ( "%L" , General . Map . Options . CurrentName ) ;
outp = outp . Replace ( "\"%AP\"" , p_apq ) ;
outp = outp . Replace ( "%AP" , p_ap ) ;
outp = outp . Replace ( "%S" , skill . ToString ( ) ) ;
outp = outp . Replace ( "%NM" , p_nm ) ;
// Return result
return outp ;
}
2015-10-26 11:58:33 +00:00
//mxd
private bool AlreadyTesting ( )
{
if ( process ! = null )
{
General . ShowWarningMessage ( "Game engine is already running." + Environment . NewLine + "Please close '" + process . MainModule . FileName + "' first." , MessageBoxButtons . OK ) ;
return true ;
}
return false ;
}
2009-04-19 18:07:22 +00:00
#endregion
#region = = = = = = = = = = = = = = = = = = Test
// This saves the map to a temporary file and launches a test
[BeginAction("testmap")]
public void Test ( )
{
2015-10-26 11:58:33 +00:00
if ( AlreadyTesting ( ) | | ! General . Editing . Mode . OnMapTestBegin ( false ) ) return ; //mxd
2013-09-11 09:47:53 +00:00
TestAtSkill ( General . Map . ConfigSettings . TestSkill ) ;
2014-03-23 09:08:51 +00:00
General . Editing . Mode . OnMapTestEnd ( false ) ; //mxd
2009-04-19 18:07:22 +00:00
}
2012-06-07 01:06:37 +00:00
2013-09-11 09:47:53 +00:00
//mxd
[BeginAction("testmapfromview")]
2014-12-03 23:15:26 +00:00
public void TestFromView ( )
{
2015-10-26 11:58:33 +00:00
if ( AlreadyTesting ( ) | | ! General . Editing . Mode . OnMapTestBegin ( true ) ) return ;
2013-09-11 09:47:53 +00:00
TestAtSkill ( General . Map . ConfigSettings . TestSkill ) ;
2014-03-23 09:08:51 +00:00
General . Editing . Mode . OnMapTestEnd ( true ) ;
2013-09-11 09:47:53 +00:00
}
2009-04-19 18:07:22 +00:00
2015-09-06 22:15:21 +00:00
// This saves the map to a temporary file and launches a test with the given skill
2009-04-19 18:07:22 +00:00
public void TestAtSkill ( int skill )
{
Cursor oldcursor = Cursor . Current ;
// Check if configuration is OK
2016-02-01 22:04:00 +00:00
if ( string . IsNullOrEmpty ( General . Map . ConfigSettings . TestProgram ) | | ! File . Exists ( General . Map . ConfigSettings . TestProgram ) )
2009-04-19 18:07:22 +00:00
{
2012-11-13 21:11:53 +00:00
//mxd. Let's be more precise
string message ;
if ( General . Map . ConfigSettings . TestProgram = = "" )
message = "Your test program is not set for the current game configuration" ;
else
message = "Current test program has invalid path" ;
2009-04-19 18:07:22 +00:00
// Show message
Cursor . Current = Cursors . Default ;
2012-11-13 21:11:53 +00:00
DialogResult result = General . ShowWarningMessage ( message + ". Would you like to set up your test program now?" , MessageBoxButtons . YesNo ) ;
2009-04-19 18:07:22 +00:00
if ( result = = DialogResult . Yes )
{
// Show game configuration on the right page
General . MainWindow . ShowConfigurationPage ( 2 ) ;
}
return ;
}
// No custom parameters?
if ( ! General . Map . ConfigSettings . CustomParameters )
{
// Set parameters to the default ones
General . Map . ConfigSettings . TestParameters = General . Map . Config . TestParameters ;
General . Map . ConfigSettings . TestShortPaths = General . Map . Config . TestShortPaths ;
}
2009-07-02 14:15:47 +00:00
// Remove temporary file
try { File . Delete ( tempwad ) ; }
catch ( Exception ) { }
2009-04-19 18:07:22 +00:00
// Save map to temporary file
Cursor . Current = Cursors . WaitCursor ;
tempwad = General . MakeTempFilename ( General . Map . TempPath , "wad" ) ;
2009-08-23 13:55:49 +00:00
General . Plugins . OnMapSaveBegin ( SavePurpose . Testing ) ;
if ( General . Map . SaveMap ( tempwad , SavePurpose . Testing ) )
2009-04-19 18:07:22 +00:00
{
2009-05-10 19:04:47 +00:00
// No compiler errors?
if ( General . Map . Errors . Count = = 0 )
2009-04-19 18:07:22 +00:00
{
2009-05-10 19:04:47 +00:00
// Make arguments
2014-03-12 10:39:24 +00:00
string args = ConvertParameters ( General . Map . ConfigSettings . TestParameters , skill , General . Map . ConfigSettings . TestShortPaths ) ;
2009-05-10 19:04:47 +00:00
// Setup process info
2014-03-12 10:39:24 +00:00
ProcessStartInfo processinfo = new ProcessStartInfo ( ) ;
2009-05-10 19:04:47 +00:00
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 ) ;
2014-03-23 09:08:51 +00:00
General . MainWindow . DisplayStatus ( StatusType . Info , "Launching " + processinfo . FileName + "..." ) ;
2009-05-10 19:04:47 +00:00
try
{
// Start the program
2014-03-23 09:08:51 +00:00
process = Process . Start ( processinfo ) ;
process . EnableRaisingEvents = true ; //mxd
process . Exited + = ProcessOnExited ; //mxd
Cursor . Current = oldcursor ; //mxd
2009-05-10 19:04:47 +00:00
}
catch ( Exception e )
2009-04-19 18:07:22 +00:00
{
2009-05-10 19:04:47 +00:00
// Unable to start the program
2013-07-19 15:30:58 +00:00
General . ShowErrorMessage ( "Unable to start the test program, " + e . GetType ( ) . Name + ": " + e . Message , MessageBoxButtons . OK ) ;
2009-04-19 18:07:22 +00:00
}
}
2009-05-10 19:04:47 +00:00
else
2009-04-19 18:07:22 +00:00
{
2009-05-10 19:04:47 +00:00
General . MainWindow . DisplayStatus ( StatusType . Warning , "Unable to test the map due to script errors." ) ;
2009-04-19 18:07:22 +00:00
}
}
2014-03-23 09:08:51 +00:00
}
//mxd
2014-12-03 23:15:26 +00:00
private void TestingFinished ( )
{
2014-03-23 09:08:51 +00:00
//Done
TimeSpan deltatime = TimeSpan . FromTicks ( process . ExitTime . Ticks - process . StartTime . Ticks ) ;
process = null ;
General . WriteLogLine ( "Test program has finished." ) ;
General . WriteLogLine ( "Run time: " + deltatime . TotalSeconds . ToString ( "###########0.00" ) + " seconds" ) ;
General . MainWindow . DisplayReady ( ) ;
2009-07-02 14:15:47 +00:00
// Clean up temp file
CleanTempFile ( General . Map ) ;
2013-09-11 15:01:55 +00:00
2009-08-23 13:55:49 +00:00
General . Plugins . OnMapSaveEnd ( SavePurpose . Testing ) ;
2009-04-19 18:07:22 +00:00
General . MainWindow . FocusDisplay ( ) ;
Model rendering (all modes): UDMF scale, pitch and roll are now displayed.
Thing Edit Form, UDMF: added controls for setting pitch, roll, scale, render style, fill color, alpha, health and score.
Visual mode, UDMF: UDMF scale is now applied when rendering sprites.
Added Thing Statistics form (Edit -> View Thing Types...), which shows all loaded thing types with some additional info.
Visual mode: sprites with negative ScaleX and positive ScaleY were not rendered properly.
Classic modes: display was not updated after loading a sprite.
Current testing engine change was not saved on closing the program when no other game configuration settings were changed.
2014-04-30 10:01:22 +00:00
if ( General . Editing . Mode is ClassicMode ) General . MainWindow . RedrawDisplay ( ) ;
2009-04-19 18:07:22 +00:00
}
2014-03-23 09:08:51 +00:00
//mxd
2014-12-03 23:15:26 +00:00
private void ProcessOnExited ( object sender , EventArgs eventArgs )
{
General . MainWindow . Invoke ( new EngineExitedCallback ( TestingFinished ) ) ;
2014-03-23 09:08:51 +00:00
}
2009-07-02 14:15:47 +00:00
// This deletes the previous temp file and creates a new, empty temp file
private void CleanTempFile ( MapManager manager )
{
// Remove temporary file
try { File . Delete ( tempwad ) ; }
catch ( Exception ) { }
// Make new empty temp file
tempwad = General . MakeTempFilename ( manager . TempPath , "wad" ) ;
File . WriteAllText ( tempwad , "" ) ;
}
2009-04-19 18:07:22 +00:00
#endregion
}
}