Example #1
0
        public Visualizer(
            string songPath,
            string saveFile,
            Song song,
            Chart originalChart,
            ExpressedChart expressedChart,
            string expressedChartConfigName,
            PerformedChart performedChart,
            string performedChartConfigName,
            Chart generatedChart)
        {
            SongPath                 = songPath;
            SaveFile                 = saveFile;
            Song                     = song;
            OriginalChart            = originalChart;
            ExpressedChart           = expressedChart;
            ExpressedChartConfigName = expressedChartConfigName;
            PerformedChart           = performedChart;
            PerformedChartConfigName = performedChartConfigName;
            GeneratedChart           = generatedChart;

            ExpressedChartX = 0;
            foreach (var chartColInfo in ChartColumnInfo)
            {
                ExpressedChartX += chartColInfo.Width;
            }
            ExpressedChartX += (originalChart.NumInputs * ArrowW);

            GeneratedChartX = ExpressedChartX;
            foreach (var expressedColInfo in ExpressionColumnInfo)
            {
                GeneratedChartX += expressedColInfo.Width;
            }

            if (!SongPath.EndsWith(System.IO.Path.DirectorySeparatorChar.ToString()))
            {
                SongPath += System.IO.Path.DirectorySeparatorChar.ToString();
            }

            // Determine the relative src path.
            if (string.IsNullOrEmpty(VisualizationDir))
            {
                throw new Exception("VisualizationDir is not set. Set with InitializeVisualizationDir.");
            }
            SrcPath = Fumen.Path.GetRelativePath(SaveFile, GetSrcDir());
            SrcPath = SrcPath.Replace('\\', '/');
            if (!SrcPath.EndsWith("/"))
            {
                SrcPath += "/";
            }
        }
Example #2
0
        /// <summary>
        /// Adds charts to the given song and write a visualization per chart, if configured to do so.
        /// </summary>
        /// <param name="song">Song to add charts to.</param>
        /// <param name="songArgs">SongArgs for the song file.</param>
        private static void AddCharts(Song song, SongArgs songArgs)
        {
            LogInfo("Processing Song.", songArgs.FileInfo, songArgs.RelativePath, song);

            var fileNameNoExtension = songArgs.FileInfo.Name;

            if (!string.IsNullOrEmpty(songArgs.FileInfo.Extension))
            {
                fileNameNoExtension =
                    fileNameNoExtension.Substring(0, songArgs.FileInfo.Name.Length - songArgs.FileInfo.Extension.Length);
            }

            var extension = songArgs.FileInfo.Extension.ToLower();

            if (extension.StartsWith("."))
            {
                extension = extension.Substring(1);
            }

            var newCharts             = new List <Chart>();
            var chartsIndicesToRemove = new List <int>();

            foreach (var chart in song.Charts)
            {
                if (chart.Layers.Count == 1 &&
                    chart.Type == Config.Instance.InputChartType &&
                    chart.NumPlayers == 1 &&
                    chart.NumInputs == InputStepGraph.NumArrows &&
                    Config.Instance.DifficultyMatches(chart.DifficultyType))
                {
                    // Check if there is an existing chart.
                    var(currentChart, currentChartIndex) = FindChart(
                        song,
                        Config.Instance.OutputChartType,
                        chart.DifficultyType,
                        OutputStepGraph.NumArrows);
                    if (currentChart != null)
                    {
                        var fumenGenerated = GetFumenGeneratedVersion(currentChart, out var version);

                        // Check if we should skip or overwrite the chart.
                        switch (Config.Instance.OverwriteBehavior)
                        {
                        case OverwriteBehavior.DoNotOverwrite:
                            continue;

                        case OverwriteBehavior.IfFumenGenerated:
                            if (!fumenGenerated)
                            {
                                continue;
                            }
                            break;

                        case OverwriteBehavior.IfFumenGeneratedAndNewerVersion:
                            if (!fumenGenerated || version >= Version)
                            {
                                continue;
                            }
                            break;

                        case OverwriteBehavior.Always:
                        default:
                            break;
                        }
                    }

                    // Create an ExpressedChart.
                    var(ecc, eccName) = Config.Instance.GetExpressedChartConfig(songArgs.FileInfo, chart.DifficultyType);
                    var expressedChart = ExpressedChart.CreateFromSMEvents(
                        chart.Layers[0].Events,
                        InputStepGraph,
                        ecc,
                        chart.DifficultyRating,
                        GetLogIdentifier(songArgs.FileInfo, songArgs.RelativePath, song, chart));
                    if (expressedChart == null)
                    {
                        LogError("Failed to create ExpressedChart.", songArgs.FileInfo, songArgs.RelativePath, song, chart);
                        continue;
                    }

                    // Create a PerformedChart.
                    var(pcc, pccName) = Config.Instance.GetPerformedChartConfig(songArgs.FileInfo, chart.DifficultyType);
                    var performedChart = PerformedChart.CreateFromExpressedChart(
                        OutputStepGraph,
                        pcc,
                        OutputStartNodes,
                        expressedChart,
                        GeneratePerformedChartRandomSeed(songArgs.FileInfo.Name),
                        GetLogIdentifier(songArgs.FileInfo, songArgs.RelativePath, song, chart));
                    if (performedChart == null)
                    {
                        LogError("Failed to create PerformedChart.", songArgs.FileInfo, songArgs.RelativePath, song, chart);
                        continue;
                    }

                    // At this point we have succeeded, so add the chart index to remove if appropriate.
                    if (currentChart != null)
                    {
                        chartsIndicesToRemove.Add(currentChartIndex);
                    }

                    // Create Events for the new Chart.
                    var events = performedChart.CreateSMChartEvents();
                    CopyNonPerformanceEvents(chart.Layers[0].Events, events);
                    events.Sort(new SMEventComparer());

                    // Sanity check on note counts.
                    if (events.Count != chart.Layers[0].Events.Count)
                    {
                        var mineString = NoteChars[(int)NoteType.Mine].ToString();
                        // Disregard discrepancies in mine counts
                        var newChartNonMineEventCount = 0;
                        foreach (var newEvent in events)
                        {
                            if (newEvent.SourceType != mineString)
                            {
                                newChartNonMineEventCount++;
                            }
                        }

                        var oldChartNonMineEventCount = 0;
                        foreach (var oldEvent in chart.Layers[0].Events)
                        {
                            if (oldEvent.SourceType != mineString)
                            {
                                oldChartNonMineEventCount++;
                            }
                        }

                        if (newChartNonMineEventCount != oldChartNonMineEventCount)
                        {
                            MetricPosition firstDiscrepancyPosition = null;
                            var            i = 0;
                            while (i < events.Count && i < chart.Layers[0].Events.Count)
                            {
                                if (events[i].SourceType != chart.Layers[0].Events[i].SourceType ||
                                    events[i].Position != chart.Layers[0].Events[i].Position)
                                {
                                    firstDiscrepancyPosition = chart.Layers[0].Events[i].Position;
                                    break;
                                }

                                i++;
                            }

                            LogError(
                                "Programmer error. Discrepancy in non-mine Event counts."
                                + $" Old: {oldChartNonMineEventCount}, New: {newChartNonMineEventCount}."
                                + $" First discrepancy position: {firstDiscrepancyPosition}.",
                                songArgs.FileInfo, songArgs.RelativePath, song, chart);
                            continue;
                        }
                    }

                    // Create a new Chart for these Events.
                    var newChart = new Chart
                    {
                        Artist = chart.Artist,
                        ArtistTransliteration = chart.ArtistTransliteration,
                        Genre = chart.Genre,
                        GenreTransliteration = chart.GenreTransliteration,
                        Author               = FormatWithVersion(chart.Author),
                        Description          = FormatWithVersion(chart.Description),
                        MusicFile            = chart.MusicFile,
                        ChartOffsetFromMusic = chart.ChartOffsetFromMusic,
                        Tempo            = chart.Tempo,
                        DifficultyRating = chart.DifficultyRating,
                        DifficultyType   = chart.DifficultyType,
                        Extras           = chart.Extras,
                        Type             = Config.Instance.OutputChartType,
                        NumPlayers       = 1,
                        NumInputs        = OutputStepGraph.NumArrows
                    };
                    newChart.Layers.Add(new Layer {
                        Events = events
                    });
                    newCharts.Add(newChart);

                    LogInfo(
                        $"Generated new {newChart.Type} {newChart.DifficultyType} Chart from {chart.Type} {chart.DifficultyType} Chart"
                        + $" using ExpressedChartConfig \"{eccName}\" (BracketParsingMethod {expressedChart.GetBracketParsingMethod():G})"
                        + $" and PerformedChartConfig \"{pccName}\".",
                        songArgs.FileInfo, songArgs.RelativePath, song, newChart);

                    // Write a visualization.
                    if (Config.Instance.OutputVisualizations && CanOutputVisualizations)
                    {
                        var visualizationDirectory = Fumen.Path.Combine(VisualizationDir, songArgs.RelativePath);
                        Directory.CreateDirectory(visualizationDirectory);
                        var saveFile = Fumen.Path.GetWin32FileSystemFullPath(
                            Fumen.Path.Combine(visualizationDirectory,
                                               $"{fileNameNoExtension}-{chart.DifficultyType}-{extension}.html"));

                        try
                        {
                            var visualizer = new Visualizer(
                                songArgs.CurrentDir,
                                saveFile,
                                song,
                                chart,
                                expressedChart,
                                eccName,
                                performedChart,
                                pccName,
                                newChart
                                );
                            visualizer.Write();
                        }
                        catch (Exception e)
                        {
                            LogError($"Failed to write visualization to \"{saveFile}\". {e}",
                                     songArgs.FileInfo, songArgs.RelativePath, song, newChart);
                        }
                    }
                }
            }

            LogInfo(
                $"Generated {newCharts.Count} new {Config.Instance.OutputChartType} Charts (replaced {chartsIndicesToRemove.Count}).",
                songArgs.FileInfo, songArgs.RelativePath, song);

            // Remove overwritten charts.
            if (chartsIndicesToRemove.Count > 0)
            {
                // Ensure the indices are sorted descending so they don't shift when removing.
                chartsIndicesToRemove.Sort((a, b) => b.CompareTo(a));
                foreach (var i in chartsIndicesToRemove)
                {
                    song.Charts.RemoveAt(i);
                }
            }

            // Add new charts.
            song.Charts.AddRange(newCharts);
        }