private void Parse(string bm) { Info.Filename = bm; Info.BeatmapHash = MD5FromFile(bm); using (StreamReader sR = new StreamReader(bm)) { string currentSection = ""; while (sR.Peek() != -1) { string line = sR.ReadLine(); //Check for section tag if (line.StartsWith("[")) { currentSection = line; continue; } //Check for commented-out line //or blank lines if (line.StartsWith("//") || line.Length == 0) continue; //Check for version string if (line.StartsWith("osu file format")) Info.Format = Convert.ToInt32(line.Substring(17).Replace(Environment.NewLine, "").Replace(" ", "")); //Do work for [General], [Metadata], [Difficulty] and [Editor] sections if ((currentSection == "[General]") || (currentSection == "[Metadata]") || (currentSection == "[Difficulty]") || (currentSection == "[Editor]")) { string[] reSplit = line.Split(':'); string cProperty = reSplit[0].TrimEnd(); bool isValidProperty = false; foreach (string k in BM_Sections.Keys) { if (k.Contains(cProperty)) isValidProperty = true; } if (!isValidProperty) continue; //Check for blank value string cValue = reSplit[1].Trim(); //Import properties into Info switch (cProperty) { case "EditorBookmarks": { string[] marks = cValue.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries); foreach (string m in marks.Where(m => m != "")) Info.EditorBookmarks.Add(Convert.ToInt32(m)); } break; case "Bookmarks": { string[] marks = cValue.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries); foreach (string m in marks.Where(m => m != "")) Info.Bookmarks.Add(Convert.ToInt32(m)); } break; case "Tags": string[] tags = cValue.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); foreach (string t in tags) Info.Tags.Add(t); break; case "Mode": Info.Mode = (GameMode)Convert.ToInt32(cValue); break; case "OverlayPosition": Info.OverlayPosition = (OverlayOptions)Enum.Parse(typeof(OverlayOptions), cValue); break; case "AlwaysShowPlayfield": Info.AlwaysShowPlayfield = Convert.ToBoolean(Convert.ToInt32(cValue)); break; default: FieldInfo fi = Info.GetType().GetField(cProperty); PropertyInfo pi = Info.GetType().GetProperty(cProperty); if (fi != null) { if (fi.FieldType == typeof(float?)) fi.SetValue(Info, (float?)Convert.ToDouble(cValue)); if (fi.FieldType == typeof(float)) fi.SetValue(Info, (float)Convert.ToDouble(cValue)); else if ((fi.FieldType == typeof(int?)) || (fi.FieldType == typeof(int))) fi.SetValue(Info, Convert.ToInt32(cValue)); else if (fi.FieldType == typeof(string)) fi.SetValue(Info, cValue); break; } if (pi.PropertyType == typeof(float?)) pi.SetValue(Info, (float?)Convert.ToDouble(cValue), null); if (pi.PropertyType == typeof(float)) pi.SetValue(Info, (float)Convert.ToDouble(cValue), null); else if ((pi.PropertyType == typeof(int?)) || (pi.PropertyType == typeof(int))) pi.SetValue(Info, Convert.ToInt32(cValue), null); else if (pi.PropertyType == typeof(string)) pi.SetValue(Info, cValue, null); break; } continue; } //The following are version-dependent, the version is stored as a numeric value inside Info.Format //Do work for [Events] section if (currentSection == "[Events]") { string[] reSplit = line.Split(','); switch (reSplit[0].ToLower()) { case "0": case "1": case "video": Info.Events.Add(new ContentEvent { Type = reSplit[0].ToLower() == "1" || reSplit[0].ToLower() == "video" ? ContentType.Video : ContentType.Image, StartTime = Convert.ToInt32(reSplit[1]), Filename = reSplit[2].Replace("\"", "") }); break; case "2": Info.Events.Add(new BreakEvent { StartTime = Convert.ToInt32(reSplit[1]), EndTime = Convert.ToInt32(reSplit[2]) }); break; case "3": Info.Events.Add(new BackgroundColourEvent { StartTime = Convert.ToInt32(reSplit[1]), Colour = new Colour { R = Convert.ToInt32(reSplit[2]), G = Convert.ToInt32(reSplit[3]), B = Convert.ToInt32(reSplit[4]) }, }); break; } } //Do work for [TimingPoints] section if (currentSection == "[TimingPoints]") { TimingPoint tempTimingPoint = new TimingPoint(); float[] values = { 0, 0, 4, 0, 0, 100, 0, 0, 0 }; string[] reSplit = line.Split(','); for (int i = 0; i < reSplit.Length; i++) values[i] = (float)Convert.ToDouble(reSplit[i]); tempTimingPoint.Time = (float)Convert.ToDouble(values[0]); tempTimingPoint.BpmDelay = (float)Convert.ToDouble(values[1]); tempTimingPoint.TimeSignature = Convert.ToInt32(values[2]); tempTimingPoint.SampleSet = Convert.ToInt32(values[3]); tempTimingPoint.CustomSampleSet = Convert.ToInt32(values[4]); tempTimingPoint.VolumePercentage = Convert.ToInt32(values[5]); tempTimingPoint.InheritsBPM = !Convert.ToBoolean(Convert.ToInt32(values[6])); tempTimingPoint.VisualOptions = (TimingPointOptions)Convert.ToInt32(values[7]); Info.TimingPoints.Add(tempTimingPoint); } //Do work for [Colours] section if (currentSection == "[Colours]") { string property = line.Substring(0, line.IndexOf(':', 1)).Trim(); string value = line.Substring(line.IndexOf(':', 1) + 1).Trim(); string[] reSplit = value.Split(','); if (property.Length > 5 && property.Substring(0, 5) == "Combo") { Combo newCombo = new Combo { Colour = new Colour { R = Convert.ToInt32(reSplit[0]), G = Convert.ToInt32(reSplit[1]), B = Convert.ToInt32(reSplit[2]) } }; try { newCombo.ComboNumber = Convert.ToInt32(property.Substring(5, 1)); } catch { Debug.Assert(false, "Invalid combonumber at index 5. " + line); continue; } } else if (property.Length > 5 && property == "SliderBorder") { Info.SliderBorder = new Colour { R = Convert.ToInt32(reSplit[0]), G = Convert.ToInt32(reSplit[1]), B = Convert.ToInt32(reSplit[2]) }; } } //Do work for [HitObjects] section if (currentSection == "[HitObjects]") { string[] reSplit = line.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); CircleObject newObject = new CircleObject { Radius = 40 - 4 * (Info.CircleSize - 2), Location = new Point2(Convert.ToInt32(reSplit[0]), Convert.ToInt32(reSplit[1])), StartTime = (float)Convert.ToDouble(reSplit[2]), Type = (HitObjectType)Convert.ToInt32(reSplit[3]), Effect = (EffectType)Convert.ToInt32(reSplit[4]) }; if ((newObject.Type & HitObjectType.Slider) > 0) { newObject = new SliderObject(newObject); ((SliderObject)newObject).Velocity = Info.SliderMultiplier; switch (reSplit[5].Substring(0, 1)) { case "B": ((SliderObject)newObject).Type = SliderType.Bezier; break; case "C": ((SliderObject)newObject).Type = SliderType.CSpline; break; case "L": ((SliderObject)newObject).Type = SliderType.Linear; break; case "P": ((SliderObject)newObject).Type = SliderType.PSpline; break; } string[] pts = reSplit[5].Split(new[] { "|" }, StringSplitOptions.None); //Todo: Check this if (Format <= 4) ((SliderObject)newObject).Points.Add(newObject.Location); //Always exclude index 1, this will contain the type for (int i = 1; i <= pts.Length - 1; i++) { Point2 p = new Point2((float)Convert.ToDouble(pts[i].Substring(0, pts[i].IndexOf(":", StringComparison.InvariantCulture))), (float)Convert.ToDouble(pts[i].Substring(pts[i].IndexOf(":", StringComparison.InvariantCulture) + 1))); ((SliderObject)newObject).Points.Add(p); } ((SliderObject)newObject).RepeatCount = Convert.ToInt32(reSplit[6]); float tempMaxPoints; if (float.TryParse(reSplit[7], out tempMaxPoints)) ((SliderObject)newObject).MaxPoints = tempMaxPoints; } if ((newObject.Type & HitObjectType.Spinner) > 0) { newObject = new SpinnerObject(newObject); ((SpinnerObject)newObject).EndTime = (float)Convert.ToDouble(reSplit[5]); } Info.HitObjects.Add(newObject); } } } //Copy the fields/properties of Info locally foreach (FieldInfo fi in Info.GetType().GetFields()) { FieldInfo ff = GetType().GetField(fi.Name); ff.SetValue(this, fi.GetValue(Info)); } foreach (PropertyInfo pi in Info.GetType().GetProperties()) { PropertyInfo ff = GetType().GetProperty(pi.Name); ff.SetValue(this, pi.GetValue(Info, null), null); } }
private void Parse(string bm) { FileInfo ffii = new FileInfo(bm); Info.Folder = ffii.DirectoryName; Info.Filename = bm; Info.BeatmapHash = MD5FromFile(bm); using (StreamReader sR = new StreamReader(bm)) { string currentSection = ""; while (sR.Peek() != -1) { string line = sR.ReadLine(); //Check for section tag if (line.StartsWith("[")) { currentSection = line; continue; } //Check for commented-out line //or blank lines if (line.StartsWith("//") || line.Length == 0) { continue; } //Check for version string if (line.StartsWith("osu file format")) { Info.Format = Convert.ToInt32(line.Substring(17).Replace(Environment.NewLine, "").Replace(" ", "")); } //Do work for [General], [Metadata], [Difficulty] and [Editor] sections if ((currentSection == "[General]") || (currentSection == "[Metadata]") || (currentSection == "[Difficulty]") || (currentSection == "[Editor]")) { string[] reSplit = line.Split(':'); string cProperty = reSplit[0].TrimEnd(); bool isValidProperty = false; foreach (string k in BM_Sections.Keys) { if (k.Contains(cProperty)) { isValidProperty = true; } } if (!isValidProperty) { continue; } //Check for blank value string cValue = reSplit[1].Trim(); //Import properties into Info switch (cProperty) { case "EditorBookmarks": { string[] marks = cValue.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries); foreach (string m in marks.Where(m => m != "")) { Info.EditorBookmarks.Add(Convert.ToInt32(m)); } } break; case "Bookmarks": { string[] marks = cValue.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries); foreach (string m in marks.Where(m => m != "")) { Info.Bookmarks.Add(Convert.ToInt32(m)); } } break; case "Tags": string[] tags = cValue.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); foreach (string t in tags) { Info.Tags.Add(t); } break; case "Mode": Info.Mode = (GameMode)Convert.ToInt32(cValue); break; case "OverlayPosition": Info.OverlayPosition = (OverlayOptions)Enum.Parse(typeof(OverlayOptions), cValue); break; case "AlwaysShowPlayfield": Info.AlwaysShowPlayfield = Convert.ToBoolean(Convert.ToInt32(cValue)); break; default: FieldInfo fi = Info.GetType().GetField(cProperty); PropertyInfo pi = Info.GetType().GetProperty(cProperty); if (fi != null) { if (fi.FieldType == typeof(float?)) { fi.SetValue(Info, (float?)Convert.ToDouble(cValue)); } if (fi.FieldType == typeof(float)) { fi.SetValue(Info, (float)Convert.ToDouble(cValue)); } else if ((fi.FieldType == typeof(int?)) || (fi.FieldType == typeof(int))) { fi.SetValue(Info, Convert.ToInt32(cValue)); } else if (fi.FieldType == typeof(string)) { fi.SetValue(Info, cValue); } break; } if (pi.PropertyType == typeof(float?)) { pi.SetValue(Info, (float?)Convert.ToDouble(cValue), null); } if (pi.PropertyType == typeof(float)) { pi.SetValue(Info, (float)Convert.ToDouble(cValue), null); } else if ((pi.PropertyType == typeof(int?)) || (pi.PropertyType == typeof(int))) { pi.SetValue(Info, Convert.ToInt32(cValue), null); } else if (pi.PropertyType == typeof(string)) { pi.SetValue(Info, cValue, null); } break; } continue; } //The following are version-dependent, the version is stored as a numeric value inside Info.Format //Do work for [Events] section if (currentSection == "[Events]") { string[] reSplit = line.Split(','); switch (reSplit[0].ToLower()) { case "0": case "1": case "video": Info.Events.Add(new ContentEvent { Type = reSplit[0].ToLower() == "1" || reSplit[0].ToLower() == "video" ? ContentType.Video : ContentType.Image, StartTime = Convert.ToInt32(reSplit[1]), Filename = reSplit[2].Replace("\"", "") }); break; case "2": Info.Events.Add(new BreakEvent { StartTime = Convert.ToInt32(reSplit[1]), EndTime = Convert.ToInt32(reSplit[2]) }); break; case "3": Info.Events.Add(new BackgroundColourEvent { StartTime = Convert.ToInt32(reSplit[1]), Colour = new Colour { R = Convert.ToInt32(reSplit[2]), G = Convert.ToInt32(reSplit[3]), B = Convert.ToInt32(reSplit[4]) }, }); break; } } //Do work for [TimingPoints] section if (currentSection == "[TimingPoints]") { TimingPoint tempTimingPoint = new TimingPoint(); float[] values = { 0, 0, 4, 0, 0, 100, 0, 0, 0 }; string[] reSplit = line.Split(','); for (int i = 0; i < reSplit.Length; i++) { values[i] = (float)Convert.ToDouble(reSplit[i]); } tempTimingPoint.InheritsBPM = !Convert.ToBoolean(Convert.ToInt32(values[6])); tempTimingPoint.beatLength = values[1]; if (values[1] > 0) { tempTimingPoint.bpm = Math.Round(60000 / tempTimingPoint.beatLength); } else if (values[1] < 0) { tempTimingPoint.velocity = Math.Abs(100 / tempTimingPoint.beatLength); } tempTimingPoint.Time = (float)Convert.ToDouble(values[0]); tempTimingPoint.BpmDelay = (float)Convert.ToDouble(values[1]); tempTimingPoint.TimeSignature = Convert.ToInt32(values[2]); tempTimingPoint.SampleSet = Convert.ToInt32(values[3]); tempTimingPoint.CustomSampleSet = Convert.ToInt32(values[4]); tempTimingPoint.VolumePercentage = Convert.ToInt32(values[5]); tempTimingPoint.VisualOptions = (TimingPointOptions)Convert.ToInt32(values[7]); Info.TimingPoints.Add(tempTimingPoint); this.TimingPoints.Add(tempTimingPoint); } for (int i = 1, l = TimingPoints.Count; i < l; i++) { if (TimingPoints[i].bpm == 0) { TimingPoints[i].beatLength = TimingPoints[i - 1].beatLength; TimingPoints[i].bpm = TimingPoints[i - 1].bpm; } } //Do work for [Colours] section if (currentSection == "[Colours]") { string property = line.Substring(0, line.IndexOf(':', 1)).Trim(); string value = line.Substring(line.IndexOf(':', 1) + 1).Trim(); string[] reSplit = value.Split(','); if (property.Length > 5 && property.Substring(0, 5) == "Combo") { Combo newCombo = new Combo { Colour = new Colour { R = Convert.ToInt32(reSplit[0]), G = Convert.ToInt32(reSplit[1]), B = Convert.ToInt32(reSplit[2]) } }; try { newCombo.ComboNumber = Convert.ToInt32(property.Substring(5, 1)); } catch { Debug.Assert(false, "Invalid combonumber at index 5. " + line); continue; } } else if (property.Length > 5 && property == "SliderBorder") { Info.SliderBorder = new Colour { R = Convert.ToInt32(reSplit[0]), G = Convert.ToInt32(reSplit[1]), B = Convert.ToInt32(reSplit[2]) }; } } //Do work for [HitObjects] section if (currentSection == "[HitObjects]") { string[] reSplit = line.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); CircleObject newObject = new CircleObject { BaseLocation = new Point2(Convert.ToInt32(reSplit[0]), Convert.ToInt32(reSplit[1])), StartTime = (float)Convert.ToDouble(reSplit[2]), Type = (HitObjectType)Convert.ToInt32(reSplit[3]), Effect = (EffectType)Convert.ToInt32(reSplit[4]), Beatmap = this, }; if ((newObject.Type & HitObjectType.Slider) > 0) { newObject = new SliderObject(newObject); ((SliderObject)newObject).Velocity = Info.SliderMultiplier * TimingPointByTime(newObject.StartTime).SliderBpm / 600f; switch (reSplit[5].Substring(0, 1)) { case "B": ((SliderObject)newObject).Type = SliderType.Bezier; break; case "C": ((SliderObject)newObject).Type = SliderType.CSpline; break; case "L": //((SliderObject)newObject).Type = SliderType.Linear; ((SliderObject)newObject).Type = SliderType.Bezier; break; case "P": ((SliderObject)newObject).Type = SliderType.PSpline; break; } string[] pts = reSplit[5].Split(new[] { "|" }, StringSplitOptions.None); ((SliderObject)newObject).Points.Add(newObject.BaseLocation + new Point2()); //Always exclude index 1, this will contain the type for (int i = 1; i <= pts.Length - 1; i++) { Point2 p = new Point2((float)Convert.ToDouble(pts[i].Substring(0, pts[i].IndexOf(":", StringComparison.InvariantCulture))), (float)Convert.ToDouble(pts[i].Substring(pts[i].IndexOf(":", StringComparison.InvariantCulture) + 1))); ((SliderObject)newObject).Points.Add(p); } /* * var pxPerBeat = beatmap.SliderMultiplier * 100 * timing.velocity; * var beatsNumber = (hitObject.pixelLength * hitObject.repeatCount) / pxPerBeat; * hitObject.duration = Math.ceil(beatsNumber * timing.beatLength); * hitObject.endTime = hitObject.startTime + hitObject.duration; */ ((SliderObject)newObject).RepeatCount = Convert.ToInt32(reSplit[6]); ((SliderObject)newObject).PixelLength = Convert.ToSingle(reSplit[7]); if (float.TryParse(reSplit[7], out float tempMaxPoints)) { ((SliderObject)newObject).MaxPoints = tempMaxPoints; } ((SliderObject)newObject).CreateCurves(); var timing = TimingPointByTime(newObject.StartTime); var pxPerBeat = Info.SliderMultiplier * 100 * timing.velocity; var beatsNumber = ((SliderObject)newObject).PixelLength * ((SliderObject)newObject).RepeatCount / pxPerBeat; var duration = (int)Math.Ceiling(beatsNumber * timing.beatLength); ((SliderObject)newObject).duration = duration; } if ((newObject.Type & HitObjectType.Spinner) > 0) { newObject = new SpinnerObject(newObject); ((SpinnerObject)newObject).EndTime = (float)Convert.ToDouble(reSplit[5]); } Info.HitObjects.Add(newObject); } } } //Copy the fields/properties of Info locally foreach (FieldInfo fi in Info.GetType().GetFields()) { FieldInfo ff = GetType().GetField(fi.Name); ff.SetValue(this, fi.GetValue(Info)); } foreach (PropertyInfo pi in Info.GetType().GetProperties()) { PropertyInfo ff = GetType().GetProperty(pi.Name); ff.SetValue(this, pi.GetValue(Info, null), null); } }