Esempio n. 1
0
        private string Run_Program(MapCleanerVm args, BackgroundWorker worker, DoWorkEventArgs _)
        {
            var result = new Classes.Tools.MapCleaner.MapCleanerResult();

            var reader = EditorReaderStuff.GetFullEditorReaderOrNot();

            if (args.Paths.Length == 1)
            {
                var editor = EditorReaderStuff.GetNewestVersionOrNot(args.Paths[0], reader);

                List <TimingPoint> orgininalTimingPoints = editor.Beatmap.BeatmapTiming.TimingPoints.Select(tp => tp.Copy()).ToList();
                int oldTimingPointsCount = editor.Beatmap.BeatmapTiming.TimingPoints.Count;

                result.Add(Classes.Tools.MapCleaner.CleanMap(editor, args.MapCleanerArgs, worker));

                // Update result with removed count
                int removed = oldTimingPointsCount - editor.Beatmap.BeatmapTiming.TimingPoints.Count;
                result.TimingPointsRemoved += removed;

                List <TimingPoint> newTimingPoints = editor.Beatmap.BeatmapTiming.TimingPoints;
                Monitor_Differences(orgininalTimingPoints, newTimingPoints);

                // Save the file
                editor.SaveFile();
            }
            else
            {
                foreach (string path in args.Paths)
                {
                    var editor = EditorReaderStuff.GetNewestVersionOrNot(path, reader);

                    int oldTimingPointsCount = editor.Beatmap.BeatmapTiming.TimingPoints.Count;

                    result.Add(Classes.Tools.MapCleaner.CleanMap(editor, args.MapCleanerArgs, worker));

                    // Update result with removed count
                    int removed = oldTimingPointsCount - editor.Beatmap.BeatmapTiming.TimingPoints.Count;
                    result.TimingPointsRemoved += removed;

                    // Save the file
                    editor.SaveFile();
                }
            }

            // Do stuff
            if (args.Quick)
            {
                RunFinished?.Invoke(this, new RunToolCompletedEventArgs(true, reader != null));
            }

            // Make an accurate message
            string message = $"Successfully {(result.TimingPointsRemoved < 0 ? "added" : "removed")} {Math.Abs(result.TimingPointsRemoved)} {(Math.Abs(result.TimingPointsRemoved) == 1 ? "greenline" : "greenlines")}" +
                             (args.MapCleanerArgs.ResnapObjects ? $" and resnapped {result.ObjectsResnapped} {(result.ObjectsResnapped == 1 ? "object" : "objects")}" : "") +
                             (args.MapCleanerArgs.RemoveUnusedSamples ? $" and removed {result.SamplesRemoved} unused {(result.SamplesRemoved == 1 ? "sample" : "samples")}" : "") + "!";

            return(args.Quick ? string.Empty : message);
        }
        private string PlaceHitsounds(Arguments args, BackgroundWorker worker, DoWorkEventArgs _)
        {
            if (args.Zones.Count == 0)
            {
                return("There are no zones!");
            }

            var reader = EditorReaderStuff.GetFullEditorReaderOrNot();

            foreach (string path in args.Paths)
            {
                var      editor   = EditorReaderStuff.GetNewestVersionOrNot(path, reader);
                Beatmap  beatmap  = editor.Beatmap;
                Timeline timeline = beatmap.GetTimeline();

                for (int i = 0; i < timeline.TimelineObjects.Count; i++)
                {
                    var tlo = timeline.TimelineObjects[i];

                    var    column = args.Zones.FirstOrDefault();
                    double best   = double.MaxValue;
                    foreach (var c in args.Zones)
                    {
                        double dist = c.Distance(tlo.Origin.Pos);
                        if (dist < best)
                        {
                            best   = dist;
                            column = c;
                        }
                    }

                    if (column == null)
                    {
                        continue;
                    }

                    tlo.Filename     = column.Filename;
                    tlo.SampleSet    = column.SampleSet;
                    tlo.AdditionSet  = column.AdditionsSet;
                    tlo.CustomIndex  = column.CustomIndex;
                    tlo.SampleVolume = 0;
                    tlo.SetHitsound(column.Hitsound);
                    tlo.HitsoundsToOrigin();

                    UpdateProgressBar(worker, (int)(100f * i / beatmap.HitObjects.Count));
                }

                // Save the file
                editor.SaveFile();
            }

            // Do stuff
            RunFinished?.Invoke(this, new RunToolCompletedEventArgs(true, reader != null, args.Quick));

            return(args.Quick ? "" : "Done!");
        }
        private static string Copy_Metadata(MetadataManagerVm arg, BackgroundWorker worker, DoWorkEventArgs _)
        {
            var paths    = arg.ExportPath.Split('|');
            var mapsDone = 0;

            var reader = EditorReaderStuff.GetFullEditorReaderOrNot();

            foreach (var path in paths)
            {
                var editor  = EditorReaderStuff.GetNewestVersionOrNot(path, reader);
                var beatmap = editor.Beatmap;

                beatmap.Metadata["ArtistUnicode"].Value = arg.Artist;
                beatmap.Metadata["Artist"].Value        = arg.RomanisedArtist;
                beatmap.Metadata["TitleUnicode"].Value  = arg.Title;
                beatmap.Metadata["Title"].Value         = arg.RomanisedTitle;
                beatmap.Metadata["Creator"].Value       = arg.BeatmapCreator;
                beatmap.Metadata["Source"].Value        = arg.Source;
                beatmap.Metadata["Tags"].Value          = arg.Tags;

                beatmap.General["PreviewTime"] = new TValue(arg.PreviewTime.ToRoundInvariant());
                if (arg.UseComboColours)
                {
                    beatmap.ComboColours = new List <ComboColour>(arg.ComboColours);
                    beatmap.SpecialColours.Clear();
                    foreach (var specialColour in arg.SpecialColours)
                    {
                        beatmap.SpecialColours.Add(specialColour.Name, specialColour);
                    }
                }

                // Save the file with name update because we updated the metadata
                editor.SaveFileWithNameUpdate();

                // Update progressbar
                if (worker != null && worker.WorkerReportsProgress)
                {
                    worker.ReportProgress(++mapsDone * 100 / paths.Length);
                }
            }

            // Make an accurate message
            var message = $"Successfully exported metadata to {mapsDone} {(mapsDone == 1 ? "beatmap" : "beatmaps")}!";

            return(message);
        }
Esempio n. 4
0
        private string ExportPattern(PatternGalleryVm args, BackgroundWorker worker, DoWorkEventArgs _)
        {
            var reader = EditorReaderStuff.GetFullEditorReaderOrNot();
            var editor = EditorReaderStuff.GetNewestVersionOrNot(IOHelper.GetCurrentBeatmapOrCurrentBeatmap(), reader);

            var pattern = args.Patterns.FirstOrDefault(o => o.IsSelected);

            if (pattern == null)
            {
                throw new Exception("No pattern has been selected to export.");
            }

            var patternBeatmap = pattern.GetPatternBeatmap(args.FileHandler);

            var patternPlacer = args.OsuPatternPlacer;

            if (reader != null)
            {
                patternPlacer.PlaceOsuPatternAtTime(patternBeatmap, editor.Beatmap, reader.EditorTime(), false);
            }
            else
            {
                patternPlacer.PlaceOsuPattern(patternBeatmap, editor.Beatmap, protectBeatmapPattern: false);
            }

            editor.SaveFile();

            // Increase pattern use count and time
            pattern.UseCount++;
            pattern.LastUsedTime = DateTime.Now;

            // Complete progressbar
            if (worker != null && worker.WorkerReportsProgress)
            {
                worker.ReportProgress(100);
            }

            // Do stuff
            if (args.Quick)
            {
                RunFinished?.Invoke(this, new RunToolCompletedEventArgs(true, reader != null));
            }

            return("Successfully exported pattern!");
        }
        private string Copy_Timing(TimingCopierVm arg, BackgroundWorker worker, DoWorkEventArgs _)
        {
            string[] paths    = arg.ExportPath.Split('|');
            int      mapsDone = 0;

            var reader = EditorReaderStuff.GetFullEditorReaderOrNot();

            foreach (string exportPath in paths)
            {
                var editorTo   = EditorReaderStuff.GetNewestVersionOrNot(exportPath, reader);
                var editorFrom = EditorReaderStuff.GetNewestVersionOrNot(arg.ImportPath, reader);

                Beatmap beatmapTo   = editorTo.Beatmap;
                Beatmap beatmapFrom = editorFrom.Beatmap;

                Timing timingTo   = beatmapTo.BeatmapTiming;
                Timing timingFrom = beatmapFrom.BeatmapTiming;

                // Get markers for hitobjects if mode 1 is used
                List <Marker> markers = new List <Marker>();
                if (arg.ResnapMode == "Number of beats between objects stays the same")
                {
                    markers = GetMarkers(beatmapTo, timingTo);
                }

                // Rid the beatmap of redlines
                // If a greenline exists at the same time as a redline then the redline ceizes to exist
                // Else convert the redline to a greenline: Inherited = false & MpB = -100
                List <TimingPoint> removeList = new List <TimingPoint>();
                foreach (TimingPoint redline in timingTo.GetAllRedlines())
                {
                    TimingPoint greenlineHere = timingTo.GetGreenlineAtTime(redline.Offset);
                    if (greenlineHere.Offset == redline.Offset)
                    {
                        removeList.Add(redline);
                    }
                    else
                    {
                        redline.Uninherited = false;
                        redline.MpB         = -100;
                    }
                }
                foreach (TimingPoint tp in removeList)
                {
                    timingTo.TimingPoints.Remove(tp);
                }

                // Make new timing points changes
                List <TimingPointsChange> timingPointsChanges = new List <TimingPointsChange>();

                // Add redlines
                List <TimingPoint> redlines = timingFrom.GetAllRedlines();
                foreach (TimingPoint tp in redlines)
                {
                    timingPointsChanges.Add(new TimingPointsChange(tp, mpb: true, meter: true, inherited: true, omitFirstBarLine: true));
                }

                // Apply timing changes
                TimingPointsChange.ApplyChanges(timingTo, timingPointsChanges);

                if (arg.ResnapMode == "Number of beats between objects stays the same")
                {
                    redlines = timingTo.GetAllRedlines();
                    List <double> newBookmarks = new List <double>();
                    double        lastTime     = redlines.FirstOrDefault().Offset;
                    foreach (Marker marker in markers)
                    {
                        // Get redlines between this and last marker
                        TimingPoint redline = timingTo.GetRedlineAtTime(lastTime, redlines.FirstOrDefault());

                        double beatsFromLastTime = marker.BeatsFromLastMarker;
                        while (true)
                        {
                            List <TimingPoint> redlinesBetween = redlines.Where(o => o.Offset <= lastTime + redline.MpB * beatsFromLastTime && o.Offset > lastTime).ToList();

                            if (redlinesBetween.Count == 0)
                            {
                                break;
                            }

                            TimingPoint first = redlinesBetween.First();
                            double      diff  = first.Offset - lastTime;
                            beatsFromLastTime -= diff / redline.MpB;

                            redline  = first;
                            lastTime = first.Offset;
                        }

                        // Last time is the time of the last redline in between
                        double newTime = lastTime + redline.MpB * beatsFromLastTime;
                        newTime     = timingTo.Resnap(newTime, arg.Snap1, arg.Snap2, firstTp: redlines.FirstOrDefault());
                        marker.Time = newTime;

                        lastTime = marker.Time;
                    }

                    // Add the bookmarks
                    foreach (Marker marker in markers)
                    {
                        // Check whether the marker is a bookmark
                        if (marker.Object is double)
                        {
                            // Don't resnap bookmarks
                            newBookmarks.Add((double)marker.Object);
                        }
                    }
                    beatmapTo.SetBookmarks(newBookmarks);
                }
                else if (arg.ResnapMode == "Just resnap")
                {
                    // Resnap hitobjects
                    foreach (HitObject ho in beatmapTo.HitObjects)
                    {
                        ho.ResnapSelf(timingTo, arg.Snap1, arg.Snap2, firstTp: redlines.FirstOrDefault());
                        ho.ResnapEnd(timingTo, arg.Snap1, arg.Snap2, firstTp: redlines.FirstOrDefault());
                    }

                    // Resnap greenlines
                    foreach (TimingPoint tp in timingTo.GetAllGreenlines())
                    {
                        tp.ResnapSelf(timingTo, arg.Snap1, arg.Snap2, firstTP: redlines.FirstOrDefault());
                    }
                }
                else
                {
                    // Don't move objects
                }

                // Save the file
                editorTo.SaveFile();

                // Update progressbar
                if (worker != null && worker.WorkerReportsProgress)
                {
                    worker.ReportProgress(++mapsDone * 100 / paths.Length);
                }
            }

            // Make an accurate message
            string message = $"Successfully copied timing to {mapsDone} {(mapsDone == 1 ? "beatmap" : "beatmaps")}!";

            return(message);
        }
Esempio n. 6
0
        private string Run_Program(AutoFailDetectorVm args, BackgroundWorker worker, DoWorkEventArgs _)
        {
            var reader  = EditorReaderStuff.GetFullEditorReaderOrNot();
            var editor  = EditorReaderStuff.GetNewestVersionOrNot(args.Paths[0], reader);
            var beatmap = editor.Beatmap;

            // Get approach time and radius of the 50 score hit window
            var ar = args.ApproachRateOverride == -1
                ? editor.Beatmap.Difficulty["ApproachRate"].DoubleValue
                : args.ApproachRateOverride;
            var approachTime = (int)Beatmap.GetApproachTime(ar);

            var od = args.OverallDifficultyOverride == -1
                ? editor.Beatmap.Difficulty["OverallDifficulty"].DoubleValue
                : args.OverallDifficultyOverride;
            var window50 = (int)Math.Ceiling(200 - 10 * od);

            // Start time and end time
            var mapStartTime = (int)beatmap.GetMapStartTime();
            var mapEndTime   = (int)beatmap.GetMapEndTime();
            var autoFailTime = (int)beatmap.GetAutoFailCheckTime();

            // Detect auto-fail
            var autoFailDetector = new Classes.Tools.AutoFailDetector(beatmap.HitObjects,
                                                                      mapStartTime, mapEndTime, autoFailTime,
                                                                      approachTime, window50, args.PhysicsUpdateLeniency);

            var autoFail = autoFailDetector.DetectAutoFail();

            if (worker != null && worker.WorkerReportsProgress)
            {
                worker.ReportProgress(33);
            }

            // Fix auto-fail
            if (args.GetAutoFailFix)
            {
                var placedFix = autoFailDetector.AutoFailFixDialogue(args.AutoPlaceFix);

                if (placedFix)
                {
                    editor.SaveFile();
                }
            }

            if (worker != null && worker.WorkerReportsProgress)
            {
                worker.ReportProgress(67);
            }

            // Set the timeline lists
            _unloadingObjects          = args.ShowUnloadingObjects ? autoFailDetector.UnloadingObjects : new List <double>();
            _potentialUnloadingObjects = args.ShowPotentialUnloadingObjects ? autoFailDetector.PotentialUnloadingObjects : new List <double>();
            _potentialDisruptors       = args.ShowPotentialDisruptors ? autoFailDetector.Disruptors : new List <double>();

            // Set end time for the timeline
            _endTimeMonitor = mapEndTime;

            // Complete progressbar
            if (worker != null && worker.WorkerReportsProgress)
            {
                worker.ReportProgress(100);
            }

            // Do stuff
            RunFinished?.Invoke(this, new RunToolCompletedEventArgs(true, false, args.Quick));

            return(autoFail ? $"{autoFailDetector.UnloadingObjects.Count} unloading objects detected and {autoFailDetector.PotentialUnloadingObjects.Count} potential unloading objects detected!" :
                   autoFailDetector.PotentialUnloadingObjects.Count > 0 ? $"No auto-fail, but {autoFailDetector.PotentialUnloadingObjects.Count} potential unloading objects detected." :
                   "No auto-fail detected.");
        }
Esempio n. 7
0
        private string Copy_Hitsounds(HitsoundCopierVm arg, BackgroundWorker worker)
        {
            var doMutedIndex = arg.MutedIndex >= 0;

            var paths        = arg.PathTo.Split('|');
            var mapsDone     = 0;
            var sampleSchema = new SampleSchema();

            var reader = EditorReaderStuff.GetFullEditorReaderOrNot();

            foreach (var pathTo in paths)
            {
                BeatmapEditor editorTo  = EditorReaderStuff.GetNewestVersionOrNot(pathTo, reader);;
                Beatmap       beatmapTo = editorTo.Beatmap;
                Beatmap       beatmapFrom;

                if (!string.IsNullOrEmpty(arg.PathFrom))
                {
                    var editorFrom = EditorReaderStuff.GetNewestVersionOrNot(arg.PathFrom, reader);
                    beatmapFrom = editorFrom.Beatmap;
                }
                else
                {
                    // Copy from an empty beatmap similar to the map to copy to
                    beatmapFrom = beatmapTo.DeepCopy();
                    beatmapFrom.HitObjects.Clear();
                    beatmapFrom.BeatmapTiming.Clear();
                }

                Timeline processedTimeline;

                if (arg.CopyMode == 0)
                {
                    // Every defined hitsound and sampleset on hitsound gets copied to their copyTo destination
                    // Timelines
                    var tlTo   = beatmapTo.GetTimeline();
                    var tlFrom = beatmapFrom.GetTimeline();

                    var volumeMuteTimes = arg.CopyVolumes && arg.AlwaysPreserve5Volume ? new List <double>() : null;

                    if (arg.CopyHitsounds)
                    {
                        ResetHitObjectHitsounds(beatmapTo);
                        CopyHitsounds(arg, tlFrom, tlTo);
                    }

                    // Save tlo times where timingpoint volume is 5%
                    // Timingpointchange all the undefined tlo from copyFrom
                    volumeMuteTimes?.AddRange(from tloTo in tlTo.TimelineObjects
                                              where tloTo.CanCopy && Math.Abs(tloTo.SampleVolume) < Precision.DOUBLE_EPSILON &&
                                              Math.Abs(tloTo.FenoSampleVolume - 5) < Precision.DOUBLE_EPSILON
                                              select tloTo.Time);

                    // Volumes and samplesets and customindices greenlines get copied with timingpointchanges and allafter enabled
                    var timingPointsChanges = beatmapFrom.BeatmapTiming.TimingPoints.Select(tp =>
                                                                                            new TimingPointsChange(tp, sampleset: arg.CopySampleSets, index: arg.CopySampleSets,
                                                                                                                   volume: arg.CopyVolumes)).ToList();

                    // Apply the timingpoint changes
                    TimingPointsChange.ApplyChanges(beatmapTo.BeatmapTiming, timingPointsChanges, true);

                    processedTimeline = tlTo;

                    // Return 5% volume to tlo that had it before
                    if (volumeMuteTimes != null)
                    {
                        var timingPointsChangesMute = new List <TimingPointsChange>();
                        processedTimeline.GiveTimingPoints(beatmapTo.BeatmapTiming);

                        // Exclude objects which use their own sample volume property instead
                        foreach (var tloTo in processedTimeline.TimelineObjects.Where(o => Math.Abs(o.SampleVolume) < Precision.DOUBLE_EPSILON))
                        {
                            if (volumeMuteTimes.Contains(tloTo.Time))
                            {
                                // Add timingpointschange to copy timingpoint hitsounds
                                var tp = tloTo.HitsoundTimingPoint.Copy();
                                tp.Offset = tloTo.Time;
                                tp.Volume = 5;
                                timingPointsChangesMute.Add(new TimingPointsChange(tp, volume: true));
                            }
                            else
                            {
                                // Add timingpointschange to preserve index and volume
                                var tp = tloTo.HitsoundTimingPoint.Copy();
                                tp.Offset = tloTo.Time;
                                tp.Volume = tloTo.FenoSampleVolume;
                                timingPointsChangesMute.Add(new TimingPointsChange(tp, volume: true));
                            }
                        }

                        // Apply the timingpoint changes
                        TimingPointsChange.ApplyChanges(beatmapTo.BeatmapTiming, timingPointsChangesMute);
                    }
                }
                else
                {
                    // Smarty mode
                    // Copy the defined hitsounds literally (not feno, that will be reserved for cleaner). Only the tlo that have been defined by copyFrom get overwritten.
                    var tlTo   = beatmapTo.GetTimeline();
                    var tlFrom = beatmapFrom.GetTimeline();

                    var timingPointsChanges = new List <TimingPointsChange>();
                    var mode         = (GameMode)beatmapTo.General["Mode"].IntValue;
                    var mapDir       = editorTo.GetParentFolder();
                    var firstSamples = HitsoundImporter.AnalyzeSamples(mapDir);

                    if (arg.CopyHitsounds)
                    {
                        CopyHitsounds(arg, beatmapTo, tlFrom, tlTo, timingPointsChanges, mode, mapDir, firstSamples, ref sampleSchema);
                    }

                    if (arg.CopyBodyHitsounds)
                    {
                        // Remove timingpoints in beatmapTo that are in a sliderbody/spinnerbody for both beatmapTo and BeatmapFrom
                        foreach (var tp in from ho in beatmapTo.HitObjects
                                 from tp in ho.BodyHitsounds
                                 where beatmapFrom.HitObjects.Any(o => o.Time <tp.Offset && o.EndTime> tp.Offset)
                                 where !tp.Uninherited
                                 select tp)
                        {
                            beatmapTo.BeatmapTiming.Remove(tp);
                        }

                        // Get timingpointschanges for every timingpoint from beatmapFrom that is in a sliderbody/spinnerbody for both beatmapTo and BeatmapFrom
                        timingPointsChanges.AddRange(from ho in beatmapFrom.HitObjects
                                                     from tp in ho.BodyHitsounds
                                                     where beatmapTo.HitObjects.Any(o => o.Time <tp.Offset && o.EndTime> tp.Offset)
                                                     select new TimingPointsChange(tp.Copy(), sampleset: arg.CopySampleSets, index: arg.CopySampleSets,
                                                                                   volume: arg.CopyVolumes));
                    }

                    // Apply the timingpoint changes
                    TimingPointsChange.ApplyChanges(beatmapTo.BeatmapTiming, timingPointsChanges);

                    processedTimeline = tlTo;
                }

                if (arg.CopyStoryboardedSamples)
                {
                    if (arg.CopyMode == 0)
                    {
                        beatmapTo.StoryboardSoundSamples.Clear();
                    }

                    beatmapTo.GiveObjectsGreenlines();
                    processedTimeline.GiveTimingPoints(beatmapTo.BeatmapTiming);

                    var mapDir       = editorTo.GetParentFolder();
                    var firstSamples = HitsoundImporter.AnalyzeSamples(mapDir, true);

                    var samplesTo = new HashSet <StoryboardSoundSample>(beatmapTo.StoryboardSoundSamples);
                    var mode      = (GameMode)beatmapTo.General["Mode"].IntValue;

                    foreach (var sampleFrom in beatmapFrom.StoryboardSoundSamples)
                    {
                        if (arg.IgnoreHitsoundSatisfiedSamples)
                        {
                            var tloHere = processedTimeline.TimelineObjects.FindAll(o =>
                                                                                    Math.Abs(o.Time - sampleFrom.StartTime) <= arg.TemporalLeniency);
                            var samplesHere = new HashSet <string>();
                            foreach (var tlo in tloHere)
                            {
                                foreach (var filename in tlo.GetPlayingFilenames(mode))
                                {
                                    var samplePath      = Path.Combine(mapDir, filename);
                                    var fullPathExtLess = Path.Combine(Path.GetDirectoryName(samplePath),
                                                                       Path.GetFileNameWithoutExtension(samplePath));

                                    if (firstSamples.Keys.Contains(fullPathExtLess))
                                    {
                                        samplePath = firstSamples[fullPathExtLess];
                                    }

                                    samplesHere.Add(samplePath);
                                }
                            }

                            var sbSamplePath      = Path.Combine(mapDir, sampleFrom.FilePath);
                            var sbFullPathExtLess = Path.Combine(Path.GetDirectoryName(sbSamplePath),
                                                                 Path.GetFileNameWithoutExtension(sbSamplePath));

                            if (firstSamples.Keys.Contains(sbFullPathExtLess))
                            {
                                sbSamplePath = firstSamples[sbFullPathExtLess];
                            }

                            if (samplesHere.Contains(sbSamplePath))
                            {
                                continue;
                            }
                        }

                        // Add the StoryboardSoundSamples from beatmapFrom to beatmapTo if it doesn't already have the sample
                        if (!samplesTo.Contains(sampleFrom))
                        {
                            beatmapTo.StoryboardSoundSamples.Add(sampleFrom);
                        }
                    }

                    // Sort the storyboarded samples
                    beatmapTo.StoryboardSoundSamples.Sort();
                }

                if (arg.MuteSliderends)
                {
                    var timingPointsChanges = new List <TimingPointsChange>();
                    beatmapTo.GiveObjectsGreenlines();
                    processedTimeline.GiveTimingPoints(beatmapTo.BeatmapTiming);

                    foreach (var tloTo in processedTimeline.TimelineObjects)
                    {
                        if (FilterMuteTlo(tloTo, beatmapTo, arg))
                        {
                            // Set volume to 5%, remove all hitsounds, apply customindex and sampleset
                            tloTo.SampleSet   = arg.MutedSampleSet;
                            tloTo.AdditionSet = 0;
                            tloTo.Normal      = false;
                            tloTo.Whistle     = false;
                            tloTo.Finish      = false;
                            tloTo.Clap        = false;

                            tloTo.HitsoundsToOrigin();

                            // Add timingpointschange to copy timingpoint hitsounds
                            var tp = tloTo.HitsoundTimingPoint.Copy();
                            tp.Offset      = tloTo.Time;
                            tp.SampleSet   = arg.MutedSampleSet;
                            tp.SampleIndex = arg.MutedIndex;
                            tp.Volume      = 5;
                            timingPointsChanges.Add(new TimingPointsChange(tp, sampleset: true, index: doMutedIndex,
                                                                           volume: true));
                        }
                        else
                        {
                            // Add timingpointschange to preserve index and volume and sampleset
                            var tp = tloTo.HitsoundTimingPoint.Copy();
                            tp.Offset = tloTo.Time;
                            timingPointsChanges.Add(new TimingPointsChange(tp, sampleset: true, index: doMutedIndex,
                                                                           volume: true));
                        }
                    }

                    // Apply the timingpoint changes
                    TimingPointsChange.ApplyChanges(beatmapTo.BeatmapTiming, timingPointsChanges);
                }

                // Save the file
                editorTo.SaveFile();

                // Export the sample schema if there are samples
                if (sampleSchema.Count > 0)
                {
                    string exportFolder = MainWindow.ExportPath;

                    DirectoryInfo di = new DirectoryInfo(exportFolder);
                    foreach (FileInfo file in di.GetFiles())
                    {
                        file.Delete();
                    }

                    HitsoundExporter.ExportSampleSchema(sampleSchema, exportFolder);

                    System.Diagnostics.Process.Start(exportFolder);
                }

                // Update progressbar
                if (worker != null && worker.WorkerReportsProgress)
                {
                    worker.ReportProgress(++mapsDone * 100 / paths.Length);
                }
            }

            return("Done!");
        }
        private string Adjust_Timing(Arguments arg, BackgroundWorker worker, DoWorkEventArgs _)
        {
            // Count
            int RedlinesAdded = 0;

            var reader = EditorReaderStuff.GetFullEditorReaderOrNot();

            foreach (string path in arg.Paths)
            {
                // Open beatmap
                var     editor  = EditorReaderStuff.GetNewestVersionOrNot(path, reader);
                Beatmap beatmap = editor.Beatmap;
                Timing  timing  = beatmap.BeatmapTiming;

                // Get all the times to snap
                List <Marker> markers = new List <Marker>();
                if (arg.Objects)
                {
                    foreach (HitObject ho in beatmap.HitObjects)
                    {
                        markers.Add(new Marker(ho.Time));
                    }
                }
                if (arg.Bookmarks)
                {
                    foreach (double time in beatmap.GetBookmarks())
                    {
                        markers.Add(new Marker(time));
                    }
                }
                if (arg.Greenlines)
                {
                    // Get the offsets of greenlines
                    foreach (TimingPoint tp in timing.TimingPoints)
                    {
                        if (tp.Uninherited == false)
                        {
                            markers.Add(new Marker(tp.Offset));
                        }
                    }
                }
                if (arg.Redlines)
                {
                    // Get the offsets of redlines
                    foreach (TimingPoint tp in timing.TimingPoints)
                    {
                        if (tp.Uninherited == true)
                        {
                            markers.Add(new Marker(tp.Offset));
                        }
                    }
                }

                // Update progressbar
                if (worker != null && worker.WorkerReportsProgress)
                {
                    worker.ReportProgress(20);
                }

                // Sort the markers
                markers = markers.OrderBy(o => o.Time).ToList();

                // Calculate the beats between time and the last time or redline for each time
                // Time the same is 0
                // Time a little after is smallest snap
                for (int i = 0; i < markers.Count; i++)
                {
                    Marker marker = markers[i];
                    double time   = marker.Time;

                    TimingPoint redline = timing.GetRedlineAtTime(time - 1);

                    // Resnap to that redline only
                    double resnappedTime = timing.Resnap(time, arg.BeatDivisors, false, tp: redline);

                    // Calculate beats from the redline
                    double beatsFromRedline = (resnappedTime - redline.Offset) / redline.MpB;

                    // Avoid problems
                    if (MathHelper.ApproximatelyEquivalent(beatsFromRedline, 0, 0.0001))
                    {
                        beatsFromRedline = arg.BeatDivisors.Max(o => o.GetValue());
                    }
                    if (time == redline.Offset)
                    {
                        beatsFromRedline = 0;
                    }

                    // Initialize the beats from last marker
                    double beatsFromLastMarker = beatsFromRedline;

                    // Get the times between redline and this time
                    List <Marker> timesBefore = markers.Where(o => o.Time <time && o.Time> redline.Offset).ToList();

                    if (timesBefore.Count > 0)
                    {
                        // Get the last time info
                        double lastTime       = timesBefore.Last().Time;
                        double resnappedTimeL = timing.Resnap(lastTime, arg.BeatDivisors, false);

                        // Change the beats from last marker
                        beatsFromLastMarker = (resnappedTime - resnappedTimeL) / redline.MpB;

                        // Avoid problems
                        if (MathHelper.ApproximatelyEquivalent(beatsFromLastMarker, 0, 0.0001))
                        {
                            beatsFromLastMarker = arg.BeatDivisors.Max(o => o.GetValue());
                        }
                        if (lastTime == time)
                        {
                            beatsFromLastMarker = 0;
                        }
                    }

                    // Set the variable
                    marker.BeatsFromLastMarker = beatsFromLastMarker;
                }

                // Remove redlines except the first redline
                if (!arg.Redlines)
                {
                    var first = timing.TimingPoints.FirstOrDefault(o => o.Uninherited);
                    timing.RemoveAll(o => o.Uninherited && o != first);
                }

                // Update progressbar
                if (worker != null && worker.WorkerReportsProgress)
                {
                    worker.ReportProgress(40);
                }

                // Loop through all the markers
                for (int i = 0; i < markers.Count; i++)
                {
                    Marker marker = markers[i];
                    double time   = marker.Time;

                    TimingPoint redline = timing.GetRedlineAtTime(time - 1);

                    double beatsFromLastMarker = arg.BeatsBetween != -1 ? arg.BeatsBetween : marker.BeatsFromLastMarker;

                    // Skip if 0 beats from last marker
                    if (beatsFromLastMarker == 0)
                    {
                        continue;
                    }

                    // Get the times between redline and this time including this time
                    List <Marker> markersBefore = markers.Where(o => o.Time <time && o.Time> redline.Offset).ToList();
                    markersBefore.Add(marker);

                    // Calculate MpB
                    // Average MpB from timesBefore and use time from redline
                    double mpb = 0;
                    double beatsFromRedline = 0;
                    foreach (Marker markerB in markersBefore)
                    {
                        beatsFromRedline += markerB.BeatsFromLastMarker;
                        mpb += GetMpB(markerB.Time - redline.Offset, beatsFromRedline, 0);
                    }
                    mpb /= markersBefore.Count;

                    // Check if this MpB doesn't make the markers go offsnap too far
                    bool canChangeRedline = CheckMpB(mpb, markersBefore, redline, arg);

                    // Make changes
                    if (canChangeRedline)
                    {
                        // Round the MpB to human values first
                        mpb = HumanRoundMpB(mpb, markersBefore, redline, arg);

                        // Change the MpB of the redline
                        redline.MpB = mpb;
                    }
                    else
                    {
                        // Get the last time info and not the current
                        markersBefore.Remove(marker);
                        double lastTime = markersBefore.Last().Time;

                        // Make new redline
                        TimingPoint newRedline    = redline.Copy();
                        TimingPoint lastHitsounds = timing.GetTimingPointAtTime(lastTime + 5);
                        newRedline.Offset           = lastTime;
                        newRedline.OmitFirstBarLine = arg.OmitBarline; // Set omit to the argument
                        newRedline.Kiai             = lastHitsounds.Kiai;
                        newRedline.SampleIndex      = lastHitsounds.SampleIndex;
                        newRedline.SampleSet        = lastHitsounds.SampleSet;
                        newRedline.Volume           = lastHitsounds.Volume;
                        timing.Add(newRedline);

                        // Set the MpB
                        newRedline.MpB = GetMpB(time - lastTime, beatsFromLastMarker, arg.Leniency);

                        // Update the counter
                        RedlinesAdded++;
                    }

                    // Update progressbar
                    if (worker != null && worker.WorkerReportsProgress)
                    {
                        worker.ReportProgress(i * 60 / markers.Count + 40);
                    }
                }

                // Save the file
                editor.SaveFile();
            }


            // Complete progressbar
            if (worker != null && worker.WorkerReportsProgress)
            {
                worker.ReportProgress(100);
            }

            // Make an accurate message
            string message = "Successfully added ";

            message += RedlinesAdded;
            if (Math.Abs(RedlinesAdded) == 1)
            {
                message += " redlines!";
            }
            else
            {
                message += " redlines!";
            }
            return(message);
        }
Esempio n. 9
0
        private string ExportPattern(PatternGalleryVm args, BackgroundWorker worker, DoWorkEventArgs _)
        {
            EditorReader reader;
            double       exportTime       = 0;
            bool         usePatternOffset = false;

            switch (args.ExportTimeMode)
            {
            case ExportTimeMode.Current:
                try {
                    reader     = EditorReaderStuff.GetFullEditorReader();
                    exportTime = reader.EditorTime();
                }
                catch (Exception e) {
                    throw new Exception("Could not fetch the current editor time.", e);
                }
                break;

            case ExportTimeMode.Pattern:
                reader           = EditorReaderStuff.GetFullEditorReaderOrNot();
                usePatternOffset = true;
                break;

            case ExportTimeMode.Custom:
                reader     = EditorReaderStuff.GetFullEditorReaderOrNot();
                exportTime = args.CustomExportTime;
                break;

            default:
                throw new ArgumentOutOfRangeException(nameof(ExportTimeMode), "Invalid value encountered");
            }

            var editor = EditorReaderStuff.GetNewestVersionOrNot(args.Paths[0], reader);

            var patternCount = args.Patterns.Count(o => o.IsSelected);

            if (patternCount == 0)
            {
                throw new Exception("No pattern has been selected to export.");
            }

            var patternPlacer = args.OsuPatternPlacer;

            foreach (var pattern in args.Patterns.Where(o => o.IsSelected))
            {
                var patternBeatmap = pattern.GetPatternBeatmap(args.FileHandler);

                if (usePatternOffset)
                {
                    patternPlacer.PlaceOsuPattern(patternBeatmap, editor.Beatmap, protectBeatmapPattern: false);
                }
                else
                {
                    patternPlacer.PlaceOsuPatternAtTime(patternBeatmap, editor.Beatmap, exportTime, false);
                }

                // Increase pattern use count and time
                pattern.UseCount++;
                pattern.LastUsedTime = DateTime.Now;
            }

            editor.SaveFile();

            // Complete progressbar
            if (worker != null && worker.WorkerReportsProgress)
            {
                worker.ReportProgress(100);
            }

            // Do stuff
            RunFinished?.Invoke(this, new RunToolCompletedEventArgs(true, reader != null, args.Quick));

            return("Successfully exported pattern!");
        }
        private string TransformProperties(PropertyTransformerVm vm, BackgroundWorker worker, DoWorkEventArgs _)
        {
            var reader = EditorReaderStuff.GetFullEditorReaderOrNot();

            foreach (string path in vm.ExportPaths)
            {
                Editor editor;
                if (Path.GetExtension(path) == ".osb")
                {
                    editor = new StoryboardEditor(path);
                }
                else
                {
                    editor = EditorReaderStuff.GetNewestVersionOrNot(path, reader);
                }

                if (editor is BeatmapEditor beatmapEditor)
                {
                    Beatmap beatmap = beatmapEditor.Beatmap;

                    List <TimingPointsChange> timingPointsChanges = new List <TimingPointsChange>();
                    foreach (TimingPoint tp in beatmap.BeatmapTiming.TimingPoints)
                    {
                        // Offset
                        if (vm.TimingpointOffsetMultiplier != 1 || vm.TimingpointOffsetOffset != 0)
                        {
                            if (Filter(tp.Offset, tp.Offset, vm))
                            {
                                tp.Offset = Math.Round(tp.Offset * vm.TimingpointOffsetMultiplier +
                                                       vm.TimingpointOffsetOffset);
                            }
                        }

                        // BPM
                        if (vm.TimingpointBPMMultiplier != 1 || vm.TimingpointBPMOffset != 0)
                        {
                            if (tp.Uninherited)
                            {
                                if (Filter(tp.GetBpm(), tp.Offset, vm))
                                {
                                    double newBPM = tp.GetBpm() * vm.TimingpointBPMMultiplier + vm.TimingpointBPMOffset;
                                    newBPM = vm.ClipProperties
                                        ? MathHelper.Clamp(newBPM, 15, 10000)
                                        : newBPM; // Clip the value if specified
                                    tp.MpB = 60000 / newBPM;
                                }
                            }
                        }

                        // Slider Velocity
                        if (vm.TimingpointSVMultiplier != 1 || vm.TimingpointSVOffset != 0)
                        {
                            if (Filter(beatmap.BeatmapTiming.GetSvMultiplierAtTime(tp.Offset), tp.Offset, vm))
                            {
                                TimingPoint tpchanger = tp.Copy();
                                double      newSV     =
                                    beatmap.BeatmapTiming.GetSvMultiplierAtTime(tp.Offset) *
                                    vm.TimingpointSVMultiplier + vm.TimingpointSVOffset;
                                newSV = vm.ClipProperties
                                    ? MathHelper.Clamp(newSV, 0.1, 10)
                                    : newSV; // Clip the value if specified
                                tpchanger.MpB = -100 / newSV;
                                timingPointsChanges.Add(new TimingPointsChange(tpchanger, mpb: true));
                            }
                        }

                        // Index
                        if (vm.TimingpointIndexMultiplier != 1 || vm.TimingpointIndexOffset != 0)
                        {
                            if (Filter(tp.SampleIndex, tp.Offset, vm))
                            {
                                int newIndex =
                                    (int)Math.Round(tp.SampleIndex * vm.TimingpointIndexMultiplier +
                                                    vm.TimingpointIndexOffset);
                                tp.SampleIndex = vm.ClipProperties ? MathHelper.Clamp(newIndex, 0, int.MaxValue) : newIndex;
                            }
                        }

                        // Volume
                        if (vm.TimingpointVolumeMultiplier != 1 || vm.TimingpointVolumeOffset != 0)
                        {
                            if (Filter(tp.Volume, tp.Offset, vm))
                            {
                                int newVolume =
                                    (int)Math.Round(tp.Volume * vm.TimingpointVolumeMultiplier +
                                                    vm.TimingpointVolumeOffset);
                                tp.Volume = vm.ClipProperties ? MathHelper.Clamp(newVolume, 5, 100) : newVolume;
                            }
                        }
                    }

                    UpdateProgressBar(worker, 20);

                    // Hitobject time
                    if (vm.HitObjectTimeMultiplier != 1 || vm.HitObjectTimeOffset != 0)
                    {
                        foreach (HitObject ho in beatmap.HitObjects)
                        {
                            // Get the end time early because the start time gets modified
                            double oldEndTime = ho.GetEndTime(false);

                            if (Filter(ho.Time, ho.Time, vm))
                            {
                                ho.Time = Math.Round(ho.Time * vm.HitObjectTimeMultiplier + vm.HitObjectTimeOffset);
                            }

                            // Transform end time of hold notes and spinner
                            if ((ho.IsHoldNote || ho.IsSpinner) &&
                                Filter(oldEndTime, oldEndTime, vm))
                            {
                                ho.EndTime = Math.Round(oldEndTime * vm.HitObjectTimeMultiplier + vm.HitObjectTimeOffset);
                            }
                        }
                    }

                    UpdateProgressBar(worker, 30);

                    // Bookmark time
                    if (vm.BookmarkTimeMultiplier != 1 || vm.BookmarkTimeOffset != 0)
                    {
                        List <double> newBookmarks = new List <double>();
                        List <double> bookmarks    = beatmap.GetBookmarks();
                        foreach (double bookmark in bookmarks)
                        {
                            if (Filter(bookmark, bookmark, vm))
                            {
                                newBookmarks.Add(
                                    Math.Round(bookmark * vm.BookmarkTimeMultiplier + vm.BookmarkTimeOffset));
                            }
                            else
                            {
                                newBookmarks.Add(bookmark);
                            }
                        }

                        beatmap.SetBookmarks(newBookmarks);
                    }

                    UpdateProgressBar(worker, 40);

                    // Storyboarded event time
                    if (vm.SBEventTimeMultiplier != 1 || vm.SBEventTimeOffset != 0)
                    {
                        foreach (Event ev in beatmap.StoryboardLayerBackground.Concat(beatmap.StoryboardLayerFail)
                                 .Concat(beatmap.StoryboardLayerPass).Concat(beatmap.StoryboardLayerForeground)
                                 .Concat(beatmap.StoryboardLayerOverlay))
                        {
                            TransformEventTime(ev, vm.SBEventTimeMultiplier, vm.SBEventTimeOffset, vm);
                        }
                    }

                    UpdateProgressBar(worker, 50);

                    // Storyboarded sample time
                    if (vm.SBSampleTimeMultiplier != 1 || vm.SBSampleTimeOffset != 0)
                    {
                        foreach (StoryboardSoundSample ss in beatmap.StoryboardSoundSamples)
                        {
                            if (Filter(ss.StartTime, ss.StartTime, vm))
                            {
                                ss.StartTime =
                                    (int)Math.Round(ss.StartTime * vm.SBSampleTimeMultiplier + vm.SBSampleTimeOffset);
                            }
                        }
                    }

                    UpdateProgressBar(worker, 60);

                    // Break time
                    if (vm.BreakTimeMultiplier != 1 || vm.BreakTimeOffset != 0)
                    {
                        foreach (Break br in beatmap.BreakPeriods)
                        {
                            if (Filter(br.StartTime, br.StartTime, vm))
                            {
                                br.StartTime =
                                    (int)Math.Round(br.StartTime * vm.BreakTimeMultiplier + vm.BreakTimeOffset);
                            }

                            if (Filter(br.EndTime, br.EndTime, vm))
                            {
                                br.EndTime = (int)Math.Round(br.EndTime * vm.BreakTimeMultiplier + vm.BreakTimeOffset);
                            }
                        }
                    }

                    UpdateProgressBar(worker, 70);

                    // Video start time
                    if (vm.VideoTimeMultiplier != 1 || vm.VideoTimeOffset != 0)
                    {
                        foreach (Event ev in beatmap.BackgroundAndVideoEvents)
                        {
                            if (ev is Video video)
                            {
                                if (Filter(video.StartTime, video.StartTime, vm))
                                {
                                    video.StartTime =
                                        (int)Math.Round(video.StartTime * vm.VideoTimeMultiplier + vm.VideoTimeOffset);
                                }
                            }
                        }
                    }

                    UpdateProgressBar(worker, 80);

                    // Preview point time
                    if (vm.PreviewTimeMultiplier != 1 || vm.PreviewTimeOffset != 0)
                    {
                        if (beatmap.General.ContainsKey("PreviewTime") &&
                            beatmap.General["PreviewTime"].IntValue != -1)
                        {
                            var previewTime = beatmap.General["PreviewTime"].DoubleValue;
                            if (Filter(previewTime, previewTime, vm))
                            {
                                var newPreviewTime =
                                    Math.Round(previewTime * vm.PreviewTimeMultiplier + vm.PreviewTimeOffset);
                                beatmap.General["PreviewTime"].SetDouble(newPreviewTime);
                            }
                        }
                    }

                    UpdateProgressBar(worker, 90);

                    TimingPointsChange.ApplyChanges(beatmap.BeatmapTiming, timingPointsChanges);

                    // Save the file
                    beatmapEditor.SaveFile();

                    UpdateProgressBar(worker, 100);
                }
                else if (editor is StoryboardEditor storyboardEditor)
                {
                    StoryBoard storyboard = storyboardEditor.StoryBoard;

                    // Storyboarded event time
                    if (vm.SBEventTimeMultiplier != 1 || vm.SBEventTimeOffset != 0)
                    {
                        foreach (Event ev in storyboard.StoryboardLayerBackground.Concat(storyboard.StoryboardLayerFail)
                                 .Concat(storyboard.StoryboardLayerPass).Concat(storyboard.StoryboardLayerForeground)
                                 .Concat(storyboard.StoryboardLayerOverlay))
                        {
                            TransformEventTime(ev, vm.SBEventTimeMultiplier, vm.SBEventTimeOffset, vm);
                        }
                    }

                    UpdateProgressBar(worker, 50);

                    // Storyboarded sample time
                    if (vm.SBSampleTimeMultiplier != 1 || vm.SBSampleTimeOffset != 0)
                    {
                        foreach (StoryboardSoundSample ss in storyboard.StoryboardSoundSamples)
                        {
                            if (Filter(ss.StartTime, ss.StartTime, vm))
                            {
                                ss.StartTime =
                                    (int)Math.Round(ss.StartTime * vm.SBSampleTimeMultiplier + vm.SBSampleTimeOffset);
                            }
                        }
                    }

                    UpdateProgressBar(worker, 70);

                    // Video start time
                    if (vm.VideoTimeMultiplier != 1 || vm.VideoTimeOffset != 0)
                    {
                        foreach (Event ev in storyboard.BackgroundAndVideoEvents)
                        {
                            if (ev is Video video)
                            {
                                if (Filter(video.StartTime, video.StartTime, vm))
                                {
                                    video.StartTime =
                                        (int)Math.Round(video.StartTime * vm.VideoTimeMultiplier + vm.VideoTimeOffset);
                                }
                            }
                        }
                    }

                    UpdateProgressBar(worker, 90);

                    // Save the file
                    storyboardEditor.SaveFile();

                    UpdateProgressBar(worker, 100);
                }
            }

            return("Done!");
        }
Esempio n. 11
0
        private string TransformProperties(PropertyTransformerVm vm, BackgroundWorker worker, DoWorkEventArgs _)
        {
            bool   doFilterMatch = vm.MatchFilter != -1 && vm.EnableFilters;
            bool   doFilterRange = (vm.MinTimeFilter != -1 || vm.MaxTimeFilter != -1) && vm.EnableFilters;
            double min           = vm.MinTimeFilter == -1 ? double.NegativeInfinity : vm.MinTimeFilter;
            double max           = vm.MaxTimeFilter == -1 ? double.PositiveInfinity : vm.MaxTimeFilter;

            var reader = EditorReaderStuff.GetFullEditorReaderOrNot();

            foreach (string path in vm.ExportPaths)
            {
                var     editor  = EditorReaderStuff.GetNewestVersionOrNot(path, reader);
                Beatmap beatmap = editor.Beatmap;

                // Count all the total amount of things to loop through
                int loops      = 0;
                int totalLoops = beatmap.BeatmapTiming.TimingPoints.Count;
                if (vm.HitObjectTimeMultiplier != 1 || vm.HitObjectTimeOffset != 0)
                {
                    totalLoops += beatmap.HitObjects.Count;
                }
                if (vm.BookmarkTimeMultiplier != 1 || vm.BookmarkTimeOffset != 0)
                {
                    totalLoops += beatmap.GetBookmarks().Count;
                }
                if (vm.SBSampleTimeMultiplier != 1 || vm.SBSampleTimeOffset != 0)
                {
                    totalLoops += beatmap.StoryboardSoundSamples.Count;
                }

                List <TimingPointsChange> timingPointsChanges = new List <TimingPointsChange>();
                foreach (TimingPoint tp in beatmap.BeatmapTiming.TimingPoints)
                {
                    // Offset
                    if (vm.TimingpointOffsetMultiplier != 1 || vm.TimingpointOffsetOffset != 0)
                    {
                        if (Filter(tp.Offset, tp.Offset, doFilterMatch, doFilterRange, vm.MatchFilter, min, max))
                        {
                            tp.Offset = Math.Round(tp.Offset * vm.TimingpointOffsetMultiplier + vm.TimingpointOffsetOffset);
                        }
                    }

                    // BPM
                    if (vm.TimingpointBPMMultiplier != 1 || vm.TimingpointBPMOffset != 0)
                    {
                        if (tp.Uninherited)
                        {
                            if (Filter(tp.GetBpm(), tp.Offset, doFilterMatch, doFilterRange, vm.MatchFilter, min, max))
                            {
                                double newBPM = tp.GetBpm() * vm.TimingpointBPMMultiplier + vm.TimingpointBPMOffset;
                                newBPM = vm.ClipProperties ? MathHelper.Clamp(newBPM, 15, 10000) : newBPM;  // Clip the value if specified
                                tp.MpB = 60000 / newBPM;
                            }
                        }
                    }

                    // Slider Velocity
                    if (vm.TimingpointSVMultiplier != 1 || vm.TimingpointSVOffset != 0)
                    {
                        if (Filter(beatmap.BeatmapTiming.GetSvMultiplierAtTime(tp.Offset), tp.Offset, doFilterMatch, doFilterRange, vm.MatchFilter, min, max))
                        {
                            TimingPoint tpchanger = tp.Copy();
                            double      newSV     = beatmap.BeatmapTiming.GetSvMultiplierAtTime(tp.Offset) * vm.TimingpointSVMultiplier + vm.TimingpointSVOffset;
                            newSV         = vm.ClipProperties ? MathHelper.Clamp(newSV, 0.1, 10) : newSV; // Clip the value if specified
                            tpchanger.MpB = -100 / newSV;
                            timingPointsChanges.Add(new TimingPointsChange(tpchanger, mpb: true));
                        }
                    }

                    // Index
                    if (vm.TimingpointIndexMultiplier != 1 || vm.TimingpointIndexOffset != 0)
                    {
                        if (Filter(tp.SampleIndex, tp.Offset, doFilterMatch, doFilterRange, vm.MatchFilter, min, max))
                        {
                            int newIndex = (int)Math.Round(tp.SampleIndex * vm.TimingpointIndexMultiplier + vm.TimingpointIndexOffset);
                            tp.SampleIndex = vm.ClipProperties ? MathHelper.Clamp(newIndex, 0, 100) : newIndex;
                        }
                    }

                    // Volume
                    if (vm.TimingpointVolumeMultiplier != 1 || vm.TimingpointVolumeOffset != 0)
                    {
                        if (Filter(tp.Volume, tp.Offset, doFilterMatch, doFilterRange, vm.MatchFilter, min, max))
                        {
                            int newVolume = (int)Math.Round(tp.Volume * vm.TimingpointVolumeMultiplier + vm.TimingpointVolumeOffset);
                            tp.Volume = vm.ClipProperties ? MathHelper.Clamp(newVolume, 5, 100) : newVolume;
                        }
                    }

                    // Update progress bar
                    loops++;
                    UpdateProgressBar(worker, loops * 100 / totalLoops);
                }

                // Hitobject Time
                if (vm.HitObjectTimeMultiplier != 1 || vm.HitObjectTimeOffset != 0)
                {
                    foreach (HitObject ho in beatmap.HitObjects)
                    {
                        if (Filter(ho.Time, ho.Time, doFilterMatch, doFilterRange, vm.MatchFilter, min, max))
                        {
                            ho.Time = Math.Round(ho.Time * vm.HitObjectTimeMultiplier + vm.HitObjectTimeOffset);
                        }

                        // Update progress bar
                        loops++;
                        UpdateProgressBar(worker, loops * 100 / totalLoops);
                    }
                }

                // Bookmark Time
                if (vm.BookmarkTimeMultiplier != 1 || vm.BookmarkTimeOffset != 0)
                {
                    List <double> newBookmarks = new List <double>();
                    List <double> bookmarks    = beatmap.GetBookmarks();
                    foreach (double bookmark in bookmarks)
                    {
                        if (Filter(bookmark, bookmark, doFilterMatch, doFilterRange, vm.MatchFilter, min, max))
                        {
                            newBookmarks.Add(Math.Round(bookmark * vm.BookmarkTimeMultiplier + vm.BookmarkTimeOffset));
                        }
                        else
                        {
                            newBookmarks.Add(bookmark);
                        }

                        // Update progress bar
                        loops++;
                        UpdateProgressBar(worker, loops * 100 / totalLoops);
                    }
                    beatmap.SetBookmarks(newBookmarks);
                }

                // Storyboarded sample Time
                if (vm.SBSampleTimeMultiplier != 1 || vm.SBSampleTimeOffset != 0)
                {
                    foreach (StoryboardSoundSample ss in beatmap.StoryboardSoundSamples)
                    {
                        if (Filter(ss.Time, ss.Time, doFilterMatch, doFilterRange, vm.MatchFilter, min, max))
                        {
                            ss.Time = Math.Round(ss.Time * vm.SBSampleTimeMultiplier + vm.SBSampleTimeOffset);
                        }

                        // Update progress bar
                        loops++;
                        UpdateProgressBar(worker, loops * 100 / totalLoops);
                    }
                }

                // Preview point time
                if (vm.PreviewTimeMultiplier != 1 || vm.PreviewTimeOffset != 0)
                {
                    if (beatmap.General.ContainsKey("PreviewTime") && beatmap.General["PreviewTime"].IntValue != -1)
                    {
                        var previewTime = beatmap.General["PreviewTime"].DoubleValue;
                        if (Filter(previewTime, previewTime, doFilterMatch, doFilterRange, vm.MatchFilter, min, max))
                        {
                            var newPreviewTime = Math.Round(previewTime * vm.PreviewTimeMultiplier + vm.PreviewTimeOffset);
                            beatmap.General["PreviewTime"].SetDouble(newPreviewTime);
                        }
                    }
                }

                TimingPointsChange.ApplyChanges(beatmap.BeatmapTiming, timingPointsChanges);

                // Save the file
                editor.SaveFile();
            }

            return("Done!");
        }
        private static string Export_ComboColours(ComboColourStudioVm arg, BackgroundWorker worker, DoWorkEventArgs _)
        {
            var paths    = arg.ExportPath.Split('|');
            var mapsDone = 0;

            var orderedColourPoints = arg.Project.ColourPoints.OrderBy(o => o.Time).ToList();
            var orderedComboColours = arg.Project.ComboColours.OrderBy(o => o.Name).ToList();

            var reader = EditorReaderStuff.GetFullEditorReaderOrNot();

            foreach (var path in paths)
            {
                var editor  = EditorReaderStuff.GetNewestVersionOrNot(path, reader);
                var beatmap = editor.Beatmap;

                // Setting the combo colours
                beatmap.ComboColours = new List <ComboColour>(arg.Project.ComboColours);

                // Setting the combo skips
                if (beatmap.HitObjects.Count > 0 && orderedColourPoints.Count > 0)
                {
                    int lastColourPointColourIndex = -1;
                    var lastColourPoint            = orderedColourPoints[0];
                    int lastColourIndex            = 0;
                    var exceptions = new List <ColourPoint>();
                    foreach (var newCombo in beatmap.HitObjects.Where(o => o.ActualNewCombo && !o.IsSpinner))
                    {
                        int comboLength = GetComboLength(newCombo, beatmap.HitObjects);
                        //Console.WriteLine(comboLength);

                        // Get the colour point for this new combo
                        var colourPoint    = GetColourPoint(orderedColourPoints, newCombo.Time, exceptions, comboLength <= arg.Project.MaxBurstLength);
                        var colourSequence = colourPoint.ColourSequence.ToList();

                        // Add the colour point to the exceptions so it doesnt get used again
                        if (colourPoint.Mode == ColourPointMode.Burst)
                        {
                            exceptions.Add(colourPoint);
                        }

                        // Get the last colour index on the sequence of this colour point
                        lastColourPointColourIndex = lastColourPointColourIndex == -1 || lastColourPoint.Equals(colourPoint) ?
                                                     lastColourPointColourIndex :
                                                     colourSequence.FindIndex(o => o.Name == orderedComboColours[lastColourIndex].Name);

                        // Get the next colour index on this colour point
                        // Check if colourSequence count is 0 to prevent division by 0
                        var colourPointColourIndex = lastColourPointColourIndex == -1 || colourSequence.Count == 0
                            ? 0
                            : lastColourPoint.Equals(colourPoint) ?
                                                     MathHelper.Mod(lastColourPointColourIndex + 1, colourSequence.Count) :
                                                     // If the colour point changed try going back to index 0
                                                     lastColourPointColourIndex == 0 && colourSequence.Count > 1 ? 1 : 0;

                        //Console.WriteLine("colourPointColourIndex: " + colourPointColourIndex);
                        //Console.WriteLine("colourPointColour: " + colourPoint.ColourSequence[colourPointColourIndex].Name);

                        // Find the combo index of the chosen colour in the sequence
                        // Check if the colourSequence count is 0 to prevent an out-of-range exception
                        var colourIndex = colourSequence.Count == 0 ? MathHelper.Mod(lastColourIndex + 1, orderedComboColours.Count) :
                                          orderedComboColours.FindIndex(o => o.Name == colourSequence[colourPointColourIndex].Name);

                        if (colourIndex == -1)
                        {
                            throw new ArgumentException($"Can not use colour {colourSequence[colourPointColourIndex].Name} of colour point at offset {colourPoint.Time} because it does not exist in the combo colours.");
                        }

                        //Console.WriteLine("colourIndex: " + colourIndex);

                        var comboIncrease = MathHelper.Mod(colourIndex - lastColourIndex, arg.Project.ComboColours.Count);

                        // Do -1 combo skip since it always does +1 combo colour for each new combo which is not on a spinner
                        newCombo.ComboSkip = MathHelper.Mod(comboIncrease - 1, arg.Project.ComboColours.Count);

                        // Set new combo to true for the case this is the first object and new combo is false
                        if (!newCombo.NewCombo && newCombo.ComboSkip != 0)
                        {
                            newCombo.NewCombo = true;
                        }

                        //Console.WriteLine("comboSkip: " + newCombo.ComboSkip);

                        lastColourPointColourIndex = colourPointColourIndex;
                        lastColourPoint            = colourPoint;
                        lastColourIndex            = colourIndex;
                    }
                }

                // Save the file
                editor.SaveFile();

                // Update progressbar
                if (worker != null && worker.WorkerReportsProgress)
                {
                    worker.ReportProgress(++mapsDone * 100 / paths.Length);
                }
            }

            // Make an accurate message
            var message = $"Successfully exported colours to {mapsDone} {(mapsDone == 1 ? "beatmap" : "beatmaps")}!";

            return(message);
        }