public SongMetadata ParseFile() { string fileName = Metadata.ChartFullPath; // Used for v0.1 Dictionary <int, SlideCollection> tempSlideDict = new Dictionary <int, SlideCollection>(); // Used for v1.0 Dictionary <int, Hold> tempHoldDict = new Dictionary <int, Hold>(); // Temp parsing variables int beatsPerMeasure = 4; // Ultimately this should be tied to time signature int measure = -1; double subDiv; int noteClass, lane, width, id, endLane, endWidth; bool inNoteData = false; try { using (StreamReader sr = new StreamReader(new FileStream(fileName, FileMode.Open, FileAccess.Read))) { while (!sr.EndOfStream) { string line = sr.ReadLine(); // Whatever metadata stuff isn't covered in SongMetadata if (Metadata.Version.Major == 1 && Metadata.Version.Minor == 0) { // Look for BPM change tags if (Regex.Match(line, "^(#BPMC)").Success) { var bpmSplit = Regex.Split(line, @"[(#BPMC)\s:.]+").Where(s => !String.IsNullOrEmpty(s)); if (bpmSplit.Count() == 3) { id = bpmSplit.ElementAt(0).ParseBase36(); measure = int.Parse(bpmSplit.ElementAt(1)); subDiv = double.Parse(bpmSplit.ElementAt(2)) / 192.0; Metadata.BpmEvents.Add(new BpmChangeEvent(Metadata.BpmIndex[id], beatsPerMeasure * (measure + subDiv))); } else { Console.WriteLine("Failed to parse BPM Change: {0}", line); } } else { // Must have seen the "#NOTES" tag before parsing notes if (Regex.Match(line, "^(#NOTES)").Success) { inNoteData = true; } else if (Regex.Match(line, "^(#ENDNOTES)").Success) { inNoteData = false; } if (inNoteData) { if (Regex.Match(line, "^[0-9]{3}$").Success) { measure = int.Parse(line); } else if (Regex.Match(line, "^[0-9]{3}:").Success) { var timeSplit = Regex.Split(line, ":").Where(s => !String.IsNullOrEmpty(s)); if (timeSplit.Count() == 2) // It should only be 2 { subDiv = double.Parse(timeSplit.ElementAt(0)) / 192.0; var noteSplit = Regex.Split(timeSplit.ElementAt(1), ",").Where(s => !String.IsNullOrEmpty(s)); foreach (var note in noteSplit) { if (Regex.Match(note, "^[2-3]{1}").Success) // Motions { noteClass = note[0].ToString().ParseBase36(); Motions.Add(new Note(beatsPerMeasure * (measure + subDiv), 0, 16, noteClass == 2 ? Motion.Up : Motion.Down)); } else if (Regex.Match(note, "^[0-1]{1}[0-9a-fA-F]{2}$").Success) // Any step { noteClass = note[0].ToString().ParseBase36(); lane = note[1].ToString().ParseBase36(); width = note[2].ToString().ParseBase36() + 1; Steps.Add(new Note(beatsPerMeasure * (measure + subDiv), lane, width, noteClass == 0 ? Side.Left : Side.Right)); } else if (Regex.Match(note, "^[4-9a-bA-B]{1}[0-9a-fA-F]{2}[0-9a-zA-Z]{1}").Success) // Hold/Slides { noteClass = note[0].ToString().ParseBase36(); id = note[1].ToString().ParseBase36(); lane = note[2].ToString().ParseBase36(); width = note[3].ToString().ParseBase36() + 1; switch (noteClass) { case 4: // Left hold start case 5: // Right hold start // Check to see if a hold is already active-- if so, commit it and start a new one if (tempHoldDict.ContainsKey(id)) { CommitHold(tempHoldDict[id]); tempHoldDict.Remove(id); } // Create the new note tempHoldDict.Add(id, new Hold(beatsPerMeasure * (measure + subDiv), lane, width, noteClass == 4 ? Side.Left : Side.Right)); break; case 6: // Slide waypoint // Check to see this slide is still active if (tempHoldDict.ContainsKey(id)) { tempHoldDict[id].AddNote(new Note(beatsPerMeasure * (measure + subDiv), lane, width) { Type = NoteType.Hold }); } else { Console.WriteLine("Corresponding hold/slide for this waypoint is not active. Check file. Line: {0}", line); } break; case 7: // Hold / Slide end // Check to see this hold/slide is still active if (tempHoldDict.ContainsKey(id)) { tempHoldDict[id].AddNote(new Note(beatsPerMeasure * (measure + subDiv), lane, width) { Type = NoteType.Hold }); CommitHold(tempHoldDict[id]); tempHoldDict.Remove(id); } else { Console.WriteLine("Corresponding hold/slide for this endpoint is not active. Check file. Line: {0}", line); } break; case 8: // Simple Shuffle Waypoint // Check to see this hold/slide is still active if (tempHoldDict.ContainsKey(id)) { tempHoldDict[id].AddNote(new Note(beatsPerMeasure * (measure + subDiv), lane, width) { Type = NoteType.Shuffle }); } else { Console.WriteLine("Corresponding hold/slide for this shuffle is not active. Check file. Line: {0}", line); } break; case 9: // Complex Shuffle waypoint // Check to see this hold/slide is still active if (tempHoldDict.ContainsKey(id)) { // Check to see if there's enough parameters if (note.Length == 6) { endLane = note[4].ToString().ParseBase36(); endWidth = note[5].ToString().ParseBase36() + 1; tempHoldDict[id].AddNote(new Note(beatsPerMeasure * (measure + subDiv), lane, width, endLane, endWidth) { Type = NoteType.Shuffle }); } else { Console.WriteLine("Not enough parameters for this complex shuffle. Line: {0}", line); } } else { Console.WriteLine("Corresponding hold/slide for this shuffle is not active. Check file. Line: {0}", line); } break; case 10: // Simple Shuffle End // Check to see this hold/slide is still active if (tempHoldDict.ContainsKey(id)) { tempHoldDict[id].AddNote(new Note(beatsPerMeasure * (measure + subDiv), lane, width) { Type = NoteType.Shuffle }); CommitHold(tempHoldDict[id]); tempHoldDict.Remove(id); } else { Console.WriteLine("Corresponding hold/slide for this shuffle end is not active. Check file. Line: {0}", line); } break; case 11: // Complex shuffle end // Check to see this hold/slide is still active if (tempHoldDict.ContainsKey(id)) { // Check to see if there's enough parameters if (note.Length == 6) { endLane = note[4].ToString().ParseBase36(); endWidth = note[5].ToString().ParseBase36() + 1; tempHoldDict[id].AddNote(new Note(beatsPerMeasure * (measure + subDiv), lane, width, endLane, endWidth) { Type = NoteType.Shuffle }); CommitHold(tempHoldDict[id]); tempHoldDict.Remove(id); } else { Console.WriteLine("Not enough parameters for this complex shuffle end. Line: {0}", line); } } else { Console.WriteLine("Corresponding hold/slide for this shuffle end is not active. Check file. Line: {0}", line); } break; default: break; } } else { Console.WriteLine("Failed to parse note: {0}", line); } } } else { Console.WriteLine("Empty notestring found: {0}", line); } } } } } else if (Metadata.Version.Major == 0) { // Regex match for standard steps if (Regex.Match(line, "[#][0-9]{3}[1]").Success) { var parsed = ParseLine(line); double noteSub = 1.0 / parsed.Notes.Count; for (int i = 0; i < parsed.Notes.Count; i++) { switch (parsed.Notes[i].Item1) { case 1: // Left Step Steps.Add(new Note(4 * (parsed.Measure + i * noteSub), parsed.LaneIndex, parsed.Notes[i].Item2, Side.Left)); break; case 2: // Right Step Steps.Add(new Note(4 * (parsed.Measure + i * noteSub), parsed.LaneIndex, parsed.Notes[i].Item2, Side.Right)); break; case 3: // Motion Up Motions.Add(new Note(4 * (parsed.Measure + i * noteSub), 0, 16, Motion.Up)); break; case 4: // Motion Down Motions.Add(new Note(4 * (parsed.Measure + i * noteSub), 0, 16, Motion.Down)); break; default: // Rest notes / spacers (0) are ignored break; } } } // Regex match for hold/slides else if (Regex.IsMatch(line, "[#][0-9]{3}[2-3]")) { var parsed = ParseLine(line); double noteSub = 1.0 / parsed.Notes.Count; for (int i = 0; i < parsed.Notes.Count; i++) { Side side = parsed.NoteClass == 2 ? Side.Left : Side.Right; switch (parsed.Notes[i].Item1) { case 1: // Start a new note // Check to see if a hold is already active-- if so, commit it and start a new one if (tempSlideDict.ContainsKey(parsed.NoteIdentifier)) { CommitHold(tempSlideDict[parsed.NoteIdentifier]); tempSlideDict.Remove(parsed.NoteIdentifier); } // Create a collection tempSlideDict.Add(parsed.NoteIdentifier, new SlideCollection()); // Add this note to it tempSlideDict[parsed.NoteIdentifier].Notes.Add(new Tuple <NoteParse, int>(parsed, i)); break; case 2: // End a hold note with no shuffle case 3: // End a hold note with a shuffle case 4: // Add a midpoint with no shuffle case 5: // Add a midpoint with a shuffle if (!tempSlideDict.ContainsKey(parsed.NoteIdentifier)) { tempSlideDict.Add(parsed.NoteIdentifier, new SlideCollection()); // Add a new one (it will f**k up shit if this happens) } // Add this note to it tempSlideDict[parsed.NoteIdentifier].Notes.Add(new Tuple <NoteParse, int>(parsed, i)); break; default: // Rest notes / spacers (0) are ignored break; } } } // Parse BPM changes else if (Regex.IsMatch(line, "[#][0-9]{3}(08:)")) { //var parsed = ParseLine(line); var split = line.Replace(" ", string.Empty).Split(':'); var parsed = new NoteParse(); parsed.Measure = Convert.ToDouble(line.Substring(1, 3)); parsed.Notes = new List <Tuple <int, int> >(); for (int i = 0; i < split[1].Length; i += 2) { string idStr = split[1][i].ToString() + split[1][i + 1].ToString(); parsed.Notes.Add(new Tuple <int, int>(idStr.ParseBase36(), 0)); } double noteSub = 1.0 / parsed.Notes.Count; for (int i = 0; i < parsed.Notes.Count; i++) { if (parsed.Notes[i].Item1 == 0) { break; } else { Metadata.BpmEvents.Add(new BpmChangeEvent(Metadata.BpmIndex[parsed.Notes[i].Item1], 4 * (parsed.Measure + i * noteSub))); } } } } } } // Close out any remaining hold notes foreach (var tempHold in tempSlideDict) { CommitHold(tempHold.Value); } // Sort holds for drawing later Holds = Holds.OrderBy(x => x.StartNote.BeatLocation).ToList(); // Add Beat Markers var noteLast = Steps.Count > 0 ? Steps.Max(x => x.BeatLocation) : 0; var holdLast = Holds.Count > 0 ? Holds.Max(x => x.Notes.Max(y => y.BeatLocation)) : 0; var motionLast = Motions.Count > 0 ? Motions.Max(x => x.BeatLocation) : 0; double lastBeat = Math.Max(noteLast, holdLast); lastBeat = Math.Ceiling(Math.Max(lastBeat, motionLast)); for (int i = 0; i <= (int)lastBeat; i += 4) { Markers.Add(new BeatMarker(i)); } TotalNotes += Steps.Count(); TotalNotes += Holds.Count(); foreach (var hold in Holds) { TotalNotes += hold.GradePoints.Count; // Additional beat counters for holds/slides TotalNotes += hold.Notes.Count(x => x.Type == NoteType.Shuffle); } TotalNotes += Motions.Count(); } catch (Exception e) { StyleStarLogger.WriteEntry("Exception in NoteCollection.ParseFile() => Input: " + fileName + ", Exception: " + e.Message + ", Stack Trace: " + e.StackTrace + (e.InnerException != null ? ", Inner Exception: " + e.InnerException.Message : "")); } return(Metadata); }
private void Parse(string fileName) { try { ChartFullPath = Path.GetFullPath(fileName); FilePath = Path.GetDirectoryName(fileName) + @"\"; List <string> children = new List <string>(); using (StreamReader sr = new StreamReader(new FileStream(fileName, FileMode.Open, FileAccess.Read))) { while (!sr.EndOfStream) { string line = sr.ReadLine(); string parse; if (StringExtensions.TrySearchTag(line, "VERSION", out parse)) { Version = Version.Parse(parse); } if (StringExtensions.TrySearchTag(line, "WAVE", out parse)) { SongFilename = parse; } if (StringExtensions.TrySearchTag(line, "WAVEOFFSET", out parse)) { PlaybackOffset = Convert.ToDouble(parse); } if (StringExtensions.TrySearchTag(line, "TITLE", out parse)) { Title = parse; } if (StringExtensions.TrySearchTag(line, "ARTIST", out parse)) { Artist = parse; } if (StringExtensions.TrySearchTag(line, "DESIGNER", out parse)) { Designer = parse; } if (StringExtensions.TrySearchTag(line, "DIFFICULTY", out parse)) { Difficulty = (Difficulty)Convert.ToInt32(parse); } if (StringExtensions.TrySearchTag(line, "PLAYLEVEL", out parse)) { Level = Convert.ToInt32(parse); } if (StringExtensions.TrySearchTag(line, "JACKET", out parse)) { Jacket = parse; } if (StringExtensions.TrySearchTag(line, "TITLECARD", out parse)) { TitleCard = parse; } if (StringExtensions.TrySearchTag(line, "ARTISTCARD", out parse)) { ArtistCard = parse; } if (StringExtensions.TrySearchTag(line, "COLORFORE", out parse)) { ColorFore = Util.ParseFromHex(parse); } if (StringExtensions.TrySearchTag(line, "COLORBACK", out parse)) { ColorBack = Util.ParseFromHex(parse); } if (StringExtensions.TrySearchTag(line, "COLORACCENT", out parse)) { ColorAccent = Util.ParseFromHex(parse); } if (StringExtensions.TrySearchTag(line, "SONGID", out parse)) { SongID = parse; } if (Regex.IsMatch(line, "(#BPM)")) { string[] bpmParse = line.Split(new string[] { "#BPM", ": " }, StringSplitOptions.RemoveEmptyEntries); if (!BpmIndex.ContainsKey(bpmParse[0].ParseBase36())) { BpmIndex.Add(bpmParse[0].ParseBase36(), Convert.ToDouble(bpmParse[1])); } else { // Print an error here in some log somewhere Console.WriteLine("When parsing " + fileName + ", multiple BPM definitions were found with the same ID number."); } } if (StringExtensions.TrySearchTag(line, "CHART", out parse)) { IsMetadataFile = true; if (!parse.EndsWith(Defines.ChartExtension)) { parse += Defines.ChartExtension; } children.Add(FilePath + parse); } } } foreach (var child in children) { ChildMetadata.Add(new SongMetadata(this, child)); } if (String.IsNullOrEmpty(Jacket)) //AlbumImage = Globals.Textures["FallbackJacket"]; // FIXME { } else { AlbumImage = TextureUtil.LoadPngAsSprite(FilePath + Jacket); //using (FileStream fs = new FileStream(FilePath + Jacket, FileMode.Open)) //{ // AlbumImage = Texture2D.FromStream(Globals.GraphicsManager.GraphicsDevice, fs); //} } if (!String.IsNullOrEmpty(TitleCard)) { if (File.Exists(FilePath + TitleCard)) { TitleImage = TextureUtil.LoadPngAsSprite(FilePath + TitleCard); //using (FileStream fs = new FileStream(FilePath + TitleCard, FileMode.Open)) //{ // TitleImage = Texture2D.FromStream(Globals.GraphicsManager.GraphicsDevice, fs); //} } } if (!String.IsNullOrEmpty(ArtistCard)) { if (File.Exists(FilePath + ArtistCard)) { ArtistImage = TextureUtil.LoadPngAsSprite(FilePath + ArtistCard); //using (FileStream fs = new FileStream(FilePath + ArtistCard, FileMode.Open)) //{ // ArtistImage = Texture2D.FromStream(Globals.GraphicsManager.GraphicsDevice, fs); //} } } if (String.IsNullOrEmpty(SongID)) { SongID = Title + "_" + Artist + "_" + Level.ToString(); } } catch (Exception e) { StyleStarLogger.WriteEntry("Exception in SongMetadata.Parse() => Input: " + fileName + ", Exception: " + e.Message + ", Stack Trace: " + e.StackTrace + (e.InnerException != null ? ", Inner Exception: " + e.InnerException.Message : "")); } }