// Helps in parsing the changes file
        private void ParseSecondary(ref CISRecordChanges isrc, string sSecondary)
        {
            string sCmd = "";
            string sArg = "";
            string sNum = "";
            int nState = 0;
            foreach (char c in sSecondary)
            {
                switch (nState)
                {
                    case 0:
                        // Leading whitespace
                        if (!char.IsWhiteSpace(c))
                        {
                            sNum += c;
                            nState = 1;
                        }
                        break;
                    case 1:
                        // Number
                        if (char.IsDigit(c))
                        {
                            sNum += c;
                        }
                        else
                        {
                            if (!char.IsWhiteSpace(c))
                            {
                                sCmd += c;
                                nState = 3;
                            }
                            nState = 2;
                        }
                        break;
                    case 2:
                        // Space after number
                        if (!char.IsWhiteSpace(c))
                        {
                            sCmd += c;
                            nState = 3;
                        }
                        break;
                    case 3:
                        if (!char.IsWhiteSpace(c))
                        {
                            sCmd += c;
                        }
                        else
                        {
                            nState = 4;
                        }
                        break;
                    case 4:
                        // Space after cmd
                        if (!char.IsWhiteSpace(c))
                        {
                            sArg += c;
                            nState = 5;
                        }
                        break;
                    case 5:
                        sArg += c;
                        break;
                }

                if (nState == 99)
                {
                    // End of string parsing
                    break;
                }
            }

            if (isrc != null)
            {
                switch (sCmd)
                {
                    case "OBJE":
                        {
                            CMultimediaFileReference mfr = new CMultimediaFileReference(m_gedcom);
                            mfr.m_bFromGEDCOM = false;
                            isrc.m_alMfrs.Add(mfr);
                            isrc.m_mfrCurrent = mfr;
                            break;
                        }
                    case "FILE":
                        if (isrc.m_mfrCurrent != null)
                        {
                            sArg = sArg.Replace('/', '\\');
                            isrc.m_mfrCurrent.m_sMultimediaFileReference = sArg;
                        }
                        break;
                    case "FORM":
                        if (isrc.m_mfrCurrent != null)
                        {
                            isrc.m_mfrCurrent.m_sMultimediaFormat = sArg;
                        }
                        break;
                    case "TITL":
                        if (isrc.m_mfrCurrent != null)
                        {
                            isrc.m_mfrCurrent.m_sDescriptiveTitle = sArg;
                        }
                        break;
                    case "_ORDER":
                        if (isrc.m_mfrCurrent != null)
                        {
                            isrc.m_mfrCurrent.m_nOrderIndex = int.Parse(sArg);
                        }
                        break;
                    case "_VISIBLE":
                        if (isrc.m_mfrCurrent != null)
                        {
                            isrc.m_mfrCurrent.m_bVisible = (sArg.ToUpper() == "TRUE") ? true : false;
                        }
                        break;
                    case "_GEDCOM":
                        if (isrc.m_mfrCurrent != null)
                        {
                            isrc.m_mfrCurrent.m_bFromGEDCOM = (sArg.ToUpper() == "TRUE") ? true : false;
                        }
                        break;
                    case "_INLINE":
                        if (isrc.m_mfrCurrent != null)
                        {
                            isrc.m_mfrCurrent.m_bEmbedded = true;
                            isrc.m_mfrCurrent.m_xrefEmbedded = sArg;
                        }
                        break;
                    case "_REGION":
                        if (isrc.m_mfrCurrent != null)
                        {
                            int nFirstComma = sArg.IndexOf(',');
                            if (nFirstComma > 0 && nFirstComma + 1 < sArg.Length)
                            {
                                string sAsid = sArg.Substring(0, nFirstComma);
                                string sArea = sArg.Substring(nFirstComma + 1);
                                CAsidPair asidPair = new CAsidPair(sAsid, sArea);
                                isrc.m_mfrCurrent.m_asidPair = asidPair;
                            }
                        }
                        break;
                }
            }
            return;
        }
        // Parses the changes file
        private Hashtable ParseSelectionFile(string filename, ref EVersion eVersion)
        {
            Hashtable htSelection = new Hashtable();
            FileStream fileStream = null;
            StreamReader streamReader = null;
            uint uLineNumber = 1;
            string sVersion = "";

            try
            {
                LogFile.TheLogFile.WriteLine(LogFile.DT_APP, LogFile.EDebugLevel.Note, String.Format("opening changes file {0}", filename));

                fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read);
                streamReader = new StreamReader(fileStream, System.Text.Encoding.ASCII);

                string sLine;
                sLine = streamReader.ReadLine();
                // First gedcomLine should be version number
                if (sLine.Length >= 8 && sLine.Substring(0, 11) == "// GEDmill ")
                {
                    sVersion = sLine.Substring(11);
                }
                if (sVersion.Length < 5 || m_sSoftwareVersion.Length < 5)
                {
                    eVersion = EVersion.pre1p8;
                }
                else
                {
                    switch (sVersion.Substring(0, 6))
                    {
                        case "1.10.0":
                        case "1.10.1":
                        case "1.10.2":
                        case "1.10.3":
                        case "1.10.4":
                        case "1.11.0":
                            eVersion = EVersion.v1p10;
                            break;
                        default:
                            switch (sVersion.Substring(0, 5))
                            {
                                case "1.8.0":
                                case "1.8.1":
                                case "1.8.2":
                                case "1.8.3":
                                case "1.8.4":
                                case "1.8.5":
                                case "1.8.6":
                                case "1.8.7":
                                case "1.8.8":
                                case "1.8.9":
                                    eVersion = EVersion.v1p8;
                                    break;
                                case "1.9.0":
                                case "1.9.1":
                                case "1.9.2":
                                    eVersion = EVersion.v1p9;
                                    break;
                            }
                            break;
                    }
                }
                if (eVersion == EVersion.pre1p8)
                {
                    if (sLine.IndexOf("GEDmill") == -1)
                    {
                        DialogResult dialogResult = MessageBocx.Show(this, "The file you have selected is not a valid GEDmill file.", "Invalid File",
                            MessageBoxButtons.OK, MessageBoxIcon.Exclamation, false);
                        return null;
                    }
                    sVersion = "pre 1.8.0";
                }
                LogFile.TheLogFile.WriteLine(LogFile.DT_APP, LogFile.EDebugLevel.Note, String.Format("version is {0}:{1}", sVersion, sLine));

                CISRecordChanges isrc = null;
                while ((sLine = streamReader.ReadLine()) != null)
                {
                    string xref = "";
                    string sSecondary = "";
                    // Parse characters in gedcomLine until whitespace or // found
                    int n = 0;
                    bool bSecondary = false;
                    while (n < sLine.Length)
                    {
                        char c = sLine[n];
                        if (char.IsWhiteSpace(c))
                        {
                            if (xref.Length == 0)
                            {
                                // Must be a secondary data gedcomLine
                                bSecondary = true;
                            }
                            else
                            {
                                // Whitespace at end of xref gedcomLine
                                break;
                            }
                        }
                        if ((c == '#'))
                        {
                            // # is the new comment starter. (Used to be // but that screwed up //machine/file/path/filenames )
                            break;
                        }
                        if (bSecondary)
                        {
                            sSecondary += c;
                        }
                        else
                        {
                            xref += c;
                        }
                        n++;
                    }
                    if (xref.Length > 0)
                    {
                        isrc = new CISRecordChanges(true);
                        htSelection[xref] = isrc;
                    }
                    if (sSecondary.Length > 0)
                    {
                        ParseSecondary(ref isrc, sSecondary);
                    }
                    uLineNumber++;
                }
            }
            catch (Exception e)
            {
                LogFile.TheLogFile.WriteLine(LogFile.DT_APP, LogFile.EDebugLevel.Error, String.Format("A problem was encountered while reading the file : {0} line {1}", e.ToString(), uLineNumber));
            }
            finally
            {
                if (streamReader != null)
                {
                    streamReader.Close();
                }

                if (fileStream != null)
                {
                    fileStream.Close();
                }
            }
            return htSelection;
        }
 // Reads the MFRs from the changes file (MFR here is for the user's modifications to the multimedia attached to a record)
 private bool LoadMFRs(CISRecord isr, CISRecordChanges isrc)
 {
     bool bDifferencesDetected = false;
     for (int n = isr.m_alUniqueFileRefs.Count - 1; n >= 0; --n) // Can't use foreach, because then Remove() would corrupt iterator.
     {
         CMultimediaFileReference mfr = (CMultimediaFileReference)(isr.m_alUniqueFileRefs[n]);
         if (mfr.m_bFromGEDCOM == false)
         {
             isr.m_alUniqueFileRefs.RemoveAt(n);
         }
         else
         {
             mfr.m_bVisible = false;
         }
     }
     foreach (CMultimediaFileReference mfr in isrc.m_alMfrs)
     {
         CMultimediaFileReference mfrFound = null;
         foreach (CMultimediaFileReference mfrUnique in isr.m_alUniqueFileRefs)
         {
             if (mfr.m_bEmbedded && mfrUnique.m_bEmbedded)
             {
                 if (mfr.m_xrefEmbedded == mfrUnique.m_xrefEmbedded)
                 {
                     mfrFound = mfrUnique;
                     break;
                 }
             }
             if (!mfr.m_bEmbedded && !mfrUnique.m_bEmbedded)
             {
                 if (mfr.m_sMultimediaFileReference == mfrUnique.m_sMultimediaFileReference)
                 {
                     mfrFound = mfrUnique;
                     break;
                 }
             }
         }
         if (mfrFound == null)
         {
             //Must be a user added file. Add it to unique mfrs
             if (File.Exists(mfr.m_sMultimediaFileReference))
             {
                 isr.m_alUniqueFileRefs.Add(mfr);
                 if (mfr.m_bFromGEDCOM)
                 {
                     // User must have removed this file in the interim
                     bDifferencesDetected = true;
                 }
             }
             else
             {
                 // File has been deleted or moved in the interim
                 bDifferencesDetected = true;
             }
         }
         else
         {
             if (mfrFound.m_bEmbedded)
             {
                 string sKeepEmbeddedFilename = mfrFound.m_sMultimediaFileReference;
                 mfrFound.CopyFrom(mfr);
                 mfrFound.m_sMultimediaFileReference = sKeepEmbeddedFilename;
             }
             else
             {
                 mfrFound.CopyFrom(mfr);
             }
         }
     }
     // Fix duplicates of order values
     foreach (CMultimediaFileReference imfr in isr.m_alUniqueFileRefs)
     {
         foreach (CMultimediaFileReference dimfr in isr.m_alUniqueFileRefs)
         {
             int nOrderIndex = imfr.m_nOrderIndex;
             if (imfr != dimfr && nOrderIndex == dimfr.m_nOrderIndex)
             {
                 foreach (CMultimediaFileReference iimfr in isr.m_alUniqueFileRefs)
                 {
                     if (iimfr.m_nOrderIndex >= nOrderIndex)
                     {
                         iimfr.m_nOrderIndex += 1;
                     }
                 }
                 imfr.m_nOrderIndex = nOrderIndex;
             }
         }
     }
     return (bDifferencesDetected);
 }