void initialize()
 {
     _invalidError        = _invalidError.Replace("{0}", "TIE");
     _endOfMissionIndexer = new Indexer <string>(_endOfMissionMessages, 63);
     FlightGroups         = new FlightGroupCollection();
     FlightGroupsBriefing = new FlightGroupCollection();
     Briefing             = new Briefing();
 }
        /// <summary>Saves the mission to <see cref="MissionFile.MissionPath"/></summary>
        /// <exception cref="UnauthorizedAccessException">Write permissions for <see cref="MissionFile.MissionPath"/> are denied</exception>
        public void Save()
        {
            //[JB] Added backup logic.  See the TIE Save() function for comments.
            if (File.Exists(MissionPath) && (File.GetAttributes(MissionPath) & FileAttributes.ReadOnly) != 0)
            {
                throw new UnauthorizedAccessException("Cannot save, existing file is read-only.");
            }

            FileStream fs = null;
            string     backup = MissionPath.ToLower().Replace(".xwi", "_xwi.bak");
            bool       backupCreated = false, writerCreated = false;

            if (File.Exists(MissionPath) && MissionPath.ToLower() != backup)
            {
                try
                {
                    if (File.Exists(backup))
                    {
                        File.Delete(backup);
                    }
                    File.Copy(MissionPath, backup);
                    backupCreated = true;
                }
                catch { }
            }
            try
            {
                if (File.Exists(MissionPath))
                {
                    File.Delete(MissionPath);
                }
                fs = File.OpenWrite(MissionPath);
                BinaryWriter bw = new BinaryWriter(fs, System.Text.Encoding.GetEncoding(437)); //[JB] Changed encoding to IBM437 (OEM United States) to properly handle the DOS ASCII character set.
                writerCreated = true;
                bw.Write((short)0x2);                                                          //Platform
                bw.Write(TimeLimitMinutes);
                bw.Write(EndEvent);
                bw.Write(RndSeed);
                bw.Write(Location);
                for (int i = 0; i < 3; i++)
                {
                    long p = fs.Position;
                    bw.Write(_endOfMissionMessages[i].ToCharArray()); bw.Write('\0');
                    fs.Position = p + 0x40;
                }
                int numFG = 0;
                int numOG = 0;
                for (int i = 0; i < FlightGroups.Count; i++)
                {
                    if (FlightGroups[i].CraftType > 0)
                    {
                        numFG++;
                    }
                    if (FlightGroups[i].ObjectType > 0)
                    {
                        numOG++;
                    }
                }
                bw.Write((short)numFG);
                bw.Write((short)numOG);


                #region Flightgroups
                for (int i = 0; i < FlightGroups.Count; i++)
                {
                    if (FlightGroups[i].CraftType == 0)
                    {
                        continue;
                    }

                    long p = fs.Position;

                    #region Craft
                    bw.Write(FlightGroups[i].Name.ToCharArray());
                    fs.Position = p + 0x10;
                    bw.Write(FlightGroups[i].Cargo.ToCharArray());
                    fs.Position = p + 0x20;
                    bw.Write(FlightGroups[i].SpecialCargo.ToCharArray());
                    fs.Position = p + 0x30;
                    bw.Write((short)FlightGroups[i].SpecialCargoCraft);
                    bw.Write((short)FlightGroups[i].CraftType);
                    bw.Write((short)FlightGroups[i].IFF);
                    bw.Write((short)FlightGroups[i].Status1);
                    bw.Write((short)FlightGroups[i].NumberOfCraft);
                    bw.Write((short)FlightGroups[i].NumberOfWaves);
                    #endregion
                    #region Arr/Dep
                    bw.Write((short)FlightGroups[i].ArrivalEvent);
                    bw.Write((short)FlightGroups[i].ArrivalDelay);
                    bw.Write((short)FlightGroups[i].ArrivalFG);
                    bw.Write((short)FlightGroups[i].Mothership);
                    bw.Write((short)FlightGroups[i].ArrivalHyperspace);
                    bw.Write((short)FlightGroups[i].DepartureHyperspace);
                    #endregion

                    //Waypoints (7 real waypoints, rest are virtualized BRF coordinate sets)
                    for (int j = 0; j < 4; j++)
                    {
                        for (int k = 0; k < 7; k++)
                        {
                            bw.Write(FlightGroups[i].Waypoints[k][j] /* * (j == 1 ? -1 : 1))*/);
                        }
                    }

                    //More craft info
                    bw.Write((short)FlightGroups[i].Formation);
                    bw.Write((short)FlightGroups[i].PlayerCraft);
                    bw.Write((short)FlightGroups[i].AI);
                    bw.Write(FlightGroups[i].Order);
                    bw.Write(FlightGroups[i].DockTimeThrottle);
                    bw.Write((short)FlightGroups[i].Markings);
                    bw.Write((short)FlightGroups[i].Markings);                      //Was Unknown1. Another color value. Official missions always have the same color.
                    bw.Write(FlightGroups[i].Objective);
                    bw.Write(FlightGroups[i].TargetPrimary);
                    bw.Write(FlightGroups[i].TargetSecondary);
                }
                // OBJECT GROUPS
                for (int i = 0; i < FlightGroups.Count; i++)
                {
                    if (FlightGroups[i].ObjectType == 0)
                    {
                        continue;
                    }

                    long p = fs.Position;

                    bw.Write(FlightGroups[i].Name.ToCharArray());
                    fs.Position = p + 0x10;
                    bw.Write(FlightGroups[i].Cargo.ToCharArray());
                    fs.Position = p + 0x20;
                    bw.Write(FlightGroups[i].SpecialCargo.ToCharArray());
                    fs.Position = p + 0x30;
                    bw.Write(FlightGroups[i].SpecialCargoCraft);

                    bw.Write(FlightGroups[i].ObjectType);
                    bw.Write((short)FlightGroups[i].IFF);
                    //The format begins to deviate here
                    bw.Write((short)FlightGroups[i].Formation);
                    bw.Write((short)FlightGroups[i].NumberOfCraft);
                    bw.Write(FlightGroups[i].Waypoints[0][0]);
                    bw.Write(FlightGroups[i].Waypoints[0][1]);
                    bw.Write(FlightGroups[i].Waypoints[0][2]);
                    bw.Write(FlightGroups[i].Yaw);                     //Conversion to/from degrees handled in the editor. This helps preserve the exact values used by pilot proving ground platforms.
                    bw.Write(FlightGroups[i].Pitch);
                    bw.Write(FlightGroups[i].Roll);
                }
                #endregion
                fs.SetLength(fs.Position);
                fs.Close();
            }
            catch
            {
                if (fs != null)
                {
                    fs.Close();
                }
                if (writerCreated && backupCreated)
                {
                    File.Delete(MissionPath);
                    File.Copy(backup, MissionPath);
                    File.Delete(backup);
                }
                throw;
            }
            if (backupCreated)
            {
                File.Delete(backup);
            }

            //Finished saving XWI file.  Now save the BRF file.
            string BriefingPath = MissionPath;
            bool   upper;                                           //This stuff is merely to try and make the BRF extension match the case of the XWI, so the file names look nice and tidy.
            int    extPos = BriefingPath.LastIndexOf('.');
            if (extPos >= 0 && extPos < BriefingPath.Length - 1)
            {
                upper        = char.IsUpper(BriefingPath[extPos + 1]);           //Detect case from the first character of the extension.
                BriefingPath = BriefingPath.Remove(extPos + 1);                  //Strip extension so a new one can be added.
            }
            else
            {
                upper         = char.IsUpper(BriefingPath[BriefingPath.Length - 1]);           //If for some reason the file has no extension, detect from the last character of the name.
                BriefingPath += ".";
            }
            BriefingPath += upper ? "BRF" : "brf";

            if (File.Exists(BriefingPath) && (File.GetAttributes(BriefingPath) & FileAttributes.ReadOnly) != 0)
            {
                throw new UnauthorizedAccessException("Cannot save briefing, existing file is read-only.");
            }

            fs            = null;
            backup        = BriefingPath.ToLower().Replace(".brf", "_brf.bak");
            backupCreated = false; writerCreated = false;

            if (File.Exists(BriefingPath) && BriefingPath.ToLower() != backup)
            {
                try
                {
                    if (File.Exists(backup))
                    {
                        File.Delete(backup);
                    }
                    File.Copy(BriefingPath, backup);
                    backupCreated = true;
                }
                catch { }
            }
            try
            {
                if (File.Exists(BriefingPath))
                {
                    File.Delete(BriefingPath);
                }
                fs = File.OpenWrite(BriefingPath);
                BinaryWriter bw = new BinaryWriter(fs, System.Text.Encoding.GetEncoding(437)); //[JB] Changed encoding to IBM437 (OEM United States) to properly handle the DOS ASCII character set.
                writerCreated = true;
                bw.Write((short)2);                                                            //Version
                bw.Write((short)FlightGroupsBriefing.Count);
                bw.Write(Briefing.MaxCoordSet);                                                //Coordinate count;
                long p  = 0;
                int  wp = 0;
                for (int i = 0; i < Briefing.MaxCoordSet; i++)                  //Coordinate count
                {
                    //Just in case there too many coord sets than what the editor allows, read them but only load them if the indexes are valid.
                    if (i == 0)
                    {
                        wp = 0;                              //SP1
                    }
                    else
                    {
                        wp = 7 + i - 1;                          //CS1 starts at [7], but at this point i==1 so subtract to compensate
                    }
                    if (wp >= 10)
                    {
                        wp = -1;
                    }

                    for (int j = 0; j < FlightGroupsBriefing.Count; j++)
                    {
                        for (int k = 0; k < 3; k++)
                        {
                            short dat = 0;
                            if (wp >= 0)
                            {
                                dat = FlightGroupsBriefing[j].Waypoints[wp][k];
                            }

                            bw.Write(dat);
                        }
                    }
                }
                for (int i = 0; i < FlightGroupsBriefing.Count; i++)
                {
                    if (FlightGroupsBriefing[i].IsFlightGroup())
                    {
                        bw.Write((short)FlightGroupsBriefing[i].CraftType);
                    }
                    else
                    {
                        bw.Write(FlightGroupsBriefing[i].ObjectType);
                    }

                    bw.Write((short)FlightGroupsBriefing[i].IFF);
                    bw.Write((short)FlightGroupsBriefing[i].NumberOfCraft);
                    bw.Write((short)FlightGroupsBriefing[i].NumberOfWaves);

                    p = fs.Position;
                    bw.Write(FlightGroupsBriefing[i].Name.ToCharArray());
                    fs.Position = p + 0x10;
                    bw.Write(FlightGroupsBriefing[i].Cargo.ToCharArray());
                    fs.Position = p + 0x20;
                    bw.Write(FlightGroupsBriefing[i].SpecialCargo.ToCharArray());
                    fs.Position = p + 0x30;

                    bw.Write(FlightGroupsBriefing[i].SpecialCargoCraft);
                    bw.Write(FlightGroupsBriefing[i].Yaw);
                    bw.Write(FlightGroupsBriefing[i].Pitch);
                    bw.Write(FlightGroupsBriefing[i].Roll);
                }

                #region WindowUISettings
                short count = (short)Briefing.WindowSettings.Count;
                bw.Write(count);
                for (int i = 0; i < count; i++)
                {
                    for (int j = 0; j < 5; j++)
                    {
                        BriefingUIItem item = Briefing.WindowSettings[i].Items[j];
                        bw.Write(item.Top);
                        bw.Write(item.Left);
                        bw.Write(item.Bottom);
                        bw.Write(item.Right);
                        bw.Write(Convert.ToInt16(item.IsVisible));
                    }
                }
                #endregion WindowUISettings

                #region Pages
                bw.Write((short)Briefing.Pages.Count);
                for (int i = 0; i < Briefing.Pages.Count; i++)
                {
                    BriefingPage pg = Briefing.GetBriefingPage(i);
                    bw.Write(pg.Length);
                    bw.Write(pg.EventsLength);
                    bw.Write(pg.CoordSet);
                    bw.Write(pg.PageType);

                    byte[] briefBuffer = new byte[pg.EventsLength * 2];
                    Buffer.BlockCopy(pg.Events, 0, briefBuffer, 0, briefBuffer.Length);
                    bw.Write(briefBuffer);
                }
                #endregion Pages

                bw.Write(TimeLimitMinutes);
                bw.Write(EndEvent);
                bw.Write(RndSeed);
                bw.Write(Briefing.MissionLocation);

                p = fs.Position;
                for (int i = 0; i < 3; i++)
                {
                    bw.Write(EndOfMissionMessages[i].ToCharArray());
                    fs.Position = p + ((i + 1) * 64);
                }

                p = fs.Position;
                for (int i = 0; i < FlightGroupsBriefing.Count; i++)
                {
                    fs.Position += 68;
                    bw.Write((short)FlightGroupsBriefing[i].PlayerCraft);
                    fs.Position = p + ((i + 1) * 90);
                }

                #region Text/Tags
                bw.Write((short)32);                 //Count
                for (int i = 0; i < 32; i++)
                {
                    bw.Write((short)Briefing.BriefingTag[i].Length);
                    bw.Write(Briefing.BriefingTag[i].ToCharArray());
                }
                bw.Write((short)32);
                for (int i = 0; i < 32; i++)
                {
                    string t   = Briefing.RemoveBrackets(Briefing.BriefingString[i]);
                    int    len = t.Length;
                    bw.Write((short)len);
                    bw.Write(t.ToCharArray());
                    byte[] highlight = Briefing.TranslateStringToHighlight(Briefing.BriefingString[i]);
                    bw.Write(highlight);
                }
                #endregion Text/Tags
                fs.SetLength(fs.Position);
                fs.Close();
            }
            catch
            {
                if (fs != null)
                {
                    fs.Close();
                }
                if (writerCreated && backupCreated)
                {
                    File.Delete(BriefingPath);
                    File.Copy(backup, BriefingPath);
                    File.Delete(backup);
                }
                throw;
            }
            if (backupCreated)
            {
                File.Delete(backup);
            }
        }
        /// <summary>Loads a mission briefing (.BRF) file from an open FileStream</summary>
        /// <param name="stream">Opened FileStream to mission briefing file</param>
        /// <exception cref="InvalidDataException"><paramref name="stream"/> is not a valid X-wing mission briefing file</exception>
        public void LoadBriefingFromStream(FileStream stream)
        {
            BinaryReader br = new BinaryReader(stream, System.Text.Encoding.GetEncoding(437));              //[JB] Changed encoding to IBM437 (OEM United States) to properly handle the DOS ASCII character set.

            string str;
            short  s;

            s = br.ReadInt16();             //PlatformID
            if (s != 2)
            {
                throw new InvalidDataException("Not a valid X-wing briefing file.");
            }
            short shipCount  = br.ReadInt16();
            short coordCount = br.ReadInt16();

            FlightGroupsBriefing = new FlightGroupCollection(shipCount);
            int wp;

            for (int i = 0; i < coordCount; i++)
            {
                //Just in case there too many coord sets than what the editor allows, read them but only load them if the indexes are valid.
                if (i == 0)
                {
                    wp = 0;                          //SP1
                }
                else
                {
                    wp = 7 + i - 1;                      //CS1 starts at [7], but at this point i==1 so subtract to compensate
                }
                if (wp >= 10)
                {
                    wp = -1;
                }
                for (int j = 0; j < shipCount; j++)
                {
                    for (int k = 0; k < 3; k++)
                    {
                        short dat = br.ReadInt16();
                        if (wp >= 0)
                        {
                            FlightGroupsBriefing[j].Waypoints[wp][k] = dat;
                        }
                    }
                    if (wp >= 0 && wp < 7)
                    {
                        FlightGroupsBriefing[j].Waypoints[wp][3] = 1;
                    }
                }
            }
            if (coordCount < 2)
            {
                coordCount = 2;                              //Sanity check for editor purposes.  All LEC missions have 2 sets.
            }
            else if (coordCount > 4)
            {
                coordCount = 4;
            }
            Briefing.MaxCoordSet = coordCount;

            for (int i = 0; i < shipCount; i++)
            {
                s = br.ReadInt16();                 //craft type
                if (s < 18)
                {
                    FlightGroupsBriefing[i].CraftType  = (byte)s;
                    FlightGroupsBriefing[i].ObjectType = 0;
                }
                else
                {
                    FlightGroupsBriefing[i].CraftType  = 0;
                    FlightGroupsBriefing[i].ObjectType = s;
                }

                FlightGroupsBriefing[i].IFF               = (byte)br.ReadInt16();
                FlightGroupsBriefing[i].NumberOfCraft     = (byte)br.ReadInt16();
                FlightGroupsBriefing[i].NumberOfWaves     = (byte)br.ReadInt16();
                FlightGroupsBriefing[i].Name              = new string(br.ReadChars(16));
                FlightGroupsBriefing[i].Cargo             = new string(br.ReadChars(16));
                FlightGroupsBriefing[i].SpecialCargo      = new string(br.ReadChars(16));
                FlightGroupsBriefing[i].SpecialCargoCraft = br.ReadInt16();
                FlightGroupsBriefing[i].Yaw               = br.ReadInt16();
                FlightGroupsBriefing[i].Pitch             = br.ReadInt16();
                FlightGroupsBriefing[i].Roll              = br.ReadInt16();
            }

            #region WindowUISettings
            short count = br.ReadInt16();              //Setting count.  Usually 2, but not always.
            Briefing.ResetUISettings(count);
            for (int i = 0; i < count; i++)
            {
                for (int j = 0; j < 5; j++)
                {
                    BriefingUIItem item = Briefing.WindowSettings[i].Items[j];
                    item.Top       = br.ReadInt16();
                    item.Left      = br.ReadInt16();
                    item.Bottom    = br.ReadInt16();
                    item.Right     = br.ReadInt16();
                    item.IsVisible = Convert.ToBoolean(br.ReadInt16());
                }
            }
            #endregion WindowUISettings

            #region Pages
            count = br.ReadInt16();
            Briefing.ResetPages(count);
            for (int i = 0; i < count; i++)
            {
                BriefingPage pg = Briefing.Pages[i];
                pg.Length       = br.ReadInt16();           //total ticks
                pg.EventsLength = br.ReadInt16();           //Count of Int16s
                pg.CoordSet     = br.ReadInt16();
                pg.PageType     = br.ReadInt16();

                byte[] briefBuffer = br.ReadBytes(pg.EventsLength * 2);
                Buffer.BlockCopy(briefBuffer, 0, pg.Events, 0, briefBuffer.Length);
            }

            #endregion Pages

            stream.Position += 6;

            /*s = br.ReadInt16();  //TimeLimitMinutes?
             * s = br.ReadInt16();  //EndEvent?
             * s = br.ReadInt16();  //Unknown1?*/
            Briefing.MissionLocation = br.ReadInt16();
            stream.Position         += 3 * 64;

            /*for (int i = 0; i < 3; i++)  //EndOfMissionMessages
             *      str = new string(br.ReadChars(64));*/
            stream.Position += 90 * shipCount;

            /*for (int i = 0; i < shipCount; i++)
             * {
             *      //No idea what data this is, or even what order.
             *      br.ReadBytes(68);
             *      s = br.ReadInt16(); //PlayerCraft?
             *      br.ReadBytes(20);
             * }*/

            #region Text/Tags
            count = br.ReadInt16();              //Tags
            Briefing.ResizeTagList(count);
            for (int i = 0; i < count; i++)
            {
                short len = br.ReadInt16();
                Briefing.BriefingTag[i] = new string(br.ReadChars(len));
            }
            count = br.ReadInt16();              //Text
            Briefing.ResizeStringList(count);
            for (int i = 0; i < count; i++)
            {
                short len = br.ReadInt16();
                str = new string(br.ReadChars(len));
                byte[] highlight = new byte[len];
                br.Read(highlight, 0, len);
                Briefing.BriefingString[i] = Briefing.TranslateHighlightToString(str, highlight);
            }
            #endregion Text/Tags
        }