diff --git a/src/NCMDumpCLI/NCMDumpCLI.csproj b/src/NCMDump.CLI/NCMDump.CLI.csproj similarity index 79% rename from src/NCMDumpCLI/NCMDumpCLI.csproj rename to src/NCMDump.CLI/NCMDump.CLI.csproj index 7ab55f9..422e480 100644 --- a/src/NCMDumpCLI/NCMDumpCLI.csproj +++ b/src/NCMDump.CLI/NCMDump.CLI.csproj @@ -5,10 +5,9 @@ net8.0 enable enable - NCMDumpCLI NCMDump.ico app.manifest - 2.3.0 + 2.4.0 x64 @@ -18,8 +17,10 @@ + + - + diff --git a/src/NCMDumpCLI/NCMDump.ico b/src/NCMDump.CLI/NCMDump.ico similarity index 100% rename from src/NCMDumpCLI/NCMDump.ico rename to src/NCMDump.CLI/NCMDump.ico diff --git a/src/NCMDumpCLI/NCMDumpCLI - Backup.csproj b/src/NCMDump.CLI/NCMDumpCLI - Backup.csproj similarity index 100% rename from src/NCMDumpCLI/NCMDumpCLI - Backup.csproj rename to src/NCMDump.CLI/NCMDumpCLI - Backup.csproj diff --git a/src/NCMDump.CLI/Program.cs b/src/NCMDump.CLI/Program.cs new file mode 100644 index 0000000..421ce9c --- /dev/null +++ b/src/NCMDump.CLI/Program.cs @@ -0,0 +1,77 @@ +using NCMDump.Core; + +namespace NCMDump.CLI +{ + public class Application + { + public static NCMDumper Dumper = new(); + + public static void Main(params string[] args) + { + int depth = 0; + if (args.Length == 0) + { + if (OperatingSystem.IsWindows()) + { + Console.WriteLine("Drag [*.ncm] file or directory on exe to start..."); + Console.WriteLine("./ncmdump.exe [ ... ]"); + } + if (OperatingSystem.IsLinux()) + { + Console.WriteLine("./ncmdump [ ... ]"); + } + return; + } + + try + { + foreach (string path in args) + { + if (new DirectoryInfo(path).Exists) + { + WalkThrough(new DirectoryInfo(path)); + } + else if (new FileInfo(path).Exists) + { + Console.Write($"Dumping: {new FileInfo(path).FullName} ......"); + if (Dumper.Convert(path)) Console.WriteLine("OK"); + else Console.WriteLine("Fail"); + Console.WriteLine(); + } + } + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + + Console.Write("Press Enter to Exit..."); + Console.ReadLine(); + return; + + void WalkThrough(DirectoryInfo dir) + { + depth++; + if (depth > 16) + { + depth--; + return; + } + Console.WriteLine("DIR: " + dir.FullName); + foreach (DirectoryInfo d in dir.GetDirectories()) + { + WalkThrough(d); + } + foreach (FileInfo f in dir.EnumerateFiles("*.ncm")) + { + Console.Write($"Dumping: {f.FullName} ......"); + if (Dumper.Convert(f.FullName)) Console.WriteLine("OK"); + else Console.WriteLine("...Fail"); + } + + Console.WriteLine(); + depth--; + } + } + } +} \ No newline at end of file diff --git a/src/NCMDumpCLI/app.manifest b/src/NCMDump.CLI/app.manifest similarity index 97% rename from src/NCMDumpCLI/app.manifest rename to src/NCMDump.CLI/app.manifest index eee9064..6b02c6d 100644 --- a/src/NCMDumpCLI/app.manifest +++ b/src/NCMDump.CLI/app.manifest @@ -39,7 +39,7 @@ - + diff --git a/src/NCMDumpCLI/ncmdump.png b/src/NCMDump.CLI/ncmdump.png similarity index 100% rename from src/NCMDumpCLI/ncmdump.png rename to src/NCMDump.CLI/ncmdump.png diff --git a/src/NCMDump.Core/IRC4_NCM_Stream.cs b/src/NCMDump.Core/IRC4_NCM_Stream.cs new file mode 100644 index 0000000..cab148c --- /dev/null +++ b/src/NCMDump.Core/IRC4_NCM_Stream.cs @@ -0,0 +1,21 @@ +namespace NCMDump.Core +{ + public interface IRC4_NCM_Stream : IDisposable + { + public bool CanRead { get; } + public bool CanSeek { get; } + public bool CanWrite { get; } + public long Length { get; } + public long Position { get; set; } + + public long Seek(long offset, SeekOrigin origin); + + public int Read(byte[] buffer, int offset, int count); + + public int Read(Span buffer); + + public Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken); + + public ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/src/NCMDumpCore/MetaInfo.cs b/src/NCMDump.Core/MetaInfo.cs similarity index 96% rename from src/NCMDumpCore/MetaInfo.cs rename to src/NCMDump.Core/MetaInfo.cs index 6188ff1..6981f1f 100644 --- a/src/NCMDumpCore/MetaInfo.cs +++ b/src/NCMDump.Core/MetaInfo.cs @@ -1,4 +1,4 @@ -namespace NCMDumpCore +namespace NCMDump.Core { public class MetaInfo { diff --git a/src/NCMDumpCore/NCMDumpCore.csproj b/src/NCMDump.Core/NCMDump.Core.csproj similarity index 90% rename from src/NCMDumpCore/NCMDumpCore.csproj rename to src/NCMDump.Core/NCMDump.Core.csproj index 991d571..40fd00d 100644 --- a/src/NCMDumpCore/NCMDumpCore.csproj +++ b/src/NCMDump.Core/NCMDump.Core.csproj @@ -5,7 +5,7 @@ net8.0; enable enable - 2.3.0 + 2.4.0 x64 diff --git a/src/NCMDumpCore/NCMDumper.cs b/src/NCMDump.Core/NCMDumper.cs similarity index 86% rename from src/NCMDumpCore/NCMDumper.cs rename to src/NCMDump.Core/NCMDumper.cs index e042c8e..e6352fa 100644 --- a/src/NCMDumpCore/NCMDumper.cs +++ b/src/NCMDump.Core/NCMDumper.cs @@ -6,13 +6,13 @@ using System.Text.Json; using TagLib; -namespace NCMDumpCore +namespace NCMDump.Core { public class NCMDumper { private readonly int vectorSize = Vector256.Count; - private readonly byte[] coreKey = [0x68, 0x7A, 0x48, 0x52, 0x41, 0x6D, 0x73, 0x6F, 0x35, 0x6B, 0x49, 0x6E, 0x62, 0x61, 0x78, 0x57]; - private readonly byte[] metaKey = [0x23, 0x31, 0x34, 0x6C, 0x6A, 0x6B, 0x5F, 0x21, 0x5C, 0x5D, 0x26, 0x30, 0x55, 0x3C, 0x27, 0x28]; + private static readonly byte[] coreKey = [0x68, 0x7A, 0x48, 0x52, 0x41, 0x6D, 0x73, 0x6F, 0x35, 0x6B, 0x49, 0x6E, 0x62, 0x61, 0x78, 0x57]; + private static readonly byte[] metaKey = [0x23, 0x31, 0x34, 0x6C, 0x6A, 0x6B, 0x5F, 0x21, 0x5C, 0x5D, 0x26, 0x30, 0x55, 0x3C, 0x27, 0x28]; private bool VerifyHeader(ref MemoryStream ms) { @@ -53,7 +53,7 @@ private byte[] ReadRC4Key(ref MemoryStream ms) { aes.Mode = CipherMode.ECB; aes.Key = coreKey; - var cleanText = aes.DecryptEcb(buffer.ToArray(), PaddingMode.PKCS7).ToArray()[17..]; + var cleanText = aes.DecryptEcb(buffer, PaddingMode.PKCS7)[17..]; return cleanText; } } @@ -80,15 +80,16 @@ private MetaInfo ReadMeta(ref MemoryStream ms) buffer[i] ^= 0x63; } - buffer = System.Convert.FromBase64String(Encoding.ASCII.GetString(buffer.ToArray()[22..])); + buffer = System.Convert.FromBase64String(Encoding.ASCII.GetString(buffer[22..])); // decrypt meta data which is a json contains info of the song using (Aes aes = Aes.Create()) { aes.Mode = CipherMode.ECB; aes.Key = metaKey; - var cleanText = aes.DecryptEcb(buffer.ToArray(), PaddingMode.PKCS7); - var MetaJsonString = Encoding.UTF8.GetString(cleanText[6..]); + var cleanText = aes.DecryptEcb(buffer, PaddingMode.PKCS7); + var MetaJsonString = Encoding.UTF8.GetString(cleanText.AsSpan(6)); + JsonSerializerOptions option = new JsonSerializerOptions(); MetaInfo metainfo = JsonSerializer.Deserialize(MetaJsonString); return metainfo; } @@ -96,7 +97,7 @@ private MetaInfo ReadMeta(ref MemoryStream ms) private async Task ReadAudioData(MemoryStream ms, byte[] Key) { - using (RC4_NCM_Stream rc4s = new(ms, Key)) + using (IRC4_NCM_Stream rc4s = new RC4_NCM_Stream(ms, Key)) { byte[] data = new byte[ms.Length - ms.Position]; Memory m_data = new(data); @@ -105,7 +106,7 @@ private async Task ReadAudioData(MemoryStream ms, byte[] Key) } } - private void AddTag(string fileName, byte[]? ImgData, MetaInfo metainfo) + private async Task AddTag(string fileName, byte[]? ImgData, MetaInfo metainfo) { var tagfile = TagLib.File.Create(fileName); @@ -113,16 +114,16 @@ private void AddTag(string fileName, byte[]? ImgData, MetaInfo metainfo) if (ImgData is not null) { var PicEmbedded = new Picture(new ByteVector(ImgData)); - tagfile.Tag.Pictures = new Picture[] { PicEmbedded }; + tagfile.Tag.Pictures = [PicEmbedded]; } //Use Internet Picture else if (metainfo.albumPic != "") { - byte[]? NetImgData = FetchUrl(new Uri(metainfo.albumPic)); + byte[]? NetImgData = await FetchUrl(new Uri(metainfo.albumPic)); if (NetImgData is not null) { var PicFromNet = new Picture(new ByteVector(NetImgData)); - tagfile.Tag.Pictures = new Picture[] { PicFromNet }; + tagfile.Tag.Pictures = [PicFromNet]; } } @@ -130,16 +131,16 @@ private void AddTag(string fileName, byte[]? ImgData, MetaInfo metainfo) tagfile.Tag.Title = metainfo.musicName; tagfile.Tag.Performers = metainfo.artist.Select(x => x[0]).ToArray(); tagfile.Tag.Album = metainfo.album; - tagfile.Tag.Subtitle = String.Join(@";", metainfo.alias); + tagfile.Tag.Subtitle = string.Join(@";", metainfo.alias); tagfile.Save(); } - private byte[]? FetchUrl(Uri uri) + private async Task FetchUrl(Uri uri) { HttpClient client = new(); try { - var response = client.GetAsync(uri).Result; + var response = await client.GetAsync(uri); Console.WriteLine(response.StatusCode); if (response.StatusCode == HttpStatusCode.OK) { @@ -230,8 +231,7 @@ public async Task ConvertAsync(string path) //Flush Audio Data to disk drive string OutputPath = path[..path.LastIndexOf('.')]; - string format = metainfo.format; - if (format is null or "") format = "mp3"; + string format = metainfo.format ?? "mp3"; await System.IO.File.WriteAllBytesAsync($"{OutputPath}.{format}", AudioData); //Add tag and cover diff --git a/src/NCMDumpCore/RC4_NCM.cs b/src/NCMDump.Core/RC4_NCM.cs similarity index 68% rename from src/NCMDumpCore/RC4_NCM.cs rename to src/NCMDump.Core/RC4_NCM.cs index 1a64a6a..ca1c7ef 100644 --- a/src/NCMDumpCore/RC4_NCM.cs +++ b/src/NCMDump.Core/RC4_NCM.cs @@ -1,4 +1,4 @@ -namespace NCMDumpCore +namespace NCMDump.Core { public class RC4_NCM { @@ -19,12 +19,11 @@ public RC4_NCM(byte[] key) public byte[] Encrypt(byte[] data) { - Span span = new(data); - Encrypt(ref span); - return span.ToArray(); + Encrypt(data.AsSpan()); + return data; } - public int Encrypt(ref Span data) + public int Encrypt(Span data) { for (int m = 0; m < data.Length; m++) { @@ -37,13 +36,7 @@ public int Encrypt(ref Span data) public int Encrypt(Memory data) { - for (int m = 0; m < data.Length; m++) - { - i = (i + 1) & 0xFF; - j = (i + Keybox[i]) & 0xFF; - data.Span[m] ^= Keybox[(Keybox[i] + Keybox[j]) & 0xFF]; - } - return data.Length; + return Encrypt(data.Span); } public byte[] Decrypt(byte[] data) @@ -53,7 +46,12 @@ public byte[] Decrypt(byte[] data) public int Decrypt(Span data) { - return Encrypt(ref data); + return Encrypt(data); + } + + public int Decrypt(Memory data) + { + return Encrypt(data); } } } \ No newline at end of file diff --git a/src/NCMDump.Core/RC4_NCM_Stream.cs b/src/NCMDump.Core/RC4_NCM_Stream.cs new file mode 100644 index 0000000..b85468f --- /dev/null +++ b/src/NCMDump.Core/RC4_NCM_Stream.cs @@ -0,0 +1,83 @@ +namespace NCMDump.Core +{ + public class RC4_NCM_Stream : Stream, IRC4_NCM_Stream + { + private readonly Stream _innerStream; + private readonly RC4_NCM rc4; + + public RC4_NCM_Stream(Stream innerStream, byte[] key) + { + this._innerStream = innerStream; + rc4 = new RC4_NCM(key); + } + + public override bool CanRead => _innerStream.CanRead; + public override bool CanSeek => _innerStream.CanSeek; + public override bool CanWrite => _innerStream.CanWrite; + public override long Length => _innerStream.Length; + + public override long Position + { + get => _innerStream.Position; + set => _innerStream.Position = value; + } + + public override void Flush() + { + _innerStream.Flush(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + int bytesRead = Read(buffer.AsSpan(offset, count)); + return bytesRead; + } + + public override int Read(Span buffer) + { + int bytesRead = _innerStream.Read(buffer); + rc4.Encrypt(buffer); + return bytesRead; + } + + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + return new Task(() => Read(buffer.AsSpan(offset, count))); + } + + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) + { + int bytesRead = _innerStream.Read(buffer.Span); + return new ValueTask(rc4.Encrypt(buffer)); + } + + public override long Seek(long offset, SeekOrigin origin) + { + return _innerStream.Seek(offset, origin); + } + + public override void SetLength(long value) + { + _innerStream.SetLength(value); + } + + public override bool CanTimeout => base.CanTimeout; + + public override void Close() => base.Close(); + + public override void Write(byte[] buffer, int offset, int count) + => throw new NotSupportedException(); + + public override void Write(ReadOnlySpan buffer) + => throw new NotSupportedException(); + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + => throw new NotSupportedException(); + + public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + => throw new NotSupportedException(); + + public override void CopyTo(Stream destination, int bufferSize) + => throw new NotSupportedException(); + } +} \ No newline at end of file diff --git a/src/NCMDump.Net.sln b/src/NCMDump.Net.sln index 5524e59..0099679 100644 --- a/src/NCMDump.Net.sln +++ b/src/NCMDump.Net.sln @@ -3,13 +3,13 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.31912.275 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NCMDumpCore", "NCMDumpCore\NCMDumpCore.csproj", "{6D29FF99-9A20-4AC2-8246-746C2AE4FB73}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NCMDump.Core", "NCMDump.Core\NCMDump.Core.csproj", "{6D29FF99-9A20-4AC2-8246-746C2AE4FB73}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NCMDumpCLI", "NCMDumpCLI\NCMDumpCLI.csproj", "{4CB104C3-FE3A-4CF4-9910-5FB0150B22A6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NCMDump.CLI", "NCMDump.CLI\NCMDump.CLI.csproj", "{4CB104C3-FE3A-4CF4-9910-5FB0150B22A6}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NCMDumpGUI", "NCMDumpGUI\NCMDumpGUI.csproj", "{F4B097C1-58BB-4A79-8997-8225639D2A92}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NCMDump.WPF", "NCMDump.WPF\NCMDump.WPF.csproj", "{F4B097C1-58BB-4A79-8997-8225639D2A92}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NCMDumpGUI_WinUI", "NCMDumpGUI_WinUI\NCMDumpGUI_WinUI.csproj", "{1F6956D8-3F8D-4F68-893E-790329FAA441}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NCMDump.WinUI", "NCMDump.WinUI\NCMDump.WinUI.csproj", "{1F6956D8-3F8D-4F68-893E-790329FAA441}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/src/NCMDumpGUI/App.xaml b/src/NCMDump.WPF/App.xaml similarity index 92% rename from src/NCMDumpGUI/App.xaml rename to src/NCMDump.WPF/App.xaml index 3a323f0..853b313 100644 --- a/src/NCMDumpGUI/App.xaml +++ b/src/NCMDump.WPF/App.xaml @@ -1,4 +1,4 @@ - diff --git a/src/NCMDumpGUI/App.xaml.cs b/src/NCMDump.WPF/App.xaml.cs similarity index 85% rename from src/NCMDumpGUI/App.xaml.cs rename to src/NCMDump.WPF/App.xaml.cs index 8eb8cc7..2c8ceca 100644 --- a/src/NCMDumpGUI/App.xaml.cs +++ b/src/NCMDump.WPF/App.xaml.cs @@ -1,17 +1,18 @@ -using Microsoft.Extensions.DependencyInjection; -using NCMDumpCore; -using System; +using System; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Windows; +using Microsoft.Extensions.DependencyInjection; +using NCMDump.Core; -namespace NCMDumpGUI +namespace NCMDump.WPF { public partial class App : Application { private IServiceProvider _serviceProvider; private ObservableCollection NCMCollection = new(); + private int depth; protected override void OnStartup(StartupEventArgs e) { @@ -26,8 +27,8 @@ protected override void OnStartup(StartupEventArgs e) MainWindowViewModel vm = _serviceProvider.GetRequiredService(); vm.NCMCollection = NCMCollection; } - var mainWindow = _serviceProvider.GetRequiredService(); - mainWindow.Show(); + MainWindow = _serviceProvider.GetRequiredService(); + MainWindow.Show(); } private void ConfigureServices(IServiceCollection services) @@ -65,6 +66,12 @@ private void OnDrop(string[] args) private void WalkThrough(DirectoryInfo dir) { + depth++; + if (depth > 16) + { + depth--; + return; + } foreach (DirectoryInfo d in dir.GetDirectories()) { WalkThrough(d); @@ -74,6 +81,7 @@ private void WalkThrough(DirectoryInfo dir) if (f.FullName.EndsWith(@".ncm") && !NCMCollection.Any(x => x.FilePath == f.FullName)) NCMCollection.Add(new NCMProcessStatus(f.FullName, "Await")); } + depth--; } } } \ No newline at end of file diff --git a/src/NCMDumpGUI/AssemblyInfo.cs b/src/NCMDump.WPF/AssemblyInfo.cs similarity index 100% rename from src/NCMDumpGUI/AssemblyInfo.cs rename to src/NCMDump.WPF/AssemblyInfo.cs diff --git a/src/NCMDumpGUI/Converter/BackdropConverter.cs b/src/NCMDump.WPF/Converter/BackdropConverter.cs similarity index 98% rename from src/NCMDumpGUI/Converter/BackdropConverter.cs rename to src/NCMDump.WPF/Converter/BackdropConverter.cs index ac1131d..cd5f429 100644 --- a/src/NCMDumpGUI/Converter/BackdropConverter.cs +++ b/src/NCMDump.WPF/Converter/BackdropConverter.cs @@ -3,7 +3,7 @@ using System.Windows.Data; using Wpf.Ui.Controls; -namespace NCMDumpGUI.Converter +namespace NCMDump.WPF.Converter { public class BackdropConverter : IValueConverter { diff --git a/src/NCMDumpGUI/Converter/InverseBoolConverter.cs b/src/NCMDump.WPF/Converter/InverseBoolConverter.cs similarity index 95% rename from src/NCMDumpGUI/Converter/InverseBoolConverter.cs rename to src/NCMDump.WPF/Converter/InverseBoolConverter.cs index dd7e77c..579797d 100644 --- a/src/NCMDumpGUI/Converter/InverseBoolConverter.cs +++ b/src/NCMDump.WPF/Converter/InverseBoolConverter.cs @@ -2,7 +2,7 @@ using System.Globalization; using System.Windows.Data; -namespace NCMDumpGUI.Converter +namespace NCMDump.WPF.Converter { public class InverseBoolConverter : IValueConverter { diff --git a/src/NCMDumpGUI/MainWindow.xaml b/src/NCMDump.WPF/MainWindow.xaml similarity index 91% rename from src/NCMDumpGUI/MainWindow.xaml rename to src/NCMDump.WPF/MainWindow.xaml index 1b2a02c..87fe8c1 100644 --- a/src/NCMDumpGUI/MainWindow.xaml +++ b/src/NCMDump.WPF/MainWindow.xaml @@ -1,13 +1,13 @@ - + Command="{Binding AddFileCommand}" + IsEnabled="{Binding IsBusy, Converter={StaticResource InverseBoolConverter}}" /> + Command="{Binding AddFolderCommand}" + IsEnabled="{Binding IsBusy, Converter={StaticResource InverseBoolConverter}}" /> + Command="{Binding ClearCommand}" + IsEnabled="{Binding IsBusy, Converter={StaticResource InverseBoolConverter}}" /> \ No newline at end of file diff --git a/src/NCMDumpGUI/MainWindow.xaml.cs b/src/NCMDump.WPF/MainWindow.xaml.cs similarity index 98% rename from src/NCMDumpGUI/MainWindow.xaml.cs rename to src/NCMDump.WPF/MainWindow.xaml.cs index d8ceed0..3e33337 100644 --- a/src/NCMDumpGUI/MainWindow.xaml.cs +++ b/src/NCMDump.WPF/MainWindow.xaml.cs @@ -3,7 +3,7 @@ using Wpf.Ui.Appearance; using Wpf.Ui.Controls; -namespace NCMDumpGUI +namespace NCMDump.WPF { public partial class MainWindow : FluentWindow { diff --git a/src/NCMDumpGUI/MainWindowViewModel.cs b/src/NCMDump.WPF/MainWindowViewModel.cs similarity index 76% rename from src/NCMDumpGUI/MainWindowViewModel.cs rename to src/NCMDump.WPF/MainWindowViewModel.cs index cefd1f2..598035f 100644 --- a/src/NCMDumpGUI/MainWindowViewModel.cs +++ b/src/NCMDump.WPF/MainWindowViewModel.cs @@ -1,22 +1,21 @@ -using CommunityToolkit.Mvvm.ComponentModel; -using CommunityToolkit.Mvvm.Input; -using Microsoft.WindowsAPICodePack.Dialogs; -using NCMDumpCore; -using System; +using System; using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Threading.Tasks; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using Microsoft.Win32; +using NCMDump.Core; using Wpf.Ui.Appearance; using Wpf.Ui.Controls; -namespace NCMDumpGUI +namespace NCMDump.WPF { - [ObservableObject] - public partial class MainWindowViewModel + public partial class MainWindowViewModel : ObservableObject { - private readonly NCMDumper Core; + private readonly NCMDumper Dumper; private bool _isBusy = false; public bool IsBusy @@ -66,14 +65,20 @@ public WindowBackdropType SelectedBackdrop } } + public IAsyncRelayCommand AddFolderCommand { get; } + public IAsyncRelayCommand AddFileCommand { get; } + public IAsyncRelayCommand ClearCommand { get; } + public IAsyncRelayCommand ConvertCommand { get; } + public IAsyncRelayCommand ThemeCommand { get; } + public MainWindowViewModel(NCMDumper _core) { - Core = _core; + Dumper = _core; WillDeleteNCM = true; ApplicationTitle = "NCMDump.NET"; NCMCollection = []; - AddFolderCommand = new AsyncRelayCommand(FolderDialog); - AddFileCommand = new AsyncRelayCommand(FileDialog); + AddFolderCommand = new AsyncRelayCommand(OpenSelectFolderDialog); + AddFileCommand = new AsyncRelayCommand(OpenSelectFileDialog); ClearCommand = new AsyncRelayCommand(ClearList, () => NCMCollection.Count > 0); ConvertCommand = new AsyncRelayCommand(StartConvert, () => NCMCollection.Count > 0); ThemeCommand = new AsyncRelayCommand(SwitchTheme); @@ -96,18 +101,18 @@ public MainWindowViewModel(NCMDumper _core) } } - public void OnDrop(string[] args) + public void OnDrop(params string[] paths) { - foreach (string _path in args) + foreach (string path in paths) { - if (new DirectoryInfo(_path).Exists) + if (new DirectoryInfo(path).Exists) { - WalkThrough(new DirectoryInfo(_path)); + WalkThrough(new DirectoryInfo(path)); } - else if (new FileInfo(_path).Exists) + else if (new FileInfo(path).Exists) { - if (_path.EndsWith(@".ncm") && !NCMCollection.Any(x => x.FilePath == _path)) - NCMCollection.Add(new NCMProcessStatus(_path, "Await")); + if (path.EndsWith(@".ncm") && !NCMCollection.Any(x => x.FilePath == path)) + NCMCollection.Add(new NCMProcessStatus(path, "Await")); } } ConvertCommand.NotifyCanExecuteChanged(); @@ -127,12 +132,6 @@ private void WalkThrough(DirectoryInfo dir) } } - public IAsyncRelayCommand AddFolderCommand { get; } - public IAsyncRelayCommand AddFileCommand { get; } - public IAsyncRelayCommand ClearCommand { get; } - public IAsyncRelayCommand ConvertCommand { get; } - public IAsyncRelayCommand ThemeCommand { get; } - private async Task StartConvert() { IsBusy = true; @@ -142,19 +141,19 @@ await Parallel.ForAsync(0, NCMCollection.Count, async (i, state) => { try { - if (await Core.ConvertAsync(NCMCollection[i].FilePath)) + if (await Dumper.ConvertAsync(NCMCollection[i].FilePath)) { NCMCollection[i].FileStatus = "Success"; - try + if (WillDeleteNCM) { - if (WillDeleteNCM) + try { File.Delete(NCMCollection[i].FilePath); } - } - catch (Exception ex) - { - Debug.WriteLine(ex.ToString()); + catch (Exception ex) + { + Debug.WriteLine(ex.ToString()); + } } } else @@ -174,35 +173,29 @@ await Parallel.ForAsync(0, NCMCollection.Count, async (i, state) => IsBusy = false; } - private async Task FolderDialog() + private async Task OpenSelectFolderDialog() { - var dialog = new CommonOpenFileDialog + OpenFolderDialog ofp = new OpenFolderDialog { - IsFolderPicker = true, - Title = "选择文件夹" + Multiselect = true, }; - if (dialog.ShowDialog() == CommonFileDialogResult.Ok) + if (ofp.ShowDialog() == true) { - string folderPath = dialog.FileName; - OnDrop([folderPath]); + OnDrop(ofp.FolderNames); } } - private async Task FileDialog() + private async Task OpenSelectFileDialog() { - Microsoft.Win32.OpenFileDialog ofp = new() + OpenFileDialog ofp = new OpenFileDialog { Multiselect = true, Filter = "NCM File(*.ncm)|*.ncm" }; if (ofp.ShowDialog() == true) { - foreach (string file in ofp.FileNames) - { - if (file.EndsWith(@".ncm") && !NCMCollection.Any(x => x.FilePath == file)) - NCMCollection.Add(new NCMProcessStatus(file, "Await")); - } + OnDrop(ofp.FileNames); } } diff --git a/src/NCMDumpGUI/NCMDumpGUI.csproj b/src/NCMDump.WPF/NCMDump.WPF.csproj similarity index 66% rename from src/NCMDumpGUI/NCMDumpGUI.csproj rename to src/NCMDump.WPF/NCMDump.WPF.csproj index 1fe6a79..3b2ffb2 100644 --- a/src/NCMDumpGUI/NCMDumpGUI.csproj +++ b/src/NCMDump.WPF/NCMDump.WPF.csproj @@ -6,8 +6,9 @@ enable True NCMDump.ico - 2.3.0 + 2.4.0 x64 + app.manifest @@ -16,15 +17,13 @@ - + + + - - - - - + diff --git a/src/NCMDumpGUI/NCMDump.ico b/src/NCMDump.WPF/NCMDump.ico similarity index 100% rename from src/NCMDumpGUI/NCMDump.ico rename to src/NCMDump.WPF/NCMDump.ico diff --git a/src/NCMDumpGUI/NCMProcessStatus.cs b/src/NCMDump.WPF/NCMProcessStatus.cs similarity index 53% rename from src/NCMDumpGUI/NCMProcessStatus.cs rename to src/NCMDump.WPF/NCMProcessStatus.cs index 412acd9..28e84d0 100644 --- a/src/NCMDumpGUI/NCMProcessStatus.cs +++ b/src/NCMDump.WPF/NCMProcessStatus.cs @@ -1,18 +1,18 @@ using CommunityToolkit.Mvvm.ComponentModel; -namespace NCMDumpGUI +namespace NCMDump.WPF { public partial class NCMProcessStatus : ObservableObject { - private string filePath; + private string _filePath; public string FilePath { - get => filePath; - set => SetProperty(ref filePath, value); + get => _filePath; + set => SetProperty(ref _filePath, value); } - public string _filestatus; + private string _filestatus; public string FileStatus { @@ -20,10 +20,10 @@ public string FileStatus set => SetProperty(ref _filestatus, value); } - public NCMProcessStatus(string _path, string _status) + public NCMProcessStatus(string path, string status) { - FilePath = _path; - FileStatus = _status; + FilePath = path; + FileStatus = status; } } } \ No newline at end of file diff --git a/src/NCMDumpGUI/Properties/launchSettings.json b/src/NCMDump.WPF/Properties/launchSettings.json similarity index 74% rename from src/NCMDumpGUI/Properties/launchSettings.json rename to src/NCMDump.WPF/Properties/launchSettings.json index 1a1c9aa..d0be389 100644 --- a/src/NCMDumpGUI/Properties/launchSettings.json +++ b/src/NCMDump.WPF/Properties/launchSettings.json @@ -1,6 +1,6 @@ { "profiles": { - "NCMDumpGUI": { + "NCMDump.WPF": { "commandName": "Project" } } diff --git a/src/NCMDump.WPF/app.manifest b/src/NCMDump.WPF/app.manifest new file mode 100644 index 0000000..8672c07 --- /dev/null +++ b/src/NCMDump.WPF/app.manifest @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + PerMonitorV2 + + + + diff --git a/src/NCMDumpGUI/ncmdump.png b/src/NCMDump.WPF/ncmdump.png similarity index 100% rename from src/NCMDumpGUI/ncmdump.png rename to src/NCMDump.WPF/ncmdump.png diff --git a/src/NCMDumpGUI_WinUI/App.xaml b/src/NCMDump.WinUI/App.xaml similarity index 95% rename from src/NCMDumpGUI_WinUI/App.xaml rename to src/NCMDump.WinUI/App.xaml index b73358c..bd0a680 100644 --- a/src/NCMDumpGUI_WinUI/App.xaml +++ b/src/NCMDump.WinUI/App.xaml @@ -1,6 +1,6 @@ diff --git a/src/NCMDump.WinUI/App.xaml.cs b/src/NCMDump.WinUI/App.xaml.cs new file mode 100644 index 0000000..528d1d1 --- /dev/null +++ b/src/NCMDump.WinUI/App.xaml.cs @@ -0,0 +1,160 @@ +using System.Diagnostics; +using System.Reflection; +using System.Runtime.InteropServices; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.UI.Dispatching; +using Microsoft.UI.Xaml; +using Microsoft.Windows.AppLifecycle; +using Microsoft.Windows.AppNotifications; +using NCMDump.Core; +using NCMDump.WinUI.ViewModels; + +// To learn more about WinUI, the WinUI project structure, +// and more about our project templates, see: http://aka.ms/winui-project-info. + +namespace NCMDump.WinUI +{ + /// + /// Provides application-specific behavior to supplement the default Application class. + /// + public partial class App : Application + { + private IServiceProvider _serviceProvider; + + /// + /// Initializes the singleton application object. This is the first line of authored code + /// executed, and as such is the logical equivalent of main() or WinMain(). + /// + public App() + { + this.InitializeComponent(); + } + + /// + /// Invoked when the application is launched. + /// + /// Details about the launch request and process. + protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args) + { + var services = new ServiceCollection(); + ConfigureServices(services); + _serviceProvider = services.BuildServiceProvider(); + + MainWindow = _serviceProvider.GetRequiredService(); + MainWindow.Activate(); + + AppNotificationManager notificationManager = AppNotificationManager.Default; + notificationManager.NotificationInvoked += NotificationManager_NotificationInvoked; + notificationManager.Register(); + + var activatedArgs = AppInstance.GetCurrent().GetActivatedEventArgs(); + var activationKind = activatedArgs.Kind; + if (activationKind != ExtendedActivationKind.AppNotification) + { + LaunchAndBringToForegroundIfNeeded(); + } + else + { + HandleNotification((AppNotificationActivatedEventArgs)activatedArgs.Data); + } + } + + public Window MainWindow; + + private void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + } + + public static TEnum GetEnum(string text) where TEnum : struct + { + if (!typeof(TEnum).GetTypeInfo().IsEnum) + { + throw new InvalidOperationException("Generic parameter 'TEnum' must be an enum."); + } + return (TEnum)Enum.Parse(typeof(TEnum), text); + } + + private void NotificationManager_NotificationInvoked(AppNotificationManager sender, AppNotificationActivatedEventArgs args) + { + HandleNotification(args); + } + + private void LaunchAndBringToForegroundIfNeeded() + { + if (MainWindow == null) + { + MainWindow = _serviceProvider.GetRequiredService(); + MainWindow.Activate(); + + // Additionally we show using our helper, since if activated via a app notification, it doesn't + // activate the window correctly + WindowHelper.ShowWindow(MainWindow); + } + else + { + WindowHelper.ShowWindow(MainWindow); + } + } + + private void HandleNotification(AppNotificationActivatedEventArgs args) + { + // Use the dispatcher from the window if present, otherwise the app dispatcher + var dispatcherQueue = MainWindow?.DispatcherQueue ?? DispatcherQueue.GetForCurrentThread(); + + dispatcherQueue.TryEnqueue(async delegate + { + var arguments = args.Arguments; + switch (args.Arguments["action"]) + { + // Send a background message + case "clicked": + Debug.WriteLine("Clicked"); + // TODO: Send it + + // If the UI app isn't open + if (MainWindow == null) + { + // Close since we're done + Process.GetCurrentProcess().Kill(); + } + + break; + + // View a message + case "viewMessage": + + // Launch/bring window to foreground + LaunchAndBringToForegroundIfNeeded(); + + // TODO: Open the message + break; + } + }); + } + } + + internal static class WindowHelper + { + [DllImport("user32.dll")] + private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool SetForegroundWindow(IntPtr hWnd); + + public static void ShowWindow(Window window) + { + // Bring the window to the foreground... first get the window handle... + var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(window); + + // Restore window if minimized... requires DLL import above + ShowWindow(hwnd, 0x00000009); + + // And call SetForegroundWindow... requires DLL import above + SetForegroundWindow(hwnd); + } + } +} \ No newline at end of file diff --git a/src/NCMDumpGUI_WinUI/Assets/LockScreenLogo.scale-200.png b/src/NCMDump.WinUI/Assets/LockScreenLogo.scale-200.png similarity index 100% rename from src/NCMDumpGUI_WinUI/Assets/LockScreenLogo.scale-200.png rename to src/NCMDump.WinUI/Assets/LockScreenLogo.scale-200.png diff --git a/src/NCMDumpGUI_WinUI/Assets/NCMDump.ico b/src/NCMDump.WinUI/Assets/NCMDump.ico similarity index 100% rename from src/NCMDumpGUI_WinUI/Assets/NCMDump.ico rename to src/NCMDump.WinUI/Assets/NCMDump.ico diff --git a/src/NCMDumpGUI_WinUI/Assets/SplashScreen.scale-200.png b/src/NCMDump.WinUI/Assets/SplashScreen.scale-200.png similarity index 100% rename from src/NCMDumpGUI_WinUI/Assets/SplashScreen.scale-200.png rename to src/NCMDump.WinUI/Assets/SplashScreen.scale-200.png diff --git a/src/NCMDumpGUI_WinUI/Assets/Square150x150Logo.scale-200.png b/src/NCMDump.WinUI/Assets/Square150x150Logo.scale-200.png similarity index 100% rename from src/NCMDumpGUI_WinUI/Assets/Square150x150Logo.scale-200.png rename to src/NCMDump.WinUI/Assets/Square150x150Logo.scale-200.png diff --git a/src/NCMDumpGUI_WinUI/Assets/Square44x44Logo.scale-200.png b/src/NCMDump.WinUI/Assets/Square44x44Logo.scale-200.png similarity index 100% rename from src/NCMDumpGUI_WinUI/Assets/Square44x44Logo.scale-200.png rename to src/NCMDump.WinUI/Assets/Square44x44Logo.scale-200.png diff --git a/src/NCMDumpGUI_WinUI/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/src/NCMDump.WinUI/Assets/Square44x44Logo.targetsize-24_altform-unplated.png similarity index 100% rename from src/NCMDumpGUI_WinUI/Assets/Square44x44Logo.targetsize-24_altform-unplated.png rename to src/NCMDump.WinUI/Assets/Square44x44Logo.targetsize-24_altform-unplated.png diff --git a/src/NCMDumpGUI_WinUI/Assets/StoreLogo.png b/src/NCMDump.WinUI/Assets/StoreLogo.png similarity index 100% rename from src/NCMDumpGUI_WinUI/Assets/StoreLogo.png rename to src/NCMDump.WinUI/Assets/StoreLogo.png diff --git a/src/NCMDumpGUI_WinUI/Assets/Wide310x150Logo.scale-200.png b/src/NCMDump.WinUI/Assets/Wide310x150Logo.scale-200.png similarity index 100% rename from src/NCMDumpGUI_WinUI/Assets/Wide310x150Logo.scale-200.png rename to src/NCMDump.WinUI/Assets/Wide310x150Logo.scale-200.png diff --git a/src/NCMDumpGUI_WinUI/Assets/ncmdump.png b/src/NCMDump.WinUI/Assets/ncmdump.png similarity index 100% rename from src/NCMDumpGUI_WinUI/Assets/ncmdump.png rename to src/NCMDump.WinUI/Assets/ncmdump.png diff --git a/src/NCMDumpGUI_WinUI/Converter/BackdropConverter.cs b/src/NCMDump.WinUI/Converter/BackdropConverter.cs similarity index 94% rename from src/NCMDumpGUI_WinUI/Converter/BackdropConverter.cs rename to src/NCMDump.WinUI/Converter/BackdropConverter.cs index 5919b97..83158ee 100644 --- a/src/NCMDumpGUI_WinUI/Converter/BackdropConverter.cs +++ b/src/NCMDump.WinUI/Converter/BackdropConverter.cs @@ -1,8 +1,7 @@ using Microsoft.UI.Xaml.Data; using Microsoft.UI.Xaml.Media; -using System; -namespace NCMDumpGUI_WinUI.Converter +namespace NCMDump.WinUI.Converter { public class BackdropConverter : IValueConverter { diff --git a/src/NCMDumpGUI_WinUI/MainWindow.xaml b/src/NCMDump.WinUI/MainWindow.xaml similarity index 85% rename from src/NCMDumpGUI_WinUI/MainWindow.xaml rename to src/NCMDump.WinUI/MainWindow.xaml index 35e7751..a59a15a 100644 --- a/src/NCMDumpGUI_WinUI/MainWindow.xaml +++ b/src/NCMDump.WinUI/MainWindow.xaml @@ -1,6 +1,6 @@ @@ -64,13 +64,13 @@ - - - + + + - - - + + + @@ -82,13 +82,13 @@ - - - + + + - - - + + + diff --git a/src/NCMDumpGUI_WinUI/MainWindow.xaml.cs b/src/NCMDump.WinUI/MainWindow.xaml.cs similarity index 97% rename from src/NCMDumpGUI_WinUI/MainWindow.xaml.cs rename to src/NCMDump.WinUI/MainWindow.xaml.cs index 643ff10..87099ba 100644 --- a/src/NCMDumpGUI_WinUI/MainWindow.xaml.cs +++ b/src/NCMDump.WinUI/MainWindow.xaml.cs @@ -1,9 +1,7 @@ using CommunityToolkit.WinUI.UI.Controls; using Microsoft.UI.Dispatching; using Microsoft.UI.Xaml; -using NCMDumpGUI_WinUI.ViewModels; -using System; -using System.Linq; +using NCMDump.WinUI.ViewModels; using Windows.ApplicationModel.DataTransfer; using Windows.Storage; using Windows.UI.ViewManagement; @@ -11,7 +9,7 @@ // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. -namespace NCMDumpGUI_WinUI +namespace NCMDump.WinUI { /// /// An empty window that can be used on its own or navigated to within a Frame. @@ -112,7 +110,7 @@ private void DataGrid_SizeChanged(object sender, SizeChangedEventArgs e) private void Window_Closed(object sender, WindowEventArgs e) { - App.Current.Exit(); + Application.Current.Exit(); } } } \ No newline at end of file diff --git a/src/NCMDumpGUI_WinUI/Models/NCMProcessStatus.cs b/src/NCMDump.WinUI/Models/NCMProcessStatus.cs similarity index 97% rename from src/NCMDumpGUI_WinUI/Models/NCMProcessStatus.cs rename to src/NCMDump.WinUI/Models/NCMProcessStatus.cs index 56c5f50..f1c51ba 100644 --- a/src/NCMDumpGUI_WinUI/Models/NCMProcessStatus.cs +++ b/src/NCMDump.WinUI/Models/NCMProcessStatus.cs @@ -3,7 +3,7 @@ using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Media; -namespace NCMDumpGUI_WinUI.Models +namespace NCMDump.WinUI.Models { public partial class NCMProcessStatus : ObservableObject { diff --git a/src/NCMDumpGUI_WinUI/NCMDumpGUI_WinUI.csproj b/src/NCMDump.WinUI/NCMDump.WinUI.csproj similarity index 77% rename from src/NCMDumpGUI_WinUI/NCMDumpGUI_WinUI.csproj rename to src/NCMDump.WinUI/NCMDump.WinUI.csproj index 41ed934..51dfa1b 100644 --- a/src/NCMDumpGUI_WinUI/NCMDumpGUI_WinUI.csproj +++ b/src/NCMDump.WinUI/NCMDump.WinUI.csproj @@ -3,7 +3,7 @@ WinExe net8.0-windows10.0.19041.0 10.0.19041.0 - NCMDumpGUI_WinUI + NCMDump.WinUI app.manifest x64 win-x64 @@ -13,19 +13,12 @@ 10.0.19041.0 true x64 - 2.3.0 + 2.4.0 WinUI Assets\NCMDump.ico - en-US + None + enable - - - - - - - - @@ -52,19 +45,17 @@ - + - - - - - + + + @@ -75,25 +66,19 @@ --> - - - - - PreserveNewest PreserveNewest - - MSBuild:Compile + - + + + + + + + + + + + + + + + diff --git a/src/NCMDumpGUI_WinUI/Properties/launchSettings.json b/src/NCMDump.WinUI/Properties/launchSettings.json similarity index 70% rename from src/NCMDumpGUI_WinUI/Properties/launchSettings.json rename to src/NCMDump.WinUI/Properties/launchSettings.json index 276e077..2f5f800 100644 --- a/src/NCMDumpGUI_WinUI/Properties/launchSettings.json +++ b/src/NCMDump.WinUI/Properties/launchSettings.json @@ -1,6 +1,6 @@ { "profiles": { - "NCMDumpGUI_WinUI (Unpackaged)": { + "NCMDump.WinUI (Unpackaged)": { "commandName": "Project", "nativeDebugging": false } diff --git a/src/NCMDumpGUI_WinUI/Resources/StringDictionary.xaml b/src/NCMDump.WinUI/Resources/StringDictionary.xaml similarity index 100% rename from src/NCMDumpGUI_WinUI/Resources/StringDictionary.xaml rename to src/NCMDump.WinUI/Resources/StringDictionary.xaml diff --git a/src/NCMDumpGUI_WinUI/ViewModels/MainWindowViewModel.cs b/src/NCMDump.WinUI/ViewModels/MainWindowViewModel.cs similarity index 79% rename from src/NCMDumpGUI_WinUI/ViewModels/MainWindowViewModel.cs rename to src/NCMDump.WinUI/ViewModels/MainWindowViewModel.cs index 9b5537d..86ac17c 100644 --- a/src/NCMDumpGUI_WinUI/ViewModels/MainWindowViewModel.cs +++ b/src/NCMDump.WinUI/ViewModels/MainWindowViewModel.cs @@ -1,20 +1,18 @@ -using CommunityToolkit.Mvvm.ComponentModel; +using System.Collections.ObjectModel; +using System.Diagnostics; +using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using CommunityToolkit.WinUI.Helpers; using Microsoft.UI.Dispatching; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Media; -using Microsoft.WindowsAPICodePack.Dialogs; -using NCMDumpCore; -using NCMDumpGUI_WinUI.Models; -using System; -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Threading.Tasks; +using Microsoft.Windows.AppNotifications; +using Microsoft.Windows.AppNotifications.Builder; +using NCMDump.Core; +using NCMDump.WinUI.Models; +using Windows.Storage.Pickers; -namespace NCMDumpGUI_WinUI.ViewModels +namespace NCMDump.WinUI.ViewModels { public partial class MainWindowViewModel : ObservableObject { @@ -108,7 +106,7 @@ public MainWindowViewModel(NCMDumper _core) } } - public void OnDrop(string[] args) + public void OnDrop(params string[] args) { foreach (string _path in args) { @@ -204,46 +202,47 @@ await Parallel.ForAsync(0, NCMCollection.Count, async (i, state) => GC.WaitForPendingFinalizers(); IsBusy = false; + + int total = NCMCollection.Count; + int success = NCMCollection.Count(x => x.FileStatus == "Success"); + //https://learn.microsoft.com/zh-cn/windows/apps/windows-app-sdk/migrate-to-windows-app-sdk/guides/toast-notifications?tabs=appsdk + var builder = new AppNotificationBuilder() + .AddText($"Dumped! ") + .AddText($"Success ({success}/{total})") + .AddArgument("action", "clicked"); + + AppNotificationManager.Default.Show(builder.BuildNotification()); } private async Task FolderDialog() { - var dialog = new CommonOpenFileDialog - { - IsFolderPicker = true - }; - - if (dialog.ShowDialog() == CommonFileDialogResult.Ok) + var hwnd = WinRT.Interop.WindowNative.GetWindowHandle((App.Current as App).MainWindow); + FolderPicker folderPicker = new FolderPicker(); + folderPicker.ViewMode = PickerViewMode.Thumbnail; + folderPicker.FileTypeFilter.Add("*"); + WinRT.Interop.InitializeWithWindow.Initialize(folderPicker, hwnd); + var folder = await folderPicker.PickSingleFolderAsync(); + if (folder != null) { - string folderPath = dialog.FileName; - OnDrop([folderPath]); + OnDrop([folder.Path]); } } private async Task FileDialog() { - Microsoft.Win32.OpenFileDialog ofp = new Microsoft.Win32.OpenFileDialog(); - ofp.Multiselect = true; - ofp.Filter = "NCM File(*.ncm)|*.ncm"; - if (ofp.ShowDialog() == true) - { - OnDrop(ofp.FileNames); - } - //WinRT shithole - //var hwnd = WinRT.Interop.WindowNative.GetWindowHandle((App.Current as App).MainWindow); - //FileOpenPicker filePicker = new FileOpenPicker(); - //filePicker.ViewMode = PickerViewMode.List; - //filePicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary; - //filePicker.FileTypeFilter.Add("*"); - //WinRT.Interop.InitializeWithWindow.Initialize(filePicker, hwnd); - //var files = await filePicker.PickMultipleFilesAsync(); - //if (files != null) - //{ - // var filelist = files.Select(x => x.Path).ToArray(); - // OnDrop(filelist); - //} + var hwnd = WinRT.Interop.WindowNative.GetWindowHandle((App.Current as App).MainWindow); + FileOpenPicker filePicker = new FileOpenPicker(); + filePicker.ViewMode = PickerViewMode.Thumbnail; + filePicker.FileTypeFilter.Add(".ncm"); + WinRT.Interop.InitializeWithWindow.Initialize(filePicker, hwnd); + var files = await filePicker.PickMultipleFilesAsync(); + if (files != null) + { + var filelist = files.Select(x => x.Path).ToArray(); + OnDrop(filelist); + } } private async Task ClearList() diff --git a/src/NCMDump.WinUI/app.manifest b/src/NCMDump.WinUI/app.manifest new file mode 100644 index 0000000..781a0c6 --- /dev/null +++ b/src/NCMDump.WinUI/app.manifest @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + PerMonitorV2 + + + \ No newline at end of file diff --git a/src/NCMDumpCLI/Program.cs b/src/NCMDumpCLI/Program.cs deleted file mode 100644 index d47e0ad..0000000 --- a/src/NCMDumpCLI/Program.cs +++ /dev/null @@ -1,61 +0,0 @@ -using NCMDumpCore; - -public class NCMDumpCLI -{ - public static void Main(params string[] args) - { - NCMDumper Core = new(); - - if (args.Length == 0) - { - Console.WriteLine("Drag [*.ncm] file or directory on exe to start"); - } - else - { - try - { - foreach (string path in args) - { - if (new DirectoryInfo(path).Exists) - { - WalkThrough(new DirectoryInfo(path)); - } - else if (new FileInfo(path).Exists) - { - Console.WriteLine("FILE: " + new FileInfo(path).FullName); - if (Core.Convert(path)) Console.WriteLine("... Convert OK"); - else Console.WriteLine("... Convert Fail"); - Console.WriteLine(); - } - Console.WriteLine(); - } - } - catch (Exception ex) - { - Console.WriteLine(ex.Message); - } - } - - Console.Write("Press Any Key to Exit..."); - Console.ReadLine(); - return; - - void WalkThrough(DirectoryInfo dir) - { - Console.WriteLine("DIR: " + dir.FullName); - foreach (DirectoryInfo d in dir.GetDirectories()) - { - WalkThrough(d); - } - foreach (FileInfo f in dir.EnumerateFiles()) - { - Console.WriteLine("Converting : " + f.FullName); - if (Core.Convert(f.FullName)) Console.WriteLine("...OK"); - else Console.WriteLine("...Fail"); - Console.WriteLine(); - } - - Console.WriteLine(); - } - } -} \ No newline at end of file diff --git a/src/NCMDumpCore/RC4_NCM_Stream.cs b/src/NCMDumpCore/RC4_NCM_Stream.cs deleted file mode 100644 index 95f2539..0000000 --- a/src/NCMDumpCore/RC4_NCM_Stream.cs +++ /dev/null @@ -1,106 +0,0 @@ -namespace NCMDumpCore -{ - public class RC4_NCM_Stream : Stream - { - private readonly Stream innerStream; - private readonly RC4_NCM rc4; - - public RC4_NCM_Stream(Stream innerStream, byte[] key) - { - this.innerStream = innerStream; - rc4 = new RC4_NCM(key); - } - - public override bool CanRead => innerStream.CanRead; - public override bool CanSeek => innerStream.CanSeek; - public override bool CanWrite => innerStream.CanWrite; - public override long Length => innerStream.Length; - - public override long Position - { - get => innerStream.Position; - set => innerStream.Position = value; - } - - public override void Flush() - { - innerStream.Flush(); - } - - public override int Read(byte[] buffer, int offset, int count) - { - Span span = new byte[count]; - innerStream.Seek(offset, SeekOrigin.Current); - int bytesRead = Read(span); - span.CopyTo(buffer); - - //int bytesRead = innerStream.Read(buffer, offset, count); - //byte[] decryptedBytes = rc4.Encrypt(buffer[offset..(offset + bytesRead)]); - //Array.Copy(decryptedBytes, 0, buffer, offset, decryptedBytes.Length); - return bytesRead; - } - - public override int Read(Span buffer) - { - int bytesRead = innerStream.Read(buffer); - rc4.Encrypt(ref buffer); - return bytesRead; - } - - public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - innerStream.Seek(offset, SeekOrigin.Current); - int bytesRead = await Task.Run(() => Read(buffer)); - return bytesRead; - } - - public override async ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) - { - int bytesRead = innerStream.Read(buffer.Span); - await Task.Run(() => rc4.Encrypt(buffer)); - return bytesRead; - } - - public override long Seek(long offset, SeekOrigin origin) - { - return innerStream.Seek(offset, origin); - } - - public override void SetLength(long value) - { - innerStream.SetLength(value); - } - - public override void Write(byte[] buffer, int offset, int count) - { - Span span = buffer.AsSpan(offset, count); - rc4.Encrypt(ref span); - innerStream.Write(span); - } - - public override void Write(ReadOnlySpan buffer) - { - Span span = new byte[buffer.Length]; - buffer.CopyTo(span); - rc4.Encrypt(ref span); - - innerStream.Write(buffer); - } - - public override async Task WriteAsync(byte[] data, int offset, int count, CancellationToken cancellationToken) - { - byte[] buffer = data[offset..(offset + count)]; - byte[] encrypted = await Task.Run(() => rc4.Encrypt(buffer)); - await innerStream.WriteAsync(encrypted, cancellationToken); - return; - } - - public override async ValueTask WriteAsync(ReadOnlyMemory data, CancellationToken cancellationToken = default) - { - byte[] buffer = data.ToArray(); - await Task.Run(() => rc4.Encrypt(buffer)); - await innerStream.WriteAsync(buffer, cancellationToken); - return; - } - } -} \ No newline at end of file diff --git a/src/NCMDumpGUI/NCMDumpGUI - Backup.csproj b/src/NCMDumpGUI/NCMDumpGUI - Backup.csproj deleted file mode 100644 index ffb87fa..0000000 --- a/src/NCMDumpGUI/NCMDumpGUI - Backup.csproj +++ /dev/null @@ -1,31 +0,0 @@ - - - - WinExe - net7.0-windows10.0.19041.0 - enable - True - NCMDump.ico - True - 1.6.2 - x64 - - - - - - - - - - - - - - - - - - - - diff --git a/src/NCMDumpGUI_WinUI/App.xaml.cs b/src/NCMDumpGUI_WinUI/App.xaml.cs deleted file mode 100644 index 11a440a..0000000 --- a/src/NCMDumpGUI_WinUI/App.xaml.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using Microsoft.UI.Xaml; -using NCMDumpCore; -using NCMDumpGUI_WinUI.ViewModels; -using System; -using System.Reflection; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace NCMDumpGUI_WinUI -{ - /// - /// Provides application-specific behavior to supplement the default Application class. - /// - public partial class App : Application - { - private IServiceProvider _serviceProvider; - - /// - /// Initializes the singleton application object. This is the first line of authored code - /// executed, and as such is the logical equivalent of main() or WinMain(). - /// - public App() - { - this.InitializeComponent(); - } - - /// - /// Invoked when the application is launched. - /// - /// Details about the launch request and process. - protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args) - { - var services = new ServiceCollection(); - ConfigureServices(services); - _serviceProvider = services.BuildServiceProvider(); - MainWindow = _serviceProvider.GetRequiredService(); - MainWindow.Activate(); - } - - public Window MainWindow; - - private void ConfigureServices(IServiceCollection services) - { - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - } - - public static TEnum GetEnum(string text) where TEnum : struct - { - if (!typeof(TEnum).GetTypeInfo().IsEnum) - { - throw new InvalidOperationException("Generic parameter 'TEnum' must be an enum."); - } - return (TEnum)Enum.Parse(typeof(TEnum), text); - } - } -} \ No newline at end of file diff --git a/src/NCMDumpGUI_WinUI/app.manifest b/src/NCMDumpGUI_WinUI/app.manifest deleted file mode 100644 index 9caf2e7..0000000 --- a/src/NCMDumpGUI_WinUI/app.manifest +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - PerMonitorV2 - - - \ No newline at end of file