Пример #1
0
 public LaneHoldEndNote(LaneHoldEndNote other)
     : base(other)
 {
 }
Пример #2
0
        /// <summary>
        /// Parses the sm/ssc string representation of measures and notes as
        /// events into the given Chart.
        /// </summary>
        /// <param name="chart">Chart to parse notes into.</param>
        /// <param name="notesStr">String representation of notes from an sm or ssc chart.</param>
        /// <returns>Whether the notes represent a valid chart or not.</returns>
        protected bool ParseNotes(Chart chart, string notesStr)
        {
            if (chart.NumInputs < 1)
            {
                Logger?.Warn(
                    $"Cannot parse notes for {chart.Type} {chart.DifficultyType} Chart. Unknown number of inputs. This Chart will be ignored.");
                return(false);
            }

            var validChart           = true;
            var player               = 0;
            var measure              = 0;
            var currentMeasureEvents = new List <Event>();

            notesStr = notesStr.Trim(SMCommon.SMAllWhiteSpace);
            var notesStrsPerPlayer = notesStr.Split('&');

            foreach (var notesStrForPlayer in notesStrsPerPlayer)
            {
                var holding = new bool[chart.NumInputs];
                var rolling = new bool[chart.NumInputs];

                // RemoveEmptyEntries seems wrong, but matches Stepmania parsing logic.
                var measures = notesStrForPlayer.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                foreach (var measureStr in measures)
                {
                    var lines = measureStr.Trim(SMCommon.SMAllWhiteSpace)
                                .Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
                    var linesInMeasure = lines.Length;
                    var lineInMeasure  = 0;
                    foreach (var line in lines)
                    {
                        var trimmedLine = line.Trim(SMCommon.SMAllWhiteSpace);

                        // Parse this line as note data
                        for (int charIndex = 0, laneIndex = 0;
                             charIndex < trimmedLine.Length && laneIndex < chart.NumInputs;
                             charIndex++, laneIndex++)
                        {
                            // Get the note type.
                            var c          = trimmedLine[charIndex];
                            var noteType   = SMCommon.NoteType.None;
                            var noteString = SMCommon.NoteStrings[(int)noteType];
                            for (var i = 0; i < SMCommon.NoteChars.Length; i++)
                            {
                                if (c == SMCommon.NoteChars[i])
                                {
                                    noteType   = (SMCommon.NoteType)i;
                                    noteString = SMCommon.NoteStrings[i];
                                    break;
                                }
                            }

                            // Validation.
                            if (noteType == SMCommon.NoteType.Tap ||
                                noteType == SMCommon.NoteType.Mine ||
                                noteType == SMCommon.NoteType.Lift ||
                                noteType == SMCommon.NoteType.Fake ||
                                noteType == SMCommon.NoteType.KeySound ||
                                noteType == SMCommon.NoteType.HoldStart ||
                                noteType == SMCommon.NoteType.RollStart)
                            {
                                if (holding[laneIndex])
                                {
                                    Logger?.Error(
                                        $"Invalid {chart.Type} {chart.DifficultyType} Chart. {noteString} during hold on lane {laneIndex} during measure {measure}. This Chart will be ignored.");
                                    validChart = false;
                                }

                                if (rolling[laneIndex])
                                {
                                    Logger?.Error(
                                        $"Invalid {chart.Type} {chart.DifficultyType} Chart. {noteString} during roll on lane {laneIndex} during measure {measure}. This Chart will be ignored.");
                                    validChart = false;
                                }
                            }
                            else if (noteType == SMCommon.NoteType.HoldEnd)
                            {
                                if (!holding[laneIndex] && !rolling[laneIndex])
                                {
                                    Logger?.Error(
                                        $"Invalid {chart.Type} {chart.DifficultyType} Chart. {noteString} while neither holding nor rolling on lane {laneIndex} during measure {measure}. This Chart will be ignored.");
                                    validChart = false;
                                }
                            }
                            if (!validChart)
                            {
                                break;
                            }

                            // Create a LaneNote based on the note type.
                            LaneNote note = null;
                            if (noteType == SMCommon.NoteType.Tap ||
                                noteType == SMCommon.NoteType.Fake ||
                                noteType == SMCommon.NoteType.Lift)
                            {
                                note = new LaneTapNote {
                                    SourceType = c.ToString()
                                };
                            }
                            else if (noteType == SMCommon.NoteType.Mine ||
                                     noteType == SMCommon.NoteType.KeySound)
                            {
                                note = new LaneNote {
                                    SourceType = c.ToString()
                                };
                            }
                            else if (noteType == SMCommon.NoteType.HoldStart)
                            {
                                holding[laneIndex] = true;
                                note = new LaneHoldStartNote {
                                    SourceType = c.ToString()
                                };
                            }
                            else if (noteType == SMCommon.NoteType.RollStart)
                            {
                                rolling[laneIndex] = true;
                                note = new LaneHoldStartNote {
                                    SourceType = c.ToString()
                                };
                            }
                            else if (noteType == SMCommon.NoteType.HoldEnd)
                            {
                                note = new LaneHoldEndNote {
                                    SourceType = c.ToString()
                                };
                                holding[laneIndex] = false;
                                rolling[laneIndex] = false;
                            }

                            // Keysound parsing.
                            // TODO: Parse keysounds properly. For now, putting them in SourceExtras.
                            if (charIndex + 1 < trimmedLine.Length && trimmedLine[charIndex + 1] == '[')
                            {
                                var startIndex = charIndex + 1;
                                while (charIndex < trimmedLine.Length)
                                {
                                    if (trimmedLine[charIndex] == ']')
                                    {
                                        break;
                                    }
                                    charIndex++;
                                }

                                var endIndex = charIndex - 1;
                                if (endIndex > startIndex && note != null)
                                {
                                    if (int.TryParse(trimmedLine.Substring(startIndex, endIndex - startIndex),
                                                     out var keySoundIndex))
                                    {
                                        note.Extras.AddSourceExtra(SMCommon.TagFumenKeySoundIndex, keySoundIndex, true);
                                    }
                                }
                            }

                            // Deprecated Attack parsing
                            if (charIndex + 1 < trimmedLine.Length && trimmedLine[charIndex + 1] == '{')
                            {
                                while (charIndex < trimmedLine.Length)
                                {
                                    if (trimmedLine[charIndex] == '}')
                                    {
                                        break;
                                    }
                                    charIndex++;
                                }
                            }

                            // Deprecated Item parsing
                            if (charIndex + 1 < trimmedLine.Length && trimmedLine[charIndex + 1] == '<')
                            {
                                while (charIndex < trimmedLine.Length)
                                {
                                    if (trimmedLine[charIndex] == '>')
                                    {
                                        break;
                                    }
                                    charIndex++;
                                }
                            }

                            // No note at this position, continue.
                            if (null == note)
                            {
                                continue;
                            }

                            // Configure common parameters on the note and add it.
                            var beat = (lineInMeasure * SMCommon.NumBeatsPerMeasure) / linesInMeasure;
                            var subDivisionNumerator   = lineInMeasure * SMCommon.NumBeatsPerMeasure - linesInMeasure * beat;
                            var subDivisionDenominator = linesInMeasure;
                            note.Lane     = laneIndex;
                            note.Player   = player;
                            note.Position = new MetricPosition(measure, beat, subDivisionNumerator, subDivisionDenominator);
                            currentMeasureEvents.Add(note);
                        }

                        if (!validChart)
                        {
                            break;
                        }

                        // Advance line marker
                        lineInMeasure++;
                    }

                    if (!validChart)
                    {
                        break;
                    }

                    chart.Layers[0].Events.AddRange(currentMeasureEvents);
                    currentMeasureEvents.Clear();
                    measure++;
                }

                // Validation.
                for (var i = 0; i < chart.NumInputs; i++)
                {
                    if (holding[i])
                    {
                        Logger?.Error(
                            $"Invalid {chart.Type} {chart.DifficultyType} Chart. Incomplete hold on lane {i}. This Chart will be ignored.");
                        validChart = false;
                    }
                    if (rolling[i])
                    {
                        Logger?.Error(
                            $"Invalid {chart.Type} {chart.DifficultyType} Chart. Incomplete roll on lane {i}. This Chart will be ignored.");
                        validChart = false;
                    }
                }

                if (!validChart)
                {
                    break;
                }

                player++;
            }

            return(validChart);
        }