From 09de9cc0a7138c801a0c0f5391d92ab90d88a8b1 Mon Sep 17 00:00:00 2001 From: Alex <15199219+muskit@users.noreply.github.com> Date: Tue, 19 Aug 2025 00:27:53 -0700 Subject: [PATCH] flesh out the data scan UI, tweak selection table --- Data/Database.cs | 20 ++- Data/Song/Consts.cs | 5 - Data/Song/Song.cs | 3 +- Program.cs | 16 +- UI/Dialogs/DataOpen.axaml.cs | 95 ---------- .../{DataOpen.axaml => DataScanning.axaml} | 42 +++-- UI/Dialogs/DataScanning.axaml.cs | 170 ++++++++++++++++++ UI/Dialogs/Welcome.axaml.cs | 2 +- UI/MainWindow.axaml | 4 +- UI/MainWindow.axaml.cs | 7 + UI/Views/Selection/Selection.axaml | 3 +- 11 files changed, 237 insertions(+), 130 deletions(-) delete mode 100644 UI/Dialogs/DataOpen.axaml.cs rename UI/Dialogs/{DataOpen.axaml => DataScanning.axaml} (53%) create mode 100644 UI/Dialogs/DataScanning.axaml.cs diff --git a/Data/Database.cs b/Data/Database.cs index c79dd46..59dbca7 100644 --- a/Data/Database.cs +++ b/Data/Database.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Data.Common; using System.IO; using System.Linq; using System.Linq.Expressions; @@ -65,13 +66,24 @@ public static class Database // skip non-canon difficulties if (diff == Difficulty.None || diff == Difficulty.WorldsEnd) continue; - if (GetDiffPair(dataPath, data, diff) is var pair && pair != null) + var lvl = ((FloatPropertyData)data[Consts.DIFF_LVL_KEY[diff]]).Value; + if (lvl == 0) continue; // skip nonexistent level + + // check chart existence + var chartFilePath = Path.Combine(dataPath, "MusicData", song.Id, $"{song.Id}_{Consts.DIFF_FILENAME_PREPEND[diff]}.mer"); + if (!File.Exists(chartFilePath)) { - song.charts.Add((diff, pair.Value.Item1, pair.Value.Item2)); + // TODO: add warning message to DataScan + Console.WriteLine($"[MISSING CHART] {song.Id} {song.Artist} - {song.Name} / {diff}"); + continue; } + + // TODO: check audio existence; add warning but don't skip + + song.Levels[(int)diff] = lvl; } - Dispatcher.UIThread.Invoke(() => Songs.Add(song)); + Dispatcher.UIThread.Post(() => Songs.Add(song)); } catch (Exception e) { @@ -83,7 +95,6 @@ public static class Database private static (Entry, Chart)? GetDiffPair(string dataPath, StructPropertyData song, Difficulty diff) { - var level = ((FloatPropertyData)song[Consts.DIFF_LVL_KEY[diff]]).Value; if (level == 0) return null; @@ -97,6 +108,7 @@ public static class Database InferClearThresholdFromDifficulty = false }); e.ClearThreshold = clearThreshold; + e.Difficulty = diff; var c = NotationSerializer.ToChart(chartFilePath, new NotationReadArgs { InferClearThresholdFromDifficulty = false diff --git a/Data/Song/Consts.cs b/Data/Song/Consts.cs index 732ec05..6b5cb51 100644 --- a/Data/Song/Consts.cs +++ b/Data/Song/Consts.cs @@ -32,11 +32,6 @@ public static class Consts public static readonly IReadOnlyDictionary NUM_SOURCE = _NUM_SOURCE; public static readonly IReadOnlyDictionary SOURCE_NUM = _NUM_SOURCE.ToDictionary(p => p.Value, p => p.Key); - private static string[] _DIFFICULTIES = { - "Normal", "Hard", "Expert", "Inferno" - }; - public static readonly IReadOnlyList DIFFICULTIES = _DIFFICULTIES; - private static readonly Dictionary _DIFF_LVL_KEY = new() { {Difficulty.Normal, "DifficultyNormalLv"}, diff --git a/Data/Song/Song.cs b/Data/Song/Song.cs index 654aaa0..dae241d 100644 --- a/Data/Song/Song.cs +++ b/Data/Song/Song.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Avalonia.Controls; using Avalonia.Media; using SaturnData.Notation.Core; @@ -21,9 +22,9 @@ public class Song public required float PreviewTime { get; set; } public required float PreviewLen { get; set; } public string SourceName => Consts.NUM_SOURCE[Source]; + public float?[] Levels { get; set; } = { null, null, null, null }; // TODO: For SaturnData.Entry instances, use this Guid format: // MERCURY_[SONGID]_[DIFF] (each var is int) - public List<(Difficulty, Entry, Chart)> charts = new(); } \ No newline at end of file diff --git a/Program.cs b/Program.cs index f6f402b..17e17b1 100644 --- a/Program.cs +++ b/Program.cs @@ -4,6 +4,7 @@ using Avalonia; using System; using MercuryConverter.UI; +using Avalonia.Logging; class Program { @@ -13,7 +14,18 @@ class Program [STAThread] public static void Main(string[] args) { - BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); + // BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); + try + { + // prepare and run your App here + BuildAvaloniaApp() + .StartWithClassicDesktopLifetime(args); + } + catch (Exception e) + { + // here we can work with the exception, for example add it to our log file + Console.WriteLine($"App exception!!\b{e}"); + } } // Avalonia configuration, don't remove; also used by visual designer. @@ -21,5 +33,5 @@ class Program => AppBuilder.Configure() .UsePlatformDetect() .WithInterFont() - .LogToTrace(); + .LogToTrace(LogEventLevel.Debug); } diff --git a/UI/Dialogs/DataOpen.axaml.cs b/UI/Dialogs/DataOpen.axaml.cs deleted file mode 100644 index 369165a..0000000 --- a/UI/Dialogs/DataOpen.axaml.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Avalonia.Controls; -using Avalonia.Platform.Storage; -using Avalonia.Threading; -using MercuryConverter.Data; -using UAssetAPI; -using UAssetAPI.UnrealTypes; - -namespace MercuryConverter.UI.Dialogs; - -public partial class DataOpen : UserControl -{ - public static DataOpen? Instance { get; private set; } - public DataOpen() - { - Instance = this; - InitializeComponent(); - - if (!Design.IsDesignMode) - Run(); - } - - public void Run() - { - Task.Run(async () => - { - var path = ""; // TODO: set to current data path - - // Content selection - while (true) - { - var selectedPath = await BeginDirSelection(); - Console.WriteLine($"selectedPath={selectedPath}"); - if (selectedPath != "" && Directory.Exists(selectedPath)) - { - path = selectedPath; - break; - } - // Display error message - - } - - BeginDataScan(path); - }); - } - - private async Task BeginDirSelection() - { - IReadOnlyList? dirSelection = null; - - await Dispatcher.UIThread.Invoke(async () => - { - // Update UI - ScanView.IsVisible = false; - SelectView.IsVisible = true; - - await Task.Delay(200); - - dirSelection = await TopLevel.GetTopLevel(MainWindow.Instance)!.StorageProvider.OpenFolderPickerAsync - ( - new FolderPickerOpenOptions - { - Title = "Locate Data Folder", - AllowMultiple = false, - } - ); - }); - - if (dirSelection!.Count <= 0) - { - return ""; - } - - return dirSelection!.First().TryGetLocalPath()!; - } - - private void BeginDataScan(string dataPath) - { - Console.WriteLine(dataPath); - // Update UI - Dispatcher.UIThread.Invoke(() => - { - SelectView.IsVisible = false; - ScanStatus.Text = "scanning..."; - ScanPath.Text = dataPath; - ScanView.IsVisible = true; - }); - - Database.SetupNew(dataPath); - } -} \ No newline at end of file diff --git a/UI/Dialogs/DataOpen.axaml b/UI/Dialogs/DataScanning.axaml similarity index 53% rename from UI/Dialogs/DataOpen.axaml rename to UI/Dialogs/DataScanning.axaml index 74f122f..0c5c54e 100644 --- a/UI/Dialogs/DataOpen.axaml +++ b/UI/Dialogs/DataScanning.axaml @@ -4,33 +4,39 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:MercuryConverter.UI.Dialogs" xmlns:progRing="clr-namespace:AvaloniaProgressRing;assembly=AvaloniaProgressRing" - x:Class="MercuryConverter.UI.Dialogs.DataOpen" + x:Class="MercuryConverter.UI.Dialogs.DataScanning" > - - - - - - - - + + + + + + + + + + + + + + + + + - -