/// <summary>
        /// Perform tasks upon form loading.
        /// </summary>
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            if (!File.Exists(ArchivePath))
            {
                MarathonMessageBox.Show
                (
                    "Please put 'player.arc' from your copy of SONIC THE HEDGEHOG into the root folder containing this application!",
                    "Missing Archive",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Warning
                );

                Close();

                return;
            }

            // Get script data.
            GetLuaScripts();

            // Create random seed.
            TextBoxDark_Seed.Text = RandomNumberGenerator.Next().ToString();
        }
        /// <summary>
        /// Writes the final changes made by the user.
        /// </summary>
        private void WriteFinalisedArchive()
        {
#if !DEBUG
            try
            {
#endif
            if (CheckBoxDark_CreateMod.Checked)
            {
                // Seed used for randomisation - use anyway, even if not randomised.
                string seed = TextBoxDark_Seed.Text.UseSafeFormattedCharacters(),

                // Root of the mod directory.
                       modRoot = Path.Combine(Program.WorkingDirectory, $"Character Swap ({seed})"),

                // Full path to archives for directory creation.
                       modFullPath = Path.Combine(modRoot, "core", "archives");

                // Create mod structure.
                Directory.CreateDirectory(modFullPath);

                // Write mod config with seed.
                ModHelper.WriteConfig(Path.Combine(modRoot, "mod.ini"), seed);

                // Save archive as mod with swapped characters.
                LoadedArchive.Save(Path.Combine(modFullPath, "player.arc"));
            }
            else
            {
                if (CheckBoxDark_OverwriteArchive.Checked)
                {
                    // Overwrite original archive with swapped characters.
                    LoadedArchive.Save(ArchivePath, true);
                }
                else
                {
                    // Write to new archive with swapped characters.
                    LoadedArchive.Save($"{ArchivePath}.swap", true);
                }
            }

            MarathonMessageBox.Show
            (
                "Finished processing your character swap - have fun!",
                "Processing Complete",
                MessageBoxButtons.OK,
                MessageBoxIcon.Information
            );
#if !DEBUG
        }

        catch (Exception ex)
        {
            MarathonMessageBox.Show
            (
                $"Failed to save archive data...\n\n{ex}",
                "Fatal Error",
                MessageBoxButtons.OK,
                MessageBoxIcon.Error
            );
        }
#endif
        }
        /// <summary>
        /// Process character swaps upon clicking.
        /// </summary>
        private void ButtonDark_Swap_Click(object sender, EventArgs e)
        {
            // Used for storage.
            ArchiveDirectory player = null;

            if (!File.Exists(ArchivePath))
            {
                MarathonMessageBox.Show
                (
                    "The original archive loaded has been removed - unable to decompress contents.",
                    "Missing Archive",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error
                );

                Close();

                return;
            }
#if !DEBUG
            try
            {
#endif
            // Decompress all data.
            LoadedArchive.Decompress(ref LoadedArchive.Data);
#if !DEBUG
        }
        catch
        {
            MarathonMessageBox.Show
            (
                "Failed to decompress 'player.arc' - the archive may be corrupt or invalid.",
                "Archive Error",
                MessageBoxButtons.OK,
                MessageBoxIcon.Error
            );

            Close();

            return;
        }
#endif
            // Check if 'xenon\player\' returns null.
            if ((player = LoadedArchive.JumpToDirectory(@"xenon\player\")) == null)
            {
                // If so, then check if 'ps3\player\' returns null.
                if ((player = LoadedArchive.JumpToDirectory(@"ps3\player\")) == null)
                {
                    MarathonMessageBox.Show
                    (
                        "Failed to locate the scripts directory...",
                        "Archive Error",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Error
                    );

                    Close();
                }
            }

            for (int i = 0; i < player.TotalContentsCount; i++)
            {
                ArchiveFile script = (ArchiveFile)player.Data[i];

                switch (script.Name.ToLower())
                {
                    /* Due to some backwards decisions from our good friends at Microsoft,
                     * DropDownLists cannot have pre-defined text and I'm not using standard DropDowns for this...
                     *
                     * I did warn you above. */

                    #region Characters
                case "sonic_new.lub":
                    ReplaceScriptData(script, ComboBoxDark_Sonic);
                    break;

                case "shadow.lub":
                    ReplaceScriptData(script, ComboBoxDark_Shadow);
                    break;

                case "silver.lub":
                    ReplaceScriptData(script, ComboBoxDark_Silver);
                    break;

                case "princess.lub":
                    ReplaceScriptData(script, ComboBoxDark_PrincessElise);
                    break;

                case "sonic_fast.lub":
                    ReplaceScriptData(script, ComboBoxDark_SonicMachSpeed);
                    break;

                case "supersonic.lub":
                    ReplaceScriptData(script, ComboBoxDark_SuperSonic);
                    break;

                case "supershadow.lub":
                    ReplaceScriptData(script, ComboBoxDark_SuperShadow);
                    break;

                case "supersilver.lub":
                    ReplaceScriptData(script, ComboBoxDark_SuperSilver);
                    break;

                case "amy.lub":
                    ReplaceScriptData(script, ComboBoxDark_Amy);
                    break;

                case "blaze.lub":
                    ReplaceScriptData(script, ComboBoxDark_Blaze);
                    break;

                case "knuckles.lub":
                    ReplaceScriptData(script, ComboBoxDark_Knuckles);
                    break;

                case "omega.lub":
                    ReplaceScriptData(script, ComboBoxDark_Omega);
                    break;

                case "rouge.lub":
                    ReplaceScriptData(script, ComboBoxDark_Rouge);
                    break;

                case "tails.lub":
                    ReplaceScriptData(script, ComboBoxDark_Tails);
                    break;
                    #endregion

                    #region Vehicles
                case "snow_board.lub":
                    ReplaceScriptData(script, ComboBoxDark_SnowboardCrisisCity);
                    break;

                case "snow_board_wap.lub":
                    ReplaceScriptData(script, ComboBoxDark_SnowboardWhiteAcropolis);
                    break;

                case "shadow_bike.lub":
                    ReplaceScriptData(script, ComboBoxDark_Bike);
                    break;

                case "shadow_glider.lub":
                    ReplaceScriptData(script, ComboBoxDark_Glider);
                    break;

                case "shadow_hover.lub":
                    ReplaceScriptData(script, ComboBoxDark_Hovercraft);
                    break;

                case "shadow_jeep.lub":
                    ReplaceScriptData(script, ComboBoxDark_Jeep);
                    break;

                case "shadow_none.lub":
                    ReplaceScriptData(script, ComboBoxDark_None);
                    break;
                    #endregion

                    #region Bosses
                case "sonic_boss_dr3.lub":
                    ReplaceScriptData(script, ComboBoxDark_EggWyvern);
                    break;

                case "silver_boss_dr2.lub":
                    ReplaceScriptData(script, ComboBoxDark_EggGenesis);
                    break;

                case "silver_boss_iblis1.lub":
                    ReplaceScriptData(script, ComboBoxDark_IblisPhase1);
                    break;

                case "silver_boss_iblis3.lub":
                    ReplaceScriptData(script, ComboBoxDark_IblisPhase3);
                    break;

                case "boss_sonic.lub":
                    ReplaceScriptData(script, ComboBoxDark_SonicBoss);
                    break;

                case "boss_shadow.lub":
                    ReplaceScriptData(script, ComboBoxDark_ShadowBoss);
                    break;

                case "boss_silver.lub":
                    ReplaceScriptData(script, ComboBoxDark_SilverBoss);
                    break;
                    #endregion

                    #region Artificial Intelligence
                case "npc_sonic.lub":
                    ReplaceScriptData(script, ComboBoxDark_SonicNPC);
                    break;

                case "npc_shadow.lub":
                    ReplaceScriptData(script, ComboBoxDark_ShadowNPC);
                    break;

                case "npc_silver.lub":
                    ReplaceScriptData(script, ComboBoxDark_SilverNPC);
                    break;

                case "npc_princess.lub":
                    ReplaceScriptData(script, ComboBoxDark_PrincessEliseNPC);
                    break;

                case "npc_supersonic.lub":
                    ReplaceScriptData(script, ComboBoxDark_SuperSonicNPC);
                    break;

                case "npc_supershadow.lub":
                    ReplaceScriptData(script, ComboBoxDark_SuperShadowNPC);
                    break;

                case "npc_supersilver.lub":
                    ReplaceScriptData(script, ComboBoxDark_SuperSilverNPC);
                    break;

                case "npc_amy.lub":
                    ReplaceScriptData(script, ComboBoxDark_AmyNPC);
                    break;

                case "npc_blaze.lub":
                    ReplaceScriptData(script, ComboBoxDark_BlazeNPC);
                    break;

                case "npc_knuckles.lub":
                    ReplaceScriptData(script, ComboBoxDark_KnucklesNPC);
                    break;

                case "npc_omega.lub":
                    ReplaceScriptData(script, ComboBoxDark_OmegaNPC);
                    break;

                case "npc_rouge.lub":
                    ReplaceScriptData(script, ComboBoxDark_RougeNPC);
                    break;

                case "npc_tails.lub":
                    ReplaceScriptData(script, ComboBoxDark_TailsNPC);
                    break;
                    #endregion

                    #region Character Select
                case "select_sonic.lub":
                    ReplaceScriptData(script, ComboBoxDark_SonicMenu);
                    break;

                case "select_shadow.lub":
                    ReplaceScriptData(script, ComboBoxDark_ShadowMenu);
                    break;

                case "select_silver.lub":
                    ReplaceScriptData(script, ComboBoxDark_SilverMenu);
                    break;

                case "select_amy.lub":
                    ReplaceScriptData(script, ComboBoxDark_AmyMenu);
                    break;

                case "select_blaze.lub":
                    ReplaceScriptData(script, ComboBoxDark_BlazeMenu);
                    break;

                case "select_knuckles.lub":
                    ReplaceScriptData(script, ComboBoxDark_KnucklesMenu);
                    break;

                case "select_omega.lub":
                    ReplaceScriptData(script, ComboBoxDark_OmegaMenu);
                    break;

                case "select_rouge.lub":
                    ReplaceScriptData(script, ComboBoxDark_RougeMenu);
                    break;

                case "select_tails.lub":
                    ReplaceScriptData(script, ComboBoxDark_TailsMenu);
                    break;
                    #endregion
                }
            }

            // Replace script data with the selected one.
            void ReplaceScriptData(ArchiveFile script, ComboBox comboBox)
            {
                // Find script entered into the combo box.
                ArchiveFile scriptToUse = Scripts.Find(x => x.Name == comboBox.Text);

                if (script.Name != scriptToUse.Name)
                {
                    // Create replacement file with the original name and replacement data.
                    var replacement = new U8Archive.U8ArchiveFile(scriptToUse)
                    {
                        Name = script.Name
                    };

                    // Replace the script with the requested replacement.
                    LoadedArchive.ReplaceFile(script, replacement);
                }
            }

            // Write swapped character archive.
            WriteFinalisedArchive();
        }
        /// <summary>
        /// Loads the archive and gets the valid character scripts.
        /// </summary>
        private void GetLuaScripts()
        {
            // List of names for all scripts in the archive.
            List <string> scriptNames = new List <string>();

#if !DEBUG
            try
            {
#endif
            // Load 'player.arc' into memory.
            LoadedArchive = new U8Archive(ArchivePath);
#if !DEBUG
        }
        catch
        {
            MarathonMessageBox.Show
            (
                "Failed to load 'player.arc' - the archive may be corrupt or invalid.",
                "Archive Error",
                MessageBoxButtons.OK,
                MessageBoxIcon.Error
            );

            Close();

            return;
        }
#endif
            // Get all player Lua scripts.
            Scripts.AddRange
            (
                LoadedArchive.GetFiles().Where(x => !ForbiddenScripts.Any(x.Name.Contains) && Path.GetExtension(x.Name) == ".lub")
            );

            // Add all script names to the list.
            foreach (ArchiveFile file in Scripts)
            {
                scriptNames.Add(file.Name);
            }

            // Populate all combo boxes.
            foreach (GroupContainer group in Controls.OfType <GroupContainer>())
            {
                PopulateComboBoxesInGroup(group, scriptNames);
            }

            // Set default combo box strings.
            foreach (ArchiveFile script in Scripts)
            {
                switch (script.Name.ToLower())
                {
                    /* Due to some backwards decisions from our good friends at Microsoft,
                     * DropDownLists cannot have pre-defined text and I'm not using standard DropDowns for this...
                     *
                     * You've been warned, this is one hefty switch statement, and I wont lie to you, there's another
                     * one just below all of this mess. */

                    #region Characters
                case "sonic_new.lub":
                    ComboBoxDark_Sonic.Text = script.Name;
                    break;

                case "shadow.lub":
                    ComboBoxDark_Shadow.Text = script.Name;
                    break;

                case "silver.lub":
                    ComboBoxDark_Silver.Text = script.Name;
                    break;

                case "princess.lub":
                    ComboBoxDark_PrincessElise.Text = script.Name;
                    break;

                case "sonic_fast.lub":
                    ComboBoxDark_SonicMachSpeed.Text = script.Name;
                    break;

                case "supersonic.lub":
                    ComboBoxDark_SuperSonic.Text = script.Name;
                    break;

                case "supershadow.lub":
                    ComboBoxDark_SuperShadow.Text = script.Name;
                    break;

                case "supersilver.lub":
                    ComboBoxDark_SuperSilver.Text = script.Name;
                    break;

                case "amy.lub":
                    ComboBoxDark_Amy.Text = script.Name;
                    break;

                case "blaze.lub":
                    ComboBoxDark_Blaze.Text = script.Name;
                    break;

                case "knuckles.lub":
                    ComboBoxDark_Knuckles.Text = script.Name;
                    break;

                case "omega.lub":
                    ComboBoxDark_Omega.Text = script.Name;
                    break;

                case "rouge.lub":
                    ComboBoxDark_Rouge.Text = script.Name;
                    break;

                case "tails.lub":
                    ComboBoxDark_Tails.Text = script.Name;
                    break;
                    #endregion

                    #region Vehicles
                case "snow_board.lub":
                    ComboBoxDark_SnowboardCrisisCity.Text = script.Name;
                    break;

                case "snow_board_wap.lub":
                    ComboBoxDark_SnowboardWhiteAcropolis.Text = script.Name;
                    break;

                case "shadow_bike.lub":
                    ComboBoxDark_Bike.Text = script.Name;
                    break;

                case "shadow_glider.lub":
                    ComboBoxDark_Glider.Text = script.Name;
                    break;

                case "shadow_hover.lub":
                    ComboBoxDark_Hovercraft.Text = script.Name;
                    break;

                case "shadow_jeep.lub":
                    ComboBoxDark_Jeep.Text = script.Name;
                    break;

                case "shadow_none.lub":
                    ComboBoxDark_None.Text = script.Name;
                    break;
                    #endregion

                    #region Bosses
                case "sonic_boss_dr3.lub":
                    ComboBoxDark_EggWyvern.Text = script.Name;
                    break;

                case "silver_boss_dr2.lub":
                    ComboBoxDark_EggGenesis.Text = script.Name;
                    break;

                case "silver_boss_iblis1.lub":
                    ComboBoxDark_IblisPhase1.Text = script.Name;
                    break;

                case "silver_boss_iblis3.lub":
                    ComboBoxDark_IblisPhase3.Text = script.Name;
                    break;

                case "boss_sonic.lub":
                    ComboBoxDark_SonicBoss.Text = script.Name;
                    break;

                case "boss_shadow.lub":
                    ComboBoxDark_ShadowBoss.Text = script.Name;
                    break;

                case "boss_silver.lub":
                    ComboBoxDark_SilverBoss.Text = script.Name;
                    break;
                    #endregion

                    #region Artificial Intelligence
                case "npc_sonic.lub":
                    ComboBoxDark_SonicNPC.Text = script.Name;
                    break;

                case "npc_shadow.lub":
                    ComboBoxDark_ShadowNPC.Text = script.Name;
                    break;

                case "npc_silver.lub":
                    ComboBoxDark_SilverNPC.Text = script.Name;
                    break;

                case "npc_princess.lub":
                    ComboBoxDark_PrincessEliseNPC.Text = script.Name;
                    break;

                case "npc_supersonic.lub":
                    ComboBoxDark_SuperSonicNPC.Text = script.Name;
                    break;

                case "npc_supershadow.lub":
                    ComboBoxDark_SuperShadowNPC.Text = script.Name;
                    break;

                case "npc_supersilver.lub":
                    ComboBoxDark_SuperSilverNPC.Text = script.Name;
                    break;

                case "npc_amy.lub":
                    ComboBoxDark_AmyNPC.Text = script.Name;
                    break;

                case "npc_blaze.lub":
                    ComboBoxDark_BlazeNPC.Text = script.Name;
                    break;

                case "npc_knuckles.lub":
                    ComboBoxDark_KnucklesNPC.Text = script.Name;
                    break;

                case "npc_omega.lub":
                    ComboBoxDark_OmegaNPC.Text = script.Name;
                    break;

                case "npc_rouge.lub":
                    ComboBoxDark_RougeNPC.Text = script.Name;
                    break;

                case "npc_tails.lub":
                    ComboBoxDark_TailsNPC.Text = script.Name;
                    break;
                    #endregion

                    #region Character Select
                case "select_sonic.lub":
                    ComboBoxDark_SonicMenu.Text = script.Name;
                    break;

                case "select_shadow.lub":
                    ComboBoxDark_ShadowMenu.Text = script.Name;
                    break;

                case "select_silver.lub":
                    ComboBoxDark_SilverMenu.Text = script.Name;
                    break;

                case "select_amy.lub":
                    ComboBoxDark_AmyMenu.Text = script.Name;
                    break;

                case "select_blaze.lub":
                    ComboBoxDark_BlazeMenu.Text = script.Name;
                    break;

                case "select_knuckles.lub":
                    ComboBoxDark_KnucklesMenu.Text = script.Name;
                    break;

                case "select_omega.lub":
                    ComboBoxDark_OmegaMenu.Text = script.Name;
                    break;

                case "select_rouge.lub":
                    ComboBoxDark_RougeMenu.Text = script.Name;
                    break;

                case "select_tails.lub":
                    ComboBoxDark_TailsMenu.Text = script.Name;
                    break;
                    #endregion
                }
            }

            // Populates the combo boxes in the requested group with the script names.
            void PopulateComboBoxesInGroup(GroupContainer group, List <string> scripts)
            {
                // Update all combo box strings.
                foreach (ComboBoxDark comboBox in group.WorkingArea.Controls.OfType <ComboBoxDark>())
                {
                    comboBox.Items.AddRange(scripts.ToArray());
                }
            }
        }