diff --git a/.github/workflows/aquamai.yaml b/.github/workflows/aquamai.yaml deleted file mode 100644 index 93836c47..00000000 --- a/.github/workflows/aquamai.yaml +++ /dev/null @@ -1,72 +0,0 @@ -name: AquaMai Build - -on: - workflow_dispatch: - push: - paths: - - AquaMai/** - branches: - - v1-dev - pull_request_target: - paths: - - AquaMai/** - branches: - - v1-dev - -jobs: - build: - runs-on: windows-latest - steps: - - name: Fix Git line encoding bug - run: | - git config --global core.autocrlf false - git config --global core.eol lf - - - uses: actions/checkout@v4 - - - name: Checkout Assets - uses: clansty/checkout@main - with: - repository: MewoLab/AquaMai-Build-Assets - ssh-key: ${{ secrets.BUILD_ASSETS_KEY }} - path: build-assets - max-attempts: 50 - min-retry-interval: 1 - max-retry-interval: 5 - - - name: Build AquaMai - shell: cmd - run: | - copy /y build-assets\SDEZ\* AquaMai\Libs - cd AquaMai - dotnet build -c Release /p:DefineConstants="CI" - - - name: Prepare artifact - shell: cmd - run: | - cd AquaMai\Output - mkdir Upload - move AquaMai.dll Upload - move AquaMai.*.toml Upload - - - uses: actions/upload-artifact@v4 - with: - name: AquaMai - path: AquaMai\Output\Upload - - - name: Send to Telegram - if: github.event_name != 'pull_request_target' - run: | - $Uri = "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMediaGroup" - $Form = @{ - chat_id = "-1002231087502" - media = @( - @{ type = "document"; media = "attach://aquamai_main"; caption = "${{ github.event.commits[0].message }}" }, - @{ type = "document"; media = "attach://aquamai_zh" } - @{ type = "document"; media = "attach://aquamai_en" } - ) | ConvertTo-Json - aquamai_main = Get-Item AquaMai\Output\Upload\AquaMai.dll - aquamai_zh = Get-Item AquaMai\Output\Upload\AquaMai.zh.toml - aquamai_en = Get-Item AquaMai\Output\Upload\AquaMai.en.toml - } - Invoke-RestMethod -Uri $uri -Form $Form -Method Post diff --git a/AquaMai/.gitignore b/AquaMai/.gitignore deleted file mode 100644 index df21fd82..00000000 --- a/AquaMai/.gitignore +++ /dev/null @@ -1,374 +0,0 @@ - -# Created by https://www.toptal.com/developers/gitignore/api/git,visualstudio -# Edit at https://www.toptal.com/developers/gitignore?templates=git,visualstudio - -### Git ### -# Created by git for backups. To disable backups in Git: -# $ git config --global mergetool.keepBackup false -*.orig - -# Created by git when using merge tools for conflicts -*.BACKUP.* -*.BASE.* -*.LOCAL.* -*.REMOTE.* -*_BACKUP_*.txt -*_BASE_*.txt -*_LOCAL_*.txt -*_REMOTE_*.txt - -### VisualStudio ### -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. -## -## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore - -# User-specific files -*.rsuser -*.suo -*.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Mono auto generated files -mono_crash.* - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -[Aa][Rr][Mm]/ -[Aa][Rr][Mm]64/ -bld/ -[Bb]in/ -[Oo]bj/ -[Ll]og/ -[Ll]ogs/ - -# Visual Studio 2015/2017 cache/options directory -.vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ - -# Visual Studio 2017 auto generated files -Generated\ Files/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUnit -*.VisualState.xml -TestResult.xml -nunit-*.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -# Benchmark Results -BenchmarkDotNet.Artifacts/ - -# .NET Core -project.lock.json -project.fragment.lock.json -artifacts/ - -# StyleCop -StyleCopReport.xml - -# Files built by Visual Studio -*_i.c -*_p.c -*_h.h -*.ilk -*.meta -*.obj -*.iobj -*.pch -*.pdb -*.ipdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*_wpftmp.csproj -*.log -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db -*.VC.VC.opendb - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# Visual Studio Trace Files -*.e2e - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# AxoCover is a Code Coverage Tool -.axoCover/* -!.axoCover/settings.json - -# Coverlet is a free, cross platform Code Coverage Tool -coverage*[.json, .xml, .info] - -# Visual Studio code coverage results -*.coverage -*.coveragexml - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# Note: Comment the next line if you want to checkin your web deploy settings, -# but database connection strings (with potential passwords) will be unencrypted -*.pubxml -*.publishproj - -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -PublishScripts/ - -# NuGet Packages -*.nupkg -# NuGet Symbol Packages -*.snupkg -# Uncomment if necessary however generally it will be regenerated when needed -#!**/[Pp]ackages/repositories.config -# NuGet v3's project.json files produces more ignorable files -*.nuget.props -*.nuget.targets - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Windows Store app package directories and files -AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt -*.appx -*.appxbundle -*.appxupload - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!?*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.jfm -*.pfx -*.publishsettings -orleans.codegen.cs - -# Including strong name files can present a security risk -# (https://github.com/github/gitignore/pull/2483#issue-259490424) -#*.snk - -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm -ServiceFabricBackup/ -*.rptproj.bak - -# SQL Server files -*.mdf -*.ldf -*.ndf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings -*.rptproj.rsuser -*- [Bb]ackup.rdl -*- [Bb]ackup ([0-9]).rdl -*- [Bb]ackup ([0-9][0-9]).rdl - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat -node_modules/ - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) -*.vbw - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe -paket-files/ - -# FAKE - F# Make -.fake/ - -# CodeRush personal settings -.cr/personal - -# Python Tools for Visual Studio (PTVS) -__pycache__/ -*.pyc - -# Cake - Uncomment if you are using it -# tools/** -# !tools/packages.config - -# Tabs Studio -*.tss - -# Telerik's JustMock configuration file -*.jmconfig - -# BizTalk build output -*.btp.cs -*.btm.cs -*.odx.cs -*.xsd.cs - -# OpenCover UI analysis results -OpenCover/ - -# Azure Stream Analytics local run output -ASALocalRun/ - -# MSBuild Binary and Structured Log -*.binlog - -# NVidia Nsight GPU debugger configuration file -*.nvuser - -# MFractors (Xamarin productivity tool) working folder -.mfractor/ - -# Local History for Visual Studio -.localhistory/ - -# BeatPulse healthcheck temp database -healthchecksdb - -# Backup folder for Package Reference Convert tool in Visual Studio 2017 -MigrationBackup/ - -# Ionide (cross platform F# VS Code tools) working folder -.ionide/ - -# End of https://www.toptal.com/developers/gitignore/api/git,visualstudio - -Output -.idea diff --git a/AquaMai/AquaMai.Build/AquaMai.Build.csproj b/AquaMai/AquaMai.Build/AquaMai.Build.csproj deleted file mode 100644 index ff6b804d..00000000 --- a/AquaMai/AquaMai.Build/AquaMai.Build.csproj +++ /dev/null @@ -1,50 +0,0 @@ - - - - Release - AnyCPU - {4C0C68C3-8B2E-4CA8-A26D-AE87CF2A38A5} - Library - AquaMai.Build - AquaMai.Build - netstandard2.0 - 512 - true - 12 - 414;NU1702 - $(ProjectDir)../Libs/ - $(ProjectDir)../Output/ - false - false - - - - false - None - true - prompt - 4 - true - false - - - - DEBUG - - - - - - - - - $(LibsPath)Mono.Cecil.dll - - - - - - - - - diff --git a/AquaMai/AquaMai.Build/GenerateExampleConfig.cs b/AquaMai/AquaMai.Build/GenerateExampleConfig.cs deleted file mode 100644 index 4e7b0028..00000000 --- a/AquaMai/AquaMai.Build/GenerateExampleConfig.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.IO; -using AquaMai.Config.Interfaces; -using AquaMai.Config.HeadlessLoader; -using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; - -public class GenerateExampleConfig : Task -{ - [Required] - public string DllPath { get; set; } - - [Required] - public string OutputPath { get; set; } - - public override bool Execute() - { - try - { - var configInterface = HeadlessConfigLoader.LoadFromPacked(DllPath); - var config = configInterface.CreateConfig(); - foreach (var lang in (string[]) ["en", "zh"]) - { - var configSerializer = configInterface.CreateConfigSerializer(new IConfigSerializer.Options() - { - Lang = lang, - IncludeBanner = true, - OverrideLocaleValue = true - }); - var example = configSerializer.Serialize(config); - File.WriteAllText(Path.Combine(OutputPath, $"AquaMai.{lang}.toml"), example); - } - - return true; - } - catch (Exception e) - { - Log.LogErrorFromException(e, true); - return false; - } - } -} diff --git a/AquaMai/AquaMai.Build/PostBuildPatch.cs b/AquaMai/AquaMai.Build/PostBuildPatch.cs deleted file mode 100644 index f2bb2a0a..00000000 --- a/AquaMai/AquaMai.Build/PostBuildPatch.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.IO; -using System.IO.Compression; -using System.Linq; -using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; -using Mono.Cecil; - -public class PostBuildPatch : Task -{ - [Required] - public string DllPath { get; set; } - - public override bool Execute() - { - try - { - var assembly = AssemblyDefinition.ReadAssembly(new MemoryStream(File.ReadAllBytes(DllPath))); - CompressEmbeddedAssemblies(assembly); - var outputStream = new MemoryStream(); - assembly.Write(outputStream); - File.WriteAllBytes(DllPath, outputStream.ToArray()); - return true; - } - catch (Exception e) - { - Log.LogErrorFromException(e, true); - return false; - } - } - - private void CompressEmbeddedAssemblies(AssemblyDefinition assembly) - { - foreach (var resource in assembly.MainModule.Resources.ToList()) - { - if (resource.Name.EndsWith(".dll") && resource is EmbeddedResource embeddedResource) - { - using var compressedStream = new MemoryStream(); - using (var deflateStream = new DeflateStream(compressedStream, CompressionLevel.Optimal)) - { - embeddedResource.GetResourceStream().CopyTo(deflateStream); - } - var compressedBytes = compressedStream.ToArray(); - - Log.LogMessage($"Compressed {resource.Name} from {embeddedResource.GetResourceStream().Length} to {compressedBytes.Length} bytes"); - - assembly.MainModule.Resources.Remove(resource); - assembly.MainModule.Resources.Add(new EmbeddedResource(resource.Name + ".compressed", resource.Attributes, compressedBytes)); - } - } - } -} diff --git a/AquaMai/AquaMai.Config.HeadlessLoader/AquaMai.Config.HeadlessLoader.csproj b/AquaMai/AquaMai.Config.HeadlessLoader/AquaMai.Config.HeadlessLoader.csproj deleted file mode 100644 index e23c3ba3..00000000 --- a/AquaMai/AquaMai.Config.HeadlessLoader/AquaMai.Config.HeadlessLoader.csproj +++ /dev/null @@ -1,45 +0,0 @@ - - - - Release - AnyCPU - {6B5E1F3E-D012-4CFB-A2FA-26A6CE06BE66} - Library - AquaMai.Config.HeadlessLoader - AquaMai.Config.HeadlessLoader - netstandard2.0 - 512 - true - 12 - 414;NU1702 - $(ProjectDir)../Libs/ - $(ProjectDir)../Output/ - false - false - - - - false - None - true - prompt - 4 - true - false - - - - DEBUG - - - - - - - - - $(LibsPath)Mono.Cecil.dll - - - - diff --git a/AquaMai/AquaMai.Config.HeadlessLoader/ConfigAssemblyLoader.cs b/AquaMai/AquaMai.Config.HeadlessLoader/ConfigAssemblyLoader.cs deleted file mode 100644 index 22e1f96f..00000000 --- a/AquaMai/AquaMai.Config.HeadlessLoader/ConfigAssemblyLoader.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using Mono.Cecil; - -namespace AquaMai.Config.HeadlessLoader; - -class ConfigAssemblyLoader -{ - public static Assembly LoadConfigAssembly(AssemblyDefinition assembly) - { - var references = assembly.MainModule.AssemblyReferences; - foreach (var reference in references) - { - if (reference.Name == "mscorlib" || reference.Name == "System" || reference.Name.StartsWith("System.")) - { - reference.Name = "netstandard"; - reference.Version = new Version(2, 0, 0, 0); - reference.PublicKeyToken = null; - } - } - - var targetFrameworkAttribute = assembly.CustomAttributes.FirstOrDefault(attr => attr.AttributeType.Name == "TargetFrameworkAttribute"); - if (targetFrameworkAttribute != null) - { - targetFrameworkAttribute.ConstructorArguments.Clear(); - targetFrameworkAttribute.ConstructorArguments.Add(new CustomAttributeArgument( - assembly.MainModule.TypeSystem.String, ".NETStandard,Version=v2.0")); - targetFrameworkAttribute.Properties.Clear(); - targetFrameworkAttribute.Properties.Add(new Mono.Cecil.CustomAttributeNamedArgument( - "FrameworkDisplayName", new CustomAttributeArgument(assembly.MainModule.TypeSystem.String, ".NET Standard 2.0"))); - } - - var stream = new MemoryStream(); - assembly.Write(stream); - FixLoadedAssemblyResolution(); - return AppDomain.CurrentDomain.Load(stream.ToArray()); - } - - private static bool FixedLoadedAssemblyResolution = false; - - // XXX: Why, without this, the already loaded assemblies are not resolved? - public static void FixLoadedAssemblyResolution() - { - if (FixedLoadedAssemblyResolution) - { - return; - } - FixedLoadedAssemblyResolution = true; - - var loadedAssemblies = new Dictionary(); - foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) - { - loadedAssemblies[assembly.FullName] = assembly; - } - - AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => - { - if (loadedAssemblies.TryGetValue(args.Name, out var assembly)) - { - return assembly; - } - return null; - }; - } -} diff --git a/AquaMai/AquaMai.Config.HeadlessLoader/CustomAssemblyResolver.cs b/AquaMai/AquaMai.Config.HeadlessLoader/CustomAssemblyResolver.cs deleted file mode 100644 index 4bf96750..00000000 --- a/AquaMai/AquaMai.Config.HeadlessLoader/CustomAssemblyResolver.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Mono.Cecil; - -namespace AquaMai.Config.HeadlessLoader; - -public class CustomAssemblyResolver : DefaultAssemblyResolver -{ - public new void RegisterAssembly(AssemblyDefinition assembly) - { - base.RegisterAssembly(assembly); - } -} diff --git a/AquaMai/AquaMai.Config.HeadlessLoader/HeadlessConfigInterface.cs b/AquaMai/AquaMai.Config.HeadlessLoader/HeadlessConfigInterface.cs deleted file mode 100644 index a86fcf64..00000000 --- a/AquaMai/AquaMai.Config.HeadlessLoader/HeadlessConfigInterface.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using System.Reflection; -using AquaMai.Config.Interfaces; -using Mono.Cecil; - -namespace AquaMai.Config.HeadlessLoader; - -public class HeadlessConfigInterface -{ - private readonly Assembly loadedConfigAssembly; - - public IReflectionProvider ReflectionProvider { get; init; } - public IReflectionManager ReflectionManager { get; init; } - - public string ApiVersion { get; init; } - - public HeadlessConfigInterface(Assembly loadedConfigAssembly, AssemblyDefinition modsAssembly) - { - this.loadedConfigAssembly = loadedConfigAssembly; - - ReflectionProvider = Activator.CreateInstance( - loadedConfigAssembly.GetType("AquaMai.Config.Reflection.MonoCecilReflectionProvider"), [modsAssembly]) as IReflectionProvider; - ReflectionManager = Activator.CreateInstance( - loadedConfigAssembly.GetType("AquaMai.Config.Reflection.ReflectionManager"), [ReflectionProvider]) as IReflectionManager; - ApiVersion = loadedConfigAssembly - .GetType("AquaMai.Config.ApiVersion") - .GetField("Version", BindingFlags.Public | BindingFlags.Static) - .GetRawConstantValue() as string; - } - - public IConfigView CreateConfigView(string tomlString = null) - { - return Activator.CreateInstance( - loadedConfigAssembly.GetType("AquaMai.Config.ConfigView"), - tomlString == null ? [] : [tomlString]) as IConfigView; - } - - public IConfig CreateConfig() - { - return Activator.CreateInstance( - loadedConfigAssembly.GetType("AquaMai.Config.Config"), [ReflectionManager]) as IConfig; - } - - public IConfigParser GetConfigParser() - { - return loadedConfigAssembly - .GetType("AquaMai.Config.ConfigParser") - .GetField("Instance", BindingFlags.Public | BindingFlags.Static) - .GetValue(null) as IConfigParser; - } - - public IConfigSerializer CreateConfigSerializer(IConfigSerializer.Options options) - { - return Activator.CreateInstance( - loadedConfigAssembly.GetType("AquaMai.Config.ConfigSerializer"), [options]) as IConfigSerializer; - } - - public IConfigMigrationManager GetConfigMigrationManager() - { - return loadedConfigAssembly - .GetType("AquaMai.Config.Migration.ConfigMigrationManager") - .GetField("Instance", BindingFlags.Public | BindingFlags.Static) - .GetValue(null) as IConfigMigrationManager; - } -} diff --git a/AquaMai/AquaMai.Config.HeadlessLoader/HeadlessConfigLoader.cs b/AquaMai/AquaMai.Config.HeadlessLoader/HeadlessConfigLoader.cs deleted file mode 100644 index e685c26c..00000000 --- a/AquaMai/AquaMai.Config.HeadlessLoader/HeadlessConfigLoader.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Mono.Cecil; - -namespace AquaMai.Config.HeadlessLoader; - -public class HeadlessConfigLoader -{ - public static HeadlessConfigInterface LoadFromPacked(string fileName) - { - using var file = new FileStream(fileName, FileMode.Open); - return LoadFromPacked(file); - } - - public static HeadlessConfigInterface LoadFromPacked(byte[] assemblyBinary) - => LoadFromPacked(new MemoryStream(assemblyBinary)); - - public static HeadlessConfigInterface LoadFromPacked(Stream assemblyStream) - => LoadFromPacked(AssemblyDefinition.ReadAssembly(assemblyStream)); - - public static HeadlessConfigInterface LoadFromPacked(AssemblyDefinition assembly) - { - return LoadFromUnpacked( - ResourceLoader.LoadEmbeddedAssemblies(assembly).Values); - } - - public static HeadlessConfigInterface LoadFromUnpacked(IEnumerable assemblyBinariess) => - LoadFromUnpacked(assemblyBinariess.Select(binary => new MemoryStream(binary))); - - public static HeadlessConfigInterface LoadFromUnpacked(IEnumerable assemblyStreams) - { - var resolver = new CustomAssemblyResolver(); - var assemblies = assemblyStreams - .Select( - assemblyStream => - AssemblyDefinition.ReadAssembly( - assemblyStream, - new ReaderParameters() { - AssemblyResolver = resolver - })) - .ToArray(); - foreach (var assembly in assemblies) - { - resolver.RegisterAssembly(assembly); - } - - var configAssembly = assemblies.First(assembly => assembly.Name.Name == "AquaMai.Config"); - if (configAssembly == null) - { - throw new InvalidOperationException("AquaMai.Config assembly not found"); - } - var loadedConfigAssembly = ConfigAssemblyLoader.LoadConfigAssembly(configAssembly); - var modsAssembly = assemblies.First(assembly => assembly.Name.Name == "AquaMai.Mods"); - if (modsAssembly == null) - { - throw new InvalidOperationException("AquaMai.Mods assembly not found"); - } - return new(loadedConfigAssembly, modsAssembly); - } -} diff --git a/AquaMai/AquaMai.Config.HeadlessLoader/Polyfills.cs b/AquaMai/AquaMai.Config.HeadlessLoader/Polyfills.cs deleted file mode 100644 index eb2da113..00000000 --- a/AquaMai/AquaMai.Config.HeadlessLoader/Polyfills.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace System.Runtime.CompilerServices -{ - internal static class IsExternalInit {} -} diff --git a/AquaMai/AquaMai.Config.HeadlessLoader/ResourceLoader.cs b/AquaMai/AquaMai.Config.HeadlessLoader/ResourceLoader.cs deleted file mode 100644 index e57a1357..00000000 --- a/AquaMai/AquaMai.Config.HeadlessLoader/ResourceLoader.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using System.IO.Compression; -using System.Linq; -using Mono.Cecil; - -namespace AquaMai.Config.HeadlessLoader; - -public class ResourceLoader -{ - private const string DLL_SUFFIX = ".dll"; - private const string COMPRESSED_SUFFIX = ".compressed"; - private const string DLL_COMPRESSED_SUFFIX = $"{DLL_SUFFIX}{COMPRESSED_SUFFIX}"; - - public static Dictionary LoadEmbeddedAssemblies(AssemblyDefinition assembly) - { - return assembly.MainModule.Resources - .Where(resource => resource.Name.ToLower().EndsWith(DLL_SUFFIX) || resource.Name.ToLower().EndsWith(DLL_COMPRESSED_SUFFIX)) - .Select(LoadResource) - .Where(data => data.Name != null) - .ToDictionary(data => data.Name, data => data.Stream); - } - - public static (string Name, Stream Stream) LoadResource(Resource resource) - { - if (resource is EmbeddedResource embeddedResource) - { - if (resource.Name.ToLower().EndsWith(COMPRESSED_SUFFIX)) - { - var decompressedStream = new MemoryStream(); - using (var deflateStream = new DeflateStream(embeddedResource.GetResourceStream(), CompressionMode.Decompress)) - { - deflateStream.CopyTo(decompressedStream); - } - decompressedStream.Position = 0; - return (resource.Name.Substring(0, resource.Name.Length - COMPRESSED_SUFFIX.Length), decompressedStream); - } - return (resource.Name, embeddedResource.GetResourceStream()); - } - return (null, null); - } -} diff --git a/AquaMai/AquaMai.Config.Interfaces/AquaMai.Config.Interfaces.csproj b/AquaMai/AquaMai.Config.Interfaces/AquaMai.Config.Interfaces.csproj deleted file mode 100644 index 95c911c2..00000000 --- a/AquaMai/AquaMai.Config.Interfaces/AquaMai.Config.Interfaces.csproj +++ /dev/null @@ -1,40 +0,0 @@ - - - - Release - AnyCPU - {DF1536F9-3B06-4463-B654-4CC3E708B610} - Library - AquaMai.Config.Interfaces - AquaMai.Config.Interfaces - net472 - 512 - true - 12 - 414 - $(ProjectDir)../Libs/;$(AssemblySearchPaths) - $(ProjectDir)../Output/ - false - false - - - - false - None - true - prompt - 4 - true - false - - - - DEBUG - - - - - - - - diff --git a/AquaMai/AquaMai.Config.Interfaces/IConfig.cs b/AquaMai/AquaMai.Config.Interfaces/IConfig.cs deleted file mode 100644 index 5f3f4a02..00000000 --- a/AquaMai/AquaMai.Config.Interfaces/IConfig.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; - -namespace AquaMai.Config.Interfaces; - -public interface IConfig -{ - public interface IEntryState - { - public bool IsDefault { get; } - public object DefaultValue { get; } - public object Value { get; set; } - } - - public interface ISectionState - { - public bool IsDefault { get; set; } - public bool DefaultEnabled { get; } - public bool Enabled { get; set; } - } - - public IReflectionManager ReflectionManager { get; } - - public ISectionState GetSectionState(IReflectionManager.ISection section); - public ISectionState GetSectionState(Type type); - public void SetSectionEnabled(IReflectionManager.ISection section, bool enabled); - public IEntryState GetEntryState(IReflectionManager.IEntry entry); - public void SetEntryValue(IReflectionManager.IEntry entry, object value); -} diff --git a/AquaMai/AquaMai.Config.Interfaces/IConfigComment.cs b/AquaMai/AquaMai.Config.Interfaces/IConfigComment.cs deleted file mode 100644 index db219eb3..00000000 --- a/AquaMai/AquaMai.Config.Interfaces/IConfigComment.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace AquaMai.Config.Interfaces; - -public interface IConfigComment -{ - string CommentEn { get; init; } - string CommentZh { get; init; } - public string GetLocalized(string lang); -} diff --git a/AquaMai/AquaMai.Config.Interfaces/IConfigEntryAttribute.cs b/AquaMai/AquaMai.Config.Interfaces/IConfigEntryAttribute.cs deleted file mode 100644 index 9d1564bc..00000000 --- a/AquaMai/AquaMai.Config.Interfaces/IConfigEntryAttribute.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace AquaMai.Config.Interfaces; - -public interface IConfigEntryAttribute -{ - IConfigComment Comment { get; } - bool HideWhenDefault { get; } -} \ No newline at end of file diff --git a/AquaMai/AquaMai.Config.Interfaces/IConfigMigrationManager.cs b/AquaMai/AquaMai.Config.Interfaces/IConfigMigrationManager.cs deleted file mode 100644 index 0c343b5e..00000000 --- a/AquaMai/AquaMai.Config.Interfaces/IConfigMigrationManager.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace AquaMai.Config.Interfaces; - -public interface IConfigMigrationManager -{ - public IConfigView Migrate(IConfigView config); - public string GetVersion(IConfigView config); - public string LatestVersion { get; } -} diff --git a/AquaMai/AquaMai.Config.Interfaces/IConfigParser.cs b/AquaMai/AquaMai.Config.Interfaces/IConfigParser.cs deleted file mode 100644 index 5dda2d83..00000000 --- a/AquaMai/AquaMai.Config.Interfaces/IConfigParser.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace AquaMai.Config.Interfaces; - -public interface IConfigParser -{ - public void Parse(IConfig config, string tomlString); - public void Parse(IConfig config, IConfigView configView); -} diff --git a/AquaMai/AquaMai.Config.Interfaces/IConfigSectionAttribute.cs b/AquaMai/AquaMai.Config.Interfaces/IConfigSectionAttribute.cs deleted file mode 100644 index b8bdb41d..00000000 --- a/AquaMai/AquaMai.Config.Interfaces/IConfigSectionAttribute.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace AquaMai.Config.Interfaces; - -public interface IConfigSectionAttribute -{ - IConfigComment Comment { get; } - bool ExampleHidden { get; } - bool DefaultOn { get; } - bool AlwaysEnabled { get; } -} \ No newline at end of file diff --git a/AquaMai/AquaMai.Config.Interfaces/IConfigSerializer.cs b/AquaMai/AquaMai.Config.Interfaces/IConfigSerializer.cs deleted file mode 100644 index e1b8fe83..00000000 --- a/AquaMai/AquaMai.Config.Interfaces/IConfigSerializer.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace AquaMai.Config.Interfaces; - -public interface IConfigSerializer -{ - public record Options - { - public string Lang { get; init; } - public bool IncludeBanner { get; init; } - public bool OverrideLocaleValue { get; init; } - } - - public string Serialize(IConfig config); -} diff --git a/AquaMai/AquaMai.Config.Interfaces/IConfigView.cs b/AquaMai/AquaMai.Config.Interfaces/IConfigView.cs deleted file mode 100644 index 29094741..00000000 --- a/AquaMai/AquaMai.Config.Interfaces/IConfigView.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace AquaMai.Config.Interfaces; - -public interface IConfigView -{ - public void SetValue(string path, object value); - public T GetValueOrDefault(string path, T defaultValue = default); - public bool TryGetValue(string path, out T resultValue); - public bool Remove(string path); - public string ToToml(); - public IConfigView Clone(); -} diff --git a/AquaMai/AquaMai.Config.Interfaces/IReflectionManager.cs b/AquaMai/AquaMai.Config.Interfaces/IReflectionManager.cs deleted file mode 100644 index 41be9ef9..00000000 --- a/AquaMai/AquaMai.Config.Interfaces/IReflectionManager.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System.Collections.Generic; -using System; - -namespace AquaMai.Config.Interfaces; - -public interface IReflectionManager -{ - public interface IEntry - { - public string Path { get; } - public string Name { get; } - public IReflectionField Field { get; } - public IConfigEntryAttribute Attribute { get; init; } - } - - public interface ISection - { - public string Path { get; } - public IReflectionType Type { get; } - public List Entries { get; } - public IConfigSectionAttribute Attribute { get; init; } - } - - public IEnumerable Sections { get; } - - public IEnumerable Entries { get; } - - public bool ContainsSection(string path); - - public bool TryGetSection(string path, out ISection section); - - public bool TryGetSection(Type type, out ISection section); - - public ISection GetSection(string path); - - public ISection GetSection(Type type); - - public bool ContainsEntry(string path); - - public bool TryGetEntry(string path, out IEntry entry); - - public IEntry GetEntry(string path); -} diff --git a/AquaMai/AquaMai.Config.Interfaces/IReflectionProvider.cs b/AquaMai/AquaMai.Config.Interfaces/IReflectionProvider.cs deleted file mode 100644 index 19f7fc10..00000000 --- a/AquaMai/AquaMai.Config.Interfaces/IReflectionProvider.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; - -namespace AquaMai.Config.Interfaces; - -public interface IReflectionField -{ - public string Name { get; } - public Type FieldType { get; } - - public T GetCustomAttribute() where T : Attribute; - public object GetValue(object objIsNull); - public void SetValue(object objIsNull, object value); -} - -public interface IReflectionType -{ - public string FullName { get; } - public string Namespace { get; } - - public T GetCustomAttribute() where T : Attribute; - public IReflectionField[] GetFields(BindingFlags bindingAttr); -} - -public interface IReflectionProvider -{ - public IReflectionType[] GetTypes(); - public Dictionary GetEnum(string enumName); -} diff --git a/AquaMai/AquaMai.Config.Interfaces/Polyfills.cs b/AquaMai/AquaMai.Config.Interfaces/Polyfills.cs deleted file mode 100644 index eb2da113..00000000 --- a/AquaMai/AquaMai.Config.Interfaces/Polyfills.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace System.Runtime.CompilerServices -{ - internal static class IsExternalInit {} -} diff --git a/AquaMai/AquaMai.Config/ApiVersion.cs b/AquaMai/AquaMai.Config/ApiVersion.cs deleted file mode 100644 index 35959b54..00000000 --- a/AquaMai/AquaMai.Config/ApiVersion.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace AquaMai.Config; - -public static class ApiVersion -{ - // Using a raw string for API version instead of a constant for maximum compatibility. - // When breaking changes are made, increment the major version. - // When new APIs are added in a backwards-compatible but non-forward-compatible manner, increment the minor version. - public const string Version = "1.0"; -} diff --git a/AquaMai/AquaMai.Config/AquaMai.Config.csproj b/AquaMai/AquaMai.Config/AquaMai.Config.csproj deleted file mode 100644 index d80f92bd..00000000 --- a/AquaMai/AquaMai.Config/AquaMai.Config.csproj +++ /dev/null @@ -1,60 +0,0 @@ - - - - Release - AnyCPU - {DF1536F9-3B06-4463-B654-4CC3E708B610} - Library - AquaMai.Config - AquaMai.Config - net472 - 512 - true - 12 - 414 - $(ProjectDir)../Libs/;$(AssemblySearchPaths) - $(ProjectDir)../Output/ - false - false - - - - false - None - true - prompt - 4 - true - false - - - - DEBUG - - - - - - - - - - - - - - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - all - - - - - diff --git a/AquaMai/AquaMai.Config/Attributes/ConfigCollapseNamespaceAttribute.cs b/AquaMai/AquaMai.Config/Attributes/ConfigCollapseNamespaceAttribute.cs deleted file mode 100644 index dab14350..00000000 --- a/AquaMai/AquaMai.Config/Attributes/ConfigCollapseNamespaceAttribute.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace AquaMai.Config.Attributes; - -// When The most inner namespace is the same name of the class, it should be collapsed. -// The class must be the only class in the namespace with a [ConfigSection] attribute. -[AttributeUsage(AttributeTargets.Class)] -public class ConfigCollapseNamespaceAttribute : Attribute -{} diff --git a/AquaMai/AquaMai.Config/Attributes/ConfigComment.cs b/AquaMai/AquaMai.Config/Attributes/ConfigComment.cs deleted file mode 100644 index 4a9ebb03..00000000 --- a/AquaMai/AquaMai.Config/Attributes/ConfigComment.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using AquaMai.Config.Interfaces; - -namespace AquaMai.Config.Attributes; - -public record ConfigComment(string CommentEn, string CommentZh) : IConfigComment -{ - public string GetLocalized(string lang) => lang switch - { - "en" => CommentEn ?? "", - "zh" => CommentZh ?? "", - _ => throw new ArgumentException($"Unsupported language: {lang}") - }; -} diff --git a/AquaMai/AquaMai.Config/Attributes/ConfigEntryAttribute.cs b/AquaMai/AquaMai.Config/Attributes/ConfigEntryAttribute.cs deleted file mode 100644 index 4ea36334..00000000 --- a/AquaMai/AquaMai.Config/Attributes/ConfigEntryAttribute.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using AquaMai.Config.Interfaces; - -namespace AquaMai.Config.Attributes; - -public enum SpecialConfigEntry -{ - None, - Locale -} - -[AttributeUsage(AttributeTargets.Field)] -public class ConfigEntryAttribute( - string en = null, - string zh = null, - // NOTE: Don't use this argument to hide any useful options. - // Only use it to hide options that really won't be used. - bool hideWhenDefault = false, - // NOTE: Use this argument to mark special config entries that need special handling. - SpecialConfigEntry specialConfigEntry = SpecialConfigEntry.None) : Attribute, IConfigEntryAttribute -{ - public IConfigComment Comment { get; } = new ConfigComment(en, zh); - public bool HideWhenDefault { get; } = hideWhenDefault; - public SpecialConfigEntry SpecialConfigEntry { get; } = specialConfigEntry; -} diff --git a/AquaMai/AquaMai.Config/Attributes/ConfigSectionAttribute.cs b/AquaMai/AquaMai.Config/Attributes/ConfigSectionAttribute.cs deleted file mode 100644 index 100c4c44..00000000 --- a/AquaMai/AquaMai.Config/Attributes/ConfigSectionAttribute.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using AquaMai.Config.Interfaces; - -namespace AquaMai.Config.Attributes; - -[AttributeUsage(AttributeTargets.Class)] -public class ConfigSectionAttribute( - string en = null, - string zh = null, - // It will be hidden if the default value is preserved. - bool exampleHidden = false, - // A "Disabled = true" entry is required to disable the section. - bool defaultOn = false, - // NOTE: You probably shouldn't use this. Only the "General" section is using this. - // Implies defaultOn = true. - bool alwaysEnabled = false) : Attribute, IConfigSectionAttribute -{ - public IConfigComment Comment { get; } = new ConfigComment(en, zh); - public bool ExampleHidden { get; } = exampleHidden; - public bool DefaultOn { get; } = defaultOn || alwaysEnabled; - public bool AlwaysEnabled { get; } = alwaysEnabled; -} diff --git a/AquaMai/AquaMai.Config/Attributes/EnableCondition.cs b/AquaMai/AquaMai.Config/Attributes/EnableCondition.cs deleted file mode 100644 index 2a1e8d99..00000000 --- a/AquaMai/AquaMai.Config/Attributes/EnableCondition.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System; - -namespace AquaMai.Config.Attributes; - -public enum EnableConditionOperator -{ - Equal, - NotEqual, - GreaterThan, - LessThan, - GreaterThanOrEqual, - LessThanOrEqual -} - -public class EnableCondition( - Type referenceType, - string referenceMember, - EnableConditionOperator @operator, - object rightSideValue) : Attribute -{ - public Type ReferenceType { get; } = referenceType; - public string ReferenceMember { get; } = referenceMember; - public EnableConditionOperator Operator { get; } = @operator; - public object RightSideValue { get; } = rightSideValue; - - // Referencing a field in another class and checking if it's true. - public EnableCondition(Type referenceType, string referenceMember) - : this(referenceType, referenceMember, EnableConditionOperator.Equal, true) - { } - - // Referencing a field in the same class and comparing it with a value. - public EnableCondition(string referenceMember, EnableConditionOperator condition, object value) - : this(null, referenceMember, condition, value) - { } - - // Referencing a field in the same class and checking if it's true. - public EnableCondition(string referenceMember) - : this(referenceMember, EnableConditionOperator.Equal, true) - { } - - public bool Evaluate(Type selfType) - { - var referenceType = ReferenceType ?? selfType; - var referenceField = referenceType.GetField( - ReferenceMember, - System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic); - var referenceProperty = referenceType.GetProperty( - ReferenceMember, - System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic); - if (referenceField == null && referenceProperty == null) - { - throw new ArgumentException($"Field or property {ReferenceMember} not found in {referenceType.FullName}"); - } - var referenceMemberValue = referenceField != null ? referenceField.GetValue(null) : referenceProperty.GetValue(null); - switch (Operator) - { - case EnableConditionOperator.Equal: - return referenceMemberValue.Equals(RightSideValue); - case EnableConditionOperator.NotEqual: - return !referenceMemberValue.Equals(RightSideValue); - case EnableConditionOperator.GreaterThan: - case EnableConditionOperator.LessThan: - case EnableConditionOperator.GreaterThanOrEqual: - case EnableConditionOperator.LessThanOrEqual: - var comparison = (IComparable)referenceMemberValue; - return Operator switch - { - EnableConditionOperator.GreaterThan => comparison.CompareTo(RightSideValue) > 0, - EnableConditionOperator.LessThan => comparison.CompareTo(RightSideValue) < 0, - EnableConditionOperator.GreaterThanOrEqual => comparison.CompareTo(RightSideValue) >= 0, - EnableConditionOperator.LessThanOrEqual => comparison.CompareTo(RightSideValue) <= 0, - _ => throw new NotImplementedException(), - }; - default: - throw new NotImplementedException(); - } - } -} diff --git a/AquaMai/AquaMai.Config/Config.cs b/AquaMai/AquaMai.Config/Config.cs deleted file mode 100644 index 9e445c2d..00000000 --- a/AquaMai/AquaMai.Config/Config.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System; -using System.Collections.Generic; -using AquaMai.Config.Interfaces; -using AquaMai.Config.Reflection; - -namespace AquaMai.Config; - -public class Config : IConfig -{ - // NOTE: If a section's state is default, all underlying entries' states are default as well. - - public record SectionState : IConfig.ISectionState - { - public bool IsDefault { get; set; } - public bool DefaultEnabled { get; init; } - public bool Enabled { get; set; } - } - - public record EntryState : IConfig.IEntryState - { - public bool IsDefault { get; set; } - public object DefaultValue { get; init; } - public object Value { get; set; } - } - - private readonly Dictionary sections = new(StringComparer.OrdinalIgnoreCase); - private readonly Dictionary entries = new(StringComparer.OrdinalIgnoreCase); - - public readonly ReflectionManager reflectionManager; - public IReflectionManager ReflectionManager => reflectionManager; - - public Config(ReflectionManager reflectionManager) - { - this.reflectionManager = reflectionManager; - - foreach (var section in reflectionManager.SectionValues) - { - InitializeSection(section); - } - } - - private void InitializeSection(ReflectionManager.Section section) - { - sections.Add(section.Path, new SectionState() - { - IsDefault = true, - DefaultEnabled = section.Attribute.DefaultOn, - Enabled = section.Attribute.DefaultOn - }); - - foreach (var entry in section.Entries) - { - var defaultValue = entry.Field.GetValue(null); - if (defaultValue == null) - { - throw new InvalidOperationException($"Null default value for entry {entry.Path} is not allowed."); - } - - entries.Add(entry.Path, new EntryState() - { - IsDefault = true, - DefaultValue = defaultValue, - Value = defaultValue - }); - } - } - - public IConfig.ISectionState GetSectionState(IReflectionManager.ISection section) - { - return sections[section.Path]; - } - - public IConfig.ISectionState GetSectionState(Type type) - { - if (!ReflectionManager.TryGetSection(type, out var section)) - { - throw new ArgumentException($"Type {type.FullName} is not a config section."); - } - - return sections[section.Path]; - } - - public void SetSectionEnabled(IReflectionManager.ISection section, bool enabled) - { - sections[section.Path].IsDefault = false; - sections[section.Path].Enabled = enabled; - } - - public IConfig.IEntryState GetEntryState(IReflectionManager.IEntry entry) - { - return entries[entry.Path]; - } - - public void SetEntryValue(IReflectionManager.IEntry entry, object value) - { - if (value.GetType() != entry.Field.FieldType) - { - throw new ArgumentException($"Value type {value.GetType().FullName} does not match entry type {entry.Field.FieldType.FullName}."); - } - - entry.Field.SetValue(null, value); - entries[entry.Path].IsDefault = false; - entries[entry.Path].Value = value; - } -} \ No newline at end of file diff --git a/AquaMai/AquaMai.Config/ConfigParser.cs b/AquaMai/AquaMai.Config/ConfigParser.cs deleted file mode 100644 index cbca0980..00000000 --- a/AquaMai/AquaMai.Config/ConfigParser.cs +++ /dev/null @@ -1,125 +0,0 @@ -using System; -using Tomlet.Models; -using AquaMai.Config.Interfaces; -using AquaMai.Config.Reflection; -using AquaMai.Config.Migration; -using System.Linq; - -namespace AquaMai.Config; - -public class ConfigParser : IConfigParser -{ - public readonly static ConfigParser Instance = new(); - - private readonly static string[] supressUnrecognizedConfigPaths = ["Version"]; - private readonly static string[] supressUnrecognizedConfigPathSuffixes = [ - ".Disabled", // For section enable state. - ".Disable", // For section enable state, but the wrong key, warn later. - ".Enabled", // For section enable state, but the wrong key, warn later. - ".Enable", // For section enable state, but the wrong key, warn later. - ]; - - private ConfigParser() - {} - - public void Parse(IConfig config, string tomlString) - { - var configView = new ConfigView(tomlString); - Parse(config, configView); - } - - public void Parse(IConfig config, IConfigView configView) - { - var configVersion = ConfigMigrationManager.Instance.GetVersion(configView); - if (configVersion != ConfigMigrationManager.Instance.LatestVersion) - { - throw new InvalidOperationException($"Config version mismatch: expected {ConfigMigrationManager.Instance.LatestVersion}, got {configVersion}"); - } - Hydrate((Config)config, ((ConfigView)configView).root, ""); - } - - private static void Hydrate(Config config, TomlValue value, string path) - { - if (config.ReflectionManager.TryGetSection(path, out var section)) - { - ParseSectionEnableState(config, (ReflectionManager.Section)section, value, path); - } - - if (value is TomlTable table) - { - bool isLeaf = true; - foreach (var subKey in table.Keys) - { - var subValue = table.GetValue(subKey); - var subPath = path == "" ? subKey : $"{path}.{subKey}"; - if (subValue is TomlTable) - { - isLeaf = false; - } - Hydrate(config, subValue, subPath); - } - // A leaf dictionary, which has no child dictionaries, must be a section. - if (isLeaf && section == null) - { - Utility.Log($"Unrecognized config section: {path}"); - } - } - else - { - // It's an config entry value (or a primitive type for enabling a section). - if (!config.ReflectionManager.ContainsSection(path) && - !config.ReflectionManager.ContainsEntry(path) && - !supressUnrecognizedConfigPaths.Any(s => path.Equals(s, StringComparison.OrdinalIgnoreCase)) && - !supressUnrecognizedConfigPathSuffixes.Any(suffix => path.EndsWith(suffix, StringComparison.OrdinalIgnoreCase))) - { - Utility.Log($"Unrecognized config entry: {path}"); - return; - } - - if (config.ReflectionManager.TryGetEntry(path, out var entry)) - { - try - { - var parsedValue = Utility.ParseTomlValue(entry.Field.FieldType, value); - config.SetEntryValue(entry, parsedValue); - } - catch (Exception e) - { - Utility.Log($"Error hydrating config ({path} = {value.StringValue}): {e.Message}"); - } - } - } - } - - public static void ParseSectionEnableState( - Config config, - ReflectionManager.Section section, - TomlValue value, - string path) - { - if (value is TomlTable table) - { - foreach (var unexpectedKey in (string[]) ["Enable", "Enabled", "Disable"]) - { - if (Utility.TomlContainsKeyCaseInsensitive(table, unexpectedKey)) - { - Utility.Log($"Unexpected key \"{unexpectedKey}\" for enable status under \"{path}\". Only \"Disabled\" is parsed."); - } - } - - if (Utility.TomlTryGetValueCaseInsensitive(table, "Disabled", out var disableValue) && !section.Attribute.AlwaysEnabled) - { - var disabled = Utility.IsTruty(disableValue, path + ".Disabled"); - config.SetSectionEnabled(section, !disabled); - } - else - { - config.SetSectionEnabled(section, true); - } - } - else - { - config.SetSectionEnabled(section, Utility.IsTruty(value, path)); - } - } -} diff --git a/AquaMai/AquaMai.Config/ConfigSerializer.cs b/AquaMai/AquaMai.Config/ConfigSerializer.cs deleted file mode 100644 index eb9da5ac..00000000 --- a/AquaMai/AquaMai.Config/ConfigSerializer.cs +++ /dev/null @@ -1,186 +0,0 @@ -using System; -using System.Reflection; -using System.Text; -using AquaMai.Config.Attributes; -using AquaMai.Config.Interfaces; -using AquaMai.Config.Migration; -using Tomlet.Models; - -namespace AquaMai.Config; - -public class ConfigSerializer(IConfigSerializer.Options Options) : IConfigSerializer -{ - private const string BANNER_ZH = - """ - 这是 AquaMai 的 TOML 配置文件 - - - 井号 # 开头的行为注释,被注释掉的内容不会生效 - - 为方便使用 VSCode 等编辑器进行编辑,被注释掉的配置内容使用一个井号 #,而注释文本使用两个井号 ## - - 以方括号包裹的行,如 [OptionalCategory.Section],为一个栏目 - - 将默认被注释(即默认禁用)的栏目取消注释即可启用 - - 若要禁用一个默认启用的栏目,请在栏目下添加「Disabled = true」配置项,删除它/注释它不会有效 - - 形如「键 = 值」为一个配置项 - - 配置项应用到其上方最近的栏目,请不要在一个栏目被注释掉的情况下开启其配置项(会加到上一个栏目,无效) - - 当对应栏目启用时,配置项生效,无论是否将其取消注释 - - 注释掉的配置项保留其注释中的默认值,默认值可能会随版本更新而变化 - - 该文件的格式和文字注释是固定的,配置文件将在启动时被重写,无法解析的内容将被删除 - - 试试使用 MaiChartManager 图形化配置 AquaMai 吧! - https://github.com/clansty/MaiChartManager - """; - - private const string BANNER_EN = - """ - This is the TOML configuration file of AquaMai. - - - Lines starting with a hash # are comments. Commented content will not take effect. - - For easily editing with editors (e.g. VSCode), commented configuration content uses a single hash #, while comment text uses two hashes ##. - - Lines with square brackets like [OptionalCategory.Section] are sections. - - Uncomment a section that is commented out by default (i.e. disabled by default) to enable it. - - To disable a section that is enabled by default, add a "Disable = true" entry under the section. Removing/commenting it will not work. - - Lines like "Key = Value" is a configuration entry. - - Configuration entries apply to the nearest section above them. Do not enable a configuration entry when its section is commented out (will be added to the previous section, which is invalid). - - Configuration entries take effect when the corresponding section is enabled, regardless of whether they are uncommented. - - Commented configuration entries retain their default values (shown in the comment), which may change with version updates. - - The format and text comments of this file are fixed. The configuration file will be rewritten at startup, and unrecognizable content will be deleted. - """; - - private readonly IConfigSerializer.Options Options = Options; - - public string Serialize(IConfig config) - { - StringBuilder sb = new(); - if (Options.IncludeBanner) - { - var banner = Options.Lang == "zh" ? BANNER_ZH : BANNER_EN; - if (banner != null) - { - AppendComment(sb, banner.TrimEnd()); - sb.AppendLine(); - } - } - - // Version - AppendEntry(sb, null, "Version", ConfigMigrationManager.Instance.LatestVersion); - - foreach (var section in ((Config)config).reflectionManager.SectionValues) - { - var sectionState = config.GetSectionState(section); - - // If the state is default, print the example only. If the example is hidden, skip it. - if (sectionState.IsDefault && section.Attribute.ExampleHidden) - { - continue; - } - sb.AppendLine(); - - AppendComment(sb, section.Attribute.Comment); - - if (// If the section is hidden and hidden by default, print it as commented. - sectionState.IsDefault && !sectionState.Enabled && - // If the section is marked as always enabled, print it normally. - !section.Attribute.AlwaysEnabled) - { - sb.AppendLine($"#[{section.Path}]"); - } - else // If the section is overridden, or is enabled by any means, print it normally. - { - sb.AppendLine($"[{section.Path}]"); - } - - if (!section.Attribute.AlwaysEnabled) - { - // If the section is disabled explicitly, print the "Disabled" meta entry. - if (!sectionState.IsDefault && !sectionState.Enabled) - { - AppendEntry(sb, null, "Disabled", value: true); - } - // If the section is enabled by default, print the "Disabled" meta entry as commented. - else if (sectionState.IsDefault && section.Attribute.DefaultOn) - { - AppendEntry(sb, null, "Disabled", value: false, commented: true); - } - // Otherwise, don't print the "Disabled" meta entry. - } - - // Print entries. - foreach (var entry in section.entries) - { - var entryState = config.GetEntryState(entry); - AppendComment(sb, entry.Attribute.Comment); - if (((ConfigEntryAttribute)entry.Attribute).SpecialConfigEntry == SpecialConfigEntry.Locale && Options.OverrideLocaleValue) - { - AppendEntry(sb, section.Path, entry.Name, Options.Lang); - } - else - { - AppendEntry(sb, section.Path, entry.Name, entryState.Value, entryState.IsDefault); - } - } - } - - return sb.ToString(); - } - - private static string SerializeTomlValue(string diagnosticPath, object value) - { - var type = value.GetType(); - if (value is bool b) - { - return b ? "true" : "false"; - } - else if (value is string str) - { - return new TomlString(str).SerializedValue; - } - else if (type.IsEnum) - { - return new TomlString(value.ToString()).SerializedValue; - } - else if (Utility.IsIntegerType(type)) - { - return value.ToString(); - } - else if (Utility.IsFloatType(type)) - { - return new TomlDouble(Convert.ToDouble(value)).SerializedValue; - } - else - { - var currentMethod = MethodBase.GetCurrentMethod(); - throw new NotImplementedException($"Unsupported config entry type: {type.FullName} ({diagnosticPath}). Please implement in {currentMethod.DeclaringType.FullName}.{currentMethod.Name}"); - } - } - - private void AppendComment(StringBuilder sb, IConfigComment comment) - { - if (comment != null) - { - AppendComment(sb, comment.GetLocalized(Options.Lang)); - } - } - - private static void AppendComment(StringBuilder sb, string comment) - { - comment = comment.Trim(); - if (!string.IsNullOrEmpty(comment)) - { - foreach (var line in comment.Split('\n')) - { - sb.AppendLine($"## {line}"); - } - } - } - - private static void AppendEntry(StringBuilder sb, string diagnosticsSection, string key, object value, bool commented = false) - { - if (commented) - { - sb.Append('#'); - } - var diagnosticsPath = string.IsNullOrEmpty(diagnosticsSection) - ? key - : $"{diagnosticsSection}.{key}"; - sb.AppendLine($"{key} = {SerializeTomlValue(diagnosticsPath, value)}"); - } -} diff --git a/AquaMai/AquaMai.Config/ConfigView.cs b/AquaMai/AquaMai.Config/ConfigView.cs deleted file mode 100644 index 5344aaee..00000000 --- a/AquaMai/AquaMai.Config/ConfigView.cs +++ /dev/null @@ -1,142 +0,0 @@ -using System; -using System.Linq; -using AquaMai.Config.Interfaces; -using Tomlet; -using Tomlet.Models; - -namespace AquaMai.Config; - -public class ConfigView : IConfigView -{ - public readonly TomlTable root; - - public ConfigView() - { - root = new TomlTable(); - } - - public ConfigView(TomlTable root) - { - this.root = root; - } - - public ConfigView(string tomlString) - { - var tomlValue = new TomlParser().Parse(tomlString); - if (tomlValue is not TomlTable tomlTable) - { - throw new ArgumentException($"Invalid TOML, expected a table, got: {tomlValue.GetType()}"); - } - root = tomlTable; - } - - public TomlTable EnsureDictionary(string path) - { - var pathComponents = path.Split('.'); - var current = root; - foreach (var component in pathComponents) - { - if (!current.TryGetValue(component, out var next)) - { - next = new TomlTable(); - current.Put(component, next); - } - current = (TomlTable)next; - } - return current; - } - - public void SetValue(string path, object value) - { - var pathComponents = path.Split('.'); - var current = root; - foreach (var component in pathComponents.Take(pathComponents.Length - 1)) - { - if (!current.TryGetValue(component, out var next)) - { - next = new TomlTable(); - current.Put(component, next); - } - current = (TomlTable)next; - } - - if (value == null) - { - current.Keys.Remove(pathComponents.Last()); - return; - } - current.Put(pathComponents.Last(), value); - } - - public T GetValueOrDefault(string path, T defaultValue = default) - { - return TryGetValue(path, out T resultValue) ? resultValue : defaultValue; - } - - public bool TryGetValue(string path, out T resultValue) - { - var pathComponents = path.Split('.'); - var current = root; - foreach (var component in pathComponents.Take(pathComponents.Length - 1)) - { - if (!Utility.TomlTryGetValueCaseInsensitive(current, component, out var next) || next is not TomlTable nextTable) - { - resultValue = default; - return false; - } - current = nextTable; - } - if (!Utility.TomlTryGetValueCaseInsensitive(current, pathComponents.Last(), out var value)) - { - resultValue = default; - return false; - } - if (typeof(T) == typeof(object)) - { - resultValue = (T)(object)value; - return true; - } - try - { - resultValue = Utility.ParseTomlValue(value); - return true; - } - catch (Exception e) - { - Utility.Log($"Failed to parse value at {path}: {e.Message}"); - resultValue = default; - return false; - } - } - - public bool Remove(string path) - { - var pathComponents = path.Split('.'); - var current = root; - foreach (var component in pathComponents.Take(pathComponents.Length - 1)) - { - if (!Utility.TomlTryGetValueCaseInsensitive(current, component, out var next) || next is not TomlTable nextTable) - { - return false; - } - current = (TomlTable)next; - } - var keyToRemove = pathComponents.Last(); - var keysCaseSensitive = current.Keys.Where(k => string.Equals(k, keyToRemove, StringComparison.OrdinalIgnoreCase)); - foreach (var key in keysCaseSensitive) - { - current.Entries.Remove(key); - } - return keysCaseSensitive.Any(); - } - - public string ToToml() - { - return root.SerializedValue; - } - - public IConfigView Clone() - { - return new ConfigView(ToToml()); - } -} diff --git a/AquaMai/AquaMai.Config/FodyWeavers.xml b/AquaMai/AquaMai.Config/FodyWeavers.xml deleted file mode 100644 index 645a09e7..00000000 --- a/AquaMai/AquaMai.Config/FodyWeavers.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - tomlet - $AquaMai.Config$_ - - diff --git a/AquaMai/AquaMai.Config/FodyWeavers.xsd b/AquaMai/AquaMai.Config/FodyWeavers.xsd deleted file mode 100644 index 968f9b5e..00000000 --- a/AquaMai/AquaMai.Config/FodyWeavers.xsd +++ /dev/null @@ -1,111 +0,0 @@ - - - - - - - - - - - - A regular expression matching the assembly names to include in merging. - - - - - A regular expression matching the assembly names to exclude from merging. - - - - - A regular expression matching the resource names to include in merging. - - - - - A regular expression matching the resource names to exclude from merging. - - - - - A switch to control whether the imported types are hidden (made private) or keep their visibility unchanged. Default is 'true' - - - - - A string that is used as prefix for the namespace of the imported types. - - - - - A switch to control whether to import the full assemblies or only the referenced types. Default is 'false' - - - - - A switch to enable compacting of the target assembly by skipping properties, events and unused methods. Default is 'false' - - - - - - A regular expression matching the assembly names to include in merging. - - - - - A regular expression matching the assembly names to exclude from merging. - - - - - A regular expression matching the resource names to include in merging. - - - - - A regular expression matching the resource names to exclude from merging. - - - - - A switch to control whether the imported types are hidden (made private) or keep their visibility unchanged. Default is 'true' - - - - - A string that is used as prefix for the namespace of the imported types. - - - - - A switch to control whether to import the full assemblies or only the referenced types. Default is 'false' - - - - - A switch to enable compacting of the target assembly by skipping properties, events and unused methods. Default is 'false' - - - - - - - - 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. - - - - - A comma-separated list of error codes that can be safely ignored in assembly verification. - - - - - 'false' to turn off automatic generation of the XML Schema file. - - - - - \ No newline at end of file diff --git a/AquaMai/AquaMai.Config/Migration/ConfigMigrationManager.cs b/AquaMai/AquaMai.Config/Migration/ConfigMigrationManager.cs deleted file mode 100644 index fe4ce8f0..00000000 --- a/AquaMai/AquaMai.Config/Migration/ConfigMigrationManager.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using AquaMai.Config.Interfaces; - -namespace AquaMai.Config.Migration; - -public class ConfigMigrationManager : IConfigMigrationManager -{ - public static readonly ConfigMigrationManager Instance = new(); - - private readonly Dictionary migrationMap = - new List - { - new ConfigMigration_V1_0_V2_0(), - new ConfigMigration_V2_0_V2_1() - }.ToDictionary(m => m.FromVersion); - - public string LatestVersion { get; } - - private ConfigMigrationManager() - { - LatestVersion = migrationMap.Values - .Select(m => m.ToVersion) - .OrderByDescending(version => - { - var versionParts = version.Split('.').Select(int.Parse).ToArray(); - return versionParts[0] * 100000 + versionParts[1]; - }) - .First(); - } - - public IConfigView Migrate(IConfigView config) - { - var currentVersion = GetVersion(config); - while (migrationMap.ContainsKey(currentVersion)) - { - var migration = migrationMap[currentVersion]; - Utility.Log($"Migrating config from v{migration.FromVersion} to v{migration.ToVersion}"); - config = migration.Migrate(config); - currentVersion = migration.ToVersion; - } - - if (currentVersion != LatestVersion) - { - throw new ArgumentException($"Could not migrate the config from v{currentVersion} to v{LatestVersion}"); - } - - return config; - } - - public string GetVersion(IConfigView config) - { - if (config.TryGetValue("Version", out var version)) - { - return version; - } - - // Assume v1.0 if not found - return "1.0"; - } -} \ No newline at end of file diff --git a/AquaMai/AquaMai.Config/Migration/ConfigMigration_V1_0_V2_0.cs b/AquaMai/AquaMai.Config/Migration/ConfigMigration_V1_0_V2_0.cs deleted file mode 100644 index 23c26637..00000000 --- a/AquaMai/AquaMai.Config/Migration/ConfigMigration_V1_0_V2_0.cs +++ /dev/null @@ -1,350 +0,0 @@ -using System; -using System.Collections.Generic; -using AquaMai.Config.Interfaces; -using AquaMai.Config.Types; - -namespace AquaMai.Config.Migration; - -public class ConfigMigration_V1_0_V2_0 : IConfigMigration -{ - public string FromVersion => "1.0"; - public string ToVersion => "2.0"; - - public IConfigView Migrate(IConfigView src) - { - var dst = new ConfigView(); - - dst.SetValue("Version", ToVersion); - - // UX (legacy) - MapBooleanTrueToSectionEnable(src, dst, "UX.TestProof", "GameSystem.TestProof"); - if (src.GetValueOrDefault("UX.QuickSkip")) - { - // NOTE: UX.QuickSkip was a 4-in-1 large patch in earlier V1, then split since ModKeyMap was introduced. - dst.SetValue("UX.OneKeyEntryEnd.Key", "Service"); - dst.SetValue("UX.OneKeyEntryEnd.LongPress", true); - dst.SetValue("UX.OneKeyRetrySkip.RetryKey", "Service"); - dst.SetValue("UX.OneKeyRetrySkip.RetryLongPress", false); - dst.SetValue("UX.OneKeyRetrySkip.SkipKey", "Select1P"); - dst.SetValue("UX.OneKeyRetrySkip.SkipLongPress", false); - dst.EnsureDictionary("GameSystem.QuickRetry"); - } - if (src.GetValueOrDefault("UX.HideSelfMadeCharts")) - { - dst.SetValue("UX.HideSelfMadeCharts.Key", "Service"); - dst.SetValue("UX.HideSelfMadeCharts.LongPress", false); - } - MapBooleanTrueToSectionEnable(src, dst, "UX.LoadJacketPng", "GameSystem.Assets.LoadLocalImages"); - MapBooleanTrueToSectionEnable(src, dst, "UX.SkipWarningScreen", "Tweaks.TimeSaving.SkipStartupWarning"); - MapBooleanTrueToSectionEnable(src, dst, "UX.SkipToMusicSelection", "Tweaks.TimeSaving.EntryToMusicSelection"); - MapBooleanTrueToSectionEnable(src, dst, "UX.SkipEventInfo", "Tweaks.TimeSaving.SkipEventInfo"); - MapBooleanTrueToSectionEnable(src, dst, "UX.SelectionDetail", "UX.SelectionDetail"); - if (src.GetValueOrDefault("UX.CustomNoteSkin") || - src.GetValueOrDefault("UX.CustomSkins")) - { - dst.SetValue("Fancy.CustomSkins.SkinsDir", "LocalAssets/Skins"); - } - MapBooleanTrueToSectionEnable(src, dst, "UX.JudgeDisplay4B", "Fancy.GamePlay.JudgeDisplay4B"); - MapBooleanTrueToSectionEnable(src, dst, "UX.CustomTrackStartDiff", "Fancy.CustomTrackStartDiff"); - MapBooleanTrueToSectionEnable(src, dst, "UX.TrackStartProcessTweak", "Fancy.GamePlay.TrackStartProcessTweak"); - MapBooleanTrueToSectionEnable(src, dst, "UX.DisableTrackStartTabs", "Fancy.GamePlay.DisableTrackStartTabs"); - MapBooleanTrueToSectionEnable(src, dst, "UX.RealisticRandomJudge", "Fancy.GamePlay.RealisticRandomJudge"); - - // Utils (legacy) - if (src.GetValueOrDefault("Utils.Windowed") || - src.GetValueOrDefault("Utils.Width") != 0 || - src.GetValueOrDefault("Utils.Height") != 0) - { - // NOTE: the default "false, 0, 0" was effective earlier in V1, but won't be migrated as enabled in V2. - MapValueOrDefaultToEntryValue(src, dst, "Utils.Windowed", "GameSystem.Window.Windowed", false); - MapValueOrDefaultToEntryValue(src, dst, "Utils.Width", "GameSystem.Window.Width", 0); - MapValueOrDefaultToEntryValue(src, dst, "Utils.Height", "GameSystem.Window.Height", 0); - } - if (src.GetValueOrDefault("Utils.PracticeMode") || src.GetValueOrDefault("Utils.PractiseMode")) // Typo of typo is the correct word - { - dst.SetValue("UX.PracticeMode.Key", "Test"); - dst.SetValue("UX.PracticeMode.LongPress", false); - } - - // Fix (legacy) - MapBooleanTrueToSectionEnable(src, dst, "Fix.SlideJudgeTweak", "Fancy.GamePlay.BreakSlideJudgeBlink"); - MapBooleanTrueToSectionEnable(src, dst, "Fix.BreakSlideJudgeBlink", "Fancy.GamePlay.BreakSlideJudgeBlink"); - MapBooleanTrueToSectionEnable(src, dst, "Fix.SlideJudgeTweak", "Fancy.GamePlay.FanJudgeFlip"); - MapBooleanTrueToSectionEnable(src, dst, "Fix.FanJudgeFlip", "Fancy.GamePlay.FanJudgeFlip"); - // NOTE: This (FixCircleSlideJudge) was enabled by default in V1, but non-default in V2 since it has visual changes - MapBooleanTrueToSectionEnable(src, dst, "Fix.SlideJudgeTweak", "Fancy.GamePlay.AlignCircleSlideJudgeDisplay"); - MapBooleanTrueToSectionEnable(src, dst, "Fix.FixCircleSlideJudge", "Fancy.GamePlay.AlignCircleSlideJudgeDisplay"); - - // Performance (legacy) - MapBooleanTrueToSectionEnable(src, dst, "Performance.ImproveLoadSpeed", "Tweaks.TimeSaving.SkipStartupDelays"); - - // TimeSaving (legacy) - MapBooleanTrueToSectionEnable(src, dst, "TimeSaving.ShowNetErrorDetail", "Utils.ShowNetErrorDetail"); - - // UX - MapValueToEntryValueIfNonNullOrDefault(src, dst, "UX.Locale", "General.Locale", ""); - MapBooleanTrueToSectionEnable(src, dst, "UX.SinglePlayer", "GameSystem.SinglePlayer"); - MapBooleanTrueToSectionEnable(src, dst, "UX.HideMask", "Fancy.HideMask"); - MapBooleanTrueToSectionEnable(src, dst, "UX.LoadAssetsPng", "GameSystem.Assets.LoadLocalImages"); - MapBooleanTrueToSectionEnable(src, dst, "UX.LoadAssetBundleWithoutManifest", "GameSystem.Assets.LoadAssetBundleWithoutManifest"); - MapBooleanTrueToSectionEnable(src, dst, "UX.RandomBgm", "Fancy.RandomBgm"); - MapBooleanTrueToSectionEnable(src, dst, "UX.DemoMaster", "Fancy.DemoMaster"); - MapBooleanTrueToSectionEnable(src, dst, "UX.ExtendTimer", "GameSystem.DisableTimeout"); - MapBooleanTrueToSectionEnable(src, dst, "UX.ImmediateSave", "UX.ImmediateSave"); - MapBooleanTrueToSectionEnable(src, dst, "UX.LoadLocalBga", "GameSystem.Assets.UseJacketAsDummyMovie"); - if (src.GetValueOrDefault("UX.CustomFont")) - { - dst.SetValue("GameSystem.Assets.Fonts.Paths", "LocalAssets/font.ttf"); - dst.SetValue("GameSystem.Assets.Fonts.AddAsFallback", false); - } - MapBooleanTrueToSectionEnable(src, dst, "UX.TouchToButtonInput", "GameSystem.TouchToButtonInput"); - MapBooleanTrueToSectionEnable(src, dst, "UX.HideHanabi", "Fancy.GamePlay.HideHanabi"); - MapBooleanTrueToSectionEnable(src, dst, "UX.SlideFadeInTweak", "Fancy.GamePlay.SlideFadeInTweak"); - MapBooleanTrueToSectionEnable(src, dst, "UX.JudgeAccuracyInfo", "UX.JudgeAccuracyInfo"); - MapValueToEntryValueIfNonNullOrDefault(src, dst, "UX.CustomVersionString", "Fancy.CustomVersionString.VersionString", ""); - MapValueToEntryValueIfNonNullOrDefault(src, dst, "UX.CustomPlaceName", "Fancy.CustomPlaceName.PlaceName", ""); - MapValueToEntryValueIfNonNullOrDefault(src, dst, "UX.ExecOnIdle", "Fancy.Triggers.ExecOnIdle", ""); - MapValueToEntryValueIfNonNullOrDefault(src, dst, "UX.ExecOnEntry", "Fancy.Triggers.ExecOnEntry", ""); - - // Cheat - var unlockTickets = src.GetValueOrDefault("Cheat.TicketUnlock"); - var unlockMaps = src.GetValueOrDefault("Cheat.MapUnlock"); - var unlockUtage = src.GetValueOrDefault("Cheat.UnlockUtage"); - if (unlockTickets || - unlockMaps || - unlockUtage) - { - dst.SetValue("GameSystem.Unlock.Tickets", unlockTickets); - dst.SetValue("GameSystem.Unlock.Maps", unlockMaps); - dst.SetValue("GameSystem.Unlock.Utage", unlockUtage); - } - - // Fix - MapBooleanTrueToSectionEnable(src, dst, "Fix.SkipVersionCheck", "Tweaks.SkipUserVersionCheck"); - if (!src.GetValueOrDefault("Fix.RemoveEncryption")) - { - dst.SetValue("GameSystem.RemoveEncryption.Disabled", true); // Enabled by default in V2 - } - if (!src.GetValueOrDefault("Fix.ForceAsServer", true)) - { - dst.SetValue("GameSettings.ForceAsServer.Disabled", true); // Enabled by default in V2 - } - if (src.GetValueOrDefault("Fix.ForceFreePlay")) - { - dst.SetValue("GameSettings.CreditConfig.IsFreePlay", true); - } - if (src.GetValueOrDefault("Fix.ForcePaidPlay")) - { - dst.SetValue("GameSettings.CreditConfig.IsFreePlay", false); - dst.SetValue("GameSettings.CreditConfig.LockCredits", 24u); - } - MapValueToEntryValueIfNonNullOrDefault(src, dst, "Fix.ExtendNotesPool", "Fancy.GamePlay.ExtendNotesPool.Count", 0); - MapBooleanTrueToSectionEnable(src, dst, "Fix.FrameRateLock", "Tweaks.LockFrameRate"); - if (src.GetValueOrDefault("Font.FontFix") && - !src.GetValueOrDefault("UX.CustomFont")) - { - dst.SetValue("GameSystem.Assets.Fonts.Paths", "%SYSTEMROOT%/Fonts/msyhbd.ttc"); - dst.SetValue("GameSystem.Assets.Fonts.AddAsFallback", true); - } - MapBooleanTrueToSectionEnable(src, dst, "Fix.RealisticRandomJudge", "Fancy.GamePlay.RealisticRandomJudge"); - if (src.GetValueOrDefault("UX.SinglePlayer")) - { - if (src.TryGetValue("Fix.HanabiFix", out bool hanabiFix)) - { - // If it's enabled or disabled explicitly, use the value, otherwise left empty use the default V2 value (enabled). - dst.SetValue("GameSystem.SinglePlayer.FixHanabi", hanabiFix); - } - } - MapBooleanTrueToSectionEnable(src, dst, "Fix.IgnoreAimeServerError", "Tweaks.IgnoreAimeServerError"); - MapBooleanTrueToSectionEnable(src, dst, "Fix.TouchResetAfterTrack", "Tweaks.ResetTouchAfterTrack"); - - // Utils - MapBooleanTrueToSectionEnable(src, dst, "Utils.LogUserId", "Utils.LogUserId"); - MapValueToEntryValueIfNonNullOrDefault(src, dst, "Utils.JudgeAdjustA", "GameSettings.JudgeAdjust.A", 0); - MapValueToEntryValueIfNonNullOrDefault(src, dst, "Utils.JudgeAdjustB", "GameSettings.JudgeAdjust.B", 0); - MapValueToEntryValueIfNonNullOrDefault(src, dst, "Utils.TouchDelay", "GameSettings.JudgeAdjust.TouchDelay", 0u); - MapBooleanTrueToSectionEnable(src, dst, "Utils.SelectionDetail", "UX.SelectionDetail"); - MapBooleanTrueToSectionEnable(src, dst, "Utils.ShowNetErrorDetail", "Utils.ShowNetErrorDetail"); - MapBooleanTrueToSectionEnable(src, dst, "Utils.ShowErrorLog", "Utils.ShowErrorLog"); - MapBooleanTrueToSectionEnable(src, dst, "Utils.FrameRateDisplay", "Utils.DisplayFrameRate"); - MapValueToEntryValueIfNonNullOrDefault(src, dst, "Utils.TouchPanelBaudRate", "GameSystem.TouchPanelBaudRate.BaudRate", 0); - - // TimeSaving - MapBooleanTrueToSectionEnable(src, dst, "TimeSaving.SkipWarningScreen", "Tweaks.TimeSaving.SkipStartupWarning"); - MapBooleanTrueToSectionEnable(src, dst, "TimeSaving.ImproveLoadSpeed", "Tweaks.TimeSaving.SkipStartupDelays"); - MapBooleanTrueToSectionEnable(src, dst, "TimeSaving.SkipToMusicSelection", "Tweaks.TimeSaving.EntryToMusicSelection"); - MapBooleanTrueToSectionEnable(src, dst, "TimeSaving.SkipEventInfo", "Tweaks.TimeSaving.SkipEventInfo"); - MapBooleanTrueToSectionEnable(src, dst, "TimeSaving.IWontTapOrSlideVigorously", "Tweaks.TimeSaving.IWontTapOrSlideVigorously"); - MapBooleanTrueToSectionEnable(src, dst, "TimeSaving.SkipGameOverScreen", "Tweaks.TimeSaving.SkipGoodbyeScreen"); - MapBooleanTrueToSectionEnable(src, dst, "TimeSaving.SkipTrackStart", "Tweaks.TimeSaving.SkipTrackStart"); - MapBooleanTrueToSectionEnable(src, dst, "TimeSaving.ShowQuickEndPlay", "UX.QuickEndPlay"); - - // Visual - if (src.GetValueOrDefault("Visual.CustomSkins")) - { - dst.SetValue("Fancy.CustomSkins.SkinsDir", "LocalAssets/Skins"); - } - MapBooleanTrueToSectionEnable(src, dst, "Visual.JudgeDisplay4B", "Fancy.GamePlay.JudgeDisplay4B"); - MapBooleanTrueToSectionEnable(src, dst, "Visual.CustomTrackStartDiff", "Fancy.CustomTrackStartDiff"); - MapBooleanTrueToSectionEnable(src, dst, "Visual.TrackStartProcessTweak", "Fancy.GamePlay.TrackStartProcessTweak"); - MapBooleanTrueToSectionEnable(src, dst, "Visual.DisableTrackStartTabs", "Fancy.GamePlay.DisableTrackStartTabs"); - MapBooleanTrueToSectionEnable(src, dst, "Visual.FanJudgeFlip", "Fancy.GamePlay.FanJudgeFlip"); - MapBooleanTrueToSectionEnable(src, dst, "Visual.BreakSlideJudgeBlink", "Fancy.GamePlay.BreakSlideJudgeBlink"); - MapBooleanTrueToSectionEnable(src, dst, "Visual.SlideArrowAnimation", "Fancy.GamePlay.SlideArrowAnimation"); - MapBooleanTrueToSectionEnable(src, dst, "Visual.SlideLayerReverse", "Fancy.GamePlay.SlideLayerReverse"); - - // ModKeyMap - var keyQuickSkip = src.GetValueOrDefault("ModKeyMap.QuickSkip", "None"); - var keyInGameRetry = src.GetValueOrDefault("ModKeyMap.InGameRetry", "None"); - var keyInGameSkip = src.GetValueOrDefault("ModKeyMap.InGameSkip", "None"); - var keyPractiseMode = src.GetValueOrDefault("ModKeyMap.PractiseMode", "None"); - var keyHideSelfMadeCharts = src.GetValueOrDefault("ModKeyMap.HideSelfMadeCharts", "None"); - if (keyQuickSkip != "None") - { - dst.SetValue("UX.OneKeyEntryEnd.Key", keyQuickSkip); - MapValueToEntryValueIfNonNull(src, dst, "ModKeyMap.QuickSkipLongPress", "UX.OneKeyEntryEnd.LongPress"); - } - if (keyInGameRetry != "None" || keyInGameSkip != "None") - { - dst.SetValue("UX.OneKeyRetrySkip.RetryKey", keyInGameRetry); - if (keyInGameRetry != "None") - { - MapValueToEntryValueIfNonNull(src, dst, "ModKeyMap.InGameRetryLongPress", "UX.OneKeyRetrySkip.RetryLongPress"); - } - dst.SetValue("UX.OneKeyRetrySkip.SkipKey", keyInGameSkip); - if (keyInGameSkip != "None") - { - MapValueToEntryValueIfNonNull(src, dst, "ModKeyMap.InGameSkipLongPress", "UX.OneKeyRetrySkip.SkipLongPress"); - } - } - if (keyPractiseMode != "None") - { - dst.SetValue("UX.PracticeMode.Key", keyPractiseMode); - MapValueToEntryValueIfNonNull(src, dst, "ModKeyMap.PractiseModeLongPress", "UX.PracticeMode.LongPress"); - } - if (keyHideSelfMadeCharts != "None") - { - dst.SetValue("UX.HideSelfMadeCharts.Key", keyHideSelfMadeCharts); - MapValueToEntryValueIfNonNull(src, dst, "ModKeyMap.HideSelfMadeChartsLongPress", "UX.HideSelfMadeCharts.LongPress"); - } - MapBooleanTrueToSectionEnable(src, dst, "ModKeyMap.EnableNativeQuickRetry", "GameSystem.QuickRetry"); - if (src.TryGetValue("ModKeyMap.TestMode", out var testMode) && - testMode != "" && - testMode != "Test") - { - dst.SetValue("DeprecationWarning.v1_0_ModKeyMap_TestMode", true); - } - MapBooleanTrueToSectionEnable(src, dst, "ModKeyMap.TestModeLongPress", "GameSystem.TestProof"); - - // WindowState - if (src.GetValueOrDefault("WindowState.Enable")) - { - MapValueOrDefaultToEntryValue(src, dst, "WindowState.Windowed", "GameSystem.Window.Windowed", false); - MapValueOrDefaultToEntryValue(src, dst, "WindowState.Width", "GameSystem.Window.Width", 0); - MapValueOrDefaultToEntryValue(src, dst, "WindowState.Height", "GameSystem.Window.Height", 0); - } - - // CustomCameraId - if (src.GetValueOrDefault("CustomCameraId.Enable")) - { - dst.EnsureDictionary("GameSystem.CustomCameraId"); - MapValueToEntryValueIfNonNullOrDefault(src, dst, "CustomCameraId.PrintCameraList", "GameSystem.CustomCameraId.PrintCameraList", false); - MapValueToEntryValueIfNonNullOrDefault(src, dst, "CustomCameraId.LeftQrCamera", "GameSystem.CustomCameraId.LeftQrCamera", 0); - MapValueToEntryValueIfNonNullOrDefault(src, dst, "CustomCameraId.RightQrCamera", "GameSystem.CustomCameraId.RightQrCamera", 0); - MapValueToEntryValueIfNonNullOrDefault(src, dst, "CustomCameraId.PhotoCamera", "GameSystem.CustomCameraId.PhotoCamera", 0); - MapValueToEntryValueIfNonNullOrDefault(src, dst, "CustomCameraId.ChimeCamera", "GameSystem.CustomCameraId.ChimeCamera", 0); - } - - // TouchSensitivity - if (src.GetValueOrDefault("TouchSensitivity.Enable")) - { - dst.EnsureDictionary("GameSettings.TouchSensitivity"); - var areas = new[] - { - "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", - "B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8", - "C1", "C2", - "D1", "D2", "D3", "D4", "D5", "D6", "D7", "D8", - "E1", "E2", "E3", "E4", "E5", "E6", "E7", "E8", - }; - foreach (var area in areas) - { - MapValueToEntryValueIfNonNull(src, dst, $"TouchSensitivity.{area}", $"GameSettings.TouchSensitivity.{area}"); - } - } - - // CustomKeyMap - if (src.GetValueOrDefault("CustomKeyMap.Enable")) - { - dst.EnsureDictionary("GameSystem.KeyMap"); - var keys = new[] - { - "Test", "Service", - "Button1_1P", "Button3_1P", "Button4_1P", "Button2_1P", "Button5_1P", "Button6_1P", "Button7_1P", "Button8_1P", - "Select_1P", - "Button1_2P", "Button2_2P", "Button3_2P", "Button4_2P", "Button5_2P", "Button6_2P", "Button7_2P", "Button8_2P", - "Select_2P" - }; - foreach (var key in keys) - { - if (src.TryGetValue($"CustomKeyMap.{key}", out var value) && - Enum.TryParse(value, out var keyCode)) - { - dst.SetValue($"GameSystem.KeyMap.{key}", keyCode.ToString()); - } - } - } - - // MaimaiDX2077 (WTF is the name?) - MapBooleanTrueToSectionEnable(src, dst, "MaimaiDX2077.CustomNoteTypePatch", "Fancy.GamePlay.CustomNoteTypes"); - - // Default enabled in V2 - dst.EnsureDictionary("GameSystem.RemoveEncryption"); - dst.EnsureDictionary("GameSettings.ForceAsServer"); - - return dst; - } - - // An value in the old config maps to an entry value in the new config. - // Any existing value, including zero, is valid. - private void MapValueToEntryValueIfNonNull(IConfigView src, ConfigView dst, string srcKey, string dstKey) - { - if (src.TryGetValue(srcKey, out var value)) - { - dst.SetValue(dstKey, value); - } - } - - // An value in the old config maps to an entry value in the new config. - // Null or default value is ignored. - private void MapValueToEntryValueIfNonNullOrDefault(IConfigView src, ConfigView dst, string srcKey, string dstKey, T defaultValue) - { - if (src.TryGetValue(srcKey, out var value) && !EqualityComparer.Default.Equals(value, defaultValue)) - { - dst.SetValue(dstKey, value); - } - } - - // An value in the old config maps to an entry value in the new config. - // Null value is replaced with a default value. - private void MapValueOrDefaultToEntryValue(IConfigView src, ConfigView dst, string srcKey, string dstKey, T defaultValue) - { - if (src.TryGetValue(srcKey, out var value)) - { - dst.SetValue(dstKey, value); - } - else - { - dst.SetValue(dstKey, defaultValue); - } - } - - // An boolean value in the old config maps to a default-off section's enable in the new config. - private void MapBooleanTrueToSectionEnable(IConfigView src, ConfigView dst, string srcKey, string dstKey) - { - if (src.GetValueOrDefault(srcKey)) - { - dst.EnsureDictionary(dstKey); - } - } -} diff --git a/AquaMai/AquaMai.Config/Migration/ConfigMigration_V2_0_V2_1.cs b/AquaMai/AquaMai.Config/Migration/ConfigMigration_V2_0_V2_1.cs deleted file mode 100644 index e41058ad..00000000 --- a/AquaMai/AquaMai.Config/Migration/ConfigMigration_V2_0_V2_1.cs +++ /dev/null @@ -1,44 +0,0 @@ -using AquaMai.Config.Interfaces; -using Tomlet.Models; - -namespace AquaMai.Config.Migration; - -public class ConfigMigration_V2_0_V2_1 : IConfigMigration -{ - public string FromVersion => "2.0"; - public string ToVersion => "2.1"; - - public IConfigView Migrate(IConfigView src) - { - var dst = src.Clone(); - dst.SetValue("Version", ToVersion); - - if (IsSectionEnabled(src, "Tweaks.ResetTouchAfterTrack")) - { - dst.Remove("Tweaks.ResetTouchAfterTrack"); - dst.SetValue("Tweaks.ResetTouch.AfterTrack", true); - } - - return dst; - } - - public bool IsSectionEnabled(IConfigView src, string path) - { - if (src.TryGetValue(path, out object section)) - { - if (section is bool enabled) - { - return enabled; - } - else if (section is TomlTable table) - { - if (Utility.TomlTryGetValueCaseInsensitive(table, "Disabled", out var disabled)) - { - return !Utility.IsTrutyOrDefault(disabled); - } - return true; - } - } - return false; - } -} diff --git a/AquaMai/AquaMai.Config/Migration/IConfigMigration.cs b/AquaMai/AquaMai.Config/Migration/IConfigMigration.cs deleted file mode 100644 index 2c7c9d11..00000000 --- a/AquaMai/AquaMai.Config/Migration/IConfigMigration.cs +++ /dev/null @@ -1,10 +0,0 @@ -using AquaMai.Config.Interfaces; - -namespace AquaMai.Config.Migration; - -public interface IConfigMigration -{ - public string FromVersion { get; } - public string ToVersion { get; } - public IConfigView Migrate(IConfigView config); -} diff --git a/AquaMai/AquaMai.Config/Polyfills.cs b/AquaMai/AquaMai.Config/Polyfills.cs deleted file mode 100644 index eb2da113..00000000 --- a/AquaMai/AquaMai.Config/Polyfills.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace System.Runtime.CompilerServices -{ - internal static class IsExternalInit {} -} diff --git a/AquaMai/AquaMai.Config/Reflection/MonoCecilAssemblyReflectionProvider.cs b/AquaMai/AquaMai.Config/Reflection/MonoCecilAssemblyReflectionProvider.cs deleted file mode 100644 index 69afece2..00000000 --- a/AquaMai/AquaMai.Config/Reflection/MonoCecilAssemblyReflectionProvider.cs +++ /dev/null @@ -1,254 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using AquaMai.Config.Attributes; -using AquaMai.Config.Interfaces; -using Mono.Cecil; -using Mono.Cecil.Cil; - -namespace AquaMai.Config.Reflection; - -public class MonoCecilReflectionProvider : IReflectionProvider -{ - public record ReflectionField( - string Name, - Type FieldType, - object Value, - IDictionary Attributes) : IReflectionField - { - public object Value { get; set; } = Value; - - public T GetCustomAttribute() where T : Attribute => Attributes.TryGetValue(typeof(T), out var value) ? (T)value : null; - public object GetValue(object obj) => Value; - public void SetValue(object obj, object value) => Value = value; - } - - public record ReflectionType( - string FullName, - string Namespace, - IReflectionField[] Fields, - IDictionary Attributes) : IReflectionType - { - public T GetCustomAttribute() where T : Attribute => Attributes.TryGetValue(typeof(T), out var value) ? (T)value : null; - public IReflectionField[] GetFields(BindingFlags bindingAttr) => Fields; - } - - private static readonly Type[] attributeTypes = - [ - typeof(ConfigCollapseNamespaceAttribute), - typeof(ConfigSectionAttribute), - typeof(ConfigEntryAttribute), - ]; - - private readonly IReflectionType[] reflectionTypes = []; - private readonly Dictionary> enums = []; - - public IReflectionType[] GetTypes() => reflectionTypes; - public Dictionary GetEnum(string enumName) => enums[enumName]; - - public MonoCecilReflectionProvider(AssemblyDefinition assembly) - { - reflectionTypes = assembly.MainModule.Types.Select(cType => { - var typeAttributes = InstantiateAttributes(cType.CustomAttributes); - var fields = cType.Fields.Select(cField => { - try - { - var fieldAttributes = InstantiateAttributes(cField.CustomAttributes); - if (fieldAttributes.Count == 0) - { - return null; - } - var type = GetRuntimeType(cField.FieldType); - var defaultValue = GetFieldDefaultValue(cType, cField, type); - return new ReflectionField(cField.Name, type, defaultValue, fieldAttributes); - } - catch (Exception e) - { - Console.WriteLine(e); - } - return null; - }).Where(field => field != null).ToArray(); - return new ReflectionType(cType.FullName, cType.Namespace, fields, typeAttributes); - }).ToArray(); - enums = assembly.MainModule.Types - .Where(cType => cType.IsEnum) - .ToDictionary(cType => - cType.FullName, - cType => cType.Fields - .Where(cField => cField.IsPublic && cField.IsStatic && cField.Constant != null) - .ToDictionary(cField => cField.Name, cField => cField.Constant)); - } - - private Dictionary InstantiateAttributes(ICollection attribute) => - attribute - .Select(InstantiateAttribute) - .Where(a => a != null) - .ToDictionary(a => a.GetType(), a => a); - - private object InstantiateAttribute(CustomAttribute attribute) => - attributeTypes.FirstOrDefault(t => t.FullName == attribute.AttributeType.FullName) switch - { - Type type => Activator.CreateInstance(type, - attribute.Constructor.Parameters - .Select((parameter, i) => - { - var runtimeType = GetRuntimeType(parameter.ParameterType); - var value = attribute.ConstructorArguments[i].Value; - if (runtimeType.IsEnum) - { - return Enum.Parse(runtimeType, value.ToString()); - } - return value; - }) - .ToArray()), - _ => null - }; - - private Type GetRuntimeType(TypeReference typeReference) { - if (typeReference.IsGenericInstance) - { - var genericInstance = (GenericInstanceType)typeReference; - var genericType = GetRuntimeType(genericInstance.ElementType); - var genericArguments = genericInstance.GenericArguments.Select(GetRuntimeType).ToArray(); - return genericType.MakeGenericType(genericArguments); - } - - var type = Type.GetType(typeReference.FullName); - if (type == null) - { - throw new TypeLoadException($"Type {typeReference.FullName} not found."); - } - return type; - } - - private static object GetFieldDefaultValue(TypeDefinition cType, FieldDefinition cField, Type fieldType) - { - object defaultValue = null; - var cctor = cType.Methods.SingleOrDefault(m => m.Name == ".cctor"); - if (cctor != null) - { - var store = cctor.Body.Instructions.SingleOrDefault(i => i.OpCode == OpCodes.Stsfld && i.Operand == cField); - if (store != null) - { - var loadOperand = ParseConstantLoadOperand(store.Previous); - if (fieldType == typeof(bool)) - { - defaultValue = Convert.ToBoolean(loadOperand); - } - else - { - defaultValue = loadOperand; - } - } - } - - if (defaultValue == null && cField.HasDefault) - { - throw new InvalidOperationException($"Field {cType.FullName}.{cField.Name} has default value but no .cctor stsfld instruction."); - } - defaultValue ??= GetDefaultValue(fieldType); - - if (fieldType.IsEnum) - { - var enumType = fieldType.GetEnumUnderlyingType(); - // Assume casting is safe since we're getting the default value from the field - var castedValue = Convert.ChangeType(defaultValue, enumType); - if (Enum.IsDefined(fieldType, castedValue)) - { - return Enum.ToObject(fieldType, castedValue); - } - } - - return defaultValue; - } - - private static object ParseConstantLoadOperand(Instruction instruction) - { - if (instruction.OpCode == OpCodes.Ldc_I4_M1) - { - return -1; - } - if (instruction.OpCode == OpCodes.Ldc_I4_0) - { - return 0; - } - if (instruction.OpCode == OpCodes.Ldc_I4_1) - { - return 1; - } - if (instruction.OpCode == OpCodes.Ldc_I4_2) - { - return 2; - } - if (instruction.OpCode == OpCodes.Ldc_I4_3) - { - return 3; - } - if (instruction.OpCode == OpCodes.Ldc_I4_4) - { - return 4; - } - if (instruction.OpCode == OpCodes.Ldc_I4_5) - { - return 5; - } - if (instruction.OpCode == OpCodes.Ldc_I4_6) - { - return 6; - } - if (instruction.OpCode == OpCodes.Ldc_I4_7) - { - return 7; - } - if (instruction.OpCode == OpCodes.Ldc_I4_8) - { - return 8; - } - if (instruction.OpCode == OpCodes.Ldc_I4_S) - { - return Convert.ToInt32((sbyte)instruction.Operand); - } - if (instruction.OpCode == OpCodes.Ldc_I4) - { - return (int)instruction.Operand; - } - if (instruction.OpCode == OpCodes.Ldc_I8) - { - return (long)instruction.Operand; - } - if (instruction.OpCode == OpCodes.Ldc_R4) - { - return (float)instruction.Operand; - } - if (instruction.OpCode == OpCodes.Ldc_R8) - { - return (double)instruction.Operand; - } - if (instruction.OpCode == OpCodes.Ldstr) - { - return (string)instruction.Operand; - } - else - { - var currentMethod = MethodBase.GetCurrentMethod(); - throw new NotImplementedException($"Unsupported constant load: {instruction}. Please implement in {currentMethod.DeclaringType.FullName}.{currentMethod.Name}"); - } - } - - private static object GetDefaultValue(Type type) - { - if (type.IsValueType) - { - return Activator.CreateInstance(type); - } - else if (type == typeof(string)) - { - return string.Empty; - } - else - { - return null; - } - } -} diff --git a/AquaMai/AquaMai.Config/Reflection/ReflectionManager.cs b/AquaMai/AquaMai.Config/Reflection/ReflectionManager.cs deleted file mode 100644 index 7fb1ae0a..00000000 --- a/AquaMai/AquaMai.Config/Reflection/ReflectionManager.cs +++ /dev/null @@ -1,178 +0,0 @@ -using System.Reflection; -using System.Linq; -using System.Collections.Generic; -using AquaMai.Config.Attributes; -using AquaMai.Config.Interfaces; -using System; - -namespace AquaMai.Config.Reflection; - -public class ReflectionManager : IReflectionManager -{ - public record Entry : IReflectionManager.IEntry - { - public string Path { get; init; } - public string Name { get; init; } - public IReflectionField Field { get; init; } - public IConfigEntryAttribute Attribute { get; init; } - } - - public record Section : IReflectionManager.ISection - { - public string Path { get; init; } - public IReflectionType Type { get; init; } - public IConfigSectionAttribute Attribute { get; init; } - public List entries; - public List Entries => entries.Cast().ToList(); - } - - private readonly Dictionary sections = new(StringComparer.OrdinalIgnoreCase); - private readonly Dictionary entries = new(StringComparer.OrdinalIgnoreCase); - private readonly Dictionary sectionsByFullName = []; - - public ReflectionManager(IReflectionProvider reflectionProvider) - { - var prefix = "AquaMai.Mods."; - var types = reflectionProvider.GetTypes().Where(t => t.FullName.StartsWith(prefix)); - var collapsedNamespaces = new HashSet(); - foreach (var type in types) - { - var sectionAttribute = type.GetCustomAttribute(); - if (sectionAttribute == null) continue; - if (collapsedNamespaces.Contains(type.Namespace)) - { - throw new Exception($"Collapsed namespace {type.Namespace} contains multiple sections"); - } - var path = type.FullName.Substring(prefix.Length); - if (type.GetCustomAttribute() != null) - { - var separated = path.Split('.'); - if (separated[separated.Length - 2] != separated[separated.Length - 1]) - { - throw new Exception($"Type {type.FullName} is not collapsable"); - } - path = string.Join(".", separated.Take(separated.Length - 1)); - collapsedNamespaces.Add(type.Namespace); - } - - var sectionEntries = new List(); - foreach (var field in type.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) - { - var entryAttribute = field.GetCustomAttribute(); - if (entryAttribute == null) continue; - var transformedName = Utility.ToPascalCase(field.Name); - var entryPath = $"{path}.{transformedName}"; - var entry = new Entry() - { - Path = entryPath, - Name = transformedName, - Field = field, - Attribute = entryAttribute - }; - sectionEntries.Add(entry); - entries.Add(entryPath, entry); - } - - var section = new Section() - { - Path = path, - Type = type, - Attribute = sectionAttribute, - entries = sectionEntries - }; - sections.Add(path, section); - sectionsByFullName.Add(type.FullName, section); - } - - var order = reflectionProvider.GetEnum("AquaMai.Mods.SectionNameOrder"); - sections = sections - .OrderBy(x => x.Key) - .OrderBy(x => - { - var parts = x.Key.Split('.'); - for (int i = parts.Length; i > 0; i--) - { - var key = string.Join("_", parts.Take(i)); - if (order.TryGetValue(key, out var value)) - { - return (int)value; - } - } - Utility.Log($"Section {x.Key} has no order defined, defaulting to int.MaxValue"); - return int.MaxValue; - }) - .ToDictionary(x => x.Key, x => x.Value, StringComparer.OrdinalIgnoreCase); - } - - public IEnumerable
SectionValues => sections.Values; - public IEnumerable Sections => sections.Values.Cast(); - - public IEnumerable EntryValues => entries.Values; - public IEnumerable Entries => entries.Values.Cast(); - - public bool ContainsSection(string path) - { - return sections.ContainsKey(path); - } - - public bool TryGetSection(string path, out IReflectionManager.ISection section) - { - if (sections.TryGetValue(path, out var sectionValue)) - { - section = sectionValue; - return true; - } - section = null; - return false; - } - - public bool TryGetSection(Type type, out IReflectionManager.ISection section) - { - bool result = sectionsByFullName.TryGetValue(type.FullName, out var sectionValue); - section = sectionValue; - return result; - } - - public IReflectionManager.ISection GetSection(string path) - { - if (!TryGetSection(path, out var section)) - { - throw new KeyNotFoundException($"Section {path} not found"); - } - return section; - } - - public IReflectionManager.ISection GetSection(Type type) - { - if (!TryGetSection(type.FullName, out var section)) - { - throw new KeyNotFoundException($"Section {type.FullName} not found"); - } - return section; - } - - public bool ContainsEntry(string path) - { - return entries.ContainsKey(path); - } - - public bool TryGetEntry(string path, out IReflectionManager.IEntry entry) - { - if (entries.TryGetValue(path, out var entryValue)) - { - entry = entryValue; - return true; - } - entry = null; - return false; - } - - public IReflectionManager.IEntry GetEntry(string path) - { - if (!TryGetEntry(path, out var entry)) - { - throw new KeyNotFoundException($"Entry {path} not found"); - } - return entry; - } -} diff --git a/AquaMai/AquaMai.Config/Reflection/SystemReflectionProvider.cs b/AquaMai/AquaMai.Config/Reflection/SystemReflectionProvider.cs deleted file mode 100644 index fe03a0de..00000000 --- a/AquaMai/AquaMai.Config/Reflection/SystemReflectionProvider.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using AquaMai.Config.Interfaces; - -namespace AquaMai.Config.Reflection; - -public class SystemReflectionProvider(Assembly assembly) : IReflectionProvider -{ - public class ReflectionField(FieldInfo field) : IReflectionField - { - public FieldInfo UnderlyingField { get; } = field; - - public string Name => UnderlyingField.Name; - public Type FieldType => UnderlyingField.FieldType; - public T GetCustomAttribute() where T : Attribute => UnderlyingField.GetCustomAttribute(); - public object GetValue(object obj) => UnderlyingField.GetValue(obj); - public void SetValue(object obj, object value) => UnderlyingField.SetValue(obj, value); - } - - public class ReflectionType(Type type) : IReflectionType - { - public Type UnderlyingType { get; } = type; - - public string FullName => UnderlyingType.FullName; - public string Namespace => UnderlyingType.Namespace; - public T GetCustomAttribute() where T : Attribute => UnderlyingType.GetCustomAttribute(); - public IReflectionField[] GetFields(BindingFlags bindingAttr) => Array.ConvertAll(UnderlyingType.GetFields(bindingAttr), f => new ReflectionField(f)); - } - - public Assembly UnderlyingAssembly { get; } = assembly; - - public IReflectionType[] GetTypes() => Array.ConvertAll(UnderlyingAssembly.GetTypes(), t => new ReflectionType(t)); - - public Dictionary GetEnum(string enumName) - { - var enumType = UnderlyingAssembly.GetType(enumName); - if (enumType == null) return null; - var enumValues = Enum.GetValues(enumType); - var enumDict = new Dictionary(); - foreach (var enumValue in enumValues) - { - enumDict.Add(enumValue.ToString(), enumValue); - } - return enumDict; - } -} diff --git a/AquaMai/AquaMai.Config/Types/KeyCodeID.cs b/AquaMai/AquaMai.Config/Types/KeyCodeID.cs deleted file mode 100644 index f1c7615c..00000000 --- a/AquaMai/AquaMai.Config/Types/KeyCodeID.cs +++ /dev/null @@ -1,146 +0,0 @@ -namespace AquaMai.Config.Types; - -public enum KeyCodeID -{ - None, - Backspace, - Tab, - Clear, - Return, - Pause, - Escape, - Space, - Exclaim, - DoubleQuote, - Hash, - Dollar, - Ampersand, - Quote, - LeftParen, - RightParen, - Asterisk, - Plus, - Comma, - Minus, - Period, - Slash, - Alpha0, - Alpha1, - Alpha2, - Alpha3, - Alpha4, - Alpha5, - Alpha6, - Alpha7, - Alpha8, - Alpha9, - Colon, - Semicolon, - Less, - Equals, - Greater, - Question, - At, - LeftBracket, - Backslash, - RightBracket, - Caret, - Underscore, - BackQuote, - A, - B, - C, - D, - E, - F, - G, - H, - I, - J, - K, - L, - M, - N, - O, - P, - Q, - R, - S, - T, - U, - V, - W, - X, - Y, - Z, - Delete, - Keypad0, - Keypad1, - Keypad2, - Keypad3, - Keypad4, - Keypad5, - Keypad6, - Keypad7, - Keypad8, - Keypad9, - KeypadPeriod, - KeypadDivide, - KeypadMultiply, - KeypadMinus, - KeypadPlus, - KeypadEnter, - KeypadEquals, - UpArrow, - DownArrow, - RightArrow, - LeftArrow, - Insert, - Home, - End, - PageUp, - PageDown, - F1, - F2, - F3, - F4, - F5, - F6, - F7, - F8, - F9, - F10, - F11, - F12, - F13, - F14, - F15, - Numlock, - CapsLock, - ScrollLock, - RightShift, - LeftShift, - RightControl, - LeftControl, - RightAlt, - LeftAlt, - RightCommand, - RightApple, - LeftCommand, - LeftApple, - LeftWindows, - RightWindows, - AltGr, - Help, - Print, - SysReq, - Break, - Menu, - Mouse0, - Mouse1, - Mouse2, - Mouse3, - Mouse4, - Mouse5, - Mouse6, -} diff --git a/AquaMai/AquaMai.Config/Types/KeyCodeOrName.cs b/AquaMai/AquaMai.Config/Types/KeyCodeOrName.cs deleted file mode 100644 index 400de783..00000000 --- a/AquaMai/AquaMai.Config/Types/KeyCodeOrName.cs +++ /dev/null @@ -1,53 +0,0 @@ -namespace AquaMai.Config.Types; - -public enum KeyCodeOrName -{ - None, - Alpha0, - Alpha1, - Alpha2, - Alpha3, - Alpha4, - Alpha5, - Alpha6, - Alpha7, - Alpha8, - Alpha9, - Keypad0, - Keypad1, - Keypad2, - Keypad3, - Keypad4, - Keypad5, - Keypad6, - Keypad7, - Keypad8, - Keypad9, - F1, - F2, - F3, - F4, - F5, - F6, - F7, - F8, - F9, - F10, - F11, - F12, - Insert, - Delete, - Home, - End, - PageUp, - PageDown, - UpArrow, - DownArrow, - LeftArrow, - RightArrow, - - Select1P, - Select2P, - Service, - Test, -} diff --git a/AquaMai/AquaMai.Config/Utility.cs b/AquaMai/AquaMai.Config/Utility.cs deleted file mode 100644 index 73af4f18..00000000 --- a/AquaMai/AquaMai.Config/Utility.cs +++ /dev/null @@ -1,193 +0,0 @@ -using System; -using System.Reflection; -using Tomlet.Models; - -namespace AquaMai.Config; - -public static class Utility -{ - public static Action LogFunction = Console.WriteLine; - - public static bool IsTruty(TomlValue value, string path = null) - { - return value switch - { - TomlBoolean boolean => boolean.Value, - TomlLong @long => @long.Value != 0, - _ => throw new ArgumentException( - path == null - ? $"Non-boolish TOML type {value.GetType().Name} value: {value}" - : $"When parsing {path}, got non-boolish TOML type {value.GetType().Name} value: {value}") - }; - } - - public static bool IsTrutyOrDefault(TomlValue value, bool defaultValue = false) - { - return value switch - { - TomlBoolean boolean => boolean.Value, - TomlLong @long => @long.Value != 0, - _ => defaultValue - }; - } - - public static bool IsIntegerType(Type type) - { - return type == typeof(sbyte) || type == typeof(short) || type == typeof(int) || type == typeof(long) - || type == typeof(byte) || type == typeof(ushort) || type == typeof(uint) || type == typeof(ulong); - } - - public static bool IsFloatType(Type type) - { - return type == typeof(float) || type == typeof(double); - } - - public static bool IsNumberType(Type type) - { - return IsIntegerType(type) || IsFloatType(type); - } - - public static T ParseTomlValue(TomlValue value) - { - return (T)ParseTomlValue(typeof(T), value); - } - - public static object ParseTomlValue(Type type, TomlValue value) - { - if (type == typeof(bool)) - { - return IsTruty(value); - } - else if (IsNumberType(type)) - { - if (TryGetTomlNumberObject(value, out var numberObject)) - { - return Convert.ChangeType(numberObject, type); - } - else - { - throw new InvalidCastException($"Non-number TOML type: {value.GetType().Name}"); - } - } - else if (type == typeof(string)) - { - if (value is TomlString @string) - { - return @string.Value; - } - else - { - throw new InvalidCastException($"Non-string TOML type: {value.GetType().Name}"); - } - } - else if (type.IsEnum) - { - if (value is TomlString @string) - { - try - { - return Enum.Parse(type, @string.Value); - } - catch - { - throw new InvalidCastException($"Invalid enum {type.FullName} value: {@string.SerializedValue}"); - } - } - else if (value is TomlLong @long) - { - if (Enum.IsDefined(type, @long.Value)) - { - try - { - return Enum.ToObject(type, @long.Value); - } - catch - {} - } - throw new InvalidCastException($"Invalid enum {type.FullName} value: {@long.Value}"); - } - else - { - throw new InvalidCastException($"Non-enum TOML type: {value.GetType().Name}"); - } - } - else - { - var currentMethod = MethodBase.GetCurrentMethod(); - throw new NotImplementedException($"Unsupported config entry type: {type.FullName}. Please implement in {currentMethod.DeclaringType.FullName}.{currentMethod.Name}"); - } - } - - private static bool TryGetTomlNumberObject(TomlValue value, out object numberObject) - { - if (value is TomlLong @long) - { - numberObject = @long.Value; - return true; - } - else if (value is TomlDouble @double) - { - numberObject = @double.Value; - return true; - } - else - { - numberObject = null; - return false; - } - } - - public static bool TomlTryGetValueCaseInsensitive(TomlTable table, string key, out TomlValue value) - { - // Prefer exact match - if (table.TryGetValue(key, out value)) - { - return true; - } - // Fallback to case-insensitive match - foreach (var kvp in table) - { - if (string.Equals(kvp.Key, key, StringComparison.OrdinalIgnoreCase)) - { - value = kvp.Value; - return true; - } - } - value = null; - return false; - } - - public static bool TomlContainsKeyCaseInsensitive(TomlTable table, string key) - { - // Prefer exact match - if (table.ContainsKey(key)) - { - return true; - } - // Fallback to case-insensitive match - foreach (var kvp in table) - { - if (string.Equals(kvp.Key, key, StringComparison.OrdinalIgnoreCase)) - { - return true; - } - } - return false; - } - - public static string ToPascalCase(string str) - { - return str.Length switch - { - 0 => str, - 1 => char.ToUpperInvariant(str[0]).ToString(), - _ => char.ToUpperInvariant(str[0]) + str.Substring(1) - }; - } - - // We can test the configuration related code without loading the mod into the game. - public static void Log(string message) - { - LogFunction(message); - } -} diff --git a/AquaMai/AquaMai.Core/AquaMai.Core.csproj b/AquaMai/AquaMai.Core/AquaMai.Core.csproj deleted file mode 100644 index 5a2a9837..00000000 --- a/AquaMai/AquaMai.Core/AquaMai.Core.csproj +++ /dev/null @@ -1,132 +0,0 @@ - - - - Release - AnyCPU - {33C0D4ED-6A84-4659-9A05-12D43D75D0B3} - Library - AquaMai.Core - AquaMai.Core - net472 - 512 - true - 12 - 414 - $(ProjectDir)../Libs/;$(AssemblySearchPaths) - $(ProjectDir)../Output/ - false - false - - - - false - None - true - prompt - 4 - true - false - - - - DEBUG - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PublicResXFileCodeGenerator - Locale.Designer.cs - - - Locale.resx - - - - diff --git a/AquaMai/AquaMai.Core/Attributes/EnableGameVersionAttribute.cs b/AquaMai/AquaMai.Core/Attributes/EnableGameVersionAttribute.cs deleted file mode 100644 index 97191007..00000000 --- a/AquaMai/AquaMai.Core/Attributes/EnableGameVersionAttribute.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; - -namespace AquaMai.Core.Attributes; - -[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] -public class EnableGameVersionAttribute(uint minVersion = 0, uint maxVersion = 0, bool noWarn = false) : Attribute -{ - public uint MinVersion { get; } = minVersion; - public uint MaxVersion { get; } = maxVersion; - public bool NoWarn { get; } = noWarn; - - public bool ShouldEnable(uint gameVersion) - { - if (MinVersion > 0 && MinVersion > gameVersion) return false; - if (MaxVersion > 0 && MaxVersion < gameVersion) return false; - return true; - } -} diff --git a/AquaMai/AquaMai.Core/Attributes/EnableIfAttribute.cs b/AquaMai/AquaMai.Core/Attributes/EnableIfAttribute.cs deleted file mode 100644 index 43f3cc8f..00000000 --- a/AquaMai/AquaMai.Core/Attributes/EnableIfAttribute.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System; - -namespace AquaMai.Core.Attributes; - -public enum EnableConditionOperator -{ - Equal, - NotEqual, - GreaterThan, - LessThan, - GreaterThanOrEqual, - LessThanOrEqual -} - -[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] -public class EnableIfAttribute( - Type referenceType, - string referenceMember, - EnableConditionOperator @operator, - object rightSideValue) : Attribute -{ - public Type ReferenceType { get; } = referenceType; - public string ReferenceMember { get; } = referenceMember; - public EnableConditionOperator Operator { get; } = @operator; - public object RightSideValue { get; } = rightSideValue; - - // Referencing a field in another class and checking if it's true. - public EnableIfAttribute(Type referenceType, string referenceMember) - : this(referenceType, referenceMember, EnableConditionOperator.Equal, true) - { } - - // Referencing a field in the same class and comparing it with a value. - public EnableIfAttribute(string referenceMember, EnableConditionOperator condition, object value) - : this(null, referenceMember, condition, value) - { } - - // Referencing a field in the same class and checking if it's true. - public EnableIfAttribute(string referenceMember) - : this(referenceMember, EnableConditionOperator.Equal, true) - { } - - public bool ShouldEnable(Type selfType) - { - var referenceType = ReferenceType ?? selfType; - var referenceField = referenceType.GetField( - ReferenceMember, - System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic); - var referenceProperty = referenceType.GetProperty( - ReferenceMember, - System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic); - if (referenceField == null && referenceProperty == null) - { - throw new ArgumentException($"Field or property {ReferenceMember} not found in {referenceType.FullName}"); - } - var referenceMemberValue = referenceField != null ? referenceField.GetValue(null) : referenceProperty.GetValue(null); - switch (Operator) - { - case EnableConditionOperator.Equal: - return referenceMemberValue.Equals(RightSideValue); - case EnableConditionOperator.NotEqual: - return !referenceMemberValue.Equals(RightSideValue); - case EnableConditionOperator.GreaterThan: - case EnableConditionOperator.LessThan: - case EnableConditionOperator.GreaterThanOrEqual: - case EnableConditionOperator.LessThanOrEqual: - var comparison = (IComparable)referenceMemberValue; - return Operator switch - { - EnableConditionOperator.GreaterThan => comparison.CompareTo(RightSideValue) > 0, - EnableConditionOperator.LessThan => comparison.CompareTo(RightSideValue) < 0, - EnableConditionOperator.GreaterThanOrEqual => comparison.CompareTo(RightSideValue) >= 0, - EnableConditionOperator.LessThanOrEqual => comparison.CompareTo(RightSideValue) <= 0, - _ => throw new NotImplementedException(), - }; - default: - throw new NotImplementedException(); - } - } -} diff --git a/AquaMai/AquaMai.Core/Attributes/EnableImplicitlyIfAttribute.cs b/AquaMai/AquaMai.Core/Attributes/EnableImplicitlyIfAttribute.cs deleted file mode 100644 index 71fb02bd..00000000 --- a/AquaMai/AquaMai.Core/Attributes/EnableImplicitlyIfAttribute.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace AquaMai.Core.Attributes; - -// If the field or property with this name is true, the patch will be implicitly enabled, regardless of the config state. -// This is handled outside the config module, while The config state won't be actually set to enabled by it. -// Won't bypass the restriction of [EnableIf()] and [EnableGameVersion()]. -[AttributeUsage(AttributeTargets.Class)] -public class EnableImplicitlyIf(string memberName) : Attribute -{ - public string MemberName { get; } = memberName; -} diff --git a/AquaMai/AquaMai.Core/ConfigLoader.cs b/AquaMai/AquaMai.Core/ConfigLoader.cs deleted file mode 100644 index e87088fc..00000000 --- a/AquaMai/AquaMai.Core/ConfigLoader.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using System.Reflection; -using MelonLoader; -using AquaMai.Config; -using AquaMai.Config.Interfaces; -using AquaMai.Config.Migration; - -namespace AquaMai.Core; - -public static class ConfigLoader -{ - private static string ConfigFile => "AquaMai.toml"; - private static string ConfigExampleFile(string lang) => $"AquaMai.{lang}.toml"; - private static string OldConfigFile(string version) => $"AquaMai.toml.old-v{version}."; - - private static Config.Config config; - - public static Config.Config Config => config; - - public static bool LoadConfig(Assembly modsAssembly) - { - Utility.LogFunction = MelonLogger.Msg; - - config = new( - new Config.Reflection.ReflectionManager( - new Config.Reflection.SystemReflectionProvider(modsAssembly))); - - if (!File.Exists(ConfigFile)) - { - var examples = GenerateExamples(); - foreach (var (lang, example) in examples) - { - var filename = ConfigExampleFile(lang); - File.WriteAllText(filename, example); - } - MelonLogger.Error("======================================!!!"); - MelonLogger.Error("AquaMai.toml not found! Please create it."); - MelonLogger.Error("找不到配置文件 AquaMai.toml!请创建。"); - MelonLogger.Error("Example copied to AquaMai.en.toml"); - MelonLogger.Error("示例已复制到 AquaMai.zh.toml"); - MelonLogger.Error("========================================="); - return false; - } - - var configText = File.ReadAllText(ConfigFile); - var configView = new ConfigView(configText); - var configVersion = ConfigMigrationManager.Instance.GetVersion(configView); - if (configVersion != ConfigMigrationManager.Instance.LatestVersion) - { - File.WriteAllText(OldConfigFile(configVersion), configText); - configView = (ConfigView)ConfigMigrationManager.Instance.Migrate(configView); - } - - // Read AquaMai.toml to load settings - ConfigParser.Instance.Parse(config, configView); - - return true; - } - - public static void SaveConfig(string lang) - { - File.WriteAllText(ConfigFile, SerailizeCurrentConfig(lang)); - } - - private static string SerailizeCurrentConfig(string lang) => - new ConfigSerializer(new IConfigSerializer.Options() - { - Lang = lang, - IncludeBanner = true, - OverrideLocaleValue = true - }).Serialize(config); - - private static IDictionary GenerateExamples() - { - var examples = new Dictionary(); - foreach (var lang in (string[]) ["en", "zh"]) - { - examples[lang] = SerailizeCurrentConfig(lang); - } - return examples; - } -} diff --git a/AquaMai/AquaMai.Core/Helpers/EnableConditionHelper.cs b/AquaMai/AquaMai.Core/Helpers/EnableConditionHelper.cs deleted file mode 100644 index 475c24ae..00000000 --- a/AquaMai/AquaMai.Core/Helpers/EnableConditionHelper.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; -using System.Collections; -using System.Reflection; -using AquaMai.Core.Attributes; -using AquaMai.Core.Resources; -using HarmonyLib; -using MelonLoader; - -namespace AquaMai.Core.Helpers; - -public class EnableConditionHelper -{ - [HarmonyPostfix] - [HarmonyPatch("HarmonyLib.PatchTools", "GetPatchMethod")] - public static void PostGetPatchMethod(ref MethodInfo __result) - { - if (__result != null) - { - if (ShouldSkipMethodOrClass(__result.GetCustomAttribute, __result.ReflectedType, __result.Name)) - { - __result = null; - } - } - } - - [HarmonyPostfix] - [HarmonyPatch("HarmonyLib.PatchTools", "GetPatchMethods")] - public static void PostGetPatchMethods(ref IList __result) - { - for (int i = 0; i < __result.Count; i++) - { - var harmonyMethod = Traverse.Create(__result[i]).Field("info").GetValue() as HarmonyMethod; - var method = harmonyMethod.method; - if (ShouldSkipMethodOrClass(method.GetCustomAttribute, method.ReflectedType, method.Name)) - { - __result.RemoveAt(i); - i--; - } - } - } - - public static bool ShouldSkipClass(Type type) - { - return ShouldSkipMethodOrClass(type.GetCustomAttribute, type); - } - - private static bool ShouldSkipMethodOrClass(Func getCustomAttribute, Type type, string methodName = "") - { - var displayName = type.FullName + (string.IsNullOrEmpty(methodName) ? "" : $".{methodName}"); - var enableIf = (EnableIfAttribute)getCustomAttribute(typeof(EnableIfAttribute)); - if (enableIf != null && !enableIf.ShouldEnable(type)) - { -# if DEBUG - MelonLogger.Msg($"Skipping {displayName} due to EnableIf condition"); -# endif - return true; - } - var enableGameVersion = (EnableGameVersionAttribute)getCustomAttribute(typeof(EnableGameVersionAttribute)); - if (enableGameVersion != null && !enableGameVersion.ShouldEnable(GameInfo.GameVersion)) - { -# if DEBUG - MelonLogger.Msg($"Skipping {displayName} due to EnableGameVersion condition"); -# endif - if (!enableGameVersion.NoWarn) - { - MelonLogger.Warning(string.Format(Locale.SkipIncompatiblePatch, type)); - } - return true; - } - return false; - } -} diff --git a/AquaMai/AquaMai.Core/Helpers/FileSystem.cs b/AquaMai/AquaMai.Core/Helpers/FileSystem.cs deleted file mode 100644 index 3eab6f0b..00000000 --- a/AquaMai/AquaMai.Core/Helpers/FileSystem.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.IO; - -namespace AquaMai.Core.Helpers; - -public static class FileSystem -{ - public static string ResolvePath(string path) - { - var varExpanded = Environment.ExpandEnvironmentVariables(path); - return Path.IsPathRooted(varExpanded) - ? varExpanded - : Path.Combine(Environment.CurrentDirectory, varExpanded); - } -} diff --git a/AquaMai/AquaMai.Core/Helpers/GameInfo.cs b/AquaMai/AquaMai.Core/Helpers/GameInfo.cs deleted file mode 100644 index 117f724b..00000000 --- a/AquaMai/AquaMai.Core/Helpers/GameInfo.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Reflection; -using MAI2System; - -namespace AquaMai.Core.Helpers; - -public class GameInfo -{ - public static uint GameVersion { get; } = GetGameVersion(); - - private static uint GetGameVersion() - { - return (uint)typeof(ConstParameter).GetField("NowGameVersion", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy).GetValue(null); - } - - public static string GameId { get; } = GetGameId(); - - private static string GetGameId() - { - return typeof(ConstParameter).GetField("GameIDStr", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy).GetValue(null) as string; - } -} diff --git a/AquaMai/AquaMai.Core/Helpers/GuiSizes.cs b/AquaMai/AquaMai.Core/Helpers/GuiSizes.cs deleted file mode 100644 index 30efa7b2..00000000 --- a/AquaMai/AquaMai.Core/Helpers/GuiSizes.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using HarmonyLib; -using UnityEngine; - -namespace AquaMai.Core.Helpers; - -public static class GuiSizes -{ - public static bool SinglePlayer { get; set; } = false; - public static float PlayerWidth => Screen.height / 1920f * 1080; - public static float PlayerCenter => SinglePlayer ? Screen.width / 2f : Screen.width / 2f - PlayerWidth / 2; - public static int FontSize => (int)(PlayerWidth * .015f); - public static float LabelHeight => FontSize * 1.5f; - public static float Margin => PlayerWidth * .005f; - - private static Color backgroundColor = new(147 / 256f, 160 / 256f, 173 / 256f, .8f); - - public static void SetupStyles() - { - var buttonStyle = GUI.skin.button; - buttonStyle.normal.textColor = Color.white; - buttonStyle.normal.background = Texture2D.whiteTexture; - buttonStyle.hover.background = Texture2D.whiteTexture; - buttonStyle.active.background = Texture2D.whiteTexture; - buttonStyle.border = new RectOffset(0, 0, 0, 0); - buttonStyle.margin = new RectOffset(0, 0, 0, 0); - buttonStyle.padding = new RectOffset(10, 10, 10, 10); - buttonStyle.overflow = new RectOffset(0, 0, 0, 0); - - var boxStyle = GUI.skin.box; - boxStyle.border = new RectOffset(0, 0, 0, 0); - boxStyle.normal.background = Texture2D.whiteTexture; - - GUI.backgroundColor = backgroundColor; - } - - [HarmonyPatch] - public class BoxBackground - { - public static IEnumerable TargetMethods() - { - return typeof(GUI).GetMethods().Where(x => x.Name == "Box"); - } - - public static void Prefix() - { - GUI.backgroundColor = new Color(62 / 256f, 62 / 256f, 66 / 256f, .6f); - } - - public static void Postfix() - { - GUI.backgroundColor = backgroundColor; - } - } -} diff --git a/AquaMai/AquaMai.Core/Helpers/KeyListener.cs b/AquaMai/AquaMai.Core/Helpers/KeyListener.cs deleted file mode 100644 index 0215dd4d..00000000 --- a/AquaMai/AquaMai.Core/Helpers/KeyListener.cs +++ /dev/null @@ -1,146 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using AquaMai.Config.Types; -using HarmonyLib; -using Main; -using Manager; -using MelonLoader; -using UnityEngine; - -namespace AquaMai.Core.Helpers; - -public static class KeyListener -{ - private static readonly Dictionary _keyPressFrames = []; - private static readonly Dictionary _keyPressFramesPrev = []; - - static KeyListener() - { - foreach (KeyCodeOrName key in Enum.GetValues(typeof(KeyCodeOrName))) - { - _keyPressFrames[key] = 0; - _keyPressFramesPrev[key] = 0; - } - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(GameMainObject), "Update")] - public static void CheckLongPush() - { - foreach (KeyCodeOrName key in Enum.GetValues(typeof(KeyCodeOrName))) - { - _keyPressFramesPrev[key] = _keyPressFrames[key]; - if (GetKeyPush(key)) - { -# if DEBUG - MelonLogger.Msg($"CheckLongPush {key} is push {_keyPressFrames[key]}"); -# endif - _keyPressFrames[key]++; - } - else - { - _keyPressFrames[key] = 0; - } - } - } - - public static bool GetKeyPush(KeyCodeOrName key) => - key switch - { - KeyCodeOrName.None => false, - < KeyCodeOrName.Select1P => Input.GetKey(key.GetKeyCode()), - KeyCodeOrName.Test => InputManager.GetSystemInputPush(InputManager.SystemButtonSetting.ButtonTest), - KeyCodeOrName.Service => InputManager.GetSystemInputPush(InputManager.SystemButtonSetting.ButtonService), - KeyCodeOrName.Select1P => InputManager.GetButtonPush(0, InputManager.ButtonSetting.Select), - KeyCodeOrName.Select2P => InputManager.GetButtonPush(1, InputManager.ButtonSetting.Select), - _ => throw new ArgumentOutOfRangeException(nameof(key), key, "我也不知道这是什么键") - }; - - public static bool GetKeyDown(KeyCodeOrName key) - { - // return key switch - // { - // KeyCodeOrName.None => false, - // < KeyCodeOrName.Select1P => Input.GetKeyDown(key.GetKeyCode()), - // KeyCodeOrName.Test => InputManager.GetSystemInputDown(InputManager.SystemButtonSetting.ButtonTest), - // KeyCodeOrName.Service => InputManager.GetSystemInputDown(InputManager.SystemButtonSetting.ButtonService), - // KeyCodeOrName.Select1P => InputManager.GetButtonDown(0, InputManager.ButtonSetting.Select), - // KeyCodeOrName.Select2P => InputManager.GetButtonDown(1, InputManager.ButtonSetting.Select), - // _ => throw new ArgumentOutOfRangeException(nameof(key), key, "我也不知道这是什么键") - // }; - - // 不用这个,我们检测按键是否弹起以及弹起之前按下的时间是否小于 30,这样可以防止要长按时按下的时候就触发 - return _keyPressFrames[key] == 0 && 0 < _keyPressFramesPrev[key] && _keyPressFramesPrev[key] < 30; - } - - public static bool GetKeyDownOrLongPress(KeyCodeOrName key, bool isLongPress) - { - bool ret; - if (isLongPress) - { - ret = _keyPressFrames[key] == 60; - } - else - { - ret = GetKeyDown(key); - } - -# if DEBUG - if (ret) - { - MelonLogger.Msg($"Key {key} is pressed, long press: {isLongPress}"); - MelonLogger.Msg(new StackTrace()); - } -# endif - return ret; - } - - private static KeyCode GetKeyCode(this KeyCodeOrName keyCodeOrName) => - keyCodeOrName switch - { - KeyCodeOrName.Alpha0 => KeyCode.Alpha0, - KeyCodeOrName.Alpha1 => KeyCode.Alpha1, - KeyCodeOrName.Alpha2 => KeyCode.Alpha2, - KeyCodeOrName.Alpha3 => KeyCode.Alpha3, - KeyCodeOrName.Alpha4 => KeyCode.Alpha4, - KeyCodeOrName.Alpha5 => KeyCode.Alpha5, - KeyCodeOrName.Alpha6 => KeyCode.Alpha6, - KeyCodeOrName.Alpha7 => KeyCode.Alpha7, - KeyCodeOrName.Alpha8 => KeyCode.Alpha8, - KeyCodeOrName.Alpha9 => KeyCode.Alpha9, - KeyCodeOrName.Keypad0 => KeyCode.Keypad0, - KeyCodeOrName.Keypad1 => KeyCode.Keypad1, - KeyCodeOrName.Keypad2 => KeyCode.Keypad2, - KeyCodeOrName.Keypad3 => KeyCode.Keypad3, - KeyCodeOrName.Keypad4 => KeyCode.Keypad4, - KeyCodeOrName.Keypad5 => KeyCode.Keypad5, - KeyCodeOrName.Keypad6 => KeyCode.Keypad6, - KeyCodeOrName.Keypad7 => KeyCode.Keypad7, - KeyCodeOrName.Keypad8 => KeyCode.Keypad8, - KeyCodeOrName.Keypad9 => KeyCode.Keypad9, - KeyCodeOrName.F1 => KeyCode.F1, - KeyCodeOrName.F2 => KeyCode.F2, - KeyCodeOrName.F3 => KeyCode.F3, - KeyCodeOrName.F4 => KeyCode.F4, - KeyCodeOrName.F5 => KeyCode.F5, - KeyCodeOrName.F6 => KeyCode.F6, - KeyCodeOrName.F7 => KeyCode.F7, - KeyCodeOrName.F8 => KeyCode.F8, - KeyCodeOrName.F9 => KeyCode.F9, - KeyCodeOrName.F10 => KeyCode.F10, - KeyCodeOrName.F11 => KeyCode.F11, - KeyCodeOrName.F12 => KeyCode.F12, - KeyCodeOrName.Insert => KeyCode.Insert, - KeyCodeOrName.Delete => KeyCode.Delete, - KeyCodeOrName.Home => KeyCode.Home, - KeyCodeOrName.End => KeyCode.End, - KeyCodeOrName.PageUp => KeyCode.PageUp, - KeyCodeOrName.PageDown => KeyCode.PageDown, - KeyCodeOrName.UpArrow => KeyCode.UpArrow, - KeyCodeOrName.DownArrow => KeyCode.DownArrow, - KeyCodeOrName.LeftArrow => KeyCode.LeftArrow, - KeyCodeOrName.RightArrow => KeyCode.RightArrow, - _ => throw new ArgumentOutOfRangeException(nameof(keyCodeOrName), keyCodeOrName, "游戏功能键需要单独处理") - }; -} diff --git a/AquaMai/AquaMai.Core/Helpers/MessageHelper.cs b/AquaMai/AquaMai.Core/Helpers/MessageHelper.cs deleted file mode 100644 index b0e6b64f..00000000 --- a/AquaMai/AquaMai.Core/Helpers/MessageHelper.cs +++ /dev/null @@ -1,39 +0,0 @@ -using DB; -using HarmonyLib; -using Manager; -using MelonLoader; -using Process; - -namespace AquaMai.Core.Helpers; - -public class MessageHelper -{ - private static IGenericManager _genericManager = null; - - [HarmonyPostfix] - [HarmonyPatch(typeof(ProcessManager), "SetMessageManager")] - private static void OnSetMessageManager(IGenericManager genericManager) - { - _genericManager = genericManager; - } - - public static void ShowMessage(string message, WindowSizeID size = WindowSizeID.Middle, string title = null) - { - if (_genericManager is null) - { - MelonLogger.Error($"[MessageHelper] Unable to show message: `{message}` GenericManager is null"); - return; - } - - _genericManager.Enqueue(0, WindowMessageID.CollectionAttentionEmptyFavorite, new WindowParam() - { - hideTitle = title is null, - replaceTitle = true, - title = title, - replaceText = true, - text = message, - changeSize = true, - sizeID = size, - }); - } -} diff --git a/AquaMai/AquaMai.Core/Helpers/MusicDirHelper.cs b/AquaMai/AquaMai.Core/Helpers/MusicDirHelper.cs deleted file mode 100644 index 112a4e62..00000000 --- a/AquaMai/AquaMai.Core/Helpers/MusicDirHelper.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Collections.Generic; -using HarmonyLib; - -namespace AquaMai.Core.Helpers; - -public class MusicDirHelper -{ - private static Dictionary _map = new(); - - [HarmonyPostfix] - [HarmonyPatch(typeof(Manager.MaiStudio.Serialize.MusicData), "AddPath")] - private static void AddPath(Manager.MaiStudio.Serialize.MusicData __instance, string parentPath) - { - _map[__instance.GetID()] = parentPath; - } - - public static string LookupPath(int id) - { - return _map.GetValueOrDefault(id); - } - - public static string LookupPath(Manager.MaiStudio.Serialize.MusicData musicData) - { - return LookupPath(musicData.GetID()); - } - - public static string LookupPath(Manager.MaiStudio.MusicData musicData) - { - return LookupPath(musicData.GetID()); - } -} diff --git a/AquaMai/AquaMai.Core/Helpers/SharedInstances.cs b/AquaMai/AquaMai.Core/Helpers/SharedInstances.cs deleted file mode 100644 index 6f05f1fa..00000000 --- a/AquaMai/AquaMai.Core/Helpers/SharedInstances.cs +++ /dev/null @@ -1,25 +0,0 @@ -using HarmonyLib; -using Main; -using Process; - -namespace AquaMai.Core.Helpers; - -public class SharedInstances -{ - public static ProcessDataContainer ProcessDataContainer { get; private set; } - public static GameMainObject GameMainObject { get; private set; } - - [HarmonyPrefix] - [HarmonyPatch(typeof(ProcessDataContainer), MethodType.Constructor)] - public static void OnCreateProcessDataContainer(ProcessDataContainer __instance) - { - ProcessDataContainer = __instance; - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(GameMainObject), "Awake")] - public static void OnCreateGameMainObject(GameMainObject __instance) - { - GameMainObject = __instance; - } -} diff --git a/AquaMai/AquaMai.Core/Helpers/Shim.cs b/AquaMai/AquaMai.Core/Helpers/Shim.cs deleted file mode 100644 index 44b9725f..00000000 --- a/AquaMai/AquaMai.Core/Helpers/Shim.cs +++ /dev/null @@ -1,133 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using HarmonyLib; -using MAI2.Util; -using Manager; -using Manager.UserDatas; -using MelonLoader; -using Net; -using Net.Packet; -using Net.Packet.Mai2; -using Net.VO; - -namespace AquaMai.Core.Helpers; - -public static class Shim -{ - private static T Iife(Func func) => func(); - - public static readonly string apiSuffix = Iife(() => - { - try - { - var baseNetQueryConstructor = typeof(NetQuery) - .GetConstructors() - .First(); - return ((INetQuery)baseNetQueryConstructor.Invoke( - baseNetQueryConstructor - .GetParameters() - .Select((parameter, i) => i == 0 ? "" : parameter.DefaultValue) - .ToArray())).Api; - } - catch (Exception e) - { - MelonLogger.Error($"Failed to resolve the API suffix: {e}"); - return null; - } - }); - - public static string RemoveApiSuffix(string api) - { - return !string.IsNullOrEmpty(apiSuffix) && api.EndsWith(apiSuffix) - ? api.Substring(0, api.Length - apiSuffix.Length) - : api; - } - - public delegate string GetAccessTokenMethod(int index); - public static readonly GetAccessTokenMethod GetAccessToken = Iife(() => - { - var tOperationManager = Traverse.Create(Singleton.Instance); - var tGetAccessToken = tOperationManager.Method("GetAccessToken", [typeof(int)]); - if (!tGetAccessToken.MethodExists()) - { - return (index) => throw new MissingMethodException("No matching OperationManager.GetAccessToken() method found"); - } - return (index) => tGetAccessToken.GetValue(index); - }); - - public delegate PacketUploadUserPlaylog PacketUploadUserPlaylogCreator(int index, UserData src, int trackNo, Action onDone, Action onError = null); - public static readonly PacketUploadUserPlaylogCreator CreatePacketUploadUserPlaylog = Iife(() => - { - var type = typeof(PacketUploadUserPlaylog); - if (type.GetConstructor([typeof(int), typeof(UserData), typeof(int), typeof(Action), typeof(Action)]) is ConstructorInfo ctor1) - { - return (index, src, trackNo, onDone, onError) => - { - var args = new object[] { index, src, trackNo, onDone, onError }; - return (PacketUploadUserPlaylog)ctor1.Invoke(args); - }; - } - else if (type.GetConstructor([typeof(int), typeof(UserData), typeof(int), typeof(string), typeof(Action), typeof(Action)]) is ConstructorInfo ctor2) - { - return (index, src, trackNo, onDone, onError) => - { - var accessToken = GetAccessToken(index); - var args = new object[] { index, src, trackNo, accessToken, onDone, onError }; - return (PacketUploadUserPlaylog)ctor2.Invoke(args); - }; - } - else - { - throw new MissingMethodException("No matching PacketUploadUserPlaylog constructor found"); - } - }); - - public delegate PacketUpsertUserAll PacketUpsertUserAllCreator(int index, UserData src, Action onDone, Action onError = null); - public static readonly PacketUpsertUserAllCreator CreatePacketUpsertUserAll = Iife(() => - { - var type = typeof(PacketUpsertUserAll); - if (type.GetConstructor([typeof(int), typeof(UserData), typeof(Action), typeof(Action)]) is ConstructorInfo ctor1) - { - return (index, src, onDone, onError) => - { - var args = new object[] { index, src, onDone, onError }; - return (PacketUpsertUserAll)ctor1.Invoke(args); - }; - } - else if (type.GetConstructor([typeof(int), typeof(UserData), typeof(string), typeof(Action), typeof(Action)]) is ConstructorInfo ctor2) - { - return (index, src, onDone, onError) => - { - var accessToken = GetAccessToken(index); - var args = new object[] { index, src, accessToken, onDone, onError }; - return (PacketUpsertUserAll)ctor2.Invoke(args); - }; - } - else - { - throw new MissingMethodException("No matching PacketUpsertUserAll constructor found"); - } - }); - - public static IEnumerable[] GetUserScoreList(UserData userData) - { - var tUserData = Traverse.Create(userData); - - var tScoreList = tUserData.Property("ScoreList"); - if (tScoreList.PropertyExists()) - { - return tScoreList.GetValue[]>(); - } - - var tScoreDic = tUserData.Property("ScoreDic"); - if (tScoreDic.PropertyExists()) - { - var scoreDic = tScoreDic.GetValue[]>(); - return scoreDic.Select(dic => dic.Values).ToArray(); - } - - throw new MissingFieldException("No matching UserData.ScoreList/ScoreDic found"); - } -} diff --git a/AquaMai/AquaMai.Core/Resources/I18nSingleAssemblyHook.cs b/AquaMai/AquaMai.Core/Resources/I18nSingleAssemblyHook.cs deleted file mode 100644 index 2fdd490d..00000000 --- a/AquaMai/AquaMai.Core/Resources/I18nSingleAssemblyHook.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Globalization; -using System.Resources; -using HarmonyLib; - -namespace AquaMai.Core.Resources; - -public class I18nSingleAssemblyHook -{ - [HarmonyPatch(typeof(ResourceManager), "InternalGetResourceSet", typeof(CultureInfo), typeof(bool), typeof(bool))] - [HarmonyPrefix] - public static bool GetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents, ref ResourceSet __result, ResourceManager __instance) - { - var GetResourceFileName = __instance.GetType().GetMethod("GetResourceFileName", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); - var resourceFileName = (string)GetResourceFileName.Invoke(__instance, [culture]); - var ResourcesAssembly = typeof(I18nSingleAssemblyHook).Assembly; - var manifestResourceStream = ResourcesAssembly.GetManifestResourceStream(resourceFileName); - if (manifestResourceStream == null) - { - return true; - } - - var resourceGroveler = __instance.GetType().GetField("resourceGroveler", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(__instance); - var CreateResourceSet = resourceGroveler.GetType().GetMethod("CreateResourceSet", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); - var resourceSet = CreateResourceSet.Invoke(resourceGroveler, [manifestResourceStream, ResourcesAssembly]); - var AddResourceSet = __instance.GetType().GetMethod("AddResourceSet", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); - var localResourceSets = __instance.GetType().GetField("_resourceSets", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(__instance); - object[] args = [localResourceSets, culture.Name, resourceSet]; - AddResourceSet.Invoke(null, args); - __result = (ResourceSet)args[2]; - return false; - } -} diff --git a/AquaMai/AquaMai.Core/Resources/Locale.Designer.cs b/AquaMai/AquaMai.Core/Resources/Locale.Designer.cs deleted file mode 100644 index e46e620e..00000000 --- a/AquaMai/AquaMai.Core/Resources/Locale.Designer.cs +++ /dev/null @@ -1,361 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace AquaMai.Core.Resources { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - public class Locale { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Locale() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AquaMai.Core.Resources.Locale", typeof(Locale).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to You are using AquaMai CI build version. This version is built from the latest mainline code and may contain undocumented configuration changes or potential issues.. - /// - public static string CiBuildAlertContent { - get { - return ResourceManager.GetString("CiBuildAlertContent", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Important Notice: Test Version. - /// - public static string CiBuildAlertTitle { - get { - return ResourceManager.GetString("CiBuildAlertTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Loaded!. - /// - public static string Loaded { - get { - return ResourceManager.GetString("Loaded", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Errors detected while loading! - ///- Are you using a modified Assembly-CSharp.dll, which will cause inconsistent functions and cannot find the functions that need to be modified - ///- Check for conflicting mods, or enabled incompatible options. - /// - public static string LoadError { - get { - return ResourceManager.GetString("LoadError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to End. - /// - public static string MarkRepeatEnd { - get { - return ResourceManager.GetString("MarkRepeatEnd", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Start. - /// - public static string MarkRepeatStart { - get { - return ResourceManager.GetString("MarkRepeatStart", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Aime reader error. - /// - public static string NetErrIsAliveAimeReader { - get { - return ResourceManager.GetString("NetErrIsAliveAimeReader", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Aime server error. - /// - public static string NetErrIsAliveAimeServer { - get { - return ResourceManager.GetString("NetErrIsAliveAimeServer", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Server communication error. - /// - public static string NetErrIsAliveServer { - get { - return ResourceManager.GetString("NetErrIsAliveServer", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Data download not success. - /// - public static string NetErrWasDownloadSuccessOnce { - get { - return ResourceManager.GetString("NetErrWasDownloadSuccessOnce", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Pause. - /// - public static string Pause { - get { - return ResourceManager.GetString("Pause", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Play Count:{0}. - /// - public static string PlayCount { - get { - return ResourceManager.GetString("PlayCount", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Playlog save error. - /// - public static string PlaylogSaveError { - get { - return ResourceManager.GetString("PlaylogSaveError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to SSS+ => DXRating += {0}. - /// - public static string RatingUpWhenSSSp { - get { - return ResourceManager.GetString("RatingUpWhenSSSp", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Repeat end time cannot be less than repeat start time. - /// - public static string RepeatEndTimeLessThenStartTime { - get { - return ResourceManager.GetString("RepeatEndTimeLessThenStartTime", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Loop Not Set. - /// - public static string RepeatNotSet { - get { - return ResourceManager.GetString("RepeatNotSet", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Reset. - /// - public static string RepeatReset { - get { - return ResourceManager.GetString("RepeatReset", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Loop Set. - /// - public static string RepeatStartEndSet { - get { - return ResourceManager.GetString("RepeatStartEndSet", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Loop Start Set. - /// - public static string RepeatStartSet { - get { - return ResourceManager.GetString("RepeatStartSet", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Please set repeat start time first. - /// - public static string RepeatStartTimeNotSet { - get { - return ResourceManager.GetString("RepeatStartTimeNotSet", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Saving... Do not exit the game. - /// - public static string SavingDontExit { - get { - return ResourceManager.GetString("SavingDontExit", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Seek <<. - /// - public static string SeekBackward { - get { - return ResourceManager.GetString("SeekBackward", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Seek >>. - /// - public static string SeekForward { - get { - return ResourceManager.GetString("SeekForward", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Hide Self-Made Charts. - /// - public static string SelfMadeChartsHide { - get { - return ResourceManager.GetString("SelfMadeChartsHide", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Show Self-Made Charts. - /// - public static string SelfMadeChartsShow { - get { - return ResourceManager.GetString("SelfMadeChartsShow", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Skip. - /// - public static string Skip { - get { - return ResourceManager.GetString("Skip", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to > Skipping incompatible patch: {0}. - /// - public static string SkipIncompatiblePatch { - get { - return ResourceManager.GetString("SkipIncompatiblePatch", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Speed. - /// - public static string Speed { - get { - return ResourceManager.GetString("Speed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Speed -. - /// - public static string SpeedDown { - get { - return ResourceManager.GetString("SpeedDown", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Speed Reset. - /// - public static string SpeedReset { - get { - return ResourceManager.GetString("SpeedReset", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Speed +. - /// - public static string SpeedUp { - get { - return ResourceManager.GetString("SpeedUp", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Touch panel reset. - /// - public static string TouchPanelReset { - get { - return ResourceManager.GetString("TouchPanelReset", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to UserAll Upsert Error. - /// - public static string UserAllUpsertError { - get { - return ResourceManager.GetString("UserAllUpsertError", resourceCulture); - } - } - } -} diff --git a/AquaMai/AquaMai.Core/Resources/Locale.resx b/AquaMai/AquaMai.Core/Resources/Locale.resx deleted file mode 100644 index 8ee645de..00000000 --- a/AquaMai/AquaMai.Core/Resources/Locale.resx +++ /dev/null @@ -1,122 +0,0 @@ - - - - - - - - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Seek << - - - Seek >> - - - Pause - - - Start - - - End - - - Reset - - - Loop Not Set - - - Loop Start Set - - - Loop Set - - - Speed - - - - Speed + - - - Speed - - - Speed Reset - - - Errors detected while loading! -- Are you using a modified Assembly-CSharp.dll, which will cause inconsistent functions and cannot find the functions that need to be modified -- Check for conflicting mods, or enabled incompatible options - - - Saving... Do not exit the game - - - Loaded! - - - Server communication error - - - Aime reader error - - - Aime server error - - - Data download not success - - - SSS+ => DXRating += {0} - - - Skip - - - > Skipping incompatible patch: {0} - - - Please set repeat start time first - - - Repeat end time cannot be less than repeat start time - - - Important Notice: Test Version - - - You are using AquaMai CI build version. This version is built from the latest mainline code and may contain undocumented configuration changes or potential issues. - - - Play Count:{0} - - - Hide Self-Made Charts - - - Show Self-Made Charts - - - UserAll Upsert Error - - - Playlog save error - - - Touch panel reset - - diff --git a/AquaMai/AquaMai.Core/Resources/Locale.zh.resx b/AquaMai/AquaMai.Core/Resources/Locale.zh.resx deleted file mode 100644 index fac48af0..00000000 --- a/AquaMai/AquaMai.Core/Resources/Locale.zh.resx +++ /dev/null @@ -1,115 +0,0 @@ - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - 倒退 << - - - 快进 >> - - - 暂停 - - - 标记结尾 - - - 标记开头 - - - 循环未设定 - - - 循环解除 - - - 循环已设定 - - - 循环开头已设定 - - - 速度 - - - 速度 - - - - 速度重置 - - - 速度 + - - - 加载过程中检测到错误! -- 你是否正在使用魔改的 Assembly-CSharp.dll,这会导致函数不一致而无法找到需要修改的函数 -- 请检查是否有冲突的 Mod,或者开启了不兼容的选项 - - - 正在保存… 请不要关闭游戏 - - - 加载完成! - - - 主服务器通信错误 - - - Aime 读卡器错误 - - - AimeDB 通信错误 - - - 数据下载不成功 - - - 推到鸟加可上 {0} 分 - - - 跳过 - - - > 已跳过加载不兼容的功能: {0} - - - 循环结束时间不能早于开始时间 - - - 请先设置循环开始时间 - - - 重要提示:测试版本 - - - 您正在使用的是 AquaMai CI 构建版本。由于该版本基于最新的主线代码构建,可能包含未通知的配置文件变更或潜在问题。 - - - 游玩次数: {0} - - - 已隐藏所有自制谱 - - - 已显示自制谱 - - - 保存 UserAll 失败 - - - 保存 Playlog 失败 - - - 触摸面板已重置 - - diff --git a/AquaMai/AquaMai.Core/Startup.cs b/AquaMai/AquaMai.Core/Startup.cs deleted file mode 100644 index 8695ad87..00000000 --- a/AquaMai/AquaMai.Core/Startup.cs +++ /dev/null @@ -1,199 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Reflection; -using AquaMai.Core.Attributes; -using AquaMai.Core.Helpers; -using AquaMai.Core.Resources; -using MelonLoader; -using UnityEngine; - -namespace AquaMai.Core; - -public class Startup -{ - private static HarmonyLib.Harmony _harmony; - - private static bool _hasErrors; - - private enum ModLifecycleMethod - { - // Invoked before all patches are applied, including core patches - OnBeforeAllPatch, - // Invoked after all patches are applied - OnAfterAllPatch, - // Invoked before the current patch is applied - OnBeforePatch, - // Invoked after the current patch is applied - // Subclasses are treated as separate patches - OnAfterPatch, - // Invoked when an error occurs applying the current patch - // Lifecycle methods' excpetions not included - // Subclasses' error not included - OnPatchError - } - - private static bool ShouldEnableImplicitly(Type type) - { - var implicitEnableAttribute = type.GetCustomAttribute(); - if (implicitEnableAttribute == null) return false; - var referenceField = type.GetField(implicitEnableAttribute.MemberName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); - var referenceProperty = type.GetProperty(implicitEnableAttribute.MemberName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); - if (referenceField == null && referenceProperty == null) - { - throw new ArgumentException($"Field or property {implicitEnableAttribute.MemberName} not found in {type.FullName}"); - } - var referenceMemberValue = referenceField != null ? referenceField.GetValue(null) : referenceProperty.GetValue(null); - if ((bool)referenceMemberValue) - { - MelonLogger.Msg($"Enabled {type.FullName} implicitly"); - return true; - } - return false; - } - - private static void InvokeLifecycleMethod(Type type, ModLifecycleMethod methodName) - { - var method = type.GetMethod(methodName.ToString(), BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); - if (method == null) - { - return; - } - var parameters = method.GetParameters(); - var arguments = parameters.Select(p => - { - if (p.ParameterType == typeof(HarmonyLib.Harmony)) return _harmony; - throw new InvalidOperationException($"Unsupported parameter type {p.ParameterType} in lifecycle method {type.FullName}.{methodName}"); - }).ToArray(); - try - { - method.Invoke(null, arguments); - } - catch (TargetInvocationException e) - { - MelonLogger.Error($"Failed to invoke lifecycle method {type.FullName}.{methodName}: {e.InnerException}"); - _hasErrors = true; - } - } - - private static void CollectWantedPatches(List wantedPatches, Type type) - { - if (EnableConditionHelper.ShouldSkipClass(type)) - { - return; - } - - wantedPatches.Add(type); - foreach (var nested in type.GetNestedTypes()) - { - CollectWantedPatches(wantedPatches, nested); - } - } - - private static void ApplyPatch(Type type) - { - MelonLogger.Msg($"> Applying {type}"); - try - { - InvokeLifecycleMethod(type, ModLifecycleMethod.OnBeforePatch); - _harmony.PatchAll(type); - InvokeLifecycleMethod(type, ModLifecycleMethod.OnAfterPatch); - } - catch (Exception e) - { - MelonLogger.Error($"Failed to patch {type}: {e}"); - InvokeLifecycleMethod(type, ModLifecycleMethod.OnPatchError); - _hasErrors = true; - } - } - - private static string ResolveLocale() - { - var localeConfigEntry = ConfigLoader.Config.ReflectionManager.GetEntry("General.Locale"); - var localeValue = (string)ConfigLoader.Config.GetEntryState(localeConfigEntry).Value; - return localeValue switch - { - "en" => localeValue, - "zh" => localeValue, - _ => Application.systemLanguage switch - { - SystemLanguage.Chinese or SystemLanguage.ChineseSimplified or SystemLanguage.ChineseTraditional => "zh", - SystemLanguage.English => "en", - _ => "en" - } - }; - } - - public static void Initialize(Assembly modsAssembly, HarmonyLib.Harmony harmony) - { - MelonLogger.Msg("Loading mod settings..."); - - var configLoaded = ConfigLoader.LoadConfig(modsAssembly); - var lang = ResolveLocale(); - if (configLoaded) - { - ConfigLoader.SaveConfig(lang); // Re-save the config as soon as possible - } - - _harmony = harmony; - - // Init locale with patching C# runtime - // https://stackoverflow.com/questions/1952638/single-assembly-multi-language-windows-forms-deployment-ilmerge-and-satellite-a - ApplyPatch(typeof(I18nSingleAssemblyHook)); - Locale.Culture = CultureInfo.GetCultureInfo(lang); // Must be called after I18nSingleAssemblyHook patched - - // The patch list is ordered - List wantedPatches = []; - - // Must be patched first to support [EnableIf(...)] and [EnableGameVersion(...)] - CollectWantedPatches(wantedPatches, typeof(EnableConditionHelper)); - // Core helpers patched first - CollectWantedPatches(wantedPatches, typeof(MessageHelper)); - CollectWantedPatches(wantedPatches, typeof(MusicDirHelper)); - CollectWantedPatches(wantedPatches, typeof(SharedInstances)); - CollectWantedPatches(wantedPatches, typeof(GuiSizes)); - CollectWantedPatches(wantedPatches, typeof(KeyListener)); - - // Collect patches based on the config - var config = ConfigLoader.Config; - foreach (var section in config.ReflectionManager.Sections) - { - var reflectionType = (Config.Reflection.SystemReflectionProvider.ReflectionType)section.Type; - var type = reflectionType.UnderlyingType; - if (!config.GetSectionState(section).Enabled && !ShouldEnableImplicitly(type)) continue; - CollectWantedPatches(wantedPatches, type); - } - - foreach (var type in wantedPatches) - { - InvokeLifecycleMethod(type, ModLifecycleMethod.OnBeforeAllPatch); - } - foreach (var type in wantedPatches) - { - ApplyPatch(type); - } - foreach (var type in wantedPatches) - { - InvokeLifecycleMethod(type, ModLifecycleMethod.OnAfterAllPatch); - } - - if (_hasErrors) - { - MelonLogger.Warning("========================================================================!!!\n" + Locale.LoadError); - MelonLogger.Warning("==========================================================================="); - } - -# if CI - MelonLogger.Warning(Locale.CiBuildAlertTitle); - MelonLogger.Warning(Locale.CiBuildAlertContent); -# endif - - MelonLogger.Msg(Locale.Loaded); - } - - public static void OnGUI() - { - GuiSizes.SetupStyles(); - } -} diff --git a/AquaMai/AquaMai.Mods/AquaMai.Mods.csproj b/AquaMai/AquaMai.Mods/AquaMai.Mods.csproj deleted file mode 100644 index b04c32a1..00000000 --- a/AquaMai/AquaMai.Mods/AquaMai.Mods.csproj +++ /dev/null @@ -1,128 +0,0 @@ - - - - Release - AnyCPU - {8731C0E0-53BE-4B1B-9828-193E738C6865} - Library - AquaMai.Mods - AquaMai.Mods - net472 - 512 - true - 12 - 414 - $(ProjectDir)../Libs/;$(AssemblySearchPaths) - $(ProjectDir)../Output/ - false - false - - - - false - None - true - prompt - 4 - true - false - - - - DEBUG - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/AquaMai/AquaMai.Mods/DeprecationWarning.cs b/AquaMai/AquaMai.Mods/DeprecationWarning.cs deleted file mode 100644 index 4afacde8..00000000 --- a/AquaMai/AquaMai.Mods/DeprecationWarning.cs +++ /dev/null @@ -1,29 +0,0 @@ -using AquaMai.Config.Attributes; - -namespace AquaMai.Mods; - -[ConfigSection( - en: """ - These options have been deprecated and no longer work in the current version. - Remove them to get rid of the warning message at startup. - """, - zh: """ - 这些配置项已经被废弃,在当前版本不再生效 - 删除它们以去除启动时的警告信息 - """, - exampleHidden: true)] -public class DeprecationWarning -{ - [ConfigEntry(hideWhenDefault: true)] - public static readonly bool v1_0_ModKeyMap_TestMode; - - // Print friendly warning messages here. - // Please keep them up-to-date while refactoring the config. - public static void OnBeforeAllPatch() - { - if (v1_0_ModKeyMap_TestMode) - { - MelonLoader.MelonLogger.Warning("ModKeyMap.TestMode has been deprecated (> v1.0). Please use GameSystem.KeyMap.Test instead."); - } - } -} diff --git a/AquaMai/AquaMai.Mods/Fancy/CustomLogo.cs b/AquaMai/AquaMai.Mods/Fancy/CustomLogo.cs deleted file mode 100644 index 54c127f9..00000000 --- a/AquaMai/AquaMai.Mods/Fancy/CustomLogo.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using AquaMai.Config.Attributes; -using AquaMai.Core.Helpers; -using HarmonyLib; -using Monitor; -using Process; -using UnityEngine; -using UnityEngine.UI; - -namespace AquaMai.Mods.Fancy; - -[ConfigSection( - en: "Replace the \"SEGA\" and \"ALL.Net\" logos with custom ones.", - zh: "用自定义的图片替换「SEGA」和「ALL.Net」的标志")] -public class CustomLogo -{ - [ConfigEntry( - en: "Replace the \"SEGA\" logo with a random PNG image from this directory.", - zh: "从此目录中随机选择一张 PNG 图片用于「SEGA」标志")] - private static readonly string segaLogoDir = "LocalAssets/SegaLogo"; - - [ConfigEntry( - en: "Replace the \"ALL.Net\" logo with a random PNG image from this directory.", - zh: "从此目录中随机选择一张 PNG 图片用于「ALL.Net」标志")] - private static readonly string allNetLogoDir = "LocalAssets/AllNetLogo"; - - private readonly static List segaLogo = []; - private readonly static List allNetLogo = []; - - public static void OnBeforePatch() - { - EnumSprite(segaLogo, FileSystem.ResolvePath(segaLogoDir)); - EnumSprite(allNetLogo, FileSystem.ResolvePath(allNetLogoDir)); - } - - private static void EnumSprite(List collection, string path) - { - if (!Directory.Exists(path)) return; - foreach (var file in Directory.EnumerateFiles(path, "*.png")) - { - var data = File.ReadAllBytes(file); - var texture2D = new Texture2D(1, 1, TextureFormat.RGBA32, false); - if (texture2D.LoadImage(data)) - { - collection.Add(Sprite.Create(texture2D, new Rect(0f, 0f, texture2D.width, texture2D.height), new Vector2(0.5f, 0.5f))); - } - } - } - - [HarmonyPatch(typeof(AdvertiseProcess), "OnStart")] - [HarmonyPostfix] - private static void AdvProcessPostFix(AdvertiseMonitor[] ____monitors) - { - if (segaLogo.Count > 0) - { - var logo = segaLogo[UnityEngine.Random.Range(0, segaLogo.Count)]; - foreach (var monitor in ____monitors) - { - monitor.transform.Find("Canvas/Main/SegaAllNet_LOGO/NUL_ADT_SegaAllNet_LOGO/SegaLogo").GetComponent().sprite = logo; - } - } - - if (allNetLogo.Count > 0) - { - var logo = allNetLogo[UnityEngine.Random.Range(0, allNetLogo.Count)]; - foreach (var monitor in ____monitors) - { - monitor.transform.Find("Canvas/Main/SegaAllNet_LOGO/NUL_ADT_SegaAllNet_LOGO/AllNetLogo").GetComponent().sprite = logo; - } - } - } -} diff --git a/AquaMai/AquaMai.Mods/Fancy/CustomPlaceName.cs b/AquaMai/AquaMai.Mods/Fancy/CustomPlaceName.cs deleted file mode 100644 index a54f3e72..00000000 --- a/AquaMai/AquaMai.Mods/Fancy/CustomPlaceName.cs +++ /dev/null @@ -1,45 +0,0 @@ -using AquaMai.Config.Attributes; -using HarmonyLib; -using Manager; - -namespace AquaMai.Mods.Fancy; - -[ConfigSection( - en: """ - Custom shop name in photo. - Also enable shop name display in SDGA. - """, - zh: """ - 自定义拍照的店铺名称 - 同时在 SDGA 中会启用店铺名称的显示(但是不会在游戏里有设置) - """)] -public class CustomPlaceName -{ - [ConfigEntry] - private static readonly string placeName = ""; - - [HarmonyPostfix] - [HarmonyPatch(typeof(OperationManager), "CheckAuth_Proc")] - public static void CheckAuth_Proc(OperationManager __instance) - { - if (string.IsNullOrEmpty(placeName)) - { - return; - } - - __instance.ShopData.ShopName = placeName; - __instance.ShopData.ShopNickName = placeName; - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(ResultCardBaseController), "Initialize")] - public static void Initialize(ResultCardBaseController __instance) - { - if (string.IsNullOrEmpty(placeName)) - { - return; - } - - __instance.SetVisibleStoreName(true); - } -} diff --git a/AquaMai/AquaMai.Mods/Fancy/CustomSkins.cs b/AquaMai/AquaMai.Mods/Fancy/CustomSkins.cs deleted file mode 100644 index 46f6f422..00000000 --- a/AquaMai/AquaMai.Mods/Fancy/CustomSkins.cs +++ /dev/null @@ -1,402 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using AquaMai.Config.Attributes; -using AquaMai.Core.Helpers; -using HarmonyLib; -using MelonLoader; -using Monitor; -using Monitor.Game; -using Process; -using UnityEngine; - -namespace AquaMai.Mods.Fancy; - -[ConfigSection( - en: """ - Provide the ability to use custom skins (advanced feature). - Load skin textures from custom paths. - """, - zh: """ - 提供自定义皮肤的能力(高级功能) - 从自定义路径中加载皮肤贴图 - """)] -public class CustomSkins -{ - [ConfigEntry] - private static readonly string skinsDir = "LocalAssets/Skins"; - - private static readonly List ImageExts = [".png", ".jpg", ".jpeg"]; - private static readonly List SlideFanFields = ["_normalSlideFan", "_eachSlideFan", "_breakSlideFan", "_breakSlideFanEff"]; - private static readonly List CustomTrackStartFields = ["_musicBase", "_musicTab", "_musicLvBase", "_musicLvText"]; - - private static Sprite customOutline; - private readonly static Sprite[,] customSlideFan = new Sprite[4, 11]; - - public static readonly Sprite[,] CustomJudge = new Sprite[2, ((int)NoteJudge.ETiming.End + 1)]; - public static readonly Sprite[,,,] CustomJudgeSlide = new Sprite[2, 3, 2, ((int)NoteJudge.ETiming.End + 1)]; - public static readonly Texture2D[] CustomTrackStart = new Texture2D[4]; - - private static bool LoadIntoGameNoteImageContainer(string fieldName, int? idx1, int? idx2, Texture2D texture) - { - // 先确定确实有这个 Field, 如果没有的话可以直接跳过这个文件 - var fieldTraverse = Traverse.Create(typeof(GameNoteImageContainer)).Field(fieldName); - if (!fieldTraverse.FieldExists()) - { - MelonLogger.Msg($"[CustomNoteSkin] Cannot found field {fieldName}"); - return false; - } - - var fieldType = fieldTraverse.GetValueType(); - if (!idx1.HasValue) - { - // 目标 Field 应当是单个 Sprite - if (fieldType != typeof(Sprite)) - { - MelonLogger.Msg($"[CustomNoteSkin] Field {fieldName} is a {fieldType.Name}, not a Sprite"); - return false; - } - - var target = fieldTraverse.GetValue(); - var pivot = new Vector2(target.pivot.x / target.rect.width, target.pivot.y / target.rect.height); - var custom = Sprite.Create( - texture, new Rect(0, 0, texture.width, texture.height), pivot, 1f, - 0, SpriteMeshType.Tight, target.border - ); - fieldTraverse.SetValue(custom); - } - else if (!idx2.HasValue) - { - // 目标 Field 是一维数组 - if (fieldType != typeof(Sprite[])) - { - MelonLogger.Msg($"[CustomNoteSkin] Field {fieldName} is a {fieldType.Name}, not a Sprite[]"); - return false; - } - - var targetArray = fieldTraverse.GetValue(); - var target = targetArray[idx1.Value]; - var pivot = new Vector2(target.pivot.x / target.rect.width, target.pivot.y / target.rect.height); - var custom = Sprite.Create( - texture, new Rect(0, 0, texture.width, texture.height), pivot, 1f, - 0, SpriteMeshType.Tight, target.border - ); - targetArray[idx1.Value] = custom; - } - else - { - // 目标 Field 是二维数组 - if (fieldType != typeof(Sprite[,])) - { - MelonLogger.Msg($"[CustomNoteSkin] Field {fieldName} is a {fieldType.Name}, not a Sprite[,]"); - return false; - } - - var targetArray = fieldTraverse.GetValue(); - var target = targetArray[idx1.Value, idx2.Value]; - var pivot = new Vector2(target.pivot.x / target.rect.width, target.pivot.y / target.rect.height); - var custom = Sprite.Create( - texture, new Rect(0, 0, texture.width, texture.height), pivot, 1f, - 0, SpriteMeshType.Tight, target.border - ); - targetArray[idx1.Value, idx2.Value] = custom; - } - - return true; - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(GameNotePrefabContainer), "Initialize")] - private static void LoadNoteSkin() - { - var resolvedDir = FileSystem.ResolvePath(skinsDir); - if (!Directory.Exists(resolvedDir)) return; - - foreach (var laFile in Directory.EnumerateFiles(resolvedDir)) - { - if (!ImageExts.Contains(Path.GetExtension(laFile).ToLowerInvariant())) continue; - var texture = new Texture2D(1, 1, TextureFormat.RGBA32, false); - texture.LoadImage(File.ReadAllBytes(laFile)); - - var name = Path.GetFileNameWithoutExtension(laFile); - var args = name.Split('_'); - // 文件名的格式是 XXXXXXXX_A_B 表示 GameNoteImageContainer._XXXXXXXX[A, B] - // 视具体情况, A, B 可能不存在 - var fieldName = '_' + args[0]; - int? idx1 = (args.Length < 2) ? null : (int.TryParse(args[1], out var temp) ? temp : null); - int? idx2 = (args.Length < 3) ? null : (int.TryParse(args[2], out temp) ? temp : null); - int? idx3 = (args.Length < 4) ? null : (int.TryParse(args[3], out temp) ? temp : null); - - Traverse traverse; - - if (CustomTrackStartFields.Contains(fieldName)) - { - var i = CustomTrackStartFields.IndexOf(fieldName); - CustomTrackStart[i] = texture; - MelonLogger.Msg($"[CustomNoteSkin] Successfully loaded {name}"); - continue; - } - - if (fieldName == "_outline") - { - customOutline = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f), 1f); - MelonLogger.Msg($"[CustomNoteSkin] Successfully loaded {name}"); - continue; - } - - if (fieldName == "_judgeNormal" || fieldName == "_judgeBreak") - { - if (!idx1.HasValue) - { - MelonLogger.Msg($"[CustomNoteSkin] Field {fieldName} needs a index"); - continue; - } - - var i = (fieldName == "_judgeBreak") ? 1 : 0; - CustomJudge[i, idx1.Value] = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f), 1f); - MelonLogger.Msg($"[CustomNoteSkin] Successfully loaded {name}"); - continue; - } - - if (fieldName == "_judgeSlideNormal" || fieldName == "_judgeSlideBreak") - { - if (!idx1.HasValue || !idx2.HasValue || !idx3.HasValue) - { - MelonLogger.Msg($"[CustomNoteSkin] Field {fieldName} needs 3 indices"); - continue; - } - - var i = (fieldName == "_judgeSlideBreak") ? 1 : 0; - Vector2 pivot; - switch (idx1.Value) - { - case 0 when idx2.Value == 0: - pivot = new Vector2(0f, 0.5f); - break; - case 0 when idx2.Value == 1: - pivot = new Vector2(1f, 0.5f); - break; - case 1 when idx2.Value == 0: - pivot = new Vector2(0f, 0.3f); - break; - case 1 when idx2.Value == 1: - pivot = new Vector2(1f, 0.3f); - break; - case 2 when idx2.Value == 0: - pivot = new Vector2(0.5f, 0.8f); - break; - case 2 when idx2.Value == 1: - pivot = new Vector2(0.5f, 0.2f); - break; - default: - pivot = new Vector2(0.5f, 0.5f); - break; - } - CustomJudgeSlide[i, idx1.Value, idx2.Value, idx3.Value] = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), pivot, 1f); - MelonLogger.Msg($"[CustomNoteSkin] Successfully loaded {name}"); - continue; - } - - if (SlideFanFields.Contains(fieldName)) - { - if (!idx1.HasValue) - { - MelonLogger.Msg($"[CustomNoteSkin] Field {fieldName} needs a index"); - continue; - } - - var i = SlideFanFields.IndexOf(fieldName); - customSlideFan[i, idx1.Value] = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(1f, 0.5f), 1f); - MelonLogger.Msg($"[CustomNoteSkin] Successfully loaded {name}"); - continue; - } - - if (fieldName == "_touchJust") - { - traverse = Traverse.Create(GameNotePrefabContainer.TouchTapB); - var noticeObject = traverse.Field("NoticeObject").Value; - var target = noticeObject.GetComponent(); - var pivot = new Vector2( - target.sprite.pivot.x / target.sprite.rect.width, - target.sprite.pivot.y / target.sprite.rect.height - ); - var custom = Sprite.Create( - texture, new Rect(0, 0, texture.width, texture.height), pivot, 1f, - 0, SpriteMeshType.Tight, target.sprite.border - ); - target.sprite = custom; - - traverse = Traverse.Create(GameNotePrefabContainer.TouchTapC); - noticeObject = traverse.Field("NoticeObject").Value; - noticeObject.GetComponent().sprite = custom; - MelonLogger.Msg($"[CustomNoteSkin] Successfully loaded {name}"); - continue; - } - - if (fieldName == "_touchHold") - { - if (!idx1.HasValue) - { - MelonLogger.Msg($"[CustomNoteSkin] Field {fieldName} needs a index"); - continue; - } - - traverse = Traverse.Create(GameNotePrefabContainer.TouchHoldC); - var target = traverse.Field("ColorsObject").Value; - var renderer = target[idx1.Value]; - var pivot = new Vector2( - renderer.sprite.pivot.x / renderer.sprite.rect.width, - renderer.sprite.pivot.y / renderer.sprite.rect.height - ); - var custom = Sprite.Create( - texture, new Rect(0, 0, texture.width, texture.height), pivot, 1f, - 0, SpriteMeshType.Tight, renderer.sprite.border - ); - renderer.sprite = custom; - MelonLogger.Msg($"[CustomNoteSkin] Successfully loaded {name}"); - continue; - } - - if (fieldName == "_normalTouchBorder") - { - if (!idx1.HasValue) - { - MelonLogger.Msg($"[CustomNoteSkin] Field {fieldName} needs a index"); - continue; - } - - traverse = Traverse.Create(GameNotePrefabContainer.TouchReserve); - var target = traverse.Field("_reserveSingleSprite").Value; - var targetSprite = target[idx1.Value - 2]; - var pivot = new Vector2( - targetSprite.pivot.x / targetSprite.rect.width, - targetSprite.pivot.y / targetSprite.rect.height - ); - target[idx1.Value - 2] = Sprite.Create( - texture, new Rect(0, 0, texture.width, texture.height), pivot, 1f, - 0, SpriteMeshType.Tight, targetSprite.border - ); - MelonLogger.Msg($"[CustomNoteSkin] Successfully loaded {name}"); - continue; - } - - if (fieldName == "_eachTouchBorder") - { - if (!idx1.HasValue) - { - MelonLogger.Msg($"[CustomNoteSkin] Field {fieldName} needs a index"); - continue; - } - - traverse = Traverse.Create(GameNotePrefabContainer.TouchReserve); - var target = traverse.Field("_reserveEachSprite").Value; - var targetSprite = target[idx1.Value - 2]; - var pivot = new Vector2( - targetSprite.pivot.x / targetSprite.rect.width, - targetSprite.pivot.y / targetSprite.rect.height - ); - target[idx1.Value - 2] = Sprite.Create( - texture, new Rect(0, 0, texture.width, texture.height), pivot, 1f, - 0, SpriteMeshType.Tight, targetSprite.border - ); - MelonLogger.Msg($"[CustomNoteSkin] Successfully loaded {name}"); - continue; - } - - if (LoadIntoGameNoteImageContainer(fieldName, idx1, idx2, texture)) - { - MelonLogger.Msg($"[CustomNoteSkin] Successfully loaded {name}"); - } - } - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(GameCtrl), "Initialize")] - private static void ChangeOutlineTexture(GameObject ____guideEndPointObj) - { - if (____guideEndPointObj != null && customOutline != null) - { - ____guideEndPointObj.GetComponent().sprite = customOutline; - } - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(SlideFan), "Initialize")] - private static void ChangeFanTexture( - SpriteRenderer[] ____spriteLines, SpriteRenderer[] ____effectSprites, bool ___BreakFlag, bool ___EachFlag - ) - { - Vector3 position; - Sprite sprite; - if (___BreakFlag) - { - for (var i = 0; i < 11; i++) - { - sprite = customSlideFan[2, i]; - if (sprite != null) - { - ____spriteLines[2 * i].sprite = sprite; - position = ____spriteLines[2 * i].transform.localPosition; - ____spriteLines[2 * i].transform.localPosition = new Vector3(0, position.y, position.z); - ____spriteLines[2 * i].color = Color.white; - - ____spriteLines[2 * i + 1].sprite = sprite; - position = ____spriteLines[2 * i + 1].transform.localPosition; - ____spriteLines[2 * i + 1].transform.localPosition = new Vector3(0, position.y, position.z); - ____spriteLines[2 * i + 1].color = Color.white; - } - - sprite = customSlideFan[3, i]; - if (sprite != null) - { - ____effectSprites[2 * i].sprite = sprite; - position = ____effectSprites[2 * i].transform.localPosition; - ____effectSprites[2 * i].transform.localPosition = new Vector3(0, position.y, position.z); - ____effectSprites[2 * i].color = Color.white; - - ____effectSprites[2 * i + 1].sprite = sprite; - position = ____effectSprites[2 * i + 1].transform.localPosition; - ____effectSprites[2 * i + 1].transform.localPosition = new Vector3(0, position.y, position.z); - ____effectSprites[2 * i + 1].color = Color.white; - } - } - } - else if (___EachFlag) - { - for (var i = 0; i < 11; i++) - { - sprite = customSlideFan[1, i]; - if (sprite != null) - { - ____spriteLines[2 * i].sprite = sprite; - position = ____spriteLines[2 * i].transform.localPosition; - ____spriteLines[2 * i].transform.localPosition = new Vector3(0, position.y, position.z); - ____spriteLines[2 * i].color = Color.white; - - ____spriteLines[2 * i + 1].sprite = sprite; - position = ____spriteLines[2 * i + 1].transform.localPosition; - ____spriteLines[2 * i + 1].transform.localPosition = new Vector3(0, position.y, position.z); - ____spriteLines[2 * i + 1].color = Color.white; - } - } - } - else - { - for (var i = 0; i < 11; i++) - { - sprite = customSlideFan[0, i]; - if (sprite != null) - { - ____spriteLines[2 * i].sprite = sprite; - position = ____spriteLines[2 * i].transform.localPosition; - ____spriteLines[2 * i].transform.localPosition = new Vector3(0, position.y, position.z); - ____spriteLines[2 * i].color = Color.white; - - ____spriteLines[2 * i + 1].sprite = sprite; - position = ____spriteLines[2 * i + 1].transform.localPosition; - ____spriteLines[2 * i + 1].transform.localPosition = new Vector3(0, position.y, position.z); - ____spriteLines[2 * i + 1].color = Color.white; - } - } - } - } -} diff --git a/AquaMai/AquaMai.Mods/Fancy/CustomTrackStartDiff.cs b/AquaMai/AquaMai.Mods/Fancy/CustomTrackStartDiff.cs deleted file mode 100644 index 41f3623c..00000000 --- a/AquaMai/AquaMai.Mods/Fancy/CustomTrackStartDiff.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System.Collections.Generic; -using AquaMai.Config.Attributes; -using HarmonyLib; -using Monitor; -using UI; -using UnityEngine; -using UnityEngine.UI; - -namespace AquaMai.Mods.Fancy; - -[ConfigSection( - en: """ - Custom track start difficulty image (not really custom difficulty). - Requires CustomSkins to be enabled. - Will load four image resources through custom skins: musicBase, musicTab, musicLvBase, musicLvText. - """, - zh: """ - 自定义在歌曲开始界面上显示的难度贴图 (并不是真的自定义难度) - 需要启用自定义皮肤功能 - 会通过自定义皮肤加载四个图片资源: musicBase, musicTab, musicLvBase, musicLvText - """)] -public class CustomTrackStartDiff -{ - // 自定义在歌曲开始界面上显示的难度 (并不是真的自定义难度) - // 需要启用自定义皮肤功能 - // 会加载四个图片资源: musicBase, musicTab, musicLvBase, musicLvText - - [HarmonyPostfix] - [HarmonyPatch(typeof(TrackStartMonitor), "SetTrackStart")] - private static void DisableTabs( - MultipleImage ____musicBaseImage, - MultipleImage ____musicTabImage, - SpriteCounter ____difficultySingle, - SpriteCounter ____difficultyDouble, - Image ____levelTextImage, - List ____musicLevelSpriteSheets, - TimelineRoot ____musicDetail - ) - { - var texture = CustomSkins.CustomTrackStart[0]; - if (texture != null) - { - ____musicBaseImage.MultiSprites[6] = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f), 100f); - ____musicBaseImage.ChangeSprite(6); - } - - texture = CustomSkins.CustomTrackStart[1]; - if (texture != null) - { - ____musicTabImage.MultiSprites[6] = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f), 100f); - ____musicTabImage.ChangeSprite(6); - } - - texture = CustomSkins.CustomTrackStart[2]; - if (texture != null) - { - var lvBase = Traverse.Create(____musicDetail).Field("_lv_Base").Value; - lvBase.MultiSprites[6] = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f), 100f); - lvBase.ChangeSprite(6); - } - - texture = CustomSkins.CustomTrackStart[3]; - if (texture != null) - { - var original = ____musicLevelSpriteSheets[0].Sheet; - var sheet = new Sprite[original.Length]; - for (var i = 0; i < original.Length; i++) - { - var sprite = original[i]; - sheet[i] = Sprite.Create(texture, sprite.textureRect, new Vector2(0.5f, 0.5f), 100f); - } - - ____difficultySingle.SetSpriteSheet(sheet); - ____difficultyDouble.SetSpriteSheet(sheet); - ____levelTextImage.sprite = sheet[14]; - } - } -} diff --git a/AquaMai/AquaMai.Mods/Fancy/CustomVersionString.cs b/AquaMai/AquaMai.Mods/Fancy/CustomVersionString.cs deleted file mode 100644 index cbada55d..00000000 --- a/AquaMai/AquaMai.Mods/Fancy/CustomVersionString.cs +++ /dev/null @@ -1,30 +0,0 @@ -using AquaMai.Config.Attributes; -using HarmonyLib; - -namespace AquaMai.Mods.Fancy; - -[ConfigSection( - en: "Set the version string displayed at the top-right corner of the screen.", - zh: "把右上角的版本更改为自定义文本")] -public class CustomVersionString -{ - [ConfigEntry] - private static readonly string versionString = ""; - - /* - * Patch displayVersionString Property Getter - */ - [HarmonyPrefix] - [HarmonyPatch(typeof(MAI2System.Config), "displayVersionString", MethodType.Getter)] - public static bool GetDisplayVersionString(ref string __result) - { - if (string.IsNullOrEmpty(versionString)) - { - return true; - } - - __result = versionString; - // Return false to block the original method - return false; - } -} diff --git a/AquaMai/AquaMai.Mods/Fancy/DemoMaster.cs b/AquaMai/AquaMai.Mods/Fancy/DemoMaster.cs deleted file mode 100644 index 12939c44..00000000 --- a/AquaMai/AquaMai.Mods/Fancy/DemoMaster.cs +++ /dev/null @@ -1,34 +0,0 @@ -using AquaMai.Config.Attributes; -using DB; -using HarmonyLib; -using MAI2.Util; -using Manager; -using Process; - -namespace AquaMai.Mods.Fancy; - -[ConfigSection( - en: "Play \"Master\" difficulty on Demo screen.", - zh: "在闲置时的演示画面上播放紫谱而不是绿谱")] -public class DemoMaster -{ - [HarmonyPostfix] - [HarmonyPatch(typeof(AdvDemoProcess), "OnStart")] - public static void AdvDemoProcessPostStart() - { - for (int i = 0; i < 2; i++) - { - var userOption = Singleton.Instance.GetGameScore(i).UserOption; - userOption.NoteSpeed = OptionNotespeedID.Speed6_5; - userOption.TouchSpeed = OptionTouchspeedID.Speed7_0; - } - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(GamePlayManager), "InitializeAdvertise")] - public static void PreInitializeAdvertise() - { - GameManager.SelectDifficultyID[0] = 3; - GameManager.SelectDifficultyID[1] = 3; - } -} diff --git a/AquaMai/AquaMai.Mods/Fancy/GamePlay/AlignCircleSlideJudgeDisplay.cs b/AquaMai/AquaMai.Mods/Fancy/GamePlay/AlignCircleSlideJudgeDisplay.cs deleted file mode 100644 index 508e5e88..00000000 --- a/AquaMai/AquaMai.Mods/Fancy/GamePlay/AlignCircleSlideJudgeDisplay.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using AquaMai.Config.Attributes; -using HarmonyLib; -using Manager; -using Monitor; -using UnityEngine; - -namespace AquaMai.Mods.Fancy.GamePlay; - -[ConfigSection( - en: """ - Make the judgment display of circular Slides align precisely with the judgment line (originally a bit off). - Just like in majdata. - """, - zh: """ - 让圆弧形的 Slide 的判定显示与判定线精确对齐 (原本会有一点歪) - 就像 majdata 里那样 - """)] -public class AlignCircleSlideJudgeDisplay -{ - /* - * 这个 Patch 让圆弧形的 Slide 的判定显示与判定线精确对齐 (原本会有一点歪), 就像 majdata 里那样 - */ - [HarmonyPostfix] - [HarmonyPatch(typeof(SlideRoot), "Initialize")] - private static void FixJudgePosition( - SlideRoot __instance, SlideType ___EndSlideType, SlideJudge ___JudgeObj - ) - { - if (null != ___JudgeObj) - { - float z = ___JudgeObj.transform.localPosition.z; - if (___EndSlideType == SlideType.Slide_Circle_L) - { - float angle = -45.0f - 45.0f * __instance.EndButtonId; - double angleRad = Math.PI / 180.0 * (angle + 90 + 22.5 + 2.6415); - ___JudgeObj.transform.localPosition = new Vector3(480f * (float)Math.Cos(angleRad), 480f * (float)Math.Sin(angleRad), z); - ___JudgeObj.transform.localRotation = Quaternion.Euler(0.0f, 0.0f, angle); - } - else if (___EndSlideType == SlideType.Slide_Circle_R) - { - float angle = -45.0f * __instance.EndButtonId; - double angleRad = Math.PI / 180.0 * (angle + 90 - 22.5 - 2.6415); - ___JudgeObj.transform.localPosition = new Vector3(480f * (float)Math.Cos(angleRad), 480f * (float)Math.Sin(angleRad), z); - ___JudgeObj.transform.localRotation = Quaternion.Euler(0.0f, 0.0f, angle); - } - } - } - - -} diff --git a/AquaMai/AquaMai.Mods/Fancy/GamePlay/BreakSlideJudgeBlink.cs b/AquaMai/AquaMai.Mods/Fancy/GamePlay/BreakSlideJudgeBlink.cs deleted file mode 100644 index 175f605c..00000000 --- a/AquaMai/AquaMai.Mods/Fancy/GamePlay/BreakSlideJudgeBlink.cs +++ /dev/null @@ -1,33 +0,0 @@ -using AquaMai.Config.Attributes; -using HarmonyLib; -using Monitor; -using UnityEngine; - -namespace AquaMai.Mods.Fancy.GamePlay; - -[ConfigSection( - en: """ - This Patch makes the Critical judgment of BreakSlide also flicker like BreakTap. - Recommended to use with custom skins (otherwise the visual effect may not be good). - """, - zh: """ - 这个 Patch 让 BreakSlide 的 Critical 判定也可以像 BreakTap 一样闪烁 - 推荐与自定义皮肤一起使用 (否则视觉效果可能并不好) - """)] -public class BreakSlideJudgeBlink -{ - /* - * 这个 Patch 让 BreakSlide 的 Critical 判定也可以像 BreakTap 一样闪烁 - * 推荐与自定义皮肤一起使用 (否则视觉效果可能并不好) - */ - [HarmonyPostfix] - [HarmonyPatch(typeof(SlideJudge), "UpdateBreakEffectAdd")] - private static void FixBreakSlideJudgeBlink( - SpriteRenderer ___SpriteRenderAdd, int ____addEffectCount - ) - { - if (!___SpriteRenderAdd.gameObject.activeSelf) return; - float num = (____addEffectCount & 0b10) >> 1; - ___SpriteRenderAdd.color = new Color(num, num, num, 1f); - } -} diff --git a/AquaMai/AquaMai.Mods/Fancy/GamePlay/CustomNoteTypes/CustomNoteTypes.cs b/AquaMai/AquaMai.Mods/Fancy/GamePlay/CustomNoteTypes/CustomNoteTypes.cs deleted file mode 100644 index 0614c970..00000000 --- a/AquaMai/AquaMai.Mods/Fancy/GamePlay/CustomNoteTypes/CustomNoteTypes.cs +++ /dev/null @@ -1,433 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Reflection.Emit; -using AquaMai.Config.Attributes; -using DB; -using HarmonyLib; -using MAI2.Util; -using Manager; -using MelonLoader; -using Monitor; -using UnityEngine; -using AquaMai.Mods.Fancy.GamePlay.CustomNoteTypes.Libs; - -namespace AquaMai.Mods.Fancy.GamePlay.CustomNoteTypes; - -[ConfigCollapseNamespace] -[ConfigSection( - en: "Custom Note Types.", - zh: "自定义 Note 类型" -)] -public class CustomNoteTypes -{ - /* - * ========== ========== ========== ========== ========== ========== ========== ========== - * 以下内容是为了添加新的 MA2 语法用于表示自定义的 note 类型 - * The following part is to add new MA2 command to Sinmai (representing custom note types) - * - * New note types: - * 1. Slide Super-new Super-hot (NMSSS, BRSSS, EXSSS, BXSSS, CNSSS): - * Definition: ??SSS [bar] [grid] [start pos] [wait] [duration] [end pos] [slide code (string)] - * Represent a slide note with highly customized path (using slide code) - * - * TODO (?) - * Mine notes (P.S. Mine-slides will automatically progress itself) - * Individual tracing duration in conn. slides - * Touch-slides / slides not ending in group A - * Non-C TouchHold - * Spinning tailless star (something like 1$$) - * Hyper Speed Definition ? - */ - public static int TotalMa2RecordCount = -1; - public static int LastMa2RecordID = -1; - public static Array Ma2FileRecordData; - - public static void OnAfterPatch() - { - var arrayTraverse = Traverse.Create(typeof(Ma2fileRecordID)).Field("s_Ma2fileRecord_Data"); - var targetArray = arrayTraverse.GetValue(); - - var nextId = targetArray.Length; - object[][] newEntries = - [ - [nextId++, "NMSSS", "过新过热Slide", NotesTypeID.Def.Slide, SlideType.Slide_MAX, 8, Ma2Category.MA2_Note, 2, 2, 2, 2, 2, 2, 0], - [nextId++, "BRSSS", "过新过热BreakSlide", NotesTypeID.Def.BreakSlide, SlideType.Slide_MAX, 8, Ma2Category.MA2_Note, 2, 2, 2, 2, 2, 2, 0], - [nextId++, "EXSSS", "过新过热ExSlide", NotesTypeID.Def.ExSlide, SlideType.Slide_MAX, 8, Ma2Category.MA2_Note, 2, 2, 2, 2, 2, 2, 0], - [nextId++, "BXSSS", "过新过热ExBreakSlide", NotesTypeID.Def.ExBreakSlide, SlideType.Slide_MAX, 8, Ma2Category.MA2_Note, 2, 2, 2, 2, 2, 2, 0], - [nextId++, "CNSSS", "过新过热ConnSlide", NotesTypeID.Def.ConnectSlide, SlideType.Slide_MAX, 8, Ma2Category.MA2_Note, 2, 2, 2, 2, 2, 2, 0], - ]; - - // Ma2fileRecordID.Ma2fileRecord_Data is private, so we need this shit. - var structType = targetArray.GetValue(0).GetType(); - var constructor = AccessTools.Constructor(structType, - [ - typeof(int), typeof(string), typeof(string), typeof(NotesTypeID.Def), typeof(SlideType), typeof(int), - typeof(Ma2Category), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), - typeof(int) - ]); - - Ma2FileRecordData = Array.CreateInstance(structType, targetArray.Length + newEntries.Length); - for (var i = 0; i < targetArray.Length; i++) - { - Ma2FileRecordData.SetValue(targetArray.GetValue(i), i); - } - - for (var i = 0; i < newEntries.Length; i++) - { - var j = targetArray.Length + i; - var obj = constructor.Invoke(newEntries[i]); - Ma2FileRecordData.SetValue(obj, j); - } - - arrayTraverse.SetValue(Ma2FileRecordData); - TotalMa2RecordCount = Ma2FileRecordData.Length; - LastMa2RecordID = TotalMa2RecordCount - 1; - MelonLogger.Msg($"[CustomNoteType] MA2 record data extended, total count: {TotalMa2RecordCount}"); - - // Initialize related classes ... - SlideDataBuilder.InitializeHitAreasLookup(); - MelonLogger.Msg($"[CustomNoteType] HitAreasLookup initialized, total count: {SlideDataBuilder.HitAreasLookup.Count}"); - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(Ma2fileRecordID), "findID")] - public static bool FindIDPrefix(string enumName, ref Ma2fileRecordID.Def __result) - { - // I don't know why but patching findID() leads to a completely invalid result - // Sometimes it will even throw an exception - // So I can only prefix it and override it - __result = Ma2fileRecordID.Def.Invalid; - for (var i = 0; i < TotalMa2RecordCount; i++) - { - var item = Ma2FileRecordData.GetValue(i); - if (Traverse.Create(item).Field("enumName").Value == enumName) - { - __result = (Ma2fileRecordID.Def)i; - } - } - - return false; - } - - [HarmonyPatch] - public static class Ma2RecordValidation - { - public static IEnumerable TargetMethods() - { - return - [ - // AccessTools.Method(typeof(Ma2fileRecordID), "findID"), - AccessTools.Method(typeof(Ma2fileRecordID), "clamp"), - AccessTools.Method(typeof(Ma2fileRecordID), "getClampValue"), - AccessTools.Method(typeof(Ma2fileRecordID), "isValid"), - AccessTools.Method(typeof(Ma2fileRecordID_Extension), "isValid"), - ]; - } - - public static IEnumerable Transpiler(IEnumerable instructions) - { - - foreach (var inst in instructions) - { - if (inst.LoadsConstant(142)) - { - var instNew = new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(CustomNoteTypes), "TotalMa2RecordCount")); - yield return instNew; - } - else if (inst.LoadsConstant(141)) - { - var instNew = new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(CustomNoteTypes), "LastMa2RecordID")); - yield return instNew; - } - else - { - yield return inst; - } - } - } - } - - /* - * ========== ========== ========== ========== ========== ========== ========== ========== - * 以下内容是给新的 MA2 语法写解析器 - */ - - /* - * 给新建的 noteData 初始化应有的数据, 仅仅是照搬了 NotesReader.loadNote - */ - public static void PrepareBasicNoteData(NoteData noteData, NotesReader reader, - MA2Record record, int index, ref int noteIndex, OptionMirrorID mirrorMode) - { - noteData.type = record.getType().getNotesTypeId(); - noteData.time.init(record.getBar(), record.getGrid(), reader); - noteData.end = noteData.time; - noteData.startButtonPos = MaiGeometry.MirrorInfo[(int)mirrorMode, record.getPos()]; - noteData.index = index; - var num = record.getGrid() % 96; - if (num == 0) - { - noteData.beatType = NoteData.BeatType.BeatType04; - } - else if (num % 48 == 0) - { - noteData.beatType = NoteData.BeatType.BeatType08; - } - else if (num % 24 == 0) - { - noteData.beatType = NoteData.BeatType.BeatType16; - } - else if (num % 16 == 0) - { - noteData.beatType = NoteData.BeatType.BeatType24; - } - else - { - noteData.beatType = NoteData.BeatType.BeatTypeOther; - } - noteData.indexNote = noteIndex; - ++noteIndex; - } - - /* - * 给新建的 noteData 填入基本的 slide 相关数据, 仅仅是照搬了 NotesReader.loadNote - */ - public static void PrepareBasicSlideData(NoteData noteData, NotesReader reader, MA2Record record, int noteIndex, - ref int slideIndex, OptionMirrorID mirrorMode) - { - noteData.indexSlide = slideIndex++; - var slideData = noteData.slideData; - var slideWaitLen = record.getSlideWaitLen(); - var slideShootLen = record.getSlideShootLen(); - slideData.targetNote = MaiGeometry.MirrorInfo[(int)mirrorMode, record.getSlideEndPos()]; - slideData.shoot.time.init(record.getBar(), record.getGrid() + slideWaitLen, reader); - slideData.shoot.index = noteIndex; - slideData.arrive.time.init(record.getBar(), record.getGrid() + slideWaitLen + slideShootLen, reader); - slideData.arrive.index = noteIndex; - noteData.end = slideData.arrive.time; - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(NotesReader), "loadNote")] - public static bool LoadCustomNote(NotesReader __instance, ref bool __result, NotesData ____note, int ____playerID, - MA2Record rec, int index, ref int noteIndex, ref int slideIndex) - { - if (rec.getType() < Ma2fileRecordID.Def.End) - { - // builtin record type - return true; - } - - MelonLogger.Msg($"[CustomNoteType] Custom note | {rec._str.Count} | {rec.getStr(0)} {rec.getStr(1)} {rec.getStr(2)} {rec.getStr(3)} {rec.getStr(4)} {rec.getStr(5)} {rec.getStr(6)} {rec.getStr(7)} {rec.getStr(8)}"); - - var flag = true; - switch (rec.getType().getEnumName()) - { - case "NMSSS": - case "BRSSS": - case "EXSSS": - case "BXSSS": - case "CNSSS": - var noteData = new CustomSlideNoteData(); - var mirrorMode = Singleton.Instance.GetGameScore(____playerID).UserOption.MirrorMode; - PrepareBasicNoteData(noteData, __instance, rec, index, ref noteIndex, mirrorMode); - PrepareBasicSlideData(noteData, __instance, rec, noteIndex, ref slideIndex, mirrorMode); - var success = noteData.ParseSlideCode(rec.getStr(7), mirrorMode); - if (success) - { - ____note._noteData.Add(noteData); - } - else - { - flag = false; - } - break; - default: - flag = false; - break; - } - __result = flag; - return false; - } - - /* - * ========== ========== ========== ========== ========== ========== ========== ========== - * 以下内容是为了实现自定义 Slide - * - */ - - /* - * 把 GetSlidePath 和 GetSlideHitArea 和 GetSlideLength 重定向到我可以控制的函数上, 并且多推几个参数进来 - */ - [HarmonyPatch] - public static class SlideNoteDataHack - { - public static IEnumerable TargetMethods() - { - return - [ - AccessTools.Method(typeof(SlideRoot), "Initialize"), - AccessTools.Method(typeof(SlideRoot), "GetSlideArrowNum", [typeof(NoteData)]), - AccessTools.Method(typeof(StarNote), "Initialize"), - AccessTools.Method(typeof(BreakStarNote), "Initialize"), - ]; - } - - public static IEnumerable Transpiler(IEnumerable instructions) - { - var methodGetSlidePath = AccessTools.Method(typeof(SlideManager), "GetSlidePath"); - var methodGetSlidePathRedirect = AccessTools.Method(typeof(CustomNoteTypes), "GetSlidePathRedirect"); - var methodGetSlideHitArea = AccessTools.Method(typeof(SlideManager), "GetSlideHitArea"); - var methodGetSlideHitAreaRedirect = AccessTools.Method(typeof(CustomNoteTypes), "GetSlideHitAreaRedirect"); - var methodGetSlideLength = AccessTools.Method(typeof(SlideManager), "GetSlideLength"); - var methodGetSlideLengthRedirect = AccessTools.Method(typeof(CustomNoteTypes), "GetSlideLengthRedirect"); - var fieldSlideData = AccessTools.Field(typeof(NoteData), "slideData"); - - var oldInstList = new List(instructions); - var newInstList = new List(); - CodeInstruction instToInject = null; - - for (var i = 0; i < oldInstList.Count; ++i) - { - var inst = oldInstList[i]; - if (inst.LoadsField(fieldSlideData)) - { - // 以 GetSlidePath 为例, 我们需要把下面这个调用: - // Singleton.Instance.GetSlidePath( - // noteData.slideData.type, noteData.startButtonPos, - // noteData.slideData.targetNote, this.ButtonId - // ) - // 里的 noteData 拿到手 - // 所以就记录上一次 ldfld NoteData::slideData 的位置, 往前找一个 IL code - // 找到的就是 load 这个 noteData 的位置 - // 然后在后续调用 GetSlidePath 时, 先重复一遍 load 把这个 noteData 入栈, 然后重定向到一个新的函数上去 - instToInject = oldInstList[i - 1]; - newInstList.Add(inst); - } - else if (inst.Calls(methodGetSlidePath)) - { - newInstList.Add(instToInject!.Clone()); - newInstList.Add(new CodeInstruction(OpCodes.Call, methodGetSlidePathRedirect)); - instToInject = null; - } - else if (inst.Calls(methodGetSlideHitArea)) - { - newInstList.Add(instToInject!.Clone()); - newInstList.Add(new CodeInstruction(OpCodes.Call, methodGetSlideHitAreaRedirect)); - instToInject = null; - } - else if (inst.Calls(methodGetSlideLength)) - { - newInstList.Add(instToInject!.Clone()); - newInstList.Add(new CodeInstruction(OpCodes.Call, methodGetSlideLengthRedirect)); - instToInject = null; - } - else - { - newInstList.Add(inst); - } - } - return newInstList; - } - } - - - public static List GetSlidePathRedirect(SlideManager instance, SlideType slideType, int start, int end, - int starButton, NoteData noteData) - { - // MelonLogger.Msg($"[CustomNoteType] GetSlidePath Redirected!"); - // MelonLogger.Msg($"{noteData.indexNote} {noteData.indexSlide} {slideType} {start} {end} {starButton}"); - if (noteData is CustomSlideNoteData data) - { - // MelonLogger.Msg($"[CustomNoteType] Successfully injected custom path {data.SlideCode}"); - return data.SlidePathList[starButton]; - } - return instance.GetSlidePath(slideType, start, end, starButton); - } - - public static List GetSlideHitAreaRedirect(SlideManager instance, SlideType slideType, - int start, int end, int starButton, NoteData noteData) - { - // MelonLogger.Msg($"[CustomNoteType] GetSlideHitArea Redirected!"); - // MelonLogger.Msg($"{noteData.indexNote} {noteData.indexSlide} {slideType} {start} {end} {starButton}"); - if (noteData is CustomSlideNoteData data) - { - // MelonLogger.Msg($"[CustomNoteType] Successfully injected custom hit areas {data.SlideCode}"); - return data.SlideHitAreasList[starButton]; - } - return instance.GetSlideHitArea(slideType, start, end, starButton); - } - - public static float GetSlideLengthRedirect(SlideManager instance, SlideType slideType, - int start, int end, NoteData noteData) - { - // MelonLogger.Msg($"[CustomNoteType] GetSlideLength Redirected!"); - // MelonLogger.Msg($"{noteData.indexNote} {noteData.indexSlide} {slideType} {start} {end}"); - if (noteData is CustomSlideNoteData data) - { - // MelonLogger.Msg($"[CustomNoteType] Successfully injected custom path length {data.SlideCode}"); - return data.SlidePathLength; - } - return instance.GetSlideLength(slideType, start, end); - } - - - - [HarmonyPatch] - public static class Debuging - { - public static IEnumerable TargetMethods() - { - return - [ - AccessTools.Method(typeof(SlideRoot), "Initialize"), - // AccessTools.Method(typeof(SlideRoot), "GetSlideArrowNum", []), - // AccessTools.Method(typeof(SlideRoot), "GetSlideArrowNum", [typeof(NoteData)]), - // AccessTools.Method(typeof(SlideRoot), "GetArrowData"), - // AccessTools.Method(typeof(SlideRoot), "totalDistance"), - // AccessTools.Method(typeof(SlideRoot), "GetActiveArrowNum"), - // AccessTools.Method(typeof(SlideJudge), "SetJudgeType"), - ]; - } - - public static void Prefix(MethodBase __originalMethod, object[] __args) - { - var msg = "[CustomNoteType] Before "; - msg += __originalMethod.DeclaringType!.FullName + "." + __originalMethod.Name + " ("; - var infos = __originalMethod.GetParameters() - .Select((x, i) => x.ParameterType.FullName + " " + x.Name + " = " + GetString(__args[i])) - .ToArray(); - msg += infos.Length > 0 ? infos.Aggregate((a, b) => a + ", " + b) : "void"; - msg += ")"; - MelonLogger.Msg(msg); - } - - public static void Postfix(MethodBase __originalMethod, object[] __args) - { - var msg = "[CustomNoteType] After "; - msg += __originalMethod.DeclaringType!.FullName + "." + __originalMethod.Name + " ("; - var infos = __originalMethod.GetParameters() - .Select((x, i) => x.ParameterType.FullName + " " + x.Name + " = " + GetString(__args[i])) - .ToArray(); - msg += infos.Length > 0 ? infos.Aggregate((a, b) => a + ", " + b) : "void"; - msg += ")"; - MelonLogger.Msg(msg); - } - - public static string GetString(object value) - { - if (value is CustomSlideNoteData data) - { - return $""; - } - - if (value is NoteData data2) - { - return $""; - } - - return value.ToString(); - } - } -} diff --git a/AquaMai/AquaMai.Mods/Fancy/GamePlay/CustomNoteTypes/Libs/CustomSlideNoteData.cs b/AquaMai/AquaMai.Mods/Fancy/GamePlay/CustomNoteTypes/Libs/CustomSlideNoteData.cs deleted file mode 100644 index a7531e8a..00000000 --- a/AquaMai/AquaMai.Mods/Fancy/GamePlay/CustomNoteTypes/Libs/CustomSlideNoteData.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using DB; -using Manager; -using MelonLoader; -using UnityEngine; - -namespace AquaMai.Mods.Fancy.GamePlay.CustomNoteTypes.Libs; - -public class CustomSlideNoteData: NoteData -{ - public string SlideCode; - public List> SlidePathList = new List>(); - public List> SlideHitAreasList = new List>(); - public float SlidePathLength; - - public bool ParseSlideCode(string slideCode, OptionMirrorID mirrorMode) - { - if (string.IsNullOrEmpty(slideCode)) - { - return false; - } - - SlidePathList.Clear(); - SlideHitAreasList.Clear(); - - this.SlideCode = slideCode; - var path = SlideCodeParser.Parse(slideCode); - if (path == null) - { - return false; - } - - var arrowData = SlideDataBuilder.BuildArrowData(path); - SlidePathLength = (float)path.GetPathLength(); - var hitAreaData = SlideDataBuilder.BuildHitAreas(path); - for (var i = 0; i < 8; i++) - { - SlidePathList.Add(SlideDataBuilder.ConvertAndRotateArrowData(arrowData, i, mirrorMode)); - SlideHitAreasList.Add(SlideDataBuilder.ConvertAndRotateHitAreas(hitAreaData, i, mirrorMode)); - } - - var msg = string.Join(", ", - hitAreaData.Select(x => x.PanelAreas).Select(x => string.Join("/", x.Cast()))); - MelonLogger.Msg(msg); - - this.slideData.type = path.GetEndType(mirrorMode); - - return true; - } -} diff --git a/AquaMai/AquaMai.Mods/Fancy/GamePlay/CustomNoteTypes/Libs/MaiGeometry.cs b/AquaMai/AquaMai.Mods/Fancy/GamePlay/CustomNoteTypes/Libs/MaiGeometry.cs deleted file mode 100644 index 7866424b..00000000 --- a/AquaMai/AquaMai.Mods/Fancy/GamePlay/CustomNoteTypes/Libs/MaiGeometry.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System; -using System.Numerics; - -namespace AquaMai.Mods.Fancy.GamePlay.CustomNoteTypes.Libs; - -public static class MaiGeometry -{ - public struct CircleStruct(Complex center, double radius) - { - public Complex Center = center; - public double Radius = radius; - } - - public static readonly double CanvasWidth = 1080.0; - public static readonly double MainRadius = 480.0; - public static readonly double CenterRadius = MainRadius * Math.Cos(Math.PI * 3 / 8); - public static readonly double GroupBRadius = CenterRadius / Math.Cos(Math.PI / 8); - - private static readonly double _b = Math.Cos(Math.PI / 8) / 2; - private static readonly double _a = 1 - _b; - private static readonly double _theta = Math.PI / 4; - private static readonly double _s = (_a * _a + _b * _b - 2 * _a * _b * Math.Cos(_theta)) / - (2 * _a - 2 * _b * Math.Cos(_theta)); - - public static readonly double PPQQRadius = MainRadius * _b; - public static readonly double TransferRadius = MainRadius * (_b + _s); - public static readonly double EdgeTransferAngle = _theta; - public static readonly double PPQQTransferAngle = - Math.Acos((_s * _s + _b * _b - (_a - _s) * (_a - _s)) / (2 * _b * _s)); - - public static readonly double DefaultDistance = MainRadius * Math.PI / 32; - - public static readonly int[,] MirrorInfo = new int[4, 17] - { - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }, // Normal - { 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, 16 }, // L <-> R - { 3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12, 16 }, // U <-> D - { 4, 5, 6, 7, 0, 1, 2, 3, 12, 13, 14, 15, 8, 9, 10, 11, 16 } // rotate 180 deg - }; - - /// - /// Note: idx is 1-based, not 0-based - /// - public static Complex PointGroupA(int idx) - { - var angle = Math.PI * (5.0 / 8.0 - idx / 4.0); - return Complex.FromPolarCoordinates(MainRadius, angle); - } - - /// - /// Note: idx is 1-based, not 0-based - /// - public static Complex PointGroupB(int idx) - { - var angle = Math.PI * (5.0 / 8.0 - idx / 4.0); - return Complex.FromPolarCoordinates(GroupBRadius, angle); - } - - public static Complex Center() - { - return Complex.Zero; - } - - /// - /// idx 0 is center circle, idx 1~8 are ppqq circles, idx 9 is outer circle - /// - public static CircleStruct GetCircle(int idx) - { - if (idx == 0) - { - return new CircleStruct(Complex.Zero, CenterRadius); - } - - if (idx == 9) - { - return new CircleStruct(Complex.Zero, MainRadius); - } - - var angle = Math.PI * (3.0 / 4.0 - idx / 4.0); - var center = Complex.FromPolarCoordinates(PPQQRadius, angle); - return new CircleStruct(center, PPQQRadius); - } - - /// - /// Note: idx is 1-based, not 0-based - /// - /// CircleStruct TransferCircle, double TransferStartAngle, double TransferEndAngle - public static Tuple TransferOutData(int idx, bool isccw) - { - var ppqqRad = Math.PI * (3.0 / 4.0 - idx / 4.0); - double startAngle, endAngle; - if (isccw) - { - startAngle = ppqqRad - PPQQTransferAngle; - endAngle = ppqqRad + EdgeTransferAngle; - } - else - { - startAngle = ppqqRad + PPQQTransferAngle; - endAngle = ppqqRad - EdgeTransferAngle; - } - var d = MainRadius - TransferRadius; - var center = Complex.FromPolarCoordinates(d, endAngle); - return new Tuple(new CircleStruct(center, TransferRadius), - Math.IEEERemainder(startAngle, Math.PI * 2), Math.IEEERemainder(endAngle, Math.PI * 2)); - } -} diff --git a/AquaMai/AquaMai.Mods/Fancy/GamePlay/CustomNoteTypes/Libs/ParametricSlidePath.cs b/AquaMai/AquaMai.Mods/Fancy/GamePlay/CustomNoteTypes/Libs/ParametricSlidePath.cs deleted file mode 100644 index 26832c42..00000000 --- a/AquaMai/AquaMai.Mods/Fancy/GamePlay/CustomNoteTypes/Libs/ParametricSlidePath.cs +++ /dev/null @@ -1,226 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using DB; -using Manager; - -namespace AquaMai.Mods.Fancy.GamePlay.CustomNoteTypes.Libs; - -public class ParametricSlidePath -{ - public enum ParseMarker - { - None = 0, - SmoothAlign, - ForceAlign, - SharpCorner - } - - public abstract class PathSegment - { - public ParseMarker ParseMarker = ParseMarker.None; - public double ArrowDistance = MaiGeometry.DefaultDistance; - - public abstract bool DoAngleLerp { get; } - - public abstract Complex GetPointAt(double t); - - public abstract Complex GetTangentAt(double t); - - public abstract double GetSegmentLength(); - - public void SetParseMarker(ParseMarker marker) => ParseMarker = marker; - - public void SetArrowDistance(double distance) => ArrowDistance = distance; - } - - public class LineSegment(Complex start, Complex end) : PathSegment - { - public readonly Complex StartPoint = start; - public readonly Complex EndPoint = end; - - public override bool DoAngleLerp { get; } = false; - - public override Complex GetPointAt(double t) - { - return StartPoint + (EndPoint - StartPoint) * t; - } - - public override Complex GetTangentAt(double t) - { - var v = EndPoint - StartPoint; - return v / v.Magnitude; - } - - public override double GetSegmentLength() - { - return (EndPoint - StartPoint).Magnitude; - } - } - - public class ArcSegment(MaiGeometry.CircleStruct circle, double startAngle, double endAngle) : PathSegment - { - public readonly MaiGeometry.CircleStruct Circle = circle; - public readonly double StartAngle = startAngle; - public readonly double EndAngle = endAngle; - - public override bool DoAngleLerp { get; } = true; - - public override Complex GetPointAt(double t) - { - var angle = StartAngle + t * (EndAngle - StartAngle); - return Circle.Center + Complex.FromPolarCoordinates(Circle.Radius, angle); - } - - public override Complex GetTangentAt(double t) - { - var angle = StartAngle + t * (EndAngle - StartAngle); - if (StartAngle < EndAngle) - { - return Complex.FromPolarCoordinates(1, angle) * Complex.ImaginaryOne; - } - else - { - return Complex.FromPolarCoordinates(-1, angle) * Complex.ImaginaryOne; - } - } - - public override double GetSegmentLength() - { - return Math.Abs(EndAngle - StartAngle) * Circle.Radius; - } - } - - public class CircleSegment(MaiGeometry.CircleStruct circle, double startAngle, bool isCcw) : PathSegment - { - public readonly MaiGeometry.CircleStruct Circle = circle; - public readonly double StartAngle = startAngle; - public readonly bool IsCcw = isCcw; - - public override bool DoAngleLerp { get; } = true; - - public override Complex GetPointAt(double t) - { - double angle; - if (IsCcw) - { - angle = StartAngle + t * Math.PI * 2f; - } - else - { - angle = StartAngle - t * Math.PI * 2f; - } - - return Circle.Center + Complex.FromPolarCoordinates(Circle.Radius, angle); - } - - public override Complex GetTangentAt(double t) - { - double angle; - if (IsCcw) - { - angle = StartAngle + t * Math.PI * 2f; - return Complex.FromPolarCoordinates(1, angle) * Complex.ImaginaryOne; - } - else - { - angle = StartAngle - t * Math.PI * 2f; - return Complex.FromPolarCoordinates(-1, angle) * Complex.ImaginaryOne; - } - } - - public override double GetSegmentLength() - { - return Math.PI * Circle.Radius * 2; - } - } - - - public readonly PathSegment[] Segments; - public readonly double[] Fractions; - public readonly double[] AccumulatedLengths; - - public ParametricSlidePath(IEnumerable pathSegments) - { - Segments = pathSegments.ToArray(); - if (Segments.Length == 0) - { - throw new ArgumentException("At least one path segment is required."); - } - var lengths = Segments.Select(s => s.GetSegmentLength()); - var sum = 0.0; - AccumulatedLengths = lengths.Select(x => (sum += x)).ToArray(); - Fractions = AccumulatedLengths.Select(x => x / sum).ToArray(); - } - - public PathSegment GetSegmentAt(double t, out double segmentT) - { - if (t <= 0.0) - { - segmentT = 0.0; - return Segments[0]; - } - - if (t >= 1.0) - { - segmentT = 1.0; - return Segments[Segments.Length - 1]; - } - - var idx = Array.BinarySearch(Fractions, t); - if (idx < 0) - { - idx = ~idx; // first entry > t - } - // if idx >= 0 then idx is the entry == t - // so Fractions[idx-1] < t and Fractions[idx] >= t - // Note: Fractions[i] marks the end point of Segments[i] - - if (idx >= Segments.Length) - { - segmentT = 1.0; - return Segments[Segments.Length - 1]; - } - - if (idx == 0) - { - segmentT = t / Fractions[0]; - return Segments[0]; - } - - segmentT = (t - Fractions[idx - 1]) / (Fractions[idx] - Fractions[idx - 1]); - return Segments[idx]; - } - - public double GetPathLength() => AccumulatedLengths[AccumulatedLengths.Length - 1]; - - public Complex GetPointAt(double t) - { - var segment = GetSegmentAt(t, out var segT); - return segment.GetPointAt(segT); - } - - public Complex GetTangentAt(double t) - { - var segment = GetSegmentAt(t, out var segT); - return segment.GetTangentAt(segT); - } - - public SlideType GetEndType(OptionMirrorID mirrorMode) - { - var lastSegment = Segments[Segments.Length - 1]; - var flip = mirrorMode == OptionMirrorID.LR || mirrorMode == OptionMirrorID.UD; - if (lastSegment is CircleSegment circle) - { - return circle.IsCcw != flip ? SlideType.Slide_Circle_L : SlideType.Slide_Circle_R; - } - - if (lastSegment is ArcSegment arc) - { - return (arc.EndAngle > arc.StartAngle) != flip ? SlideType.Slide_Circle_L : SlideType.Slide_Circle_R; - } - - return SlideType.Slide_Straight; - } -} diff --git a/AquaMai/AquaMai.Mods/Fancy/GamePlay/CustomNoteTypes/Libs/SlideCodeParser.cs b/AquaMai/AquaMai.Mods/Fancy/GamePlay/CustomNoteTypes/Libs/SlideCodeParser.cs deleted file mode 100644 index 1aaeb7a4..00000000 --- a/AquaMai/AquaMai.Mods/Fancy/GamePlay/CustomNoteTypes/Libs/SlideCodeParser.cs +++ /dev/null @@ -1,260 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using MelonLoader; - -namespace AquaMai.Mods.Fancy.GamePlay.CustomNoteTypes.Libs; - -public static class SlideCodeParser -{ - public enum CommandType - { - Invalid = -1, - NodeA = 0, - NodeB = 1, - NodeC = 2, - OrbitCCW = 3, - OrbitCW = 4, - NodeEnd = 5 - } - - public struct Command(CommandType type, int value) - { - public CommandType Type = type; - public int Value = value; - - public static bool IsSame(Command a, Command b) - { - return a.Type == b.Type && a.Value == b.Value; - } - } - - public static readonly char[] CommandChars = - [ - 'A', 'B', 'C', 'P', 'Q', 'K' - ]; - - public static int TryParseDigit(char c) - { - if (c >= '0' && c <= '9') return c - '0'; - return -1; - } - - public static List ParseCommands(string code) - { - if (!CommandChars.Contains(code[1])) - { - throw new ArgumentException($"the 2nd char should be a command"); - } - - if (code[code.Length - 2] != 'K') - { - throw new ArgumentException($"should end with 'K' command"); - } - - var commands = new List(); - var currentType = CommandType.NodeA; - var value = TryParseDigit(code[0]); - if (value < 0) throw new ArgumentException($"invalid char '{code[0]}'"); - - commands.Add(new Command(currentType, value)); - - for (var ptr = 1; ptr < code.Length; ptr++) - { - var ch = code[ptr]; - if (CommandChars.Contains(ch)) - { - currentType = (CommandType) Array.IndexOf(CommandChars, ch); - if (currentType == CommandType.NodeC) - { - commands.Add(new Command(CommandType.NodeC, 0)); - } - } - else - { - value = TryParseDigit(ch); - if (value < 0) throw new ArgumentException($"invalid char '{ch}'"); - if (currentType == CommandType.NodeC) - { - throw new ArgumentException($"digit should not follow 'C'"); - } - commands.Add(new Command(currentType, value)); - } - } - return commands; - } - - public static Complex GetNodePosition(Command cmd) - { - switch (cmd.Type) - { - case CommandType.NodeA: - case CommandType.NodeEnd: - return MaiGeometry.PointGroupA(cmd.Value); - case CommandType.NodeB: - return MaiGeometry.PointGroupB(cmd.Value); - case CommandType.NodeC: - return MaiGeometry.Center(); - default: - throw new ArgumentException($"invalid type for node: {cmd.Type}"); - } - } - - public static void NodeToNode(SlidePathGenerator generator, Command last, Command current) - { - if (Command.IsSame(last, current)) return; - generator.LineToPoint(GetNodePosition(current)); - } - - public static void NodeToOrbit(SlidePathGenerator generator, Command last, Command current) - { - var isCcw = (current.Type == CommandType.OrbitCCW); - var node = GetNodePosition(last); - var orbit = MaiGeometry.GetCircle(current.Value); - var diff = node - orbit.Center; - if (Math.Abs(diff.Magnitude - orbit.Radius) < 0.1) - { - if (last.Type == CommandType.NodeA && current.Value == 9) - { - generator.TrySetLastParseMarker(ParametricSlidePath.ParseMarker.ForceAlign); - } - return; // node on circle, do nothing - } - - if (diff.Magnitude < orbit.Radius) - throw new ArgumentException($"impossible: {last.Type}{last.Value} -> Orbit{current.Value}"); - - generator.TangentToCircle(orbit, isCcw); - } - - public static void OrbitToNode(SlidePathGenerator generator, Command last, Command current) - { - var isCcw = (last.Type == CommandType.OrbitCCW); - var node = GetNodePosition(current); - var orbit = MaiGeometry.GetCircle(last.Value); - var diff = node - orbit.Center; - if (Math.Abs(diff.Magnitude - orbit.Radius) < 0.1) - { - generator.ArcToAngle(orbit.Center, diff.Phase, isCcw, false); - return; - } - - if (diff.Magnitude < orbit.Radius) - throw new ArgumentException($"impossible: Orbit{last.Value} -> {current.Type}{current.Value}"); - - generator.ArcToTangentTowards(node, orbit.Center, isCcw); - generator.LineToPoint(node); - } - - public static void OrbitToOrbit(SlidePathGenerator generator, Command last, Command current) - { - if (current.Type != last.Type) throw new ArgumentException($"orbit type mismatch"); - - var isCcw = (last.Type == CommandType.OrbitCCW); - var lastOrbit = MaiGeometry.GetCircle(last.Value); - var currentOrbit = MaiGeometry.GetCircle(current.Value); - if (current.Value == last.Value) - { - generator.FullCircle(lastOrbit.Center, isCcw); - return; - } - - if (last.Value == 0 && current.Value == 9 || last.Value == 9 && current.Value == 0) - throw new ArgumentException($"impossible: Orbit{last.Value} -> Orbit{current.Value}"); - - if (current.Value == 9) - { - var data = MaiGeometry.TransferOutData(last.Value, isCcw); - generator.ArcToAngle(lastOrbit.Center, data.Item2, isCcw, false); - generator.ArcToAngle(data.Item1.Center, data.Item3, isCcw, false); - generator.TrySetLastParseMarker(ParametricSlidePath.ParseMarker.SmoothAlign); - return; - } - - if (last.Value == 9) - { - var data = MaiGeometry.TransferOutData(current.Value, !isCcw); - generator.ArcToAngle(lastOrbit.Center, data.Item3, isCcw, true); - generator.ArcToAngle(data.Item1.Center, data.Item2, isCcw, false); - return; - } - - generator.ExternTangentTransfer(lastOrbit.Center, currentOrbit, isCcw); - } - - public static ParametricSlidePath Parse(string code) - { - try - { - var commands = ParseCommands(code); - var lastCmd = commands[0]; - // The first command is guarantee to be 'A' - var generator = SlidePathGenerator.BeginAt(MaiGeometry.PointGroupA(lastCmd.Value)); - - for (var i = 1; i < commands.Count; i++) - { - var cmd = commands[i]; - switch (cmd.Type) - { - case CommandType.NodeA: - case CommandType.NodeB: - case CommandType.NodeC: - case CommandType.NodeEnd: - switch (lastCmd.Type) - { - case CommandType.NodeA: - case CommandType.NodeB: - case CommandType.NodeC: - NodeToNode(generator, lastCmd, cmd); - break; - case CommandType.OrbitCCW: - case CommandType.OrbitCW: - OrbitToNode(generator, lastCmd, cmd); - break; - case CommandType.NodeEnd: - throw new ArgumentException($"'K' should be the last command"); - default: - throw new ArgumentOutOfRangeException(); - } - break; - case CommandType.OrbitCCW: - case CommandType.OrbitCW: - switch (lastCmd.Type) - { - case CommandType.NodeA: - case CommandType.NodeB: - case CommandType.NodeC: - NodeToOrbit(generator, lastCmd, cmd); - break; - case CommandType.OrbitCCW: - case CommandType.OrbitCW: - OrbitToOrbit(generator, lastCmd, cmd); - break; - case CommandType.NodeEnd: - throw new ArgumentException($"'K' should be the last command"); - default: - throw new ArgumentOutOfRangeException(); - } - break; - default: - throw new ArgumentOutOfRangeException(); - } - - lastCmd = cmd; - } - - return generator.GeneratePath(); - } - catch (ArgumentException e) - { - var msg = $"Invalid code: {code}"; - if (e.Message != "") - { - msg += $", {e.Message}"; - } - MelonLogger.Error(msg); - return null; - } - } -} diff --git a/AquaMai/AquaMai.Mods/Fancy/GamePlay/CustomNoteTypes/Libs/SlideDataBuilder.cs b/AquaMai/AquaMai.Mods/Fancy/GamePlay/CustomNoteTypes/Libs/SlideDataBuilder.cs deleted file mode 100644 index 6f6abb5b..00000000 --- a/AquaMai/AquaMai.Mods/Fancy/GamePlay/CustomNoteTypes/Libs/SlideDataBuilder.cs +++ /dev/null @@ -1,473 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Numerics; -using DB; -using Manager; -using Vector4 = UnityEngine.Vector4; - -namespace AquaMai.Mods.Fancy.GamePlay.CustomNoteTypes.Libs; - -public static class SlideDataBuilder -{ - public readonly struct ArrowData(Complex point, Complex tangent, double length) - { - public readonly Complex Point = point; - public readonly Complex Tangent = tangent; - public readonly double Length = length; - } - - public static List BuildArrowData(ParametricSlidePath path) - { - var result = new List(); - var totalLength = path.GetPathLength(); - var totalSegCount = path.Segments.Length; - - var length = 0.0; - var segIdx = 0; - var isSwitching = false; - - while (length < totalLength) - { - var t = length / totalLength; - var pt = path.GetPointAt(t); - var tg = path.GetTangentAt(t); - - var t2 = (length + 10.0) / totalLength; - if ((path.GetTangentAt(t2) - tg).Magnitude < 0.2) // 0.2 -> ~ 11.48 deg apart - { - // use secant instead of tangent (for better visual quality) - tg = path.GetPointAt(t2) - pt; - tg /= tg.Magnitude; - } - - if (isSwitching) - { - // around connecting point of 2 segments, smoothing the transition - var last = result[result.Count - 1]; - var vec = pt - last.Point; - vec /= vec.Magnitude; - if ((tg - last.Tangent).Magnitude < 0.2) - { - var x = 0.5 * (last.Tangent + vec); - x /= x.Magnitude; - result[result.Count - 1] = new ArrowData(last.Point, x, last.Length); - tg = 0.5 * (tg + vec); - tg /= tg.Magnitude; - } - } - - result.Add(new ArrowData(pt, tg, length)); - isSwitching = false; - - var nextLength = length + path.Segments[segIdx].ArrowDistance; - if (segIdx < totalSegCount - 1 && nextLength >= path.AccumulatedLengths[segIdx]) - { - isSwitching = true; - - if (path.Segments[segIdx].ParseMarker == ParametricSlidePath.ParseMarker.ForceAlign) - { - // in this case the next point is forced to be 1 unit after the connecting point - nextLength = path.AccumulatedLengths[segIdx] + path.Segments[segIdx + 1].ArrowDistance; - - // P.S. 这种情况一般是出现在一条直线连接到外圈, 这个处理是为了让外圈的箭头对齐 - } - - if (path.Segments[segIdx + 1].ParseMarker == ParametricSlidePath.ParseMarker.SmoothAlign) - { - // arrow distance of the next segment is tempered in order to align arrow - var delta = path.AccumulatedLengths[segIdx + 1] - length; - var n = Math.Round(delta / MaiGeometry.DefaultDistance); - path.Segments[segIdx + 1].SetArrowDistance(delta / n); - nextLength = length + delta / n; - - // P.S. 这种情况出现在 ppqq 圈进入外圈, 可以把转移轨道的箭头间距微调一下, 也是让外圈对齐 - } - - segIdx++; - } - - length = nextLength; - } - - // 把路径终点补上 - result.Add(new ArrowData(path.GetPointAt(1.0), path.GetTangentAt(1.0), totalLength)); - - return result; - } - - /// - /// Convert arrow data to sinmai format (Vector4) - /// - /// arrow data generated by BuildArrowData() - /// button index of slide-star - /// mirror mode in user option - /// sinmai format arrow data, referenced to slide-star - public static List ConvertAndRotateArrowData(IEnumerable data, int starButton, - OptionMirrorID mirrorMode) - { - // SBGA 用 Vector4 存储了 slide 箭头的坐标与取向 - // x, y 是平面坐标, z 是从起点到此处的路径长度 (px), w 是旋转的角度 (0 ~ 360 deg) (注意与切线方向差了 180 度) - // 坐标原点是屏幕中心, x 轴向右, y 轴向上 - // w 的零点是朝向正右 (对应于箭头朝向正左), 逆时针为正方向 - // 此外, sinmai 实际上是把所有 slide 路径相对于星星头存储的, 再在 SlideRoot 里通过 transform 转到合适的位置 - // 判定区也是相对于星星头存储, 用 InputManager.ConvertTouchPanelRotatePush() 执行旋转 - // 但是 slide code 定义的是绝对位置, 所以要逆向转回去, 以保证无论星星头在哪个键获取到的路径在处理过后都是一样的 - // 然后还需要处理镜像的问题 - - var arrowList = new List(); - var rotor = Complex.FromPolarCoordinates(1.0, Math.PI / 4.0 * starButton); - foreach (var arrow in data) - { - var pos = arrow.Point; - var tangent = arrow.Tangent; - switch (mirrorMode) - { - case OptionMirrorID.Normal: - break; - case OptionMirrorID.LR: - pos = Complex.Conjugate(pos) * -1.0; - tangent = Complex.Conjugate(tangent) * -1.0; - break; - case OptionMirrorID.UD: - pos = Complex.Conjugate(pos); - tangent = Complex.Conjugate(tangent); - break; - case OptionMirrorID.UDLR: - pos *= -1.0; - tangent *= -1.0; - break; - default: - break; - } - pos *= rotor; - tangent *= rotor; - var angle = tangent.Phase * 180.0 / Math.PI + 180.0; // Phase is in [-PI, PI] - arrowList.Add(new Vector4((float) pos.Real, (float) pos.Imaginary, (float) arrow.Length, (float) angle)); - } - return arrowList; - } - - public readonly struct HitAreaData(double push, double release, int[] areas) - { - public readonly double PushDistance = push; - public readonly double ReleaseDistance = release; - public readonly int[] PanelAreas = areas; - } - - public static readonly Dictionary HitAreasLookup = new Dictionary(); - - public static void InitializeHitAreasLookup() - { - for (var i = 0; i < 8; i++) - { - for (var j = 0; j < 8; j++) - { - var diff = (j - i) & 7; // you know this is actually % 8 ... for same negative number compat - int tmp, tmp2; - - // Ai -> Aj - var key = (i << 5) | j; - switch (diff) - { - case 1: - case 7: - HitAreasLookup[key] = - [ - new HitAreaData(0.32, 0.68, [i]), - new HitAreaData(1.00, 1.00, [j]) - ]; - break; - case 2: - tmp = (i + 1) & 7; - HitAreasLookup[key] = - [ - new HitAreaData(0.20, 0.38, [i]), - new HitAreaData(0.62, 0.80, [tmp, tmp | 8]), - new HitAreaData(1.00, 1.00, [j]) - ]; - break; - case 6: - tmp = (i - 1) & 7; - HitAreasLookup[key] = - [ - new HitAreaData(0.20, 0.38, [i]), - new HitAreaData(0.62, 0.80, [tmp, tmp | 8]), - new HitAreaData(1.00, 1.00, [j]) - ]; - break; - default: - break; - } - // Bi -> Bj - key = ((i | 8) << 5) | (j | 8); - switch (diff) - { - case 1: - case 7: - HitAreasLookup[key] = - [ - new HitAreaData(0.44, 0.56, [i | 8]), - new HitAreaData(1.00, 1.00, [j | 8]) - ]; - break; - case 2: - tmp = (i + 1) & 7; - HitAreasLookup[key] = - [ - new HitAreaData(0.22, 0.35, [i | 8]), - new HitAreaData(0.65, 0.78, [tmp | 8, 16]), - new HitAreaData(1.00, 1.00, [j | 8]) - ]; - break; - case 6: - tmp = (i - 1) & 7; - HitAreasLookup[key] = - [ - new HitAreaData(0.22, 0.35, [i | 8]), - new HitAreaData(0.65, 0.78, [tmp | 8, 16]), - new HitAreaData(1.00, 1.00, [j | 8]) - ]; - break; - case 3: - tmp = (i + 1) & 7; - tmp2 = (i + 2) & 7; - HitAreasLookup[key] = - [ - new HitAreaData(0.15, 0.28, [i | 8]), - new HitAreaData(0.48, 0.52, [tmp | 8, 16]), - new HitAreaData(0.72, 0.85, [tmp2 | 8, 16]), - new HitAreaData(1.00, 1.00, [j | 8]) - ]; - break; - case 5: - tmp = (i - 1) & 7; - tmp2 = (i - 2) & 7; - HitAreasLookup[key] = - [ - new HitAreaData(0.15, 0.28, [i | 8]), - new HitAreaData(0.48, 0.52, [tmp | 8, 16]), - new HitAreaData(0.72, 0.85, [tmp2 | 8, 16]), - new HitAreaData(1.00, 1.00, [j | 8]) - ]; - break; - default: - break; - } - // Ai <-> Bj - key = (i << 5) | (j | 8); - var key2 = ((j | 8) << 5) | i; - switch (diff) - { - case 0: - HitAreasLookup[key] = - [ - new HitAreaData(0.60, 0.75, [i]), - new HitAreaData(1.00, 1.00, [j | 8]) - ]; - HitAreasLookup[key2] = - [ - new HitAreaData(0.25, 0.40, [j | 8]), - new HitAreaData(1.00, 1.00, [i]) - ]; - break; - case 1: - case 7: - HitAreasLookup[key] = - [ - new HitAreaData(0.45, 0.77, [i]), - new HitAreaData(1.00, 1.00, [j | 8]) - ]; - HitAreasLookup[key2] = - [ - new HitAreaData(0.23, 0.55, [j | 8]), - new HitAreaData(1.00, 1.00, [i]) - ]; - break; - case 3: - tmp = (i + 1) & 7; - tmp2 = (i + 2) & 7; - HitAreasLookup[key] = - [ - new HitAreaData(0.25, 0.34, [i]), - new HitAreaData(0.54, 0.68, [i | 8, tmp | 8]), - new HitAreaData(0.85, 0.90, [tmp2 | 8, 16]), - new HitAreaData(1.00, 1.00, [j | 8]) - ]; - HitAreasLookup[key2] = - [ - new HitAreaData(0.10, 0.15, [j | 8]), - new HitAreaData(0.32, 0.46, [tmp2 | 8, 16]), - new HitAreaData(0.66, 0.75, [i | 8, tmp | 8]), - new HitAreaData(1.00, 1.00, [i]) - ]; - break; - case 5: - tmp = (i - 1) & 7; - tmp2 = (i - 2) & 7; - HitAreasLookup[key] = - [ - new HitAreaData(0.25, 0.34, [i]), - new HitAreaData(0.54, 0.68, [i | 8, tmp | 8]), - new HitAreaData(0.85, 0.90, [tmp2 | 8, 16]), - new HitAreaData(1.00, 1.00, [j | 8]) - ]; - HitAreasLookup[key2] = - [ - new HitAreaData(0.10, 0.15, [j | 8]), - new HitAreaData(0.32, 0.46, [tmp2 | 8, 16]), - new HitAreaData(0.66, 0.75, [i | 8, tmp | 8]), - new HitAreaData(1.00, 1.00, [i]) - ]; - break; - default: - break; - } - // C <-> Bj - key = (16 << 5) | (j | 8); - key2 = ((j | 8) << 5) | 16; - HitAreasLookup[key] = - [ - new HitAreaData(0.50, 0.70, [16]), - new HitAreaData(1.00, 1.00, [j | 8]) - ]; - HitAreasLookup[key2] = - [ - new HitAreaData(0.30, 0.50, [j | 8]), - new HitAreaData(1.00, 1.00, [16]) - ]; - } - } - } - - public static List BuildHitAreas(ParametricSlidePath path) - { - var nodeList = new List>(); - var totalLength = path.GetPathLength(); - var count = (int)Math.Round(totalLength / 10.0); - int? lastNode = null; - var enterLength = 0.0; - for (var i = 0; i < count; i++) - { - var t = (double)i / count; - var pt = path.GetPointAt(t); - int? node = null; - - if (pt.Magnitude < 55.0) - { - node = 16; - } - else for (var j = 0; j < 8; j++) - { - var phi = Math.PI * (3.0 / 8.0 - j / 4.0); - if ((pt - Complex.FromPolarCoordinates(440.0, phi)).Magnitude < 80.0) - { - node = j; - break; - } - - if ((pt - Complex.FromPolarCoordinates(210.0, phi)).Magnitude < 45.0) - { - node = j | 8; - break; - } - } - - if (lastNode != node) - { - var length = t * totalLength; - if (lastNode == null) - { - enterLength = length; - } - else - { - nodeList.Add(new Tuple(lastNode.Value, (length + enterLength) / 2.0)); - if (node != null) - { - enterLength = length; - } - } - } - - lastNode = node; - } - nodeList.Add(new Tuple(lastNode!.Value, totalLength)); - nodeList[0] = new Tuple(nodeList[0].Item1, 0.0); - - var result = new List(); - result.Add(new HitAreaData(0.0, 0.0, [nodeList[0].Item1])); - for (var i = 1; i < nodeList.Count; i++) - { - var key = (nodeList[i - 1].Item1 << 5) | nodeList[i].Item1; - var segmentLength = nodeList[i].Item2 - nodeList[i - 1].Item2; - var data = HitAreasLookup[key]; - var area = result[result.Count - 1]; - result[result.Count - 1] = new HitAreaData( - area.PushDistance + segmentLength * data[0].PushDistance, - area.ReleaseDistance + segmentLength * data[0].ReleaseDistance, - area.PanelAreas - ); - for (var j = 1; j < data.Length; j++) - { - result.Add(new HitAreaData( - segmentLength * (data[j].PushDistance - data[j - 1].ReleaseDistance), - segmentLength * (data[j].ReleaseDistance - data[j].PushDistance), - data[j].PanelAreas - )); - } - } - - double lastPushDistance = 0.0; - if (path.GetEndType(OptionMirrorID.Normal) == SlideType.Slide_Straight) - { - var diff = nodeList[nodeList.Count - 1].Item1 - nodeList[nodeList.Count - 2].Item1; - diff %= 8; - lastPushDistance = diff switch - { - 1 or 2 or 6 or 7 => 130.0, - _ => 159.0 - }; - } - else - { - lastPushDistance = 175.0; - } - - var last2ndArea = result[result.Count - 2]; - var lastArea = result[result.Count - 1]; - var distance = last2ndArea.ReleaseDistance + lastArea.PushDistance + lastArea.ReleaseDistance; - result[result.Count - 2] = new HitAreaData(last2ndArea.PushDistance, distance - lastPushDistance, last2ndArea.PanelAreas); - result[result.Count - 1] = new HitAreaData(lastPushDistance, 0.0, lastArea.PanelAreas); - - return result; - } - - - - /// - /// Convert hit area data to sinmai format (Vector4) - /// - /// hit area data generated by BuildHitAreas() - /// button index of slide-star - /// mirror mode in user option - /// sinmai format arrow data, referenced to slide-star - public static List ConvertAndRotateHitAreas(IEnumerable data, int starButton, - OptionMirrorID mirrorMode) - { - var hitAreaList = new List(); - foreach (var hitAreaData in data) - { - var hitArea = new SlideManager.HitArea(); - hitArea.PushDistance = hitAreaData.PushDistance; - hitArea.ReleaseDistance = hitAreaData.ReleaseDistance; - foreach (var pad in hitAreaData.PanelAreas) - { - var converted = MaiGeometry.MirrorInfo[(int) mirrorMode, pad]; - converted = converted == 16 ? 16 : (converted - starButton) & 0b111 | converted & 0b1000; - hitArea.HitPoints.Add((InputManager.TouchPanelArea) converted); - } - hitAreaList.Add(hitArea); - } - return hitAreaList; - } -} diff --git a/AquaMai/AquaMai.Mods/Fancy/GamePlay/CustomNoteTypes/Libs/SlidePathGenerator.cs b/AquaMai/AquaMai.Mods/Fancy/GamePlay/CustomNoteTypes/Libs/SlidePathGenerator.cs deleted file mode 100644 index 788567a8..00000000 --- a/AquaMai/AquaMai.Mods/Fancy/GamePlay/CustomNoteTypes/Libs/SlidePathGenerator.cs +++ /dev/null @@ -1,131 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Numerics; - -namespace AquaMai.Mods.Fancy.GamePlay.CustomNoteTypes.Libs; - -public class SlidePathGenerator -{ - public List PathSegments = new List(); - public Complex CurrentEndPoint = Complex.Zero; - - public static SlidePathGenerator BeginAt(Complex point) - { - var obj = new SlidePathGenerator(); - obj.CurrentEndPoint = point; - return obj; - } - - public static double CalcTangentAngle(Complex point, MaiGeometry.CircleStruct circle, bool isCcw) - { - var hypot = point - circle.Center; - var angleDelta = Math.Acos(circle.Radius / hypot.Magnitude); - var tanAngle = hypot.Phase + (isCcw ? angleDelta : -angleDelta); - return Math.IEEERemainder(tanAngle, Math.PI * 2.0); - } - - public void TrySetLastParseMarker(ParametricSlidePath.ParseMarker marker) - { - if (PathSegments.Count <= 0) return; - PathSegments[PathSegments.Count - 1].SetParseMarker(marker); - } - - public void LineToPoint(Complex point) - { - PathSegments.Add(new ParametricSlidePath.LineSegment(CurrentEndPoint, point)); - CurrentEndPoint = point; - } - - public void TangentToCircle(MaiGeometry.CircleStruct circle, bool isCcw) - { - var inAngle = CalcTangentAngle(CurrentEndPoint, circle, isCcw); - var inPoint = Complex.FromPolarCoordinates(circle.Radius, inAngle) + circle.Center; - LineToPoint(inPoint); - } - - /// Note: endAngle should be in range [-PI, PI] - public void ArcToAngle(Complex center, double endAngle, bool isCcw, bool skipIfZero) - { - var diff = CurrentEndPoint - center; - var circle = new MaiGeometry.CircleStruct(center, diff.Magnitude); - var startAngle = diff.Phase; - // startAngle and endAngle in range [-PI, PI] - if (isCcw) - { - if (startAngle > endAngle) - { - startAngle -= 2 * Math.PI; - } - - if (Math.Abs(endAngle - startAngle) < 0.001) - { - if (skipIfZero) return; - endAngle += 2 * Math.PI; - } - } - else - { - if (startAngle < endAngle) - { - startAngle += 2 * Math.PI; - } - - if (Math.Abs(endAngle - startAngle) < 0.001) - { - if (skipIfZero) return; - endAngle -= 2 * Math.PI; - } - } - - var seg = new ParametricSlidePath.ArcSegment(circle, startAngle, endAngle); - PathSegments.Add(seg); - CurrentEndPoint = seg.GetPointAt(1f); - } - - public void ArcToTangentTowards(Complex target, Complex center, bool isCcw) - { - var diff = CurrentEndPoint - center; - var endAngle = CalcTangentAngle(target, new MaiGeometry.CircleStruct(center, diff.Magnitude), !isCcw); - ArcToAngle(center, endAngle, isCcw, false); - } - - public void FullCircle(Complex center, bool isCcw) - { - var diff = CurrentEndPoint - center; - var circle = new MaiGeometry.CircleStruct(center, diff.Magnitude); - PathSegments.Add(new ParametricSlidePath.CircleSegment(circle, diff.Phase, isCcw)); - // CurrentEndPoint not changed - } - - public void ExternTangentTransfer(Complex currentCenter, MaiGeometry.CircleStruct targetCircle, bool isCcw) - { - var diff = CurrentEndPoint - currentCenter; - double endAngle; - if (Math.Abs(diff.Magnitude - targetCircle.Radius) < 0.001) - { - // two circles are approximately same radius - var vector = targetCircle.Center - currentCenter; - vector *= isCcw ? -Complex.ImaginaryOne : Complex.ImaginaryOne; - endAngle = vector.Phase; - } - else if (targetCircle.Radius > diff.Magnitude) - { - // target circle larger - var helperCircle = new MaiGeometry.CircleStruct(targetCircle.Center, targetCircle.Radius - diff.Magnitude); - endAngle = CalcTangentAngle(currentCenter, helperCircle, isCcw); - } - else - { - var helperCircle = new MaiGeometry.CircleStruct(currentCenter, diff.Magnitude - targetCircle.Radius); - endAngle = CalcTangentAngle(targetCircle.Center, helperCircle, !isCcw); - } - ArcToAngle(currentCenter, endAngle, isCcw, false); - var inPoint = Complex.FromPolarCoordinates(targetCircle.Radius, endAngle) + targetCircle.Center; - LineToPoint(inPoint); - } - - public ParametricSlidePath GeneratePath() - { - return new ParametricSlidePath(PathSegments); - } -} diff --git a/AquaMai/AquaMai.Mods/Fancy/GamePlay/DisableTrackStartTabs.cs b/AquaMai/AquaMai.Mods/Fancy/GamePlay/DisableTrackStartTabs.cs deleted file mode 100644 index 439b8579..00000000 --- a/AquaMai/AquaMai.Mods/Fancy/GamePlay/DisableTrackStartTabs.cs +++ /dev/null @@ -1,50 +0,0 @@ -using AquaMai.Config.Attributes; -using HarmonyLib; -using Monitor; -using TMPro; -using UI; -using UnityEngine; - -namespace AquaMai.Mods.Fancy.GamePlay; - -[ConfigSection( - en: """ - Disable the TRACK X text, DX/Standard display box, and the derakkuma at the bottom of the screen in the song start screen. - For recording chart confirmation. - """, - zh: """ - 在歌曲开始界面, 把 TRACK X 字样, DX/标准谱面的显示框, 以及画面下方的滴蜡熊隐藏掉 - 录制谱面确认用 - """)] -public class DisableTrackStartTabs -{ - // 在歌曲开始界面, 把 TRACK X 字样, DX/标准谱面的显示框, 以及画面下方的滴蜡熊隐藏掉, 让他看起来不那么 sinmai, 更像是 majdata - - [HarmonyPostfix] - [HarmonyPatch(typeof(TrackStartMonitor), "SetTrackStart")] - private static void DisableTabs( - SpriteCounter ____trackNumber, SpriteCounter ____bossTrackNumber, SpriteCounter ____utageTrackNumber, - MultipleImage ____musicTabImage, GameObject[] ____musicTabObj, GameObject ____derakkumaRoot, - TimelineRoot ____musicDetail - ) - { - ____trackNumber.transform.parent.gameObject.SetActive(false); - ____bossTrackNumber.transform.parent.gameObject.SetActive(false); - ____utageTrackNumber.transform.parent.gameObject.SetActive(false); - ____musicTabImage.gameObject.SetActive(false); - ____musicTabObj[0].gameObject.SetActive(false); - ____musicTabObj[1].gameObject.SetActive(false); - ____musicTabObj[2].gameObject.SetActive(false); - ____derakkumaRoot.SetActive(false); - var traverse = Traverse.Create(____musicDetail); - traverse.Field("_achivement_Base").Value.ChangeSprite(1); - traverse.Field("_clearRank_Base").Value.ChangeSprite(1); - traverse.Field("_achivement_Text").Value.gameObject.SetActive(false); - traverse.Field("_achivement_decimal_Text").Value.gameObject.SetActive(false); - traverse.Field("_achivement_percent_Text").Value.gameObject.SetActive(false); - traverse.Field("_clearRank_Image").Value.gameObject.SetActive(false); - traverse.Field("_deluxScore_Obj").Value.SetActive(false); - traverse.Field("_comboRank_Image").Value.ChangeSprite(0); - traverse.Field("_syncRank_Image").Value.ChangeSprite(0); - } -} diff --git a/AquaMai/AquaMai.Mods/Fancy/GamePlay/ExtendNotesPool.cs b/AquaMai/AquaMai.Mods/Fancy/GamePlay/ExtendNotesPool.cs deleted file mode 100644 index 245a01b0..00000000 --- a/AquaMai/AquaMai.Mods/Fancy/GamePlay/ExtendNotesPool.cs +++ /dev/null @@ -1,129 +0,0 @@ -using System.Collections.Generic; -using AquaMai.Core.Attributes; -using AquaMai.Config.Attributes; -using HarmonyLib; -using MAI2.Util; -using Manager; -using Monitor; -using Monitor.Game; -using UnityEngine; - -namespace AquaMai.Mods.Fancy.GamePlay; - -[ConfigSection( - en: "Add notes sprite to the pool to prevent use up.", - zh: "增加更多待命的音符贴图,防止奇怪的自制谱用完音符贴图池")] -[EnableGameVersion(23000)] -public class ExtendNotesPool -{ - [ConfigEntry( - en: "Number of objects to add.", - zh: "要增加的对象数量")] - private readonly static int count = 128; - - [HarmonyPostfix] - [HarmonyPatch(typeof(GameCtrl), "CreateNotePool")] - public static void CreateNotePool(ref GameCtrl __instance, - GameObject ____tapListParent, List ____tapObjectList, - GameObject ____holdListParent, List ____holdObjectList, - GameObject ____breakHoldListParent, List ____breakHoldObjectList, - GameObject ____starListParent, List ____starObjectList, - GameObject ____breakStarListParent, List ____breakStarObjectList, - GameObject ____breakListParent, List ____breakObjectList, - GameObject ____touchListParent, List ____touchBObjectList, - GameObject ____touchCTapListParent, List ____touchCTapObjectList, - GameObject ____touchCHoldListParent, List ____touchCHoldObjectList, - GameObject ____slideListParent, List ____slideObjectList, - GameObject ____fanSlideListParent, List ____fanSlideObjectList, - GameObject ____slideJudgeListParent, List ____judgeSlideObjectList, - GameObject ____guideListParent, List ____guideObjectList, - GameObject ____barGuideListParent, List ____barGuideObjectList, - List ____arrowObjectList, List ____breakArrowObjectList - ) - { - for (var i = 0; i < count; i++) - { - var tapNote = Object.Instantiate(GameNotePrefabContainer.Tap, ____tapListParent.transform); - tapNote.gameObject.SetActive(false); - tapNote.ParentTransform = ____tapListParent.transform; - ____tapObjectList.Add(tapNote); - - var holdNote = Object.Instantiate(GameNotePrefabContainer.Hold, ____holdListParent.transform); - holdNote.gameObject.SetActive(false); - holdNote.ParentTransform = ____holdListParent.transform; - ____holdObjectList.Add(holdNote); - - var breakHoldNote = Object.Instantiate(GameNotePrefabContainer.BreakHold, ____breakHoldListParent.transform); - breakHoldNote.gameObject.SetActive(false); - breakHoldNote.ParentTransform = ____holdListParent.transform; - ____breakHoldObjectList.Add(breakHoldNote); - - var starNote = Object.Instantiate(GameNotePrefabContainer.Star, ____starListParent.transform); - starNote.gameObject.SetActive(false); - starNote.ParentTransform = ____starListParent.transform; - ____starObjectList.Add(starNote); - - var breakStarNote = Object.Instantiate(GameNotePrefabContainer.BreakStar, ____breakStarListParent.transform); - breakStarNote.gameObject.SetActive(false); - breakStarNote.ParentTransform = ____breakStarListParent.transform; - ____breakStarObjectList.Add(breakStarNote); - - var breakNote = Object.Instantiate(GameNotePrefabContainer.Break, ____breakListParent.transform); - breakNote.gameObject.SetActive(false); - breakNote.ParentTransform = ____breakListParent.transform; - ____breakObjectList.Add(breakNote); - - var touchNoteB = Object.Instantiate(GameNotePrefabContainer.TouchTapB, ____touchListParent.transform); - touchNoteB.gameObject.SetActive(false); - touchNoteB.ParentTransform = ____touchListParent.transform; - ____touchBObjectList.Add(touchNoteB); - - var touchNoteC = Object.Instantiate(GameNotePrefabContainer.TouchTapC, ____touchCTapListParent.transform); - touchNoteC.gameObject.SetActive(false); - touchNoteC.ParentTransform = ____touchCTapListParent.transform; - ____touchCTapObjectList.Add(touchNoteC); - - var touchHoldC = Object.Instantiate(GameNotePrefabContainer.TouchHoldC, ____touchCHoldListParent.transform); - touchHoldC.gameObject.SetActive(false); - touchHoldC.ParentTransform = ____touchCHoldListParent.transform; - ____touchCHoldObjectList.Add(touchHoldC); - - var slideRoot = Object.Instantiate(GameNotePrefabContainer.Slide, ____slideListParent.transform); - slideRoot.gameObject.SetActive(false); - slideRoot.ParentTransform = ____slideListParent.transform; - ____slideObjectList.Add(slideRoot); - - var slideFan = Object.Instantiate(GameNotePrefabContainer.SlideFan, ____fanSlideListParent.transform); - slideFan.gameObject.SetActive(false); - slideFan.ParentTransform = ____fanSlideListParent.transform; - ____fanSlideObjectList.Add(slideFan); - - var slideJudge = Object.Instantiate(GameNotePrefabContainer.SlideJudge, ____slideJudgeListParent.transform); - slideJudge.gameObject.SetActive(false); - slideJudge.ParentTransform = ____slideJudgeListParent.transform; - slideJudge.SetOption(Singleton.Instance.GetGameScore(__instance.MonitorIndex).UserOption.DispJudge); - ____judgeSlideObjectList.Add(slideJudge); - - for (var j = 0; j < 50; j++) - { - var spriteRenderer = Object.Instantiate(GameNotePrefabContainer.Arrow, ____slideListParent.transform); - spriteRenderer.gameObject.SetActive(false); - ____arrowObjectList.Add(spriteRenderer); - - var breakSlide = Object.Instantiate(GameNotePrefabContainer.BreakArrow, ____slideListParent.transform); - breakSlide.gameObject.SetActive(false); - ____breakArrowObjectList.Add(breakSlide); - } - - var noteGuide = Object.Instantiate(GameNotePrefabContainer.Guide, ____guideListParent.transform); - noteGuide.gameObject.SetActive(false); - noteGuide.ParentTransform = ____guideListParent.transform; - ____guideObjectList.Add(noteGuide); - - var barGuide = Object.Instantiate(GameNotePrefabContainer.BarGuide, ____barGuideListParent.transform); - barGuide.gameObject.SetActive(false); - barGuide.ParentTransform = ____barGuideListParent.transform; - ____barGuideObjectList.Add(barGuide); - } - } -} diff --git a/AquaMai/AquaMai.Mods/Fancy/GamePlay/FanJudgeFlip.cs b/AquaMai/AquaMai.Mods/Fancy/GamePlay/FanJudgeFlip.cs deleted file mode 100644 index bad70ed8..00000000 --- a/AquaMai/AquaMai.Mods/Fancy/GamePlay/FanJudgeFlip.cs +++ /dev/null @@ -1,42 +0,0 @@ -using AquaMai.Config.Attributes; -using HarmonyLib; -using Monitor; - -namespace AquaMai.Mods.Fancy.GamePlay; - -[ConfigSection( - en: """ - Make the judgment display of WiFi Slide different in up and down (originally all WiFi judgment displays are towards the center), just like in majdata. - The reason for this bug is that SEGA forgot to assign EndButtonId to WiFi. - """, - zh: """ - 这个 Patch 让 WiFi Slide 的判定显示有上下的区别 (原本所有 WiFi 的判定显示都是朝向圆心的), 就像 majdata 里那样 - 这个 bug 产生的原因是 SBGA 忘记给 WiFi 的 EndButtonId 赋值了 - """)] -public class FanJudgeFlip -{ - /* - * 这个 Patch 让 WiFi Slide 的判定显示有上下的区别 (原本所有 WiFi 的判定显示都是朝向圆心的), 就像 majdata 里那样 - * 这个 bug 产生的原因是 SBGA 忘记给 WiFi 的 EndButtonId 赋值了 - * 不过需要注意的是, 考虑到圆弧形 Slide 的判定显示就是永远朝向圆心的, 我个人会觉得这个 Patch 关掉更好看一点 - */ - [HarmonyPostfix] - [HarmonyPatch(typeof(SlideFan), "Initialize")] - private static void FixFanJudgeFilp( - int[] ___GoalButtonId, SlideJudge ___JudgeObj - ) - { - if (null != ___JudgeObj) - { - if (2 <= ___GoalButtonId[1] && ___GoalButtonId[1] <= 5) - { - ___JudgeObj.Flip(false); - ___JudgeObj.transform.Rotate(0.0f, 0.0f, 180f); - } - else - { - ___JudgeObj.Flip(true); - } - } - } -} diff --git a/AquaMai/AquaMai.Mods/Fancy/GamePlay/HideHanabi.cs b/AquaMai/AquaMai.Mods/Fancy/GamePlay/HideHanabi.cs deleted file mode 100644 index e84e02d0..00000000 --- a/AquaMai/AquaMai.Mods/Fancy/GamePlay/HideHanabi.cs +++ /dev/null @@ -1,24 +0,0 @@ -using AquaMai.Config.Attributes; -using Fx; -using HarmonyLib; -using Monitor; -using UnityEngine; - -namespace AquaMai.Mods.Fancy.GamePlay; - -[ConfigSection( - en: "Hide hanabi completely.", - zh: "完全隐藏烟花")] -public class HideHanabi -{ - [HarmonyPatch(typeof(TapCEffect), "SetUpParticle")] - [HarmonyPostfix] - public static void FixZeroSize(TapCEffect __instance, FX_Mai2_Note_Color ____particleControler) - { - var entities = ____particleControler.GetComponentsInChildren(true); - foreach (var entity in entities) - { - entity.maxParticleSize = 0f; - } - } -} diff --git a/AquaMai/AquaMai.Mods/Fancy/GamePlay/JudgeDisplay4B.cs b/AquaMai/AquaMai.Mods/Fancy/GamePlay/JudgeDisplay4B.cs deleted file mode 100644 index 29c64edd..00000000 --- a/AquaMai/AquaMai.Mods/Fancy/GamePlay/JudgeDisplay4B.cs +++ /dev/null @@ -1,86 +0,0 @@ -using AquaMai.Config.Attributes; -using HarmonyLib; -using Monitor; -using UnityEngine; - -namespace AquaMai.Mods.Fancy.GamePlay; - -[ConfigSection( - en: """ - More detailed judgment display. - Requires CustomSkins to be enabled and the resource file to be downloaded. - https://github.com/hykilpikonna/AquaDX/releases/download/nightly/JudgeDisplay4B.7z - """, - zh: """ - 更精细的判定表示 - 需开启 CustomSkins 并下载资源文件 - https://github.com/hykilpikonna/AquaDX/releases/download/nightly/JudgeDisplay4B.7z - """)] -public class JudgeDisplay4B -{ - // 精确到子判定的自定义判定显示, 需要启用自定义皮肤功能 (理论上不启用自定义皮肤不会崩游戏, 只不过此时这个功能显然不会生效) - - [HarmonyPostfix] - [HarmonyPatch(typeof(SlideJudge), "Initialize")] - private static void SlideJudgeDisplay4B( - SpriteRenderer ___SpriteRenderAdd, SpriteRenderer ___SpriteRender, - SlideJudge.SlideJudgeType ____judgeType, SlideJudge.SlideAngle ____angle, - NoteJudge.ETiming judge, float msec, bool isBreak - ) - { - var i = isBreak ? 1 : 0; - Sprite sprite = CustomSkins.CustomJudgeSlide[i, (int)____judgeType, (int)____angle, (int)judge]; - if (sprite != null) { - ___SpriteRender.sprite = sprite; - } - - if (isBreak && judge == NoteJudge.ETiming.Critical) - { - sprite = CustomSkins.CustomJudgeSlide[i, (int)____judgeType, (int)____angle, (int) NoteJudge.ETiming.End]; - if (sprite != null) - { - ___SpriteRenderAdd.sprite = sprite; - } - } - } - - - [HarmonyPostfix] - [HarmonyPatch(typeof(JudgeGrade), "Initialize")] - private static void JudgeGradeDisplay4B( - SpriteRenderer ___SpriteRender, - NoteJudge.ETiming judge, float msec, NoteJudge.EJudgeType type - ) - { - var i = (type == NoteJudge.EJudgeType.Break) ? 1 : 0; - Sprite sprite = CustomSkins.CustomJudge[i, (int)judge]; - if (sprite != null) { - ___SpriteRender.sprite = sprite; - } - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(JudgeGrade), "InitializeBreak")] - private static void JudgeGradeBreakDisplay4B( - SpriteRenderer ___SpriteRenderAdd, - NoteJudge.ETiming judge, float msec, NoteJudge.EJudgeType type - ) - { - if (judge == NoteJudge.ETiming.Critical) - { - var sprite = CustomSkins.CustomJudge[1, (int) NoteJudge.ETiming.End]; - if (sprite != null) - { - ___SpriteRenderAdd.sprite = sprite; - } - } - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(JudgeGrade), "InitializeBreak")] - private static void InitializeBreakFix(ref NoteJudge.EJudgeType type) - { - type = NoteJudge.EJudgeType.Break; - } - -} diff --git a/AquaMai/AquaMai.Mods/Fancy/GamePlay/RealisticRandomJudge.cs b/AquaMai/AquaMai.Mods/Fancy/GamePlay/RealisticRandomJudge.cs deleted file mode 100644 index a73ab94e..00000000 --- a/AquaMai/AquaMai.Mods/Fancy/GamePlay/RealisticRandomJudge.cs +++ /dev/null @@ -1,39 +0,0 @@ -using AquaMai.Config.Attributes; -using HarmonyLib; -using Manager; - -namespace AquaMai.Mods.Fancy.GamePlay; - -[ConfigSection( - en: """ - Make the AutoPlay random judgment mode really randomize all judgments (down to sub-judgments). - The original random judgment will only produce all 15 judgment results from Miss(TooFast) ~ Critical ~ Miss(TooLate). - Here, it is changed to a triangular distribution to produce all 15 judgment results from Miss(TooFast) ~ Critical ~ Miss(TooLate). - Of course, it will not consider whether the original Note really has a corresponding judgment (such as Slide should not have non-Critical Prefect). - """, - zh: """ - 让 AutoPlay 的随机判定模式真的会随机产生所有的判定 (精确到子判定) - 原本的随机判定只会等概率产生 Critical, LateGreat1st, LateGood, Miss(TooLate) - 这里改成三角分布产生从 Miss(TooFast) ~ Critical ~ Miss(TooLate) 的所有 15 种判定结果 - 当然, 此处并不会考虑原本那个 Note 是不是真的有对应的判定 (比如 Slide 实际上不应该有小 p 之类的) - """)] -public class RealisticRandomJudge -{ - // 让 AutoPlay 的随机判定模式真的会随机产生所有的判定 (精确到子判定) - // 原本的随机判定只会等概率产生 Critical, LateGreat1st, LateGood, Miss(TooLate) - // 这里改成三角分布产生从 Miss(TooFast) ~ Critical ~ Miss(TooLate) 的所有 15 种判定结果 - // 当然, 此处并不会考虑原本那个 Note 是不是真的有对应的判定 (比如 Slide 实际上不应该有小 p 之类的) - - [HarmonyPostfix] - [HarmonyPatch(typeof(GameManager), "AutoJudge")] - private static NoteJudge.ETiming RealAutoJudgeRandom(NoteJudge.ETiming retval) - { - if (GameManager.AutoPlay == GameManager.AutoPlayMode.Random) - { - var x = UnityEngine.Random.Range(0, 8); - x += UnityEngine.Random.Range(0, 8); - return (NoteJudge.ETiming) x; - } - return retval; - } -} diff --git a/AquaMai/AquaMai.Mods/Fancy/GamePlay/SlideArrowAnimation.cs b/AquaMai/AquaMai.Mods/Fancy/GamePlay/SlideArrowAnimation.cs deleted file mode 100644 index 16db4308..00000000 --- a/AquaMai/AquaMai.Mods/Fancy/GamePlay/SlideArrowAnimation.cs +++ /dev/null @@ -1,118 +0,0 @@ -using System.Collections.Generic; -using AquaMai.Config.Attributes; -using HarmonyLib; -using Manager; -using Monitor; -using Monitor.Game; -using Process; -using UnityEngine; - -namespace AquaMai.Mods.Fancy.GamePlay; - -[ConfigSection( - en: "Make the Slide Track disappear with an inward-shrinking animation, similar to AstroDX.", - zh: "使 Slide Track 消失时有类似 AstroDX 一样的向内缩入的动画")] -public class SlideArrowAnimation -{ - private static List _animatingSpriteRenderers = []; - - [HarmonyTranspiler] - [HarmonyPatch(typeof(SlideRoot), "NoteCheck")] - private static IEnumerable GetUnVisibleColorHook(IEnumerable instructions) - { - var methodGetUnVisibleColor = AccessTools.Method(typeof(SlideRoot), "GetUnVisibleColor"); - - var oldInstList = new List(instructions); - var newInstList = new List(); - - for (var i = 0; i < oldInstList.Count; i++) - { - var inst = oldInstList[i]; - if (inst.Calls(methodGetUnVisibleColor)) - { - // 现在栈上应该有: SpriteRenderer, SlideRoot(this) - // 这一条 IL 会消耗 this, 调用 GetUnVisibleColor(), 推一个 Color 到栈上 - // 然后接下来的一条 IL 是调用 SpriteRenderer.color 的 setter 把 SpriteRenderer 和 Color 一起消耗掉 - // 我们现在直接用一个 static method 消耗掉 SpriteRenderer 和 this - // 所以要忽略当前 IL, 再忽略下一条 IL, 然后构造一个 Call - - // ReSharper disable once ConvertClosureToMethodGroup - var redirect = CodeInstruction.Call((SpriteRenderer r, SlideRoot s) => OnSlideArrowDisable(r, s)); - newInstList.Add(redirect); - i++; // 跳过下一条 IL - } - else - { - newInstList.Add(inst); - } - } - return newInstList; - } - - public static void OnSlideArrowDisable(SpriteRenderer renderer, SlideRoot slideRoot) - { - _animatingSpriteRenderers.Add(renderer); - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(SlideRoot), "SetArrowObject")] - private static void RemoveArrowAnimation(GameObject arrowobj) - { - var spriteRenderer = arrowobj.GetComponent(); - spriteRenderer.transform.localScale = Vector3.one; - if (_animatingSpriteRenderers.Contains(spriteRenderer)) - { - _animatingSpriteRenderers.Remove(spriteRenderer); - } - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(SlideRoot), "SetBreakArrowObject")] - private static void RemoveBreakArrowAnimation(GameObject breakArrowobj) - { - var breakSlideObj = breakArrowobj.GetComponent(); - breakSlideObj.SpriteRender.transform.localScale = Vector3.one; - if (_animatingSpriteRenderers.Contains(breakSlideObj.SpriteRender)) - { - _animatingSpriteRenderers.Remove(breakSlideObj.SpriteRender); - } - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(GameCtrl), "UpdateNotes")] - private static void OnGameCtrlUpdateNotesLast() - { - for (var num = _animatingSpriteRenderers.Count - 1; num >= 0; num--) - { - var spriteRenderer = _animatingSpriteRenderers[num]; - if (spriteRenderer == null || !spriteRenderer.gameObject.activeSelf) - { - _animatingSpriteRenderers.RemoveAt(num); - } - else - { - var localScale = spriteRenderer.transform.localScale; - var scale = localScale.y - NotesManager.GetAddMSec() / 150f; - if (scale <= 0) - { - spriteRenderer.transform.localScale = new Vector3(1f, 0f, 1f); - spriteRenderer.color = new Color(1f, 1f, 1f, 0f); - _animatingSpriteRenderers.RemoveAt(num); - } - else - { - localScale.y = scale; - spriteRenderer.color = new Color(1f, 1f, 1f, Mathf.Sqrt(scale)); - spriteRenderer.transform.localScale = localScale; - } - } - } - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(GameProcess), "SetRelease")] - private static void OnBeforeGameProcessSetRelease() - { - _animatingSpriteRenderers.Clear(); - } -} diff --git a/AquaMai/AquaMai.Mods/Fancy/GamePlay/SlideFadeInTweak.cs b/AquaMai/AquaMai.Mods/Fancy/GamePlay/SlideFadeInTweak.cs deleted file mode 100644 index 1a597c52..00000000 --- a/AquaMai/AquaMai.Mods/Fancy/GamePlay/SlideFadeInTweak.cs +++ /dev/null @@ -1,129 +0,0 @@ -using System; -using System.Collections.Generic; -using AquaMai.Config.Attributes; -using HarmonyLib; -using MAI2.Util; -using Manager; -using Monitor; -using UnityEngine; - -namespace AquaMai.Mods.Fancy.GamePlay; - -[ConfigSection( - zh: "让星星在启动拍等待期间从 50% 透明度渐入为 100%,取代原本在击打星星头时就完成渐入", - en: "Slides will fade in instead of instantly appearing.")] -public class SlideFadeInTweak -{ - [HarmonyPrefix] - [HarmonyPatch(typeof(SlideRoot), "UpdateAlpha")] - private static bool UpdateAlphaOverwrite( - SlideRoot __instance, - ref bool ___UpdateAlphaFlag, - float ___StartMsec, float ___AppearMsec, float ___StarLaunchMsec, float ___DefaultMsec, - int ____dispLaneNum, bool ___BreakFlag, - List ____spriteRenders, List ____breakSpriteRenders - ) - { - if (!___UpdateAlphaFlag) - return false; - - var currentMsec = NotesManager.GetCurrentMsec(); - var slideSpeed = (int) Singleton.Instance.GetGameScore(__instance.MonitorId).UserOption.SlideSpeed; - var defaultFadeInLength = (21 - slideSpeed) / 10.5f * ___DefaultMsec; - var fadeInFirstMsec = Math.Max(___StartMsec, ___AppearMsec - defaultFadeInLength); - var fadeInSecondMsec = Math.Max(___AppearMsec, ___StarLaunchMsec - defaultFadeInLength); - // var fadeInSecondMsec = ___AppearMsec; - var color = new Color(1f, 1f, 1f, 1f); - - if (currentMsec >= ___StarLaunchMsec) - { - ___UpdateAlphaFlag = false; - } - else if (currentMsec < fadeInFirstMsec) - { - color.a = 0.0f; - } - else if (fadeInFirstMsec <= currentMsec && currentMsec < ___AppearMsec) - { - var fadeInLength = Math.Min(200.0f, ___AppearMsec - fadeInFirstMsec); - color.a = 0.5f * Math.Min(1f, (currentMsec - fadeInFirstMsec) / fadeInLength); - } - else if (___AppearMsec <= currentMsec && currentMsec < fadeInSecondMsec) - { - color.a = 0.5f; - } - else if (fadeInSecondMsec <= currentMsec && currentMsec < ___StarLaunchMsec) - { - var fadeInLength = Math.Min(200.0f, ___StarLaunchMsec - fadeInSecondMsec); - // var fadeInLength = ___StarLaunchMsec - fadeInSecondMsec; - color.a = 0.5f + 0.5f * Math.Min(1f, (currentMsec - fadeInSecondMsec) / fadeInLength); - } - - if (!___BreakFlag) - { - for (var index = 0; index < ____dispLaneNum; ++index) - { - if (index >= ____spriteRenders.Count) break; - ____spriteRenders[index].color = color; - } - } - else - { - for (var index = 0; index < ____dispLaneNum; ++index) - { - if (index >= ____breakSpriteRenders.Count) break; - ____breakSpriteRenders[index].SpriteRender.color = color; - } - } - - return false; - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(SlideFan), "UpdateAlpha")] - private static bool UpdateFanAlphaOverwrite( - SlideRoot __instance, - float ___StartMsec, float ___AppearMsec, float ___StarLaunchMsec, float ___DefaultMsec, - Color ____defaultColor, SpriteRenderer[] ____spriteLines - ) - { - var currentMsec = NotesManager.GetCurrentMsec(); - - var slideSpeed = (int) Singleton.Instance.GetGameScore(__instance.MonitorId).UserOption.SlideSpeed; - var defaultFadeInLength = (21 - slideSpeed) / 10.5f * ___DefaultMsec; - var fadeInFirstMsec = Math.Max(___StartMsec, ___AppearMsec - defaultFadeInLength); - var fadeInSecondMsec = Math.Max(___AppearMsec, ___StarLaunchMsec - defaultFadeInLength); - // var fadeInSecondMsec = ___AppearMsec; - var color = ____defaultColor; - - if (currentMsec < fadeInFirstMsec) - { - color.a = 0.0f; - } - else if (fadeInFirstMsec <= currentMsec && currentMsec < ___AppearMsec) - { - var fadeInLength = Math.Min(200.0f, ___AppearMsec - fadeInFirstMsec); - color.a = 0.3f * Math.Min(1f, (currentMsec - fadeInFirstMsec) / fadeInLength); - } - else if (___AppearMsec <= currentMsec && currentMsec < fadeInSecondMsec) - { - color.a = 0.3f; - } - else if (fadeInSecondMsec <= currentMsec && currentMsec < ___StarLaunchMsec) - { - var fadeInLength = Math.Min(200.0f, ___StarLaunchMsec - fadeInSecondMsec); - // var fadeInLength = ___StarLaunchMsec - fadeInSecondMsec; - color.a = 0.3f + 0.3f * Math.Min(1f, (currentMsec - fadeInSecondMsec) / fadeInLength); - } - else - { - color.a = 0.6f; - } - - foreach (SpriteRenderer spriteLine in ____spriteLines) - spriteLine.color = color; - - return false; - } - -} diff --git a/AquaMai/AquaMai.Mods/Fancy/GamePlay/SlideLayerReverse.cs b/AquaMai/AquaMai.Mods/Fancy/GamePlay/SlideLayerReverse.cs deleted file mode 100644 index 689b32a6..00000000 --- a/AquaMai/AquaMai.Mods/Fancy/GamePlay/SlideLayerReverse.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System.Collections.Generic; -using AquaMai.Config.Attributes; -using HarmonyLib; -using Monitor; -using UnityEngine; - -namespace AquaMai.Mods.Fancy.GamePlay; - -[ConfigSection( - en: """ - Invert the Slide hierarchy, so that the new Slide appears on top like Maimai classic. - Enable to support color changing effects achieved by overlaying multiple stars. - """, - zh: """ - 反转 Slide 层级, 使新出现的 Slide 像旧框一样显示在上层 - 启用以支持通过叠加多个星星达成的变色效果 - """)] -public class SlideLayerReverse -{ - [HarmonyPostfix] - [HarmonyPatch(typeof(SlideRoot), "Initialize")] - private static void CalcArrowLayer( - bool ___BreakFlag, List ____spriteRenders, List ____breakSpriteRenders, - int ___SlideIndex, int ____baseArrowSortingOrder - ) - { - // 原本的 sortingOrder 是 -(SlideIndex + _baseArrowSortingOrder + index) - // 令 orderBase = SlideIndex + _baseArrowSortingOrder - // 分配给这条 slide 的 sortingOrder 范围是 -(orderBase + count - 1) ~ -(orderBase) - // 现在要保留 slide 内部箭头顺序, 但使得 slide 间次序反转 - // 范围会变成 orderBase ~ orderBase + count - 1 - // 其中原本是 -(orderBase) 的箭头应该调整为 orderBase + count - 1 - - var orderBase = ___SlideIndex + ____baseArrowSortingOrder; // SlideIndex + _baseArrowSortingOrder - if (!___BreakFlag) - { - var lastIdx = ____spriteRenders.Count - 1; - for (var index = 0; index < ____spriteRenders.Count; index++) - { - var renderer = ____spriteRenders[index]; - renderer.sortingOrder = -32700 + orderBase + lastIdx - index; - } - } - else - { - var lastIdx = ____breakSpriteRenders.Count - 1; - for (var index = 0; index < ____breakSpriteRenders.Count; index++) - { - var breakSlide = ____breakSpriteRenders[index]; - breakSlide.SetSortingOrder(-32700 + orderBase + lastIdx - index); - } - } - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(SlideFan), "Initialize")] - private static void CalcFanArrowLayer( - SpriteRenderer[] ____spriteLines, SpriteRenderer[] ____effectSprites, - int ___SlideIndex, int ____baseArrowSortingOrder - ) - { - var orderBase = ___SlideIndex + ____baseArrowSortingOrder; // SlideIndex + _baseArrowSortingOrder - var lastIdx = ____spriteLines.Length - 1; - for (var index = 0; index < ____spriteLines.Length; index++) - { - var renderer = ____spriteLines[index]; - renderer.sortingOrder = -32700 + orderBase + lastIdx - index; - } - lastIdx = ____effectSprites.Length - 1; - for (var index = 0; index < ____effectSprites.Length; index++) - { - var renderer = ____effectSprites[index]; - renderer.sortingOrder = 1000 + orderBase + lastIdx - index; - } - } -} diff --git a/AquaMai/AquaMai.Mods/Fancy/GamePlay/TrackStartProcessTweak.cs b/AquaMai/AquaMai.Mods/Fancy/GamePlay/TrackStartProcessTweak.cs deleted file mode 100644 index 0774d9df..00000000 --- a/AquaMai/AquaMai.Mods/Fancy/GamePlay/TrackStartProcessTweak.cs +++ /dev/null @@ -1,77 +0,0 @@ -using AquaMai.Config.Attributes; -using HarmonyLib; -using Process; -using UnityEngine; - -namespace AquaMai.Mods.Fancy.GamePlay; - -[ConfigSection( - en: """ - Delayed the animation of the song start screen. - For recording chart confirmation. - """, - zh: """ - 推迟了歌曲开始界面的动画 - 录制谱面确认用 - """)] -public class TrackStartProcessTweak -{ - // 总之这个 Patch 没啥用, 是我个人用 sinmai 录谱面确认时用得到, 顺手也写进来了 - // 具体而言就是推迟了歌曲开始界面的动画便于后期剪辑 - - [HarmonyPrefix] - [HarmonyPatch(typeof(TrackStartProcess), "OnUpdate")] - private static bool DelayAnimation( - TrackStartProcess.TrackStartSequence ____state, - ref float ____timeCounter, - ProcessDataContainer ___container - ) - { - if (____state == TrackStartProcess.TrackStartSequence.Wait) - { - // 将开始动画(就是“噔噔, 噔 噔噔”)推迟 - float temp = ____timeCounter + Time.deltaTime; - if (____timeCounter < 1.0f && temp >= 1.0f) - { - // 这是用来让转场动画继续播放的, 原本就是这个时候 notify 的同时开始播放开始动画 - // 现在把开始动画往后延 - ___container.processManager.NotificationFadeIn(); - } - ____timeCounter = temp; - if (____timeCounter >= 3.0f) - { - return true; - // 原 method 的逻辑是这样 - // case TrackStartProcess.TrackStartSequence.Wait: - // this._timeCounter += Time.deltaTime; - // if ((double) this._timeCounter >= 1.0) - // { - // this._timeCounter = 0.0f; - // this._state = TrackStartProcess.TrackStartSequence.Disp; - // /* 一些开始播放开始动画的代码 */ - // this.container.processManager.NotificationFadeIn(); - // break; - // } - // break; - // 所以只要在 prefix 里面等到 timeCounter 达到我们想要的值以后再执行原 method 就好 - // 这里有个细节: NotificationFadeIn() 会被执行两遍, 这其实不好, 是个潜在 bug - // 不过由于此处把开始动画往后推了 2s, 转场动画已经结束把 Process 释放掉了, 所以第二遍会找不到 Process 就没效果 - } - return false; - } - else if (____state == TrackStartProcess.TrackStartSequence.DispEnd) - { - // 将开始动画结束以后的转场动画推迟 - ____timeCounter += Time.deltaTime; // timeCounter 会在先前由原本的 method 归零 - if (____timeCounter >= 1.0f) - { - return true; - } - return false; - } - return true; - } - - -} - diff --git a/AquaMai/AquaMai.Mods/Fancy/HideMask.cs b/AquaMai/AquaMai.Mods/Fancy/HideMask.cs deleted file mode 100644 index 013ee7ab..00000000 --- a/AquaMai/AquaMai.Mods/Fancy/HideMask.cs +++ /dev/null @@ -1,18 +0,0 @@ -using AquaMai.Config.Attributes; -using HarmonyLib; -using UnityEngine; - -namespace AquaMai.Mods.Fancy; - -[ConfigSection( - en: "Remove the circle mask of the game screen.", - zh: "移除游戏画面的圆形遮罩")] -public class HideMask -{ - [HarmonyPrefix] - [HarmonyPatch(typeof(Main.GameMain), "LateInitialize", typeof(MonoBehaviour), typeof(Transform), typeof(Transform))] - public static void LateInitialize(MonoBehaviour gameMainObject) - { - GameObject.Find("Mask").SetActive(false); - } -} diff --git a/AquaMai/AquaMai.Mods/Fancy/README.md b/AquaMai/AquaMai.Mods/Fancy/README.md deleted file mode 100644 index 2f4f6dbc..00000000 --- a/AquaMai/AquaMai.Mods/Fancy/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Fancy - -All the fancy features, even if not required by most players, are welcomed to this category, whether for personalization, for beautify, for self-made charts or for other uncommon purposes. - -These patches may not well-tested by the project maintainers and could be enabled only if you know what you're doing. - -Patches affect the gameplay should go to the GamePlay subcategory. diff --git a/AquaMai/AquaMai.Mods/Fancy/RandomBgm.cs b/AquaMai/AquaMai.Mods/Fancy/RandomBgm.cs deleted file mode 100644 index bec48456..00000000 --- a/AquaMai/AquaMai.Mods/Fancy/RandomBgm.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using AquaMai.Config.Attributes; -using AquaMai.Core.Helpers; -using HarmonyLib; -using Mai2.Mai2Cue; -using MAI2.Util; -using Manager; -using MelonLoader; - -namespace AquaMai.Mods.Fancy; - -[ConfigSection( - en: """ - Random BGM. - Put Mai2Cue.{acb,awb} of old version of the game in the configured directory and rename them. - Won't work with 2P mode. - """, - zh: """ - 在配置的目录下放置了旧版游戏的 Mai2Cue.{acb,awb} 并重命名的话,可以在播放游戏 BGM 的时候随机播放这里面的旧版游戏 BGM - 无法在 2P 模式下工作 - """)] -public class RandomBgm -{ - [ConfigEntry] - private static readonly string mai2CueDir = "LocalAssets/Mai2Cue"; - - private static List _acbs = new List(); - - [HarmonyPostfix] - [HarmonyPatch(typeof(SoundManager), "Initialize")] - public static void Init() - { - var resolvedDir = FileSystem.ResolvePath(mai2CueDir); - if (!Directory.Exists(resolvedDir)) return; - var files = Directory.EnumerateFiles(resolvedDir); - foreach (var file in files) - { - if (!file.EndsWith(".acb")) continue; - // Seems there's limit for max opened ACB files - _acbs.Add(Path.ChangeExtension(file, null)); - } - - MelonLogger.Msg($"Random BGM loaded {_acbs.Count} files"); - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(SoundManager), "Play")] - public static void PrePlay(ref SoundManager.AcbID acbID, int cueID) - { - if (acbID != SoundManager.AcbID.Default) return; - if (_acbs.Count == 0) return; - var cueIndex = (Cue)cueID; - switch (cueIndex) - { - case Cue.BGM_ENTRY: - case Cue.BGM_COLLECTION: - case Cue.BGM_RESULT_CLEAR: - case Cue.BGM_RESULT: - var acb = _acbs[UnityEngine.Random.Range(0, _acbs.Count)]; - acbID = SoundManager.AcbID.Max; - var result = Singleton.Instance.LoadCueSheet((int)acbID, acb); - MelonLogger.Msg($"Picked {acb} for {cueIndex}, result: {result}"); - return; - default: - return; - } - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(SoundManager), "PlayBGM")] - public static bool PrePlayBGM(ref int target) - { - switch (target) - { - case 0: - return true; - case 1: - return false; - case 2: - target = 0; - return true; - default: - return false; - } - } -} diff --git a/AquaMai/AquaMai.Mods/Fancy/Triggers.cs b/AquaMai/AquaMai.Mods/Fancy/Triggers.cs deleted file mode 100644 index 7b4ceaf8..00000000 --- a/AquaMai/AquaMai.Mods/Fancy/Triggers.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.Diagnostics; -using AquaMai.Config.Attributes; -using HarmonyLib; -using Process; - -namespace AquaMai.Mods.Fancy; - -[ConfigSection( - en: "Triggers for executing commands at certain events.", - zh: "在一定时机执行命令的触发器")] -public class Triggers -{ - [ConfigEntry( - en: "Execute some command on game idle.", - zh: """ - 在游戏闲置的时候执行指定的命令脚本 - 比如说可以在游戏闲置是降低显示器的亮度 - """)] - private static readonly string execOnIdle = ""; - - [ConfigEntry( - en: "Execute some command on game start.", - zh: "在玩家登录的时候执行指定的命令脚本")] - private static readonly string execOnEntry = ""; - - [HarmonyPrefix] - [HarmonyPatch(typeof(AdvertiseProcess), "OnStart")] - public static void AdvertiseProcessPreStart() - { - if (!string.IsNullOrWhiteSpace(execOnIdle)) - { - Exec(execOnIdle); - } - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(EntryProcess), "OnStart")] - public static void EntryProcessPreStart() - { - if (!string.IsNullOrWhiteSpace(execOnEntry)) - { - Exec(execOnEntry); - } - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(MusicSelectProcess), "OnStart")] - public static void MusicSelectProcessPreStart() - { - if (!string.IsNullOrWhiteSpace(execOnEntry)) - { - Exec(execOnEntry); - } - } - - private static void Exec(string command) - { - var process = new System.Diagnostics.Process(); - process.StartInfo.FileName = "cmd.exe"; - process.StartInfo.Arguments = "/c " + command; - process.StartInfo.UseShellExecute = true; - process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; - process.StartInfo.WorkingDirectory = Environment.CurrentDirectory; - - process.Start(); - } -} diff --git a/AquaMai/AquaMai.Mods/Fix/Common.cs b/AquaMai/AquaMai.Mods/Fix/Common.cs deleted file mode 100644 index f4e1c54e..00000000 --- a/AquaMai/AquaMai.Mods/Fix/Common.cs +++ /dev/null @@ -1,139 +0,0 @@ -using System.Net; -using HarmonyLib; -using Manager; -using Net; -using UnityEngine; -using AquaMai.Config.Attributes; -using AquaMai.Core.Attributes; - -namespace AquaMai.Mods.Fix; - -[ConfigSection(exampleHidden: true, defaultOn: true)] -public class Common -{ - [ConfigEntry] - private readonly static bool preventIniFileClear = true; - - [EnableIf(nameof(preventIniFileClear))] - [HarmonyPrefix] - [HarmonyPatch(typeof(MAI2System.IniFile), "clear")] - private static bool PreIniFileClear() - { - return false; - } - - [ConfigEntry] - private readonly static bool fixDebugInput = true; - - [EnableIf(nameof(fixDebugInput))] - [HarmonyPrefix] - [HarmonyPatch(typeof(DebugInput), "GetKey")] - private static bool GetKey(ref bool __result, KeyCode name) - { - __result = UnityEngine.Input.GetKey(name); - return false; - } - - [EnableIf(nameof(fixDebugInput))] - [HarmonyPrefix] - [HarmonyPatch(typeof(DebugInput), "GetKeyDown")] - private static bool GetKeyDown(ref bool __result, KeyCode name) - { - __result = UnityEngine.Input.GetKeyDown(name); - return false; - } - - [EnableIf(nameof(fixDebugInput))] - [HarmonyPrefix] - [HarmonyPatch(typeof(DebugInput), "GetMouseButton")] - private static bool GetMouseButton(ref bool __result, int button) - { - __result = UnityEngine.Input.GetMouseButton(button); - return false; - } - - [EnableIf(nameof(fixDebugInput))] - [HarmonyPrefix] - [HarmonyPatch(typeof(DebugInput), "GetMouseButtonDown")] - private static bool GetMouseButtonDown(ref bool __result, int button) - { - __result = UnityEngine.Input.GetMouseButtonDown(button); - return false; - } - - [ConfigEntry] - private readonly static bool bypassCakeHashCheck = true; - - [EnableIf(nameof(bypassCakeHashCheck))] - [HarmonyPostfix] - [HarmonyPatch(typeof(NetHttpClient), MethodType.Constructor)] - private static void OnNetHttpClientConstructor(NetHttpClient __instance) - { - // Bypass Cake.dll hash check - var tInstance = Traverse.Create(__instance).Field("isTrueDll"); - if (tInstance.FieldExists()) - { - tInstance.SetValue(true); - } - } - - [ConfigEntry] - private readonly static bool restoreCertificateValidation = true; - - [EnableIf(nameof(restoreCertificateValidation))] - [HarmonyPostfix] - [HarmonyPatch(typeof(NetHttpClient), "Create")] - private static void OnNetHttpClientCreate() - { - // Unset the certificate validation callback (SSL pinning) to restore the default behavior - ServicePointManager.ServerCertificateValidationCallback = null; - } - - [ConfigEntry] - private readonly static bool forceNonTarget = true; - - [EnableIf(nameof(forceNonTarget))] - [HarmonyPrefix] - [HarmonyPatch(typeof(MAI2System.Config), "IsTarget", MethodType.Getter)] - private static bool PreIsTarget(ref bool __result) - { - // Who is teaching others to set `Target = 1`?! - __result = false; - return false; - } - - [ConfigEntry] - private readonly static bool forceIgnoreError = true; - - [EnableIf(nameof(forceIgnoreError))] - [HarmonyPrefix] - [HarmonyPatch(typeof(MAI2System.Config), "IsIgnoreError", MethodType.Getter)] - private static bool PreIsIgnoreError(ref bool __result) - { - __result = true; - return false; - } - - [ConfigEntry] - private readonly static bool bypassSpecialNumCheck = true; - - public static void OnAfterPatch(HarmonyLib.Harmony h) - { - if (bypassSpecialNumCheck) - { - if (typeof(GameManager).GetMethod("CalcSpecialNum") is null) return; - h.PatchAll(typeof(CalcSpecialNumPatch)); - } - } - - private class CalcSpecialNumPatch - { - [HarmonyPrefix] - [HarmonyPatch(typeof(GameManager), "CalcSpecialNum")] - private static bool CalcSpecialNum(ref int __result) - { - __result = 1024; - return false; - } - } -} diff --git a/AquaMai/AquaMai.Mods/Fix/DebugFeature.cs b/AquaMai/AquaMai.Mods/Fix/DebugFeature.cs deleted file mode 100644 index fb4b10aa..00000000 --- a/AquaMai/AquaMai.Mods/Fix/DebugFeature.cs +++ /dev/null @@ -1,212 +0,0 @@ -using System; -using System.Reflection; -using AquaMai.Config.Attributes; -using HarmonyLib; -using MAI2.Util; -using Manager; -using MelonLoader; -using Monitor; -using Process; -using UnityEngine; - -namespace AquaMai.Mods.Fix; - -[ConfigSection(exampleHidden: true, defaultOn: true)] -public class DebugFeature -{ - public static bool IsPolyfill { get; private set; } - private static GameProcess _gameProcess; - private static MovieController _gameMovie; - private static GameMonitor[] _monitors; - private static object _debugFeatureOriginal; - private static System.Type _debugFeatureType; - - [HarmonyPatch(typeof(GameProcess), "OnStart")] - [HarmonyPostfix] - public static void Init(GameProcess __instance, MovieController ____gameMovie, GameMonitor[] ____monitors) - { - _gameProcess = __instance; - _gameMovie = ____gameMovie; - _monitors = ____monitors; - PolyFill.timer = 0; - } - - public static void OnBeforePatch(HarmonyLib.Harmony h) - { - var original = typeof(GameProcess).GetField("debugFeature", BindingFlags.NonPublic | BindingFlags.Instance); - if (original is null) - { - MelonLogger.Msg(" > [DebugFeature] Running Polyfill"); - IsPolyfill = true; - h.PatchAll(typeof(PolyFill)); - } - else - { - MelonLogger.Msg(" > [DebugFeature] Already included"); - _debugFeatureType = typeof(GameProcess).GetNestedType("DebugFeature", BindingFlags.Instance | BindingFlags.NonPublic); - h.PatchAll(typeof(GetOriginal)); - } - } - - public static bool Pause - { - get - { - if (IsPolyfill) - { - return PolyFill.isPause; - } - - return (bool)_debugFeatureType.GetField("_debugPause", BindingFlags.Instance | BindingFlags.Public).GetValue(_debugFeatureOriginal); - } - - set - { - if (IsPolyfill) - { - PolyFill.isPause = value; - } - else - { - _debugFeatureType.GetField("_debugPause", BindingFlags.Instance | BindingFlags.Public).SetValue(_debugFeatureOriginal, value); - } - - SoundManager.PauseMusic(value); - _gameMovie.Pause(value); - NotesManager.Pause(value); - } - } - - public static void Seek(int msec) - { - Singleton.Instance.Initialize(); - if (IsPolyfill) - { - PolyFill.DebugTimeSkip(msec); - } - else - { - _debugFeatureType.GetMethod("DebugTimeSkip", BindingFlags.Instance | BindingFlags.Public).Invoke(_debugFeatureOriginal, new object[] { msec }); - } - } - - public static double CurrentPlayMsec - { - [Obsolete("不要用它,它有问题。用 PracticeMode.CurrentPlayMsec")] - get - { - if (IsPolyfill) - { - return PolyFill.timer; - } - - return (double)_debugFeatureType.GetField("_debugTimer", BindingFlags.Instance | BindingFlags.Public).GetValue(_debugFeatureOriginal); - } - set - { - if (IsPolyfill) - { - PolyFill.timer = value; - } - else - { - _debugFeatureType.GetField("_debugTimer", BindingFlags.Instance | BindingFlags.Public).SetValue(_debugFeatureOriginal, value); - } - - Seek(0); - } - } - - private static class GetOriginal - { - [HarmonyPatch(typeof(GameProcess), "OnStart")] - [HarmonyPostfix] - public static void Postfix(object ___debugFeature) - { - _debugFeatureOriginal = ___debugFeature; - } - } - - private static class PolyFill - { - public static bool isPause; - public static double timer; - - public static void DebugTimeSkip(int addMsec) - { - _gameMovie.Pause(pauseFlag: true); - NotesManager.Pause(true); - if (addMsec >= 0) - { - timer += addMsec; - } - else - { - timer = timer + addMsec >= 0.0 ? timer + addMsec : 0.0; - } - - _gameMovie.SetSeekFrame(timer); - SoundManager.SeekMusic((int)timer); - for (int i = 0; i < _monitors.Length; i++) - { - _monitors[i].Seek((int)timer); - } - - // magic number, dont know why - NotesManager.StartPlay((int)timer + 91); - NotesManager.Pause(isPause); - if (!isPause) - { - SoundManager.PauseMusic(pause: false); - _gameMovie.Pause(pauseFlag: false); - } - else - { - _gameMovie.Pause(pauseFlag: true); - } - - _gameProcess.UpdateNotes(); - } - - [HarmonyPatch(typeof(GameProcess), "OnUpdate")] - [HarmonyPostfix] - public static void Postfix(byte ____sequence) - { - if (____sequence != 4) return; - // GameSequence.Play - if (!isPause) - { - timer += GameManager.GetGameMSecAddD(); - } - - if (Input.GetKeyDown(KeyCode.Home)) - { - GameManager.AutoPlay = (GameManager.AutoPlayMode)((int)(GameManager.AutoPlay + 1) % Enum.GetNames(typeof(GameManager.AutoPlayMode)).Length); - } - else if (Input.GetKeyDown(KeyCode.Return)) - { - isPause = !isPause; - SoundManager.PauseMusic(isPause); - _gameMovie.Pause(isPause); - NotesManager.Pause(isPause); - } - else if (DebugInput.GetKeyDown(KeyCode.LeftArrow) || DebugInput.GetKeyDown(KeyCode.RightArrow)) - { - var num23 = 0; - if (DebugInput.GetKeyDown(KeyCode.LeftArrow)) - { - num23 = -1000; - } - - if (DebugInput.GetKeyDown(KeyCode.RightArrow)) - { - num23 = 1000; - } - - int addMsec = ((!DebugInput.GetKey(KeyCode.LeftShift) && !DebugInput.GetKey(KeyCode.RightShift)) ? ((!DebugInput.GetKey(KeyCode.LeftControl) && !DebugInput.GetKey(KeyCode.RightControl)) ? num23 : (num23 * 10)) : (num23 * 5)); - Singleton.Instance.Initialize(); - DebugTimeSkip(addMsec); - } - } - } -} diff --git a/AquaMai/AquaMai.Mods/Fix/DisableReboot.cs b/AquaMai/AquaMai.Mods/Fix/DisableReboot.cs deleted file mode 100644 index 04effc6f..00000000 --- a/AquaMai/AquaMai.Mods/Fix/DisableReboot.cs +++ /dev/null @@ -1,87 +0,0 @@ -using AquaMai.Config.Attributes; -using HarmonyLib; -using Manager.Operation; - -namespace AquaMai.Mods.Fix; - -[ConfigSection(exampleHidden: true, defaultOn: true)] -public class DisableReboot -{ - // IsAutoRebootNeeded - [HarmonyPrefix] - [HarmonyPatch(typeof(MaintenanceTimer), "IsAutoRebootNeeded")] - public static bool IsAutoRebootNeeded(ref bool __result) - { - __result = false; - return false; - } - - // IsUnderServerMaintenance - [HarmonyPrefix] - [HarmonyPatch(typeof(MaintenanceTimer), "IsUnderServerMaintenance")] - public static bool IsUnderServerMaintenance(ref bool __result) - { - __result = false; - return false; - } - - // RemainingMinutes - // Original: private int RemainingMinutes => (this._secServerMaintenance + 59) / 60; - [HarmonyPrefix] - [HarmonyPatch(typeof(MaintenanceTimer), "RemainingMinutes", MethodType.Getter)] - public static bool RemainingMinutes(ref int __result) - { - __result = 600; - return false; - } - - // GetAutoRebootSec - [HarmonyPrefix] - [HarmonyPatch(typeof(MaintenanceTimer), "GetAutoRebootSec")] - public static bool GetAutoRebootSec(ref int __result) - { - __result = 60 * 60 * 10; - return false; - } - - // GetServerMaintenanceSec - [HarmonyPrefix] - [HarmonyPatch(typeof(MaintenanceTimer), "GetServerMaintenanceSec")] - public static bool GetServerMaintenanceSec(ref int __result) - { - __result = 60 * 60 * 10; - return false; - } - - // Execute - [HarmonyPrefix] - [HarmonyPatch(typeof(MaintenanceTimer), "Execute")] - public static bool Execute(MaintenanceTimer __instance) - { - return false; - } - - // UpdateTimes - [HarmonyPrefix] - [HarmonyPatch(typeof(MaintenanceTimer), "UpdateTimes")] - public static bool UpdateTimes(MaintenanceTimer __instance) - { - return false; - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(ClosingTimer), "IsShowRemainingMinutes")] - public static bool IsShowRemainingMinutes(ref bool __result) - { - __result = false; - return false; - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(ClosingTimer), "IsClosed")] - public static bool IsClosed(ref bool __result) - { - __result = false; - return false; - } -} diff --git a/AquaMai/AquaMai.Mods/Fix/FixCheckAuth.cs b/AquaMai/AquaMai.Mods/Fix/FixCheckAuth.cs deleted file mode 100644 index 136424c1..00000000 --- a/AquaMai/AquaMai.Mods/Fix/FixCheckAuth.cs +++ /dev/null @@ -1,21 +0,0 @@ -using AMDaemon.Allnet; -using AquaMai.Config.Attributes; -using HarmonyLib; -using Manager; -using Manager.Operation; - -namespace AquaMai.Mods.Fix; - -[ConfigSection(exampleHidden: true, defaultOn: true)] -public class FixCheckAuth -{ - [HarmonyPostfix] - [HarmonyPatch(typeof(OperationManager), "CheckAuth_Proc")] - private static void PostCheckAuthProc(ref OperationData ____operationData) - { - if (Auth.GameServerUri.StartsWith("http://") || Auth.GameServerUri.StartsWith("https://")) - { - ____operationData.ServerUri = Auth.GameServerUri; - } - } -} diff --git a/AquaMai/AquaMai.Mods/Fix/FixConnSlide.cs b/AquaMai/AquaMai.Mods/Fix/FixConnSlide.cs deleted file mode 100644 index 59b38572..00000000 --- a/AquaMai/AquaMai.Mods/Fix/FixConnSlide.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System.Collections.Generic; -using System.Reflection; -using AquaMai.Core.Attributes; -using AquaMai.Config.Attributes; -using HarmonyLib; -using Manager; - -namespace AquaMai.Mods.Fix.Legacy; - -[ConfigSection(exampleHidden: true, defaultOn: true)] -[EnableGameVersion(23000)] -public class FixConnSlide -{ - /* 这个 Patch 用于修复以下 bug: - * 非 ConnSlide 被错误解析为 ConnSlide (Fes 首日刹那旅程爆机 bug) - * 原 method 逻辑如下: - * - * if (this.IsSlideAll(noteData1.type) && (index1 + 1 < this._note._noteData.Count ? 1 : 0) != 0) - * { - * int targetNote = noteData1.slideData.targetNote; - * if (noteData1.slideData != null) - * targetNote = noteData1.slideData.targetNote; - * for (int index3 = index1; index3 < this._note._noteData.Count; ++index3) - * { - * NoteData noteData3 = this._note._noteData[index3]; - * if (this.IsSlideAll(noteData3.type) && noteData3.time == noteData1.end && noteData3.startButtonPos == targetNote && noteData3.parent == null) - * { - * noteData3.parent = noteData1.parent; - * noteData1.child.Add(noteData3); - * noteData3.isUsed = true; - * noteData3.isJudged = true; - * break; - * } - * } - * } - * - * 修复 bug 需要把第二次调用 this.IsSlideAll() 更改为 this.IsConnectNote(), 这里使用 Transpiler 解决 - */ - [HarmonyTranspiler] - [HarmonyPatch(typeof(NotesReader), "calcSlide")] - private static IEnumerable Fix(IEnumerable instructions) - { - List instList = new List(instructions); - bool found = false; - MethodInfo methodIsSlideAll = AccessTools.Method(typeof(NotesReader), "IsSlideAll"); - MethodInfo methodIsConnectNote = AccessTools.Method(typeof(NotesReader), "IsConnectNote"); - - for (int i = 0; i < instList.Count; i++) - { - CodeInstruction inst = instList[i]; - if (!found && inst.Calls(methodIsSlideAll)) - { - found = true; - continue; - } - - if (found && inst.Calls(methodIsSlideAll)) - { - inst.operand = methodIsConnectNote; - // MelonLogger.Msg($"[FixConnSlide] Successfully patched NotesReader::calcSlide"); - break; - } - } - return instList; - } -} diff --git a/AquaMai/AquaMai.Mods/Fix/FixLevelDisplay.cs b/AquaMai/AquaMai.Mods/Fix/FixLevelDisplay.cs deleted file mode 100644 index 7eab928a..00000000 --- a/AquaMai/AquaMai.Mods/Fix/FixLevelDisplay.cs +++ /dev/null @@ -1,80 +0,0 @@ -using AquaMai.Core.Attributes; -using AquaMai.Config.Attributes; -using HarmonyLib; -using MAI2.Util; -using Manager; -using Monitor; -using Monitor.MusicSelect.ChainList; -using UnityEngine; - -namespace AquaMai.Mods.Fix; - -[EnableGameVersion(24000)] -[ConfigSection(exampleHidden: true, defaultOn: true)] -public class FixLevelDisplay -{ - // Fix wrong position of level number's display for music levels with non-consistant display level and rate level (difficuly constant) - // Stock game charts have no such inconsistency, but custom charts may have (e.g. 10+ but unrated) - - [HarmonyPostfix] - [HarmonyPatch(typeof(MusicChainCardObejct), "SetLevel")] - private static void FixLevelShiftMusicChainCardObejct(MusicLevelID levelID, SpriteCounter ____digitLevel, SpriteCounter ____doubleDigitLevel, bool utage, GameObject ____difficultyUtageQuesionMarkSingleDigit, GameObject ____difficultyUtageQuesionMarkDoubleDigit) - { - switch (levelID) - { - case > MusicLevelID.Level9P: - ____digitLevel.gameObject.SetActive(value: false); - ____doubleDigitLevel.gameObject.SetActive(value: true); - ____doubleDigitLevel.ChangeText(levelID.GetLevelNum().PadRight(3)); - break; - case >= MusicLevelID.None: - ____digitLevel.gameObject.SetActive(value: true); - ____doubleDigitLevel.gameObject.SetActive(value: false); - ____digitLevel.ChangeText(levelID.GetLevelNum().PadRight(2)); - break; - } - - if (!utage) return; - switch (levelID) - { - case > MusicLevelID.Level9P: - ____difficultyUtageQuesionMarkSingleDigit.SetActive(value: false); - ____difficultyUtageQuesionMarkDoubleDigit.SetActive(value: true); - break; - case >= MusicLevelID.None: - ____difficultyUtageQuesionMarkSingleDigit.SetActive(value: true); - ____difficultyUtageQuesionMarkDoubleDigit.SetActive(value: false); - break; - } - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(SingleResultCardController), "SetLevel")] - private static void FixLevelShiftSingleResultCardController(MusicLevelID levelID, bool isUtage, ref SpriteCounter ____difficultySingle, ref SpriteCounter ____difficultyDouble, GameObject ____utageQuestionMarkSingleDigit, GameObject ____utageQuestionMarkDoubleDigit) - { - FixLevelShiftMusicChainCardObejct(levelID, ____difficultySingle, ____difficultyDouble, isUtage, ____utageQuestionMarkSingleDigit, ____utageQuestionMarkDoubleDigit); - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(TotalResultPlayer), "SetLevel")] - private static void FixLevelShiftTotalResultPlayer(MusicLevelID levelID, bool isUtage, ref SpriteCounter ____difficultySingle, ref SpriteCounter ____difficultyDouble, GameObject ____utageQuestionMarkSingleDigit, GameObject ____utageQuestionMarkDoubleDigit) - { - FixLevelShiftMusicChainCardObejct(levelID, ____difficultySingle, ____difficultyDouble, isUtage, ____utageQuestionMarkSingleDigit, ____utageQuestionMarkDoubleDigit); - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(ResultMonitor), "SetLevel")] - private static void FixLevelShiftTotalResultPlayer(MusicLevelID levelID, ref SpriteCounter ____difficultySingle, ref SpriteCounter ____difficultyDouble) - { - FixLevelShiftMusicChainCardObejct(levelID, ____difficultySingle, ____difficultyDouble, false, null, null); - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(TrackStartMonitor), "SetTrackStart")] - private static void FixLevelShiftTrackStartMonitor(int ___monitorIndex, ref SpriteCounter ____difficultySingle, ref SpriteCounter ____difficultyDouble, GameObject ____utageQuestionSingleDigit, GameObject ____utageQuestionDoubleDigit) - { - var music = Singleton.Instance.GetMusic(GameManager.SelectMusicID[___monitorIndex]); - var levelID = (MusicLevelID)music.notesData[GameManager.SelectDifficultyID[___monitorIndex]].musicLevelID; - FixLevelShiftMusicChainCardObejct(levelID, ____difficultySingle, ____difficultyDouble, music.name.id >= 100000, ____utageQuestionSingleDigit, ____utageQuestionDoubleDigit); - } -} diff --git a/AquaMai/AquaMai.Mods/Fix/FixSlideAutoPlay.cs b/AquaMai/AquaMai.Mods/Fix/FixSlideAutoPlay.cs deleted file mode 100644 index 75c28c96..00000000 --- a/AquaMai/AquaMai.Mods/Fix/FixSlideAutoPlay.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System.Collections.Generic; -using AquaMai.Config.Attributes; -using HarmonyLib; -using Manager; -using Monitor; - -namespace AquaMai.Mods.Fix; - -[ConfigSection(exampleHidden: true, defaultOn: true)] -public class FixSlideAutoPlay -{ - /* 这个 Patch 用于修复以下 bug: - * SlideFan 在 AutoPlay 时, 只有第一个箭头会消失 - * 原 method 逻辑如下: - * - * if (this.IsNoteCheckTimeStartIgnoreJudgeWait()) - * { - * // do something ... - * if (!GameManager.IsAutoPlay()) - * { - * // do something ... - * for (int index = 0; index < this._arrowPrefubs.Length && (double) index < (double) num2 * 11.0; ++index) - * { - * // do something about displaying arrows ... - * } - * } - * else - * { - * float num4 = (currentMsec - this.StarLaunchMsec) / (this.StarArriveMsec - this.StarLaunchMsec - this.lastWaitTime); - * for (int index = 0; index < this._arrowPrefubs.Length && (double) index < (double) num4 * 1.0; ++index) - * { - * // do something about displaying arrows ... - * } - * if ((double) num4 > 1.0) - * num1 = 3; - * } - * // do something ... - * } - * - * 导致这个 bug 的原因是 else 分支的 for 循环终止条件写错了, 应该是 11.0 (因为有 11 个箭头), SBGA 写成了 1.0 - * 这个 method 中一共只有 5 处 ldc.r4 的 IL Code, 依次为 10.0, 11.0, 1.0, 1.0, 0.0 - * 修复 bug 需要把第三处的 1.0 更改为 11.0, 这里使用 Transpiler 解决 - */ - [HarmonyTranspiler] - [HarmonyPatch(typeof(SlideFan), "NoteCheck")] - private static IEnumerable FixFanAutoPlayArrow(IEnumerable instructions) - { - List instList = new List(instructions); - bool found = false; - for (int i = 0; i < instList.Count; i++) - { - CodeInstruction inst = instList[i]; - if (inst.LoadsConstant(11.0)) - { - found = true; - } - - if (found && inst.LoadsConstant(1.0)) - { - inst.operand = 11.0f; - break; - } - } - return instList; - } - - /* 这个 Patch 让 Slide 在 AutoPlay 的时候, 每个区仍然会分按下和松开两段进行推进 (加上 this._hitIn 的变化) - * 原 method 逻辑如下: - * - * if (!GameManager.IsAutoPlay()) - * { - * // do somethings ... - * } - * else - * { - * float num1 = (currentMsec - this.StarLaunchMsec) / (this.StarArriveMsec - this.StarLaunchMsec - this.lastWaitTime); - * this._hitIndex = (int) ((double) this._hitAreaList.Count * (double) num1); - * if (this._hitIndex >= this._hitAreaList.Count) - * this._hitIndex = this._hitAreaList.Count - 1; - * if (this._hitIndex < 0) - * this._hitIndex = 0; - * int num2 = (int) ((double) this._dispLaneNum * this.GetDeleteArrowDistance()); - * // do somethings ... - * } - * - * 现在要在 this.GetDeleteArrowDistance() 之前插入 - * this._hitIn = ((float)this._hitAreaList.Count * num1 > (float)this._hitIndex + 0.5f); - * 这段代码, 可以采用 Prefix, GetDeleteArrowDistance() 只在两个地方调用过, 另一处就在上面的 if 分支中 (即非 AutoPlay 情况) - */ - [HarmonyPrefix] - [HarmonyPatch(typeof(SlideRoot), "GetDeleteArrowDistance")] - private static void FixSlideAutoPlayArrow( - SlideRoot __instance, ref bool ____hitIn, int ____hitIndex, List ____hitAreaList, - float ___StarLaunchMsec, float ___StarArriveMsec, float ___lastWaitTime - ) - { - if (GameManager.IsAutoPlay()) - { - float prop = (NotesManager.GetCurrentMsec() - ___StarLaunchMsec) / (___StarArriveMsec - ___StarLaunchMsec - ___lastWaitTime); - ____hitIn = ____hitAreaList.Count * prop > ____hitIndex + 0.5f; - } - } - -} diff --git a/AquaMai/AquaMai.Mods/Fix/Legacy/FixQuickRetry130.cs b/AquaMai/AquaMai.Mods/Fix/Legacy/FixQuickRetry130.cs deleted file mode 100644 index ea67cdc2..00000000 --- a/AquaMai/AquaMai.Mods/Fix/Legacy/FixQuickRetry130.cs +++ /dev/null @@ -1,30 +0,0 @@ -using AquaMai.Core.Attributes; -using AquaMai.Config.Attributes; -using HarmonyLib; -using Manager; - -namespace AquaMai.Mods.Fix.Legacy; - -[ConfigSection(exampleHidden: true, defaultOn: true)] -[EnableGameVersion(23000, 23499, noWarn: true)] -public class FixQuickRetry130 -{ - // Fix for the game not resetting Fast and Late counts when quick retrying - // For game version < 1.35.0 - [HarmonyPostfix] - [HarmonyPatch(typeof(GamePlayManager), "SetQuickRetryFrag")] - public static void PostGamePlayManagerSetQuickRetryFrag(GamePlayManager __instance, bool flag) - { - // Since 1.35.0, `GameScoreList.Initialize()` resets the Fast and Late counts - if (flag && !Traverse.Create(typeof(GameScoreList)).Methods().Contains("Initialize")) - { - for (int i = 0; i < 4; i++) - { - var gameScoreList = __instance.GetGameScore(i); - var traverse = Traverse.Create(gameScoreList); - traverse.Property("Fast").SetValue((uint)0); - traverse.Property("Late").SetValue((uint)0); - } - } - } -} diff --git a/AquaMai/AquaMai.Mods/Fix/README.md b/AquaMai/AquaMai.Mods/Fix/README.md deleted file mode 100644 index 047dd121..00000000 --- a/AquaMai/AquaMai.Mods/Fix/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Fix - -Fix of the game's bugs or removal of the game's annoying unuseful "features". - -Non-removal "Fix" patches should have no (negative, or any visual changing) side-effects on the original game. - -All patches under "Fix" should enabled by default and hide in example. They could be still turned off manually in the config. diff --git a/AquaMai/AquaMai.Mods/Fix/Stability/FixMissingCharaCrash.cs b/AquaMai/AquaMai.Mods/Fix/Stability/FixMissingCharaCrash.cs deleted file mode 100644 index 6baca63f..00000000 --- a/AquaMai/AquaMai.Mods/Fix/Stability/FixMissingCharaCrash.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Collections.Generic; -using AquaMai.Config.Attributes; -using HarmonyLib; -using Process; -using Util; - -namespace AquaMai.Mods.Fix.Stability; - -/** - * Fix character selection crashing due to missing character data - */ -[ConfigSection(exampleHidden: true, defaultOn: true)] -public class FixMissingCharaCrash -{ - // Check if the return is null. If it is, make up a color - [HarmonyPostfix] - [HarmonyPatch(typeof(CharacterSelectProces), "GetMapColorData")] - public static void GetMapColorData(ref CharacterSelectProces __instance, ref CharacterMapColorData __result) - { - if (__result != null) return; - - // 1 is a color that definitely exists - if (MapMaster.GetSlotData(1) == null) - { - MapMaster.GetSlotData(1).Load(); - } - __result = MapMaster.GetSlotData(1); - } - - // This is called when loading the music selection screen, to display characters on the top screen - [HarmonyPrefix] - [HarmonyPatch(typeof(Monitor.CommonMonitor), "SetCharacterSlot", new Type[] { typeof(MessageCharactorInfomationData) })] - public static bool SetCharacterSlot(ref MessageCharactorInfomationData data, Dictionary ____characterSlotData) - { - // Some characters are not found in this dictionary. We simply skip loading those characters - if (!____characterSlotData.ContainsKey(data.MapKey)) - { - Console.Log($"Could not get CharacterSlotData for character [Index={data.Index}, MapKey={data.MapKey}], ignoring..."); - return false; - } - - return true; - } -} diff --git a/AquaMai/AquaMai.Mods/GameSettings/CreditConfig.cs b/AquaMai/AquaMai.Mods/GameSettings/CreditConfig.cs deleted file mode 100644 index 52b472f4..00000000 --- a/AquaMai/AquaMai.Mods/GameSettings/CreditConfig.cs +++ /dev/null @@ -1,49 +0,0 @@ -using AquaMai.Config.Attributes; -using AquaMai.Core.Attributes; -using HarmonyLib; - -namespace AquaMai.Mods.GameSettings; - -[ConfigSection( - en: "Set the game to Paid Play (lock credits) or Free Play.", - zh: "设置游戏为付费游玩(锁定可用点数)或免费游玩")] -public class CreditConfig -{ - [ConfigEntry( - en: "Set to Free Play (set to false for Paid Play).", - zh: "是否免费游玩(设为 false 时为付费游玩)")] - private static readonly bool isFreePlay = true; - - [HarmonyPrefix] - [HarmonyPatch(typeof(Manager.Credit), "IsFreePlay")] - private static bool PreIsFreePlay(ref bool __result) - { - __result = isFreePlay; - return false; - } - - [ConfigEntry( - en: "Lock credits amount (only valid in Paid Play). Set to 0 to disable.", - zh: "锁定可用点数数量(仅在付费游玩时有效),设为 0 以禁用")] - private static readonly uint lockCredits = 24u; - - private static bool ShouldLockCredits => !isFreePlay && lockCredits > 0; - - [EnableIf(nameof(ShouldLockCredits))] - [HarmonyPrefix] - [HarmonyPatch(typeof(Manager.Credit), "IsGameCostEnough")] - private static bool PreIsGameCostEnough(ref bool __result) - { - __result = true; - return false; - } - - [EnableIf(nameof(ShouldLockCredits))] - [HarmonyPrefix] - [HarmonyPatch(typeof(AMDaemon.CreditUnit), "Credit", MethodType.Getter)] - private static bool PreCredit(ref uint __result) - { - __result = lockCredits; - return false; - } -} diff --git a/AquaMai/AquaMai.Mods/GameSettings/ForceAsServer.cs b/AquaMai/AquaMai.Mods/GameSettings/ForceAsServer.cs deleted file mode 100644 index eebcaef9..00000000 --- a/AquaMai/AquaMai.Mods/GameSettings/ForceAsServer.cs +++ /dev/null @@ -1,28 +0,0 @@ -using AMDaemon; -using AquaMai.Config.Attributes; -using HarmonyLib; - -namespace AquaMai.Mods.GameSettings; - -[ConfigSection( - en: "If you want to configure in-shop party-link, you should turn this off.", - zh: "如果要配置店内招募的话,应该要把这个关闭", - defaultOn: true)] -public class ForceAsServer -{ - [HarmonyPrefix] - [HarmonyPatch(typeof(LanInstall), "IsServer", MethodType.Getter)] - private static bool PreIsServer(ref bool __result) - { - __result = true; - return false; - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(Network), "IsLanAvailable", MethodType.Getter)] - private static bool PreIsLanAvailable(ref bool __result) - { - __result = false; - return false; - } -} diff --git a/AquaMai/AquaMai.Mods/GameSettings/JudgeAdjust.cs b/AquaMai/AquaMai.Mods/GameSettings/JudgeAdjust.cs deleted file mode 100644 index 82092816..00000000 --- a/AquaMai/AquaMai.Mods/GameSettings/JudgeAdjust.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System.Threading; -using AquaMai.Config.Attributes; -using HarmonyLib; -using IO; -using Manager.UserDatas; - -namespace AquaMai.Mods.GameSettings; - -[ConfigSection( - en: "Globally adjust A/B judgment (unit same as in-game options) or increase touch delay.", - zh: "全局调整 A/B 判(单位和游戏里一样)或增加触摸延迟")] -public class JudgeAdjust -{ - [ConfigEntry( - en: "Adjust A judgment.", - zh: "调整 A 判")] - private static readonly double a = 0; - - [ConfigEntry( - en: "Adjust B judgment.", - zh: "调整 B 判")] - private static readonly double b = 0; - - [ConfigEntry( - en: "Increase touch delay.", - zh: "增加触摸延迟")] - private static readonly uint touchDelay = 0; - - [HarmonyPostfix] - [HarmonyPatch(typeof(UserOption), "GetAdjustMSec")] - public static void GetAdjustMSec(ref float __result) - { - __result += (float)(a * 16.666666d); - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(UserOption), "GetJudgeTimingFrame")] - public static void GetJudgeTimingFrame(ref float __result) - { - __result += (float)b; - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(NewTouchPanel), "Recv")] - public static void NewTouchPanelRecv() - { - if (touchDelay <= 0) return; - Thread.Sleep((int)touchDelay); - } -} diff --git a/AquaMai/AquaMai.Mods/GameSettings/README.md b/AquaMai/AquaMai.Mods/GameSettings/README.md deleted file mode 100644 index 8aa1d897..00000000 --- a/AquaMai/AquaMai.Mods/GameSettings/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# GameSettings - -Overriding or adjusting the game settings that're originally configurable / modifiable, but made into patches for unification, flexibility or convenience. - -Patches changing the way the game running / behaving which are not possible in the stock game may need to go to the GameSystem category. diff --git a/AquaMai/AquaMai.Mods/GameSettings/TouchSensitivity.cs b/AquaMai/AquaMai.Mods/GameSettings/TouchSensitivity.cs deleted file mode 100644 index 181901cf..00000000 --- a/AquaMai/AquaMai.Mods/GameSettings/TouchSensitivity.cs +++ /dev/null @@ -1,154 +0,0 @@ -using System.Collections.Generic; -using System.Reflection; -using AquaMai.Config.Attributes; -using HarmonyLib; -using IO; -using Manager; -using MelonLoader; - -namespace AquaMai.Mods.GameSettings; - -[ConfigSection( - en: """ - Use custom touch sensitivity. - When enabled, the settings in Test mode will not take effect. - When disabled, the settings in Test mode is used. - - Sensitivity adjustments in Test mode are not linear. - Default sensitivity in area A: 90, 80, 70, 60, 50, 40, 30, 26, 23, 20, 10. - Default sensitivity in other areas: 70, 60, 50, 40, 30, 20, 15, 10, 5, 1, 1. - A setting of 0 in Test mode corresponds to 40, 20 here, -5 corresponds to 90, 70, +5 corresponds to 10, 1. - The higher the number in Test mode, the lower the number here, resulting in higher sensitivity for official machines. - For ADX, the sensitivity is reversed, so the higher the number here, the higher the sensitivity. - """, - zh: """ - 使用自定义触摸灵敏度 - 这里启用之后 Test 里的就不再起作用了 - 这里禁用之后就还是用 Test 里的调 - - 在 Test 模式下调整的灵敏度不是线性的 - A 区默认灵敏度 90, 80, 70, 60, 50, 40, 30, 26, 23, 20, 10 - 其他区域默认灵敏度 70, 60, 50, 40, 30, 20, 15, 10, 5, 1, 1 - Test 里设置的 0 对应的是 40, 20 这一档,-5 是 90, 70,+5 是 10, 1 - Test 里的数字越大,这里的数字越小,对于官机来说,灵敏度更大 - 而 ADX 的灵敏度是反的,所以对于 ADX,这里的数字越大,灵敏度越大 - """)] -public class TouchSensitivity -{ - [ConfigEntry] - private static readonly byte A1 = 40; - - [ConfigEntry] - private static readonly byte A2 = 40; - - [ConfigEntry] - private static readonly byte A3 = 40; - - [ConfigEntry] - private static readonly byte A4 = 40; - - [ConfigEntry] - private static readonly byte A5 = 40; - - [ConfigEntry] - private static readonly byte A6 = 40; - - [ConfigEntry] - private static readonly byte A7 = 40; - - [ConfigEntry] - private static readonly byte A8 = 40; - - [ConfigEntry] - private static readonly byte B1 = 20; - - [ConfigEntry] - private static readonly byte B2 = 20; - - [ConfigEntry] - private static readonly byte B3 = 20; - - [ConfigEntry] - private static readonly byte B4 = 20; - - [ConfigEntry] - private static readonly byte B5 = 20; - - [ConfigEntry] - private static readonly byte B6 = 20; - - [ConfigEntry] - private static readonly byte B7 = 20; - - [ConfigEntry] - private static readonly byte B8 = 20; - - [ConfigEntry] - private static readonly byte C1 = 20; - - [ConfigEntry] - private static readonly byte C2 = 20; - - [ConfigEntry] - private static readonly byte D1 = 20; - - [ConfigEntry] - private static readonly byte D2 = 20; - - [ConfigEntry] - private static readonly byte D3 = 20; - - [ConfigEntry] - private static readonly byte D4 = 20; - - [ConfigEntry] - private static readonly byte D5 = 20; - - [ConfigEntry] - private static readonly byte D6 = 20; - - [ConfigEntry] - private static readonly byte D7 = 20; - - [ConfigEntry] - private static readonly byte D8 = 20; - - [ConfigEntry] - private static readonly byte E1 = 20; - - [ConfigEntry] - private static readonly byte E2 = 20; - - [ConfigEntry] - private static readonly byte E3 = 20; - - [ConfigEntry] - private static readonly byte E4 = 20; - - [ConfigEntry] - private static readonly byte E5 = 20; - - [ConfigEntry] - private static readonly byte E6 = 20; - - [ConfigEntry] - private static readonly byte E7 = 20; - - [ConfigEntry] - private static readonly byte E8 = 20; - - [HarmonyPrefix] - [HarmonyPatch(typeof(NewTouchPanel), "SetTouchPanelSensitivity")] - public static void SetTouchPanelSensitivityPrefix(List sensitivity) - { - var configType = typeof(TouchSensitivity); - for (var i = 0; i < 34; i++) - { - var area = (InputManager.TouchPanelArea)i; - var field = configType.GetField(area.ToString(), BindingFlags.NonPublic | BindingFlags.Static); - var value = (byte)field.GetValue(null); - sensitivity[i] = value; - } - MelonLogger.Msg("[TouchSensitivity] Applied"); - } -} diff --git a/AquaMai/AquaMai.Mods/GameSystem/Assets/Fonts.cs b/AquaMai/AquaMai.Mods/GameSystem/Assets/Fonts.cs deleted file mode 100644 index e05f6218..00000000 --- a/AquaMai/AquaMai.Mods/GameSystem/Assets/Fonts.cs +++ /dev/null @@ -1,146 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using AquaMai.Config.Attributes; -using AquaMai.Core.Helpers; -using HarmonyLib; -using MelonLoader; -using TMPro; -using UnityEngine; -using UnityEngine.TextCore.LowLevel; - -namespace AquaMai.Mods.GameSystem.Assets; - -[ConfigSection( - en: "Use custom font(s) as fallback or fully replace the original game font.", - zh: "使用自定义字体作为回退(解决中文字形缺失问题),或完全替换游戏原字体")] -public class Fonts -{ - [ConfigEntry( - en: """ - Font path(s). - Use semicolon to separate multiple paths for a fallback chain. - Microsoft YaHei Bold by default. - """, - zh: """ - 字体路径 - 使用分号分隔多个路径以构成 Fallback 链 - 默认为微软雅黑 Bold - """)] - private static readonly string paths = "%SYSTEMROOT%/Fonts/msyhbd.ttc"; - - [ConfigEntry( - en: "Add custom font(s) as fallback, use original game font when possible.", - zh: "将自定义字体作为游戏原字体的回退,尽可能使用游戏原字体")] - private static readonly bool addAsFallback = true; - - private static List fontAssets = []; - private static readonly List processedFonts = []; - - private static TMP_FontAsset replacementFontAsset; - private static List fallbackFontAssets = []; - - public static void OnBeforePatch() - { - var paths = Fonts.paths - .Split(';') - .Where(p => !string.IsNullOrWhiteSpace(p)) - .Select(FileSystem.ResolvePath); - var fonts = paths - .Select(p => - { - var font = new Font(p); - if (font == null) - { - MelonLogger.Warning($"[Fonts] Font not found: {p}"); - } - return font; - }) - .Where(f => f != null); - fontAssets = fonts - .Select(f => TMP_FontAsset.CreateFontAsset(f, 90, 9, GlyphRenderMode.SDFAA, 8192, 8192)) - .ToList(); - - if (fontAssets.Count == 0) - { - MelonLogger.Warning("[Fonts] No font loaded."); - } - else if (addAsFallback) - { - fallbackFontAssets = fontAssets; - } - else - { - replacementFontAsset = fontAssets[0]; - fallbackFontAssets = fontAssets.Skip(1).ToList(); - } - } - - [HarmonyPatch(typeof(TextMeshProUGUI), "Awake")] - [HarmonyPostfix] - public static void PostAwake(TextMeshProUGUI __instance) - { - if (fontAssets.Count == 0) return; - if (processedFonts.Contains(__instance.font)) return; - - if (replacementFontAsset != null) - { - ProcessReplacement(__instance); - } - if (fallbackFontAssets.Count > 0) - { - ProcessFallback(__instance); - } - - processedFonts.Add(__instance.font); - } - - private static void ProcessReplacement(TextMeshProUGUI __instance) - { -# if DEBUG - MelonLogger.Msg($"{__instance.font.name} {__instance.text}"); -# endif - - var materialOrigin = __instance.fontMaterial; - var materialSharedOrigin = __instance.fontSharedMaterial; - __instance.font = replacementFontAsset; - -# if DEBUG - MelonLogger.Msg($"shaderKeywords {materialOrigin.shaderKeywords.Join()} {__instance.fontMaterial.shaderKeywords.Join()}"); -# endif - // __instance.fontSharedMaterial = materialSharedOrigin; - - // 这样之后该有描边的地方整个字后面都是阴影,它不知道哪里是边 - // materialOrigin.mainTexture = __instance.fontMaterial.mainTexture; - // materialOrigin.mainTextureOffset = __instance.fontMaterial.mainTextureOffset; - // materialOrigin.mainTextureScale = __instance.fontMaterial.mainTextureScale; - // __instance.fontMaterial.CopyPropertiesFromMaterial(materialOrigin); - - // 这样了之后有描边了,但是描边很细 - // __instance.fontMaterial.shader = materialOrigin.shader; - foreach (var keyword in materialOrigin.shaderKeywords) - { - __instance.fontMaterial.EnableKeyword(keyword); - } - // __instance.fontMaterial.globalIlluminationFlags = materialOrigin.globalIlluminationFlags; - - // 原来是 underlay,但是复制这三个属性之后就又变成整个字后面都是阴影了 - // __instance.fontMaterial.SetFloat(ShaderUtilities.ID_UnderlayOffsetY, materialOrigin.GetFloat(ShaderUtilities.ID_UnderlayOffsetY)); - // __instance.fontMaterial.SetFloat(ShaderUtilities.ID_UnderlayOffsetX, materialOrigin.GetFloat(ShaderUtilities.ID_UnderlayOffsetX)); - // __instance.fontMaterial.SetFloat(ShaderUtilities.ID_UnderlayDilate, materialOrigin.GetFloat(ShaderUtilities.ID_UnderlayDilate)); - - // if(materialOrigin.shaderKeywords.Contains(ShaderUtilities.Keyword_Underlay)) - // { - // __instance.fontMaterial.EnableKeyword(ShaderUtilities.Keyword_Glow); - // __instance.fontMaterial.SetFloat(ShaderUtilities.ID_GlowOuter, .5f); - // // __instance.fontMaterial.SetFloat(ShaderUtilities.ID_UnderlayOffsetX, materialOrigin.GetFloat(ShaderUtilities.ID_UnderlayOffsetX)); - // } - } - - private static void ProcessFallback(TextMeshProUGUI __instance) - { - foreach (var fontAsset in fallbackFontAssets) - { - __instance.font.fallbackFontAssetTable.Add(fontAsset); - } - } -} diff --git a/AquaMai/AquaMai.Mods/GameSystem/Assets/LoadAssetBundleWithoutManifest.cs b/AquaMai/AquaMai.Mods/GameSystem/Assets/LoadAssetBundleWithoutManifest.cs deleted file mode 100644 index 02b7f7ac..00000000 --- a/AquaMai/AquaMai.Mods/GameSystem/Assets/LoadAssetBundleWithoutManifest.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using HarmonyLib; -using UnityEngine; -using Manager; -using Util; -using AquaMai.Config.Attributes; - -namespace AquaMai.Mods.GameSystem.Assets; - -[ConfigSection( - en: "Load all existing \".ab\" image resources regardless of the AssetBundleImages manifest.", - zh: """ - 加载所有存在的 .ab 图片资源(无视 AssetBundleImages.manifest) - 导入了删除曲包之类的话,应该需要开启这个 - """)] -public class LoadAssetBundleWithoutManifest -{ - private static HashSet abFiles = new HashSet(); - - [HarmonyPostfix] - [HarmonyPatch(typeof(OptionDataManager), "CheckAssetBundle")] - public static void PostCheckAssetBundle(ref Safe.ReadonlySortedDictionary abs) - { - foreach (var ab in abs) - { - abFiles.Add(ab.Key); - } - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(AssetBundleManifest), "GetAllAssetBundles")] - public static bool PreGetAllAssetBundles(AssetBundleManifest __instance, ref string[] __result) - { - __result = abFiles.ToArray(); - return false; - } -} diff --git a/AquaMai/AquaMai.Mods/GameSystem/Assets/LoadLocalImages.cs b/AquaMai/AquaMai.Mods/GameSystem/Assets/LoadLocalImages.cs deleted file mode 100644 index 72152de9..00000000 --- a/AquaMai/AquaMai.Mods/GameSystem/Assets/LoadLocalImages.cs +++ /dev/null @@ -1,577 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using HarmonyLib; -using UnityEngine; -using System.Text.RegularExpressions; -using MAI2.Util; -using Manager; -using MelonLoader; -using Monitor; -using AquaMai.Config.Attributes; -using AquaMai.Core.Helpers; - -namespace AquaMai.Mods.GameSystem.Assets; - -[ConfigSection( - en: "Load asset images from the configured directory (for self-made charts).", - zh: "从指定目录下加载资源图片(自制谱用)")] -public class LoadLocalImages -{ - [ConfigEntry] - private static readonly string localAssetsDir = "LocalAssets"; - - private static readonly string[] imageExts = [".jpg", ".png", ".jpeg"]; - private static readonly Dictionary jacketPaths = []; - private static readonly Dictionary framePaths = []; - private static readonly Dictionary platePaths = []; - private static readonly Dictionary framemaskPaths = []; - private static readonly Dictionary framepatternPaths = []; - private static readonly Dictionary iconPaths = []; - private static readonly Dictionary charaPaths = []; - private static readonly Dictionary partnerPaths = []; - //private static readonly Dictionary navicharaPaths = []; - private static readonly Dictionary tabTitlePaths = []; - private static readonly Dictionary localAssetsContents = []; - - [HarmonyPrefix] - [HarmonyPatch(typeof(DataManager), "LoadMusicBase")] - public static void LoadMusicPostfix(List ____targetDirs) - { - foreach (var aDir in ____targetDirs) - { - if (Directory.Exists(Path.Combine(aDir, @"AssetBundleImages\jacket"))) - foreach (var file in Directory.GetFiles(Path.Combine(aDir, @"AssetBundleImages\jacket"))) - { - if (!imageExts.Contains(Path.GetExtension(file).ToLowerInvariant())) continue; - var idStr = Path.GetFileName(file).Substring("ui_jacket_".Length, 6); - jacketPaths[idStr] = file; - } - - if (Directory.Exists(Path.Combine(aDir, @"AssetBundleImages\frame"))) - foreach (var file in Directory.GetFiles(Path.Combine(aDir, @"AssetBundleImages\frame"))) - { - if (!imageExts.Contains(Path.GetExtension(file).ToLowerInvariant())) continue; - var idStr = Path.GetFileName(file).Substring("ui_frame_".Length, 6); - framePaths[idStr] = file; - } - - if (Directory.Exists(Path.Combine(aDir, @"AssetBundleImages\nameplate"))) - foreach (var file in Directory.GetFiles(Path.Combine(aDir, @"AssetBundleImages\nameplate"))) - { - if (!imageExts.Contains(Path.GetExtension(file).ToLowerInvariant())) continue; - var idStr = Path.GetFileName(file).Substring("ui_plate_".Length, 6); - platePaths[idStr] = file; - } - - if (Directory.Exists(Path.Combine(aDir, @"AssetBundleImages\framemask"))) - foreach (var file in Directory.GetFiles(Path.Combine(aDir, @"AssetBundleImages\framemask"))) - { - if (!imageExts.Contains(Path.GetExtension(file).ToLowerInvariant())) continue; - var idStr = Path.GetFileName(file).Substring("ui_framemask_".Length, 6); - framemaskPaths[idStr] = file; - } - - if (Directory.Exists(Path.Combine(aDir, @"AssetBundleImages\framepattern"))) - foreach (var file in Directory.GetFiles(Path.Combine(aDir, @"AssetBundleImages\framepattern"))) - { - if (!imageExts.Contains(Path.GetExtension(file).ToLowerInvariant())) continue; - var idStr = Path.GetFileName(file).Substring("ui_framepattern_".Length, 6); - framepatternPaths[idStr] = file; - } - - if (Directory.Exists(Path.Combine(aDir, @"AssetBundleImages\icon"))) - foreach (var file in Directory.GetFiles(Path.Combine(aDir, @"AssetBundleImages\icon"))) - { - if (!imageExts.Contains(Path.GetExtension(file).ToLowerInvariant())) continue; - var idStr = Path.GetFileName(file).Substring("ui_icon_".Length, 6); - iconPaths[idStr] = file; - } - - if (Directory.Exists(Path.Combine(aDir, @"AssetBundleImages\chara"))) - foreach (var file in Directory.GetFiles(Path.Combine(aDir, @"AssetBundleImages\chara"))) - { - if (!imageExts.Contains(Path.GetExtension(file).ToLowerInvariant())) continue; - var idStr = Path.GetFileName(file).Substring("ui_chara_".Length, 6); - charaPaths[idStr] = file; - } - - if (Directory.Exists(Path.Combine(aDir, @"AssetBundleImages\partner"))) - foreach (var file in Directory.GetFiles(Path.Combine(aDir, @"AssetBundleImages\partner"))) - { - if (!imageExts.Contains(Path.GetExtension(file).ToLowerInvariant())) continue; - var idStr = Path.GetFileName(file).Substring("ui_Partner_".Length, 6); - partnerPaths[idStr] = file; - } - //if (Directory.Exists(Path.Combine(aDir, @"AssetBundleImages\navichara\sprite\parts\ui_navichara_21"))) - // foreach (var file in Directory.GetFiles(Path.Combine(aDir, @"AssetBundleImages\navichara\sprite\parts\ui_navichara_", charaid))) - //{ - // if (!imageExts.Contains(Path.GetExtension(file).ToLowerInvariant())) continue; - //var idStr = Path.GetFileName(file).Substring("ui_navichara_".Length, 6); - // navicharaPaths[idStr] = file; - // } - - if (Directory.Exists(Path.Combine(aDir, @"Common\Sprites\Tab\Title"))) - foreach (var file in Directory.GetFiles(Path.Combine(aDir, @"Common\Sprites\Tab\Title"))) - { - if (!imageExts.Contains(Path.GetExtension(file).ToLowerInvariant())) continue; - tabTitlePaths[Path.GetFileNameWithoutExtension(file).ToLowerInvariant()] = file; - } - } - - MelonLogger.Msg($"[LoadLocalImages] Loaded {jacketPaths.Count} Jacket, {platePaths.Count} NamePlate, {framePaths.Count} Frame, {framemaskPaths.Count} FrameMask, {framepatternPaths.Count} FramePattern, {iconPaths.Count} Icon, {charaPaths.Count} Chara, {partnerPaths.Count} PartnerLogo, {tabTitlePaths.Count} Tab Titles from AssetBundleImages."); - - var resolvedDir = FileSystem.ResolvePath(localAssetsDir); - if (Directory.Exists(resolvedDir)) - foreach (var laFile in Directory.EnumerateFiles(resolvedDir)) - { - if (!imageExts.Contains(Path.GetExtension(laFile).ToLowerInvariant())) continue; - localAssetsContents[Path.GetFileNameWithoutExtension(laFile).ToLowerInvariant()] = laFile; - } - - MelonLogger.Msg($"[LoadLocalImages] Loaded {localAssetsContents.Count} LocalAssets."); - } - - private static string GetJacketPath(string id) - { - return localAssetsContents.TryGetValue(id, out var laPath) ? laPath : jacketPaths.GetValueOrDefault(id); - } - - public static Texture2D GetJacketTexture2D(string id) - { - var path = GetJacketPath(id); - if (path == null) - { - return null; - } - - var texture = new Texture2D(1, 1, TextureFormat.RGBA32, false); - texture.LoadImage(File.ReadAllBytes(path)); - return texture; - } - - public static Texture2D GetJacketTexture2D(int id) - { - return GetJacketTexture2D($"{id:000000}"); - } - - private static string GetFramePath(string id) - { - return framePaths.GetValueOrDefault(id); - } - - public static Texture2D GetFrameTexture2D(string id) - { - var path = GetFramePath(id); - if (path == null) - { - return null; - } - - var texture = new Texture2D(1, 1, TextureFormat.RGBA32, false); - texture.LoadImage(File.ReadAllBytes(path)); - return texture; - } - - private static string GetPlatePath(string id) - { - return platePaths.GetValueOrDefault(id); - } - - public static Texture2D GetPlateTexture2D(string id) - { - var path = GetPlatePath(id); - if (path == null) - { - return null; - } - - var texture = new Texture2D(1, 1, TextureFormat.RGBA32, false); - texture.LoadImage(File.ReadAllBytes(path)); - return texture; - } - - private static string GetFrameMaskPath(string id) - { - return framemaskPaths.GetValueOrDefault(id); - } - - public static Texture2D GetFrameMaskTexture2D(string id) - { - var path = GetFrameMaskPath(id); - if (path == null) - { - return null; - } - - var texture = new Texture2D(1, 1, TextureFormat.RGBA32, false); - texture.LoadImage(File.ReadAllBytes(path)); - return texture; - } - - private static string GetFramePatternPath(string id) - { - return framepatternPaths.GetValueOrDefault(id); - } - - public static Texture2D GetFramePatternTexture2D(string id) - { - var path = GetFramePatternPath(id); - if (path == null) - { - return null; - } - - var texture = new Texture2D(1, 1, TextureFormat.RGBA32, false); - texture.LoadImage(File.ReadAllBytes(path)); - return texture; - } - - private static string GetIconPath(string id) - { - return iconPaths.GetValueOrDefault(id); - } - - public static Texture2D GetIconTexture2D(string id) - { - var path = GetIconPath(id); - if (path == null) - { - return null; - } - - var texture = new Texture2D(1, 1, TextureFormat.RGBA32, false); - texture.LoadImage(File.ReadAllBytes(path)); - return texture; - } - - private static string GetCharaPath(string id) - { - return charaPaths.GetValueOrDefault(id); - } - - public static Texture2D GetCharaTexture2D(string id) - { - var path = GetCharaPath(id); - if (path == null) - { - return null; - } - - var texture = new Texture2D(1, 1, TextureFormat.RGBA32, false); - texture.LoadImage(File.ReadAllBytes(path)); - return texture; - } - - private static string GetPartnerPath(string id) - { - return partnerPaths.GetValueOrDefault(id); - } - - public static Texture2D GetPartnerTexture2D(string id) - { - var path = GetPartnerPath(id); - if (path == null) - { - return null; - } - - var texture = new Texture2D(1, 1, TextureFormat.RGBA32, false); - texture.LoadImage(File.ReadAllBytes(path)); - return texture; - } - - /* - [HarmonyPatch] - public static class TabTitleLoader - { - public static IEnumerable TargetMethods() - { - // Fxxk unity - // game load tab title by call Resources.Load directly - // patching Resources.Load need this stuff - // var method = typeof(Resources).GetMethods(BindingFlags.Public | BindingFlags.Static).First(it => it.Name == "Load" && it.IsGenericMethod).MakeGenericMethod(typeof(Sprite)); - // return [method]; - // but it not work, game will blackscreen if add prefix or postfix - // - // patching AssetBundleManager.LoadAsset will lead game memory error - // return [AccessTools.Method(typeof(AssetBundleManager), "LoadAsset", [typeof(string)], [typeof(Object)])]; - // and this is not work because game not using this - // - // we load them manually after game load and no need to hook the load progress - } - - public static bool Prefix(string path, ref Object __result) - { - if (!path.StartsWith("Common/Sprites/Tab/Title/")) return true; - var filename = Path.GetFileNameWithoutExtension(path).ToLowerInvariant(); - var locPath = localAssetsContents.TryGetValue(filename, out var laPath) ? laPath : tabTitlePaths.GetValueOrDefault(filename); - if (locPath is null) return true; - - var texture = new Texture2D(1, 1); - texture.LoadImage(File.ReadAllBytes(locPath)); - __result = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f)); - MelonLogger.Msg($"GetTabTitleSpritePrefix {locPath} {__result}"); - return false; - } - } - */ - - [HarmonyPostfix] - [HarmonyPatch(typeof(MusicSelectMonitor), "Initialize")] - public static void TabTitleLoader(MusicSelectMonitor __instance, Dictionary ____genreSprite, Dictionary ____versionSprite) - { - var genres = Singleton.Instance.GetMusicGenres(); - foreach (var (id, genre) in genres) - { - if (____genreSprite.GetValueOrDefault(id) is not null) continue; - var filename = genre.FileName.ToLowerInvariant(); - var locPath = localAssetsContents.TryGetValue(filename, out var laPath) ? laPath : tabTitlePaths.GetValueOrDefault(filename); - if (locPath is null) continue; - var texture = new Texture2D(1, 1, TextureFormat.RGBA32, false); - texture.LoadImage(File.ReadAllBytes(locPath)); - ____genreSprite[id] = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f)); - } - - var versions = Singleton.Instance.GetMusicVersions(); - foreach (var (id, version) in versions) - { - if (____versionSprite.GetValueOrDefault(id) is not null) continue; - var filename = version.FileName.ToLowerInvariant(); - var locPath = localAssetsContents.TryGetValue(filename, out var laPath) ? laPath : tabTitlePaths.GetValueOrDefault(filename); - if (locPath is null) continue; - var texture = new Texture2D(1, 1, TextureFormat.RGBA32, false); - texture.LoadImage(File.ReadAllBytes(locPath)); - ____versionSprite[id] = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f)); - } - } - - [HarmonyPatch] - public static class JacketLoader - { - public static IEnumerable TargetMethods() - { - var AM = typeof(AssetManager); - return [AM.GetMethod("GetJacketThumbTexture2D", [typeof(string)]), AM.GetMethod("GetJacketTexture2D", [typeof(string)])]; - } - - public static bool Prefix(string filename, ref Texture2D __result, AssetManager __instance) - { - var matches = Regex.Matches(filename, @"UI_Jacket_(\d+)(_s)?\.png"); - if (matches.Count < 1) - { - return true; - } - - var id = matches[0].Groups[1].Value; - - var texture = GetJacketTexture2D(id); - __result = texture ?? __instance.LoadAsset($"Jacket/UI_Jacket_{id}.png"); - - return false; - } - } - - [HarmonyPatch] - public static class FrameLoader - { - public static IEnumerable TargetMethods() - { - var AM = typeof(AssetManager); - return [AM.GetMethod("GetFrameThumbTexture2D", [typeof(string)]), AM.GetMethod("GetFrameTexture2D", [typeof(string)])]; - } - - public static bool Prefix(string filename, ref Texture2D __result, AssetManager __instance) - { - var matches = Regex.Matches(filename, @"UI_Frame_(\d+)(_s)?\.png"); - if (matches.Count < 1) - { - return true; - } - - var id = matches[0].Groups[1].Value; - - var texture = GetFrameTexture2D(id); - __result = texture ?? __instance.LoadAsset($"Frame/UI_Frame_{id}.png"); - - return false; - } - } - - [HarmonyPatch] - public static class PlateLoader - { - public static IEnumerable TargetMethods() - { - var AM = typeof(AssetManager); - return [AM.GetMethod("GetPlateTexture2D", [typeof(string)])]; - } - - public static bool Prefix(string filename, ref Texture2D __result, AssetManager __instance) - { - var matches = Regex.Matches(filename, @"UI_Plate_(\d+)\.png"); - if (matches.Count < 1) - { - return true; - } - - var id = matches[0].Groups[1].Value; - - var texture = GetPlateTexture2D(id); - __result = texture ?? __instance.LoadAsset($"NamePlate/UI_Plate_{id}.png"); - - return false; - } - } - - [HarmonyPatch] - public static class FrameMaskLoader - { - public static IEnumerable TargetMethods() - { - var AM = typeof(AssetManager); - return [AM.GetMethod("GetFrameMaskTexture2D", [typeof(string)])]; - } - - public static bool Prefix(string filename, ref Texture2D __result, AssetManager __instance) - { - var matches = Regex.Matches(filename, @"UI_FrameMask_(\d+)\.png"); - if (matches.Count < 1) - { - return true; - } - - var id = matches[0].Groups[1].Value; - - var texture = GetFrameMaskTexture2D(id); - __result = texture ?? __instance.LoadAsset($"FrameMask/UI_FrameMask_{id}.png"); - - return false; - } - } - - [HarmonyPatch] - public static class FramePatternLoader - { - public static IEnumerable TargetMethods() - { - var AM = typeof(AssetManager); - return [AM.GetMethod("GetFramePatternTexture2D", [typeof(string)])]; - } - - public static bool Prefix(string filename, ref Texture2D __result, AssetManager __instance) - { - var matches = Regex.Matches(filename, @"UI_FramePattern_(\d+)\.png"); - if (matches.Count < 1) - { - return true; - } - - var id = matches[0].Groups[1].Value; - - var texture = GetFramePatternTexture2D(id); - __result = texture ?? __instance.LoadAsset($"FramePattern/UI_FramePattern_{id}.png"); - - return false; - } - } - - // Private | Instance - [HarmonyPrefix] - [HarmonyPatch(typeof(AssetManager), "GetIconTexture2D", typeof(string))] - public static bool IconLoader(string filename, ref Texture2D __result, AssetManager __instance) - { - var matches = Regex.Matches(filename, @"UI_Icon_(\d+)\.png"); - if (matches.Count < 1) - { - return true; - } - - var id = matches[0].Groups[1].Value; - - var texture = GetIconTexture2D(id); - __result = texture ?? __instance.LoadAsset($"Icon/UI_Icon_{id}.png"); - - return false; - } - - [HarmonyPatch] - public static class CharaLoader - { - public static IEnumerable TargetMethods() - { - var AM = typeof(AssetManager); - return [AM.GetMethod("GetCharacterTexture2D", [typeof(string)])]; - } - - public static bool Prefix(string filename, ref Texture2D __result, AssetManager __instance) - { - var matches = Regex.Matches(filename, @"UI_Chara_(\d+)\.png"); - if (matches.Count < 1) - { - return true; - } - - var id = matches[0].Groups[1].Value; - - var texture = GetCharaTexture2D(id); - __result = texture ?? __instance.LoadAsset($"Chara/UI_Chara_{id}.png"); - - return false; - } - } - - [HarmonyPatch] - public static class PartnerLoader - { - public static IEnumerable TargetMethods() - { - var AM = typeof(AssetManager); - return [AM.GetMethod("GetPartnerTexture2D", [typeof(string)])]; - } - - public static bool Prefix(string filename, ref Texture2D __result, AssetManager __instance) - { - var matches = Regex.Matches(filename, @"UI_Partner_(\d+)\.png"); - if (matches.Count < 1) - { - return true; - } - - var id = matches[0].Groups[1].Value; - - var texture = GetPartnerTexture2D(id); - __result = texture ?? __instance.LoadAsset($"Partner/UI_Partner_{id}.png"); - - return false; - } - } - /* - [HarmonyPatch] - public static class FrameLoader - { - public static IEnumerable TargetMethods() - { - var AM = typeof(AssetManager); - return [AM.GetMethod("GetFrameThumbTexture2D", [typeof(string)]), AM.GetMethod("GetFrameTexture2D", [typeof(string)])]; - } - - public static bool Prefix(string filename, ref Texture2D __result, AssetManager __instance) - { - var matches = Regex.Matches(filename, @"UI_Frame_(\d+)\.png"); - if (matches.Count < 1) - { - return true; - } - - var id = matches[0].Groups[1].Value; - - var texture = GetFrameTexture2D(id); - __result = texture ?? __instance.LoadAsset($"Frame/UI_Frame_{id}.png"); - - return false; - } - } - */ -} diff --git a/AquaMai/AquaMai.Mods/GameSystem/Assets/UseJacketAsDummyMovie.cs b/AquaMai/AquaMai.Mods/GameSystem/Assets/UseJacketAsDummyMovie.cs deleted file mode 100644 index a39fb265..00000000 --- a/AquaMai/AquaMai.Mods/GameSystem/Assets/UseJacketAsDummyMovie.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.Linq; -using AquaMai.Config.Attributes; -using HarmonyLib; -using MAI2.Util; -using Manager; -using MelonLoader; -using Monitor.Game; -using UnityEngine; - -namespace AquaMai.Mods.GameSystem.Assets; - -[ConfigSection( - en: """ - Use the png jacket above as MV if no .dat found in the movie folder. - Use together with `LoadLocalImages`. - """, - zh: """ - 如果 movie 文件夹中没有 dat 格式的 MV 的话,就用歌曲的封面做背景,而不是显示迪拉熊的笑脸 - 请和 `LoadLocalImages` 一起用 - """)] -public class UseJacketAsDummyMovie -{ - [HarmonyPostfix] - [HarmonyPatch(typeof(GameCtrl), "IsReady")] - public static void LoadLocalBgaAwake(GameObject ____movieMaskObj) - { - var music = Singleton.Instance.GetMusic(GameManager.SelectMusicID[0]); - if (music is null) return; - - var moviePath = Singleton.Instance.GetMovieDataPath($"{music.movieName.id:000000}") + ".dat"; - if (!moviePath.Contains("dummy")) return; - - var jacket = LoadLocalImages.GetJacketTexture2D(music.movieName.id); - if (jacket is null) - { - MelonLogger.Msg("No jacket found for music " + music); - return; - } - - var components = ____movieMaskObj.GetComponentsInChildren(false); - var movies = components.Where(it => it.name == "Movie"); - - foreach (var movie in movies) - { - // If I create a new RawImage component, the jacket will be not be displayed - // I think it will be difficult to make it work with RawImage - // So I change the material that plays video to default sprite material - // The original player is actually a sprite renderer and plays video with a custom material - var sprite = movie.GetComponent(); - sprite.sprite = Sprite.Create(jacket, new Rect(0, 0, jacket.width, jacket.height), new Vector2(0.5f, 0.5f)); - sprite.material = new Material(Shader.Find("Sprites/Default")); - } - } -} diff --git a/AquaMai/AquaMai.Mods/GameSystem/CustomCameraId.cs b/AquaMai/AquaMai.Mods/GameSystem/CustomCameraId.cs deleted file mode 100644 index f659d644..00000000 --- a/AquaMai/AquaMai.Mods/GameSystem/CustomCameraId.cs +++ /dev/null @@ -1,142 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections; -using System.Linq; -using HarmonyLib; -using Manager; -using MelonLoader; -using UnityEngine; -using AquaMai.Config.Attributes; - -namespace AquaMai.Mods.GameSystem; - -[ConfigSection( - en: """ - Use custom CameraId rather than the default ones. - If enabled, you can customize the game to use the specified camera. - """, - zh: """ - 使用自定义的摄像头 ID 而不是默认的 - 启用后可以指定游戏使用的摄像头 - """)] -public class CustomCameraId -{ - [ConfigEntry( - en: "Print the camera list to the log when starting, can be used as a basis for modification.", - zh: "启动时打印摄像头列表到日志中,可以作为修改的依据")] - public static bool printCameraList; - - [ConfigEntry( - en: "DX Pass 1P.", - zh: "DX Pass 1P")] - public static int leftQrCamera; - - [ConfigEntry( - en: "DX Pass 2P.", - zh: "DX Pass 2P")] - public static int rightQrCamera; - - [ConfigEntry( - en: "Player Camera.", - zh: "玩家摄像头")] - public static int photoCamera; - - [ConfigEntry( - en: "WeChat QRCode Camera.", - zh: "二维码扫描摄像头")] - public static int chimeCamera; - - private static readonly Dictionary cameraTypeMap = new() - { - ["LeftQrCamera"] = "QRLeft", - ["RightQrCamera"] = "QRRight", - ["PhotoCamera"] = "Photo", - ["ChimeCamera"] = "Chime", - }; - - [HarmonyPrefix] - [HarmonyPatch(typeof(CameraManager), "CameraInitialize")] - public static bool CameraInitialize(CameraManager __instance, ref IEnumerator __result) - { - __result = CameraInitialize(__instance); - return false; - } - - private static IEnumerator CameraInitialize(CameraManager __instance) - { - var textureCache = new WebCamTexture[WebCamTexture.devices.Length]; - SortedDictionary webCamTextures = []; - foreach (var (configEntry, cameraTypeName) in cameraTypeMap) - { - int deviceId = Traverse.Create(typeof(CustomCameraId)).Field(configEntry).GetValue(); - if (deviceId < 0 || deviceId >= WebCamTexture.devices.Length) - { - MelonLogger.Warning($"[CustomCameraId] Ignoring custom camera {configEntry}: camera ID {deviceId} out of range"); - continue; - } - - if (!Enum.TryParse(cameraTypeName, out var cameraType)) - { - MelonLogger.Warning($"[CustomCameraId] Ignoring custom camera {configEntry}: camera type {cameraTypeName} not present"); - continue; - } - - if (textureCache[deviceId] != null) - { - webCamTextures[cameraType] = textureCache[deviceId]; - } - else - { - var webCamTexture = new WebCamTexture(WebCamTexture.devices[deviceId].name); - webCamTextures[cameraType] = webCamTexture; - textureCache[deviceId] = webCamTexture; - } - } - - int textureCount = webCamTextures.Count; - __instance.isAvailableCamera = new bool[textureCount]; - __instance.cameraProcMode = new CameraManager.CameraProcEnum[textureCount]; - - int textureIndex = 0; - foreach (var (cameraType, webCamTexture) in webCamTextures) - { - __instance.isAvailableCamera[textureIndex] = true; - __instance.cameraProcMode[textureIndex] = CameraManager.CameraProcEnum.Good; - CameraManager.DeviceId[(int)cameraType] = textureIndex; - textureIndex++; - } - Traverse.Create(__instance).Field("_webcamtex").SetValue(webCamTextures.Values.ToArray()); - - CameraManager.IsReady = true; - yield break; - } - - public static void OnBeforePatch() - { - if (!printCameraList) - { - return; - } - - WebCamDevice[] devices = WebCamTexture.devices; - string cameraList = "Connected Web Cameras:\n"; - for (int i = 0; i < devices.Length; i++) - { - WebCamDevice webCamDevice = devices[i]; - WebCamTexture webCamTexture = new WebCamTexture(webCamDevice.name); - webCamTexture.Play(); - cameraList += "==================================================\n"; - cameraList += "Name: " + webCamDevice.name + "\n"; - cameraList += $"ID: {i}\n"; - cameraList += $"Resolution: {webCamTexture.width} * {webCamTexture.height}\n"; - cameraList += $"FPS: {webCamTexture.requestedFPS}\n"; - webCamTexture.Stop(); - } - cameraList += "=================================================="; - - foreach (var line in cameraList.Split('\n')) - { - MelonLogger.Msg($"[CustomCameraId] {line}"); - } - } -} diff --git a/AquaMai/AquaMai.Mods/GameSystem/DisableTimeout.cs b/AquaMai/AquaMai.Mods/GameSystem/DisableTimeout.cs deleted file mode 100644 index d3a21038..00000000 --- a/AquaMai/AquaMai.Mods/GameSystem/DisableTimeout.cs +++ /dev/null @@ -1,65 +0,0 @@ -using AquaMai.Config.Attributes; -using HarmonyLib; -using Manager; -using Monitor; -using Process; -using Process.Entry.State; -using Process.ModeSelect; - -namespace AquaMai.Mods.GameSystem; - -[ConfigSection( - en: """ - Disable timers (hidden and set to 65535 seconds). - Not recommand to enable when SinglePlayer is off. - """, - zh: """ - 去除并隐藏游戏中的倒计时 - 没有开启单人模式时,不建议启用 - """)] -public class DisableTimeout -{ - [HarmonyPrefix] - [HarmonyPatch(typeof(TimerController), "PrepareTimer")] - public static void PrePrepareTimer(ref int second) - { - second = 65535; - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(CommonTimer), "SetVisible")] - public static void CommonTimerSetVisible(ref bool isVisible) - { - isVisible = false; - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(EntryProcess), "DecrementTimerSecond")] - public static bool EntryProcessDecrementTimerSecond(ContextEntry ____context) - { - SoundManager.PlaySE(Mai2.Mai2Cue.Cue.SE_SYS_SKIP, 0); - ____context.SetState(StateType.DoneEntry); - return false; - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(ModeSelectProcess), "UpdateInput")] - public static bool ModeSelectProcessUpdateInput(ModeSelectProcess __instance) - { - if (!InputManager.GetButtonDown(0, InputManager.ButtonSetting.Button05)) return true; - __instance.TimeSkipButtonAnim(InputManager.ButtonSetting.Button05); - SoundManager.PlaySE(Mai2.Mai2Cue.Cue.SE_SYS_SKIP, 0); - Traverse.Create(__instance).Method("TimeUp").GetValue(); - return false; - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(PhotoEditProcess), "MainMenuUpdate")] - public static void PhotoEditProcess(PhotoEditMonitor[] ____monitors, PhotoEditProcess __instance) - { - if (!InputManager.GetButtonDown(0, InputManager.ButtonSetting.Button04)) return; - SoundManager.PlaySE(Mai2.Mai2Cue.Cue.SE_SYS_SKIP, 0); - ____monitors[0].SetButtonPressed(InputManager.ButtonSetting.Button04); - Traverse.Create(__instance).Method("OnTimeUp").GetValue(); - } -} diff --git a/AquaMai/AquaMai.Mods/GameSystem/KeyMap.cs b/AquaMai/AquaMai.Mods/GameSystem/KeyMap.cs deleted file mode 100644 index 49971604..00000000 --- a/AquaMai/AquaMai.Mods/GameSystem/KeyMap.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System.Reflection; -using AquaMai.Config.Attributes; -using AquaMai.Config.Types; -using HarmonyLib; - -namespace AquaMai.Mods.GameSystem; - -[ConfigSection( - en: "These settings will work regardless of whether you have enabled segatools' io4 emulation.", - zh: "这里的设置无论你是否启用了 segatools 的 io4 模拟都会工作")] -public class KeyMap -{ - [ConfigEntry] - public static readonly KeyCodeID Test = (KeyCodeID)115; - - [ConfigEntry] - private static readonly KeyCodeID Service = (KeyCodeID)5; - - [ConfigEntry] - private static readonly KeyCodeID Button1_1P = (KeyCodeID)67; - - [ConfigEntry] - private static readonly KeyCodeID Button2_1P = (KeyCodeID)49; - - [ConfigEntry] - private static readonly KeyCodeID Button3_1P = (KeyCodeID)48; - - [ConfigEntry] - private static readonly KeyCodeID Button4_1P = (KeyCodeID)47; - - [ConfigEntry] - private static readonly KeyCodeID Button5_1P = (KeyCodeID)68; - - [ConfigEntry] - private static readonly KeyCodeID Button6_1P = (KeyCodeID)70; - - [ConfigEntry] - private static readonly KeyCodeID Button7_1P = (KeyCodeID)45; - - [ConfigEntry] - private static readonly KeyCodeID Button8_1P = (KeyCodeID)61; - - [ConfigEntry] - private static readonly KeyCodeID Select_1P = (KeyCodeID)25; - - [ConfigEntry] - private static readonly KeyCodeID Button1_2P = (KeyCodeID)80; - - [ConfigEntry] - private static readonly KeyCodeID Button2_2P = (KeyCodeID)81; - - [ConfigEntry] - private static readonly KeyCodeID Button3_2P = (KeyCodeID)78; - - [ConfigEntry] - private static readonly KeyCodeID Button4_2P = (KeyCodeID)75; - - [ConfigEntry] - private static readonly KeyCodeID Button5_2P = (KeyCodeID)74; - - [ConfigEntry] - private static readonly KeyCodeID Button6_2P = (KeyCodeID)73; - - [ConfigEntry] - private static readonly KeyCodeID Button7_2P = (KeyCodeID)76; - - [ConfigEntry] - private static readonly KeyCodeID Button8_2P = (KeyCodeID)79; - - [ConfigEntry] - private static readonly KeyCodeID Select_2P = (KeyCodeID)84; - - [HarmonyPatch(typeof(DB.JvsButtonTableRecord), MethodType.Constructor, typeof(int), typeof(string), typeof(string), typeof(int), typeof(string), typeof(int), typeof(int), typeof(int))] - [HarmonyPostfix] - public static void JvsButtonTableRecordConstructor(DB.JvsButtonTableRecord __instance, string Name) - { - var prop = (DB.KeyCodeID)typeof(KeyMap).GetField(Name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static).GetValue(null); - __instance.SubstituteKey = prop; - } -} diff --git a/AquaMai/AquaMai.Mods/GameSystem/QuickRetry.cs b/AquaMai/AquaMai.Mods/GameSystem/QuickRetry.cs deleted file mode 100644 index e84f9f65..00000000 --- a/AquaMai/AquaMai.Mods/GameSystem/QuickRetry.cs +++ /dev/null @@ -1,34 +0,0 @@ -using AquaMai.Core.Attributes; -using AquaMai.Config.Attributes; -using HarmonyLib; -using Manager; - -namespace AquaMai.Mods.GameSystem; - -[ConfigSection( - en: "Hold the bottom four buttons (3456) for quick retry (like in Freedom Mode, default non-utage only).", - zh: "按住下方四个按钮(3456)快速重开本局游戏(像在 Freedom Mode 中一样,默认仅对非宴谱有效)")] -[EnableGameVersion(23000)] -public class QuickRetry -{ - [ConfigEntry( - en: "Force enable in Utage.", - zh: "在宴谱中强制启用")] - private static readonly bool enableInUtage = false; - - [HarmonyPrefix] - [HarmonyPatch(typeof(Monitor.QuickRetry), "IsQuickRetryEnable")] - public static bool OnQuickRetryIsQuickRetryEnable(ref bool __result) - { - if (enableInUtage) - { - __result = true; - } - else - { - var isUtageProperty = Traverse.Create(typeof(GameManager)).Property("IsUtage"); - __result = !isUtageProperty.PropertyExists() || !isUtageProperty.GetValue(); - } - return false; - } -} diff --git a/AquaMai/AquaMai.Mods/GameSystem/README.md b/AquaMai/AquaMai.Mods/GameSystem/README.md deleted file mode 100644 index e9b0be73..00000000 --- a/AquaMai/AquaMai.Mods/GameSystem/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# GameSystem - -Patches changing the way the game running / behaving which are not possible in the stock game. See also the [GameSettings README](../GameSettings/README.md) for differences. - -Game asset related patches should go to the Assets subcategory (or the Fancy category if they're too fancy). diff --git a/AquaMai/AquaMai.Mods/GameSystem/RemoveEncryption.cs b/AquaMai/AquaMai.Mods/GameSystem/RemoveEncryption.cs deleted file mode 100644 index 9265afc2..00000000 --- a/AquaMai/AquaMai.Mods/GameSystem/RemoveEncryption.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using AquaMai.Config.Attributes; -using AquaMai.Core.Helpers; -using HarmonyLib; -using Net.Packet; - -namespace AquaMai.Mods.GameSystem; - -[ConfigSection( - en: """ - If you are using an unmodified client, requests to the server will be encrypted by default, but requests to the private server should not be encrypted. - With this option enabled, the connection will not be encrypted, and the suffix added by different versions of the client to the API names are also removed. - Please keep this option enabled normally. - """, - zh: """ - 如果你在用未经修改的客户端,会默认加密到服务器的连接,而连接私服的时候不应该加密 - 开了这个选项之后就不会加密连接了,同时也会移除不同版本的客户端可能会对 API 接口加的后缀 - 正常情况下,请保持这个选项开启 - """, - defaultOn: true)] -public class RemoveEncryption -{ - [HarmonyPrefix] - [HarmonyPatch(typeof(Packet), "Obfuscator", typeof(string))] - public static bool PreObfuscator(string srcStr, ref string __result) - { - __result = Shim.RemoveApiSuffix(srcStr); - return false; - } - - [HarmonyPatch] - public class EncryptDecrypt - { - public static IEnumerable TargetMethods() - { - var methods = AccessTools.TypeByName("Net.CipherAES").GetMethods(); - return - [ - methods.FirstOrDefault(it => it.Name == "Encrypt" && it.IsPublic), - methods.FirstOrDefault(it => it.Name == "Decrypt" && it.IsPublic) - ]; - } - - public static bool Prefix(object[] __args, ref object __result) - { - if (__args.Length == 1) - { - // public static byte[] Encrypt(byte[] data) - // public static byte[] Decrypt(byte[] encryptData) - __result = __args[0]; - } - else if (__args.Length == 2) - { - // public static bool Encrypt(byte[] data, out byte[] encryptData) - // public static bool Decrypt(byte[] encryptData, out byte[] plainData) - __args[1] = __args[0]; - __result = true; - } - return false; - } - } -} diff --git a/AquaMai/AquaMai.Mods/GameSystem/SinglePlayer.cs b/AquaMai/AquaMai.Mods/GameSystem/SinglePlayer.cs deleted file mode 100644 index be6c1edc..00000000 --- a/AquaMai/AquaMai.Mods/GameSystem/SinglePlayer.cs +++ /dev/null @@ -1,114 +0,0 @@ -using System.Collections.Generic; -using System.Reflection; -using AquaMai.Core; -using AquaMai.Core.Attributes; -using AquaMai.Config.Attributes; -using AquaMai.Mods.Fancy.GamePlay; -using HarmonyLib; -using MAI2.Util; -using Manager; -using MelonLoader; -using Monitor; -using Monitor.Common; -using Monitor.Entry; -using Monitor.Entry.Parts.Screens; -using UnityEngine; -using Fx; -using Type = System.Type; - -namespace AquaMai.Mods.GameSystem; - -// Hides the 2p (right hand side) UI. -// Note: this is not my original work. I simply interpreted the code and rewrote it as a mod. -[ConfigSection( - en: "Single player: Show 1P only, at the center of the screen.", - zh: "单人模式,不显示 2P")] -public class SinglePlayer -{ - [HarmonyPatch] - public class WhateverInitialize - { - public static IEnumerable TargetMethods() - { - var lateInitialize = AccessTools.Method(typeof(Main.GameMain), "LateInitialize", [typeof(MonoBehaviour), typeof(Transform), typeof(Transform)]); - if (lateInitialize is not null) return [lateInitialize]; - return [AccessTools.Method(typeof(Main.GameMain), "Initialize", [typeof(MonoBehaviour), typeof(Transform), typeof(Transform)])]; - } - - public static void Prefix(MonoBehaviour gameMainObject, ref Transform left, ref Transform right) - { - left.transform.position = Vector3.zero; - right.localScale = Vector3.zero; - GameObject.Find("Mask").transform.position = new Vector3(540f, 0f, 0f); - } - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(MeshButton), "IsPointInPolygon", new Type[] { typeof(Vector2[]), typeof(Vector2) })] - public static bool IsPointInPolygon(Vector2[] polygon, ref Vector2 point, MeshButton __instance, ref bool __result) - { - __result = RectTransformUtility.RectangleContainsScreenPoint(__instance.GetComponent(), point, Camera.main); - return false; - } - - [EnableGameVersion(21500, noWarn: true)] - public class SkipTimer - { - [HarmonyPostfix] - [HarmonyPatch(typeof(EntryMonitor), "DecideEntry")] - public static void PostDecideEntry(EntryMonitor __instance) - { -# if DEBUG - MelonLogger.Msg("Confirm Entry"); -# endif - TimeManager.MarkGameStartTime(); - Singleton.Instance.UpdateEvent(); - Singleton.Instance.UpdateData(); - __instance.Process.CreateDownloadProcess(); - __instance.ProcessManager.SendMessage(new Message(ProcessType.CommonProcess, 30001)); - __instance.ProcessManager.SendMessage(new Message(ProcessType.CommonProcess, 40000, 0, OperationInformationController.InformationType.Hide)); - __instance.Process.SetNextProcess(); - } - - // To prevent the "長押受付終了" overlay from appearing - [HarmonyPrefix] - [HarmonyPatch(typeof(WaitPartner), "Open")] - public static bool WaitPartnerPreOpen() - { - return false; - } - } - - [ConfigEntry( - en: "Fix hanabi effect under single-player mode (disabled automatically if HideHanabi is enabled).", - zh: "修复单人模式下的烟花效果(如果启用了 HideHanabi,则会自动禁用)")] - public static bool fixHanabi = true; - - private static bool fixHanabiDisableImplied = false; - private static bool FixHanabiEnabled => fixHanabi && !fixHanabiDisableImplied; - - [EnableIf(nameof(FixHanabiEnabled))] - [HarmonyPatch(typeof(TapCEffect), "SetUpParticle")] - [HarmonyPostfix] - public static void PostSetUpParticle(TapCEffect __instance, FX_Mai2_Note_Color ____particleControler) - { - var entities = ____particleControler.GetComponentsInChildren(true); - foreach (var entity in entities) - { - entity.maxParticleSize = 1f; - } - } - - public static void OnBeforePatch() - { - if (ConfigLoader.Config.GetSectionState(typeof(HideHanabi)).Enabled) - { - fixHanabiDisableImplied = true; - } - } - - public static void OnAfterPatch() - { - Core.Helpers.GuiSizes.SinglePlayer = true; - } -} diff --git a/AquaMai/AquaMai.Mods/GameSystem/TestProof.cs b/AquaMai/AquaMai.Mods/GameSystem/TestProof.cs deleted file mode 100644 index 387955c4..00000000 --- a/AquaMai/AquaMai.Mods/GameSystem/TestProof.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System.Diagnostics; -using System.Linq; -using AquaMai.Config.Attributes; -using AquaMai.Config.Types; -using AquaMai.Core; -using AquaMai.Core.Attributes; -using AquaMai.Core.Helpers; -using AquaMai.Mods.Tweaks; -using AquaMai.Mods.UX; -using AquaMai.Mods.UX.PracticeMode; -using HarmonyLib; -using Manager; - -namespace AquaMai.Mods.GameSystem; - -[ConfigSection( - en: """ - When enabled, test button must be long pressed to enter game test mode. - When test button is bound to other features, this option is enabled automatically. - """, - zh: """ - 启用后,测试键必须长按才能进入游戏测试模式 - 当测试键被绑定到其它功能时,此选项自动开启 - """)] -[EnableImplicitlyIf(nameof(ShouldEnableImplicitly))] -public class TestProof -{ - public static bool ShouldEnableImplicitly - { - get - { - (System.Type section, KeyCodeOrName key)[] featureKeys = - [ - (typeof(OneKeyEntryEnd), OneKeyEntryEnd.key), - (typeof(OneKeyRetrySkip), OneKeyRetrySkip.retryKey), - (typeof(OneKeyRetrySkip), OneKeyRetrySkip.skipKey), - (typeof(HideSelfMadeCharts), HideSelfMadeCharts.key), - (typeof(PracticeMode), PracticeMode.key), - (typeof(ResetTouch), ResetTouch.key), - ]; - var keyMapEnabled = ConfigLoader.Config.GetSectionState(typeof(KeyMap)).Enabled; - return featureKeys.Any(it => - // The feature is enabled and... - ConfigLoader.Config.GetSectionState(it.section).Enabled && - ( - // and the key is test, or... - it.key == KeyCodeOrName.Test || - // or the key have been mapped to the same key as test. - (keyMapEnabled && it.key.ToString() == KeyMap.Test.ToString()))); - } - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(InputManager), "GetSystemInputDown")] - public static bool GetSystemInputDown(ref bool __result, InputManager.SystemButtonSetting button, bool[] ___SystemButtonDown) - { - __result = ___SystemButtonDown[(int)button]; - if (button != InputManager.SystemButtonSetting.ButtonTest) - return false; - - var stackTrace = new StackTrace(); // get call stack - var stackFrames = stackTrace.GetFrames(); // get method calls (frames) - - if (stackFrames.Any(it => it.GetMethod().Name == "DMD")) - { - __result = KeyListener.GetKeyDownOrLongPress(KeyCodeOrName.Test, true); - } - - return false; - } -} diff --git a/AquaMai/AquaMai.Mods/GameSystem/TouchPanelBaudRate.cs b/AquaMai/AquaMai.Mods/GameSystem/TouchPanelBaudRate.cs deleted file mode 100644 index e9294249..00000000 --- a/AquaMai/AquaMai.Mods/GameSystem/TouchPanelBaudRate.cs +++ /dev/null @@ -1,30 +0,0 @@ -using AquaMai.Config.Attributes; -using HarmonyLib; -using IO; - -namespace AquaMai.Mods.GameSystem; - -[ConfigSection( - en: """ - Adjust the baud rate of the touch screen serial port, default value is 9600. - Requires hardware support. If you are unsure, don't use it. - """, - zh: """ - 调整触摸屏串口波特率,默认值 9600 - 需要硬件配合,如果你不清楚你是否可以使用,请不要使用 - """)] -public class TouchPanelBaudRate -{ - [ConfigEntry( - en: "Baud rate.", - zh: "波特率")] - private static readonly int baudRate = 9600; - - [HarmonyPatch(typeof(NewTouchPanel), "Open")] - [HarmonyPrefix] - private static void OpenPrefix(ref int ___BaudRate) - { - if (baudRate <= 0) return; - ___BaudRate = baudRate; - } -} diff --git a/AquaMai/AquaMai.Mods/GameSystem/TouchToButtonInput.cs b/AquaMai/AquaMai.Mods/GameSystem/TouchToButtonInput.cs deleted file mode 100644 index ed8ef966..00000000 --- a/AquaMai/AquaMai.Mods/GameSystem/TouchToButtonInput.cs +++ /dev/null @@ -1,59 +0,0 @@ -using AquaMai.Config.Attributes; -using HarmonyLib; -using Process; -using static Manager.InputManager; - -namespace AquaMai.Mods.GameSystem; - -[ConfigSection( - en: "Map touch actions to buttons.", - zh: "映射触摸操作至实体按键")] -public class TouchToButtonInput -{ - private static bool _isPlaying = false; - - [HarmonyPostfix] - [HarmonyPatch(typeof(GameProcess), "OnStart")] - public static void OnGameProcessStart(GameProcess __instance) - { - _isPlaying = true; - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(GameProcess), "OnRelease")] - public static void OnGameProcessRelease(GameProcess __instance) - { - _isPlaying = false; - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(Manager.InputManager), "GetButtonDown")] - public static void GetButtonDown(ref bool __result, int monitorId, ButtonSetting button) - { - if (_isPlaying || __result) return; - if (button.ToString().StartsWith("Button")) - { - __result = GetTouchPanelAreaDown(monitorId, (TouchPanelArea)button); - } - else if (button.ToString().Equals("Select")) - { - __result = GetTouchPanelAreaLongPush(monitorId, TouchPanelArea.C1, 500L) || GetTouchPanelAreaLongPush(monitorId, TouchPanelArea.C2, 500L); - } - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(Manager.InputManager), "GetButtonPush")] - public static void GetButtonPush(ref bool __result, int monitorId, ButtonSetting button) - { - if (_isPlaying || __result) return; - if (button.ToString().StartsWith("Button")) __result = GetTouchPanelAreaPush(monitorId, (TouchPanelArea)button); - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(Manager.InputManager), "GetButtonLongPush")] - public static void GetButtonLongPush(ref bool __result, int monitorId, ButtonSetting button, long msec) - { - if (_isPlaying || __result) return; - if (button.ToString().StartsWith("Button")) __result = GetTouchPanelAreaLongPush(monitorId, (TouchPanelArea)button, msec); - } -} diff --git a/AquaMai/AquaMai.Mods/GameSystem/Unlock.cs b/AquaMai/AquaMai.Mods/GameSystem/Unlock.cs deleted file mode 100644 index 0e867813..00000000 --- a/AquaMai/AquaMai.Mods/GameSystem/Unlock.cs +++ /dev/null @@ -1,90 +0,0 @@ -using AquaMai.Config.Attributes; -using AquaMai.Core.Attributes; -using MAI2System; -using Manager; -using Manager.MaiStudio; -using HarmonyLib; - -namespace AquaMai.Mods.GameSystem; - -[ConfigSection( - en: "Unlock normally locked (including normally non-unlockable) game content.", - zh: "解锁原本锁定(包括正常途径无法解锁)的游戏内容")] -public class Unlock -{ - [ConfigEntry( - en: "Unlock maps that are not in this version.", - zh: "解锁游戏里所有的区域,包括非当前版本的(并不会帮你跑完)")] - private static readonly bool maps = true; - - [EnableIf(nameof(maps))] - [HarmonyPrefix] - [HarmonyPatch(typeof(MapData), "get_OpenEventId")] - public static bool get_OpenEventId(ref StringID __result) - { - // For any map, return the event ID 1 to unlock it - var id = new Manager.MaiStudio.Serialize.StringID - { - id = 1, - str = "無期限常時解放" - }; - - var sid = new StringID(); - sid.Init(id); - - __result = sid; - return false; - } - - [ConfigEntry( - en: "Unlock normally event-only tickets.", - zh: "解锁游戏里所有可能的跑图券")] - private static readonly bool tickets = true; - - [EnableIf(nameof(tickets))] - [HarmonyPrefix] - [HarmonyPatch(typeof(TicketData), "get_ticketEvent")] - public static bool get_ticketEvent(ref StringID __result) - { - // For any ticket, return the event ID 1 to unlock it - var id = new Manager.MaiStudio.Serialize.StringID - { - id = 1, - str = "無期限常時解放" - }; - - var sid = new StringID(); - sid.Init(id); - - __result = sid; - return false; - } - - [EnableIf(nameof(tickets))] - [HarmonyPrefix] - [HarmonyPatch(typeof(TicketData), "get_maxCount")] - public static bool get_maxCount(ref int __result) - { - // Modify the maxTicketNum to 0 - // this is because TicketManager.GetTicketData adds the ticket to the list if either - // the player owns at least one ticket or the maxTicketNum = 0 - __result = 0; - return false; - } - - [ConfigEntry( - en: "Unlock Utage without the need of DXRating 10000.", - zh: "不需要万分也可以进宴会场")] - private static readonly bool utage = true; - - [EnableIf(nameof(utage))] - [EnableGameVersion(24000)] - [HarmonyPrefix] - [HarmonyPatch(typeof(GameManager), "CanUnlockUtageTotalJudgement")] - public static bool CanUnlockUtageTotalJudgement(out ConstParameter.ResultOfUnlockUtageJudgement result1P, out ConstParameter.ResultOfUnlockUtageJudgement result2P) - { - result1P = ConstParameter.ResultOfUnlockUtageJudgement.Unlocked; - result2P = ConstParameter.ResultOfUnlockUtageJudgement.Unlocked; - return false; - } -} diff --git a/AquaMai/AquaMai.Mods/GameSystem/Window.cs b/AquaMai/AquaMai.Mods/GameSystem/Window.cs deleted file mode 100644 index e203f45b..00000000 --- a/AquaMai/AquaMai.Mods/GameSystem/Window.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using System.Threading.Tasks; -using AquaMai.Config.Attributes; -using UnityEngine; - -namespace AquaMai.Mods.GameSystem; - -[ConfigSection( - en: "Windowed Mode / Window Settings.", - zh: "窗口化/窗口设置")] -public class Window -{ - [ConfigEntry( - en: "Window the game.", - zh: "窗口化游戏")] - private static readonly bool windowed = false; - - [ConfigEntry( - en: """ - Window width (and height) for windowed mode, rendering resolution for fullscreen mode. - If set to 0, windowed mode will remember the user-set size, fullscreen mode will use the current display resolution. - """, - zh: """ - 宽度(和高度)窗口化时为游戏窗口大小,全屏时为渲染分辨率 - 如果设为 0,窗口化将记住用户设定的大小,全屏时将使用当前显示器分辨率 - """)] - private static readonly int width = 0; - - [ConfigEntry( - en: "Height, as above.", - zh: "高度,同上")] - private static readonly int height = 0; - - private const int GWL_STYLE = -16; - private const int WS_WHATEVER = 0x14CF0000; - - private static IntPtr hwnd = IntPtr.Zero; - - public static void OnBeforePatch() - { - if (windowed) - { - var alreadyWindowed = Screen.fullScreenMode == FullScreenMode.Windowed; - if (width == 0 || height == 0) - { - Screen.fullScreenMode = FullScreenMode.Windowed; - } - else - { - alreadyWindowed = false; - Screen.SetResolution(width, height, FullScreenMode.Windowed); - } - - hwnd = GetWindowHandle(); - if (alreadyWindowed) - { - SetResizeable(); - } - else - { - Task.Run(async () => - { - await Task.Delay(3000); - // Screen.SetResolution has delay - SetResizeable(); - }); - } - } - else - { - var width = Window.width == 0 ? Display.main.systemWidth : Window.width; - var height = Window.height == 0 ? Display.main.systemHeight : Window.height; - Screen.SetResolution(width, height, FullScreenMode.FullScreenWindow); - } - } - - public static void SetResizeable() - { - if (hwnd == IntPtr.Zero) return; - SetWindowLongPtr(hwnd, GWL_STYLE, WS_WHATEVER); - } - - private delegate bool EnumThreadDelegate(IntPtr hwnd, IntPtr lParam); - - [DllImport("user32.dll")] - static extern bool EnumThreadWindows(int dwThreadId, EnumThreadDelegate lpfn, IntPtr lParam); - - [DllImport("Kernel32.dll")] - static extern int GetCurrentThreadId(); - - static IntPtr GetWindowHandle() - { - IntPtr returnHwnd = IntPtr.Zero; - var threadId = GetCurrentThreadId(); - EnumThreadWindows(threadId, - (hWnd, lParam) => - { - if (returnHwnd == IntPtr.Zero) returnHwnd = hWnd; - return true; - }, IntPtr.Zero); - return returnHwnd; - } - - [DllImport("user32.dll")] - static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, int dwNewLong); -} diff --git a/AquaMai/AquaMai.Mods/General.cs b/AquaMai/AquaMai.Mods/General.cs deleted file mode 100644 index 75c44b59..00000000 --- a/AquaMai/AquaMai.Mods/General.cs +++ /dev/null @@ -1,42 +0,0 @@ -using AquaMai.Config.Attributes; - -namespace AquaMai.Mods; - -// This class is for settings only. Don't patch anything here. - -[ConfigSection( - en: "AquaMai's general settings.", - zh: "AquaMai 的通用设置", - alwaysEnabled: true)] -public class General -{ - [ConfigEntry( - en: """ - Language for mod UI (en and zh supported). - If empty, the system language will be used. - The config file will also be saved in this language. - """, - zh: """ - Mod 界面的语言,支持 en 和 zh - 如果为空,将使用系统语言 - 配置文件也将以此语言保存 - """, - specialConfigEntry: SpecialConfigEntry.Locale)] - public static readonly string locale = ""; -} - -// Please add/remove corresponding entries in SectionNameOrder enum when adding/removing sections. -public enum SectionNameOrder -{ - DeprecationWarning, - General, - Fix, - GameSystem_Assets, - GameSystem, - GameSettings, - Tweaks, - Tweaks_TimeSaving, - UX, - Utils, - Fancy -} diff --git a/AquaMai/AquaMai.Mods/Tweaks/IgnoreAimeServerError.cs b/AquaMai/AquaMai.Mods/Tweaks/IgnoreAimeServerError.cs deleted file mode 100644 index 99e4fcb3..00000000 --- a/AquaMai/AquaMai.Mods/Tweaks/IgnoreAimeServerError.cs +++ /dev/null @@ -1,19 +0,0 @@ -using AquaMai.Config.Attributes; -using HarmonyLib; -using Manager; - -namespace AquaMai.Mods.Tweaks; - -[ConfigSection( - en: "Prevent gray network caused by mistakenly thinking it's an AimeDB server issue.", - zh: "防止因错误认为 AimeDB 服务器问题引起的灰网,建议开启")] -public class IgnoreAimeServerError -{ - [HarmonyPatch(typeof(OperationManager), "IsAliveAimeServer", MethodType.Getter)] - [HarmonyPrefix] - public static bool Prefix(ref bool __result) - { - __result = true; - return false; - } -} diff --git a/AquaMai/AquaMai.Mods/Tweaks/LockFrameRate.cs b/AquaMai/AquaMai.Mods/Tweaks/LockFrameRate.cs deleted file mode 100644 index f80eabbb..00000000 --- a/AquaMai/AquaMai.Mods/Tweaks/LockFrameRate.cs +++ /dev/null @@ -1,22 +0,0 @@ -using AquaMai.Config.Attributes; -using UnityEngine; - -namespace AquaMai.Mods.Tweaks; - -[ConfigSection( - en: """ - Force the frame rate limit to 60 FPS and disable vSync. - Do not use if your game has no issues. - """, - zh: """ - 强制设置帧率上限为 60 帧并关闭垂直同步 - 如果你的游戏没有问题,请不要使用 - """)] -public class LockFrameRate -{ - public static void OnBeforePatch() - { - Application.targetFrameRate = 60; - QualitySettings.vSyncCount = 0; - } -} diff --git a/AquaMai/AquaMai.Mods/Tweaks/README.md b/AquaMai/AquaMai.Mods/Tweaks/README.md deleted file mode 100644 index 64233662..00000000 --- a/AquaMai/AquaMai.Mods/Tweaks/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Tweaks - -Patches to make the game more stable, more robust and less annoying. The game is playable at all without them, but sometimes they help a lot. - -These patches don't change the way the game behaving, otherwise they may go to the GameSystem category. diff --git a/AquaMai/AquaMai.Mods/Tweaks/ResetTouch.cs b/AquaMai/AquaMai.Mods/Tweaks/ResetTouch.cs deleted file mode 100644 index 64c79d32..00000000 --- a/AquaMai/AquaMai.Mods/Tweaks/ResetTouch.cs +++ /dev/null @@ -1,43 +0,0 @@ -using AquaMai.Config.Attributes; -using AquaMai.Config.Types; -using AquaMai.Core.Helpers; -using AquaMai.Core.Resources; -using HarmonyLib; -using MAI2.Util; -using Main; -using Manager; -using Process; - -namespace AquaMai.Mods.Tweaks; - -[ConfigSection( - en: "Reset touch panel manually or after playing track.", - zh: "重置触摸面板")] -public class ResetTouch -{ - [ConfigEntry(en: "Reset touch panel after playing track.", zh: "玩完一首歌自动重置")] - private static bool afterTrack = false; - - [ConfigEntry(en: "Reset manually.", zh: "按键重置")] - public static readonly KeyCodeOrName key = KeyCodeOrName.None; - - [ConfigEntry] private static readonly bool longPress = false; - - [HarmonyPostfix] - [HarmonyPatch(typeof(ResultProcess), "OnStart")] - public static void ResultProcessOnStart() - { - if (!afterTrack) return; - SingletonStateMachine.Instance.StartTouchPanel(); - MelonLoader.MelonLogger.Msg("[TouchResetAfterTrack] Touch panel reset"); - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(GameMainObject), "Update")] - public static void OnGameMainObjectUpdate() - { - if (!KeyListener.GetKeyDownOrLongPress(key, longPress)) return; - SingletonStateMachine.Instance.StartTouchPanel(); - MessageHelper.ShowMessage(Locale.TouchPanelReset); - } -} \ No newline at end of file diff --git a/AquaMai/AquaMai.Mods/Tweaks/SkipUserVersionCheck.cs b/AquaMai/AquaMai.Mods/Tweaks/SkipUserVersionCheck.cs deleted file mode 100644 index 54a7d04e..00000000 --- a/AquaMai/AquaMai.Mods/Tweaks/SkipUserVersionCheck.cs +++ /dev/null @@ -1,22 +0,0 @@ -using AquaMai.Config.Attributes; -using HarmonyLib; -using Process.Entry.State; - -namespace AquaMai.Mods.Tweaks; - -[ConfigSection( - en: "Allow login with higher data version.", - zh: """ - 原先如果你的账号版本比当前游戏设定的版本高的话,就会不能登录 - 开了这个选项之后就可以登录了,不过你的账号版本还是会被设定为当前游戏的版本 - """)] -public class SkipUserVersionCheck -{ - [HarmonyPrefix] - [HarmonyPatch(typeof(ConfirmPlay), "IsValidVersion")] - public static bool IsValidVersion(ref bool __result) - { - __result = true; - return false; - } -} diff --git a/AquaMai/AquaMai.Mods/Tweaks/TimeSaving/EntryToMusicSelection.cs b/AquaMai/AquaMai.Mods/Tweaks/TimeSaving/EntryToMusicSelection.cs deleted file mode 100644 index 4b00be95..00000000 --- a/AquaMai/AquaMai.Mods/Tweaks/TimeSaving/EntryToMusicSelection.cs +++ /dev/null @@ -1,52 +0,0 @@ -using AquaMai.Config.Attributes; -using DB; -using HarmonyLib; -using MAI2.Util; -using Manager; -using Monitor; -using Process; -using Process.Information; - -namespace AquaMai.Mods.Tweaks.TimeSaving; - -[ConfigSection( - en: "Directly enter the song selection screen after login.", - zh: "登录完成后直接进入选歌界面")] -public class EntryToMusicSelection -{ - /* - * Highly experimental, may well break some stuff - * Works by overriding the info screen (where it shows new events and stuff) - * to directly exit to the music selection screen, skipping character and - * event selection, among others - */ - [HarmonyPrefix] - [HarmonyPatch(typeof(InformationProcess), "OnUpdate")] - public static bool OnUpdate(InformationProcess __instance, ProcessDataContainer ___container) - { - GameManager.SetMaxTrack(); - // Set headphone volume - for (var i = 0; i < 2; i++) - { - var userData = UserDataManager.Instance.GetUserData(i); - if (userData.IsEntry) - { - OptionHeadphonevolumeID headPhoneVolume = userData.Option.HeadPhoneVolume; - SoundManager.SetHeadPhoneVolume(i, headPhoneVolume.GetValue()); - } - } - ___container.processManager.AddProcess(new MusicSelectProcess(___container)); - ___container.processManager.ReleaseProcess(__instance); - return false; - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(MapResultMonitor), "Initialize")] - public static void MapResultMonitorPreInitialize(int monIndex) - { - var userData = Singleton.Instance.GetUserData(monIndex); - var index = userData.MapList.FindIndex((m) => m.ID == userData.Detail.SelectMapID); - if (index >= 0) return; - userData.MapList.Clear(); - } -} diff --git a/AquaMai/AquaMai.Mods/Tweaks/TimeSaving/IWontTapOrSlideVigorously.cs b/AquaMai/AquaMai.Mods/Tweaks/TimeSaving/IWontTapOrSlideVigorously.cs deleted file mode 100644 index 8ec9f44c..00000000 --- a/AquaMai/AquaMai.Mods/Tweaks/TimeSaving/IWontTapOrSlideVigorously.cs +++ /dev/null @@ -1,19 +0,0 @@ -using AquaMai.Config.Attributes; -using HarmonyLib; -using Monitor; - -namespace AquaMai.Mods.Tweaks.TimeSaving; - -[ConfigSection( - en: "Skip the \"Do not tap or slide vigorously\" screen, immediately proceed to the next screen once data is loaded.", - zh: "跳过“不要大力拍打或滑动哦”这个界面,数据一旦加载完就立马进入下一个界面")] -public class IWontTapOrSlideVigorously -{ - [HarmonyPrefix] - [HarmonyPatch(typeof(PlInformationMonitor), "IsPlayPlInfoEnd")] - public static bool Patch(ref bool __result) - { - __result = true; - return false; - } -} diff --git a/AquaMai/AquaMai.Mods/Tweaks/TimeSaving/SkipEventInfo.cs b/AquaMai/AquaMai.Mods/Tweaks/TimeSaving/SkipEventInfo.cs deleted file mode 100644 index 70400ed0..00000000 --- a/AquaMai/AquaMai.Mods/Tweaks/TimeSaving/SkipEventInfo.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Collections.Generic; -using AquaMai.Config.Attributes; -using HarmonyLib; -using Process; -using Process.Information; - -namespace AquaMai.Mods.Tweaks.TimeSaving; - -[ConfigSection( - en: "Skip possible prompts like \"New area discovered\", \"New songs added\", \"There are events\" during game login/registration.", - zh: "跳过登录 / 注册游戏时候可能的 “发现了新的区域哟” “乐曲增加” “有活动哟” 之类的提示")] -public class SkipEventInfo -{ - [HarmonyPostfix] - [HarmonyPatch(typeof(InformationProcess), "OnStart")] - public static void InformationProcessPostStart(ref uint ____state) - { - ____state = 3; - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(RegionalSelectProcess), "OnStart")] - public static void RegionalSelectProcessPreStart(ref Queue[] ____discoverList) - { - ____discoverList = new Queue[] { new Queue(), new Queue() }; - } -} diff --git a/AquaMai/AquaMai.Mods/Tweaks/TimeSaving/SkipGoodbyeScreen.cs b/AquaMai/AquaMai.Mods/Tweaks/TimeSaving/SkipGoodbyeScreen.cs deleted file mode 100644 index e6597d61..00000000 --- a/AquaMai/AquaMai.Mods/Tweaks/TimeSaving/SkipGoodbyeScreen.cs +++ /dev/null @@ -1,30 +0,0 @@ -using AquaMai.Config.Attributes; -using HarmonyLib; -using Monitor; -using Process; - -namespace AquaMai.Mods.Tweaks.TimeSaving; - -[ConfigSection( - en: "Skip the \"Goodbye\" screen at the end of the game.", - zh: "跳过游戏结束的「再见」界面")] -public class SkipGoodbyeScreen -{ - [HarmonyPrefix] - [HarmonyPatch(typeof(GameOverMonitor), "IsPlayEnd")] - public static bool GameOverMonitorPlayEnd(ref bool __result) - { - __result = true; - return false; - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(GameOverProcess), "OnUpdate")] - public static void GameOverProcessOnUpdate(ref GameOverProcess.GameOverSequence ____state) - { - if (____state == GameOverProcess.GameOverSequence.SkyChange) - { - ____state = GameOverProcess.GameOverSequence.Disp; - } - } -} diff --git a/AquaMai/AquaMai.Mods/Tweaks/TimeSaving/SkipStartupDelays.cs b/AquaMai/AquaMai.Mods/Tweaks/TimeSaving/SkipStartupDelays.cs deleted file mode 100644 index 9517fc9b..00000000 --- a/AquaMai/AquaMai.Mods/Tweaks/TimeSaving/SkipStartupDelays.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Diagnostics; -using AquaMai.Config.Attributes; -using HarmonyLib; -using Process; - -namespace AquaMai.Mods.Tweaks.TimeSaving; - -[ConfigSection( - en: "Skip useless 2s delays to speed up the game boot process.", - zh: """ - 在自检界面,每个屏幕结束的时候都会等两秒才进入下一个屏幕,很浪费时间 - 开了这个选项之后就不会等了 - """)] -public class SkipStartupDelays -{ - [HarmonyPrefix] - [HarmonyPatch(typeof(PowerOnProcess), "OnStart")] - public static void PrePowerOnStart(ref float ____waitTime) - { - ____waitTime = 0f; - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(StartupProcess), "OnUpdate")] - public static void PreStartupUpdate(byte ____state, ref Stopwatch ___timer) - { - if (____state == 8) - { - Traverse.Create(___timer).Field("elapsed").SetValue(2 * 10000000L); - } - } -} diff --git a/AquaMai/AquaMai.Mods/Tweaks/TimeSaving/SkipStartupWarning.cs b/AquaMai/AquaMai.Mods/Tweaks/TimeSaving/SkipStartupWarning.cs deleted file mode 100644 index 5b816681..00000000 --- a/AquaMai/AquaMai.Mods/Tweaks/TimeSaving/SkipStartupWarning.cs +++ /dev/null @@ -1,31 +0,0 @@ -using AquaMai.Config.Attributes; -using HarmonyLib; -using Monitor; - -namespace AquaMai.Mods.Tweaks.TimeSaving; - -[ConfigSection( - en: "Skip SDEZ's warning screen and logo shown after the POST sequence.", - zh: "跳过 SDEZ 启动时的 WARNING 界面")] -public class SkipStartupWarning -{ - /* - * Patch PlayLogo to disable the warning screen - */ - [HarmonyPrefix] - [HarmonyPatch(typeof (WarningMonitor), "PlayLogo")] - public static bool PlayLogo() - { - // Return false to block the original method - return false; - } - - [HarmonyPrefix] - [HarmonyPatch(typeof (WarningMonitor), "IsLogoAnimationEnd")] - public static bool IsLogoAnimationEnd(ref bool __result) - { - // Always return true to indicate the animation has ended - __result = true; - return false; - } -} diff --git a/AquaMai/AquaMai.Mods/Tweaks/TimeSaving/SkipTrackStart.cs b/AquaMai/AquaMai.Mods/Tweaks/TimeSaving/SkipTrackStart.cs deleted file mode 100644 index 625cfe32..00000000 --- a/AquaMai/AquaMai.Mods/Tweaks/TimeSaving/SkipTrackStart.cs +++ /dev/null @@ -1,19 +0,0 @@ -using AquaMai.Config.Attributes; -using HarmonyLib; -using Monitor; - -namespace AquaMai.Mods.Tweaks.TimeSaving; - -[ConfigSection( - en: "Skip TrackStart screen.", - zh: "跳过乐曲开始界面")] -public class SkipTrackStart -{ - [HarmonyPrefix] - [HarmonyPatch(typeof (TrackStartMonitor), "IsEnd")] - public static bool IsEnd(ref bool __result) - { - __result = true; - return false; - } -} diff --git a/AquaMai/AquaMai.Mods/UX/CiBuildAlert.cs b/AquaMai/AquaMai.Mods/UX/CiBuildAlert.cs deleted file mode 100644 index 27caa92f..00000000 --- a/AquaMai/AquaMai.Mods/UX/CiBuildAlert.cs +++ /dev/null @@ -1,26 +0,0 @@ -using AquaMai.Config.Attributes; -using AquaMai.Core.Attributes; -using AquaMai.Core.Helpers; -using AquaMai.Core.Resources; -using HarmonyLib; -using Process; - -namespace AquaMai.Mods.UX; - -[ConfigSection(exampleHidden: true, defaultOn: true)] -[EnableIf(nameof(isCiBuild))] -public class CiBuildAlert -{ -# if CI - private static readonly bool isCiBuild = true; -# else - private static readonly bool isCiBuild = false; -# endif - - [HarmonyPatch(typeof(AdvertiseProcess), "OnStart")] - [HarmonyPostfix] - public static void OnStart(AdvertiseProcess __instance) - { - MessageHelper.ShowMessage(Locale.CiBuildAlertContent, title: Locale.CiBuildAlertTitle); - } -} diff --git a/AquaMai/AquaMai.Mods/UX/HideSelfMadeCharts.cs b/AquaMai/AquaMai.Mods/UX/HideSelfMadeCharts.cs deleted file mode 100644 index 6a244382..00000000 --- a/AquaMai/AquaMai.Mods/UX/HideSelfMadeCharts.cs +++ /dev/null @@ -1,136 +0,0 @@ -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using AquaMai.Config.Attributes; -using AquaMai.Config.Types; -using AquaMai.Core.Helpers; -using AquaMai.Core.Resources; -using HarmonyLib; -using MAI2.Util; -using Manager; -using MelonLoader; -using Process; -using Util; - -namespace AquaMai.Mods.UX; - -[ConfigSection( - en: "One key to hide all self-made charts in the music select process. Or hide for some users.", - zh: "在选曲界面一键隐藏所有自制谱,或对一部分用户进行隐藏")] -public class HideSelfMadeCharts -{ - [ConfigEntry( - en: "Key to toggle self-made charts.", - zh: "切换自制谱显示的按键")] - public static readonly KeyCodeOrName key = KeyCodeOrName.Test; - - [ConfigEntry] public static readonly bool longPress = false; - - [ConfigEntry( - en: "One user ID per line in the file. Hide self-made charts when these users login.", - zh: "该文件中每行一个用户 ID,当这些用户登录时隐藏自制谱")] - private static readonly string selfMadeChartsDenyUsersFile = "LocalAssets/SelfMadeChartsDenyUsers.txt"; - - [ConfigEntry( - en: "One user ID per line in the file. Only show self-made charts when these users login.", - zh: "该文件中每行一个用户 ID,只有这些用户登录时才显示自制谱")] - private static readonly string selfMadeChartsWhiteListUsersFile = "LocalAssets/SelfMadeChartsWhiteListUsers.txt"; - - private static Safe.ReadonlySortedDictionary _musics; - private static Safe.ReadonlySortedDictionary _musicsNoneSelfMade; - - private static bool isShowSelfMadeCharts = true; - private static bool isForceDisable; - - [HarmonyPostfix] - [HarmonyPatch(typeof(DataManager), "GetMusics")] - public static void GetMusics(ref Safe.ReadonlySortedDictionary __result, List ____targetDirs) - { - if (_musics is null) - { - // init musics for the first time - if (__result.Count == 0) return; - _musics = __result; - var nonSelfMadeList = new SortedDictionary(); - var officialDirs = ____targetDirs.Where(it => File.Exists(Path.Combine(it, "DataConfig.xml")) || File.Exists(Path.Combine(it, "OfficialChartsMark.txt"))); - foreach (var music in __result) - { - if (officialDirs.Any(it => MusicDirHelper.LookupPath(music.Value).StartsWith(it))) - { - nonSelfMadeList.Add(music.Key, music.Value); - } - } - - _musicsNoneSelfMade = new Safe.ReadonlySortedDictionary(nonSelfMadeList); - MelonLogger.Msg($"[HideSelfMadeCharts] All music count: {__result.Count}, Official music count: {_musicsNoneSelfMade.Count}"); - } - - var stackTrace = new StackTrace(); // get call stack - var stackFrames = stackTrace.GetFrames(); // get method calls (frames) - if (stackFrames.All(it => it.GetMethod().DeclaringType.Name != "MusicSelectProcess")) return; - if (isShowSelfMadeCharts && !isForceDisable) return; - __result = _musicsNoneSelfMade; - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(MusicSelectProcess), "OnUpdate")] - public static void MusicSelectProcessOnUpdate(ref MusicSelectProcess __instance) - { - if (isForceDisable) return; - if (!KeyListener.GetKeyDownOrLongPress(key, longPress)) return; - isShowSelfMadeCharts = !isShowSelfMadeCharts; - MelonLogger.Msg($"[HideSelfMadeCharts] isShowSelfMadeCharts: {isShowSelfMadeCharts}"); - SharedInstances.ProcessDataContainer.processManager.AddProcess(new FadeProcess(SharedInstances.ProcessDataContainer, __instance, new MusicSelectProcess(SharedInstances.ProcessDataContainer))); - Task.Run(async () => - { - await Task.Delay(1000); - MessageHelper.ShowMessage(isShowSelfMadeCharts ? Locale.SelfMadeChartsShow : Locale.SelfMadeChartsHide); - }); - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(MusicSelectProcess), "OnStart")] - public static void MusicSelectProcessOnStart(ref MusicSelectProcess __instance) - { - var denyPath = FileSystem.ResolvePath(selfMadeChartsDenyUsersFile); - if (File.Exists(denyPath)) - { - var userIds = File.ReadAllLines(denyPath); - for (var i = 0; i < 2; i++) - { - var user = Singleton.Instance.GetUserData(i); - if (!user.IsEntry) continue; - if (!userIds.Contains(user.Detail.UserID.ToString())) continue; - isForceDisable = true; - return; - } - } - - var whiteListPath = FileSystem.ResolvePath(selfMadeChartsWhiteListUsersFile); - if (File.Exists(whiteListPath)) - { - var userIds = File.ReadAllLines(whiteListPath); - for (var i = 0; i < 2; i++) - { - var user = Singleton.Instance.GetUserData(i); - if (!user.IsEntry) continue; - if (userIds.Contains(user.Detail.UserID.ToString())) continue; - isForceDisable = true; - return; - } - } - - isForceDisable = false; - } - - - [HarmonyPostfix] - [HarmonyPatch(typeof(EntryProcess), "OnStart")] - public static void EntryProcessOnStart(ref EntryProcess __instance) - { - // reset status on login - isShowSelfMadeCharts = true; - } -} \ No newline at end of file diff --git a/AquaMai/AquaMai.Mods/UX/ImmediateSave.cs b/AquaMai/AquaMai.Mods/UX/ImmediateSave.cs deleted file mode 100644 index c4b918b4..00000000 --- a/AquaMai/AquaMai.Mods/UX/ImmediateSave.cs +++ /dev/null @@ -1,241 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using AquaMai.Config.Attributes; -using AquaMai.Core.Helpers; -using AquaMai.Core.Resources; -using DB; -using HarmonyLib; -using MAI2.Util; -using MAI2System; -using Manager; -using Manager.MaiStudio; -using Manager.UserDatas; -using MelonLoader; -using Monitor.Entry.Parts.Screens; -using Net.Packet; -using Net.Packet.Helper; -using Process; -using Process.UserDataNet.State.UserDataULState; -using UnityEngine; - -namespace AquaMai.Mods.UX; - -[ConfigSection( - en: "Save immediate after playing a song.", - zh: "打完一首歌的时候立即向服务器保存成绩")] -public class ImmediateSave -{ - [HarmonyPrefix] - [HarmonyPatch(typeof(StateULUserAime), "RequestUploadUserPlayLogData")] - public static bool PreRequestUploadUserPlayLogData(StateULUserAime __instance) - { - Traverse.Create(__instance).Method("RequestUploadUserPortraitData").GetValue(); - return false; - } - - private static SavingUi ui; - - [HarmonyPostfix] - [HarmonyPatch(typeof(ResultProcess), "OnStart")] - public static void ResultProcessOnStart() - { - var doneCount = 0; - - void CheckSaveDone() - { - doneCount++; - if (doneCount != 4) return; - if (ui == null) return; - UnityEngine.Object.Destroy(ui); - ui = null; - } - - for (int i = 0; i < 2; i++) - { - var j = i; - var userData = Singleton.Instance.GetUserData(i); - if (!userData.IsEntry || userData.IsGuest()) - { - doneCount += 2; - continue; - } - - if (ui == null) - { - ui = SharedInstances.GameMainObject.gameObject.AddComponent(); - } - - SaveDataFix(userData); - PacketHelper.StartPacket(Shim.CreatePacketUploadUserPlaylog(i, userData, (int)GameManager.MusicTrackNumber - 1, - delegate - { - MelonLogger.Msg("[ImmediateSave] Playlog saved"); - CheckSaveDone(); - }, - delegate(PacketStatus err) - { - SoundManager.PlaySE(Mai2.Mai2Cue.Cue.SE_ENTRY_AIME_ERROR, j); - MelonLogger.Error("[ImmediateSave] Playlog save error"); - MelonLogger.Error(err); - MessageHelper.ShowMessage(Locale.PlaylogSaveError); - CheckSaveDone(); - })); - PacketHelper.StartPacket(Shim.CreatePacketUpsertUserAll(i, userData, delegate(int code) - { - if (code == 1) - { - MelonLogger.Msg("[ImmediateSave] UserAll saved"); - CheckSaveDone(); - } - else - { - SoundManager.PlaySE(Mai2.Mai2Cue.Cue.SE_ENTRY_AIME_ERROR, j); - MelonLogger.Error("[ImmediateSave] UserAll upsert error"); - MelonLogger.Error(code); - MessageHelper.ShowMessage(Locale.UserAllUpsertError); - CheckSaveDone(); - } - }, delegate(PacketStatus err) - { - SoundManager.PlaySE(Mai2.Mai2Cue.Cue.SE_ENTRY_AIME_ERROR, j); - MelonLogger.Error("[ImmediateSave] UserAll upsert error"); - MelonLogger.Error(err); - MessageHelper.ShowMessage(Locale.UserAllUpsertError); - CheckSaveDone(); - })); - } - } - - - private static void SaveDataFix(UserData userData) - { - UserDetail detail = userData.Detail; - detail.EventWatchedDate = TimeManager.GetDateString(TimeManager.PlayBaseTime); - userData.CalcTotalValue(); - float num = 0f; - try - { - if (userData.RatingList.RatingList.Any()) - { - num = userData.RatingList.RatingList.Last().SingleRate; - } - } - catch - { - } - - num = (float)Math.Ceiling((double)((num + 1f) / GameManager.TheoryRateBorderNum) * 10.0); - float num2 = 0f; - try - { - if (userData.RatingList.NextRatingList.Any()) - { - num2 = userData.RatingList.NewRatingList.Last().SingleRate; - } - } - catch - { - } - - num2 = (float)Math.Ceiling((double)((num2 + 1f) / GameManager.TheoryRateBorderNum) * 10.0); - string logDateString = TimeManager.GetLogDateString(TimeManager.PlayBaseTime); - string timeJp = (string.IsNullOrEmpty(userData.Detail.DailyBonusDate) ? TimeManager.GetLogDateString(0L) : userData.Detail.DailyBonusDate); - if (userData.IsEntry && userData.Detail.IsNetMember >= 2 && !GameManager.IsEventMode && TimeManager.GetUnixTime(logDateString) > TimeManager.GetUnixTime(timeJp) && Singleton.Instance.IsSingleUser() && !GameManager.IsFreedomMode && !GameManager.IsCourseMode && !DoneEntry.IsWeekdayBonus(userData)) - { - userData.Detail.DailyBonusDate = logDateString; - } - - List list = new List(); - List list2 = new List(); - IEnumerable[] scoreList = Shim.GetUserScoreList(userData); - List ratingList = userData.RatingList.RatingList; - List newRatingList = userData.RatingList.NewRatingList; - int achive = RatingTableID.Rate_22.GetAchive(); - for (int j = 0; j < scoreList.Length; j++) - { - if (scoreList[j] == null) - { - continue; - } - - foreach (UserScore item2 in scoreList[j]) - { - if (achive <= item2.achivement) - { - continue; - } - - MusicData music = Singleton.Instance.GetMusic(item2.id); - if (music == null) - { - continue; - } - - UserRate item = new UserRate(item2.id, j, item2.achivement, (uint)music.version); - if (item.OldFlag) - { - if (num <= (float)item.Level && !ratingList.Contains(item)) - { - list.Add(item); - } - } - else if (num2 <= (float)item.Level && !newRatingList.Contains(item)) - { - list2.Add(item); - } - } - } - - list.Sort(); - list.Reverse(); - if (list.Count > 10) - { - list.RemoveRange(10, list.Count - 10); - } - - userData.RatingList.NextRatingList = list; - list2.Sort(); - list2.Reverse(); - if (list2.Count > 10) - { - list2.RemoveRange(10, list2.Count - 10); - } - - userData.RatingList.NextNewRatingList = list2; - - userData.Detail.LastPlayCredit = 0; - userData.Detail.LastPlayMode = 0; - if (GameManager.IsFreedomMode) - { - userData.Detail.LastPlayMode = 1; - } - - if (GameManager.IsCourseMode) - { - userData.Detail.LastPlayMode = 2; - } - - userData.Detail.LastGameId = ConstParameter.GameIDStr; - userData.Detail.LastRomVersion = Singleton.Instance.config.romVersionInfo.versionNo.versionString; - userData.Detail.LastDataVersion = Singleton.Instance.config.dataVersionInfo.versionNo.versionString; - } - - private class SavingUi : MonoBehaviour - { - public void OnGUI() - { - var y = Screen.height * .075f; - var width = GuiSizes.FontSize * 20f; - var x = GuiSizes.PlayerCenter + GuiSizes.PlayerWidth / 2f - width; - var rect = new Rect(x, y, width, GuiSizes.LabelHeight * 2.5f); - - var labelStyle = GUI.skin.GetStyle("label"); - labelStyle.fontSize = (int)(GuiSizes.FontSize * 1.2); - labelStyle.alignment = TextAnchor.MiddleCenter; - - GUI.Box(rect, ""); - GUI.Label(rect, Locale.SavingDontExit); - } - } -} \ No newline at end of file diff --git a/AquaMai/AquaMai.Mods/UX/JudgeAccuracyInfo.cs b/AquaMai/AquaMai.Mods/UX/JudgeAccuracyInfo.cs deleted file mode 100644 index 439aa6da..00000000 --- a/AquaMai/AquaMai.Mods/UX/JudgeAccuracyInfo.cs +++ /dev/null @@ -1,251 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Reflection; -using AquaMai.Config.Attributes; -using HarmonyLib; -using MAI2.Util; -using Manager; -using Monitor; -using Monitor.Result; -using Process; -using TMPro; -using UnityEngine; -using Object = UnityEngine.Object; - -namespace AquaMai.Mods.UX; - -[ConfigSection( - zh: "在游戏总结的计分板中显示击打误差的详细信息(以帧为单位)", - en: "Show detailed accuracy info in the score board.")] -public class JudgeAccuracyInfo -{ - public class AccuracyEntryList - { - public List[] DiffList = new List[TableRowNames.Length]; - public List[] RawDiffList = new List[TableRowNames.Length]; - public HashSet NoteIndices = new(); - - public AccuracyEntryList() - { - for (int i = 0; i < TableRowNames.Length; i++) - { - DiffList[i] = new List(); - RawDiffList[i] = new List(); - } - } - } - - public static AccuracyEntryList[] EntryList = new AccuracyEntryList[2]; - - [HarmonyPostfix] - [HarmonyPatch(typeof(GameProcess), "OnStart")] - private static void OnGameProcessStartFinish() - { - for (int i = 0; i < EntryList.Length; i++) - { - EntryList[i] = new AccuracyEntryList(); - } - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(ResultProcess), "OnRelease")] - private static void OnResultProcessReleaseFinish() - { - for (int i = 0; i < EntryList.Length; i++) - { - EntryList[i] = null; - Controllers[i] = null; - } - } - - [HarmonyPatch] - public static class NoteBaseJudgePatch - { - public static IEnumerable TargetMethods() - { - return - [ - AccessTools.Method(typeof(NoteBase), "Judge"), - AccessTools.Method(typeof(HoldNote), "JudgeHoldHead"), - AccessTools.Method(typeof(BreakHoldNote), "JudgeHoldHead"), - AccessTools.Method(typeof(TouchNoteB), "Judge"), - AccessTools.Method(typeof(TouchHoldC), "JudgeHoldHead"), - ]; - } - - public static void Postfix( - NoteBase __instance, bool __result, - float ___JudgeTimingDiffMsec, float ___AppearMsec, NoteJudge.EJudgeType ___JudgeType, int ___NoteIndex - ) - { - var monitor = __instance.MonitorId; - if (!__result || EntryList[monitor].NoteIndices.Contains(___NoteIndex)) return; - - EntryList[monitor].NoteIndices.Add(___NoteIndex); - - var raw = (NotesManager.GetCurrentMsec() - ___AppearMsec) - NoteJudge.JudgeAdjustMs; - switch (___JudgeType) - { - case NoteJudge.EJudgeType.Tap: - case NoteJudge.EJudgeType.Break: - { - EntryList[monitor].DiffList[0].Add(___JudgeTimingDiffMsec); - EntryList[monitor].RawDiffList[0].Add(raw); - break; - } - case NoteJudge.EJudgeType.Touch: - { - EntryList[monitor].DiffList[2].Add(___JudgeTimingDiffMsec); - EntryList[monitor].RawDiffList[2].Add(raw); - break; - } - case NoteJudge.EJudgeType.ExTap: - { - EntryList[monitor].DiffList[3].Add(___JudgeTimingDiffMsec); - EntryList[monitor].RawDiffList[3].Add(raw); - break; - } - } - - // MelonLogger.Msg($"{___JudgeType}: {___JudgeTimingDiffMsec}, {raw}"); - } - } - - - [HarmonyPostfix] - [HarmonyPatch(typeof(SlideRoot), "Judge")] - private static void SlideRootJudgePatch( - SlideRoot __instance, bool __result, - float ___JudgeTimingDiffMsec, float ___TailMsec, float ___lastWaitTimeForJudge, - NoteJudge.EJudgeType ___JudgeType, int ___NoteIndex - ) - { - var monitor = __instance.MonitorId; - var userData = Singleton.Instance.GetUserData(monitor); - if(!userData.IsEntry) return; - - if (!__result || EntryList[monitor].NoteIndices.Contains(___NoteIndex)) return; - - EntryList[monitor].NoteIndices.Add(___NoteIndex); - - var raw = (NotesManager.GetCurrentMsec() - ___TailMsec + ___lastWaitTimeForJudge) - NoteJudge.JudgeAdjustMs; - EntryList[monitor].DiffList[1].Add(___JudgeTimingDiffMsec - NoteJudge.JudgeAdjustMs); - EntryList[monitor].RawDiffList[1].Add(raw); - - // MelonLogger.Msg($"{___JudgeType}: {___JudgeTimingDiffMsec}, {raw}"); - } - - - [HarmonyPostfix] - [HarmonyPatch(typeof(ResultProcess), "OnStart")] - private static void OnResultProcessStartFinish( - ResultMonitor[] ____monitors, ResultProcess.ResultScoreViewType[] ____resultScoreViewType, UserData[] ____userData - ) - { - foreach (var monitor in ____monitors) - { - var idx = monitor.MonitorIndex; - if (!____userData[idx].IsEntry) continue; - - var fileName = $"Acc_Track_{GameManager.MusicTrackNumber}_Player_{idx}.txt"; - var filePath = Path.Combine(Environment.CurrentDirectory, fileName); - - using (var writer = new StreamWriter(filePath)) - { - for (int i = 0; i < TableRowNames.Length; i++) - { - writer.WriteLine($"Row: {TableRowNames[i]}"); - writer.WriteLine(" DiffList:"); - writer.WriteLine($" {string.Join(", ", EntryList[idx].DiffList[i])}"); - writer.WriteLine(" RawDiffList:"); - writer.WriteLine($" {string.Join(", ", EntryList[idx].RawDiffList[i])}"); - writer.WriteLine(); - } - } - - var controller = Traverse.Create(monitor).Field("_scoreBoardController").Value; - var newController = Object.Instantiate(controller, controller.transform); - newController.gameObject.GetComponent().enabled = false; - newController.transform.localPosition = Vector3.zero; - var table = ExtractTextObjs(newController); - for (var i = 0; i < TableHead.Length; i++) - { - table[0, i].text = TableHead[i]; - } - - for (var i = 0; i < TableRowNames.Length; i++) - { - table[i + 1, 0].text = TableRowNames[i]; - var num = EntryList[idx].DiffList[i].Count; - table[i + 1, 1].text = num.ToString(); - if (num <= 0) - { - table[i + 1, 2].text = "——"; - table[i + 1, 3].text = "——"; - table[i + 1, 4].text = "——"; - continue; - } - - var average = EntryList[idx].DiffList[i].Average(); - var averageFrame = average * 0.06f; - table[i + 1, 2].text = averageFrame.ToString("+0.00;-0.00;0.00", CultureInfo.InvariantCulture); - var averageRawFrame = EntryList[idx].RawDiffList[i].Average() * 0.06f; - table[i + 1, 3].text = averageRawFrame.ToString("+0.00;-0.00;0.00", CultureInfo.InvariantCulture); - - if (num <= 1) - { - table[i + 1, 4].text = "——"; - } - else - { - var deviSqr = EntryList[idx].DiffList[i].Sum(x => (x - average) * (x - average)) / (num - 1); - var devi = Mathf.Sqrt(deviSqr) * 0.06f; - table[i + 1, 4].text = devi.ToString("0.00", CultureInfo.InvariantCulture); - } - } - - newController.gameObject.SetActive(____resultScoreViewType[idx] == ResultProcess.ResultScoreViewType.VSResult); - Controllers[idx] = newController; - } - } - - private static readonly ScoreBoardController[] Controllers = new ScoreBoardController[2]; - - [HarmonyPostfix] - [HarmonyPatch(typeof(ResultMonitor), "ChangeScoreBoard")] - private static void OnChangeScoreBoard( - ResultMonitor __instance, ResultProcess.ResultScoreViewType resultScoreType - ) - { - Controllers[__instance.MonitorIndex].gameObject.SetActive(resultScoreType == ResultProcess.ResultScoreViewType.VSResult); - } - - - private static readonly string[] RowNames = ["_tap", "_hold", "_slide", "_touch", "_break"]; - private static readonly string[] ColumnNames = ["_critical", "_perfect", "_great", "_good", "_miss"]; - private static readonly string[] TableHead = ["", "NUM", "AVG", "RAW", "S.D."]; - private static readonly string[] TableRowNames = ["TAP", "SLD", "TCH", "EX"]; - - private static TextMeshProUGUI[,] ExtractTextObjs(ScoreBoardController controller) - { - var result = new TextMeshProUGUI[RowNames.Length, ColumnNames.Length]; - for (var i = 0; i < RowNames.Length; i++) - { - for (int j = 0; j < ColumnNames.Length; j++) - { - var trav = Traverse.Create(controller) - .Field(RowNames[i]) - .Field(ColumnNames[j]); - var text = trav.Field("_numberText").Value; - text.color = Color.black; - result[i, j] = text; - trav.GetValue().SetVisibleCloseBox(false); - } - } - return result; - } - -} diff --git a/AquaMai/AquaMai.Mods/UX/OneKeyEntryEnd.cs b/AquaMai/AquaMai.Mods/UX/OneKeyEntryEnd.cs deleted file mode 100644 index 5e2dee77..00000000 --- a/AquaMai/AquaMai.Mods/UX/OneKeyEntryEnd.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System.Collections.Generic; -using AquaMai.Config.Attributes; -using AquaMai.Config.Types; -using AquaMai.Core.Helpers; -using HarmonyLib; -using Mai2.Mai2Cue; -using Main; -using Manager; -using MelonLoader; -using Process; - -namespace AquaMai.Mods.UX; - -[ConfigSection( - en: "One key to proceed to music select (during entry) or end current PC (during music select).", - zh: "一键跳过登录过程直接进入选歌界面,或在选歌界面直接结束本局游戏")] -public class OneKeyEntryEnd -{ - [ConfigEntry] - public static readonly KeyCodeOrName key = KeyCodeOrName.Service; - - [ConfigEntry] - public static readonly bool longPress = true; - - [HarmonyPrefix] - [HarmonyPatch(typeof(GameMainObject), "Update")] - public static void OnGameMainObjectUpdate() - { - if (!KeyListener.GetKeyDownOrLongPress(key, longPress)) return; - MelonLogger.Msg("[QuickSkip] Activated"); - - var traverse = Traverse.Create(SharedInstances.ProcessDataContainer.processManager); - var processList = traverse.Field("_processList").GetValue>(); - - ProcessBase processToRelease = null; - - foreach (ProcessManager.ProcessControle process in processList) - { - switch (process.Process.ToString()) - { - // After login - case "Process.ModeSelect.ModeSelectProcess": - case "Process.LoginBonus.LoginBonusProcess": - case "Process.RegionalSelectProcess": - case "Process.CharacterSelectProcess": - case "Process.TicketSelect.TicketSelectProcess": - processToRelease = process.Process; - break; - - case "Process.MusicSelectProcess": - // Skip to save - SoundManager.PreviewEnd(); - SoundManager.PlayBGM(Cue.BGM_COLLECTION, 2); - SharedInstances.ProcessDataContainer.processManager.AddProcess(new FadeProcess(SharedInstances.ProcessDataContainer, process.Process, new UnlockMusicProcess(SharedInstances.ProcessDataContainer))); - break; - } - } - - if (processToRelease != null) - { - GameManager.SetMaxTrack(); - SharedInstances.ProcessDataContainer.processManager.AddProcess(new FadeProcess(SharedInstances.ProcessDataContainer, processToRelease, new MusicSelectProcess(SharedInstances.ProcessDataContainer))); - } - } -} diff --git a/AquaMai/AquaMai.Mods/UX/OneKeyRetrySkip.cs b/AquaMai/AquaMai.Mods/UX/OneKeyRetrySkip.cs deleted file mode 100644 index 2b7c1305..00000000 --- a/AquaMai/AquaMai.Mods/UX/OneKeyRetrySkip.cs +++ /dev/null @@ -1,46 +0,0 @@ -using AquaMai.Config.Attributes; -using AquaMai.Config.Types; -using AquaMai.Core.Helpers; -using HarmonyLib; -using MAI2.Util; -using Manager; -using Process; - -namespace AquaMai.Mods.UX; - -[ConfigSection( - en: "One key to retry (1.30+) or skip current chart in gameplay.", - zh: "在游戏中途一键重试(1.30+)或跳过当前谱面")] -public class OneKeyRetrySkip -{ - [ConfigEntry] - public static readonly KeyCodeOrName retryKey = KeyCodeOrName.Service; - - [ConfigEntry] - public static readonly bool retryLongPress = false; - - [ConfigEntry] - public static readonly KeyCodeOrName skipKey = KeyCodeOrName.Service; - - [ConfigEntry] - public static readonly bool skipLongPress = true; - - [HarmonyPostfix] - [HarmonyPatch(typeof(GameProcess), "OnUpdate")] - public static void PostGameProcessUpdate(GameProcess __instance, Message[] ____message, ProcessDataContainer ___container) - { - if (KeyListener.GetKeyDownOrLongPress(skipKey, skipLongPress)) - { - var traverse = Traverse.Create(__instance); - ___container.processManager.SendMessage(____message[0]); - Singleton.Instance.SetSyncResult(0); - traverse.Method("SetRelease").GetValue(); - } - - if (KeyListener.GetKeyDownOrLongPress(retryKey, retryLongPress) && GameInfo.GameVersion >= 23000) - { - // This is original typo in Assembly-CSharp - Singleton.Instance.SetQuickRetryFrag(flag: true); - } - } -} diff --git a/AquaMai/AquaMai.Mods/UX/PracticeMode/Libs/PractiseModeUI.cs b/AquaMai/AquaMai.Mods/UX/PracticeMode/Libs/PractiseModeUI.cs deleted file mode 100644 index 8bd17b29..00000000 --- a/AquaMai/AquaMai.Mods/UX/PracticeMode/Libs/PractiseModeUI.cs +++ /dev/null @@ -1,151 +0,0 @@ -using System; -using AquaMai.Mods.Fix; -using AquaMai.Core.Helpers; -using AquaMai.Core.Resources; -using Manager; -using UnityEngine; - -namespace AquaMai.Mods.UX.PracticeMode.Libs; - -public class PracticeModeUI : MonoBehaviour -{ - private static float windowTop => Screen.height - GuiSizes.PlayerWidth + GuiSizes.PlayerWidth * .22f; - private static float controlHeight => GuiSizes.PlayerWidth * .13f; - private static float sideButtonWidth => GuiSizes.PlayerWidth * .1f; - private static float centerButtonWidth => GuiSizes.PlayerWidth * .28f; - private static int fontSize => (int)(GuiSizes.PlayerWidth * .02f); - - private static Rect GetButtonRect(int pos, int row) - { - float x; - float width; - switch (pos) - { - case 0: - x = GuiSizes.PlayerCenter - centerButtonWidth / 2 - sideButtonWidth - GuiSizes.Margin; - width = sideButtonWidth; - break; - case 1: - x = GuiSizes.PlayerCenter - centerButtonWidth / 2; - width = centerButtonWidth; - break; - case 2: - x = GuiSizes.PlayerCenter + centerButtonWidth / 2 + GuiSizes.Margin; - width = sideButtonWidth; - break; - default: - throw new ArgumentOutOfRangeException(nameof(pos), pos, null); - } - - return new Rect(x, windowTop + (GuiSizes.Margin + controlHeight) * row + GuiSizes.Margin, width, controlHeight); - } - - public void OnGUI() - { - var labelStyle = GUI.skin.GetStyle("label"); - labelStyle.fontSize = fontSize; - labelStyle.alignment = TextAnchor.MiddleCenter; - - var buttonStyle = GUI.skin.GetStyle("button"); - buttonStyle.fontSize = fontSize; - - GUI.Box(new Rect( - GuiSizes.PlayerCenter - centerButtonWidth / 2 - sideButtonWidth - GuiSizes.Margin * 2, - windowTop, - centerButtonWidth + sideButtonWidth * 2 + GuiSizes.Margin * 4, - controlHeight * 4 + GuiSizes.Margin * 5 - ), ""); - - GUI.Button(GetButtonRect(0, 0), Locale.SeekBackward); - GUI.Button(GetButtonRect(1, 0), Locale.Pause); - GUI.Button(GetButtonRect(2, 0), Locale.SeekForward); - - if (PracticeMode.repeatStart == -1) - { - GUI.Button(GetButtonRect(0, 1), Locale.MarkRepeatStart); - GUI.Label(GetButtonRect(1, 1), Locale.RepeatNotSet); - } - else if (PracticeMode.repeatEnd == -1) - { - GUI.Button(GetButtonRect(0, 1), Locale.MarkRepeatEnd); - GUI.Label(GetButtonRect(1, 1), Locale.RepeatStartSet); - GUI.Button(GetButtonRect(2, 1), Locale.RepeatReset); - } - else - { - GUI.Label(GetButtonRect(1, 1), Locale.RepeatStartEndSet); - GUI.Button(GetButtonRect(2, 1), Locale.RepeatReset); - } - - GUI.Button(GetButtonRect(0, 2), Locale.SpeedDown); - GUI.Label(GetButtonRect(1, 2), $"{Locale.Speed} {PracticeMode.speed * 100:000}%"); - GUI.Button(GetButtonRect(2, 2), Locale.SpeedUp); - GUI.Button(GetButtonRect(1, 3), Locale.SpeedReset); - - GUI.Label(GetButtonRect(0, 3), $"{TimeSpan.FromMilliseconds(PracticeMode.CurrentPlayMsec):mm\\:ss\\.fff}\n{TimeSpan.FromMilliseconds(NotesManager.Instance().getPlayFinalMsec()):mm\\:ss\\.fff}"); - GUI.Button(GetButtonRect(2, 3), $"保持流速\n{(PracticeMode.keepNoteSpeed ? "ON" : "OFF")}"); - } - - public void Update() - { - if (InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.E8)) - { - PracticeMode.Seek(-1000); - } - else if (InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.E2)) - { - PracticeMode.Seek(1000); - } - else if (InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.B8) || InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.B1)) - { - DebugFeature.Pause = !DebugFeature.Pause; - if (!DebugFeature.Pause) - { - PracticeMode.Seek(0); - } - } - else if (InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.B7) && PracticeMode.repeatStart == -1) - { - PracticeMode.repeatStart = PracticeMode.CurrentPlayMsec; - } - else if (InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.B7) && PracticeMode.repeatEnd == -1) - { - PracticeMode.SetRepeatEnd(PracticeMode.CurrentPlayMsec); - } - else if (InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.B2)) - { - PracticeMode.ClearRepeat(); - } - else if (InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.B6)) - { - PracticeMode.SpeedDown(); - } - else if (InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.B3)) - { - PracticeMode.SpeedUp(); - } - else if (InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.B5) || InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.B4)) - { - PracticeMode.SpeedReset(); - } - else if (InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.E4)) - { - PracticeMode.keepNoteSpeed = !PracticeMode.keepNoteSpeed; - PracticeMode.gameCtrl?.ResetOptionSpeed(); - } - else if ( - InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.A1) || - InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.A2) || - InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.A3) || - InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.A4) || - InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.A5) || - InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.A6) || - InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.A7) || - InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.A8) - ) - { - PracticeMode.ui = null; - Destroy(this); - } - } -} diff --git a/AquaMai/AquaMai.Mods/UX/PracticeMode/PracticeMode.cs b/AquaMai/AquaMai.Mods/UX/PracticeMode/PracticeMode.cs deleted file mode 100644 index a0878ebc..00000000 --- a/AquaMai/AquaMai.Mods/UX/PracticeMode/PracticeMode.cs +++ /dev/null @@ -1,282 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Reflection; -using AquaMai.Mods.Fix; -using AquaMai.Core.Helpers; -using AquaMai.Core.Resources; -using AquaMai.Mods.UX.PracticeMode.Libs; -using HarmonyLib; -using Manager; -using Monitor; -using Monitor.Game; -using Process; -using UnityEngine; -using AquaMai.Config.Attributes; -using AquaMai.Config.Types; - -namespace AquaMai.Mods.UX.PracticeMode; - -[ConfigCollapseNamespace] -[ConfigSection( - en: "Practice Mode.", - zh: "练习模式")] -public class PracticeMode -{ - [ConfigEntry( - en: "Key to show Practice Mode UI.", - zh: "显示练习模式 UI 的按键")] - public static readonly KeyCodeOrName key = KeyCodeOrName.Test; - - [ConfigEntry] - public static readonly bool longPress = false; - - public static double repeatStart = -1; - public static double repeatEnd = -1; - public static float speed = 1; - private static CriAtomExPlayer player; - private static MovieMaterialMai2 movie; - public static GameCtrl gameCtrl; - public static bool keepNoteSpeed = false; - - public static void SetRepeatEnd(double time) - { - if (repeatStart == -1) - { - MessageHelper.ShowMessage(Locale.RepeatStartTimeNotSet); - return; - } - - if (time < repeatStart) - { - MessageHelper.ShowMessage(Locale.RepeatEndTimeLessThenStartTime); - return; - } - - repeatEnd = time; - } - - public static void ClearRepeat() - { - repeatStart = -1; - repeatEnd = -1; - } - - public static void SetSpeed() - { - player.SetPitch((float)(1200 * Math.Log(speed, 2))); - // player.SetDspTimeStretchRatio(1 / speed); - player.UpdateAll(); - - movie.player.SetSpeed(speed); - gameCtrl?.ResetOptionSpeed(); - } - - private static IEnumerator SetSpeedCoroutineInner() - { - yield return null; - SetSpeed(); - } - - public static void SetSpeedCoroutine() - { - SharedInstances.GameMainObject.StartCoroutine(SetSpeedCoroutineInner()); - } - - public static void SpeedUp() - { - speed += .05f; - if (speed > 2) - { - speed = 2; - } - - SetSpeed(); - } - - public static void SpeedDown() - { - speed -= .05f; - if (speed < 0.05) - { - speed = 0.05f; - } - - SetSpeed(); - } - - public static void SpeedReset() - { - speed = 1; - SetSpeed(); - } - - public static void Seek(int addMsec) - { - // Debug feature 里面那个 timer 不能感知变速 - // 为了和魔改版本统一,polyfill 里面不修这个 - // 这里重新实现一个能感知变速的 Seek - var msec = CurrentPlayMsec + addMsec; - if (msec < 0) - { - msec = 0; - } - - CurrentPlayMsec = msec; - } - - public static double CurrentPlayMsec - { - get => NotesManager.GetCurrentMsec() - 91; - set - { - DebugFeature.CurrentPlayMsec = value; - SetSpeedCoroutine(); - } - } - - public static PracticeModeUI ui; - - [HarmonyPatch] - public class PatchNoteSpeed - { - public static IEnumerable TargetMethods() - { - yield return AccessTools.Method(typeof(GameManager), "GetNoteSpeed"); - yield return AccessTools.Method(typeof(GameManager), "GetTouchSpeed"); - } - - public static void Postfix(ref float __result) - { - if (!keepNoteSpeed) return; - __result /= speed; - } - } - - [HarmonyPatch(typeof(GameProcess), "OnStart")] - [HarmonyPostfix] - public static void GameProcessPostStart() - { - repeatStart = -1; - repeatEnd = -1; - speed = 1; - ui = null; - } - - [HarmonyPatch(typeof(GameProcess), "OnRelease")] - [HarmonyPostfix] - public static void GameProcessPostRelease() - { - repeatStart = -1; - repeatEnd = -1; - speed = 1; - ui = null; - } - - [HarmonyPatch(typeof(GameCtrl), "Initialize")] - [HarmonyPostfix] - public static void GameCtrlPostInitialize(GameCtrl __instance) - { - gameCtrl = __instance; - } - -# if DEBUG - [HarmonyPrefix] - [HarmonyPatch(typeof(GenericProcess), "OnUpdate")] - public static void OnGenericProcessUpdate(GenericMonitor[] ____monitors) - { - if (Input.GetKeyDown(KeyCode.F11)) - { - ____monitors[0].gameObject.AddComponent(); - } - } -# endif - - [HarmonyPatch(typeof(GameProcess), "OnUpdate")] - [HarmonyPostfix] - public static void GameProcessPostUpdate(GameProcess __instance, GameMonitor[] ____monitors) - { - if (KeyListener.GetKeyDownOrLongPress(key, longPress) && ui is null) - { - ui = ____monitors[0].gameObject.AddComponent(); - } - - if (repeatStart >= 0 && repeatEnd >= 0) - { - if (CurrentPlayMsec >= repeatEnd) - { - CurrentPlayMsec = repeatStart; - } - } - } - - private static float startGap = -1f; - - [HarmonyPatch(typeof(NotesManager), "StartPlay")] - [HarmonyPostfix] - public static void NotesManagerPostUpdateTimer(float msecStartGap) - { - startGap = msecStartGap; - } - - [HarmonyPatch(typeof(NotesManager), "UpdateTimer")] - [HarmonyPrefix] - public static bool NotesManagerPostUpdateTimer(bool ____isPlaying, Stopwatch ____stopwatch, ref float ____curMSec, ref float ____curMSecPre, float ____msecStartGap) - { - var stackTrace = new StackTrace(); // get call stack - var stackFrames = stackTrace.GetFrames(); // get method calls (frames) - if(stackFrames.Select(it => it.GetMethod().DeclaringType.Name).Contains("AdvDemoProcess")) - { - return true; - } - - if (startGap != -1f) - { - ____curMSec = startGap; - ____curMSecPre = startGap; - ____stopwatch?.Reset(); - startGap = -1f; - } - else - { - ____curMSecPre = ____curMSec; - if (____isPlaying && ____stopwatch != null && !DebugFeature.Pause) - { - var num = (double)____stopwatch.ElapsedTicks / Stopwatch.Frequency * 1000.0 * speed; - ____curMSec += (float)num; - ____stopwatch.Reset(); - ____stopwatch.Start(); - } - } - - return false; - } - - [HarmonyPatch(typeof(SoundCtrl), "Initialize")] - [HarmonyPostfix] - public static void SoundCtrlPostInitialize(SoundCtrl.InitParam param, Dictionary ____players) - { - var wrapper = ____players[2]; - player = (CriAtomExPlayer)wrapper.GetType().GetField("Player").GetValue(wrapper); - // var pool = new CriAtomExStandardVoicePool(1, 8, 96000, true, 2); - // pool.AttachDspTimeStretch(); - // player.SetVoicePoolIdentifier(pool.identifier); - - // debug - // var wrapper1 = ____players[7]; - // var player1 = (CriAtomExPlayer)wrapper1.GetType().GetField("Player").GetValue(wrapper1); - // var pool = new CriAtomExStandardVoicePool(1, 8, 96000, true, 2); - // pool.AttachDspTimeStretch(); - // player1.SetVoicePoolIdentifier(pool.identifier); - // player1.SetDspTimeStretchRatio(2); - } - - [HarmonyPatch(typeof(MovieController), "Awake")] - [HarmonyPostfix] - public static void MovieControllerPostAwake(MovieMaterialMai2 ____moviePlayers) - { - movie = ____moviePlayers; - } -} diff --git a/AquaMai/AquaMai.Mods/UX/QuickEndPlay.cs b/AquaMai/AquaMai.Mods/UX/QuickEndPlay.cs deleted file mode 100644 index a8469bc5..00000000 --- a/AquaMai/AquaMai.Mods/UX/QuickEndPlay.cs +++ /dev/null @@ -1,70 +0,0 @@ -using AquaMai.Config.Attributes; -using AquaMai.Core.Helpers; -using AquaMai.Core.Resources; -using HarmonyLib; -using MAI2.Util; -using Manager; -using Monitor; -using Process; -using UnityEngine; - -namespace AquaMai.Mods.UX; - -[ConfigSection( - en: "Show a \"skip\" button like AstroDX after the notes end.", - zh: "音符结束之后显示像 AstroDX 一样的「跳过」按钮")] -public class QuickEndPlay -{ - private static int _timer; - - [HarmonyPatch(typeof(GameProcess), "OnStart")] - [HarmonyPostfix] - public static void GameProcessPostStart(GameMonitor[] ____monitors) - { - _timer = 0; - ____monitors[0].gameObject.AddComponent(); - } - - [HarmonyPatch(typeof(GameProcess), "OnUpdate")] - [HarmonyPostfix] - public static void GameProcessPostUpdate(GameProcess __instance, Message[] ____message, ProcessDataContainer ___container, byte ____sequence) - { - switch (____sequence) - { - case 9: - _timer = 0; - break; - case > 4: - _timer++; - break; - default: - _timer = 0; - break; - } - - if (_timer > 60 && (InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.B4) || InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.E4))) - { - var traverse = Traverse.Create(__instance); - ___container.processManager.SendMessage(____message[0]); - Singleton.Instance.SetSyncResult(0); - traverse.Method("SetRelease").GetValue(); - } - } - - private class Ui : MonoBehaviour - { - public void OnGUI() - { - if (_timer < 60) return; - - // 这里重新 setup 一下 style 也可以 - var x = GuiSizes.PlayerCenter; - var y = Screen.height - GuiSizes.PlayerWidth * .37f; - var width = GuiSizes.PlayerWidth * .25f; - var height = GuiSizes.PlayerWidth * .13f; - - GUI.Box(new Rect(x, y, width, height), ""); - GUI.Button(new Rect(x, y, width, height), Locale.Skip); - } - } -} diff --git a/AquaMai/AquaMai.Mods/UX/README.md b/AquaMai/AquaMai.Mods/UX/README.md deleted file mode 100644 index 80cc09ec..00000000 --- a/AquaMai/AquaMai.Mods/UX/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# UX - -Features aiming at improving the user experience and can't fit in other categories go here. Generally, UX features are triggered by some user action or let the user perceive. diff --git a/AquaMai/AquaMai.Mods/UX/SelectionDetail.cs b/AquaMai/AquaMai.Mods/UX/SelectionDetail.cs deleted file mode 100644 index 325600fc..00000000 --- a/AquaMai/AquaMai.Mods/UX/SelectionDetail.cs +++ /dev/null @@ -1,143 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using AquaMai.Core.Attributes; -using AquaMai.Config.Attributes; -using AquaMai.Core.Helpers; -using AquaMai.Core.Resources; -using HarmonyLib; -using MAI2.Util; -using Manager; -using Manager.MaiStudio; -using Manager.UserDatas; -using Monitor; -using Process; -using UnityEngine; - -namespace AquaMai.Mods.UX; - -[EnableGameVersion(23500)] -[ConfigSection( - en: "Show detail of selected song in music selection screen.", - zh: "选歌界面显示选择的歌曲的详情")] -public class SelectionDetail -{ - private static readonly Window[] window = new Window[2]; - private static MusicSelectProcess.MusicSelectData SelectData { get; set; } - private static readonly int[] difficulty = new int[2]; - - [HarmonyPostfix] - [HarmonyPatch(typeof(MusicSelectMonitor), "UpdateRivalScore")] - public static void ScrollUpdate(MusicSelectProcess ____musicSelect, MusicSelectMonitor __instance) - { - int player; - if (__instance == ____musicSelect.MonitorArray[0]) - { - player = 0; - } - else if (__instance == ____musicSelect.MonitorArray[1]) - { - player = 1; - } - else - { - return; - } - - if (window[player] != null) - { - window[player].Close(); - } - - var userData = Singleton.Instance.GetUserData(player); - if (!userData.IsEntry) return; - - if (____musicSelect.IsRandomIndex()) return; - - SelectData = ____musicSelect.GetMusic(0); - if (SelectData == null) return; - difficulty[player] = ____musicSelect.GetDifficulty(player); - - window[player] = player == 0 ? __instance.gameObject.AddComponent() : __instance.gameObject.AddComponent(); - } - - private class P1Window : Window - { - protected override int player => 0; - } - - private class P2Window : Window - { - protected override int player => 1; - } - - private abstract class Window : MonoBehaviour - { - protected abstract int player { get; } - private UserData userData => Singleton.Instance.GetUserData(player); - - public void OnGUI() - { - var dataToShow = new List(); - dataToShow.Add($"ID: {SelectData.MusicData.name.id}"); - dataToShow.Add(MusicDirHelper.LookupPath(SelectData.MusicData.name.id).Split('/').Reverse().ToArray()[3]); - if (SelectData.MusicData.genreName is not null) // SelectData.MusicData.genreName.str may not correct - dataToShow.Add(Singleton.Instance.GetMusicGenre(SelectData.MusicData.genreName.id)?.genreName); - if (SelectData.MusicData.AddVersion is not null) - dataToShow.Add(Singleton.Instance.GetMusicVersion(SelectData.MusicData.AddVersion.id)?.genreName); - var notesData = SelectData.MusicData.notesData[difficulty[player]]; - dataToShow.Add($"{notesData?.level}.{notesData?.levelDecimal}"); - - var rate = CalcB50(SelectData.MusicData, difficulty[player]); - if (rate > 0) - { - dataToShow.Add(string.Format(Locale.RatingUpWhenSSSp, rate)); - } - - var playCount = Shim.GetUserScoreList(userData)[difficulty[player]].FirstOrDefault(it => it.id == SelectData.MusicData.name.id)?.playcount ?? 0; - if (playCount > 0) - { - dataToShow.Add(string.Format(Locale.PlayCount, playCount)); - } - - - var width = GuiSizes.FontSize * 15f; - var x = GuiSizes.PlayerCenter - width / 2f + GuiSizes.PlayerWidth * player; - var y = Screen.height * 0.87f; - - var labelStyle = GUI.skin.GetStyle("label"); - labelStyle.fontSize = GuiSizes.FontSize; - labelStyle.alignment = TextAnchor.MiddleCenter; - - GUI.Box(new Rect(x, y, width, dataToShow.Count * GuiSizes.LabelHeight + 2 * GuiSizes.Margin), ""); - for (var i = 0; i < dataToShow.Count; i++) - { - GUI.Label(new Rect(x, y + GuiSizes.Margin + i * GuiSizes.LabelHeight, width, GuiSizes.LabelHeight), dataToShow[i]); - } - } - - private uint CalcB50(MusicData musicData, int difficulty) - { - var theory = new UserRate(musicData.name.id, difficulty, 1010000, (uint)musicData.version); - var list = theory.OldFlag ? userData.RatingList.RatingList : userData.RatingList.NewRatingList; - var userLowRate = list.Last(); - var userSongRate = list.FirstOrDefault(it => it.MusicId == musicData.name.id && it.Level == difficulty); - - if (!userSongRate.Equals(default(UserRate))) - { - return theory.SingleRate - userSongRate.SingleRate; - } - - if (theory.SingleRate > userLowRate.SingleRate) - { - return theory.SingleRate - userLowRate.SingleRate; - } - - return 0; - } - - public void Close() - { - Destroy(this); - } - } -} diff --git a/AquaMai/AquaMai.Mods/Utils/DisplayFrameRate.cs b/AquaMai/AquaMai.Mods/Utils/DisplayFrameRate.cs deleted file mode 100644 index 4e5a3f1b..00000000 --- a/AquaMai/AquaMai.Mods/Utils/DisplayFrameRate.cs +++ /dev/null @@ -1,53 +0,0 @@ -using AquaMai.Config.Attributes; -using AquaMai.Core.Helpers; -using HarmonyLib; -using Main; -using UnityEngine; - -namespace AquaMai.Mods.Utils; - -[ConfigSection( - en: "Display framerate.", - zh: "显示帧率")] -public class DisplayFrameRate -{ - [HarmonyPatch(typeof(GameMainObject), "Awake")] - [HarmonyPostfix] - public static void ShowUi(GameMainObject __instance) - { - __instance.gameObject.AddComponent(); - } - - private class Ui : MonoBehaviour - { - private static float sampleTime = 1f; - private static int frame; - private static float time; - private static float fps; - - public void OnGUI() - { - var labelStyle = GUI.skin.GetStyle("label"); - labelStyle.fontSize = GuiSizes.FontSize; - labelStyle.alignment = TextAnchor.MiddleCenter; - - const float x = 10f; - const float y = 10f; - var width = GuiSizes.FontSize * 7f; - var height = GuiSizes.LabelHeight * 1.5f; - - frame += 1; - time += Time.deltaTime; - - if (time >= sampleTime) - { - fps = frame / time; - frame = 0; - time = 0; - } - - GUI.Box(new Rect(x, y, width, height), ""); - GUI.Label(new Rect(x, y, width, height), $"{fps:0.0} FPS"); - } - } -} diff --git a/AquaMai/AquaMai.Mods/Utils/LogNetworkErrors.cs b/AquaMai/AquaMai.Mods/Utils/LogNetworkErrors.cs deleted file mode 100644 index 73f59923..00000000 --- a/AquaMai/AquaMai.Mods/Utils/LogNetworkErrors.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Diagnostics; -using AquaMai.Config.Attributes; -using HarmonyLib; -using Manager; -using Manager.Operation; -using MelonLoader; -using Net.Packet; - -namespace AquaMai.Mods.Utils; - -[ConfigSection(exampleHidden: true, defaultOn: true)] -public class LogNetworkErrors -{ - [HarmonyPostfix] - [HarmonyPatch(typeof(Packet), "ProcImpl")] - public static void Postfix(PacketState __result, Packet __instance) - { - if (__result == PacketState.Error) - { - MelonLogger.Msg($"[LogNetworkErrors] {__instance.Query.Api}: {__instance.Status}"); - } - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(DataDownloader), "NotifyOffline")] - public static void DataDownloader() - { - MelonLogger.Msg("[LogNetworkErrors] DataDownloader NotifyOffline"); - var stackTrace = new StackTrace(); - MelonLogger.Msg(stackTrace.ToString()); - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(OnlineCheckInterval), "NotifyOffline")] - public static void OnlineCheckInterval() - { - MelonLogger.Msg("[LogNetworkErrors] OnlineCheckInterval NotifyOffline"); - var stackTrace = new StackTrace(); - MelonLogger.Msg(stackTrace.ToString()); - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(OperationManager), "IsAliveAimeServer", MethodType.Getter)] - public static void IsAliveAimeServer(bool __result) - { - if (__result == false) - MelonLogger.Msg($"[LogNetworkErrors] IsAliveAimeServer Is {__result}"); - } -} diff --git a/AquaMai/AquaMai.Mods/Utils/LogNetworkRequests.cs b/AquaMai/AquaMai.Mods/Utils/LogNetworkRequests.cs deleted file mode 100644 index 5cd60aeb..00000000 --- a/AquaMai/AquaMai.Mods/Utils/LogNetworkRequests.cs +++ /dev/null @@ -1,188 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Runtime.CompilerServices; -using System.Text; -using Net; -using Net.Packet; -using MelonLoader; -using MelonLoader.TinyJSON; -using HarmonyLib; -using AquaMai.Core.Attributes; -using AquaMai.Config.Attributes; -using AquaMai.Core.Helpers; - -namespace AquaMai.Mods.Utils; - -[ConfigSection( - en: "Log network requests to the MelonLoader console.", - zh: "将网络请求输出到 MelonLoader 控制台")] -public class LogNetworkRequests -{ - [ConfigEntry] - private static readonly bool url = true; - - [ConfigEntry] - private static readonly bool request = true; - [ConfigEntry] - private static readonly string requestOmittedApis = "UploadUserPhotoApi,UploadUserPortraitApi"; - - [ConfigEntry] - private static readonly bool response = true; - [ConfigEntry] - private static readonly string responseOmittedApis = "GetGameEventApi"; - [ConfigEntry( - en: "Only print error responses, without the successful ones.", - zh: "仅输出出错的响应,不输出成功的响应")] - private static readonly bool responseErrorOnly = false; - - private static HashSet requestOmittedApiList = []; - private static HashSet responseOmittedApiList = []; - - private static readonly ConditionalWeakTable errorResponse = new(); - - public static void OnBeforePatch() - { - requestOmittedApiList = [.. requestOmittedApis.Split(',')]; - responseOmittedApiList = [.. responseOmittedApis.Split(',')]; - - if (responseErrorOnly && !response) - { - MelonLogger.Warning("[LogNetworkRequests] `responseErrorOnly` is enabled but `response` is disabled. Will not print any response."); - } - } - - private static string GetApiName(INetQuery netQuery) - { - return Shim.RemoveApiSuffix(netQuery.Api); - } - - [EnableIf(nameof(url))] - [HarmonyPostfix] - [HarmonyPatch(typeof(Packet), "Create")] - public static void PostCreate(Packet __instance) - { - MelonLogger.Msg($"[LogNetworkRequests] {GetApiName(__instance.Query)} URL: {MaybeGetNetPacketUrl(__instance)}"); - } - - private static string MaybeGetNetPacketUrl(Packet __instance) - { - if (Traverse.Create(__instance).Field("Client").GetValue() is not NetHttpClient client) - { - return ""; - } - if (Traverse.Create(client).Field("_request").GetValue() is not HttpWebRequest request) - { - return ""; - } - return request.RequestUri.ToString(); - } - - // Record the error responses of NetHttpClient to display. These responses could not be acquired in other ways. - [HarmonyPrefix] - [HarmonyPatch(typeof(NetHttpClient), "SetError")] - public static void PreSetError(NetHttpClient __instance, HttpWebResponse response) - { - if (response != null) - { - errorResponse.Add(__instance, response); - } - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(Packet), "ProcImpl")] - public static void PreProcImpl(Packet __instance) - { - if (request && __instance.State == PacketState.Ready) - { - var netQuery = __instance.Query; - var api = GetApiName(netQuery); - var displayRequest = InspectRequest(api, netQuery.GetRequest()); - MelonLogger.Msg($"[LogNetworkRequests] {api} Request: {displayRequest}"); - } - else if ( - response && - __instance.State == PacketState.Process && - Traverse.Create(__instance).Field("Client").GetValue() is NetHttpClient client) - { - if (client.State == NetHttpClient.StateDone && !responseErrorOnly) - { - var netQuery = __instance.Query; - var api = GetApiName(netQuery); - var displayResponse = InspectResponse(api, client.GetResponse().ToArray()); - MelonLogger.Msg($"[LogNetworkRequests] {api} Response: {displayResponse}"); - } - else if (client.State == NetHttpClient.StateError) - { - var displayError = InspectError(client); - MelonLogger.Warning($"[LogNetworkRequests] {GetApiName(__instance.Query)} Error: {displayError}"); - } - } - } - - private static string InspectRequest(string api, string request) => - requestOmittedApiList.Contains(api) - ? $"<{request.Length} characters omitted>" - : (request == "" ? "" : request); - - private static string InspectResponse(string api, byte[] response) - { - try - { - var decoded = Encoding.UTF8.GetString(response); - if (responseOmittedApiList.Contains(api)) - { - return $"<{decoded.Length} characters omitted>"; - } - else if (decoded == "") - { - return ""; - } - else if (decoded.IndexOf("\n") != -1) - { - return JSON.Dump(decoded); - } - else - { - return decoded; - } - } - catch (Exception e) - { - // Always non-empty when decoding fails. - return $""; - } - } - - private static string InspectError(NetHttpClient client) => - "<" + - $"WebExceptionStatus.{client.WebException}: " + - $"HttpStatus = {client.HttpStatus}, " + - $"Error = {JSON.Dump(client.Error)}, " + - $"Response = " + - (errorResponse.TryGetValue(client, out var response) - ? InspectErrorResponse(response) - : "null") + - ">"; - - private static string InspectErrorResponse(HttpWebResponse response) - { - try - { - var webConnectionStream = response.GetResponseStream(); - var memoryStream = new MemoryStream(); - webConnectionStream.CopyTo(memoryStream); - return InspectResponse(null, memoryStream.ToArray()); - } - catch (Exception e) - { - // The stream has alraedy been consumed? - return $""; - } - } -} diff --git a/AquaMai/AquaMai.Mods/Utils/LogUnity.cs b/AquaMai/AquaMai.Mods/Utils/LogUnity.cs deleted file mode 100644 index 3826feba..00000000 --- a/AquaMai/AquaMai.Mods/Utils/LogUnity.cs +++ /dev/null @@ -1,23 +0,0 @@ -using AquaMai.Config.Attributes; -using MelonLoader; -using UnityEngine; - -namespace AquaMai.Mods.Utils; - -[ConfigSection( - en: "Output Unity logs (for debugging purposes)", - zh: "输出 Unity 日志(调试用)", - exampleHidden: true)] -public static class LogUnity -{ - public static void OnBeforePatch() - { - Application.logMessageReceived += Log; - } - - private static void Log(string msg, string stackTrace, LogType type) - { - MelonLogger.Msg("[Unity] " + msg); - MelonLogger.Msg("[Unity] " + stackTrace); - } -} \ No newline at end of file diff --git a/AquaMai/AquaMai.Mods/Utils/LogUserId.cs b/AquaMai/AquaMai.Mods/Utils/LogUserId.cs deleted file mode 100644 index ff1fc73b..00000000 --- a/AquaMai/AquaMai.Mods/Utils/LogUserId.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using AquaMai.Config.Attributes; -using HarmonyLib; -using MelonLoader; -using Net.Packet; -using Net.Packet.Mai2; -using Net.VO.Mai2; - -namespace AquaMai.Mods.Utils; - -[ConfigSection( - en: "Log user ID on login.", - zh: "登录时将 UserID 输出到日志")] -public class LogUserId -{ - [HarmonyPostfix] - [HarmonyPatch(typeof(PacketGetUserPreview), MethodType.Constructor, typeof(ulong), typeof(string), typeof(Action), typeof(Action))] - public static void Postfix(ulong userId) - { - MelonLogger.Msg($"[LogUserId] UserLogin: {userId}"); - } -} diff --git a/AquaMai/AquaMai.Mods/Utils/README.md b/AquaMai/AquaMai.Mods/Utils/README.md deleted file mode 100644 index 59023a0e..00000000 --- a/AquaMai/AquaMai.Mods/Utils/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Utils - -Debugging or diagnostic purpose utilities goes here. They don't affect the game play at all, but may show UIs if needed. diff --git a/AquaMai/AquaMai.Mods/Utils/ShowErrorLog.cs b/AquaMai/AquaMai.Mods/Utils/ShowErrorLog.cs deleted file mode 100644 index 276a347c..00000000 --- a/AquaMai/AquaMai.Mods/Utils/ShowErrorLog.cs +++ /dev/null @@ -1,100 +0,0 @@ -using System; -using System.Collections; -using System.IO; -using System.Text.RegularExpressions; -using System.Threading; -using AquaMai.Config.Attributes; -using AquaMai.Core.Helpers; -using HarmonyLib; -using Main; -using MelonLoader; -using UnityEngine; - -namespace AquaMai.Mods.Utils; - -[ConfigSection( - en: "Show error log in the game.", - zh: "在游戏中显示错误日志窗口而不是关闭游戏进程")] -public class ShowErrorLog -{ - private static Ui _errorUi; - - [HarmonyPostfix] - [HarmonyPatch(typeof(GameMain), "ExceptionHandler")] - private static void ExceptionHandler(GameMain __instance, Exception e) - { - if (_errorUi == null) - { - _errorUi = new GameObject("ErrorUI").AddComponent(); - _errorUi.gameObject.SetActive(true); - } - string logFile = $"{MAI2System.Path.ErrorLogPath}{DateTime.Now:yyyyMMddHHmmss}.log"; - MelonLogger.Msg("Error Log:"); - if (File.Exists(logFile)) - { - MelonLogger.Error(File.ReadAllText(logFile)); - _errorUi.SetErrorLog(File.ReadAllText(logFile)); - } - else - { - MelonLogger.Error(e); - _errorUi.SetErrorLog(e.ToString()); - } - - Application.quitting += ApplicationOnQuitting; - _errorUi.StartCoroutine(_errorUi.Show()); - } - - private static void ApplicationOnQuitting() - { - Thread.Sleep(Timeout.Infinite); - } - - private class Ui : MonoBehaviour - { - private string _errorLog = ""; - - public void SetErrorLog(string text) - { - _errorLog = "Error Log:\n" + text; - } - - public void OnGUI() - { - var labelStyle = new GUIStyle(GUI.skin.label) - { - fontSize = GuiSizes.FontSize, - alignment = TextAnchor.MiddleLeft, - normal = new GUIStyleState(){textColor = Color.black} - }; - - var boxStyle = new GUIStyle(GUI.skin.box) - { - normal = new GUIStyleState() { background = Texture2D.whiteTexture } - }; - - int logLineCount = Regex.Matches(_errorLog, "\n").Count + 1; - float offset = GuiSizes.PlayerCenter * 0.12f; - var x = GuiSizes.PlayerCenter / 2f + offset / 2f; - var y = Screen.height / 1.8f; - var width = GuiSizes.PlayerCenter - offset; - var height = GuiSizes.LabelHeight * logLineCount + GuiSizes.Margin * 2; - - GUI.Box(new Rect(x, y, width, height), "", boxStyle); - GUI.Label(new Rect(x, y, width, height), _errorLog, labelStyle); - if (!GuiSizes.SinglePlayer) - { - GUI.Box(new Rect(x + GuiSizes.PlayerWidth, y, width, height), "", boxStyle); - GUI.Label(new Rect(x + GuiSizes.PlayerWidth, y, width, height), _errorLog, labelStyle); - } - } - - public IEnumerator Show() - { - while (true) - { - yield return null; // 让 Unity 处理一帧 - } - } - } -} diff --git a/AquaMai/AquaMai.Mods/Utils/ShowNetErrorDetail.cs b/AquaMai/AquaMai.Mods/Utils/ShowNetErrorDetail.cs deleted file mode 100644 index c852981b..00000000 --- a/AquaMai/AquaMai.Mods/Utils/ShowNetErrorDetail.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System.Collections.Generic; -using AquaMai.Config.Attributes; -using AquaMai.Core.Helpers; -using AquaMai.Core.Resources; -using HarmonyLib; -using MAI2.Util; -using Manager; -using Monitor; -using Process; -using UnityEngine; - -namespace AquaMai.Mods.Utils; - -[ConfigSection( - en: "Show Network error detail in the game when gray network icon appears.", - zh: "出现灰网时显示原因")] -public class ShowNetErrorDetail -{ - [HarmonyPatch(typeof(CommonProcess), "OnStart")] - [HarmonyPostfix] - public static void SetIconStatus(CommonMonitor[] ____monitors) - { - ____monitors[0].gameObject.AddComponent(); - } - - private class DetailUi : MonoBehaviour - { - public void OnGUI() - { - var errors = new List(); - if (!Singleton.Instance.IsAliveServer) - { - errors.Add(Locale.NetErrIsAliveServer); - } - - if (!Singleton.Instance.IsAliveAimeReader) - { - errors.Add(Locale.NetErrIsAliveAimeReader); - } - - if (!Singleton.Instance.IsAliveAimeServer) - { - errors.Add(Locale.NetErrIsAliveAimeServer); - } - - if (!Singleton.Instance.WasDownloadSuccessOnce) - { - errors.Add(Locale.NetErrWasDownloadSuccessOnce); - } - - if (errors.Count == 0) - { - return; - } - - var labelStyle = GUI.skin.GetStyle("label"); - labelStyle.fontSize = GuiSizes.FontSize; - labelStyle.alignment = TextAnchor.MiddleCenter; - - var x = GuiSizes.PlayerCenter + GuiSizes.PlayerWidth * .2f; - var y = Screen.height * .01f; - var width = GuiSizes.FontSize * 15f; - var height = GuiSizes.LabelHeight * errors.Count + GuiSizes.Margin * 2; - - GUI.Box(new Rect(x, y, width, height), ""); - for (var i = 0; i < errors.Count; i++) - { - GUI.Label(new Rect(x, y + GuiSizes.Margin + GuiSizes.LabelHeight * i, width, GuiSizes.LabelHeight), errors[i]); - } - } - } -} diff --git a/AquaMai/AquaMai.sln b/AquaMai/AquaMai.sln deleted file mode 100644 index 4145c5cc..00000000 --- a/AquaMai/AquaMai.sln +++ /dev/null @@ -1,82 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.6.33815.320 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AquaMai.Config.Interfaces", "AquaMai.Config.Interfaces/AquaMai.Config.Interfaces.csproj", "{50C59E41-2939-407E-9CB7-B186F33DDCF9}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AquaMai.Config", "AquaMai.Config/AquaMai.Config.csproj", "{B9CA039A-A0F1-4C87-A33E-3F959974BB3A}" - ProjectSection(ProjectDependencies) = postProject - {50C59E41-2939-407E-9CB7-B186F33DDCF9} = {50C59E41-2939-407E-9CB7-B186F33DDCF9} - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AquaMai.Config.HeadlessLoader", "AquaMai.Config.HeadlessLoader/AquaMai.Config.HeadlessLoader.csproj", "{6B5E1F3E-D012-4CFB-A2FA-26A6CE06BE66}" - ProjectSection(ProjectDependencies) = postProject - {50C59E41-2939-407E-9CB7-B186F33DDCF9} = {50C59E41-2939-407E-9CB7-B186F33DDCF9} - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AquaMai.Build", "AquaMai.Build/AquaMai.Build.csproj", "{4C0C68C3-8B2E-4CA8-A26D-AE87CF2A38A5}" - ProjectSection(ProjectDependencies) = postProject - {6B5E1F3E-D012-4CFB-A2FA-26A6CE06BE66} = {6B5E1F3E-D012-4CFB-A2FA-26A6CE06BE66} - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AquaMai.Core", "AquaMai.Core/AquaMai.Core.csproj", "{33C0D4ED-6A84-4659-9A05-12D43D75D0B3}" - ProjectSection(ProjectDependencies) = postProject - {B9CA039A-A0F1-4C87-A33E-3F959974BB3A} = {B9CA039A-A0F1-4C87-A33E-3F959974BB3A} - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AquaMai.Mods", "AquaMai.Mods/AquaMai.Mods.csproj", "{8731C0E0-53BE-4B1B-9828-193E738C6865}" - ProjectSection(ProjectDependencies) = postProject - {B9CA039A-A0F1-4C87-A33E-3F959974BB3A} = {B9CA039A-A0F1-4C87-A33E-3F959974BB3A} - {33C0D4ED-6A84-4659-9A05-12D43D75D0B3} = {33C0D4ED-6A84-4659-9A05-12D43D75D0B3} - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AquaMai", "AquaMai/AquaMai.csproj", "{788BC472-59F7-46F6-B760-65C18BA74389}" - ProjectSection(ProjectDependencies) = postProject - {4C0C68C3-8B2E-4CA8-A26D-AE87CF2A38A5} = {4C0C68C3-8B2E-4CA8-A26D-AE87CF2A38A5} - {33C0D4ED-6A84-4659-9A05-12D43D75D0B3} = {33C0D4ED-6A84-4659-9A05-12D43D75D0B3} - {8731C0E0-53BE-4B1B-9828-193E738C6865} = {8731C0E0-53BE-4B1B-9828-193E738C6865} - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Release|Any CPU = Release|Any CPU - Debug|Any CPU = Debug|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {50C59E41-2939-407E-9CB7-B186F33DDCF9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50C59E41-2939-407E-9CB7-B186F33DDCF9}.Release|Any CPU.Build.0 = Release|Any CPU - {50C59E41-2939-407E-9CB7-B186F33DDCF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50C59E41-2939-407E-9CB7-B186F33DDCF9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B9CA039A-A0F1-4C87-A33E-3F959974BB3A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B9CA039A-A0F1-4C87-A33E-3F959974BB3A}.Release|Any CPU.Build.0 = Release|Any CPU - {B9CA039A-A0F1-4C87-A33E-3F959974BB3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B9CA039A-A0F1-4C87-A33E-3F959974BB3A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6B5E1F3E-D012-4CFB-A2FA-26A6CE06BE66}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6B5E1F3E-D012-4CFB-A2FA-26A6CE06BE66}.Release|Any CPU.Build.0 = Release|Any CPU - {6B5E1F3E-D012-4CFB-A2FA-26A6CE06BE66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6B5E1F3E-D012-4CFB-A2FA-26A6CE06BE66}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4C0C68C3-8B2E-4CA8-A26D-AE87CF2A38A5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4C0C68C3-8B2E-4CA8-A26D-AE87CF2A38A5}.Release|Any CPU.Build.0 = Release|Any CPU - {4C0C68C3-8B2E-4CA8-A26D-AE87CF2A38A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4C0C68C3-8B2E-4CA8-A26D-AE87CF2A38A5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {33C0D4ED-6A84-4659-9A05-12D43D75D0B3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {33C0D4ED-6A84-4659-9A05-12D43D75D0B3}.Release|Any CPU.Build.0 = Release|Any CPU - {33C0D4ED-6A84-4659-9A05-12D43D75D0B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {33C0D4ED-6A84-4659-9A05-12D43D75D0B3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8731C0E0-53BE-4B1B-9828-193E738C6865}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8731C0E0-53BE-4B1B-9828-193E738C6865}.Release|Any CPU.Build.0 = Release|Any CPU - {8731C0E0-53BE-4B1B-9828-193E738C6865}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8731C0E0-53BE-4B1B-9828-193E738C6865}.Debug|Any CPU.Build.0 = Debug|Any CPU - {788BC472-59F7-46F6-B760-65C18BA74389}.Release|Any CPU.ActiveCfg = Release|Any CPU - {788BC472-59F7-46F6-B760-65C18BA74389}.Release|Any CPU.Build.0 = Release|Any CPU - {788BC472-59F7-46F6-B760-65C18BA74389}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {788BC472-59F7-46F6-B760-65C18BA74389}.Debug|Any CPU.Build.0 = Debug|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {DDF15A6C-2A44-4EBE-BD85-F3EE61DCD8BF} - EndGlobalSection -EndGlobal diff --git a/AquaMai/AquaMai/AquaMai.csproj b/AquaMai/AquaMai/AquaMai.csproj deleted file mode 100644 index ee3d948d..00000000 --- a/AquaMai/AquaMai/AquaMai.csproj +++ /dev/null @@ -1,74 +0,0 @@ - - - - Release - AnyCPU - {788BC472-59F7-46F6-B760-65C18BA74389} - Library - AquaMai - AquaMai - net472 - 512 - true - 12 - 414 - $(ProjectDir)../Libs/;$(AssemblySearchPaths) - $(ProjectDir)../Output/ - false - false - false - - - - false - None - true - prompt - 4 - true - false - - - - DEBUG - - - - - - - - - - - - - - - - - - - - - - - - - - - - AquaMai.Config.Interfaces.dll - - - AquaMai.Config.dll - - - AquaMai.Core.dll - - - AquaMai.Mods.dll - - - - diff --git a/AquaMai/AquaMai/AssemblyLoader.cs b/AquaMai/AquaMai/AssemblyLoader.cs deleted file mode 100644 index e5d21cd5..00000000 --- a/AquaMai/AquaMai/AssemblyLoader.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.IO.Compression; -using System.Reflection; - -namespace AquaMai; - -public static class AssemblyLoader -{ - public enum AssemblyName - { - ConfigInterfaces, - Config, - Core, - Mods, - } - - private static readonly Dictionary Assemblies = new() - { - [AssemblyName.ConfigInterfaces] = "AquaMai.Config.Interfaces.dll", - [AssemblyName.Config] = "AquaMai.Config.dll", - [AssemblyName.Core] = "AquaMai.Core.dll", - [AssemblyName.Mods] = "AquaMai.Mods.dll", - }; - - private static readonly Dictionary LoadedAssemblies = []; - - public static Assembly GetAssembly(AssemblyName assemblyName) => LoadedAssemblies[assemblyName]; - - public static void LoadAssemblies() - { - foreach (var (assemblyName, assemblyFileName) in Assemblies) - { -# if DEBUG - MelonLoader.MelonLogger.Msg($"Loading assembly \"{assemblyFileName}\"..."); -# endif - LoadedAssemblies[assemblyName] = LoadAssemblyFromResource(assemblyFileName); - } - } - - private static Assembly LoadAssemblyFromResource(string assemblyName) - { - var executingAssembly = Assembly.GetExecutingAssembly(); - using var decompressedStream = executingAssembly.GetManifestResourceStream(assemblyName); - if (decompressedStream != null) - { - return AppDomain.CurrentDomain.Load(StreamToBytes(decompressedStream)); - } - using var compressedStream = executingAssembly.GetManifestResourceStream($"{assemblyName}.compressed"); - if (compressedStream != null) - { - return AppDomain.CurrentDomain.Load(DecompressToBytes(compressedStream)); - } - throw new Exception($"Embedded assembly \"{assemblyName}\" not found."); - } - - private static byte[] StreamToBytes(Stream stream) - { - if (stream == null) - { - return []; - } - using var memoryStream = new MemoryStream(); - stream.CopyTo(memoryStream); - return memoryStream.ToArray(); - } - - private static byte[] DecompressToBytes(Stream stream) => StreamToBytes(new DeflateStream(stream, CompressionMode.Decompress)); -} diff --git a/AquaMai/AquaMai/BuildInfo.cs b/AquaMai/AquaMai/BuildInfo.cs deleted file mode 100644 index 7f2307b8..00000000 --- a/AquaMai/AquaMai/BuildInfo.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace AquaMai; - -public static class BuildInfo -{ - public const string Name = "AquaMai"; - public const string Description = "Mod for Sinmai"; - public const string Author = "Aza ft. Clansty ft. Menci"; - public const string Company = null; - public const string Version = "1.3.0"; - public const string DownloadLink = null; -} - diff --git a/AquaMai/AquaMai/Main.cs b/AquaMai/AquaMai/Main.cs deleted file mode 100644 index 505f057f..00000000 --- a/AquaMai/AquaMai/Main.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; -using MelonLoader; - -namespace AquaMai; - -public class AquaMai : MelonMod -{ - public const string AQUAMAI_SAY = """ - 如果你在 dnSpy / ILSpy 里看到了这行字,请从 resources 中解包 DLLs。 - If you see this line in dnSpy / ILSpy, please unpack the DLLs from resources. - """; - - [DllImport("kernel32.dll", SetLastError = true)] - private static extern bool SetConsoleOutputCP(uint wCodePageID); - - public override void OnInitializeMelon() - { - // Prevent Chinese characters from being garbled - SetConsoleOutputCP(65001); - - AssemblyLoader.LoadAssemblies(); - - var modsAssembly = AssemblyLoader.GetAssembly(AssemblyLoader.AssemblyName.Mods); - var coreAssembly = AssemblyLoader.GetAssembly(AssemblyLoader.AssemblyName.Core); - coreAssembly.GetType("AquaMai.Core.Startup") - .GetMethod("Initialize", BindingFlags.Public | BindingFlags.Static) - .Invoke(null, [modsAssembly, HarmonyInstance]); - } - - public override void OnGUI() - { - var coreAssembly = AssemblyLoader.GetAssembly(AssemblyLoader.AssemblyName.Core); - coreAssembly.GetType("AquaMai.Core.Startup") - .GetMethod("OnGUI", BindingFlags.Public | BindingFlags.Static) - .Invoke(null, []); - base.OnGUI(); - } -} diff --git a/AquaMai/AquaMai/Properties/AssemblyInfo.cs b/AquaMai/AquaMai/Properties/AssemblyInfo.cs deleted file mode 100644 index e566a535..00000000 --- a/AquaMai/AquaMai/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Reflection; -using MelonLoader; - -[assembly: AssemblyTitle(AquaMai.BuildInfo.Description)] -[assembly: AssemblyDescription(AquaMai.BuildInfo.Description)] -[assembly: AssemblyCompany(AquaMai.BuildInfo.Company)] -[assembly: AssemblyProduct(AquaMai.BuildInfo.Name)] -[assembly: AssemblyCopyright("Created by " + AquaMai.BuildInfo.Author)] -[assembly: AssemblyTrademark(AquaMai.BuildInfo.Company)] -[assembly: AssemblyVersion(AquaMai.BuildInfo.Version)] -[assembly: AssemblyFileVersion(AquaMai.BuildInfo.Version)] -[assembly: MelonInfo(typeof(AquaMai.AquaMai), AquaMai.BuildInfo.Name, AquaMai.BuildInfo.Version, AquaMai.BuildInfo.Author, AquaMai.BuildInfo.DownloadLink)] -[assembly: MelonColor()] -[assembly: HarmonyDontPatchAll] - -// Create and Setup a MelonGame Attribute to mark a Melon as Universal or Compatible with specific Games. -// If no MelonGame Attribute is found or any of the Values for any MelonGame Attribute on the Melon is null or empty it will be assumed the Melon is Universal. -// Values for MelonGame Attribute can be found in the Game's app.info file or printed at the top of every log directly beneath the Unity version. -[assembly: MelonGame(null, null)] diff --git a/AquaMai/Libs/.gitignore b/AquaMai/Libs/.gitignore deleted file mode 100644 index 0f6e1c13..00000000 --- a/AquaMai/Libs/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/Assembly-CSharp.dll -/AMDaemon.NET.dll diff --git a/AquaMai/Libs/0Harmony.dll b/AquaMai/Libs/0Harmony.dll deleted file mode 100644 index 0b9bdf1d..00000000 Binary files a/AquaMai/Libs/0Harmony.dll and /dev/null differ diff --git a/AquaMai/Libs/Assembly-CSharp-firstpass.dll b/AquaMai/Libs/Assembly-CSharp-firstpass.dll deleted file mode 100644 index 24a80f07..00000000 Binary files a/AquaMai/Libs/Assembly-CSharp-firstpass.dll and /dev/null differ diff --git a/AquaMai/Libs/MelonLoader.dll b/AquaMai/Libs/MelonLoader.dll deleted file mode 100644 index cf9f3987..00000000 Binary files a/AquaMai/Libs/MelonLoader.dll and /dev/null differ diff --git a/AquaMai/Libs/Mono.Cecil.dll b/AquaMai/Libs/Mono.Cecil.dll deleted file mode 100644 index 18735f93..00000000 Binary files a/AquaMai/Libs/Mono.Cecil.dll and /dev/null differ diff --git a/AquaMai/Libs/Mono.Posix.dll b/AquaMai/Libs/Mono.Posix.dll deleted file mode 100644 index 12224c3d..00000000 Binary files a/AquaMai/Libs/Mono.Posix.dll and /dev/null differ diff --git a/AquaMai/Libs/Mono.Security.dll b/AquaMai/Libs/Mono.Security.dll deleted file mode 100644 index ffa53e16..00000000 Binary files a/AquaMai/Libs/Mono.Security.dll and /dev/null differ diff --git a/AquaMai/Libs/System.Configuration.dll b/AquaMai/Libs/System.Configuration.dll deleted file mode 100644 index 62227321..00000000 Binary files a/AquaMai/Libs/System.Configuration.dll and /dev/null differ diff --git a/AquaMai/Libs/System.Core.dll b/AquaMai/Libs/System.Core.dll deleted file mode 100644 index 15a65e7e..00000000 Binary files a/AquaMai/Libs/System.Core.dll and /dev/null differ diff --git a/AquaMai/Libs/System.Numerics.dll b/AquaMai/Libs/System.Numerics.dll deleted file mode 100644 index 2ac827b8..00000000 Binary files a/AquaMai/Libs/System.Numerics.dll and /dev/null differ diff --git a/AquaMai/Libs/System.Security.dll b/AquaMai/Libs/System.Security.dll deleted file mode 100644 index 77c26fe8..00000000 Binary files a/AquaMai/Libs/System.Security.dll and /dev/null differ diff --git a/AquaMai/Libs/System.Xml.dll b/AquaMai/Libs/System.Xml.dll deleted file mode 100644 index fc607042..00000000 Binary files a/AquaMai/Libs/System.Xml.dll and /dev/null differ diff --git a/AquaMai/Libs/System.dll b/AquaMai/Libs/System.dll deleted file mode 100644 index eaa5b3a1..00000000 Binary files a/AquaMai/Libs/System.dll and /dev/null differ diff --git a/AquaMai/Libs/Unity.Analytics.DataPrivacy.dll b/AquaMai/Libs/Unity.Analytics.DataPrivacy.dll deleted file mode 100644 index 050c7d59..00000000 Binary files a/AquaMai/Libs/Unity.Analytics.DataPrivacy.dll and /dev/null differ diff --git a/AquaMai/Libs/Unity.TextMeshPro.dll b/AquaMai/Libs/Unity.TextMeshPro.dll deleted file mode 100644 index dff185c6..00000000 Binary files a/AquaMai/Libs/Unity.TextMeshPro.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.AIModule.dll b/AquaMai/Libs/UnityEngine.AIModule.dll deleted file mode 100644 index 6ab4fcf5..00000000 Binary files a/AquaMai/Libs/UnityEngine.AIModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.ARModule.dll b/AquaMai/Libs/UnityEngine.ARModule.dll deleted file mode 100644 index 1358670a..00000000 Binary files a/AquaMai/Libs/UnityEngine.ARModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.AccessibilityModule.dll b/AquaMai/Libs/UnityEngine.AccessibilityModule.dll deleted file mode 100644 index ac0d0768..00000000 Binary files a/AquaMai/Libs/UnityEngine.AccessibilityModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.AnimationModule.dll b/AquaMai/Libs/UnityEngine.AnimationModule.dll deleted file mode 100644 index 64a22601..00000000 Binary files a/AquaMai/Libs/UnityEngine.AnimationModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.AssetBundleModule.dll b/AquaMai/Libs/UnityEngine.AssetBundleModule.dll deleted file mode 100644 index 44938137..00000000 Binary files a/AquaMai/Libs/UnityEngine.AssetBundleModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.AudioModule.dll b/AquaMai/Libs/UnityEngine.AudioModule.dll deleted file mode 100644 index 5fc4a24d..00000000 Binary files a/AquaMai/Libs/UnityEngine.AudioModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.BaselibModule.dll b/AquaMai/Libs/UnityEngine.BaselibModule.dll deleted file mode 100644 index afd88972..00000000 Binary files a/AquaMai/Libs/UnityEngine.BaselibModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.ClothModule.dll b/AquaMai/Libs/UnityEngine.ClothModule.dll deleted file mode 100644 index ee35aa24..00000000 Binary files a/AquaMai/Libs/UnityEngine.ClothModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.ClusterInputModule.dll b/AquaMai/Libs/UnityEngine.ClusterInputModule.dll deleted file mode 100644 index 92d386a6..00000000 Binary files a/AquaMai/Libs/UnityEngine.ClusterInputModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.ClusterRendererModule.dll b/AquaMai/Libs/UnityEngine.ClusterRendererModule.dll deleted file mode 100644 index bbacc613..00000000 Binary files a/AquaMai/Libs/UnityEngine.ClusterRendererModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.CoreModule.dll b/AquaMai/Libs/UnityEngine.CoreModule.dll deleted file mode 100644 index eacaf1ed..00000000 Binary files a/AquaMai/Libs/UnityEngine.CoreModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.CrashReportingModule.dll b/AquaMai/Libs/UnityEngine.CrashReportingModule.dll deleted file mode 100644 index ec6fea6c..00000000 Binary files a/AquaMai/Libs/UnityEngine.CrashReportingModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.DirectorModule.dll b/AquaMai/Libs/UnityEngine.DirectorModule.dll deleted file mode 100644 index a06233fb..00000000 Binary files a/AquaMai/Libs/UnityEngine.DirectorModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.FileSystemHttpModule.dll b/AquaMai/Libs/UnityEngine.FileSystemHttpModule.dll deleted file mode 100644 index 62009f7e..00000000 Binary files a/AquaMai/Libs/UnityEngine.FileSystemHttpModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.GameCenterModule.dll b/AquaMai/Libs/UnityEngine.GameCenterModule.dll deleted file mode 100644 index b1cfc644..00000000 Binary files a/AquaMai/Libs/UnityEngine.GameCenterModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.GridModule.dll b/AquaMai/Libs/UnityEngine.GridModule.dll deleted file mode 100644 index 15fc11c9..00000000 Binary files a/AquaMai/Libs/UnityEngine.GridModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.HotReloadModule.dll b/AquaMai/Libs/UnityEngine.HotReloadModule.dll deleted file mode 100644 index 87329c3c..00000000 Binary files a/AquaMai/Libs/UnityEngine.HotReloadModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.IMGUIModule.dll b/AquaMai/Libs/UnityEngine.IMGUIModule.dll deleted file mode 100644 index 58fac971..00000000 Binary files a/AquaMai/Libs/UnityEngine.IMGUIModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.ImageConversionModule.dll b/AquaMai/Libs/UnityEngine.ImageConversionModule.dll deleted file mode 100644 index dfe7980d..00000000 Binary files a/AquaMai/Libs/UnityEngine.ImageConversionModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.InputModule.dll b/AquaMai/Libs/UnityEngine.InputModule.dll deleted file mode 100644 index 45947b88..00000000 Binary files a/AquaMai/Libs/UnityEngine.InputModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.JSONSerializeModule.dll b/AquaMai/Libs/UnityEngine.JSONSerializeModule.dll deleted file mode 100644 index 3189412e..00000000 Binary files a/AquaMai/Libs/UnityEngine.JSONSerializeModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.LocalizationModule.dll b/AquaMai/Libs/UnityEngine.LocalizationModule.dll deleted file mode 100644 index 39d5a946..00000000 Binary files a/AquaMai/Libs/UnityEngine.LocalizationModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.Networking.dll b/AquaMai/Libs/UnityEngine.Networking.dll deleted file mode 100644 index 8379c161..00000000 Binary files a/AquaMai/Libs/UnityEngine.Networking.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.ParticleSystemModule.dll b/AquaMai/Libs/UnityEngine.ParticleSystemModule.dll deleted file mode 100644 index e4bb233d..00000000 Binary files a/AquaMai/Libs/UnityEngine.ParticleSystemModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.PerformanceReportingModule.dll b/AquaMai/Libs/UnityEngine.PerformanceReportingModule.dll deleted file mode 100644 index 19f23381..00000000 Binary files a/AquaMai/Libs/UnityEngine.PerformanceReportingModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.Physics2DModule.dll b/AquaMai/Libs/UnityEngine.Physics2DModule.dll deleted file mode 100644 index 102e4abe..00000000 Binary files a/AquaMai/Libs/UnityEngine.Physics2DModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.PhysicsModule.dll b/AquaMai/Libs/UnityEngine.PhysicsModule.dll deleted file mode 100644 index c22fbe7f..00000000 Binary files a/AquaMai/Libs/UnityEngine.PhysicsModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.ProfilerModule.dll b/AquaMai/Libs/UnityEngine.ProfilerModule.dll deleted file mode 100644 index 7b22cc35..00000000 Binary files a/AquaMai/Libs/UnityEngine.ProfilerModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.ScreenCaptureModule.dll b/AquaMai/Libs/UnityEngine.ScreenCaptureModule.dll deleted file mode 100644 index f09c93d5..00000000 Binary files a/AquaMai/Libs/UnityEngine.ScreenCaptureModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.SharedInternalsModule.dll b/AquaMai/Libs/UnityEngine.SharedInternalsModule.dll deleted file mode 100644 index ecce59c3..00000000 Binary files a/AquaMai/Libs/UnityEngine.SharedInternalsModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.SpatialTracking.dll b/AquaMai/Libs/UnityEngine.SpatialTracking.dll deleted file mode 100644 index 4b3beafe..00000000 Binary files a/AquaMai/Libs/UnityEngine.SpatialTracking.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.SpriteMaskModule.dll b/AquaMai/Libs/UnityEngine.SpriteMaskModule.dll deleted file mode 100644 index 266175b0..00000000 Binary files a/AquaMai/Libs/UnityEngine.SpriteMaskModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.SpriteShapeModule.dll b/AquaMai/Libs/UnityEngine.SpriteShapeModule.dll deleted file mode 100644 index 2369af5b..00000000 Binary files a/AquaMai/Libs/UnityEngine.SpriteShapeModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.StreamingModule.dll b/AquaMai/Libs/UnityEngine.StreamingModule.dll deleted file mode 100644 index 0dc07478..00000000 Binary files a/AquaMai/Libs/UnityEngine.StreamingModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.StyleSheetsModule.dll b/AquaMai/Libs/UnityEngine.StyleSheetsModule.dll deleted file mode 100644 index 0c271909..00000000 Binary files a/AquaMai/Libs/UnityEngine.StyleSheetsModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.SubstanceModule.dll b/AquaMai/Libs/UnityEngine.SubstanceModule.dll deleted file mode 100644 index 067a2448..00000000 Binary files a/AquaMai/Libs/UnityEngine.SubstanceModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.TLSModule.dll b/AquaMai/Libs/UnityEngine.TLSModule.dll deleted file mode 100644 index 301afab5..00000000 Binary files a/AquaMai/Libs/UnityEngine.TLSModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.TerrainModule.dll b/AquaMai/Libs/UnityEngine.TerrainModule.dll deleted file mode 100644 index fa36a6b9..00000000 Binary files a/AquaMai/Libs/UnityEngine.TerrainModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.TerrainPhysicsModule.dll b/AquaMai/Libs/UnityEngine.TerrainPhysicsModule.dll deleted file mode 100644 index 96430d32..00000000 Binary files a/AquaMai/Libs/UnityEngine.TerrainPhysicsModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.TextCoreModule.dll b/AquaMai/Libs/UnityEngine.TextCoreModule.dll deleted file mode 100644 index 72e905fe..00000000 Binary files a/AquaMai/Libs/UnityEngine.TextCoreModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.TextRenderingModule.dll b/AquaMai/Libs/UnityEngine.TextRenderingModule.dll deleted file mode 100644 index dd60f45d..00000000 Binary files a/AquaMai/Libs/UnityEngine.TextRenderingModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.TilemapModule.dll b/AquaMai/Libs/UnityEngine.TilemapModule.dll deleted file mode 100644 index 7fd12a63..00000000 Binary files a/AquaMai/Libs/UnityEngine.TilemapModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.Timeline.dll b/AquaMai/Libs/UnityEngine.Timeline.dll deleted file mode 100644 index 357d2a92..00000000 Binary files a/AquaMai/Libs/UnityEngine.Timeline.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.TimelineModule.dll b/AquaMai/Libs/UnityEngine.TimelineModule.dll deleted file mode 100644 index 0946bcc4..00000000 Binary files a/AquaMai/Libs/UnityEngine.TimelineModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.UI.dll b/AquaMai/Libs/UnityEngine.UI.dll deleted file mode 100644 index 111e5e50..00000000 Binary files a/AquaMai/Libs/UnityEngine.UI.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.UIElementsModule.dll b/AquaMai/Libs/UnityEngine.UIElementsModule.dll deleted file mode 100644 index d3f1ce84..00000000 Binary files a/AquaMai/Libs/UnityEngine.UIElementsModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.UIModule.dll b/AquaMai/Libs/UnityEngine.UIModule.dll deleted file mode 100644 index f7ff46ca..00000000 Binary files a/AquaMai/Libs/UnityEngine.UIModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.UNETModule.dll b/AquaMai/Libs/UnityEngine.UNETModule.dll deleted file mode 100644 index 5c9f9ebb..00000000 Binary files a/AquaMai/Libs/UnityEngine.UNETModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.UmbraModule.dll b/AquaMai/Libs/UnityEngine.UmbraModule.dll deleted file mode 100644 index 7dd8d10f..00000000 Binary files a/AquaMai/Libs/UnityEngine.UmbraModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.UnityAnalyticsModule.dll b/AquaMai/Libs/UnityEngine.UnityAnalyticsModule.dll deleted file mode 100644 index 79835fff..00000000 Binary files a/AquaMai/Libs/UnityEngine.UnityAnalyticsModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.UnityConnectModule.dll b/AquaMai/Libs/UnityEngine.UnityConnectModule.dll deleted file mode 100644 index e11aaf81..00000000 Binary files a/AquaMai/Libs/UnityEngine.UnityConnectModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.UnityTestProtocolModule.dll b/AquaMai/Libs/UnityEngine.UnityTestProtocolModule.dll deleted file mode 100644 index 12739c5f..00000000 Binary files a/AquaMai/Libs/UnityEngine.UnityTestProtocolModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.UnityWebRequestAssetBundleModule.dll b/AquaMai/Libs/UnityEngine.UnityWebRequestAssetBundleModule.dll deleted file mode 100644 index 7a04c056..00000000 Binary files a/AquaMai/Libs/UnityEngine.UnityWebRequestAssetBundleModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.UnityWebRequestAudioModule.dll b/AquaMai/Libs/UnityEngine.UnityWebRequestAudioModule.dll deleted file mode 100644 index 1cdb9fde..00000000 Binary files a/AquaMai/Libs/UnityEngine.UnityWebRequestAudioModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.UnityWebRequestModule.dll b/AquaMai/Libs/UnityEngine.UnityWebRequestModule.dll deleted file mode 100644 index 0d537183..00000000 Binary files a/AquaMai/Libs/UnityEngine.UnityWebRequestModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.UnityWebRequestTextureModule.dll b/AquaMai/Libs/UnityEngine.UnityWebRequestTextureModule.dll deleted file mode 100644 index 40fb48a2..00000000 Binary files a/AquaMai/Libs/UnityEngine.UnityWebRequestTextureModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.UnityWebRequestWWWModule.dll b/AquaMai/Libs/UnityEngine.UnityWebRequestWWWModule.dll deleted file mode 100644 index ca0730fd..00000000 Binary files a/AquaMai/Libs/UnityEngine.UnityWebRequestWWWModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.VFXModule.dll b/AquaMai/Libs/UnityEngine.VFXModule.dll deleted file mode 100644 index 1b245f4f..00000000 Binary files a/AquaMai/Libs/UnityEngine.VFXModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.VRModule.dll b/AquaMai/Libs/UnityEngine.VRModule.dll deleted file mode 100644 index 76bb2f7b..00000000 Binary files a/AquaMai/Libs/UnityEngine.VRModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.VehiclesModule.dll b/AquaMai/Libs/UnityEngine.VehiclesModule.dll deleted file mode 100644 index 6d20013e..00000000 Binary files a/AquaMai/Libs/UnityEngine.VehiclesModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.VideoModule.dll b/AquaMai/Libs/UnityEngine.VideoModule.dll deleted file mode 100644 index 269a630d..00000000 Binary files a/AquaMai/Libs/UnityEngine.VideoModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.WindModule.dll b/AquaMai/Libs/UnityEngine.WindModule.dll deleted file mode 100644 index 944cf230..00000000 Binary files a/AquaMai/Libs/UnityEngine.WindModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.XRModule.dll b/AquaMai/Libs/UnityEngine.XRModule.dll deleted file mode 100644 index 05aaeee2..00000000 Binary files a/AquaMai/Libs/UnityEngine.XRModule.dll and /dev/null differ diff --git a/AquaMai/Libs/UnityEngine.dll b/AquaMai/Libs/UnityEngine.dll deleted file mode 100644 index ca0742f2..00000000 Binary files a/AquaMai/Libs/UnityEngine.dll and /dev/null differ diff --git a/AquaMai/Libs/mscorlib.dll b/AquaMai/Libs/mscorlib.dll deleted file mode 100644 index 42a26818..00000000 Binary files a/AquaMai/Libs/mscorlib.dll and /dev/null differ diff --git a/AquaMai/README.md b/AquaMai/README.md deleted file mode 100644 index e0392764..00000000 --- a/AquaMai/README.md +++ /dev/null @@ -1,41 +0,0 @@ -## AquaMai - -This mod is heavily WIP. More details will be added as the development progresses. - -### Features - -**Cheats** - -* Unlock all tickets - -**UX Optimization** - -* Remove the starting logo and warning cutscene -* Single Player (1P) mode -* Skip from card scanning directly to music selection (experimental) -* Disable daily automatic reboot -* Customize version text -* Skip the current song by holding 7 -* Skip "new event" and "information" screen for new players. - -**Bug Fixes** - -* Fix crash in the character selection screen - -**Performance** - -* Speed up things - -### Development - -1. Copy `Assembly-CSharp.dll` to `Libs` folder. -2. Install [.NET Framework 4.7.2 Developer Pack](https://dotnet.microsoft.com/en-us/download/dotnet-framework/thank-you/net472-developer-pack-offline-installer) -3. Open `AquaMai.sln` in JetBrains Rider. -4. Build the solution. -5. Copy `Output/AquaMai.dll` to `Mods` folder. -6. Configure and copy `AquaMai.toml` to the same folder as your game executable: `Sinmai.exe` - -### Relevant Links - -* [MelonLoader Wiki](https://melonwiki.xyz/#/modders/quickstart) -* [Harmony Docs](https://harmony.pardeike.net/articles/patching-prefix.html) diff --git a/README.md b/README.md index 5434fe52..2cc69702 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This is an attempt to rebuild the [original Aqua server](https://dev.s-ul.net/Ne ## Related Projects -* [AquaMai](./AquaMai): A maimai DX mod that adds many features to the game. +* [AquaMai](https://github.com/MewoLab/AquaMai): A maimai DX mod that adds many features to the game. * [AquaNet](./AquaNet): A new web frontend for the modern age. ### Supported Games