/// <summary>
        /// Ashita injection method.
        /// </summary>
        /// <param name="config"></param>
        /// <returns></returns>
        public static Boolean DoInjection(Configuration config)
        {
            // Ensure the configuration file exists..
            if (config == null || !File.Exists(config.FilePath))
            {
                Error("Invalid configuration file; could not launch.");
                return false;
            }

            // Obtain path to PlayOnline..
            var polPath = RegisteryHelper.GetValue<String>(String.Format("HKEY_LOCAL_MACHINE\\SOFTWARE\\PlayOnline{0}\\InstallFolder", (config.PolVersion == "JP") ? "" : config.PolVersion), "1000");
            if (string.IsNullOrEmpty(polPath))
            {
                Error("Failed to read PlayOnline path from registry.");
                return false;
            }

            // Build path to Final Fantasy registry settings..
            var ffxiPath = String.Format(
                "HKEY_LOCAL_MACHINE\\SOFTWARE\\PlayOnline{0}\\{1}\\FinalFantasyXI{2}",
                (config.PolVersion == "JP") ? "" : config.PolVersion,
                (config.PolVersion == "JP") ? "SQUARE" : "SquareEnix",
                config.TestServer ? "TestClient" : "");

            // Write FFXI window resolution..
            RegisteryHelper.SetValue(ffxiPath, "0001", config.ResolutionX);
            RegisteryHelper.SetValue(ffxiPath, "0002", config.ResolutionY);

            // Write FFXI background resolution..
            if (config.BackgroundX != -1 && config.BackgroundY != -1)
            {
                RegisteryHelper.SetValue(ffxiPath, "0003", config.BackgroundX);
                RegisteryHelper.SetValue(ffxiPath, "0004", config.BackgroundY);
            }

            // Write FFXI menu resolution..
            if (config.MenuX != -1 && config.MenuY != -1)
            {
                RegisteryHelper.SetValue(ffxiPath, "0037", config.MenuX);
                RegisteryHelper.SetValue(ffxiPath, "0038", config.MenuY);
            }

            // Write FFXI misc settings..
            RegisteryHelper.SetValue(ffxiPath, "0021", 1); // Hardware Mouse
            RegisteryHelper.SetValue(ffxiPath, "0034", (config.ShowBorder) ? 1 : 0);

            // Attempt to launch the game suspended..
            var bootFile = (config.BootFile.Length > 0) ? config.BootFile : polPath + "\\pol.exe";
            var procId = ManagedInjector.CreateSuspended(bootFile, String.Format("{0} {1}", bootFile, config.BootCommand));
            if (procId == 0)
            {
                var errorMessage = String.Format(
                    "Ashita failed to load the boot file.\r\n" +
                    "PlayOnline Path:\r\n{0}\r\n" +
                    "Last Error Code: {1}",
                    bootFile, Marshal.GetLastWin32Error().ToString()
                    );
                Error(errorMessage);
                return false;
            }

            // Create configuration mapping..
            var fileHandle = NativeMethods.CreateFileMapping(IntPtr.Zero, IntPtr.Zero, (int)NativeMethods.MemoryProtection.ReadWrite, 0, Marshal.SizeOf(typeof(AshitaSettings)), String.Format("AshitaMMFSettings_{0}", procId));
            if (fileHandle == IntPtr.Zero)
            {
                Error("Ashita failed to load configuration mapping.");
                return false;
            }

            // Map view of file into local space..
            var fileMapping = NativeMethods.MapViewOfFile(fileHandle, 0x001F, 0, 0, 0);
            if (fileMapping == IntPtr.Zero)
            {
                Error("Ashita failed to map view of configuration file.");
                return false;
            }

            // Write settings to MMF..
            var settings = new AshitaSettings
                {
                    InstallPath = AppDomain.CurrentDomain.BaseDirectory,
                    ConfigPath = config.FilePath,
                    Language = config.Language,
                    IsLoaded = false
                };
            Marshal.StructureToPtr(settings, fileMapping, true);

            // Inject Ashita into remote target..
            if (!ManagedInjector.InjectModule(procId, AppDomain.CurrentDomain.BaseDirectory + "\\Ashita Core.dll", false))
            {
                ManagedInjector.KillProcess(procId);
                Error("Ashita failed to inject into PlayOnline.");
                return false;
            }

            // Resume the game and play!
            ManagedInjector.ResumeProcess(procId);

            // Wait for Ashita to load our configurations..
            var loadedAttempts = 0;
            while (settings.IsLoaded == false)
            {
                if (loadedAttempts == 5)
                {
                    // Cleanup..
                    NativeMethods.UnmapViewOfFile(fileMapping);
                    NativeMethods.CloseHandle(fileHandle);
                    ManagedInjector.KillProcess(procId);
                    Error("Ashita failed to process the selected configuration.");
                    return false;
                }

                settings = (AshitaSettings)Marshal.PtrToStructure(fileMapping, typeof(AshitaSettings));
                Thread.Sleep(1000);
                loadedAttempts++;
            }

            // Cleanup..
            NativeMethods.UnmapViewOfFile(fileMapping);
            NativeMethods.CloseHandle(fileHandle);

            return true;
        }
        /// <summary>
        /// Command  handler when a config is being saved.
        /// </summary>
        private void SaveEditConfigClicked()
        {
            if ((this.SelectedConfig != null && this.Configurations != null))
            {
                // Edited a current configuration..
                this.TempConfig.Save();
            }
            else
            {
                // Saving a new configuration..
                var sfd = new SaveFileDialog
                    {
                        AddExtension = true,
                        CheckPathExists = true,
                        Filter = "XML Files (*.xml)|*.xml|All Files (*.*)|*.*",
                        FilterIndex = 0,
                        InitialDirectory = Path.GetFullPath(AppDomain.CurrentDomain.BaseDirectory + "\\Config\\Boot"),
                        OverwritePrompt = true,
                        RestoreDirectory = true,
                        Title = "Save configuration file..."
                    };

                if ((bool)sfd.ShowDialog(Application.Current.MainWindow))
                {
                    this.TempConfig.FilePath = sfd.FileName;
                    this.TempConfig.Save();
                }
            }

            // Cleanup..
            this.SelectedConfig = this.TempConfig = null;
            this.ConfigEditVisibility = Visibility.Hidden;

            // Reload configuration list..
            this._dataService.GetConfigurationFiles(
                (configs, error) =>
                    {
                        this.Configurations = (error == null) ?
                                                  new ObservableCollection<Configuration>(configs) :
                                                  new ObservableCollection<Configuration>();
                    });
        }
        /// <summary>
        /// Command handler when an existing config is being edited.
        /// </summary>
        private void EditConfigClicked()
        {
            if (this.SelectedConfig == null)
                return;

            this.TempConfig = (Configuration)this.SelectedConfig.Clone();
            this.ConfigEditVisibility = Visibility.Visible;
        }
 /// <summary>
 /// Command handler when a new config is being created.
 /// </summary>
 private void NewConfigClicked()
 {
     this.SelectedConfig = null;
     this.TempConfig = new Configuration();
     this.ConfigEditVisibility = Visibility.Visible;
 }
 /// <summary>
 /// Command handler when an edited config is being canceled.
 /// </summary>
 private void CancelEditConfigClicked()
 {
     this.TempConfig = null;
     this.ConfigEditVisibility = Visibility.Hidden;
 }