forked from Cookies_Github_mirror/AquaDX
[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:
146
AquaMai/AquaMai.Mods/GameSystem/Assets/Fonts.cs
Normal file
146
AquaMai/AquaMai.Mods/GameSystem/Assets/Fonts.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
577
AquaMai/AquaMai.Mods/GameSystem/Assets/LoadLocalImages.cs
Normal file
577
AquaMai/AquaMai.Mods/GameSystem/Assets/LoadLocalImages.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user