#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.Diagnostics; using System.Drawing; using System.IO; using System.Reflection; using System.Runtime.InteropServices; using System.Security.AccessControl; using System.Security.Principal; using System.Text; using System.Threading; using System.Windows.Forms; using CodeImp.DoomBuilder.Actions; using CodeImp.DoomBuilder.Config; using CodeImp.DoomBuilder.Data; using CodeImp.DoomBuilder.Editing; using CodeImp.DoomBuilder.IO; using CodeImp.DoomBuilder.Map; using CodeImp.DoomBuilder.Plugins; using CodeImp.DoomBuilder.Rendering; using CodeImp.DoomBuilder.Types; using CodeImp.DoomBuilder.Windows; using Microsoft.Win32; using SlimDX.Direct3D9; #endregion namespace CodeImp.DoomBuilder { public static class General { #region ================== API Declarations [DllImport("devil.dll")] private static extern void ilInit(); [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, uint 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); [DllImport("kernel32.dll")] internal extern static IntPtr LoadLibrary(string filename); [DllImport("kernel32.dll")] internal extern static bool FreeLibrary(IntPtr moduleptr); [DllImport("user32.dll")] internal static extern IntPtr CreateWindowEx(uint exstyle, string classname, string windowname, uint style, int x, int y, int width, int height, IntPtr parentptr, int menu, IntPtr instanceptr, string param); [DllImport("user32.dll")] internal static extern bool DestroyWindow(IntPtr windowptr); [DllImport("user32.dll")] internal static extern int SetWindowPos(IntPtr windowptr, int insertafterptr, int x, int y, int cx, int cy, int flags); [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern uint GetShortPathName([MarshalAs(UnmanagedType.LPTStr)] string longpath, [MarshalAs(UnmanagedType.LPTStr)]StringBuilder shortpath, uint buffersize); //[DllImport("user32.dll")] //internal static extern int SetScrollInfo(IntPtr windowptr, int bar, IntPtr scrollinfo, bool redraw); //[DllImport("user32.dll")] //internal static extern int GetScrollInfo(IntPtr windowptr, int bar, IntPtr scrollinfo); #endregion #region ================== Constants // SendMessage API internal const int WM_USER = 0x400; internal const int WM_SYSCOMMAND = 0x112; internal const int SC_KEYMENU = 0xF100; internal const int CB_SETITEMHEIGHT = 0x153; //internal const int CB_SHOWDROPDOWN = 0x14F; //internal const int EM_GETSCROLLPOS = WM_USER + 221; //internal const int EM_SETSCROLLPOS = WM_USER + 222; //internal const int SB_HORZ = 0; //internal const int SB_VERT = 1; //internal const int SB_CTL = 2; //internal const int SIF_RANGE = 0x1; //internal const int SIF_PAGE = 0x2; //internal const int SIF_POS = 0x4; //internal const int SIF_DISABLENOSCROLL = 0x8; //internal const int SIF_TRACKPOS = 0x16; //internal const int SIF_ALL = SIF_RANGE + SIF_PAGE + SIF_POS + SIF_TRACKPOS; // Files and Folders private const string SETTINGS_FILE = "GZBuilder.cfg"; private const string DEFAULT_SETTINGS_FILE = "GZBuilder.default.cfg"; //mxd private const string SETTINGS_DIR = "Doom Builder"; private const string LOG_FILE = "GZBuilder.log"; private const string GAME_CONFIGS_DIR = "Configurations"; private const string COMPILERS_DIR = "Compilers"; private const string PLUGINS_DIR = "Plugins"; private const string SCRIPTS_DIR = "Scripting"; private const string SCREENSHOTS_DIR = "Screenshots"; //mxd private const string SNIPPETS_DIR = "Snippets"; //mxd private const string SETUP_DIR = "Setup"; private const string SPRITES_DIR = "Sprites"; private const string HELP_FILE = "Refmanual.chm"; // SCROLLINFO structure /*internal struct ScrollInfo { public int size; // size of this structure public uint mask; // combination of SIF_ constants public int min; // minimum scrolling position public int max; // maximum scrolling position public uint page; // page size (scroll bar uses this value to determine the appropriate size of the proportional scroll box) public int pos; // position of the scroll box public int trackpos; // immediate position of a scroll box that the user is dragging }*/ #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 scriptspath; private static string snippetspath; //mxd private static string screenshotspath; //mxd private static string pluginspath; private static string spritespath; // Main objects private static Assembly thisasm; private static MainForm mainwindow; private static ProgramConfiguration settings; private static MapManager map; private static EditingManager editing; private static ActionManager actions; private static HintsManager hints; //mxd private static PluginManager plugins; private static ColorCollection colors; private static TypesManager types; private static Clock clock; private static ErrorLogger errorlogger; private static Mutex appmutex; // Configurations private static List configs; private static List compilers; private static List nodebuilders; private static Dictionary scriptconfigs; private static Dictionary compiledscriptconfigs; //mxd // States private static bool debugbuild; // Command line arguments private static string[] cmdargs; private static string autoloadfile; private static string autoloadmap; private static string autoloadconfig; private static bool autoloadstrictpatches; private static DataLocationList autoloadresources; private static bool delaymainwindow; private static bool nosettings; private static bool portablemode; //mxd //misc private static readonly Random random = new Random(); //mxd #endregion #region ================== Properties public 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; } } internal static string SettingsPath { get { return settingspath; } } //mxd internal static string LogFile { get { return logfile; } } //mxd public static string CompilersPath { get { return compilerspath; } } public static string PluginsPath { get { return pluginspath; } } public static string SpritesPath { get { return spritespath; } } public static string SnippetsPath { get { return snippetspath; } } //mxd public static string DefaultScreenshotsPath { get { return screenshotspath; } } //mxd public static ICollection CommandArgs { get { return Array.AsReadOnly(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 Configs { get { return configs; } } internal static List Nodebuilders { get { return nodebuilders; } } internal static List Compilers { get { return compilers; } } internal static Dictionary ScriptConfigs { get { return scriptconfigs; } } internal static Dictionary CompiledScriptConfigs { get { return compiledscriptconfigs; } } //mxd public static MapManager Map { get { return map; } } public static ActionManager Actions { get { return actions; } } public static HintsManager Hints { get { return hints; } } //mxd 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 AutoLoadStrictPatches { get { return autoloadstrictpatches; } } public static DataLocationList AutoLoadResources { get { return new DataLocationList(autoloadresources); } } public static bool DelayMainWindow { get { return delaymainwindow; } } public static bool NoSettings { get { return nosettings; } } public static EditingManager Editing { get { return editing; } } public static ErrorLogger ErrorLogger { get { return errorlogger; } } #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 private static Configuration LoadGameConfiguration(string filename) { // Make the full filepathname string filepathname = Path.Combine(configspath, filename); // Load configuration try { // Try loading the configuration Configuration cfg = new Configuration(filepathname, true); // Check for erors if(cfg.ErrorResult) { // Error in configuration errorlogger.Add(ErrorType.Error, "Unable to load the game configuration file \"" + filename + "\". " + "Error in file \"" + cfg.ErrorFile + "\" near line " + cfg.ErrorLine + ": " + cfg.ErrorDescription); return null; } // Check if this is a Doom Builder 2 config if(cfg.ReadSetting("type", "") != "Doom Builder 2 Game Configuration") { // Old configuration errorlogger.Add(ErrorType.Error, "Unable to load the game configuration file \"" + filename + "\". " + "This configuration is not a Doom Builder 2 game configuration."); return null; } // Return config return cfg; } catch(Exception e) { // Unable to load configuration errorlogger.Add(ErrorType.Error, "Unable to load the game configuration file \"" + filename + "\". " + e.GetType().Name + ": " + e.Message); General.WriteLog(e.StackTrace); return null; } } // This loads all game configurations private static void LoadAllGameConfigurations() { Configuration cfg; string fullfilename; // Display status mainwindow.DisplayStatus(StatusType.Busy, "Loading game configurations..."); // Make array configs = new List(); // Go for all cfg files in the configurations directory string[] 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 configs configs.Sort(); } // This loads all nodebuilder configurations private static void LoadAllNodebuilderConfigurations() { Configuration cfg; IDictionary builderslist; // Display status mainwindow.DisplayStatus(StatusType.Busy, "Loading nodebuilder configurations..."); // Make array nodebuilders = new List(); // Go for all cfg files in the compilers directory string[] filenames = Directory.GetFiles(compilerspath, "*.cfg", SearchOption.AllDirectories); foreach(string filepath in filenames) { try { // Try loading the configuration cfg = new Configuration(filepath, true); // Check for erors if(cfg.ErrorResult) { // Error in configuration errorlogger.Add(ErrorType.Error, "Unable to load the compiler configuration file \"" + Path.GetFileName(filepath) + "\". " + "Error in file \"" + cfg.ErrorFile + "\" near line " + cfg.ErrorLine + ": " + cfg.ErrorDescription); } 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 errorlogger.Add(ErrorType.Error, "Unable to load the nodebuilder configuration '" + de.Key + "' from \"" + Path.GetFileName(filepath) + "\". Error: " + e.Message); } } } } } catch(Exception) { // Unable to load configuration errorlogger.Add(ErrorType.Error, "Unable to load the compiler configuration file \"" + Path.GetFileName(filepath) + "\"."); } } // Sort the list nodebuilders.Sort(); } // This loads all script configurations private static void LoadAllScriptConfigurations() { Configuration cfg; string[] filenames; // Display status mainwindow.DisplayStatus(StatusType.Busy, "Loading script configurations..."); // Make collection scriptconfigs = new Dictionary(StringComparer.Ordinal); compiledscriptconfigs = new Dictionary(StringComparer.Ordinal); //mxd // Go for all cfg files in the scripts directory filenames = Directory.GetFiles(scriptspath, "*.cfg", SearchOption.TopDirectoryOnly); foreach(string filepath in filenames) { try { // Try loading the configuration cfg = new Configuration(filepath, true); // Check for erors if(cfg.ErrorResult) { // Error in configuration errorlogger.Add(ErrorType.Error, "Unable to load the script configuration file \"" + Path.GetFileName(filepath) + "\". " + "Error in file \"" + cfg.ErrorFile + "\" near line " + cfg.ErrorLine + ": " + cfg.ErrorDescription); } else { try { // Make script configuration ScriptConfiguration scfg = new ScriptConfiguration(cfg); string filename = Path.GetFileName(filepath); scriptconfigs.Add(filename.ToLowerInvariant(), scfg); //mxd. Store acc compilers in a separate dictionary if(scfg.ScriptType == ScriptType.ACS) compiledscriptconfigs.Add(filename.ToLowerInvariant(), scfg); } catch(Exception e) { // Unable to load configuration errorlogger.Add(ErrorType.Error, "Unable to load the script configuration \"" + Path.GetFileName(filepath) + "\". Error: " + e.Message); } } } catch(Exception e) { // Unable to load configuration errorlogger.Add(ErrorType.Error, "Unable to load the script configuration file \"" + Path.GetFileName(filepath) + "\". Error: " + e.Message); General.WriteLogLine(e.StackTrace); } } } // This loads all compiler configurations private static void LoadAllCompilerConfigurations() { Configuration cfg; Dictionary addedcompilers = new Dictionary(StringComparer.Ordinal); IDictionary compilerslist; string[] filenames; // Display status mainwindow.DisplayStatus(StatusType.Busy, "Loading compiler configurations..."); // Make array compilers = new List(); // Go for all cfg files in the compilers directory filenames = Directory.GetFiles(compilerspath, "*.cfg", SearchOption.AllDirectories); foreach(string filepath in filenames) { try { // Try loading the configuration cfg = new Configuration(filepath, true); // Check for erors if(cfg.ErrorResult) { // Error in configuration errorlogger.Add(ErrorType.Error, "Unable to load the compiler configuration file \"" + Path.GetFileName(filepath) + "\". " + "Error in file \"" + cfg.ErrorFile + "\" near line " + cfg.ErrorLine + ": " + cfg.ErrorDescription); } 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 CompilerInfo info = new CompilerInfo(Path.GetFileName(filepath), de.Key.ToString(), Path.GetDirectoryName(filepath), cfg); if(!addedcompilers.ContainsKey(info.Name)) { compilers.Add(info); addedcompilers.Add(info.Name, info); } else { errorlogger.Add(ErrorType.Error, "Compiler \"" + info.Name + "\" is defined more than once. The first definition in " + addedcompilers[info.Name].FileName + " will be used."); } } } } } catch(Exception e) { // Unable to load configuration errorlogger.Add(ErrorType.Error, "Unable to load the compiler configuration file \"" + Path.GetFileName(filepath) + "\". " + e.GetType().Name + ": " + e.Message); General.WriteLogLine(e.StackTrace); } } } // 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) { // Determine states #if DEBUG debugbuild = true; #else debugbuild = false; //mxd. Custom exception dialog. AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException; Application.ThreadException += Application_ThreadException; #endif // Enable OS visual styles Application.EnableVisualStyles(); Application.DoEvents(); // This must be here to work around a .NET bug ToolStripManager.Renderer = new ToolStripProfessionalRenderer(new TanColorTable()); // Hook to DLL loading failure event AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; // Set current thread name Thread.CurrentThread.Name = "Main Application"; // Application is running appmutex = new Mutex(false, "gzdoombuilder"); //"doombuilder2" // Get a reference to this assembly thisasm = Assembly.GetExecutingAssembly(); // Find application path //Uri localpath = new Uri(Path.GetDirectoryName(thisasm.GetName().CodeBase)); //apppath = Uri.UnescapeDataString(localpath.AbsolutePath); apppath = Path.GetDirectoryName(Application.ExecutablePath); //mxd. What was the point of using Uri here (other than to prevent lauching from a shared folder)? // Parse command-line arguments ParseCommandLineArgs(args); // Setup directories temppath = Path.GetTempPath(); setuppath = Path.Combine(apppath, SETUP_DIR); settingspath = (portablemode ? apppath : Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), SETTINGS_DIR)); //mxd configspath = Path.Combine(apppath, GAME_CONFIGS_DIR); compilerspath = Path.Combine(apppath, COMPILERS_DIR); pluginspath = Path.Combine(apppath, PLUGINS_DIR); scriptspath = Path.Combine(apppath, SCRIPTS_DIR); snippetspath = Path.Combine(apppath, SNIPPETS_DIR); //mxd screenshotspath = Path.Combine(apppath, SCREENSHOTS_DIR).Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); //mxd spritespath = Path.Combine(apppath, SPRITES_DIR); logfile = Path.Combine(settingspath, LOG_FILE); // Make program settings directory if missing if(!portablemode && !Directory.Exists(settingspath)) Directory.CreateDirectory(settingspath); // Remove the previous log file and start logging if(File.Exists(logfile)) File.Delete(logfile); General.WriteLogLine("GZDoom Builder R" + thisasm.GetName().Version.Revision + " startup"); //mxd General.WriteLogLine("Application path: " + apppath); General.WriteLogLine("Temporary path: " + temppath); General.WriteLogLine("Local settings path: " + settingspath); General.WriteLogLine("Command-line arguments: " + args.Length); for(int i = 0; i < args.Length; i++) General.WriteLogLine("Argument " + i + ": \"" + args[i] + "\""); // Load configuration General.WriteLogLine("Loading program configuration..."); settings = new ProgramConfiguration(); string defaultsettingsfile = Path.Combine(apppath, DEFAULT_SETTINGS_FILE); string usersettingsfile = nosettings ? defaultsettingsfile : Path.Combine(settingspath, SETTINGS_FILE); if(settings.Load(usersettingsfile, defaultsettingsfile)) { // Create error logger errorlogger = new ErrorLogger(); // Create action manager actions = new ActionManager(); // Bind static methods to actions General.Actions.BindMethods(typeof(General)); //mxd. Create hints manager hints = new HintsManager(); // Initialize static classes MapSet.Initialize(); ilInit(); // Create main window General.WriteLogLine("Loading main interface window..."); mainwindow = new MainForm(); mainwindow.SetupInterface(); mainwindow.UpdateInterface(); mainwindow.UpdateThingsFilters(); 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(); // Load game configurations General.WriteLogLine("Loading game configurations..."); LoadAllGameConfigurations(); // Create editing modes General.WriteLogLine("Creating editing modes manager..."); editing = new EditingManager(); // Now that all settings have been combined (core & plugins) apply the defaults General.WriteLogLine("Applying configuration settings..."); actions.ApplyDefaultShortcutKeys(); mainwindow.ApplyShortcutKeys(); foreach(ConfigurationInfo info in configs) info.ApplyDefaults(null); // Load compiler configurations General.WriteLogLine("Loading compiler configurations..."); LoadAllCompilerConfigurations(); // Load nodebuilder configurations General.WriteLogLine("Loading nodebuilder configurations..."); LoadAllNodebuilderConfigurations(); // Load script configurations General.WriteLogLine("Loading script configurations..."); LoadAllScriptConfigurations(); // 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(); //mxd. init gzdoom builder GZBuilder.GZGeneral.Init(); // Do auto map loading when window is delayed if(delaymainwindow) mainwindow.PerformAutoMapLoading(); // All done General.WriteLogLine("Startup done"); mainwindow.DisplayReady(); // Show any errors if preferred if(errorlogger.IsErrorAdded) { mainwindow.DisplayStatus(StatusType.Warning, "There were errors during program statup!"); if(!delaymainwindow && General.Settings.ShowErrorsWindow) mainwindow.ShowErrors(); } //mxd. Check enabled game configuration bool noneenabled = true; for(int i = 0; i < configs.Count; i++) { if(configs[i].Enabled) { noneenabled = false; break; } } if(noneenabled) { if(MessageBox.Show("No game configurations are currently enabled.\nPlease enable at least one game configuration", "Warning", MessageBoxButtons.OK) == DialogResult.OK) mainwindow.ShowConfiguration(); } //mxd. Check for updates? if(General.Settings.CheckForUpdates) UpdateChecker.PerformCheck(false); // Run application from the main window Application.Run(mainwindow); } else { // Terminate Terminate(false); } } // This handles DLL linking errors private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { // Check if SlimDX failed loading if(args.Name.Contains("SlimDX")) AskDownloadSlimDX(); // 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) CancelAutoMapLoad(); // Ask the user to download DirectX if(MessageBox.Show("This application requires the latest version of Microsoft DirectX 9.0 installed on your computer." + Environment.NewLine + "Do you want to install and/or update Microsoft DirectX 9.0 now?", "DirectX Error", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation) == DialogResult.Yes) { // Go to DirectX End-User Runtime Web Installer page (mxd) OpenWebsite("http://www.microsoft.com/en-us/download/details.aspx?id=35"); } // End program here Terminate(false); } // This asks the user to download SlimDX (mxd) private static void AskDownloadSlimDX() { // Cancel loading map from command-line parameters, if any. // This causes problems, because when the window is shown, the map will // be loaded and SlimDX is initialized (which we seem to be missing) CancelAutoMapLoad(); // Ask the user to download SlimDX if(MessageBox.Show("This application requires the latest version of SlimDX for .NET 2.0 installed on your computer." + Environment.NewLine + "Do you want to install SlimDX now?", "SlimDX Error", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation) == DialogResult.Yes) { // Go to SlimDX download page OpenWebsite("http://slimdx.org/download.php"); } // End program here Terminate(false); } // This parses the command line arguments private static void ParseCommandLineArgs(string[] args) { autoloadresources = new DataLocationList(); // Keep a copy cmdargs = args; // Make a queue so we can parse the values from left to right Queue argslist = new Queue(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; } // No settings? else if(string.Compare(curarg, "-NOSETTINGS", true) == 0) { // Don't load or save program settings nosettings = 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(); } // Strict patches rules? else if(string.Compare(curarg, "-STRICTPATCHES", true) == 0) { autoloadstrictpatches = true; } //mxd. Portable mode? else if(string.Compare(curarg, "-PORTABLE", true) == 0) { // Can we write stuff to apppath? try { WindowsIdentity identity = WindowsIdentity.GetCurrent(); if(identity != null) { WindowsPrincipal principal = new WindowsPrincipal(identity); DirectorySecurity security = Directory.GetAccessControl(apppath); AuthorizationRuleCollection authrules = security.GetAccessRules(true, true, typeof(SecurityIdentifier)); foreach(FileSystemAccessRule accessrule in authrules) { SecurityIdentifier id = accessrule.IdentityReference as SecurityIdentifier; if(id == null || !principal.IsInRole(id)) continue; if((FileSystemRights.WriteData & accessrule.FileSystemRights) != FileSystemRights.WriteData) continue; if(accessrule.AccessControlType == AccessControlType.Allow) { portablemode = true; } else if(accessrule.AccessControlType == AccessControlType.Deny) { //Deny usually overrides any Allow portablemode = false; break; } } } } catch(Exception) { } // Warn the user? if(!portablemode) ShowWarningMessage("Failed to enable portable mode.\nMake sure you have write premission for '" + apppath + "' directory.", MessageBoxButtons.OK); } // Resource? else if(string.Compare(curarg, "-RESOURCE", true) == 0) { DataLocation dl = new DataLocation(); // Parse resource type string resourcetype = argslist.Dequeue(); if(string.Compare(resourcetype, "WAD", true) == 0) dl.type = DataLocation.RESOURCE_WAD; else if(string.Compare(resourcetype, "DIR", true) == 0) dl.type = DataLocation.RESOURCE_DIRECTORY; else if(string.Compare(resourcetype, "PK3", true) == 0) dl.type = DataLocation.RESOURCE_PK3; else { General.WriteLogLine("Unexpected resource type \"" + resourcetype + "\" in program parameters. Expected \"wad\", \"dir\" or \"pk3\"."); break; } // We continue parsing args until an existing filename is found // all other arguments must be one of the optional keywords. while(string.IsNullOrEmpty(dl.location)) { curarg = argslist.Dequeue(); if((string.Compare(curarg, "ROOTTEXTURES", true) == 0) && (dl.type == DataLocation.RESOURCE_DIRECTORY)) { // Load images in the root directory of the resource as textures dl.option1 = true; } else if((string.Compare(curarg, "ROOTFLATS", true) == 0) && (dl.type == DataLocation.RESOURCE_DIRECTORY)) { // Load images in the root directory of the resource as flats dl.option2 = true; } else if((string.Compare(curarg, "STRICTPATCHES", true) == 0) && (dl.type == DataLocation.RESOURCE_WAD)) { // Use strict rules for patches dl.option1 = true; } else if(string.Compare(curarg, "NOTEST", true) == 0) { // Exclude this resource from testing parameters dl.notfortesting = true; } else { // This must be an existing file, or it is an invalid argument if(dl.type == DataLocation.RESOURCE_DIRECTORY) { if(Directory.Exists(curarg)) dl.location = curarg; } else { if(File.Exists(curarg)) dl.location = curarg; } if(string.IsNullOrEmpty(dl.location)) { General.WriteLogLine("Unexpected argument \"" + curarg + "\" in program parameters. Expected a valid resource option or a resource filename."); break; } } } // Add resource to list if(!string.IsNullOrEmpty(dl.location)) autoloadresources.Add(dl); } // 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("Cannot find the specified file \"" + curarg + "\""); } } } } } // This cancels automatic map loading internal static void CancelAutoMapLoad() { autoloadfile = null; } #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 if(!General.NoSettings) { General.WriteLogLine("Saving program configuration..."); settings.Save(Path.Combine(settingspath, SETTINGS_FILE)); } // Clean up if(map != null) map.Dispose(); map = null; if(editing != null) editing.Dispose(); editing = null; if(mainwindow != null) mainwindow.Dispose(); if(actions != null) actions.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 creates a new map [BeginAction("newmap")] internal static void NewMap() { MapOptions newoptions = new MapOptions(); // Cancel volatile mode, if any editing.DisengageVolatileMode(); // Ask the user to save changes (if any) if(AskSaveMap()) { // Open map options dialog MapOptionsForm optionswindow = new MapOptionsForm(newoptions, true); if(optionswindow.ShowDialog(mainwindow) == DialogResult.OK) { // Display status mainwindow.DisplayStatus(StatusType.Busy, "Creating new map..."); Cursor.Current = Cursors.WaitCursor; // Clear the display mainwindow.ClearDisplay(); mainwindow.RemoveHintsDocker(); //mxd // Trash the current map, if any if(map != null) map.Dispose(); // Let the plugins know plugins.OnMapNewBegin(); // Clear old errors (mxd) errorlogger.Clear(); // Create map manager with given options map = new MapManager(); if(map.InitializeNewMap(newoptions)) { settings.FindDefaultDrawSettings(); //mxd // Let the plugins know plugins.OnMapNewEnd(); // All done mainwindow.SetupInterface(); mainwindow.RedrawDisplay(); mainwindow.UpdateThingsFilters(); mainwindow.UpdateLinedefColorPresets(); //mxd mainwindow.UpdateInterface(); mainwindow.AddHintsDocker(); //mxd mainwindow.UpdateGZDoomPanel(); //mxd mainwindow.HideInfo(); //mxd } else { // Unable to create map manager map.Dispose(); map = null; // Show splash logo on display mainwindow.ShowSplashDisplay(); } if(errorlogger.IsErrorAdded) { // Show any errors if preferred mainwindow.DisplayStatus(StatusType.Warning, "There were errors during loading!"); if(!delaymainwindow && settings.ShowErrorsWindow) mainwindow.ShowErrors(); } else 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 editing.DisengageVolatileMode(); // Ask the user to save changes (if any) if(AskSaveMap()) { // Display status mainwindow.DisplayStatus(StatusType.Busy, "Closing map..."); WriteLogLine("Unloading map..."); Cursor.Current = Cursors.WaitCursor; // Trash the current map if(map != null) map.Dispose(); map = null; // Clear errors errorlogger.Clear(); //mxd. Clear Console #if DEBUG DebugConsole.Clear(); #endif // Show splash logo on display mainwindow.ShowSplashDisplay(); // Done Cursor.Current = Cursors.Default; editing.UpdateCurrentEditModes(); mainwindow.SetupInterface(); mainwindow.RedrawDisplay(); mainwindow.HideInfo(); mainwindow.UpdateThingsFilters(); //mxd mainwindow.UpdateLinedefColorPresets(); mainwindow.RemoveHintsDocker(); mainwindow.UpdateGZDoomPanel(); mainwindow.UpdateInterface(); mainwindow.DisplayReady(); WriteLogLine("Map unload done"); return true; } else { // User cancelled return false; } } // This loads a map from file [BeginAction("openmap")] internal static void OpenMap() { // Cancel volatile mode, if any editing.DisengageVolatileMode(); // Open map file dialog OpenFileDialog openfile = new OpenFileDialog(); openfile.Filter = "Doom WAD Files (*.wad)|*.wad"; openfile.Title = "Open Map"; if(!string.IsNullOrEmpty(settings.LastUsedMapFolder) && Directory.Exists(settings.LastUsedMapFolder)) //mxd openfile.InitialDirectory = settings.LastUsedMapFolder; //mxd 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, null); //mxd mainwindow.UpdateGZDoomPanel(); } openfile.Dispose(); } //mxd. This loads a different map from same wad file without reloading resources [BeginAction("openmapincurrentwad")] internal static void OpenMapInCurrentWad() { if (map == null || string.IsNullOrEmpty(map.FilePathName) || !File.Exists(map.FilePathName)) { Interface.DisplayStatus(StatusType.Warning, "Unable to open map from current WAD!"); return; } // Cancel volatile mode, if any Editing.DisengageVolatileMode(); // Ask the user to save changes (if any) if(!AskSaveMap()) return; // Open map options dialog ChangeMapForm changemapwindow = new ChangeMapForm(map.FilePathName, map.Options); if(changemapwindow.ShowDialog(mainwindow) != DialogResult.OK) return; // If resources don't match, perform regular map loading bool resourcesmismatch = changemapwindow.Options.Resources.Count != map.Options.Resources.Count; if (!resourcesmismatch) { for(int i = 0; i < changemapwindow.Options.Resources.Count; i++) { if(changemapwindow.Options.Resources[i].location != map.Options.Resources[i].location) { resourcesmismatch = true; break; } } } if(resourcesmismatch) { OpenMapFileWithOptions(map.FilePathName, changemapwindow.Options); return; } // Display status mainwindow.DisplayStatus(StatusType.Busy, "Switching to map '" + changemapwindow.Options.CurrentName + "'..."); WriteLogLine("Switching to map '" + changemapwindow.Options.CurrentName + "'..."); Cursor.Current = Cursors.WaitCursor; // Clear the display mainwindow.ClearDisplay(); mainwindow.RemoveHintsDocker(); //mxd //mxd. Close the script editor map.CloseScriptEditor(false); // Let the plugins know plugins.OnMapOpenBegin(); // Clear old errors ErrorLogger.Clear(); if(!map.InitializeSwitchMap(changemapwindow.Options)) return; // Clear undo history map.UndoRedo.ClearAllUndos(); map.UndoRedo.ClearAllRedos(); settings.FindDefaultDrawSettings(); //mxd // Let the plugins know plugins.OnMapOpenEnd(); // All done mainwindow.SetupInterface(); mainwindow.RedrawDisplay(); mainwindow.UpdateThingsFilters(); mainwindow.UpdateLinedefColorPresets(); //mxd mainwindow.UpdateInterface(); mainwindow.HideInfo(); mainwindow.AddHintsDocker(); //mxd mainwindow.UpdateGZDoomPanel(); //mxd if(errorlogger.IsErrorAdded) { // Show any errors if preferred mainwindow.DisplayStatus(StatusType.Warning, "There were errors during loading!"); if (!delaymainwindow && Settings.ShowErrorsWindow) mainwindow.ShowErrors(); } else { mainwindow.DisplayReady(); } Cursor.Current = Cursors.Default; } // This opens the specified file internal static void OpenMapFile(string filename, MapOptions options) { // Cancel volatile mode, if any editing.DisengageVolatileMode(); // Ask the user to save changes (if any) if(AskSaveMap()) { // Open map options dialog OpenMapOptionsForm openmapwindow = (options != null ? new OpenMapOptionsForm(filename, options) : 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(StatusType.Busy, "Opening map file..."); Cursor.Current = Cursors.WaitCursor; // Clear the display mainwindow.ClearDisplay(); mainwindow.RemoveHintsDocker(); //mxd // Trash the current map, if any if(map != null) map.Dispose(); // Let the plugins know plugins.OnMapOpenBegin(); // mxd. Clear old errors errorlogger.Clear(); // Create map manager with given options map = new MapManager(); if(map.InitializeOpenMap(filename, options)) { // Add recent file mainwindow.AddRecentFile(filename); //mxd mainwindow.UpdateGZDoomPanel(); settings.LastUsedMapFolder = Path.GetDirectoryName(filename); settings.FindDefaultDrawSettings(); // Let the plugins know plugins.OnMapOpenEnd(); // All done mainwindow.SetupInterface(); mainwindow.UpdateThingsFilters(); mainwindow.UpdateLinedefColorPresets(); //mxd mainwindow.UpdateInterface(); mainwindow.HideInfo(); mainwindow.AddHintsDocker(); //mxd //mxd. Center map in screen or on stored coordinates. Done here to avoid the view jerking around when updating the interface. if(Editing.Mode is ClassicMode) { ClassicMode mode = Editing.Mode as ClassicMode; if(options != null && options.ViewPosition.IsFinite() && !float.IsNaN(options.ViewScale)) mode.CenterOnCoordinates(options.ViewPosition, options.ViewScale); else mode.CenterInScreen(); } mainwindow.RedrawDisplay(); } else { // Unable to create map manager map.Dispose(); map = null; // Show splash logo on display mainwindow.ShowSplashDisplay(); } if(errorlogger.IsErrorAdded) { // Show any errors if preferred mainwindow.DisplayStatus(StatusType.Warning, "There were errors during loading!"); if(!delaymainwindow && settings.ShowErrorsWindow) mainwindow.ShowErrors(); } else 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() { if(map == null) return false; bool result = false; // Cancel volatile mode, if any editing.DisengageVolatileMode(); // Check if a wad file is known if(map.FilePathName == "") { // Call to SaveMapAs result = SaveMapAs(); } else { //mxd. Do we need to save the map? if (!map.MapSaveRequired(map.FilePathName, SavePurpose.Normal)) { // Still save settings file result = map.SaveSettingsFile(map.FilePathName); // Display status mainwindow.DisplayStatus(StatusType.Info, "Map is up to date. Updated map settings file."); // All done mainwindow.UpdateInterface(); return result; } // Display status mainwindow.DisplayStatus(StatusType.Busy, "Saving map file..."); Cursor.Current = Cursors.WaitCursor; // Set this to false so we can see if errors are added errorlogger.IsErrorAdded = false; // Save the map plugins.OnMapSaveBegin(SavePurpose.Normal); if(map.SaveMap(map.FilePathName, SavePurpose.Normal)) { // Add recent file mainwindow.AddRecentFile(map.FilePathName); result = true; } plugins.OnMapSaveEnd(SavePurpose.Normal); // All done mainwindow.UpdateInterface(); if(errorlogger.IsErrorAdded) { // Show any errors if preferred mainwindow.DisplayStatus(StatusType.Warning, "There were errors during saving!"); if(!delaymainwindow && settings.ShowErrorsWindow) mainwindow.ShowErrors(); } else mainwindow.DisplayStatus(StatusType.Info, "Map saved in " + map.FileTitle + "."); 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() { if(map == null) return false; bool result = false; // Cancel volatile mode, if any editing.DisengageVolatileMode(); // Show save as dialog SaveFileDialog 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; savefile.FileName = map.FileTitle; //mxd if(savefile.ShowDialog(mainwindow) == DialogResult.OK) { // Check if we're saving to the same file as the original. // Because some muppets use Save As even when saving to the same file. string currentfilename = (map.FilePathName.Length > 0) ? Path.GetFullPath(map.FilePathName).ToLowerInvariant() : ""; string savefilename = Path.GetFullPath(savefile.FileName).ToLowerInvariant(); if(currentfilename == savefilename) { SaveMap(); } else { // Display status mainwindow.DisplayStatus(StatusType.Busy, "Saving map file..."); Cursor.Current = Cursors.WaitCursor; // Set this to false so we can see if errors are added errorlogger.IsErrorAdded = false; // Save the map plugins.OnMapSaveBegin(SavePurpose.AsNewFile); if(map.SaveMap(savefile.FileName, SavePurpose.AsNewFile)) { // Add recent file mainwindow.AddRecentFile(map.FilePathName); settings.LastUsedMapFolder = Path.GetDirectoryName(map.FilePathName); //mxd result = true; } plugins.OnMapSaveEnd(SavePurpose.AsNewFile); // All done mainwindow.UpdateInterface(); if(errorlogger.IsErrorAdded) { // Show any errors if preferred mainwindow.DisplayStatus(StatusType.Warning, "There were errors during saving!"); if(!delaymainwindow && General.Settings.ShowErrorsWindow) mainwindow.ShowErrors(); } else mainwindow.DisplayStatus(StatusType.Info, "Map saved in " + map.FileTitle + "."); Cursor.Current = Cursors.Default; } } savefile.Dispose(); 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() { if(map == null) return false; bool result = false; // Cancel volatile mode, if any editing.DisengageVolatileMode(); // Show save as dialog SaveFileDialog 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(StatusType.Busy, "Saving map file..."); Cursor.Current = Cursors.WaitCursor; // Set this to false so we can see if errors are added errorlogger.IsErrorAdded = false; // Save the map plugins.OnMapSaveBegin(SavePurpose.IntoFile); if(map.SaveMap(savefile.FileName, SavePurpose.IntoFile)) { // Add recent file mainwindow.AddRecentFile(map.FilePathName); result = true; } plugins.OnMapSaveEnd(SavePurpose.IntoFile); // All done mainwindow.UpdateInterface(); if(errorlogger.IsErrorAdded) { // Show any errors if preferred mainwindow.DisplayStatus(StatusType.Warning, "There were errors during saving!"); if(!delaymainwindow && settings.ShowErrorsWindow) mainwindow.ShowErrors(); } else mainwindow.DisplayStatus(StatusType.Info, "Map saved into " + map.FileTitle + "."); Cursor.Current = Cursors.Default; } savefile.Dispose(); return result; } // This asks to save the map if needed // Returns false when action was cancelled internal static bool AskSaveMap() { // Map open and not saved? if(map != null) { if(map.IsChanged) { // Ask to save changes DialogResult 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 if(SaveMap()) { // Ask to save changes to scripts return map.AskSaveScriptChanges(); } else { // Failed to save map return false; } } else if(result == DialogResult.Cancel) { // Abort return false; } else { // Ask to save changes to scripts return map.AskSaveScriptChanges(); } } else { // Ask to save changes to scripts return map.AskSaveScriptChanges(); } } else { return true; } } #endregion #region ================== Debug // This shows a major failure public static void Fail(string message) { WriteLogLine("FAIL: " + message); #if DEBUG Debug.Fail(message); #else //mxd. Lets notify the user about our Epic Failure before crashing... ShowErrorMessage(message, MessageBoxButtons.OK); #endif Terminate(false); } // This outputs log information public static void WriteLogLine(string line) { #if DEBUG // Output to consoles Console.WriteLine(line); DebugConsole.WriteLine(DebugMessageType.LOG, line); //mxd #endif // Write to log file try { File.AppendAllText(logfile, line + Environment.NewLine); } catch(Exception) { } } // This outputs log information public static void WriteLog(string text) { #if DEBUG // Output to consoles Console.Write(text); DebugConsole.Write(DebugMessageType.LOG, text); #endif // Write to log file try { File.AppendAllText(logfile, text); } catch(Exception) { } } #endregion #region ================== Tools // This swaps two pointers public static void Swap(ref T a, ref T b) { T t = a; a = b; b = t; } // This calculates the bits needed for a number public static int BitsForInt(int v) { int[] LOGTABLE = new[] { 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 }; int r; // r will be lg(v) int t, tt; if(Int2Bool(tt = v >> 16)) { r = Int2Bool(t = tt >> 8) ? 24 + LOGTABLE[t] : 16 + LOGTABLE[tt]; } else { r = Int2Bool(t = v >> 8) ? 8 + LOGTABLE[t] : LOGTABLE[v]; } return r; } // This clamps a value public static float Clamp(float value, float min, float max) { return Math.Min(Math.Max(min, value), max); } // This clamps a value public static int Clamp(int value, int min, int max) { return Math.Min(Math.Max(min, value), max); } // This clamps a value public static byte Clamp(byte value, byte min, byte max) { return Math.Min(Math.Max(min, value), max); } //mxd. This clamps angle between 0 and 359 public static int ClampAngle(int angle) { return (angle + 360) % 360; } //mxd. This clamps angle between 0 and 359 public static float ClampAngle(float angle) { return (angle + 360) % 360; } //mxd public static int Random(int min, int max) { return random.Next(min, max + 1); //because max is never rolled } //mxd public static float Random(float min, float max) { return (float)Math.Round(min + (max - min) * random.NextDouble(), 2); } // This returns an element from a collection by index public static T GetByIndex(ICollection collection, int index) { IEnumerator 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) { return v ? 1 : 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) { // Log the message WriteLogLine(message); // Use normal cursor Cursor oldcursor = Cursor.Current; Cursor.Current = Cursors.Default; // Show message IWin32Window window = null; if((Form.ActiveForm != null) && Form.ActiveForm.Visible) window = Form.ActiveForm; DialogResult result = MessageBox.Show(window, 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) { // Log the message WriteLogLine(message); // Use normal cursor Cursor oldcursor = Cursor.Current; Cursor.Current = Cursors.Default; // Show message IWin32Window window = null; if((Form.ActiveForm != null) && Form.ActiveForm.Visible) window = Form.ActiveForm; DialogResult result = MessageBox.Show(window, message, Application.ProductName, buttons, MessageBoxIcon.Warning, defaultbutton); // Restore old cursor Cursor.Current = oldcursor; // Return result return result; } // This shows the reference manual public static void ShowHelp(string pagefile) { ShowHelp(pagefile, HELP_FILE); } // This shows the reference manual public static void ShowHelp(string pagefile, string chmfile) { // Check if the file can be found in the root string filepathname = Path.Combine(apppath, chmfile); if(!File.Exists(filepathname)) { // Check if the file exists in the plugins directory filepathname = Path.Combine(pluginspath, chmfile); if(!File.Exists(filepathname)) { // Fail WriteLogLine("ERROR: Can't find the help file \"" + chmfile + "\""); return; } } // Show help file Help.ShowHelp(mainwindow, filepathname, HelpNavigator.Topic, pagefile); } // 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; const string chars = "abcdefghijklmnopqrstuvwxyz1234567890"; int i; do { // Generate a filename filename = ""; for(i = 0; i < 8; i++) filename += chars[Random(0, chars.Length - 1)]; 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; const string chars = "abcdefghijklmnopqrstuvwxyz1234567890"; int i; do { // Generate a filename dirname = ""; for(i = 0; i < 8; i++) dirname += chars[Random(0, chars.Length - 1)]; 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) { // Image not null? if(image != null) { // Set the image panel.BackgroundImage = image; // 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(source.Width, 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(source.Width, source.Height), new RectangleF(target.Left, target.Top, target.Width, 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); } // This opens a URL in the default browser public static void OpenWebsite(string url) { RegistryKey key = null; Process p = null; string browser; try { // Get the registry key where default browser is stored key = Registry.ClassesRoot.OpenSubKey(@"HTTP\shell\open\command", false); // Trim off quotes browser = key.GetValue(null).ToString().ToLower().Replace("\"", ""); // String doesnt end in EXE? if(!browser.EndsWith("exe")) { // Get rid of everything after the ".exe" browser = browser.Substring(0, browser.LastIndexOf(".exe") + 4); } } finally { // Clean up if(key != null) key.Close(); } try { // Fork a process p = new Process(); p.StartInfo.FileName = browser; p.StartInfo.Arguments = url; p.Start(); } catch(Exception) { } // Clean up if(p != null) p.Dispose(); } // This returns the short path name for a file public static string GetShortFilePath(string longpath) { const int maxlen = 256; StringBuilder shortname = new StringBuilder(maxlen); GetShortPathName(longpath, shortname, maxlen); return shortname.ToString(); } #endregion #region ================== mxd. Uncaught exceptions handling private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e) { try { GZBuilder.Windows.ExceptionDialog dlg = new GZBuilder.Windows.ExceptionDialog(e); dlg.Setup(); if(dlg.ShowDialog() == DialogResult.Cancel) Application.Exit(); } catch { try { MessageBox.Show("Fatal Windows Forms Error", "Fatal Windows Forms Error", MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Stop); } finally { Application.Exit(); } } } private static void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs e) { string exceptionmsg = string.Empty; try { Exception ex = (Exception)e.ExceptionObject; exceptionmsg = "An application error occurred: " + ex.Message + "\n\nStack Trace:\n" + ex.StackTrace; // Since we can't prevent the app from terminating, log this to the event log. try { if (!EventLog.SourceExists("ThreadException")) EventLog.CreateEventSource("ThreadException", "Application"); // Create an EventLog instance and assign its source. using (EventLog myLog = new EventLog()) { myLog.Source = "ThreadException"; myLog.WriteEntry(exceptionmsg); } } catch (Exception exc) { MessageBox.Show("Could not write the error to the event log.\nReason: " + exc.Message + "\n\nInitial exception:\n" + exceptionmsg, "Fatal Non-UI Error", MessageBoxButtons.OK, MessageBoxIcon.Stop); } GZBuilder.Windows.ExceptionDialog dlg = new GZBuilder.Windows.ExceptionDialog(e); dlg.Setup(); dlg.ShowDialog(); } catch(Exception exc) { try { MessageBox.Show("Failed to write the error to the event log or to show the Exception Dialog.\n\nReason: " + exc.Message + "\n\nInitial exception:\n" + exceptionmsg, "Fatal Non-UI Error", MessageBoxButtons.OK, MessageBoxIcon.Stop); } finally { Application.Exit(); } } } #endregion } }