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