public static SortableBytes[] MergeAlreadySorted(SortableBytes[] a, SortableBytes[] b)
    {
        SortableBytes[] merged = new SortableBytes[a.Length + b.Length];
        int             i = 0, j = 0;

        for (int k = 0; k < merged.Length; ++k)
        {
            SortableBytes selected;

            if (i >= a.Length)
            {
                selected = b[j++];
            }
            else if (j >= b.Length)
            {
                selected = a[i++];
            }
            else
            {
                selected = a[i].tick < b[j].tick ? a[i++] : b[j++];
            }

            merged[k] = selected;
        }

        return(merged);
    }
    static byte[] GetInstrumentBytes(Song song, Song.Instrument instrument, ExportOptions exportOptions, float resolutionScaleRatio)
    {
        // Collect all bytes from each difficulty of the instrument, assigning the position for each event unsorted
        //List<SortableBytes> byteEvents = new List<SortableBytes>();
        SortableBytes[] easyBytes   = GetChartSortableBytes(song, instrument, Song.Difficulty.Easy, exportOptions);
        SortableBytes[] mediumBytes = GetChartSortableBytes(song, instrument, Song.Difficulty.Medium, exportOptions);
        SortableBytes[] hardBytes   = GetChartSortableBytes(song, instrument, Song.Difficulty.Hard, exportOptions);
        SortableBytes[] expertBytes = GetChartSortableBytes(song, instrument, Song.Difficulty.Expert, exportOptions);

        SortableBytes[]      em           = SortableBytes.MergeAlreadySorted(easyBytes, mediumBytes);
        SortableBytes[]      he           = SortableBytes.MergeAlreadySorted(hardBytes, expertBytes);
        List <SortableBytes> sortedEvents = new List <SortableBytes>(SortableBytes.MergeAlreadySorted(em, he));

        // Perform merge sort to re-order everything correctly
        //SortableBytes[] sortedEvents = new SortableBytes[easyBytes.Length + mediumBytes.Length + hardBytes.Length + expertBytes.Length];//byteEvents.ToArray();
        //SortableBytes.Sort(sortedEvents);

        // Strip out duplicate events. This may occur with cymbal flags across multiple difficulties
        for (int i = sortedEvents.Count - 1; i >= 0; --i)
        {
            int next = i + 1;
            while (next < sortedEvents.Count && sortedEvents[i].tick == sortedEvents[next].tick)
            {
                if (sortedEvents[i].bytes.SequenceEqual(sortedEvents[next].bytes))
                {
                    sortedEvents.RemoveAt(next);
                }

                ++next;
            }
        }

        return(SortableBytesToTimedEventBytes(sortedEvents.ToArray(), song, exportOptions, resolutionScaleRatio));
    }
    static byte[] GetUnrecognisedChartBytes(Chart chart, ExportOptions exportOptions)
    {
        List <SortableBytes> eventList = new List <SortableBytes>();
        del InsertionSort = (sortableByte) =>
        {
            int index = eventList.Count - 1;

            while (index >= 0 && sortableByte.tick < eventList[index].tick)
            {
                --index;
            }

            eventList.Insert(index + 1, sortableByte);
        };

        foreach (ChartObject chartObject in chart.chartObjects)
        {
            SortableBytes onEvent  = null;
            SortableBytes offEvent = null;

            Note note = chartObject as Note;
            if (note != null)
            {
                GetUnrecognisedChartNoteBytes(note, out onEvent, out offEvent);
            }

            Starpower sp = chartObject as Starpower;
            if (sp != null)     // Starpower cannot be split up between charts in a midi file
            {
                GetStarpowerBytes(sp, out onEvent, out offEvent);
            }

            ChartEvent chartEvent = chartObject as ChartEvent;
            if (chartEvent != null)     // Text events cannot be split up in the file
            {
                SortableBytes bytes = GetChartEventBytes(chartEvent);
                InsertionSort(bytes);
            }

            if (onEvent != null && offEvent != null)
            {
                InsertionSort(onEvent);

                if (offEvent.tick == onEvent.tick)
                {
                    ++offEvent.tick;
                }

                InsertionSort(offEvent);
            }
        }

        return(SortableBytesToTimedEventBytes(eventList.ToArray(), chart.song, exportOptions));
    }
    static void InsertionSort(IList <SortableBytes> eventList, SortableBytes sortableByte)
    {
        int index = eventList.Count - 1;

        while (index >= 0 && sortableByte.tick < eventList[index].tick)
        {
            --index;
        }

        eventList.Insert(index + 1, sortableByte);
    }
    static byte[] GetInstrumentBytes(Song song, Song.Instrument instrument, ExportOptions exportOptions)
    {
        // Collect all bytes from each difficulty of the instrument, assigning the position for each event unsorted
        //List<SortableBytes> byteEvents = new List<SortableBytes>();
        SortableBytes[] easyBytes   = GetChartSortableBytes(song, instrument, Song.Difficulty.Easy, exportOptions);
        SortableBytes[] mediumBytes = GetChartSortableBytes(song, instrument, Song.Difficulty.Medium, exportOptions);
        SortableBytes[] hardBytes   = GetChartSortableBytes(song, instrument, Song.Difficulty.Hard, exportOptions);
        SortableBytes[] expertBytes = GetChartSortableBytes(song, instrument, Song.Difficulty.Expert, exportOptions);

        SortableBytes[] em           = SortableBytes.MergeAlreadySorted(easyBytes, mediumBytes);
        SortableBytes[] he           = SortableBytes.MergeAlreadySorted(hardBytes, expertBytes);
        SortableBytes[] sortedEvents = SortableBytes.MergeAlreadySorted(em, he);

        // Perform merge sort to re-order everything correctly
        //SortableBytes[] sortedEvents = new SortableBytes[easyBytes.Length + mediumBytes.Length + hardBytes.Length + expertBytes.Length];//byteEvents.ToArray();
        //SortableBytes.Sort(sortedEvents);

        return(SortableBytesToTimedEventBytes(sortedEvents, song, exportOptions));
    }
    static byte[] GetUnrecognisedChartBytes(Chart chart, ExportOptions exportOptions, float resolutionScaleRatio)
    {
        List <SortableBytes> eventList = new List <SortableBytes>();

        foreach (ChartObject chartObject in chart.chartObjects)
        {
            SortableBytes onEvent  = null;
            SortableBytes offEvent = null;

            Note note = chartObject as Note;
            if (note != null)
            {
                GetUnrecognisedChartNoteBytes(note, out onEvent, out offEvent);
            }

            Starpower sp = chartObject as Starpower;
            if (sp != null)     // Starpower cannot be split up between charts in a midi file
            {
                GetStarpowerBytes(sp, out onEvent, out offEvent);
            }

            ChartEvent chartEvent = chartObject as ChartEvent;
            if (chartEvent != null)     // Text events cannot be split up in the file
            {
                SortableBytes bytes = GetChartEventBytes(chartEvent);
                InsertionSort(eventList, bytes);
            }

            if (onEvent != null && offEvent != null)
            {
                InsertionSort(eventList, onEvent);

                if (offEvent.tick == onEvent.tick)
                {
                    ++offEvent.tick;
                }

                InsertionSort(eventList, offEvent);
            }
        }

        return(SortableBytesToTimedEventBytes(eventList.ToArray(), chart.song, exportOptions, resolutionScaleRatio));
    }
    static void Merge(SortableBytes[] bytes, int left, int mid, int right)
    {
        SortableBytes[] temp = new SortableBytes[bytes.Length];
        int             i, eol, num, pos;

        eol = (mid - 1);
        pos = left;
        num = (right - left + 1);

        while ((left <= eol) && (mid <= right))
        {
            if (bytes[left].tick <= bytes[mid].tick)
            {
                temp[pos++] = bytes[left++];
            }
            else
            {
                temp[pos++] = bytes[mid++];
            }
        }

        while (left <= eol)
        {
            temp[pos++] = bytes[left++];
        }

        while (mid <= right)
        {
            temp[pos++] = bytes[mid++];
        }

        for (i = 0; i < num; i++)
        {
            bytes[right] = temp[right];
            right--;
        }
    }
 static void GetNoteNumberBytes(int noteNumber, Note note, out SortableBytes onEvent, out SortableBytes offEvent)
 {
     onEvent  = new SortableBytes(note.tick, new byte[] { ON_EVENT, (byte)noteNumber, VELOCITY });
     offEvent = new SortableBytes(note.tick + note.length, new byte[] { OFF_EVENT, (byte)noteNumber, VELOCITY });
 }
 static void GetUnrecognisedChartNoteBytes(Note note, out SortableBytes onEvent, out SortableBytes offEvent)
 {
     GetNoteNumberBytes(note.rawNote, note, out onEvent, out offEvent);
 }
    /* CHART EVENT BYTE DETERMINING
     ***********************************************************************************************/

    static void GetStarpowerBytes(Starpower sp, out SortableBytes onEvent, out SortableBytes offEvent)
    {
        onEvent  = new SortableBytes(sp.tick, new byte[] { ON_EVENT, STARPOWER_NOTE, VELOCITY });
        offEvent = new SortableBytes(sp.tick + sp.length, new byte[] { OFF_EVENT, STARPOWER_NOTE, VELOCITY });
    }
    static SortableBytes[] GetChartSortableBytes(Song song, Song.Instrument instrument, Song.Difficulty difficulty, ExportOptions exportOptions)
    {
        Chart chart = song.GetChart(instrument, difficulty);

        Chart.GameMode gameMode = chart.gameMode;

        if (exportOptions.copyDownEmptyDifficulty)
        {
            Song.Difficulty chartDiff = difficulty;
            while (chart.notes.Count <= 0)
            {
                switch (chartDiff)
                {
                case (Song.Difficulty.Easy):
                    chartDiff = Song.Difficulty.Medium;
                    break;

                case (Song.Difficulty.Medium):
                    chartDiff = Song.Difficulty.Hard;
                    break;

                case (Song.Difficulty.Hard):
                    chartDiff = Song.Difficulty.Expert;
                    break;

                case (Song.Difficulty.Expert):
                default:
                    return(new SortableBytes[0]);
                }

                chart = song.GetChart(instrument, chartDiff);
            }
        }

        List <SortableBytes> eventList = new List <SortableBytes>();

        del InsertionSort = (sortableByte) =>
        {
            int index = eventList.Count - 1;

            while (index >= 0 && sortableByte.tick < eventList[index].tick)
            {
                --index;
            }

            eventList.Insert(index + 1, sortableByte);
        };

        foreach (ChartObject chartObject in chart.chartObjects)
        {
            Note note = chartObject as Note;

            SortableBytes onEvent  = null;
            SortableBytes offEvent = null;

            if (note != null)
            {
                int noteNumber = GetMidiNoteNumber(note, gameMode, difficulty);

                GetNoteNumberBytes(noteNumber, note, out onEvent, out offEvent);

                if (exportOptions.forced)
                {
                    // Forced notes
                    if ((note.flags & Note.Flags.Forced) != 0 && note.type != Note.NoteType.Tap && (note.previous == null || (note.previous.tick != note.tick)))     // Don't overlap on chords
                    {
                        // Add a note
                        int difficultyNumber;
                        int forcingOffset;

                        if (!c_difficultyToMidiNoteWriteDict.TryGetValue(difficulty, out difficultyNumber))
                        {
                            throw new Exception("Unhandled difficulty");
                        }

                        if (!c_forcingMidiWriteOffsets.TryGetValue(note.type, out forcingOffset))
                        {
                            throw new Exception("Unhandled note type found when trying to write forcing flag");
                        }

                        int forcedNoteNumber = difficultyNumber + forcingOffset;

                        SortableBytes forceOnEvent  = new SortableBytes(note.tick, new byte[] { ON_EVENT, (byte)forcedNoteNumber, VELOCITY });
                        SortableBytes forceOffEvent = new SortableBytes(note.tick + 1, new byte[] { OFF_EVENT, (byte)forcedNoteNumber, VELOCITY });

                        InsertionSort(forceOnEvent);
                        InsertionSort(forceOffEvent);
                    }

                    int openNote = gameMode == Chart.GameMode.GHLGuitar ? (int)Note.GHLiveGuitarFret.Open : (int)Note.GuitarFret.Open;
                    // Add tap sysex events
                    if (difficulty == Song.Difficulty.Expert && note.rawNote != openNote && (note.flags & Note.Flags.Tap) != 0 && (note.previous == null || (note.previous.flags & Note.Flags.Tap) == 0))  // This note is a tap while the previous one isn't as we're creating a range
                    {
                        // Find the next non-tap note
                        Note nextNonTap = note;
                        while (nextNonTap.next != null && nextNonTap.rawNote != openNote && (nextNonTap.next.flags & Note.Flags.Tap) != 0)
                        {
                            nextNonTap = nextNonTap.next;
                        }

                        // Tap event = 08-50-53-00-00-FF-04-01, end with 01 for On, 00 for Off
                        byte[] tapOnEventBytes  = new byte[] { SYSEX_START, 0x08, 0x50, 0x53, 0x00, 0x00, 0xFF, 0x04, SYSEX_ON, SYSEX_END };
                        byte[] tapOffEventBytes = new byte[] { SYSEX_START, 0x08, 0x50, 0x53, 0x00, 0x00, 0xFF, 0x04, SYSEX_OFF, SYSEX_END };

                        SortableBytes tapOnEvent  = new SortableBytes(note.tick, tapOnEventBytes);
                        SortableBytes tapOffEvent = new SortableBytes(nextNonTap.tick + 1, tapOffEventBytes);

                        InsertionSort(tapOnEvent);
                        InsertionSort(tapOffEvent);
                    }
                }

                if (gameMode != Chart.GameMode.Drums && gameMode != Chart.GameMode.GHLGuitar &&
                    difficulty == Song.Difficulty.Expert && note.guitarFret == Note.GuitarFret.Open && (note.previous == null || (note.previous.guitarFret != Note.GuitarFret.Open)))
                {
                    // Find the next non-open note
                    Note nextNonOpen = note;
                    while (nextNonOpen.next != null && nextNonOpen.next.guitarFret == Note.GuitarFret.Open)
                    {
                        nextNonOpen = nextNonOpen.next;
                    }

                    byte diff;

                    switch (difficulty)
                    {
                    case (Song.Difficulty.Easy):
                        diff = 0;
                        break;

                    case (Song.Difficulty.Medium):
                        diff = 1;
                        break;

                    case (Song.Difficulty.Hard):
                        diff = 2;
                        break;

                    case (Song.Difficulty.Expert):
                        diff = 3;
                        break;

                    default:
                        continue;
                    }

                    byte[] openOnEventBytes  = new byte[] { SYSEX_START, 0x08, 0x50, 0x53, 0x00, 0x00, diff, 0x01, SYSEX_ON, SYSEX_END };
                    byte[] openOffEventBytes = new byte[] { SYSEX_START, 0x08, 0x50, 0x53, 0x00, 0x00, diff, 0x01, SYSEX_OFF, SYSEX_END };

                    SortableBytes openOnEvent  = new SortableBytes(note.tick, openOnEventBytes);
                    SortableBytes openOffEvent = new SortableBytes(nextNonOpen.tick + 1, openOffEventBytes);

                    InsertionSort(openOnEvent);
                    InsertionSort(openOffEvent);
                }
            }

            Starpower sp = chartObject as Starpower;
            if (sp != null && difficulty == Song.Difficulty.Expert)     // Starpower cannot be split up between charts in a midi file
            {
                GetStarpowerBytes(sp, out onEvent, out offEvent);
            }

            ChartEvent chartEvent = chartObject as ChartEvent;
            if (chartEvent != null && difficulty == Song.Difficulty.Expert)     // Text events cannot be split up in the file
            {
                InsertionSort(GetChartEventBytes(chartEvent));
            }

            if (onEvent != null && offEvent != null)
            {
                InsertionSort(onEvent);

                if (offEvent.tick == onEvent.tick)
                {
                    ++offEvent.tick;
                }

                InsertionSort(offEvent);
            }
        }

        return(eventList.ToArray());
    }
Example #12
0
 static void GetSoloBytes(ChartEvent solo, uint soloEndTick, out SortableBytes onEvent, out SortableBytes offEvent)
 {
     onEvent  = new SortableBytes(solo.tick, new byte[] { ON_EVENT, MidIOHelper.SOLO_NOTE, VELOCITY });
     offEvent = new SortableBytes(soloEndTick, new byte[] { OFF_EVENT, MidIOHelper.SOLO_NOTE, VELOCITY });
 }
Example #13
0
    static SortableBytes[] GetChartSortableBytes(Song song, Song.Instrument instrument, Song.Difficulty difficulty, ExportOptions exportOptions)
    {
        Chart chart = song.GetChart(instrument, difficulty);

        Chart.GameMode gameMode = chart.gameMode;

        if (exportOptions.copyDownEmptyDifficulty)
        {
            Song.Difficulty chartDiff = difficulty;
            while (chart.notes.Count <= 0)
            {
                switch (chartDiff)
                {
                case (Song.Difficulty.Easy):
                    chartDiff = Song.Difficulty.Medium;
                    break;

                case (Song.Difficulty.Medium):
                    chartDiff = Song.Difficulty.Hard;
                    break;

                case (Song.Difficulty.Hard):
                    chartDiff = Song.Difficulty.Expert;
                    break;

                case (Song.Difficulty.Expert):
                default:
                    return(new SortableBytes[0]);
                }

                chart = song.GetChart(instrument, chartDiff);
            }
        }

        List <SortableBytes> eventList = new List <SortableBytes>();

        ChartEvent soloOnEvent = null;

        foreach (ChartObject chartObject in chart.chartObjects)
        {
            Note note = chartObject as Note;

            SortableBytes onEvent  = null;
            SortableBytes offEvent = null;

            if (note != null)
            {
                int noteNumber = GetMidiNoteNumber(note, gameMode, difficulty);

                GetNoteNumberBytes(noteNumber, note, out onEvent, out offEvent);

                if (exportOptions.forced)
                {
                    // Forced notes
                    if ((note.flags & Note.Flags.Forced) != 0 && note.type != Note.NoteType.Tap && (note.previous == null || (note.previous.tick != note.tick)))     // Don't overlap on chords
                    {
                        // Add a note
                        int difficultyNumber;
                        int forcingOffset;

                        if (!c_difficultyToMidiNoteWriteDict.TryGetValue(difficulty, out difficultyNumber))
                        {
                            throw new Exception("Unhandled difficulty");
                        }

                        if (!c_forcingMidiWriteOffsets.TryGetValue(note.type, out forcingOffset))
                        {
                            throw new Exception("Unhandled note type found when trying to write forcing flag");
                        }

                        int forcedNoteNumber = difficultyNumber + forcingOffset;

                        SortableBytes forceOnEvent  = new SortableBytes(note.tick, new byte[] { ON_EVENT, (byte)forcedNoteNumber, VELOCITY });
                        SortableBytes forceOffEvent = new SortableBytes(note.tick + 1, new byte[] { OFF_EVENT, (byte)forcedNoteNumber, VELOCITY });

                        InsertionSort(eventList, forceOnEvent);
                        InsertionSort(eventList, forceOffEvent);
                    }

                    if (instrument == Song.Instrument.Drums && ((note.flags & Note.Flags.ProDrums_Cymbal) == 0))     // We want to write our flags if the cymbal is toggled OFF, as these notes are cymbals by default
                    {
                        int tomToggleNoteNumber;
                        if (MidIOHelper.PAD_TO_CYMBAL_LOOKUP.TryGetValue(note.drumPad, out tomToggleNoteNumber))
                        {
                            SortableBytes tomToggleOnEvent  = new SortableBytes(note.tick, new byte[] { ON_EVENT, (byte)tomToggleNoteNumber, VELOCITY });
                            SortableBytes tomToggleOffEvent = new SortableBytes(note.tick + 1, new byte[] { OFF_EVENT, (byte)tomToggleNoteNumber, VELOCITY });

                            InsertionSort(eventList, tomToggleOnEvent);
                            InsertionSort(eventList, tomToggleOffEvent);
                        }
                    }

                    int openNote = gameMode == Chart.GameMode.GHLGuitar ? (int)Note.GHLiveGuitarFret.Open : (int)Note.GuitarFret.Open;
                    // Add tap sysex events
                    if (difficulty == Song.Difficulty.Expert && note.rawNote != openNote && (note.flags & Note.Flags.Tap) != 0 && (note.previous == null || (note.previous.flags & Note.Flags.Tap) == 0))  // This note is a tap while the previous one isn't as we're creating a range
                    {
                        // Find the next non-tap note
                        Note nextNonTap = note;
                        while (nextNonTap.next != null && nextNonTap.rawNote != openNote && (nextNonTap.next.flags & Note.Flags.Tap) != 0)
                        {
                            nextNonTap = nextNonTap.next;
                        }

                        // Tap event = 08-50-53-00-00-FF-04-01, end with 01 for On, 00 for Off
                        byte[] tapOnEventBytes  = new byte[] { SYSEX_START, 0x08, 0x50, 0x53, 0x00, 0x00, 0xFF, 0x04, SYSEX_ON, SYSEX_END };
                        byte[] tapOffEventBytes = new byte[] { SYSEX_START, 0x08, 0x50, 0x53, 0x00, 0x00, 0xFF, 0x04, SYSEX_OFF, SYSEX_END };

                        SortableBytes tapOnEvent  = new SortableBytes(note.tick, tapOnEventBytes);
                        SortableBytes tapOffEvent = new SortableBytes(nextNonTap.tick + 1, tapOffEventBytes);

                        InsertionSort(eventList, tapOnEvent);
                        InsertionSort(eventList, tapOffEvent);
                    }
                }

                if (gameMode != Chart.GameMode.Drums && gameMode != Chart.GameMode.GHLGuitar &&
                    difficulty == Song.Difficulty.Expert && note.guitarFret == Note.GuitarFret.Open && (note.previous == null || (note.previous.guitarFret != Note.GuitarFret.Open)))
                {
                    // Find the next non-open note
                    Note nextNonOpen = note;
                    while (nextNonOpen.next != null && nextNonOpen.next.guitarFret == Note.GuitarFret.Open)
                    {
                        nextNonOpen = nextNonOpen.next;
                    }

                    byte diff;

                    switch (difficulty)
                    {
                    case (Song.Difficulty.Easy):
                        diff = 0;
                        break;

                    case (Song.Difficulty.Medium):
                        diff = 1;
                        break;

                    case (Song.Difficulty.Hard):
                        diff = 2;
                        break;

                    case (Song.Difficulty.Expert):
                        diff = 3;
                        break;

                    default:
                        continue;
                    }

                    byte[] openOnEventBytes  = new byte[] { SYSEX_START, 0x08, 0x50, 0x53, 0x00, 0x00, diff, 0x01, SYSEX_ON, SYSEX_END };
                    byte[] openOffEventBytes = new byte[] { SYSEX_START, 0x08, 0x50, 0x53, 0x00, 0x00, diff, 0x01, SYSEX_OFF, SYSEX_END };

                    SortableBytes openOnEvent  = new SortableBytes(note.tick, openOnEventBytes);
                    SortableBytes openOffEvent = new SortableBytes(nextNonOpen.tick + 1, openOffEventBytes);

                    InsertionSort(eventList, openOnEvent);
                    InsertionSort(eventList, openOffEvent);
                }
            }

            Starpower sp = chartObject as Starpower;
            if (sp != null && difficulty == Song.Difficulty.Expert)     // Starpower cannot be split up between charts in a midi file
            {
                GetStarpowerBytes(sp, out onEvent, out offEvent);
            }

            ChartEvent chartEvent = chartObject as ChartEvent;
            if (chartEvent != null && difficulty == Song.Difficulty.Expert)     // Text events cannot be split up in the file
            {
                if (soloOnEvent != null && chartEvent.eventName == MidIOHelper.SoloEndEventText)
                {
                    GetSoloBytes(soloOnEvent, chartEvent.tick, out onEvent, out offEvent);
                    soloOnEvent = null;
                }
                else if (chartEvent.eventName == MidIOHelper.SoloEventText)
                {
                    soloOnEvent = chartEvent;
                }
                else
                {
                    InsertionSort(eventList, GetChartEventBytes(chartEvent));
                }
            }

            if (onEvent != null && offEvent != null)
            {
                InsertionSort(eventList, onEvent);

                if (offEvent.tick == onEvent.tick)
                {
                    ++offEvent.tick;
                }

                InsertionSort(eventList, offEvent);
            }
        }

        if (soloOnEvent != null)        // Found a solo event with no end. Assume the solo lasts for the rest of the song
        {
            SortableBytes onEvent  = null;
            SortableBytes offEvent = null;

            uint soloEndTick = chart.chartObjects[chart.chartObjects.Count - 1].tick;   // In order to get a solo event the chart objects needed to have some object in this container, no need to check size, hopefully...
            GetSoloBytes(soloOnEvent, soloEndTick, out onEvent, out offEvent);

            if (onEvent != null && offEvent != null)
            {
                InsertionSort(eventList, onEvent);

                if (offEvent.tick == onEvent.tick)
                {
                    ++offEvent.tick;
                }

                InsertionSort(eventList, offEvent);
            }
        }

        return(eventList.ToArray());
    }