mirror of
https://git.do.srb2.org/STJr/UltimateZoneBuilder.git
synced 2024-11-23 20:32:34 +00:00
e029efcbe6
- General.Exit() method for plugins to close Doom Builder - command-line parameter -delaywindow to keep the main window hidden until nothing more to process. This gives plugins the ability to perform an operation without showing the main window at all if the plugin calls General.Exit() before the window is shown. Usefull for batch processes in which no interface window is desired. - some new plugin events that occur before and after making a new map and opening a map.
1353 lines
39 KiB
C#
1353 lines
39 KiB
C#
|
|
#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.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.Text;
|
|
using System.Windows.Forms;
|
|
using System.IO;
|
|
using System.Reflection;
|
|
using CodeImp.DoomBuilder.Windows;
|
|
using CodeImp.DoomBuilder.IO;
|
|
using CodeImp.DoomBuilder.Map;
|
|
using CodeImp.DoomBuilder.Geometry;
|
|
using System.Runtime.InteropServices;
|
|
using CodeImp.DoomBuilder.Actions;
|
|
using System.Diagnostics;
|
|
using CodeImp.DoomBuilder.Rendering;
|
|
using CodeImp.DoomBuilder.Config;
|
|
using SlimDX.Direct3D9;
|
|
using System.Drawing;
|
|
using CodeImp.DoomBuilder.Plugins;
|
|
using CodeImp.DoomBuilder.Types;
|
|
using System.Collections.ObjectModel;
|
|
using System.Threading;
|
|
|
|
#endregion
|
|
|
|
namespace CodeImp.DoomBuilder
|
|
{
|
|
public static class General
|
|
{
|
|
#region ================== API Declarations
|
|
|
|
//[DllImport("user32.dll")]
|
|
//internal static extern bool LockWindowUpdate(IntPtr hwnd);
|
|
|
|
[DllImport("kernel32.dll", EntryPoint="RtlZeroMemory", SetLastError=false)]
|
|
internal static extern void ZeroMemory(IntPtr dest, int size);
|
|
|
|
[DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]
|
|
internal static extern unsafe void CopyMemory(void* dst, void* src, UIntPtr length);
|
|
|
|
[DllImport("user32.dll", EntryPoint = "SendMessage", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
|
|
internal static extern int SendMessage(IntPtr hwnd, uint Msg, int wParam, int lParam);
|
|
|
|
[DllImport("user32.dll", SetLastError = true)]
|
|
internal static extern bool MessageBeep(MessageBeepType type);
|
|
|
|
#endregion
|
|
|
|
#region ================== Constants
|
|
|
|
// SendMessage API
|
|
internal const int CB_SETITEMHEIGHT = 0x153;
|
|
|
|
// Files and Folders
|
|
private const string SETTINGS_FILE = "Builder.cfg";
|
|
private const string SETTINGS_DIR = "Doom Builder";
|
|
private const string LOG_FILE = "Builder.log";
|
|
private const string GAME_CONFIGS_DIR = "Configurations";
|
|
private const string COMPILERS_DIR = "Compilers";
|
|
private const string PLUGINS_DIR = "Plugins";
|
|
private const string SETUP_DIR = "Setup";
|
|
|
|
#endregion
|
|
|
|
#region ================== Variables
|
|
|
|
// Files and Folders
|
|
private static string apppath;
|
|
private static string setuppath;
|
|
private static string settingspath;
|
|
private static string logfile;
|
|
private static string temppath;
|
|
private static string configspath;
|
|
private static string compilerspath;
|
|
private static string pluginspath;
|
|
|
|
// Main objects
|
|
private static Assembly thisasm;
|
|
private static MainForm mainwindow;
|
|
private static ProgramConfiguration settings;
|
|
private static MapManager map;
|
|
private static ActionManager actions;
|
|
private static PluginManager plugins;
|
|
private static ColorCollection colors;
|
|
private static TypesManager types;
|
|
private static Clock clock;
|
|
|
|
// Configurations
|
|
private static List<ConfigurationInfo> configs;
|
|
private static List<CompilerInfo> compilers;
|
|
private static List<NodebuilderInfo> nodebuilders;
|
|
|
|
// States
|
|
private static bool debugbuild;
|
|
|
|
// Command line arguments
|
|
private static string[] cmdargs;
|
|
private static string autoloadfile = null;
|
|
private static string autoloadmap = null;
|
|
private static string autoloadconfig = null;
|
|
private static bool delaymainwindow;
|
|
|
|
#endregion
|
|
|
|
#region ================== Properties
|
|
|
|
internal static Assembly ThisAssembly { get { return thisasm; } }
|
|
public static string AppPath { get { return apppath; } }
|
|
public static string TempPath { get { return temppath; } }
|
|
public static string ConfigsPath { get { return configspath; } }
|
|
public static string CompilersPath { get { return compilerspath; } }
|
|
public static string PluginsPath { get { return pluginspath; } }
|
|
public static ICollection<string> CommandArgs { get { return Array.AsReadOnly<string>(cmdargs); } }
|
|
internal static MainForm MainWindow { get { return mainwindow; } }
|
|
public static IMainForm Interface { get { return mainwindow; } }
|
|
public static ProgramConfiguration Settings { get { return settings; } }
|
|
public static ColorCollection Colors { get { return colors; } }
|
|
internal static List<ConfigurationInfo> Configs { get { return configs; } }
|
|
internal static List<NodebuilderInfo> Nodebuilders { get { return nodebuilders; } }
|
|
internal static List<CompilerInfo> Compilers { get { return compilers; } }
|
|
public static MapManager Map { get { return map; } }
|
|
internal static ActionManager Actions { get { return actions; } }
|
|
internal static PluginManager Plugins { get { return plugins; } }
|
|
public static Clock Clock { get { return clock; } }
|
|
public static bool DebugBuild { get { return debugbuild; } }
|
|
internal static TypesManager Types { get { return types; } }
|
|
public static string AutoLoadFile { get { return autoloadfile; } }
|
|
public static string AutoLoadMap { get { return autoloadmap; } }
|
|
public static string AutoLoadConfig { get { return autoloadconfig; } }
|
|
public static bool DelayMainWindow { get { return delaymainwindow; } }
|
|
|
|
#endregion
|
|
|
|
#region ================== Configurations
|
|
|
|
// This returns the game configuration info by filename
|
|
internal static ConfigurationInfo GetConfigurationInfo(string filename)
|
|
{
|
|
// Go for all config infos
|
|
foreach(ConfigurationInfo ci in configs)
|
|
{
|
|
// Check if filename matches
|
|
if(string.Compare(Path.GetFileNameWithoutExtension(ci.Filename),
|
|
Path.GetFileNameWithoutExtension(filename), true) == 0)
|
|
{
|
|
// Return this info
|
|
return ci;
|
|
}
|
|
}
|
|
|
|
// None found
|
|
return null;
|
|
}
|
|
|
|
// This loads and returns a game configuration
|
|
internal static Configuration LoadGameConfiguration(string filename)
|
|
{
|
|
Configuration cfg;
|
|
|
|
// Make the full filepathname
|
|
string filepathname = Path.Combine(configspath, filename);
|
|
|
|
// Load configuration
|
|
try
|
|
{
|
|
// Try loading the configuration
|
|
cfg = new Configuration(filepathname, true);
|
|
|
|
// Check for erors
|
|
if(cfg.ErrorResult != 0)
|
|
{
|
|
// Error in configuration
|
|
ShowErrorMessage("Unable to load the game configuration file \"" + filename + "\".\n" +
|
|
"Error near line " + cfg.ErrorLine + ": " + cfg.ErrorDescription, MessageBoxButtons.OK);
|
|
return null;
|
|
}
|
|
// Check if this is a Doom Builder 2 config
|
|
else if(cfg.ReadSetting("type", "") != "Doom Builder 2 Game Configuration")
|
|
{
|
|
// Old configuration
|
|
ShowErrorMessage("Unable to load the game configuration file \"" + filename + "\".\n" +
|
|
"This configuration is not a Doom Builder 2 game configuration.", MessageBoxButtons.OK);
|
|
return null;
|
|
}
|
|
else
|
|
{
|
|
// The following code was used to convert the linedef types of DB1 type
|
|
// configurations into proper categorized structures for DB2.
|
|
// I keep this code here in the repository because if it failed, I might
|
|
// need this again to convert from scratch.
|
|
/*
|
|
GameConfiguration gcfg = new GameConfiguration(cfg);
|
|
Configuration newcfg = new Configuration();
|
|
newcfg.NewConfiguration(true);
|
|
bool doommap = (gcfg.FormatInterface == "DoomMapSetIO");
|
|
|
|
foreach(LinedefActionInfo a in gcfg.SortedLinedefActions)
|
|
{
|
|
string catkey = a.Category.ToLowerInvariant().Trim();
|
|
string cattitle = a.Category;
|
|
string linekey = a.Index.ToString(CultureInfo.InvariantCulture);
|
|
string linetitle = a.Name;
|
|
string lineprefix = a.Prefix;
|
|
if(catkey.Length == 0) { catkey = "misc"; cattitle = ""; }
|
|
if(cattitle.Length > 0) newcfg.WriteSetting("linedeftypes." + catkey + ".title", cattitle);
|
|
newcfg.WriteSetting("linedeftypes." + catkey + "." + linekey + ".title", linetitle);
|
|
if(doommap) newcfg.WriteSetting("linedeftypes." + catkey + "." + linekey + ".prefix", lineprefix);
|
|
|
|
if(!doommap)
|
|
{
|
|
for(int i = 0; i < 5; i++)
|
|
{
|
|
if(a.ArgUsed[i])
|
|
{
|
|
newcfg.WriteSetting("linedeftypes." + catkey + "." + linekey + ".arg" + i.ToString(CultureInfo.InvariantCulture) + ".title", a.ArgTitle[i]);
|
|
if(a.ArgTagType[i] != TagType.None) newcfg.WriteSetting("linedeftypes." + catkey + "." + linekey + ".arg" + i.ToString(CultureInfo.InvariantCulture) + ".tag", (int)a.ArgTagType[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
newcfg.SaveConfiguration(Path.Combine(configspath, "_" + filename));
|
|
*/
|
|
|
|
// Return config
|
|
return cfg;
|
|
}
|
|
}
|
|
catch(Exception)
|
|
{
|
|
// Unable to load configuration
|
|
ShowErrorMessage("Unable to load the game configuration file \"" + filename + "\".", MessageBoxButtons.OK);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// This loads all game configurations
|
|
private static void LoadAllGameConfigurations()
|
|
{
|
|
Configuration cfg;
|
|
string[] filenames;
|
|
string name, fullfilename;
|
|
|
|
// Display status
|
|
mainwindow.DisplayStatus("Loading game configurations...");
|
|
|
|
// Make array
|
|
configs = new List<ConfigurationInfo>();
|
|
|
|
// Go for all cfg files in the configurations directory
|
|
filenames = Directory.GetFiles(configspath, "*.cfg", SearchOption.TopDirectoryOnly);
|
|
foreach(string filepath in filenames)
|
|
{
|
|
// Check if it can be loaded
|
|
cfg = LoadGameConfiguration(Path.GetFileName(filepath));
|
|
if(cfg != null)
|
|
{
|
|
fullfilename = Path.GetFileName(filepath);
|
|
ConfigurationInfo cfginfo = new ConfigurationInfo(cfg, fullfilename);
|
|
|
|
// Add to lists
|
|
General.WriteLogLine("Registered game configuration '" + cfginfo.Name + "' from '" + fullfilename + "'");
|
|
configs.Add(cfginfo);
|
|
}
|
|
}
|
|
|
|
// Sort the list
|
|
configs.Sort();
|
|
}
|
|
|
|
// This loads all nodebuilder configurations
|
|
private static void LoadAllNodebuilderConfigurations()
|
|
{
|
|
Configuration cfg;
|
|
IDictionary builderslist;
|
|
string[] filenames;
|
|
|
|
// Display status
|
|
mainwindow.DisplayStatus("Loading nodebuilder configurations...");
|
|
|
|
// Make array
|
|
nodebuilders = new List<NodebuilderInfo>();
|
|
|
|
// Go for all cfg files in the compilers directory
|
|
filenames = Directory.GetFiles(compilerspath, "*.cfg", SearchOption.TopDirectoryOnly);
|
|
foreach(string filepath in filenames)
|
|
{
|
|
try
|
|
{
|
|
// Try loading the configuration
|
|
cfg = new Configuration(filepath, true);
|
|
|
|
// Check for erors
|
|
if(cfg.ErrorResult != 0)
|
|
{
|
|
// Error in configuration
|
|
ShowErrorMessage("Unable to load the compiler configuration file \"" + Path.GetFileName(filepath) + "\".\n" +
|
|
"Error near line " + cfg.ErrorLine + ": " + cfg.ErrorDescription, MessageBoxButtons.OK);
|
|
}
|
|
else
|
|
{
|
|
// Get structures
|
|
builderslist = cfg.ReadSetting("nodebuilders", new Hashtable());
|
|
foreach(DictionaryEntry de in builderslist)
|
|
{
|
|
// Check if this is a structure
|
|
if(de.Value is IDictionary)
|
|
{
|
|
try
|
|
{
|
|
// Make nodebuilder info
|
|
nodebuilders.Add(new NodebuilderInfo(Path.GetFileName(filepath), de.Key.ToString(), cfg));
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
// Unable to load configuration
|
|
ShowErrorMessage("Unable to load the nodebuilder configuration '" + de.Key.ToString() + "' from \"" + Path.GetFileName(filepath) + "\". Error: " + e.Message, MessageBoxButtons.OK);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch(Exception)
|
|
{
|
|
// Unable to load configuration
|
|
ShowErrorMessage("Unable to load the compiler configuration file \"" + Path.GetFileName(filepath) + "\".", MessageBoxButtons.OK);
|
|
}
|
|
}
|
|
|
|
// Sort the list
|
|
nodebuilders.Sort();
|
|
}
|
|
|
|
// This loads all compiler configurations
|
|
private static void LoadAllCompilerConfigurations()
|
|
{
|
|
Configuration cfg;
|
|
IDictionary compilerslist;
|
|
string[] filenames;
|
|
|
|
// Display status
|
|
mainwindow.DisplayStatus("Loading compiler configurations...");
|
|
|
|
// Make array
|
|
compilers = new List<CompilerInfo>();
|
|
|
|
// Go for all cfg files in the compilers directory
|
|
filenames = Directory.GetFiles(compilerspath, "*.cfg", SearchOption.TopDirectoryOnly);
|
|
foreach(string filepath in filenames)
|
|
{
|
|
try
|
|
{
|
|
// Try loading the configuration
|
|
cfg = new Configuration(filepath, true);
|
|
|
|
// Check for erors
|
|
if(cfg.ErrorResult != 0)
|
|
{
|
|
// Error in configuration
|
|
ShowErrorMessage("Unable to load the compiler configuration file \"" + Path.GetFileName(filepath) + "\".\n" +
|
|
"Error near line " + cfg.ErrorLine + ": " + cfg.ErrorDescription, MessageBoxButtons.OK);
|
|
}
|
|
else
|
|
{
|
|
// Get structures
|
|
compilerslist = cfg.ReadSetting("compilers", new Hashtable());
|
|
foreach(DictionaryEntry de in compilerslist)
|
|
{
|
|
// Check if this is a structure
|
|
if(de.Value is IDictionary)
|
|
{
|
|
// Make compiler info
|
|
compilers.Add(new CompilerInfo(Path.GetFileName(filepath), de.Key.ToString(), cfg));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch(Exception)
|
|
{
|
|
// Unable to load configuration
|
|
ShowErrorMessage("Unable to load the compiler configuration file \"" + Path.GetFileName(filepath) + "\".", MessageBoxButtons.OK);
|
|
}
|
|
}
|
|
}
|
|
|
|
// This returns a nodebuilder by name
|
|
internal static NodebuilderInfo GetNodebuilderByName(string name)
|
|
{
|
|
// Go for all nodebuilders
|
|
foreach(NodebuilderInfo n in nodebuilders)
|
|
{
|
|
// Name matches?
|
|
if(n.Name == name) return n;
|
|
}
|
|
|
|
// Cannot find that nodebuilder
|
|
return null;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Startup
|
|
|
|
// Main program entry
|
|
[STAThread]
|
|
internal static void Main(string[] args)
|
|
{
|
|
Uri localpath;
|
|
Version thisversion;
|
|
|
|
// Determine states
|
|
#if DEBUG
|
|
debugbuild = true;
|
|
#else
|
|
debugbuild = false;
|
|
#endif
|
|
|
|
// Enable OS visual styles
|
|
Application.EnableVisualStyles();
|
|
Application.DoEvents(); // This must be here to work around a .NET bug
|
|
|
|
// Hook to DLL loading failure event
|
|
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
|
|
|
|
// Set current thread name
|
|
Thread.CurrentThread.Name = "Main Application";
|
|
|
|
// Get a reference to this assembly
|
|
thisasm = Assembly.GetExecutingAssembly();
|
|
thisversion = thisasm.GetName().Version;
|
|
|
|
// Find application path
|
|
localpath = new Uri(Path.GetDirectoryName(thisasm.GetName().CodeBase));
|
|
apppath = Uri.UnescapeDataString(localpath.AbsolutePath);
|
|
|
|
// Setup directories
|
|
temppath = Path.GetTempPath();
|
|
setuppath = Path.Combine(apppath, SETUP_DIR);
|
|
settingspath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), SETTINGS_DIR);
|
|
configspath = Path.Combine(apppath, GAME_CONFIGS_DIR);
|
|
compilerspath = Path.Combine(apppath, COMPILERS_DIR);
|
|
pluginspath = Path.Combine(apppath, PLUGINS_DIR);
|
|
logfile = Path.Combine(settingspath, LOG_FILE);
|
|
|
|
// Make program settings directory if missing
|
|
if(!Directory.Exists(settingspath)) Directory.CreateDirectory(settingspath);
|
|
|
|
// Remove the previous log file and start logging
|
|
if(File.Exists(logfile)) File.Delete(logfile);
|
|
General.WriteLogLine("Doom Builder " + thisversion.Major + "." + thisversion.Minor + " startup");
|
|
General.WriteLogLine("Application path: " + apppath);
|
|
General.WriteLogLine("Temporary path: " + temppath);
|
|
General.WriteLogLine("Local settings path: " + settingspath);
|
|
General.WriteLogLine("Configurations path: " + configspath);
|
|
General.WriteLogLine("Compilers path: " + compilerspath);
|
|
General.WriteLogLine("Plugins path: " + pluginspath);
|
|
General.WriteLogLine("Command-line arguments: " + args.Length);
|
|
for(int i = 0; i < args.Length; i++)
|
|
General.WriteLogLine("Argument " + i + ": \"" + args[i] + "\"");
|
|
|
|
// Parse command-line arguments
|
|
ParseCommandLineArgs(args);
|
|
|
|
// Load configuration
|
|
General.WriteLogLine("Loading program configuration...");
|
|
settings = new ProgramConfiguration();
|
|
if(settings.Load(Path.Combine(settingspath, SETTINGS_FILE),
|
|
Path.Combine(apppath, SETTINGS_FILE)))
|
|
{
|
|
// Create action manager
|
|
actions = new ActionManager();
|
|
|
|
// Bind static methods to actions
|
|
General.Actions.BindMethods(typeof(General));
|
|
|
|
// Initialize static classes
|
|
MapSet.Initialize();
|
|
|
|
// Create main window
|
|
General.WriteLogLine("Loading main interface window...");
|
|
mainwindow = new MainForm();
|
|
mainwindow.UpdateInterface();
|
|
|
|
if(!delaymainwindow)
|
|
{
|
|
// Show main window
|
|
General.WriteLogLine("Showing main interface window...");
|
|
mainwindow.Show();
|
|
mainwindow.Update();
|
|
}
|
|
|
|
// Start Direct3D
|
|
General.WriteLogLine("Starting Direct3D graphics driver...");
|
|
try { D3DDevice.Startup(); }
|
|
catch(Direct3D9NotFoundException) { AskDownloadDirectX(); return; }
|
|
catch(Direct3DX9NotFoundException) { AskDownloadDirectX(); return; }
|
|
|
|
// Load plugin manager
|
|
General.WriteLogLine("Loading plugins...");
|
|
plugins = new PluginManager();
|
|
plugins.LoadAllPlugins();
|
|
|
|
// Now that all settings have been combined (core & plugins) apply the defaults
|
|
General.WriteLogLine("Applying configuration settings...");
|
|
actions.ApplyDefaultShortcutKeys();
|
|
|
|
// Load game configurations
|
|
General.WriteLogLine("Loading game configurations...");
|
|
LoadAllGameConfigurations();
|
|
|
|
// Load compiler configurations
|
|
General.WriteLogLine("Loading compiler configurations...");
|
|
LoadAllCompilerConfigurations();
|
|
|
|
// Load nodebuilder configurations
|
|
General.WriteLogLine("Loading nodebuilder configurations...");
|
|
LoadAllNodebuilderConfigurations();
|
|
|
|
// Load color settings
|
|
General.WriteLogLine("Loading color settings...");
|
|
colors = new ColorCollection(settings.Config);
|
|
|
|
// Create application clock
|
|
General.WriteLogLine("Creating application clock...");
|
|
clock = new Clock();
|
|
|
|
// Create types manager
|
|
General.WriteLogLine("Creating types manager...");
|
|
types = new TypesManager();
|
|
|
|
// Do auto map loading when window is delayed
|
|
if(delaymainwindow)
|
|
mainwindow.PerformAutoMapLoading();
|
|
|
|
// Run application from the main window
|
|
General.WriteLogLine("Startup done");
|
|
mainwindow.DisplayReady();
|
|
Application.Run(mainwindow);
|
|
}
|
|
else
|
|
{
|
|
// Terminate
|
|
Terminate(false);
|
|
}
|
|
}
|
|
|
|
// This handles DLL linking errors
|
|
private static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
|
|
{
|
|
// Check if SlimDX failed loading
|
|
if(args.Name.Contains("SlimDX")) AskDownloadDirectX();
|
|
|
|
// Return null
|
|
return null;
|
|
}
|
|
|
|
// This asks the user to download DirectX
|
|
private static void AskDownloadDirectX()
|
|
{
|
|
// Cancel loading map from command-line parameters, if any.
|
|
// This causes problems, because when the window is shown, the map will
|
|
// be loaded and DirectX is initialized (which we seem to be missing)
|
|
autoloadfile = null;
|
|
|
|
// Ask the user to download DirectX
|
|
if(MessageBox.Show("This application requires the latest version of Microsoft DirectX installed on your computer." + Environment.NewLine +
|
|
"Do you want to install and/or update Microsoft DirectX now?", "DirectX Error", System.Windows.Forms.MessageBoxButtons.YesNo,
|
|
System.Windows.Forms.MessageBoxIcon.Exclamation) == System.Windows.Forms.DialogResult.Yes)
|
|
{
|
|
// Open DX web setup
|
|
//System.Diagnostics.Process.Start("http://www.microsoft.com/downloads/details.aspx?FamilyId=2DA43D38-DB71-4C1B-BC6A-9B6652CD92A3").WaitForExit(1000);
|
|
System.Diagnostics.Process.Start(Path.Combine(setuppath, "dxwebsetup.exe")).WaitForExit(1000);
|
|
}
|
|
|
|
// End program here
|
|
Terminate(false);
|
|
}
|
|
|
|
// This parses the command line arguments
|
|
private static void ParseCommandLineArgs(string[] args)
|
|
{
|
|
// Keep a copy
|
|
cmdargs = args;
|
|
|
|
// Make a queue so we can parse the values from left to right
|
|
Queue<string> argslist = new Queue<string>(args);
|
|
|
|
// Parse list
|
|
while(argslist.Count > 0)
|
|
{
|
|
// Get next arg
|
|
string curarg = argslist.Dequeue();
|
|
|
|
// Delay window?
|
|
if(string.Compare(curarg, "-DELAYWINDOW", true) == 0)
|
|
{
|
|
// Delay showing the main window
|
|
delaymainwindow = true;
|
|
}
|
|
// Map name info?
|
|
else if(string.Compare(curarg, "-MAP", true) == 0)
|
|
{
|
|
// Store next arg as map name information
|
|
autoloadmap = argslist.Dequeue();
|
|
}
|
|
// Config name info?
|
|
else if((string.Compare(curarg, "-CFG", true) == 0) ||
|
|
(string.Compare(curarg, "-CONFIG", true) == 0))
|
|
{
|
|
// Store next arg as config filename information
|
|
autoloadconfig = argslist.Dequeue();
|
|
}
|
|
// Every other arg
|
|
else
|
|
{
|
|
// No command to load file yet?
|
|
if(autoloadfile == null)
|
|
{
|
|
// Check if this is a file we can load
|
|
if(File.Exists(curarg))
|
|
{
|
|
// Load this file!
|
|
autoloadfile = curarg.Trim();
|
|
}
|
|
else
|
|
{
|
|
// Note in the log that we cannot find this file
|
|
General.WriteLogLine("WARNING: Cannot find the specified file \"" + curarg + "\"");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Terminate
|
|
|
|
// This is for plugins to use
|
|
public static void Exit(bool properexit)
|
|
{
|
|
// Plugin wants to exit nicely?
|
|
if(properexit)
|
|
{
|
|
// Close dialog forms first
|
|
while((Form.ActiveForm != mainwindow) && (Form.ActiveForm != null))
|
|
Form.ActiveForm.Close();
|
|
|
|
// Close main window
|
|
mainwindow.Close();
|
|
}
|
|
else
|
|
{
|
|
// Terminate, no questions asked
|
|
Terminate(true);
|
|
}
|
|
}
|
|
|
|
// This terminates the program
|
|
internal static void Terminate(bool properexit)
|
|
{
|
|
// Terminate properly?
|
|
if(properexit)
|
|
{
|
|
General.WriteLogLine("Termination requested");
|
|
|
|
// Unbind static methods from actions
|
|
General.Actions.UnbindMethods(typeof(General));
|
|
|
|
// Save colors
|
|
colors.SaveColors(settings.Config);
|
|
|
|
// Save action controls
|
|
actions.SaveSettings();
|
|
|
|
// Save game configuration settings
|
|
foreach(ConfigurationInfo ci in configs) ci.SaveSettings();
|
|
|
|
// Save settings configuration
|
|
General.WriteLogLine("Saving program configuration...");
|
|
settings.Save(Path.Combine(settingspath, SETTINGS_FILE));
|
|
|
|
// Clean up
|
|
if(map != null) map.Dispose(); map = null;
|
|
if(mainwindow != null) mainwindow.Dispose();
|
|
if(actions != null) actions.Dispose();
|
|
if(clock != null) clock.Dispose();
|
|
if(plugins != null) plugins.Dispose();
|
|
if(types != null) types.Dispose();
|
|
try { D3DDevice.Terminate(); } catch(Exception) { }
|
|
|
|
// Application ends here and now
|
|
General.WriteLogLine("Termination done");
|
|
Application.Exit();
|
|
}
|
|
else
|
|
{
|
|
// Just end now
|
|
General.WriteLogLine("Immediate program termination");
|
|
Application.Exit();
|
|
}
|
|
|
|
// Die.
|
|
Process.GetCurrentProcess().Kill();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Management
|
|
|
|
// This cancels a volatile mode, as if the user presses cancel
|
|
public static bool CancelVolatileMode()
|
|
{
|
|
// Volatile mode?
|
|
if((map != null) & (map.Mode != null) && map.Mode.Attributes.Volatile)
|
|
{
|
|
// Cancel
|
|
map.Mode.OnCancel();
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// Mode is not volatile
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// This disengages a volatile mode, leaving the choice to cancel or accept to the editing mode
|
|
public static bool DisengageVolatileMode()
|
|
{
|
|
// Volatile mode?
|
|
if((map != null) && (map.Mode != null) && map.Mode.Attributes.Volatile)
|
|
{
|
|
// Change back to normal mode
|
|
map.ChangeMode(map.PreviousStableMode.Name);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// Mode is not volatile
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// This creates a new map
|
|
[BeginAction("newmap")]
|
|
internal static void NewMap()
|
|
{
|
|
MapOptions newoptions = new MapOptions();
|
|
MapOptionsForm optionswindow;
|
|
|
|
// Cancel volatile mode, if any
|
|
General.DisengageVolatileMode();
|
|
|
|
// Ask the user to save changes (if any)
|
|
if(General.AskSaveMap())
|
|
{
|
|
// Open map options dialog
|
|
optionswindow = new MapOptionsForm(newoptions);
|
|
if(optionswindow.ShowDialog(mainwindow) == DialogResult.OK)
|
|
{
|
|
// Display status
|
|
mainwindow.DisplayStatus("Creating new map...");
|
|
Cursor.Current = Cursors.WaitCursor;
|
|
|
|
// Let the plugins know
|
|
plugins.OnMapNewBegin();
|
|
|
|
// Clear the display
|
|
mainwindow.ClearDisplay();
|
|
|
|
// Trash the current map, if any
|
|
if(map != null) map.Dispose();
|
|
|
|
// Create map manager with given options
|
|
map = new MapManager();
|
|
if(map.InitializeNewMap(newoptions))
|
|
{
|
|
// Done
|
|
}
|
|
else
|
|
{
|
|
// Unable to create map manager
|
|
map.Dispose();
|
|
map = null;
|
|
|
|
// Show splash logo on display
|
|
mainwindow.ShowSplashDisplay();
|
|
}
|
|
|
|
// Let the plugins know
|
|
plugins.OnMapNewEnd();
|
|
|
|
// All done
|
|
mainwindow.RedrawDisplay();
|
|
mainwindow.UpdateInterface();
|
|
mainwindow.HideInfo();
|
|
mainwindow.DisplayReady();
|
|
Cursor.Current = Cursors.Default;
|
|
}
|
|
}
|
|
}
|
|
|
|
// This closes the current map
|
|
[BeginAction("closemap")]
|
|
internal static void ActionCloseMap() { CloseMap(); }
|
|
internal static bool CloseMap()
|
|
{
|
|
// Cancel volatile mode, if any
|
|
General.DisengageVolatileMode();
|
|
|
|
// Ask the user to save changes (if any)
|
|
if(General.AskSaveMap())
|
|
{
|
|
// Display status
|
|
mainwindow.DisplayStatus("Closing map...");
|
|
General.WriteLogLine("Unloading map...");
|
|
Cursor.Current = Cursors.WaitCursor;
|
|
|
|
// Trash the current map
|
|
if(map != null) map.Dispose();
|
|
map = null;
|
|
|
|
// Show splash logo on display
|
|
mainwindow.ShowSplashDisplay();
|
|
|
|
// Done
|
|
Cursor.Current = Cursors.Default;
|
|
mainwindow.RedrawDisplay();
|
|
mainwindow.HideInfo();
|
|
mainwindow.UpdateInterface();
|
|
mainwindow.DisplayReady();
|
|
General.WriteLogLine("Map unload done");
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// User cancelled
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// This loads a map from file
|
|
[BeginAction("openmap")]
|
|
internal static void OpenMap()
|
|
{
|
|
OpenFileDialog openfile;
|
|
|
|
// Cancel volatile mode, if any
|
|
General.DisengageVolatileMode();
|
|
|
|
// Open map file dialog
|
|
openfile = new OpenFileDialog();
|
|
openfile.Filter = "Doom WAD Files (*.wad)|*.wad";
|
|
openfile.Title = "Open Map";
|
|
openfile.AddExtension = false;
|
|
openfile.CheckFileExists = true;
|
|
openfile.Multiselect = false;
|
|
openfile.ValidateNames = true;
|
|
if(openfile.ShowDialog(mainwindow) == DialogResult.OK)
|
|
{
|
|
// Update main window
|
|
mainwindow.Update();
|
|
|
|
// Open map file
|
|
OpenMapFile(openfile.FileName);
|
|
}
|
|
}
|
|
|
|
// This opens the specified file
|
|
internal static void OpenMapFile(string filename)
|
|
{
|
|
OpenMapOptionsForm openmapwindow;
|
|
|
|
// Cancel volatile mode, if any
|
|
General.DisengageVolatileMode();
|
|
|
|
// Ask the user to save changes (if any)
|
|
if(General.AskSaveMap())
|
|
{
|
|
// Open map options dialog
|
|
openmapwindow = new OpenMapOptionsForm(filename);
|
|
if(openmapwindow.ShowDialog(mainwindow) == DialogResult.OK)
|
|
OpenMapFileWithOptions(filename, openmapwindow.Options);
|
|
}
|
|
}
|
|
|
|
// This opens the specified file without dialog
|
|
internal static void OpenMapFileWithOptions(string filename, MapOptions options)
|
|
{
|
|
// Display status
|
|
mainwindow.DisplayStatus("Opening map file...");
|
|
Cursor.Current = Cursors.WaitCursor;
|
|
|
|
// Let the plugins know
|
|
plugins.OnMapOpenBegin();
|
|
|
|
// Clear the display
|
|
mainwindow.ClearDisplay();
|
|
|
|
// Trash the current map, if any
|
|
if(map != null) map.Dispose();
|
|
|
|
// Create map manager with given options
|
|
map = new MapManager();
|
|
if(map.InitializeOpenMap(filename, options))
|
|
{
|
|
// Add recent file
|
|
mainwindow.AddRecentFile(filename);
|
|
}
|
|
else
|
|
{
|
|
// Unable to create map manager
|
|
map.Dispose();
|
|
map = null;
|
|
|
|
// Show splash logo on display
|
|
mainwindow.ShowSplashDisplay();
|
|
}
|
|
|
|
// Let the plugins know
|
|
plugins.OnMapOpenEnd();
|
|
|
|
// All done
|
|
mainwindow.RedrawDisplay();
|
|
mainwindow.UpdateInterface();
|
|
mainwindow.HideInfo();
|
|
mainwindow.DisplayReady();
|
|
Cursor.Current = Cursors.Default;
|
|
}
|
|
|
|
// This saves the current map
|
|
// Returns tre when saved, false when cancelled or failed
|
|
[BeginAction("savemap")]
|
|
internal static void ActionSaveMap() { SaveMap(); }
|
|
internal static bool SaveMap()
|
|
{
|
|
bool result = false;
|
|
|
|
// Cancel volatile mode, if any
|
|
General.DisengageVolatileMode();
|
|
|
|
// Check if a wad file is known
|
|
if(map.FilePathName == "")
|
|
{
|
|
// Call to SaveMapAs
|
|
result = SaveMapAs();
|
|
}
|
|
else
|
|
{
|
|
// Display status
|
|
mainwindow.DisplayStatus("Saving map file...");
|
|
Cursor.Current = Cursors.WaitCursor;
|
|
|
|
// Save the map
|
|
if(map.SaveMap(map.FilePathName, MapManager.SAVE_NORMAL))
|
|
{
|
|
// Add recent file
|
|
mainwindow.AddRecentFile(map.FilePathName);
|
|
result = true;
|
|
}
|
|
|
|
// All done
|
|
mainwindow.UpdateInterface();
|
|
mainwindow.DisplayReady();
|
|
Cursor.Current = Cursors.Default;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
// This saves the current map as a different file
|
|
// Returns tre when saved, false when cancelled or failed
|
|
[BeginAction("savemapas")]
|
|
internal static void ActionSaveMapAs() { SaveMapAs(); }
|
|
internal static bool SaveMapAs()
|
|
{
|
|
SaveFileDialog savefile;
|
|
bool result = false;
|
|
|
|
// Cancel volatile mode, if any
|
|
General.DisengageVolatileMode();
|
|
|
|
// Show save as dialog
|
|
savefile = new SaveFileDialog();
|
|
savefile.Filter = "Doom WAD Files (*.wad)|*.wad";
|
|
savefile.Title = "Save Map As";
|
|
savefile.AddExtension = true;
|
|
savefile.CheckPathExists = true;
|
|
savefile.OverwritePrompt = true;
|
|
savefile.ValidateNames = true;
|
|
if(savefile.ShowDialog(mainwindow) == DialogResult.OK)
|
|
{
|
|
// Display status
|
|
mainwindow.DisplayStatus("Saving map file...");
|
|
Cursor.Current = Cursors.WaitCursor;
|
|
|
|
// Save the map
|
|
if(map.SaveMap(savefile.FileName, MapManager.SAVE_AS))
|
|
{
|
|
// Add recent file
|
|
mainwindow.AddRecentFile(map.FilePathName);
|
|
result = true;
|
|
}
|
|
|
|
// All done
|
|
mainwindow.UpdateInterface();
|
|
mainwindow.DisplayReady();
|
|
Cursor.Current = Cursors.Default;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
// This saves the current map as a different file
|
|
// Returns tre when saved, false when cancelled or failed
|
|
[BeginAction("savemapinto")]
|
|
internal static void ActionSaveMapInto() { SaveMapInto(); }
|
|
internal static bool SaveMapInto()
|
|
{
|
|
SaveFileDialog savefile;
|
|
bool result = false;
|
|
|
|
// Cancel volatile mode, if any
|
|
General.DisengageVolatileMode();
|
|
|
|
// Show save as dialog
|
|
savefile = new SaveFileDialog();
|
|
savefile.Filter = "Doom WAD Files (*.wad)|*.wad";
|
|
savefile.Title = "Save Map Into";
|
|
savefile.AddExtension = true;
|
|
savefile.CheckPathExists = true;
|
|
savefile.OverwritePrompt = false;
|
|
savefile.ValidateNames = true;
|
|
if(savefile.ShowDialog(mainwindow) == DialogResult.OK)
|
|
{
|
|
// Display status
|
|
mainwindow.DisplayStatus("Saving map file...");
|
|
Cursor.Current = Cursors.WaitCursor;
|
|
|
|
// Save the map
|
|
if(map.SaveMap(savefile.FileName, MapManager.SAVE_INTO))
|
|
{
|
|
// Add recent file
|
|
mainwindow.AddRecentFile(map.FilePathName);
|
|
result = true;
|
|
}
|
|
|
|
// All done
|
|
mainwindow.UpdateInterface();
|
|
mainwindow.DisplayReady();
|
|
Cursor.Current = Cursors.Default;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// This asks to save the map if needed
|
|
// Returns false when action was cancelled
|
|
internal static bool AskSaveMap()
|
|
{
|
|
DialogResult result;
|
|
|
|
// Map open and not saved?
|
|
if((map != null) && map.IsChanged)
|
|
{
|
|
// Ask to save changes
|
|
result = MessageBox.Show(mainwindow, "Do you want to save changes to " + map.FileTitle + " (" + map.Options.CurrentName + ")?", Application.ProductName, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
|
|
if(result == DialogResult.Yes)
|
|
{
|
|
// Save map and return true on success
|
|
return SaveMap();
|
|
}
|
|
else if(result == DialogResult.Cancel)
|
|
{
|
|
// Abort
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Continue
|
|
return true;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Debug
|
|
|
|
// This shows a major failure
|
|
public static void Fail(string message, string detailedmessage)
|
|
{
|
|
Debug.Fail(message, detailedmessage);
|
|
}
|
|
|
|
// This outputs log information
|
|
public static void WriteLogLine(string line)
|
|
{
|
|
// Output to console
|
|
Console.WriteLine(line);
|
|
|
|
// Write to log file
|
|
try { File.AppendAllText(logfile, line + Environment.NewLine); }
|
|
catch(Exception) { }
|
|
}
|
|
|
|
// This outputs log information
|
|
public static void WriteLog(string text)
|
|
{
|
|
// Output to console
|
|
Console.Write(text);
|
|
|
|
// Write to log file
|
|
try { File.AppendAllText(logfile, text); }
|
|
catch(Exception) { }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Tools
|
|
|
|
// This returns an element from a collection by index
|
|
public static T GetByIndex<T>(ICollection<T> collection, int index)
|
|
{
|
|
IEnumerator<T> e = collection.GetEnumerator();
|
|
for(int i = -1; i < index; i++) e.MoveNext();
|
|
return e.Current;
|
|
}
|
|
|
|
// This returns the next power of 2
|
|
public static int NextPowerOf2(int v)
|
|
{
|
|
int p = 0;
|
|
|
|
// Continue increasing until higher than v
|
|
while(Math.Pow(2, p) < v) p++;
|
|
|
|
// Return power
|
|
return (int)Math.Pow(2, p);
|
|
}
|
|
|
|
// Convert bool to integer
|
|
internal static int Bool2Int(bool v)
|
|
{
|
|
if(v) return 1; else return 0;
|
|
}
|
|
|
|
// Convert integer to bool
|
|
internal static bool Int2Bool(int v)
|
|
{
|
|
return (v != 0);
|
|
}
|
|
|
|
// This shows a message and logs the message
|
|
public static DialogResult ShowErrorMessage(string message, MessageBoxButtons buttons)
|
|
{
|
|
Cursor oldcursor;
|
|
DialogResult result;
|
|
|
|
// Log the message
|
|
WriteLogLine(message);
|
|
|
|
// Use normal cursor
|
|
oldcursor = Cursor.Current;
|
|
Cursor.Current = Cursors.Default;
|
|
|
|
// Show message
|
|
result = MessageBox.Show(Form.ActiveForm, message, Application.ProductName, buttons, MessageBoxIcon.Error);
|
|
|
|
// Restore old cursor
|
|
Cursor.Current = oldcursor;
|
|
|
|
// Return result
|
|
return result;
|
|
}
|
|
|
|
// This shows a message and logs the message
|
|
public static DialogResult ShowWarningMessage(string message, MessageBoxButtons buttons)
|
|
{
|
|
return ShowWarningMessage(message, buttons, MessageBoxDefaultButton.Button1);
|
|
}
|
|
|
|
// This shows a message and logs the message
|
|
public static DialogResult ShowWarningMessage(string message, MessageBoxButtons buttons, MessageBoxDefaultButton defaultbutton)
|
|
{
|
|
Cursor oldcursor;
|
|
DialogResult result;
|
|
|
|
// Log the message
|
|
WriteLogLine(message);
|
|
|
|
// Use normal cursor
|
|
oldcursor = Cursor.Current;
|
|
Cursor.Current = Cursors.Default;
|
|
|
|
// Show message
|
|
result = MessageBox.Show(Form.ActiveForm, message, Application.ProductName, buttons, MessageBoxIcon.Warning, defaultbutton);
|
|
|
|
// Restore old cursor
|
|
Cursor.Current = oldcursor;
|
|
|
|
// Return result
|
|
return result;
|
|
}
|
|
|
|
// This returns a unique temp filename
|
|
internal static string MakeTempFilename(string tempdir)
|
|
{
|
|
return MakeTempFilename(tempdir, "tmp");
|
|
}
|
|
|
|
// This returns a unique temp filename
|
|
internal static string MakeTempFilename(string tempdir, string extension)
|
|
{
|
|
string filename;
|
|
string chars = "abcdefghijklmnopqrstuvwxyz1234567890";
|
|
Random rnd = new Random();
|
|
int i;
|
|
|
|
do
|
|
{
|
|
// Generate a filename
|
|
filename = "";
|
|
for(i = 0; i < 8; i++) filename += chars[rnd.Next(chars.Length)];
|
|
filename = Path.Combine(tempdir, filename + "." + extension);
|
|
}
|
|
// Continue while file is not unique
|
|
while(File.Exists(filename) || Directory.Exists(filename));
|
|
|
|
// Return the filename
|
|
return filename;
|
|
}
|
|
|
|
// This returns a unique temp directory name
|
|
internal static string MakeTempDirname()
|
|
{
|
|
string dirname;
|
|
string chars = "abcdefghijklmnopqrstuvwxyz1234567890";
|
|
Random rnd = new Random();
|
|
int i;
|
|
|
|
do
|
|
{
|
|
// Generate a filename
|
|
dirname = "";
|
|
for(i = 0; i < 8; i++) dirname += chars[rnd.Next(chars.Length)];
|
|
dirname = Path.Combine(temppath, dirname);
|
|
}
|
|
// Continue while file is not unique
|
|
while(File.Exists(dirname) || Directory.Exists(dirname));
|
|
|
|
// Return the filename
|
|
return dirname;
|
|
}
|
|
|
|
// This shows an image in a panel either zoomed or centered depending on size
|
|
public static void DisplayZoomedImage(Panel panel, Image image)
|
|
{
|
|
// Set the image
|
|
panel.BackgroundImage = image;
|
|
|
|
// Image not null?
|
|
if(image != null)
|
|
{
|
|
// Small enough to fit in panel?
|
|
if((image.Size.Width < panel.ClientRectangle.Width) &&
|
|
(image.Size.Height < panel.ClientRectangle.Height))
|
|
{
|
|
// Display centered
|
|
panel.BackgroundImageLayout = ImageLayout.Center;
|
|
}
|
|
else
|
|
{
|
|
// Display zoomed
|
|
panel.BackgroundImageLayout = ImageLayout.Zoom;
|
|
}
|
|
}
|
|
}
|
|
|
|
// This calculates the new rectangle when one is scaled into another keeping aspect ratio
|
|
public static RectangleF MakeZoomedRect(Size source, RectangleF target)
|
|
{
|
|
return MakeZoomedRect(new SizeF((int)source.Width, (int)source.Height), target);
|
|
}
|
|
|
|
// This calculates the new rectangle when one is scaled into another keeping aspect ratio
|
|
public static RectangleF MakeZoomedRect(Size source, Rectangle target)
|
|
{
|
|
return MakeZoomedRect(new SizeF((int)source.Width, (int)source.Height),
|
|
new RectangleF((int)target.Left, (int)target.Top, (int)target.Width, (int)target.Height));
|
|
}
|
|
|
|
// This calculates the new rectangle when one is scaled into another keeping aspect ratio
|
|
public static RectangleF MakeZoomedRect(SizeF source, RectangleF target)
|
|
{
|
|
float scale;
|
|
|
|
// Image fits?
|
|
if((source.Width <= target.Width) &&
|
|
(source.Height <= target.Height))
|
|
{
|
|
// Just center
|
|
scale = 1.0f;
|
|
}
|
|
// Image is wider than tall?
|
|
else if((source.Width - target.Width) > (source.Height - target.Height))
|
|
{
|
|
// Scale down by width
|
|
scale = target.Width / source.Width;
|
|
}
|
|
else
|
|
{
|
|
// Scale down by height
|
|
scale = target.Height / source.Height;
|
|
}
|
|
|
|
// Return centered and scaled
|
|
return new RectangleF(target.Left + (target.Width - source.Width * scale) * 0.5f,
|
|
target.Top + (target.Height - source.Height * scale) * 0.5f,
|
|
source.Width * scale, source.Height * scale);
|
|
}
|
|
|
|
#endregion
|
|
|
|
[BeginAction("testaction")]
|
|
internal static void TestAction()
|
|
{
|
|
ThingEditForm t = new ThingEditForm();
|
|
t.ShowDialog(mainwindow);
|
|
t.Dispose();
|
|
}
|
|
}
|
|
}
|
|
|