// Insert beat reset after 1 measure of time signature change event according to BMS specifications. private static void BeatResetFix(ExtendedSortedSet <BMSEvent> bmev) { BMSEvent[] beatResetEvents = bmev.Where(ev => ev.type == BMSEventType.BeatReset).ToArray(); for (int i = 0, l = beatResetEvents.Length; i < l; i++) { BMSEvent currentEv = beatResetEvents[i]; int meas = currentEv.measure; if (i == l - 1 || (beatResetEvents[i + 1].measure - meas > 1 && currentEv.Data2F != 1)) { bmev.Add(new BMSEvent { measure = meas + 1, beat = 0, Data2F = 1, type = BMSEventType.BeatReset }, InsertMode.OldFirst); } } }
public override void Parse(ParseType parseType) { ResetAllData(parseType); if ((parseType & ParseType.Header) == ParseType.Header) { LnType = 1; } InitSources(); Stack <IfBlock> ifStack; ExtendedSortedSet <BMSEvent> bmev; HashSet <int> channels; Random random; int maxCombos = 0; if ((parseType & ParseType.Content) == ParseType.Content) { ifStack = new Stack <IfBlock>(); ifStack.Push(new IfBlock { parsing = true }); random = new Random(); bmev = new ExtendedSortedSet <BMSEvent>(); randomized = false; channels = null; } else if ((parseType & ParseType.ContentSummary) == ParseType.ContentSummary) { ifStack = null; random = null; bmev = null; channels = new HashSet <int>(); } else { ifStack = null; random = null; bmev = null; channels = null; } for (int i = 0, c = bmsContent.Count; i < c; i++) { try { string line = bmsContent[i]; string command, command2, param1, param2; // Command parsing int colonIndex = line.IndexOf(':', 1), spaceIndex = line.IndexOf(' ', 1); int spliiterIndex = colonIndex >= 0 && (spaceIndex > colonIndex || spaceIndex < 0) ? colonIndex : spaceIndex; if (spliiterIndex > 0) { command = line.Substring(0, spliiterIndex); if (spliiterIndex + 1 < line.Length) { param1 = line.Substring(spliiterIndex + 1).Trim(); } else { param1 = string.Empty; } } else { command = line; param1 = string.Empty; } command = command.ToLower(); // Header part if ((parseType & ParseType.Header) == ParseType.Header) { if (ParseHeaderLine(command, param1)) { continue; } } // nnnXX pattern parsing if (command.Length > 4) { int j = command.Length - 2; command2 = command.Substring(1, j - 1); param2 = command.Substring(j); } else { command2 = command; param2 = string.Empty; } // Resource part if ((parseType & ParseType.Resources) == ParseType.Resources) { if (ParseResourceLine(command2, param1, param2)) { continue; } } // Content part if ((parseType & ParseType.Content) == ParseType.Content) { if (CheckExecution(ifStack, random, command, param1)) { continue; } if (ParseContentLine(command2, param1, param2, bmev)) { continue; } } else if ((parseType & ParseType.ContentSummary) == ParseType.ContentSummary) { if (ParseSummary(command2, param1, param2, channels, out int combos)) { maxCombos += combos; continue; } } } catch (Exception ex) { throw new LineParseException(i, ex); } } if ((parseType & ParseType.Content) == ParseType.Content) { BeatResetFix(bmev); PostProcessContent(bmev); } else if ((parseType & ParseType.ContentSummary) == ParseType.ContentSummary) { this.maxCombos = maxCombos; ReportChannels(channels); } base.Parse(parseType); }
private void PostProcessContent(ExtendedSortedSet <BMSEvent> bmev) { Dictionary <int, BMSEvent> lnMarker = new Dictionary <int, BMSEvent>(); double bpm = initialBPM, beatOffset = 0, beatPerMeas = 1; TimeSpan referenceTimePoint = TimeSpan.Zero; TimeSpan stopTimePoint = TimeSpan.Zero; float stopMeasBeat = 0; EnsureCapacity(bmev.Count + 1); AddEvent(new BMSEvent { measure = 0, beat = 0, type = BMSEventType.BPM, Data2F = bpm }); foreach (BMSEvent ev in bmev) { BMSEvent converted = new BMSEvent { measure = ev.measure, beat = (float)(ev.beat * beatPerMeas), }; if (ev.measure + ev.beat == stopMeasBeat) { converted.time = stopTimePoint; } else { converted.time = referenceTimePoint + MeasureBeatToTimeSpan(ev.measure + ev.beat - beatOffset, beatPerMeas, bpm); } switch (ev.type) { case BMSEventType.BPM: double newBpm; if (ev.data1 == 8 && TryGetResourceData(ResourceType.bpm, ev.data2, out BMSResourceData bpmData)) // Extended BPM { newBpm = Convert.ToDouble(bpmData.additionalData); } else if (ev.data1 == 3 && int.TryParse(Base36.Encode((int)ev.data2), NumberStyles.HexNumber, null, out int bpmInt)) // BPM { newBpm = bpmInt; } else { continue; } if (newBpm == bpm) { continue; } converted.type = BMSEventType.BPM; converted.Data2F = newBpm; referenceTimePoint = converted.time; beatOffset = ev.measure + ev.beat; bpm = newBpm; minBpm = Math.Min(minBpm, (float)newBpm); break; case BMSEventType.BeatReset: double newBeatPerMeas = ev.Data2F; if (newBeatPerMeas == beatPerMeas) { continue; } converted.type = BMSEventType.BeatReset; converted.Data2F = ev.Data2F * 4; beatOffset = ev.measure; beatPerMeas = newBeatPerMeas; referenceTimePoint = converted.time; break; case BMSEventType.STOP: if (!TryGetResourceData(ResourceType.stop, ev.data2, out BMSResourceData stopData)) { continue; } double stopSeconds = Convert.ToDouble(stopData.additionalData) / bpm * 1.25; if (stopSeconds <= 0) { continue; } converted.type = BMSEventType.STOP; stopTimePoint = converted.time; stopMeasBeat = ev.measure + ev.beat; TimeSpan stopTime = new TimeSpan((long)Math.Round(stopSeconds * TimeSpan.TicksPerSecond)); converted.data2 = stopTime.Ticks; referenceTimePoint += stopTime; break; case BMSEventType.BMP: converted.type = BMSEventType.BMP; switch (ev.data1) { case 4: converted.data1 = 0; break; case 6: converted.data1 = -1; break; case 7: converted.data1 = 1; break; default: continue; } converted.data2 = ev.data2; break; case BMSEventType.LongNoteStart: converted.data1 = ev.data1 - 40; converted.data2 = ev.data2; converted.sliceStart = TimeSpan.Zero; converted.sliceEnd = TimeSpan.MaxValue; if (lnMarker.TryGetValue(ev.data1, out BMSEvent lnStart)) { converted.type = BMSEventType.LongNoteEnd; int index = FindEventIndex(lnStart); lnStart.time2 = converted.time; ReplaceEvent(index, lnStart); lnMarker.Remove(ev.data1); } else { converted.type = BMSEventType.LongNoteStart; lnMarker[ev.data1] = converted; } break; case BMSEventType.Unknown: continue; default: if ((ev.data1 >= 30 && ev.data1 <= 50) || ev.data1 >= 70) { continue; } converted.type = ev.type; converted.data1 = ev.data1; converted.data2 = ev.data2; converted.sliceStart = TimeSpan.Zero; converted.sliceEnd = TimeSpan.MaxValue; break; } AddEvent(converted); } }
private static bool ParseContentLine(string command2, string strParam1, string strParam2, ExtendedSortedSet <BMSEvent> bmev) { if (!int.TryParse(command2, out int verse)) { return(false); } int channel = GetChannelNumberById(strParam2); if (channel < 0) { return(false); } strParam1 = spaceMatcher.Replace(strParam1, string.Empty); int length = strParam1.Length / 2; BMSEventType evType; switch (channel) { case 1: evType = BMSEventType.WAV; break; case 2: bmev.Add(new BMSEvent { measure = verse, beat = 0, Data2F = double.Parse(strParam1), type = BMSEventType.BeatReset }, InsertMode.OldFirst); return(true); case 3: evType = BMSEventType.BPM; break; case 4: case 6: case 7: evType = BMSEventType.BMP; break; case 8: evType = BMSEventType.BPM; break; case 9: evType = BMSEventType.STOP; break; default: if (channel > 10 && channel < 30) { evType = BMSEventType.Note; } else if (channel > 50 && channel < 70) { evType = BMSEventType.LongNoteStart; } else { evType = BMSEventType.Unknown; } break; } for (int i = 0; i < length; i++) { int value = Base36.Decode(strParam1.Substring(i * 2, 2)); if (value > 0) { bmev.Add(new BMSEvent { measure = verse, beat = (float)i / length, type = evType, data1 = channel, data2 = value }, InsertMode.OldFirst); } } return(true); }