├── .editorconfig ├── .gitattributes ├── .gitignore ├── .gitmodules ├── BmsManager.Tests ├── BmsManager.Tests.csproj ├── BmsTableTests.cs ├── Data │ ├── BmsFolderTests.cs │ └── RootDirectoryTests.cs ├── UtilityTests.cs └── _ld2013_a.bms ├── BmsManager.sln ├── BmsManager ├── App.xaml ├── App.xaml.cs ├── AssemblyInfo.cs ├── Beatoraja │ ├── BeatorajaFolder.cs │ ├── BeatorajaInformation.cs │ ├── BeatorajaSong.cs │ ├── BeatorajaSongdataContext.cs │ └── BeatorajaSonginfoContext.cs ├── BmsManager.csproj ├── Data │ └── BmsTableDocument.cs ├── Entity │ ├── BmsFile.cs │ ├── BmsFolder.cs │ ├── BmsManagerContext.cs │ ├── BmsTable.cs │ ├── BmsTableData.cs │ ├── BmsTableDifficulty.cs │ └── RootDirectory.cs ├── MainWindow.xaml ├── MainWindow.xaml.cs ├── MainWindowViewModel.cs ├── Model │ ├── EntityExtensions.cs │ └── FolderLoader.cs ├── Settings.cs ├── SystemProvider.cs ├── Utility.cs ├── View │ ├── BmsFileList.xaml │ ├── BmsFileList.xaml.cs │ ├── BmsFileSearcher.xaml │ ├── BmsFileSearcher.xaml.cs │ ├── BmsTableDataList.xaml │ ├── BmsTableDataList.xaml.cs │ ├── BmsTableManager.xaml │ ├── BmsTableManager.xaml.cs │ ├── BmsTableTreeView.xaml │ ├── BmsTableTreeView.xaml.cs │ ├── DiffRegisterer.xaml │ ├── DiffRegisterer.xaml.cs │ ├── DuplicateBmsChecker.xaml │ ├── DuplicateBmsChecker.xaml.cs │ ├── Exporter.xaml │ ├── Exporter.xaml.cs │ ├── FolderRegisterer.xaml │ ├── FolderRegisterer.xaml.cs │ ├── RootTreeView.xaml │ └── RootTreeView.xaml.cs ├── ViewModel │ ├── BmsFileListViewModel.cs │ ├── BmsFileSearcherViewModel.cs │ ├── BmsFileViewModel.cs │ ├── BmsFolderViewModel.cs │ ├── BmsTableDataListViewModel.cs │ ├── BmsTableDataViewModel.cs │ ├── BmsTableManagerViewModel.cs │ ├── BmsTableTreeViewModel.cs │ ├── BmsTableViewModel.cs │ ├── DiffFileViewModel.cs │ ├── DiffRegistererViewModel.cs │ ├── DuplicateBmsCheckerViewModel.cs │ ├── ExporterViewModel.cs │ ├── FolderRegisterViewModel.cs │ ├── RootDirectoryViewModel.cs │ └── RootTreeViewModel.cs └── appsettings.json ├── DDL_SQLServer.sql ├── LICENSE └── README.md /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | 3 | # CA1416: プラットフォームの互換性を検証 4 | dotnet_diagnostic.CA1416.severity = none 5 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set the merge driver for project and solution files 8 | # 9 | # Merging from the command prompt will add diff markers to the files if there 10 | # are conflicts (Merging from VS is not affected by the settings below, in VS 11 | # the diff markers are never inserted). Diff markers may cause the following 12 | # file extensions to fail to load in VS. An alternative would be to treat 13 | # these files as binary and thus will always conflict and require user 14 | # intervention with every merge. To do so, just comment the entries below and 15 | # uncomment the group further below 16 | ############################################################################### 17 | 18 | *.sln text eol=crlf 19 | *.csproj text eol=crlf 20 | *.vbproj text eol=crlf 21 | *.vcxproj text eol=crlf 22 | *.vcproj text eol=crlf 23 | *.dbproj text eol=crlf 24 | *.fsproj text eol=crlf 25 | *.lsproj text eol=crlf 26 | *.wixproj text eol=crlf 27 | *.modelproj text eol=crlf 28 | *.sqlproj text eol=crlf 29 | *.wmaproj text eol=crlf 30 | 31 | *.xproj text eol=crlf 32 | *.props text eol=crlf 33 | *.filters text eol=crlf 34 | *.vcxitems text eol=crlf 35 | 36 | 37 | #*.sln merge=binary 38 | #*.csproj merge=binary 39 | #*.vbproj merge=binary 40 | #*.vcxproj merge=binary 41 | #*.vcproj merge=binary 42 | #*.dbproj merge=binary 43 | #*.fsproj merge=binary 44 | #*.lsproj merge=binary 45 | #*.wixproj merge=binary 46 | #*.modelproj merge=binary 47 | #*.sqlproj merge=binary 48 | #*.wwaproj merge=binary 49 | 50 | #*.xproj merge=binary 51 | #*.props merge=binary 52 | #*.filters merge=binary 53 | #*.vcxitems merge=binary 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # NuGet Symbol Packages 188 | *.snupkg 189 | # The packages folder can be ignored because of Package Restore 190 | **/[Pp]ackages/* 191 | # except build/, which is used as an MSBuild target. 192 | !**/[Pp]ackages/build/ 193 | # Uncomment if necessary however generally it will be regenerated when needed 194 | #!**/[Pp]ackages/repositories.config 195 | # NuGet v3's project.json files produces more ignorable files 196 | *.nuget.props 197 | *.nuget.targets 198 | 199 | # Microsoft Azure Build Output 200 | csx/ 201 | *.build.csdef 202 | 203 | # Microsoft Azure Emulator 204 | ecf/ 205 | rcf/ 206 | 207 | # Windows Store app package directories and files 208 | AppPackages/ 209 | BundleArtifacts/ 210 | Package.StoreAssociation.xml 211 | _pkginfo.txt 212 | *.appx 213 | *.appxbundle 214 | *.appxupload 215 | 216 | # Visual Studio cache files 217 | # files ending in .cache can be ignored 218 | *.[Cc]ache 219 | # but keep track of directories ending in .cache 220 | !?*.[Cc]ache/ 221 | 222 | # Others 223 | ClientBin/ 224 | ~$* 225 | *~ 226 | *.dbmdl 227 | *.dbproj.schemaview 228 | *.jfm 229 | *.pfx 230 | *.publishsettings 231 | orleans.codegen.cs 232 | 233 | # Including strong name files can present a security risk 234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 235 | #*.snk 236 | 237 | # Since there are multiple workflows, uncomment next line to ignore bower_components 238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 239 | #bower_components/ 240 | 241 | # RIA/Silverlight projects 242 | Generated_Code/ 243 | 244 | # Backup & report files from converting an old project file 245 | # to a newer Visual Studio version. Backup files are not needed, 246 | # because we have git ;-) 247 | _UpgradeReport_Files/ 248 | Backup*/ 249 | UpgradeLog*.XML 250 | UpgradeLog*.htm 251 | ServiceFabricBackup/ 252 | *.rptproj.bak 253 | 254 | # SQL Server files 255 | *.mdf 256 | *.ldf 257 | *.ndf 258 | 259 | # Business Intelligence projects 260 | *.rdl.data 261 | *.bim.layout 262 | *.bim_*.settings 263 | *.rptproj.rsuser 264 | *- [Bb]ackup.rdl 265 | *- [Bb]ackup ([0-9]).rdl 266 | *- [Bb]ackup ([0-9][0-9]).rdl 267 | 268 | # Microsoft Fakes 269 | FakesAssemblies/ 270 | 271 | # GhostDoc plugin setting file 272 | *.GhostDoc.xml 273 | 274 | # Node.js Tools for Visual Studio 275 | .ntvs_analysis.dat 276 | node_modules/ 277 | 278 | # Visual Studio 6 build log 279 | *.plg 280 | 281 | # Visual Studio 6 workspace options file 282 | *.opt 283 | 284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 285 | *.vbw 286 | 287 | # Visual Studio LightSwitch build output 288 | **/*.HTMLClient/GeneratedArtifacts 289 | **/*.DesktopClient/GeneratedArtifacts 290 | **/*.DesktopClient/ModelManifest.xml 291 | **/*.Server/GeneratedArtifacts 292 | **/*.Server/ModelManifest.xml 293 | _Pvt_Extensions 294 | 295 | # Paket dependency manager 296 | .paket/paket.exe 297 | paket-files/ 298 | 299 | # FAKE - F# Make 300 | .fake/ 301 | 302 | # CodeRush personal settings 303 | .cr/personal 304 | 305 | # Python Tools for Visual Studio (PTVS) 306 | __pycache__/ 307 | *.pyc 308 | 309 | # Cake - Uncomment if you are using it 310 | # tools/** 311 | # !tools/packages.config 312 | 313 | # Tabs Studio 314 | *.tss 315 | 316 | # Telerik's JustMock configuration file 317 | *.jmconfig 318 | 319 | # BizTalk build output 320 | *.btp.cs 321 | *.btm.cs 322 | *.odx.cs 323 | *.xsd.cs 324 | 325 | # OpenCover UI analysis results 326 | OpenCover/ 327 | 328 | # Azure Stream Analytics local run output 329 | ASALocalRun/ 330 | 331 | # MSBuild Binary and Structured Log 332 | *.binlog 333 | 334 | # NVidia Nsight GPU debugger configuration file 335 | *.nvuser 336 | 337 | # MFractors (Xamarin productivity tool) working folder 338 | .mfractor/ 339 | 340 | # Local History for Visual Studio 341 | .localhistory/ 342 | 343 | # BeatPulse healthcheck temp database 344 | healthchecksdb 345 | 346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 347 | MigrationBackup/ 348 | 349 | # Ionide (cross platform F# VS Code tools) working folder 350 | .ionide/ 351 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "DotNetLibraries"] 2 | path = DotNetLibraries 3 | url = https://github.com/keidrumfreak/DotNetLibraries.git 4 | [submodule "BmsParser"] 5 | path = BmsParser 6 | url = https://github.com/keidrumfreak/BmsParser.git 7 | -------------------------------------------------------------------------------- /BmsManager.Tests/BmsManager.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0-windows7.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | all 15 | runtime; build; native; contentfiles; analyzers; buildtransitive 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /BmsManager.Tests/BmsTableTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net.Http.Headers; 5 | using System.Net.Http; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Microsoft.VisualStudio.TestTools.UnitTesting; 9 | using BmsManager.Data; 10 | 11 | namespace BmsManager.Tests 12 | { 13 | [TestClass] 14 | public class BmsTableTests 15 | { 16 | [TestMethod] 17 | public void Load() 18 | { 19 | // satelliteをサンプルに使用 20 | var table = new BmsTableDocument(@"https://stellabms.xyz/sl/table.html"); 21 | table.LoadAsync(Utility.GetHttpClient()).Wait(); 22 | var data = table.ToEntity(); 23 | Assert.AreEqual(@"https://stellabms.xyz/sl", table.Home); 24 | Assert.IsTrue(data != null); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /BmsManager.Tests/Data/BmsFolderTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO.Abstractions.TestingHelpers; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using BmsManager.Entity; 8 | using CommonLib.TestHelper.UnitTesting; 9 | using Microsoft.VisualStudio.TestTools.UnitTesting; 10 | 11 | namespace BmsManager.Tests.Data 12 | { 13 | [TestClass] 14 | public class BmsFolderTests 15 | { 16 | MockFileSystem mock; 17 | [TestInitialize] 18 | public void Initialize() 19 | { 20 | mock = new MockFileSystem(); 21 | SystemProvider.Instance = new SystemProvider(mock); 22 | } 23 | 24 | //[DataTestMethod] 25 | //[DataRow("Result", "Result")] 26 | //[DataRow("Result\\", "Result¥")] 27 | //[DataRow("Result<", "Result<")] 28 | //[DataRow("Result>", "Result>")] 29 | //[DataRow("Result/", "Result/")] 30 | //[DataRow("Result*", "Result*")] 31 | //[DataRow("Result:", "Result:")] 32 | //[DataRow("Result\"", "Result”")] 33 | //[DataRow("Result?", "Result?")] 34 | //[DataRow("Result|", "Result|")] 35 | //public void Rename(string name, string rename) 36 | //{ 37 | // mock.AddDirectory(@"D:"); 38 | // mock.AddDirectory(@"D:\Test"); 39 | 40 | // var folder = new BmsFolder { Path = @"D:\Test" }; 41 | // folder.Rename(); 42 | 43 | // mock.Directory.Exists(@"D:\Test").IsFalse(); 44 | // mock.Directory.Exists(@$"D:\{rename}").IsTrue(); 45 | // folder.Path.AreEqual(@$"D:\{rename}"); 46 | //} 47 | 48 | //[TestMethod] 49 | //public void RenameIfExists() 50 | //{ 51 | // mock.AddDirectory(@"D:"); 52 | // mock.AddDirectory(@"D:\Test"); 53 | // mock.AddDirectory(@"D:\Result"); 54 | 55 | // var folder = new BmsFolder { Path = @"D:\Test" }; 56 | // folder.Rename(); 57 | 58 | // mock.Directory.Exists(@"D:\Test").IsFalse(); 59 | // mock.Directory.Exists(@$"D:\Result (2)").IsTrue(); 60 | // folder.Path.AreEqual(@$"D:\Result (2)"); 61 | 62 | // mock.AddDirectory(@"D:\Test"); 63 | 64 | // folder = new BmsFolder { Path = @"D:\Test" }; 65 | // folder.Rename(); 66 | 67 | // mock.Directory.Exists(@"D:\Test").IsFalse(); 68 | // mock.Directory.Exists(@$"D:\Result (3)").IsTrue(); 69 | // folder.Path.AreEqual(@$"D:\Result (3)"); 70 | //} 71 | } 72 | } -------------------------------------------------------------------------------- /BmsManager.Tests/Data/RootDirectoryTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.IO.Abstractions.TestingHelpers; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using BmsManager.Entity; 9 | using CommonLib.TestHelper.UnitTesting; 10 | using Microsoft.VisualStudio.TestTools.UnitTesting; 11 | 12 | namespace BmsManager.Tests.Data 13 | { 14 | [TestClass] 15 | public class RootDirectoryTests 16 | { 17 | //MockFileSystem mock; 18 | //[TestInitialize] 19 | //public void Initialize() 20 | //{ 21 | // mock = new MockFileSystem(); 22 | // SystemProvider.Instance = new SystemProvider(mock); 23 | //} 24 | 25 | //[TestMethod] 26 | //public void LoadFromFileSystem() 27 | //{ 28 | // mock.AddDirectory(@"D:\parent"); 29 | // var root1Path = @"D:\parent\root1"; 30 | // mock.AddDirectory(root1Path); 31 | // var folder1Path = @"D:\parent\root1\folder1"; 32 | // mock.AddDirectory(folder1Path); 33 | // var folder2Path = @"D:\parent\root1\folder2"; 34 | // mock.AddDirectory(folder2Path); 35 | // var root2Path = @"D:\parent\root2"; 36 | // mock.AddDirectory(root2Path); 37 | // var notRootPath = @"D:\parent\root2\notroot"; 38 | // mock.AddDirectory(notRootPath); 39 | // var childPath = @"D:\parent\root2\child"; 40 | // mock.AddDirectory(childPath); 41 | // var folder3Path = @"D:\parent\root2\child\folder3"; 42 | // mock.AddDirectory(folder3Path); 43 | // var folder4Path = @"D:\parent\root2\child\folder4"; 44 | // mock.AddDirectory(folder4Path); 45 | // var file1Path = @"D:\parent\root1\folder1\test.bms"; 46 | // mock.AddFile(file1Path, new MockFileData(File.ReadAllText(@"E:\_source\BmsManager\BmsManager.Tests\_ld2013_a.bms"))); 47 | // var file2Path = @"D:\parent\root1\folder2\test.bme"; 48 | // mock.AddFile(file2Path, new MockFileData(File.ReadAllText(@"E:\_source\BmsManager\BmsManager.Tests\_ld2013_a.bms"))); 49 | // var file3Path = @"D:\parent\root2\child\folder3\test.bml"; 50 | // mock.AddFile(file3Path, new MockFileData(File.ReadAllText(@"E:\_source\BmsManager\BmsManager.Tests\_ld2013_a.bms"))); 51 | // var file4Path = @"D:\parent\root2\child\folder4\test.pms"; 52 | // mock.AddFile(file4Path, new MockFileData(File.ReadAllText(@"E:\_source\BmsManager\BmsManager.Tests\_ld2013_a.bms"))); 53 | 54 | // var root = new RootDirectory { Path = @"D:\parent" }; 55 | // root.LoadFromFileSystem(); 56 | 57 | // var root1 = root.Children.FirstOrDefault(r => r.Path == root1Path); 58 | // if (root1 == default) Assert.Fail(); 59 | // var folder1 = root1.Folders.FirstOrDefault(f => f.Path == folder1Path); 60 | // if (folder1 == default) Assert.Fail(); 61 | // folder1.Files.Any(f => f.Path == file1Path).IsTrue(); 62 | // var folder2 = root1.Folders.FirstOrDefault(f => f.Path == folder2Path); 63 | // if (folder2 == default) Assert.Fail(); 64 | // folder2.Files.Any(f => f.Path == file2Path).IsTrue(); 65 | // var root2 = root.Children.FirstOrDefault(r => r.Path == root2Path); 66 | // root2.Children.Any(r => r.Path == notRootPath).IsFalse(); 67 | // var child = root2.Children.FirstOrDefault(r => r.Path == childPath); 68 | // if (child == default) Assert.Fail(); 69 | // var folder3 = child.Folders.FirstOrDefault(f => f.Path == folder3Path); 70 | // if (folder3 == default) Assert.Fail(); 71 | // folder3.Files.Any(f => f.Path == file3Path).IsTrue(); 72 | // var folder4 = child.Folders.FirstOrDefault(f => f.Path == folder4Path); 73 | // folder4.Files.Any(f => f.Path == file4Path).IsTrue(); 74 | //} 75 | 76 | //[TestMethod] 77 | //public void Descendants() 78 | //{ 79 | // var root = new RootDirectory { Path = @"root" }; 80 | // var child1 = new RootDirectory { Path = @"child1" }; 81 | // var child2 = new RootDirectory { Path = @"child2" }; 82 | // var son1 = new RootDirectory { Path = @"son1" }; 83 | // var son2 = new RootDirectory { Path = @"son2" }; 84 | // var son3 = new RootDirectory { Path = @"son3" }; 85 | // root.Children = new[] { child1, child2 }; 86 | // child1.Children = new[] { son1, son2 }; 87 | // child2.Children = new[] { son3 }; 88 | // AssertUtil.ArePropertyValueEqualAll(new[] { root, child1, child2, son1, son2, son3 }, root.Descendants().ToArray()); 89 | //} 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /BmsManager.Tests/UtilityTests.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keidrumfreak/BmsManager/5ae694cf2cb4501b94dc82baa87c70488f17a7ec/BmsManager.Tests/UtilityTests.cs -------------------------------------------------------------------------------- /BmsManager.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29403.142 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BmsManager.Tests", "BmsManager.Tests\BmsManager.Tests.csproj", "{FE0C1844-2390-4E64-9A85-5133690DFC40}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BmsManager", "BmsManager\BmsManager.csproj", "{9119139A-3CF6-44B6-93B9-0D61F1E3A1F4}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommonLib.Wpf", "DotNetLibraries\CommonLib\CommonLib.Wpf\CommonLib.Wpf.csproj", "{54DC128A-B203-4A03-B693-0F1F72334F41}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommonLib", "DotNetLibraries\CommonLib\CommonLib\CommonLib.csproj", "{8FD10FD5-66B6-4653-AEC9-D45BED485C59}" 13 | EndProject 14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{8B70D3AF-6EE1-42BF-81C6-19920A328460}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommonLib.TestHelper", "DotNetLibraries\CommonLib\CommonLib.TestHelper\CommonLib.TestHelper.csproj", "{ED727B8D-A8EC-4624-A0A8-60E6246AC031}" 17 | EndProject 18 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A39B8F30-5FE0-4971-BA11-6E4D22167EDB}" 19 | ProjectSection(SolutionItems) = preProject 20 | .editorconfig = .editorconfig 21 | EndProjectSection 22 | EndProject 23 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BmsParser", "BmsParser\BmsParser\BmsParser.csproj", "{11325C42-C5B5-42E0-BCE3-0F78B0F9833E}" 24 | EndProject 25 | Global 26 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 27 | Debug|Any CPU = Debug|Any CPU 28 | Release|Any CPU = Release|Any CPU 29 | EndGlobalSection 30 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 31 | {FE0C1844-2390-4E64-9A85-5133690DFC40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {FE0C1844-2390-4E64-9A85-5133690DFC40}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {FE0C1844-2390-4E64-9A85-5133690DFC40}.Release|Any CPU.ActiveCfg = Release|Any CPU 34 | {FE0C1844-2390-4E64-9A85-5133690DFC40}.Release|Any CPU.Build.0 = Release|Any CPU 35 | {9119139A-3CF6-44B6-93B9-0D61F1E3A1F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 36 | {9119139A-3CF6-44B6-93B9-0D61F1E3A1F4}.Debug|Any CPU.Build.0 = Debug|Any CPU 37 | {9119139A-3CF6-44B6-93B9-0D61F1E3A1F4}.Release|Any CPU.ActiveCfg = Release|Any CPU 38 | {9119139A-3CF6-44B6-93B9-0D61F1E3A1F4}.Release|Any CPU.Build.0 = Release|Any CPU 39 | {54DC128A-B203-4A03-B693-0F1F72334F41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 40 | {54DC128A-B203-4A03-B693-0F1F72334F41}.Debug|Any CPU.Build.0 = Debug|Any CPU 41 | {54DC128A-B203-4A03-B693-0F1F72334F41}.Release|Any CPU.ActiveCfg = Release|Any CPU 42 | {54DC128A-B203-4A03-B693-0F1F72334F41}.Release|Any CPU.Build.0 = Release|Any CPU 43 | {8FD10FD5-66B6-4653-AEC9-D45BED485C59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 44 | {8FD10FD5-66B6-4653-AEC9-D45BED485C59}.Debug|Any CPU.Build.0 = Debug|Any CPU 45 | {8FD10FD5-66B6-4653-AEC9-D45BED485C59}.Release|Any CPU.ActiveCfg = Release|Any CPU 46 | {8FD10FD5-66B6-4653-AEC9-D45BED485C59}.Release|Any CPU.Build.0 = Release|Any CPU 47 | {ED727B8D-A8EC-4624-A0A8-60E6246AC031}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 48 | {ED727B8D-A8EC-4624-A0A8-60E6246AC031}.Debug|Any CPU.Build.0 = Debug|Any CPU 49 | {ED727B8D-A8EC-4624-A0A8-60E6246AC031}.Release|Any CPU.ActiveCfg = Release|Any CPU 50 | {ED727B8D-A8EC-4624-A0A8-60E6246AC031}.Release|Any CPU.Build.0 = Release|Any CPU 51 | {11325C42-C5B5-42E0-BCE3-0F78B0F9833E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 52 | {11325C42-C5B5-42E0-BCE3-0F78B0F9833E}.Debug|Any CPU.Build.0 = Debug|Any CPU 53 | {11325C42-C5B5-42E0-BCE3-0F78B0F9833E}.Release|Any CPU.ActiveCfg = Release|Any CPU 54 | {11325C42-C5B5-42E0-BCE3-0F78B0F9833E}.Release|Any CPU.Build.0 = Release|Any CPU 55 | EndGlobalSection 56 | GlobalSection(SolutionProperties) = preSolution 57 | HideSolutionNode = FALSE 58 | EndGlobalSection 59 | GlobalSection(NestedProjects) = preSolution 60 | {FE0C1844-2390-4E64-9A85-5133690DFC40} = {8B70D3AF-6EE1-42BF-81C6-19920A328460} 61 | {ED727B8D-A8EC-4624-A0A8-60E6246AC031} = {8B70D3AF-6EE1-42BF-81C6-19920A328460} 62 | EndGlobalSection 63 | GlobalSection(ExtensibilityGlobals) = postSolution 64 | SolutionGuid = {A56D43EC-0AD5-4E22-9C20-C97AB85883F9} 65 | EndGlobalSection 66 | EndGlobal 67 | -------------------------------------------------------------------------------- /BmsManager/App.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /BmsManager/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Threading.Tasks; 4 | using System.Windows; 5 | 6 | namespace BmsManager 7 | { 8 | /// 9 | /// Interaction logic for App.xaml 10 | /// 11 | public partial class App : Application 12 | { 13 | public App() 14 | { 15 | // 未処理例外の処理 16 | // UI スレッドで実行されているコードで処理されなかったら発生する(.NET 3.0 より) 17 | DispatcherUnhandledException += App_DispatcherUnhandledException; 18 | // バックグラウンドタスク内で処理されなかったら発生する(.NET 4.0 より) 19 | TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; 20 | // 例外が処理されなかったら発生する(.NET 1.0 より) 21 | AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; 22 | } 23 | 24 | private void Application_Startup(object sender, StartupEventArgs e) 25 | { 26 | Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); 27 | } 28 | 29 | /// 30 | /// UI スレッドで発生した未処理例外を処理します。 31 | /// 32 | /// 33 | /// 34 | private void App_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e) 35 | { 36 | var exception = e.Exception as Exception; 37 | if (ConfirmUnhandledException(exception, "UI スレッド")) 38 | { 39 | e.Handled = true; 40 | } 41 | else 42 | { 43 | Environment.Exit(1); 44 | } 45 | } 46 | 47 | /// 48 | /// バックグラウンドタスクで発生した未処理例外を処理します。 49 | /// 50 | /// 51 | /// 52 | private void TaskScheduler_UnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e) 53 | { 54 | var exception = e.Exception.InnerException; 55 | if (exception != null && ConfirmUnhandledException(exception, "バックグラウンドタスク")) 56 | { 57 | e.SetObserved(); 58 | } 59 | else 60 | { 61 | Environment.Exit(1); 62 | } 63 | } 64 | 65 | /// 66 | /// 実行を継続するかどうかを選択できる場合の未処理例外を処理します。 67 | /// 68 | /// 例外オブジェクト 69 | /// 発生したスレッドの種別を示す文字列 70 | /// 継続することが選択された場合は true, それ以外は false 71 | static bool ConfirmUnhandledException(Exception e, string sourceName) 72 | { 73 | var message = $"予期せぬエラーが発生しました。続けて発生する場合は開発者に報告してください。\nプログラムの実行を継続しますか?"; 74 | if (e != null) message += $"\n({e.Message} @ {e.TargetSite?.Name})"; 75 | SystemProvider.Logger.TraceExceptionLog(e); 76 | var result = MessageBox.Show(message, $"未処理例外 ({sourceName})", MessageBoxButton.YesNo, MessageBoxImage.Warning); 77 | return result == MessageBoxResult.Yes; 78 | } 79 | 80 | /// 81 | /// 最終的に処理されなかった未処理例外を処理します。 82 | /// 83 | /// 84 | /// 85 | private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) 86 | { 87 | var exception = e.ExceptionObject as Exception; 88 | var message = $"予期せぬエラーが発生しました。続けて発生する場合は開発者に報告してください。"; 89 | if (exception != null) message += $"\n({exception.Message} @ {exception.TargetSite?.Name})"; 90 | SystemProvider.Logger.TraceExceptionLog(exception); 91 | MessageBox.Show(message, "未処理例外", MessageBoxButton.OK, MessageBoxImage.Stop); 92 | Environment.Exit(1); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /BmsManager/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using System.Windows; 3 | 4 | [assembly: ThemeInfo( 5 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 6 | //(used if a resource is not found in the page, 7 | // or application resource dictionaries) 8 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 9 | //(used if a resource is not found in the page, 10 | // app, or any theme specific resource dictionaries) 11 | )] 12 | 13 | [assembly: InternalsVisibleTo("BmsManager.Tests")] 14 | -------------------------------------------------------------------------------- /BmsManager/Beatoraja/BeatorajaFolder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.ComponentModel.DataAnnotations.Schema; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using BmsManager.Entity; 9 | using SysPath = System.IO.Path; 10 | 11 | namespace BmsManager.Beatoraja 12 | { 13 | [Table("folder")] 14 | class BeatorajaFolder 15 | { 16 | [NotMapped] 17 | public static readonly string RootCrc = "e2977170"; 18 | 19 | [Column("path"), Key] 20 | public string Path { get; set; } 21 | 22 | [Column("title")] 23 | public string Title { get; set; } 24 | 25 | [Column("subtitle")] 26 | public string Subtitle { get; set; } 27 | 28 | [Column("command")] 29 | public string Command { get; set; } 30 | 31 | [Column("type")] 32 | public int? Type { get; set; } 33 | 34 | [Column("banner")] 35 | public string Text { get; set; } 36 | 37 | [Column("parent")] 38 | public string Parent { get; set; } 39 | 40 | [Column("date")] 41 | public int? Date { get; set; } 42 | 43 | [Column("max")] 44 | public int? Max { get; set; } 45 | 46 | [Column("adddate")] 47 | public int? AddDate { get; set; } 48 | 49 | public BeatorajaFolder() { } 50 | 51 | public BeatorajaFolder(BmsFolder folder) 52 | { 53 | Title = SysPath.GetFileName(folder.Path); 54 | Path = folder.Path; 55 | Parent = Utility.GetCrc32(folder.Root.Path); 56 | Date = folder.FolderUpdateDate.ToUnixMilliseconds(); 57 | } 58 | 59 | public BeatorajaFolder(RootDirectory root) 60 | { 61 | Title = SysPath.GetFileName(root.Path); 62 | Path = root.Path; 63 | Parent = root.Parent == null ? RootCrc : Utility.GetCrc32(root.Parent.Path); 64 | Date = root.FolderUpdateDate.ToUnixMilliseconds(); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /BmsManager/Beatoraja/BeatorajaInformation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.ComponentModel.DataAnnotations.Schema; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using BmsManager.Entity; 9 | using BmsParser; 10 | 11 | namespace BmsManager.Beatoraja 12 | { 13 | [Table("information")] 14 | class BeatorajaInformation 15 | { 16 | [Column("sha256"), Key] 17 | public string Sha256 { get; set; } 18 | 19 | [Column("n")] 20 | public int? N { get; set; } 21 | 22 | [Column("ln")] 23 | public int? LN { get; set; } 24 | 25 | [Column("s")] 26 | public int? S { get; set; } 27 | 28 | [Column("ls")] 29 | public int? LS { get; set; } 30 | 31 | [Column("total")] 32 | public double? Total { get; set; } 33 | 34 | [Column("density")] 35 | public double? Density { get; set; } 36 | 37 | [Column("peakdensity")] 38 | public double? PeakDensity { get; set; } 39 | 40 | [Column("enddensity")] 41 | public double? EndDensity { get; set; } 42 | 43 | [Column("distribution")] 44 | public string Distribution { get; set; } 45 | 46 | [Column("mainbpm")] 47 | public double? MainBpm { get; set; } 48 | 49 | [Column("speedchange")] 50 | public string SpeedChange { get; set; } 51 | 52 | [Column("lanenotes")] 53 | public string LaneNotes { get; set; } 54 | 55 | public BeatorajaInformation() { } 56 | 57 | public BeatorajaInformation(BmsFile file) 58 | { 59 | Sha256 = file.Sha256; 60 | N = file.N; 61 | LN = file.LN; 62 | S = file.S; 63 | LS = file.LS; 64 | Total = file.Total; 65 | Density = file.Density; 66 | PeakDensity = file.PeakDensity; 67 | EndDensity = file.EndDensity; 68 | Distribution = file.Distribution; 69 | MainBpm = file.MainBpm; 70 | SpeedChange = file.SpeedChange; 71 | LaneNotes = file.LaneNotes; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /BmsManager/Beatoraja/BeatorajaSong.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.ComponentModel.DataAnnotations.Schema; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using BmsManager.Entity; 9 | using BmsParser; 10 | using CrSha256 = System.Security.Cryptography.SHA256; 11 | using SysPath = System.IO.Path; 12 | 13 | namespace BmsManager.Beatoraja 14 | { 15 | [Table("song")] 16 | class BeatorajaSong 17 | { 18 | [Column("md5"), Required] 19 | public string MD5 { get; set; } 20 | 21 | [Column("sha256")] 22 | public string Sha256 { get; set; } 23 | 24 | [Column("title")] 25 | public string Title { get; set; } 26 | 27 | [Column("subtitle")] 28 | public string SubTitle { get; set; } 29 | 30 | [Column("genre")] 31 | public string Genre { get; set; } 32 | 33 | [Column("artist")] 34 | public string Artist { get; set; } 35 | 36 | [Column("subartist")] 37 | public string SubArtist { get; set; } 38 | 39 | [Column("tag")] 40 | public string Tag { get; set; } 41 | 42 | [Column("path")] 43 | public string Path { get; set; } 44 | 45 | [Column("folder")] 46 | public string Folder { get; set; } 47 | 48 | [Column("stagefile")] 49 | public string StageFile { get; set; } 50 | 51 | [Column("banner")] 52 | public string Banner { get; set; } 53 | 54 | [Column("backbmp")] 55 | public string BackBmp { get; set; } 56 | 57 | [Column("preview")] 58 | public string Preview { get; set; } 59 | 60 | [Column("parent")] 61 | public string Parent { get; set; } 62 | 63 | [Column("level")] 64 | public int? Level { get; set; } 65 | 66 | [Column("difficulty")] 67 | public int? Difficulty { get; set; } 68 | 69 | [Column("maxbpm")] 70 | public int? MaxBpm { get; set; } 71 | 72 | [Column("minbpm")] 73 | public int? MinBpm { get; set; } 74 | 75 | [Column("mode")] 76 | public int? Mode { get; set; } 77 | 78 | [Column("judge")] 79 | public int? Judge { get; set; } 80 | 81 | [Column("feature")] 82 | public int? Feature { get; set; } 83 | 84 | [Column("content")] 85 | public int? Content { get; set; } 86 | 87 | [Column("date")] 88 | public int? Date { get; set; } 89 | 90 | [Column("favorite")] 91 | public int? Favorite { get; set; } 92 | 93 | [Column("notes")] 94 | public int? Notes { get; set; } 95 | 96 | [Column("adddate")] 97 | public int AddDate { get; set; } 98 | 99 | [Column("charthash")] 100 | public string ChartHash { get; set; } 101 | 102 | [Column("length")] 103 | public int? Length { get; set; } 104 | 105 | public BeatorajaSong() { } 106 | 107 | public BeatorajaSong(BmsFile file) 108 | { 109 | Title = file.Title; 110 | SubTitle = file.Subtitle; 111 | Genre = file.Genre; 112 | Artist = file.Artist; 113 | SubArtist = file.SubArtist; 114 | Path = file.Path; 115 | MD5 = file.MD5; 116 | Sha256 = file.Sha256; 117 | Banner = file.Banner; 118 | StageFile = file.StageFile; 119 | BackBmp = file.BackBmp; 120 | Preview = file.Preview; 121 | if (int.TryParse(file.PlayLevel, out var level)) 122 | { 123 | Level = level; 124 | } 125 | Mode = file.Mode; 126 | Difficulty = (int)file.Difficulty; 127 | Judge = file.Judge; 128 | MinBpm = (int)file.MinBpm; 129 | MaxBpm = (int)file.MaxBpm; 130 | Length = (int)file.Length; 131 | Notes = file.Notes; 132 | Feature = file.Feature; 133 | var content = file.Folder.HasText ? Contents.Text : 0; 134 | content |= file.HasBga ? Contents.Bga : 0; 135 | content |= file.IsNoKeySound ? Contents.NoKeySound : 0; 136 | content |= (!string.IsNullOrEmpty(file.Preview) || !string.IsNullOrEmpty(file.Folder.Preview)) ? Contents.Preview : 0; 137 | Content = (int)content; 138 | 139 | ChartHash = file.ChartHash; 140 | Folder = Utility.GetCrc32(file.Folder.Path); 141 | Parent = Utility.GetCrc32(file.Folder.Root.Path); 142 | 143 | if (Difficulty == 0) 144 | { 145 | var title = (Title + SubTitle).ToUpper(); 146 | Difficulty = title.Contains("BEGINNER") ? 1 147 | : title.Contains("NORMAL") ? 2 148 | : title.Contains("HYPER") ? 3 149 | : title.Contains("ANOTHER") ? 4 150 | : title.Contains("INSANE") ? 5 151 | : Notes < 250 ? 1 152 | : Notes < 600 ? 2 153 | : Notes < 1000 ? 3 154 | : Notes < 2000 ? 4 155 | : 5; 156 | } 157 | 158 | if (string.IsNullOrEmpty(Preview)) 159 | { 160 | Preview = file.Folder.Preview; 161 | } 162 | } 163 | 164 | 165 | [Flags] 166 | enum Contents 167 | { 168 | Text = 1, 169 | Bga = 2, 170 | Preview = 4, 171 | NoKeySound = 128 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /BmsManager/Beatoraja/BeatorajaSongdataContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Microsoft.Data.Sqlite; 7 | using Microsoft.EntityFrameworkCore; 8 | 9 | namespace BmsManager.Beatoraja 10 | { 11 | class BeatorajaSongdataContext : DbContext 12 | { 13 | public virtual DbSet Folders { get; set; } 14 | 15 | public virtual DbSet Songs { get; set; } 16 | 17 | readonly string path; 18 | 19 | public BeatorajaSongdataContext(string path) 20 | { 21 | this.path = path; 22 | Database.EnsureCreated(); 23 | } 24 | 25 | protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 26 | { 27 | optionsBuilder.UseSqlite(new SqliteConnectionStringBuilder 28 | { 29 | Mode = SqliteOpenMode.ReadWriteCreate, 30 | DataSource = path, 31 | Cache = SqliteCacheMode.Shared 32 | }.ToString()); 33 | } 34 | 35 | protected override void OnModelCreating(ModelBuilder modelBuilder) 36 | { 37 | modelBuilder.Entity() 38 | .HasKey(s => new { s.Sha256, s.Path }); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /BmsManager/Beatoraja/BeatorajaSonginfoContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Microsoft.Data.Sqlite; 7 | using Microsoft.EntityFrameworkCore; 8 | 9 | namespace BmsManager.Beatoraja 10 | { 11 | class BeatorajaSonginfoContext : DbContext 12 | { 13 | public virtual DbSet Informations { get; set; } 14 | 15 | string path; 16 | 17 | public BeatorajaSonginfoContext(string path) 18 | { 19 | this.path = path; 20 | Database.EnsureCreated(); 21 | } 22 | 23 | protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 24 | { 25 | optionsBuilder.UseSqlite(new SqliteConnectionStringBuilder 26 | { 27 | Mode = SqliteOpenMode.ReadWriteCreate, 28 | DataSource = path, 29 | Cache = SqliteCacheMode.Shared 30 | }.ToString()); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /BmsManager/BmsManager.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net8.0-windows7.0 6 | true 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | PreserveNewest 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /BmsManager/Data/BmsTableDocument.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Net.Http; 6 | using System.Text; 7 | using System.Text.Json; 8 | using System.Text.Json.Serialization; 9 | using System.Threading.Tasks; 10 | using BmsManager.Entity; 11 | using CommonLib.Net; 12 | using CommonLib.Net.Http; 13 | 14 | namespace BmsManager.Data 15 | { 16 | /// 17 | /// 難易度表 18 | /// 19 | class BmsTableDocument(string uri) : HtmlDocument(uri) 20 | { 21 | BmsTalbeHeader? header; 22 | 23 | IEnumerable? data; 24 | 25 | public string Home { get; } = uri[..uri.LastIndexOf('/')]; 26 | 27 | public override async Task LoadAsync(HttpClient client) 28 | { 29 | await base.LoadAsync(client).ConfigureAwait(false); 30 | var headerContent = Content.Descendants(Namespace + "meta")? 31 | .FirstOrDefault(e => e.Attribute("name")?.Value == "bmstable")? 32 | .Attribute("content")?.Value? 33 | .TrimStart('.', '/'); // "./"から始まっている場合がある 34 | 35 | if (headerContent == null) 36 | { 37 | throw new Exception(); 38 | } 39 | 40 | var headerUri = headerContent.StartsWith("http:") || headerContent.StartsWith("https:") 41 | ? headerContent 42 | : $"{Home}/{headerContent}"; 43 | 44 | var headerJson = await Utility.GetHttpClient().GetStringAsync(headerUri).ConfigureAwait(false); 45 | header = JsonSerializer.Deserialize(headerJson); 46 | 47 | if (header == null) 48 | { 49 | throw new Exception(); 50 | } 51 | 52 | var dataUri = header.DataUrl.StartsWith("http:") || header.DataUrl.StartsWith("https:") 53 | ? header.DataUrl 54 | : $"{Home}/{header.DataUrl.TrimStart('.', '/')}"; 55 | var dataJson = await Utility.GetHttpClient().GetStringAsync(dataUri).ConfigureAwait(false); 56 | data = JsonSerializer.Deserialize(dataJson); 57 | } 58 | 59 | public BmsTable ToEntity() 60 | { 61 | if (header == null || data == null) 62 | throw new InvalidOperationException(); 63 | 64 | return new BmsTable 65 | { 66 | Name = header.Name, 67 | Url = Uri.AbsoluteUri, 68 | Symbol = header.Symbol, 69 | Tag = header.Tag, 70 | Difficulties = data.GroupBy(d => d.Level).Select(d => new BmsTableDifficulty 71 | { 72 | Difficulty = d.Key, 73 | DifficultyOrder = header.LevelOrder != null && header.LevelOrder.Length > 0 74 | ? Array.IndexOf(header.LevelOrder, d.Key) + 1 75 | : int.TryParse(d.Key, out var index) ? index : null, 76 | TableDatas = d.Select(d => new Entity.BmsTableData 77 | { 78 | MD5 = d.MD5, 79 | LR2BmsID = d.LR2BmsID, 80 | Title = d.Title, 81 | Artist = d.Artist, 82 | Url = d.Url, 83 | DiffUrl = d.UrlDiff, 84 | DiffName = d.NameDiff, 85 | PackUrl = d.UrlPack, 86 | PackName = d.NamePack, 87 | Comment = d.Comment, 88 | OrgMD5 = d.OrgMD5 89 | }).ToList() 90 | }).OrderBy(d => d.DifficultyOrder).ToList() 91 | }; 92 | } 93 | 94 | public class BmsTalbeHeader 95 | { 96 | [JsonPropertyName("name")] 97 | public required string Name { get; set; } 98 | 99 | [JsonPropertyName("data_url")] 100 | public required string DataUrl { get; set; } 101 | 102 | [JsonPropertyName("symbol")] 103 | public required string Symbol { get; set; } 104 | 105 | [JsonPropertyName("tag")] 106 | public string? Tag { get; set; } 107 | 108 | [JsonPropertyName("level_order")] 109 | public string[]? LevelOrder { get; set; } 110 | } 111 | 112 | public class BmsTableData 113 | { 114 | [JsonPropertyName("md5")] 115 | public required string MD5 { get; set; } 116 | 117 | [JsonPropertyName("level")] 118 | public JsonElement LevelValue { get; set; } 119 | 120 | string? level; 121 | [JsonIgnore] 122 | public string Level => level ??= (LevelValue.ValueKind == JsonValueKind.String ? LevelValue.GetString() : LevelValue.GetInt32().ToString()) ?? throw new Exception(); 123 | 124 | [JsonPropertyName("lr2_bmsid")] 125 | public JsonElement? LR2BmsIDValue { get; set; } 126 | 127 | string? lr2BmsID; 128 | [JsonIgnore] 129 | public string? LR2BmsID 130 | { 131 | get 132 | { 133 | if (lr2BmsID != null) return lr2BmsID; 134 | if (!LR2BmsIDValue.HasValue) return null; 135 | return lr2BmsID = LR2BmsIDValue?.ValueKind == JsonValueKind.String ? LR2BmsIDValue.Value.GetString() : LR2BmsIDValue?.GetInt32().ToString(); 136 | } 137 | } 138 | 139 | [JsonPropertyName("title")] 140 | public string? Title { get; set; } 141 | 142 | [JsonPropertyName("artist")] 143 | public string? Artist { get; set; } 144 | 145 | [JsonPropertyName("url")] 146 | public string? Url { get; set; } 147 | 148 | [JsonPropertyName("url_diff")] 149 | public string? UrlDiff { get; set; } 150 | 151 | [JsonPropertyName("name_diff")] 152 | public string? NameDiff { get; set; } 153 | 154 | [JsonPropertyName("url_pack")] 155 | public string? UrlPack { get; set; } 156 | 157 | [JsonPropertyName("name_pack")] 158 | public string? NamePack { get; set; } 159 | 160 | [JsonPropertyName("comment")] 161 | public string? Comment { get; set; } 162 | 163 | [JsonPropertyName("org_md5")] 164 | public string? OrgMD5 { get; set; } 165 | } 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /BmsManager/Entity/BmsFile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.ComponentModel.DataAnnotations.Schema; 4 | 5 | namespace BmsManager.Entity 6 | { 7 | [Table("BmsFile")] 8 | class BmsFile 9 | { 10 | [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] 11 | public int ID { get; set; } 12 | 13 | public int FolderID { get; set; } 14 | 15 | public required string Path { get; set; } 16 | 17 | public required string Title { get; set; } 18 | 19 | public required string Subtitle { get; set; } 20 | 21 | public required string Genre { get; set; } 22 | 23 | public required string Artist { get; set; } 24 | 25 | public required string SubArtist { get; set; } 26 | 27 | public required string MD5 { get; set; } 28 | 29 | public required string Sha256 { get; set; } 30 | 31 | public required string Banner { get; set; } 32 | 33 | public required string StageFile { get; set; } 34 | 35 | public required string BackBmp { get; set; } 36 | 37 | public required string Preview { get; set; } 38 | 39 | public required string PlayLevel { get; set; } 40 | 41 | public int Mode { get; set; } 42 | 43 | public int Difficulty { get; set; } 44 | 45 | public int Judge { get; set; } 46 | 47 | public double MinBpm { get; set; } 48 | 49 | public double MaxBpm { get; set; } 50 | 51 | public int Length { get; set; } 52 | 53 | public int Notes { get; set; } 54 | 55 | public int Feature { get; set; } 56 | 57 | public bool HasBga { get; set; } 58 | 59 | public bool IsNoKeySound { get; set; } 60 | 61 | public required string ChartHash { get; set; } 62 | 63 | public int N { get; set; } 64 | 65 | public int LN { get; set; } 66 | 67 | public int S { get; set; } 68 | 69 | public int LS { get; set; } 70 | 71 | public double Total { get; set; } 72 | 73 | public double Density { get; set; } 74 | 75 | public double PeakDensity { get; set; } 76 | 77 | public double EndDensity { get; set; } 78 | 79 | public required string Distribution { get; set; } 80 | 81 | public double MainBpm { get; set; } 82 | 83 | public required string SpeedChange { get; set; } 84 | 85 | public required string LaneNotes { get; set; } 86 | 87 | BmsFolder? folder; 88 | [ForeignKey(nameof(FolderID))] 89 | public virtual BmsFolder Folder 90 | { 91 | get => folder ?? throw new InvalidOperationException(); 92 | set => folder = value; 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /BmsManager/Entity/BmsFolder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.ComponentModel.DataAnnotations.Schema; 5 | 6 | namespace BmsManager.Entity 7 | { 8 | [Table("BmsFolder")] 9 | class BmsFolder 10 | { 11 | [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] 12 | public int ID { get; set; } 13 | 14 | public int RootID { get; set; } 15 | 16 | public required string Path { get; set; } 17 | 18 | public string? Artist { get; set; } 19 | 20 | public string? Title { get; set; } 21 | 22 | public bool HasText { get; set; } 23 | 24 | public string? Preview { get; set; } 25 | 26 | public DateTime FolderUpdateDate { get; set; } 27 | 28 | [InverseProperty(nameof(BmsFile.Folder))] 29 | public virtual ICollection Files { get; set; } = []; 30 | 31 | RootDirectory? root; 32 | [ForeignKey(nameof(RootID))] 33 | public virtual RootDirectory Root 34 | { 35 | get => root ?? throw new InvalidOperationException(); 36 | set => root = value; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /BmsManager/Entity/BmsManagerContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data; 3 | using System.Data.Common; 4 | using System.Diagnostics; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | using BmsManager.Beatoraja; 9 | using BmsManager.Entity; 10 | using BmsParser; 11 | using CommonLib.IO; 12 | using Microsoft.Data.Sqlite; 13 | using Microsoft.EntityFrameworkCore; 14 | using Microsoft.Extensions.Logging; 15 | using Microsoft.Extensions.Logging.Debug; 16 | 17 | namespace BmsManager.Entity 18 | { 19 | class BmsManagerContext : DbContext 20 | { 21 | public virtual DbSet Files { get; set; } 22 | public virtual DbSet BmsFolders { get; set; } 23 | public virtual DbSet RootDirectories { get; set; } 24 | 25 | public virtual DbSet Tables { get; set; } 26 | public virtual DbSet Difficulties { get; set; } 27 | public virtual DbSet TableDatas { get; set; } 28 | 29 | public BmsManagerContext() : base() 30 | { 31 | if (Settings.Default.DatabaseKind == "SQLite") 32 | { 33 | Database.EnsureCreated(); 34 | } 35 | } 36 | 37 | public static readonly LoggerFactory LoggerFactory = new([new DebugLoggerProvider()]); 38 | 39 | protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 40 | { 41 | switch (Settings.Default.DatabaseKind) 42 | { 43 | case "SQLServer": 44 | optionsBuilder.UseSqlServer(Settings.Default.BmsManagerConnectionStrings) 45 | .EnableSensitiveDataLogging() 46 | .UseLoggerFactory(LoggerFactory); 47 | break; 48 | case "SQLite": 49 | var path = PathUtil.Combine(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule?.FileName), "bms.db"); 50 | optionsBuilder.UseSqlite(new SqliteConnectionStringBuilder 51 | { 52 | Mode = SqliteOpenMode.ReadWriteCreate, 53 | DataSource = path, 54 | Cache = SqliteCacheMode.Shared 55 | }.ToString()); 56 | break; 57 | } 58 | } 59 | 60 | public async Task ExportToBeatoragjaAsync(string songDB, string songInfoDB) 61 | { 62 | var now = DateTime.Now; 63 | var files = Files.Include(f => f.Folder) 64 | .ThenInclude(f => f.Root).AsNoTracking().ToArray(); 65 | using (var con = new BeatorajaSongdataContext(songDB)) 66 | { 67 | var mnFol = BmsFolders.Include(r => r.Root).AsNoTracking().ToArray().Select(r => new BeatorajaFolder(r)) 68 | .Concat(RootDirectories.Include(r => r.Parent).AsNoTracking().ToArray().Select(r => new BeatorajaFolder(r))); 69 | var boFol = con.Folders.ToArray(); 70 | var delFolTask = Task.Run(() => 71 | { 72 | var delFol = boFol.AsParallel().Where(bo => !mnFol.Any(mn => mn.Path == bo.Path)).ToArray(); 73 | if (delFol.Length != 0) 74 | con.Folders.RemoveRange(delFol); 75 | }); 76 | 77 | var regFolTask = Task.Run(() => 78 | { 79 | foreach (var folder in mnFol) 80 | { 81 | var entity = boFol.FirstOrDefault(bo => bo.Path == folder.Path); 82 | if (entity == default) 83 | { 84 | folder.AddDate = now.ToUnixMilliseconds(); 85 | con.Folders.Add(folder); 86 | } 87 | else 88 | { 89 | if (entity.Date != folder.Date) 90 | { 91 | entity.Date = folder.Date; 92 | } 93 | } 94 | } 95 | }); 96 | 97 | var songs = con.Songs.ToArray(); 98 | var delSongTask = Task.Run(() => 99 | { 100 | var delSong = songs.AsParallel().Where(s => !files.Any(f => f.Path == s.Path)).ToArray(); 101 | if (delSong.Length != 0) 102 | con.Songs.RemoveRange(delSong); 103 | }); 104 | 105 | var regSongTask = Task.Run(() => 106 | { 107 | foreach (var file in files) 108 | { 109 | var song = songs.FirstOrDefault(s => s.Path == file.Path); 110 | if (song == default) 111 | { 112 | con.Songs.Add(new BeatorajaSong(file) { AddDate = now.ToUnixMilliseconds() }); 113 | } 114 | else 115 | { 116 | // 同一なら変更無しのはず 117 | if (file.Sha256 != song.Sha256) 118 | { 119 | // UPDATEが面倒なのでDELETE-INSERT 120 | con.Songs.Remove(song); 121 | con.SaveChanges(); 122 | con.Songs.Add(new BeatorajaSong(file) { AddDate = now.ToUnixMilliseconds() }); 123 | con.SaveChanges(); 124 | } 125 | } 126 | } 127 | }); 128 | 129 | await Task.WhenAll(delFolTask, delSongTask, regFolTask, regSongTask); 130 | 131 | con.SaveChanges(); 132 | } 133 | 134 | using (var con = new BeatorajaSonginfoContext(songInfoDB)) 135 | { 136 | con.ChangeTracker.AutoDetectChangesEnabled = false; 137 | var distinctFile = files.GroupBy(f => f.Sha256).Select(f => f.First()); 138 | var infos = con.Informations.AsNoTracking().ToArray(); 139 | foreach (var file in distinctFile.AsParallel().Where(f => !infos.Any(i => f.Sha256 == i.Sha256))) 140 | { 141 | con.Informations.Add(new BeatorajaInformation(file)); 142 | } 143 | con.SaveChanges(); 144 | } 145 | } 146 | } 147 | 148 | static class DBExtensions 149 | { 150 | public static void AddParameter(this DbCommand cmd, string name, object value, DbType type) 151 | { 152 | var param = cmd.CreateParameter(); 153 | param.ParameterName = name; 154 | param.Value = value; 155 | param.DbType = type; 156 | cmd.Parameters.Add(param); 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /BmsManager/Entity/BmsTable.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.ComponentModel.DataAnnotations.Schema; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.EntityFrameworkCore; 7 | 8 | namespace BmsManager.Entity 9 | { 10 | [Table("BmsTable")] 11 | class BmsTable 12 | { 13 | [Key] 14 | public int ID { get; set; } 15 | 16 | public required string Url { get; set; } 17 | 18 | public required string Name { get; set; } 19 | 20 | public required string Symbol { get; set; } 21 | 22 | public string? Tag { get; set; } 23 | 24 | [InverseProperty(nameof(BmsTableDifficulty.Table))] 25 | public virtual ICollection Difficulties { get; set; } = []; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /BmsManager/Entity/BmsTableData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.ComponentModel.DataAnnotations.Schema; 4 | 5 | namespace BmsManager.Entity 6 | { 7 | [Table("BmsTableData")] 8 | class BmsTableData 9 | { 10 | [Key] 11 | public int ID { get; set; } 12 | 13 | public int BmsTableDifficultyID { get; set; } 14 | 15 | public string? MD5 { get; set; } 16 | 17 | public string? LR2BmsID { get; set; } 18 | 19 | public string? Title { get; set; } 20 | 21 | public string? Artist { get; set; } 22 | 23 | /// 24 | /// 本体URL 25 | /// 26 | public string? Url { get; set; } 27 | 28 | /// 29 | /// 差分URL 30 | /// 31 | public string? DiffUrl { get; set; } 32 | 33 | public string? DiffName { get; set; } 34 | 35 | public string? PackUrl { get; set; } 36 | 37 | public string? PackName { get; set; } 38 | 39 | public string? Comment { get; set; } 40 | 41 | public string? OrgMD5 { get; set; } 42 | 43 | BmsTableDifficulty? difficulty; 44 | [ForeignKey(nameof(BmsTableDifficultyID))] 45 | public virtual BmsTableDifficulty Difficulty 46 | { 47 | get => difficulty ?? throw new InvalidOperationException(); 48 | set => difficulty = value; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /BmsManager/Entity/BmsTableDifficulty.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.ComponentModel.DataAnnotations.Schema; 5 | 6 | namespace BmsManager.Entity 7 | { 8 | [Table("BmsTableDifficulty")] 9 | class BmsTableDifficulty 10 | { 11 | [Key] 12 | public int ID { get; set; } 13 | 14 | public int BmsTableID { get; set; } 15 | 16 | public required string Difficulty { get; set; } 17 | 18 | public int? DifficultyOrder { get; set; } 19 | 20 | [InverseProperty(nameof(BmsTableData.Difficulty))] 21 | public virtual ICollection TableDatas { get; set; } = []; 22 | 23 | BmsTable? table; 24 | [ForeignKey(nameof(BmsTableID))] 25 | public virtual BmsTable Table 26 | { 27 | get => table ?? throw new InvalidOperationException(); 28 | set => table = value; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /BmsManager/Entity/RootDirectory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.ComponentModel.DataAnnotations.Schema; 5 | 6 | namespace BmsManager.Entity 7 | { 8 | [Table("RootDirectory")] 9 | class RootDirectory 10 | { 11 | [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] 12 | public int ID { get; set; } 13 | 14 | public required string Path { get; set; } 15 | 16 | public int? ParentRootID { get; set; } 17 | 18 | public DateTime FolderUpdateDate { get; set; } 19 | 20 | [InverseProperty(nameof(BmsFolder.Root))] 21 | public virtual ICollection Folders { get; set; } = []; 22 | 23 | [ForeignKey(nameof(ParentRootID))] 24 | public virtual RootDirectory? Parent { get; set; } 25 | 26 | [InverseProperty(nameof(Parent))] 27 | public virtual ICollection Children { get; set; } = []; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /BmsManager/MainWindow.xaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 11 | 12 | 13 | 14 |