public static void ListOccurrencesAndMessages(PartsLib.Tools.MDRF.Reader.MDRFFileReader reader)
        {
            Console.WriteLine("File: '{0}' size: {1} kB{2}", Path.GetFileName(reader.FilePath), reader.FileLength * (1.0 / 1024), reader.FileIndexInfo.FileWasProperlyClosed ? "" : " NotProperlyClosed");

            int  lineCount       = 0;
            bool listMessages    = select.IsSet(Select.ListMessages);
            bool listOccurrences = select.IsSet(Select.ListOccurrences);

            ReadAndProcessFilterSpec filterSpec = new ReadAndProcessFilterSpec()
            {
                EventHandlerDelegate = (sender, pceData) => ProcessContentEventHandlerDelegate2(sender, pceData, ref lineCount),
                PCEMask = ProcessContentEvent.None.Set(ProcessContentEvent.Occurrence, listOccurrences).Set(ProcessContentEvent.Message | ProcessContentEvent.Error, listMessages),
            };

            currentReader.ReadAndProcessContents(filterSpec);

            Console.WriteLine();
        }
        private static void ProcessMDRFFile(string mdrfFilePath)
        {
            using (currentReader = new MDRFFileReader(mdrfFilePath, mdrfFileReaderBehavior: MDRFFileReaderBehavior.EnableLiveFileHeuristics))
            {
                if (!currentReader.ResultCode.IsNullOrEmpty())
                {
                    Console.WriteLine("Failed to open '{0}': {1}".CheckedFormat(System.IO.Path.GetFileName(mdrfFilePath), currentReader.ResultCode));
                    return;
                }

                if (select.IsAnySet(Select.ListInfo | Select.ListIndex | Select.ListGroupInfo | Select.ListOccurrenceInfo))
                {
                    ListSelectedMDRFParts(currentReader);
                    return;
                }

                if (select.IsAnySet(Select.ListOccurrences | Select.ListMessages))
                {
                    ListOccurrencesAndMessages(currentReader);
                    return;
                }

                string noExtensionPath = mdrfFilePath.RemoveSuffixIfNeeded(".mdrf");
                if (!fileNameTag.IsNullOrEmpty())
                {
                    noExtensionPath = "{0}_{1}".CheckedFormat(noExtensionPath, fileNameTag);
                }

                string csvPath = noExtensionPath.AddSuffixIfNeeded(".csv");

                Console.WriteLine("Processing '{0}' {1} bytes => '{2}'".CheckedFormat(System.IO.Path.GetFileName(mdrfFilePath), currentReader.FileLength, System.IO.Path.GetFileName(csvPath)));

                if (System.IO.File.Exists(csvPath))
                {
                    System.IO.File.Delete(csvPath);
                }

                using (System.IO.StreamWriter sw = new System.IO.StreamWriter(csvPath))
                {
                    bool headerAndDataOnly = select.IsSet(Select.HeaderAndDataOnly);

                    Func <IGroupInfo, bool> groupFilterDelegate = (selectedGroupList.IsNullOrEmpty() ? (Func <IGroupInfo, bool>)((IGroupInfo gi) => true) : ((IGroupInfo gi) => selectedGroupList.Any(grpName => gi.Name.Contains(grpName))));

                    IGroupInfo[] FilteredGroupInfoArray = currentReader.GroupInfoArray.Where(gi => groupFilterDelegate(gi)).ToArray();

                    if (!headerAndDataOnly)
                    {
                        sw.CheckedWriteLine("$File.Path,{0}{1}", System.IO.Path.GetFullPath(mdrfFilePath), currentReader.FileIndexInfo.FileWasProperlyClosed ? "" : ",NotProperlyClosed");

                        sw.CheckedWriteLine("$File.Size,{0}", currentReader.FileLength);
                        sw.CheckedWriteLine("$File.Date.First,{0:o}", currentReader.DateTimeInfo.UTCDateTime.ToLocalTime());
                        sw.CheckedWriteLine("$File.Date.Last,{0:o}", currentReader.FileIndexInfo.FileIndexRowArray.Select(row => row.FirstBlockDateTime + (row.LastBlockDeltaTimeStamp - row.FirstBlockDeltaTimeStamp).FromSeconds()).Max().ToLocalTime());
                        sw.CheckedWriteLine("$File.Elapsed.Hours,{0:f6}", currentReader.FileIndexInfo.LastBlockInfo.BlockDeltaTimeStamp / 3600.0);

                        foreach (var key in new string[] { "HostName", "CurrentProcess.ProcessName", "Environment.MachineName", "Environment.OSVersion", "Environment.Is64BitOperatingSystem", "Environment.ProcessorCount" })
                        {
                            sw.WriteLineKeyIfPresent(currentReader.LibraryInfo.NVS, key);
                        }

                        if (FilteredGroupInfoArray.SafeLength() <= 1)
                        {
                            sw.CheckedWriteLine("$Group.Name,{0}", FilteredGroupInfoArray.Select(gi => gi.Name).ToArray().SafeAccess(0, "[NoGroupSelected]"));
                        }
                        else
                        {
                            sw.CheckedWriteLine("$Group.Names,{0}", String.Join(",", FilteredGroupInfoArray.Select((gi, idx) => "{0}:{1}".CheckedFormat(idx + 1, gi.Name)).ToArray()));
                        }

                        if (startDeltaTime != 0.0 || endDeltaTime != double.PositiveInfinity)
                        {
                            sw.CheckedWriteLine("$Filter.DeltaTime,{0:f6},{1:f6}", startDeltaTime, endDeltaTime);
                        }

                        sw.CheckedWriteLine("");
                    }

                    string[] columnNames = new[] { "DateTime", "DeltaTime" };
                    if (FilteredGroupInfoArray.SafeLength() <= 1)
                    {
                        columnNames = columnNames.Concat(FilteredGroupInfoArray.SelectMany(gi => gi.GroupPointInfoArray.Select(gpi => gpi.Name))).ToArray();
                    }
                    else
                    {
                        columnNames = columnNames.Concat(FilteredGroupInfoArray.SelectMany((gi, idx) => gi.GroupPointInfoArray.Select(gpi => "{0}:{1}".CheckedFormat(idx + 1, gpi.Name)))).ToArray();
                    }

                    sw.CheckedWriteLine(String.Join(",", columnNames));

                    ReadAndProcessFilterSpec filterSpec = new ReadAndProcessFilterSpec()
                    {
                        FirstFileDeltaTimeStamp = startDeltaTime,
                        LastFileDeltaTimeStamp  = endDeltaTime,
                        EventHandlerDelegate    = (sender, pceData) => ProcessContentEventHandlerDelegate(sw, sender, pceData),
                        PCEMask = ProcessContentEvent.All,
                        FileIndexUserRowFlagBitsMask = 0,
                        NominalMinimumGroupAndTimeStampUpdateInterval = nominalUpdateInterval,
                        GroupFilterDelegate = groupFilterDelegate,
                    };

                    currentReader.ReadAndProcessContents(filterSpec);
                }
            }
        }