Example #1
0
        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);
            }
        }
Example #2
0
        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);
            }
        }