private ChartObjectSerializer GetSerializerForType(ChartObject obj)
 {
     if (m_mode == null)
     {
         return(null);
     }
     if (!m_serializersByType.TryGetValue(obj.GetType(), out var serializer))
     {
         serializer = m_mode.GetSerializerFor(obj);
         m_serializersByType[obj.GetType()] = serializer;
     }
     return(serializer);
 }
    static string CheckForErrorsMoonscraper(Song song, ValidationParameters validationParams, ref bool hasErrors)
    {
        bool          hasErrorsLocal = false;
        StringBuilder sb             = new StringBuilder();

        sb.AppendLine("Moonscraper validation report: ");

        // Check if any objects have exceeded the max length
        {
            uint tick = song.TimeToTick(validationParams.songLength, song.resolution);

            // Song objects
            {
                // Synctrack
                {
                    int index, length;
                    SongObjectHelper.GetRange(song.syncTrack, tick, uint.MaxValue, out index, out length);

                    for (int i = index; i < length; ++i)
                    {
                        hasErrorsLocal |= true;

                        SyncTrack st = song.syncTrack[i];

                        sb.AppendFormat("\tFound synctrack object beyond the length of the song-\n");
                        sb.AppendFormat("\t\tType = {0}, position = {1}\n", st.GetType(), st.tick);
                    }
                }

                // Events
                {
                    int index, length;
                    SongObjectHelper.GetRange(song.eventsAndSections, tick, uint.MaxValue, out index, out length);

                    for (int i = index; i < length; ++i)
                    {
                        hasErrorsLocal |= true;

                        MoonscraperChartEditor.Song.Event eventObject = song.eventsAndSections[i];

                        sb.AppendFormat("\tFound event object beyond the length of the song-\n");
                        sb.AppendFormat("\t\tType = {0}, position = {1}\n", eventObject.GetType(), eventObject.tick);
                    }
                }
            }

            // Chart objects
            foreach (Song.Instrument instrument in EnumX <Song.Instrument> .Values)
            {
                if (instrument == Song.Instrument.Unrecognised)
                {
                    continue;
                }

                foreach (Song.Difficulty difficulty in EnumX <Song.Difficulty> .Values)
                {
                    Chart chart = song.GetChart(instrument, difficulty);

                    int index, length;
                    SongObjectHelper.GetRange(chart.chartObjects, tick, uint.MaxValue, out index, out length);

                    for (int i = index; i < length; ++i)
                    {
                        hasErrorsLocal |= true;

                        ChartObject co = chart.chartObjects[i];

                        sb.AppendFormat("\tFound chart object beyond the length of the song-\n");
                        sb.AppendFormat("\t\tType = {0}, position = {1}\n", co.GetType(), co.tick);
                    }
                }
            }
        }

        if (!hasErrorsLocal)
        {
            sb.AppendLine("\tNo errors detected");
        }

        hasErrors |= hasErrorsLocal;

        return(sb.ToString());
    }