[RF] AquaMai configuration refactor (#82)

更新了配置文件格式,原有的配置文件将被自动无缝迁移,详情请见新的配置文件中的注释(例外:`SlideJudgeTweak` 不再默认启用)
旧配置文件将被重命名备份,如果更新到此版本遇到 Bug 请联系我们

Updated configuration file schema. The old config file will be migrated automatically and seamlessly. See the comments in the new configuration file for details. (Except for `SlideJudgeTweak` is no longer enabled by default)
Your old configuration file will be renamed as a backup. If you encounter any bug with this version, please contact us.
This commit is contained in:
Menci
2024-11-25 01:25:19 +08:00
committed by GitHub
parent e9ee31b22a
commit 37044dae01
217 changed files with 6051 additions and 3040 deletions

View File

@@ -0,0 +1,146 @@
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<TMP_FontAsset> fontAssets = [];
private static readonly List<TMP_FontAsset> processedFonts = [];
private static TMP_FontAsset replacementFontAsset;
private static List<TMP_FontAsset> fallbackFontAssets = [];
public static void OnBeforePatch()
{
var paths = Fonts.paths
.Split(';')
.Where(p => !string.IsNullOrWhiteSpace(p))
.Select(FileSystem.ResolvePath);
var fonts = paths
.Select(p =>
{
var font = new Font(p);
if (font == null)
{
MelonLogger.Warning($"[Fonts] Font not found: {p}");
}
return font;
})
.Where(f => f != null);
fontAssets = fonts
.Select(f => TMP_FontAsset.CreateFontAsset(f, 90, 9, GlyphRenderMode.SDFAA, 8192, 8192))
.ToList();
if (fontAssets.Count == 0)
{
MelonLogger.Warning("[Fonts] No font loaded.");
}
else if (addAsFallback)
{
fallbackFontAssets = fontAssets;
}
else
{
replacementFontAsset = fontAssets[0];
fallbackFontAssets = fontAssets.Skip(1).ToList();
}
}
[HarmonyPatch(typeof(TextMeshProUGUI), "Awake")]
[HarmonyPostfix]
public static void PostAwake(TextMeshProUGUI __instance)
{
if (fontAssets.Count == 0) return;
if (processedFonts.Contains(__instance.font)) return;
if (replacementFontAsset != null)
{
ProcessReplacement(__instance);
}
if (fallbackFontAssets.Count > 0)
{
ProcessFallback(__instance);
}
processedFonts.Add(__instance.font);
}
private static void ProcessReplacement(TextMeshProUGUI __instance)
{
# if DEBUG
MelonLogger.Msg($"{__instance.font.name} {__instance.text}");
# endif
var materialOrigin = __instance.fontMaterial;
var materialSharedOrigin = __instance.fontSharedMaterial;
__instance.font = replacementFontAsset;
# if DEBUG
MelonLogger.Msg($"shaderKeywords {materialOrigin.shaderKeywords.Join()} {__instance.fontMaterial.shaderKeywords.Join()}");
# endif
// __instance.fontSharedMaterial = materialSharedOrigin;
// 这样之后该有描边的地方整个字后面都是阴影,它不知道哪里是边
// materialOrigin.mainTexture = __instance.fontMaterial.mainTexture;
// materialOrigin.mainTextureOffset = __instance.fontMaterial.mainTextureOffset;
// materialOrigin.mainTextureScale = __instance.fontMaterial.mainTextureScale;
// __instance.fontMaterial.CopyPropertiesFromMaterial(materialOrigin);
// 这样了之后有描边了,但是描边很细
// __instance.fontMaterial.shader = materialOrigin.shader;
foreach (var keyword in materialOrigin.shaderKeywords)
{
__instance.fontMaterial.EnableKeyword(keyword);
}
// __instance.fontMaterial.globalIlluminationFlags = materialOrigin.globalIlluminationFlags;
// 原来是 underlay但是复制这三个属性之后就又变成整个字后面都是阴影了
// __instance.fontMaterial.SetFloat(ShaderUtilities.ID_UnderlayOffsetY, materialOrigin.GetFloat(ShaderUtilities.ID_UnderlayOffsetY));
// __instance.fontMaterial.SetFloat(ShaderUtilities.ID_UnderlayOffsetX, materialOrigin.GetFloat(ShaderUtilities.ID_UnderlayOffsetX));
// __instance.fontMaterial.SetFloat(ShaderUtilities.ID_UnderlayDilate, materialOrigin.GetFloat(ShaderUtilities.ID_UnderlayDilate));
// if(materialOrigin.shaderKeywords.Contains(ShaderUtilities.Keyword_Underlay))
// {
// __instance.fontMaterial.EnableKeyword(ShaderUtilities.Keyword_Glow);
// __instance.fontMaterial.SetFloat(ShaderUtilities.ID_GlowOuter, .5f);
// // __instance.fontMaterial.SetFloat(ShaderUtilities.ID_UnderlayOffsetX, materialOrigin.GetFloat(ShaderUtilities.ID_UnderlayOffsetX));
// }
}
private static void ProcessFallback(TextMeshProUGUI __instance)
{
foreach (var fontAsset in fallbackFontAssets)
{
__instance.font.fallbackFontAssetTable.Add(fontAsset);
}
}
}

View File

@@ -0,0 +1,38 @@
using System.Collections.Generic;
using System.Linq;
using HarmonyLib;
using UnityEngine;
using Manager;
using Util;
using AquaMai.Config.Attributes;
namespace AquaMai.Mods.GameSystem.Assets;
[ConfigSection(
en: "Load all existing \".ab\" image resources regardless of the AssetBundleImages manifest.",
zh: """
加载所有存在的 .ab 图片资源(无视 AssetBundleImages.manifest
导入了删除曲包之类的话,应该需要开启这个
""")]
public class LoadAssetBundleWithoutManifest
{
private static HashSet<string> abFiles = new HashSet<string>();
[HarmonyPostfix]
[HarmonyPatch(typeof(OptionDataManager), "CheckAssetBundle")]
public static void PostCheckAssetBundle(ref Safe.ReadonlySortedDictionary<string, string> abs)
{
foreach (var ab in abs)
{
abFiles.Add(ab.Key);
}
}
[HarmonyPrefix]
[HarmonyPatch(typeof(AssetBundleManifest), "GetAllAssetBundles")]
public static bool PreGetAllAssetBundles(AssetBundleManifest __instance, ref string[] __result)
{
__result = abFiles.ToArray();
return false;
}
}

View File

@@ -0,0 +1,577 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using HarmonyLib;
using UnityEngine;
using System.Text.RegularExpressions;
using MAI2.Util;
using Manager;
using MelonLoader;
using Monitor;
using AquaMai.Config.Attributes;
using AquaMai.Core.Helpers;
namespace AquaMai.Mods.GameSystem.Assets;
[ConfigSection(
en: "Load asset images from the configured directory (for self-made charts).",
zh: "从指定目录下加载资源图片(自制谱用)")]
public class LoadLocalImages
{
[ConfigEntry]
private static readonly string localAssetsDir = "LocalAssets";
private static readonly string[] imageExts = [".jpg", ".png", ".jpeg"];
private static readonly Dictionary<string, string> jacketPaths = [];
private static readonly Dictionary<string, string> framePaths = [];
private static readonly Dictionary<string, string> platePaths = [];
private static readonly Dictionary<string, string> framemaskPaths = [];
private static readonly Dictionary<string, string> framepatternPaths = [];
private static readonly Dictionary<string, string> iconPaths = [];
private static readonly Dictionary<string, string> charaPaths = [];
private static readonly Dictionary<string, string> partnerPaths = [];
//private static readonly Dictionary<string, string> navicharaPaths = [];
private static readonly Dictionary<string, string> tabTitlePaths = [];
private static readonly Dictionary<string, string> localAssetsContents = [];
[HarmonyPrefix]
[HarmonyPatch(typeof(DataManager), "LoadMusicBase")]
public static void LoadMusicPostfix(List<string> ____targetDirs)
{
foreach (var aDir in ____targetDirs)
{
if (Directory.Exists(Path.Combine(aDir, @"AssetBundleImages\jacket")))
foreach (var file in Directory.GetFiles(Path.Combine(aDir, @"AssetBundleImages\jacket")))
{
if (!imageExts.Contains(Path.GetExtension(file).ToLowerInvariant())) continue;
var idStr = Path.GetFileName(file).Substring("ui_jacket_".Length, 6);
jacketPaths[idStr] = file;
}
if (Directory.Exists(Path.Combine(aDir, @"AssetBundleImages\frame")))
foreach (var file in Directory.GetFiles(Path.Combine(aDir, @"AssetBundleImages\frame")))
{
if (!imageExts.Contains(Path.GetExtension(file).ToLowerInvariant())) continue;
var idStr = Path.GetFileName(file).Substring("ui_frame_".Length, 6);
framePaths[idStr] = file;
}
if (Directory.Exists(Path.Combine(aDir, @"AssetBundleImages\nameplate")))
foreach (var file in Directory.GetFiles(Path.Combine(aDir, @"AssetBundleImages\nameplate")))
{
if (!imageExts.Contains(Path.GetExtension(file).ToLowerInvariant())) continue;
var idStr = Path.GetFileName(file).Substring("ui_plate_".Length, 6);
platePaths[idStr] = file;
}
if (Directory.Exists(Path.Combine(aDir, @"AssetBundleImages\framemask")))
foreach (var file in Directory.GetFiles(Path.Combine(aDir, @"AssetBundleImages\framemask")))
{
if (!imageExts.Contains(Path.GetExtension(file).ToLowerInvariant())) continue;
var idStr = Path.GetFileName(file).Substring("ui_framemask_".Length, 6);
framemaskPaths[idStr] = file;
}
if (Directory.Exists(Path.Combine(aDir, @"AssetBundleImages\framepattern")))
foreach (var file in Directory.GetFiles(Path.Combine(aDir, @"AssetBundleImages\framepattern")))
{
if (!imageExts.Contains(Path.GetExtension(file).ToLowerInvariant())) continue;
var idStr = Path.GetFileName(file).Substring("ui_framepattern_".Length, 6);
framepatternPaths[idStr] = file;
}
if (Directory.Exists(Path.Combine(aDir, @"AssetBundleImages\icon")))
foreach (var file in Directory.GetFiles(Path.Combine(aDir, @"AssetBundleImages\icon")))
{
if (!imageExts.Contains(Path.GetExtension(file).ToLowerInvariant())) continue;
var idStr = Path.GetFileName(file).Substring("ui_icon_".Length, 6);
iconPaths[idStr] = file;
}
if (Directory.Exists(Path.Combine(aDir, @"AssetBundleImages\chara")))
foreach (var file in Directory.GetFiles(Path.Combine(aDir, @"AssetBundleImages\chara")))
{
if (!imageExts.Contains(Path.GetExtension(file).ToLowerInvariant())) continue;
var idStr = Path.GetFileName(file).Substring("ui_chara_".Length, 6);
charaPaths[idStr] = file;
}
if (Directory.Exists(Path.Combine(aDir, @"AssetBundleImages\partner")))
foreach (var file in Directory.GetFiles(Path.Combine(aDir, @"AssetBundleImages\partner")))
{
if (!imageExts.Contains(Path.GetExtension(file).ToLowerInvariant())) continue;
var idStr = Path.GetFileName(file).Substring("ui_Partner_".Length, 6);
partnerPaths[idStr] = file;
}
//if (Directory.Exists(Path.Combine(aDir, @"AssetBundleImages\navichara\sprite\parts\ui_navichara_21")))
// foreach (var file in Directory.GetFiles(Path.Combine(aDir, @"AssetBundleImages\navichara\sprite\parts\ui_navichara_", charaid)))
//{
// if (!imageExts.Contains(Path.GetExtension(file).ToLowerInvariant())) continue;
//var idStr = Path.GetFileName(file).Substring("ui_navichara_".Length, 6);
// navicharaPaths[idStr] = file;
// }
if (Directory.Exists(Path.Combine(aDir, @"Common\Sprites\Tab\Title")))
foreach (var file in Directory.GetFiles(Path.Combine(aDir, @"Common\Sprites\Tab\Title")))
{
if (!imageExts.Contains(Path.GetExtension(file).ToLowerInvariant())) continue;
tabTitlePaths[Path.GetFileNameWithoutExtension(file).ToLowerInvariant()] = file;
}
}
MelonLogger.Msg($"[LoadLocalImages] Loaded {jacketPaths.Count} Jacket, {platePaths.Count} NamePlate, {framePaths.Count} Frame, {framemaskPaths.Count} FrameMask, {framepatternPaths.Count} FramePattern, {iconPaths.Count} Icon, {charaPaths.Count} Chara, {partnerPaths.Count} PartnerLogo, {tabTitlePaths.Count} Tab Titles from AssetBundleImages.");
var resolvedDir = FileSystem.ResolvePath(localAssetsDir);
if (Directory.Exists(resolvedDir))
foreach (var laFile in Directory.EnumerateFiles(resolvedDir))
{
if (!imageExts.Contains(Path.GetExtension(laFile).ToLowerInvariant())) continue;
localAssetsContents[Path.GetFileNameWithoutExtension(laFile).ToLowerInvariant()] = laFile;
}
MelonLogger.Msg($"[LoadLocalImages] Loaded {localAssetsContents.Count} LocalAssets.");
}
private static string GetJacketPath(string id)
{
return localAssetsContents.TryGetValue(id, out var laPath) ? laPath : jacketPaths.GetValueOrDefault(id);
}
public static Texture2D GetJacketTexture2D(string id)
{
var path = GetJacketPath(id);
if (path == null)
{
return null;
}
var texture = new Texture2D(1, 1, TextureFormat.RGBA32, false);
texture.LoadImage(File.ReadAllBytes(path));
return texture;
}
public static Texture2D GetJacketTexture2D(int id)
{
return GetJacketTexture2D($"{id:000000}");
}
private static string GetFramePath(string id)
{
return framePaths.GetValueOrDefault(id);
}
public static Texture2D GetFrameTexture2D(string id)
{
var path = GetFramePath(id);
if (path == null)
{
return null;
}
var texture = new Texture2D(1, 1, TextureFormat.RGBA32, false);
texture.LoadImage(File.ReadAllBytes(path));
return texture;
}
private static string GetPlatePath(string id)
{
return platePaths.GetValueOrDefault(id);
}
public static Texture2D GetPlateTexture2D(string id)
{
var path = GetPlatePath(id);
if (path == null)
{
return null;
}
var texture = new Texture2D(1, 1, TextureFormat.RGBA32, false);
texture.LoadImage(File.ReadAllBytes(path));
return texture;
}
private static string GetFrameMaskPath(string id)
{
return framemaskPaths.GetValueOrDefault(id);
}
public static Texture2D GetFrameMaskTexture2D(string id)
{
var path = GetFrameMaskPath(id);
if (path == null)
{
return null;
}
var texture = new Texture2D(1, 1, TextureFormat.RGBA32, false);
texture.LoadImage(File.ReadAllBytes(path));
return texture;
}
private static string GetFramePatternPath(string id)
{
return framepatternPaths.GetValueOrDefault(id);
}
public static Texture2D GetFramePatternTexture2D(string id)
{
var path = GetFramePatternPath(id);
if (path == null)
{
return null;
}
var texture = new Texture2D(1, 1, TextureFormat.RGBA32, false);
texture.LoadImage(File.ReadAllBytes(path));
return texture;
}
private static string GetIconPath(string id)
{
return iconPaths.GetValueOrDefault(id);
}
public static Texture2D GetIconTexture2D(string id)
{
var path = GetIconPath(id);
if (path == null)
{
return null;
}
var texture = new Texture2D(1, 1, TextureFormat.RGBA32, false);
texture.LoadImage(File.ReadAllBytes(path));
return texture;
}
private static string GetCharaPath(string id)
{
return charaPaths.GetValueOrDefault(id);
}
public static Texture2D GetCharaTexture2D(string id)
{
var path = GetCharaPath(id);
if (path == null)
{
return null;
}
var texture = new Texture2D(1, 1, TextureFormat.RGBA32, false);
texture.LoadImage(File.ReadAllBytes(path));
return texture;
}
private static string GetPartnerPath(string id)
{
return partnerPaths.GetValueOrDefault(id);
}
public static Texture2D GetPartnerTexture2D(string id)
{
var path = GetPartnerPath(id);
if (path == null)
{
return null;
}
var texture = new Texture2D(1, 1, TextureFormat.RGBA32, false);
texture.LoadImage(File.ReadAllBytes(path));
return texture;
}
/*
[HarmonyPatch]
public static class TabTitleLoader
{
public static IEnumerable<MethodBase> TargetMethods()
{
// Fxxk unity
// game load tab title by call Resources.Load<Sprite> directly
// patching Resources.Load<Sprite> need this stuff
// var method = typeof(Resources).GetMethods(BindingFlags.Public | BindingFlags.Static).First(it => it.Name == "Load" && it.IsGenericMethod).MakeGenericMethod(typeof(Sprite));
// return [method];
// but it not work, game will blackscreen if add prefix or postfix
//
// patching AssetBundleManager.LoadAsset will lead game memory error
// return [AccessTools.Method(typeof(AssetBundleManager), "LoadAsset", [typeof(string)], [typeof(Object)])];
// and this is not work because game not using this
//
// we load them manually after game load and no need to hook the load progress
}
public static bool Prefix(string path, ref Object __result)
{
if (!path.StartsWith("Common/Sprites/Tab/Title/")) return true;
var filename = Path.GetFileNameWithoutExtension(path).ToLowerInvariant();
var locPath = localAssetsContents.TryGetValue(filename, out var laPath) ? laPath : tabTitlePaths.GetValueOrDefault(filename);
if (locPath is null) return true;
var texture = new Texture2D(1, 1);
texture.LoadImage(File.ReadAllBytes(locPath));
__result = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f));
MelonLogger.Msg($"GetTabTitleSpritePrefix {locPath} {__result}");
return false;
}
}
*/
[HarmonyPostfix]
[HarmonyPatch(typeof(MusicSelectMonitor), "Initialize")]
public static void TabTitleLoader(MusicSelectMonitor __instance, Dictionary<int, Sprite> ____genreSprite, Dictionary<int, Sprite> ____versionSprite)
{
var genres = Singleton<DataManager>.Instance.GetMusicGenres();
foreach (var (id, genre) in genres)
{
if (____genreSprite.GetValueOrDefault(id) is not null) continue;
var filename = genre.FileName.ToLowerInvariant();
var locPath = localAssetsContents.TryGetValue(filename, out var laPath) ? laPath : tabTitlePaths.GetValueOrDefault(filename);
if (locPath is null) continue;
var texture = new Texture2D(1, 1, TextureFormat.RGBA32, false);
texture.LoadImage(File.ReadAllBytes(locPath));
____genreSprite[id] = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f));
}
var versions = Singleton<DataManager>.Instance.GetMusicVersions();
foreach (var (id, version) in versions)
{
if (____versionSprite.GetValueOrDefault(id) is not null) continue;
var filename = version.FileName.ToLowerInvariant();
var locPath = localAssetsContents.TryGetValue(filename, out var laPath) ? laPath : tabTitlePaths.GetValueOrDefault(filename);
if (locPath is null) continue;
var texture = new Texture2D(1, 1, TextureFormat.RGBA32, false);
texture.LoadImage(File.ReadAllBytes(locPath));
____versionSprite[id] = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f));
}
}
[HarmonyPatch]
public static class JacketLoader
{
public static IEnumerable<MethodBase> TargetMethods()
{
var AM = typeof(AssetManager);
return [AM.GetMethod("GetJacketThumbTexture2D", [typeof(string)]), AM.GetMethod("GetJacketTexture2D", [typeof(string)])];
}
public static bool Prefix(string filename, ref Texture2D __result, AssetManager __instance)
{
var matches = Regex.Matches(filename, @"UI_Jacket_(\d+)(_s)?\.png");
if (matches.Count < 1)
{
return true;
}
var id = matches[0].Groups[1].Value;
var texture = GetJacketTexture2D(id);
__result = texture ?? __instance.LoadAsset<Texture2D>($"Jacket/UI_Jacket_{id}.png");
return false;
}
}
[HarmonyPatch]
public static class FrameLoader
{
public static IEnumerable<MethodBase> TargetMethods()
{
var AM = typeof(AssetManager);
return [AM.GetMethod("GetFrameThumbTexture2D", [typeof(string)]), AM.GetMethod("GetFrameTexture2D", [typeof(string)])];
}
public static bool Prefix(string filename, ref Texture2D __result, AssetManager __instance)
{
var matches = Regex.Matches(filename, @"UI_Frame_(\d+)(_s)?\.png");
if (matches.Count < 1)
{
return true;
}
var id = matches[0].Groups[1].Value;
var texture = GetFrameTexture2D(id);
__result = texture ?? __instance.LoadAsset<Texture2D>($"Frame/UI_Frame_{id}.png");
return false;
}
}
[HarmonyPatch]
public static class PlateLoader
{
public static IEnumerable<MethodBase> TargetMethods()
{
var AM = typeof(AssetManager);
return [AM.GetMethod("GetPlateTexture2D", [typeof(string)])];
}
public static bool Prefix(string filename, ref Texture2D __result, AssetManager __instance)
{
var matches = Regex.Matches(filename, @"UI_Plate_(\d+)\.png");
if (matches.Count < 1)
{
return true;
}
var id = matches[0].Groups[1].Value;
var texture = GetPlateTexture2D(id);
__result = texture ?? __instance.LoadAsset<Texture2D>($"NamePlate/UI_Plate_{id}.png");
return false;
}
}
[HarmonyPatch]
public static class FrameMaskLoader
{
public static IEnumerable<MethodBase> TargetMethods()
{
var AM = typeof(AssetManager);
return [AM.GetMethod("GetFrameMaskTexture2D", [typeof(string)])];
}
public static bool Prefix(string filename, ref Texture2D __result, AssetManager __instance)
{
var matches = Regex.Matches(filename, @"UI_FrameMask_(\d+)\.png");
if (matches.Count < 1)
{
return true;
}
var id = matches[0].Groups[1].Value;
var texture = GetFrameMaskTexture2D(id);
__result = texture ?? __instance.LoadAsset<Texture2D>($"FrameMask/UI_FrameMask_{id}.png");
return false;
}
}
[HarmonyPatch]
public static class FramePatternLoader
{
public static IEnumerable<MethodBase> TargetMethods()
{
var AM = typeof(AssetManager);
return [AM.GetMethod("GetFramePatternTexture2D", [typeof(string)])];
}
public static bool Prefix(string filename, ref Texture2D __result, AssetManager __instance)
{
var matches = Regex.Matches(filename, @"UI_FramePattern_(\d+)\.png");
if (matches.Count < 1)
{
return true;
}
var id = matches[0].Groups[1].Value;
var texture = GetFramePatternTexture2D(id);
__result = texture ?? __instance.LoadAsset<Texture2D>($"FramePattern/UI_FramePattern_{id}.png");
return false;
}
}
// Private | Instance
[HarmonyPrefix]
[HarmonyPatch(typeof(AssetManager), "GetIconTexture2D", typeof(string))]
public static bool IconLoader(string filename, ref Texture2D __result, AssetManager __instance)
{
var matches = Regex.Matches(filename, @"UI_Icon_(\d+)\.png");
if (matches.Count < 1)
{
return true;
}
var id = matches[0].Groups[1].Value;
var texture = GetIconTexture2D(id);
__result = texture ?? __instance.LoadAsset<Texture2D>($"Icon/UI_Icon_{id}.png");
return false;
}
[HarmonyPatch]
public static class CharaLoader
{
public static IEnumerable<MethodBase> TargetMethods()
{
var AM = typeof(AssetManager);
return [AM.GetMethod("GetCharacterTexture2D", [typeof(string)])];
}
public static bool Prefix(string filename, ref Texture2D __result, AssetManager __instance)
{
var matches = Regex.Matches(filename, @"UI_Chara_(\d+)\.png");
if (matches.Count < 1)
{
return true;
}
var id = matches[0].Groups[1].Value;
var texture = GetCharaTexture2D(id);
__result = texture ?? __instance.LoadAsset<Texture2D>($"Chara/UI_Chara_{id}.png");
return false;
}
}
[HarmonyPatch]
public static class PartnerLoader
{
public static IEnumerable<MethodBase> TargetMethods()
{
var AM = typeof(AssetManager);
return [AM.GetMethod("GetPartnerTexture2D", [typeof(string)])];
}
public static bool Prefix(string filename, ref Texture2D __result, AssetManager __instance)
{
var matches = Regex.Matches(filename, @"UI_Partner_(\d+)\.png");
if (matches.Count < 1)
{
return true;
}
var id = matches[0].Groups[1].Value;
var texture = GetPartnerTexture2D(id);
__result = texture ?? __instance.LoadAsset<Texture2D>($"Partner/UI_Partner_{id}.png");
return false;
}
}
/*
[HarmonyPatch]
public static class FrameLoader
{
public static IEnumerable<MethodBase> TargetMethods()
{
var AM = typeof(AssetManager);
return [AM.GetMethod("GetFrameThumbTexture2D", [typeof(string)]), AM.GetMethod("GetFrameTexture2D", [typeof(string)])];
}
public static bool Prefix(string filename, ref Texture2D __result, AssetManager __instance)
{
var matches = Regex.Matches(filename, @"UI_Frame_(\d+)\.png");
if (matches.Count < 1)
{
return true;
}
var id = matches[0].Groups[1].Value;
var texture = GetFrameTexture2D(id);
__result = texture ?? __instance.LoadAsset<Texture2D>($"Frame/UI_Frame_{id}.png");
return false;
}
}
*/
}

View File

@@ -0,0 +1,54 @@
using System.Linq;
using AquaMai.Config.Attributes;
using HarmonyLib;
using MAI2.Util;
using Manager;
using MelonLoader;
using Monitor.Game;
using UnityEngine;
namespace AquaMai.Mods.GameSystem.Assets;
[ConfigSection(
en: """
Use the png jacket above as MV if no .dat found in the movie folder.
Use together with `LoadLocalImages`.
""",
zh: """
如果 movie 文件夹中没有 dat 格式的 MV 的话,就用歌曲的封面做背景,而不是显示迪拉熊的笑脸
请和 `LoadLocalImages` 一起用
""")]
public class UseJacketAsDummyMovie
{
[HarmonyPostfix]
[HarmonyPatch(typeof(GameCtrl), "IsReady")]
public static void LoadLocalBgaAwake(GameObject ____movieMaskObj)
{
var music = Singleton<DataManager>.Instance.GetMusic(GameManager.SelectMusicID[0]);
if (music is null) return;
var moviePath = Singleton<OptionDataManager>.Instance.GetMovieDataPath($"{music.movieName.id:000000}") + ".dat";
if (!moviePath.Contains("dummy")) return;
var jacket = LoadLocalImages.GetJacketTexture2D(music.movieName.id);
if (jacket is null)
{
MelonLogger.Msg("No jacket found for music " + music);
return;
}
var components = ____movieMaskObj.GetComponentsInChildren<Component>(false);
var movies = components.Where(it => it.name == "Movie");
foreach (var movie in movies)
{
// If I create a new RawImage component, the jacket will be not be displayed
// I think it will be difficult to make it work with RawImage
// So I change the material that plays video to default sprite material
// The original player is actually a sprite renderer and plays video with a custom material
var sprite = movie.GetComponent<SpriteRenderer>();
sprite.sprite = Sprite.Create(jacket, new Rect(0, 0, jacket.width, jacket.height), new Vector2(0.5f, 0.5f));
sprite.material = new Material(Shader.Find("Sprites/Default"));
}
}
}