Decompiled source of FrozenRA v1.0.3
plugins/FrozenRA.dll
Decompiled 2 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Net; using System.Net.Sockets; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; using System.Threading; using BepInEx; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using FrozenRA.Core; using Il2CppInterop.Runtime.Attributes; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSystem.Collections.Generic; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.SceneManagement; using _K; using ifapp.Game.Common; using ifapp.Game.Data; using ifapp.Game.Scenes; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(/*Could not decode attribute arguments.*/)] [assembly: AssemblyFileVersion("1.0.3.0")] [assembly: AssemblyTitle("FrozenRA")] [assembly: AssemblyCompany("SkyFrozen")] [assembly: AssemblyCopyright("Copyright 2026 SkyFrozen and FrozenRA contributors")] [assembly: SecurityPermission((SecurityAction)8, SkipVerification = true)] [assembly: AssemblyVersion("1.0.3.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : System.Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(/*Could not decode attribute arguments.*/)] internal sealed class RefSafetyRulesAttribute : System.Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } [AttributeUsage(/*Could not decode attribute arguments.*/)] internal sealed class NullableAttribute : System.Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte value) { NullableFlags = new byte[1] { value }; } public NullableAttribute(byte[] values) { NullableFlags = values; } } [AttributeUsage(/*Could not decode attribute arguments.*/)] internal sealed class NullableContextAttribute : System.Attribute { public readonly byte Flag; public NullableContextAttribute(byte value) { Flag = value; } } } namespace FrozenRA { public static class AppearanceConfigMigration { private sealed class PendingMigration { [field: CompilerGenerated] public int Version { [CompilerGenerated] get; [CompilerGenerated] set; } = 1; [field: CompilerGenerated] public string LegacyDirectory { [CompilerGenerated] get; [CompilerGenerated] set; } [field: CompilerGenerated] public string CanonicalThemeHash { [CompilerGenerated] get; [CompilerGenerated] set; } [field: CompilerGenerated] public bool Completed { [CompilerGenerated] get; [CompilerGenerated] set; } } private static string Hash(string file) { return Convert.ToHexString(SHA256.HashData(File.ReadAllBytes(file))); } public static void Apply() { //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_0214: Unknown result type (might be due to invalid IL or missing references) //IL_0271: Unknown result type (might be due to invalid IL or missing references) //IL_02d2: Unknown result type (might be due to invalid IL or missing references) //IL_02d7: Unknown result type (might be due to invalid IL or missing references) //IL_0290: Unknown result type (might be due to invalid IL or missing references) string text = Path.Combine(AppearanceStore.Root, "appearance", "pending-editor-migration.json"); if (!File.Exists(text)) { return; } try { PendingMigration pendingMigration = JsonSerializer.Deserialize<PendingMigration>(File.ReadAllText(text), AppearanceStore.JsonOptions); if (pendingMigration == null || pendingMigration.Version != 1 || pendingMigration.Completed) { return; } string fullName = ((FileSystemInfo)Directory.GetParent(AppearanceStore.Root)).FullName; if (string.IsNullOrWhiteSpace(pendingMigration.LegacyDirectory) || Path.IsPathRooted(pendingMigration.LegacyDirectory)) { throw new InvalidDataException("旧配置目录格式错误"); } string fullPath = Path.GetFullPath(Path.Combine(fullName, pendingMigration.LegacyDirectory)); if (!fullPath.StartsWith(fullName.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar, (StringComparison)5) || string.Equals(fullPath, AppearanceStore.Root, (StringComparison)5)) { throw new InvalidDataException("旧配置目录超出范围"); } string text2 = Path.Combine(fullPath, "appearance", "theme.json"); if (File.Exists(AppearanceStore.ThemePath) && File.Exists(text2) && string.Equals(Hash(AppearanceStore.ThemePath), pendingMigration.CanonicalThemeHash, (StringComparison)5) && !string.Equals(Hash(text2), pendingMigration.CanonicalThemeHash, (StringComparison)5)) { AppearanceTheme appearanceTheme = JsonSerializer.Deserialize<AppearanceTheme>(File.ReadAllText(AppearanceStore.ThemePath), AppearanceStore.JsonOptions); AppearanceTheme appearanceTheme2 = JsonSerializer.Deserialize<AppearanceTheme>(File.ReadAllText(text2), AppearanceStore.JsonOptions); appearanceTheme2.ExportDirectory = appearanceTheme.ExportDirectory; AppearanceStore.Validate(appearanceTheme2); List<AppearanceResource> obj = new List<AppearanceResource>((System.Collections.Generic.IEnumerable<AppearanceResource>)appearanceTheme2.Images.Values); obj.AddRange((System.Collections.Generic.IEnumerable<AppearanceResource>)appearanceTheme2.Fonts.Values); HashSet<string> val = new HashSet<string>((IEqualityComparer<string>)(object)StringComparer.OrdinalIgnoreCase); Enumerator<AppearanceResource> enumerator = obj.GetEnumerator(); try { while (enumerator.MoveNext()) { AppearanceResource current = enumerator.Current; string[] array = new string[2] { current.File, current.OriginalFile }; foreach (string text3 in array) { if (!string.IsNullOrWhiteSpace(text3)) { val.Add(text3); } } } } finally { ((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } Enumerator<string> enumerator2 = val.GetEnumerator(); try { while (enumerator2.MoveNext()) { string current2 = enumerator2.Current; string text4 = AppearanceStore.SafePath(current2); string fullPath2 = Path.GetFullPath(Path.Combine(fullPath, current2.Replace('/', Path.DirectorySeparatorChar))); if (!fullPath2.StartsWith(fullPath.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar, (StringComparison)5)) { throw new InvalidDataException("旧资源路径超出范围"); } if (!File.Exists(text4)) { if (!File.Exists(fullPath2)) { throw new FileNotFoundException("旧外观资源不存在", fullPath2); } Directory.CreateDirectory(Path.GetDirectoryName(text4)); File.Copy(fullPath2, text4); } } } finally { ((System.IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose(); } string root = AppearanceStore.Root; DateTimeOffset now = DateTimeOffset.Now; string text5 = Path.Combine(root, "backups", ((DateTimeOffset)(ref now)).ToString("yyyyMMdd-HHmmss") + "-editor-migration"); Directory.CreateDirectory(text5); File.Copy(AppearanceStore.ThemePath, Path.Combine(text5, "theme.json"), true); AppearanceStore.Save(appearanceTheme2); Plugin.Logger.LogInfo((object)"[Appearance] 已迁移兼容面板中保存的外观设置"); } pendingMigration.Completed = true; string text6 = text + ".tmp"; File.WriteAllText(text6, JsonSerializer.Serialize<PendingMigration>(pendingMigration, AppearanceStore.JsonOptions)); File.Move(text6, text, true); } catch (System.Exception ex) { Plugin.Logger.LogWarning((object)("[Appearance] 兼容外观迁移未完成,保留现有设置:" + ex.Message)); } } } public static class AppearanceHost { private delegate bool WindowVisitor(System.IntPtr window, System.IntPtr parameter); private sealed class RatingSnapshot { public readonly string Name; public readonly string Delta; public readonly double Rating; public readonly List<ChartRating> Charts; public RatingSnapshot() { lock (Plugin.RatingDataGate) { Name = Plugin.PlayerName; Rating = Plugin.PlayerRating; Delta = (Plugin.HasRatingDelta ? Plugin.RatingDeltaText : ""); Charts = Plugin.ChartRatings ?? new List<ChartRating>(); } } } private sealed class Request { public string Method; public string Path; public Dictionary<string, string> Headers = new Dictionary<string, string>((IEqualityComparer<string>)(object)StringComparer.OrdinalIgnoreCase); public byte[] Body; } private static readonly object Gate = new object(); private static readonly object StartGate = new object(); private static readonly object B30FileGate = new object(); private static readonly object OverlayFileGate = new object(); private static readonly object PreviewFileGate = new object(); private static readonly object RecordsExportGate = new object(); private static System.IntPtr _editorWindow; private static int _editorOpening; private static readonly JsonSerializerOptions NetworkJsonOptions = new JsonSerializerOptions(AppearanceStore.JsonOptions) { WriteIndented = false }; private static TcpListener _listener; private static volatile bool _running; private static int _port; private static readonly string Token = Convert.ToHexString(RandomNumberGenerator.GetBytes(24)); public static int Port => _port; public static string EditorUrl => "http://127.0.0.1:" + _port + "/?key=" + Token; [DllImport("user32.dll")] private static extern bool IsWindow(System.IntPtr window); [DllImport("user32.dll")] private static extern bool IsIconic(System.IntPtr window); [DllImport("user32.dll")] private static extern bool EnumWindows(WindowVisitor visitor, System.IntPtr parameter); [DllImport("user32.dll", CharSet = (CharSet)3)] private static extern int GetWindowTextW(System.IntPtr window, StringBuilder text, int length); [DllImport("user32.dll")] private static extern bool ShowWindow(System.IntPtr window, int command); [DllImport("user32.dll")] private static extern bool SetForegroundWindow(System.IntPtr window); public static void OpenEditor() { //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Expected O, but got Unknown //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Expected O, but got Unknown //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Expected O, but got Unknown try { Start(); if (_editorWindow != System.IntPtr.Zero && IsWindow(_editorWindow)) { ShowWindow(_editorWindow, IsIconic(_editorWindow) ? 9 : 5); SetForegroundWindow(_editorWindow); } else if (Interlocked.CompareExchange(ref _editorOpening, 1, 0) == 0) { HashSet<System.IntPtr> val = EditorWindows(); string text = Path.Combine(new string[5] { Environment.GetFolderPath((SpecialFolder)42), "Microsoft", "Edge", "Application", "msedge.exe" }); if (File.Exists(text)) { ProcessStartInfo val2 = new ProcessStartInfo(text) { UseShellExecute = false }; val2.ArgumentList.Add("--app=" + EditorUrl); Process.Start(val2); } else { Process.Start(new ProcessStartInfo(EditorUrl) { UseShellExecute = true }); } ThreadPool.QueueUserWorkItem(new WaitCallback(TrackEditorWindow), (object)val); Plugin.Logger.LogInfo((object)"[Appearance] 外观编辑器已打开,保存后立即刷新游戏显示"); } } catch (System.Exception ex) { Interlocked.Exchange(ref _editorOpening, 0); Plugin.Logger.LogError((object)("[Appearance] 编辑器启动失败: " + ex.Message)); } } private static HashSet<System.IntPtr> EditorWindows() { HashSet<System.IntPtr> windows = new HashSet<System.IntPtr>(); EnumWindows(delegate(System.IntPtr window, System.IntPtr parameter) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown StringBuilder val = new StringBuilder(160); GetWindowTextW(window, val, val.Capacity); if (((object)val).ToString().StartsWith("FrozenRA 外观编辑器", (StringComparison)4)) { windows.Add(window); } return true; }, System.IntPtr.Zero); return windows; } private static void TrackEditorWindow(object state) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) try { HashSet<System.IntPtr> val = (HashSet<System.IntPtr>)state; for (int i = 0; i < 100; i++) { Enumerator<System.IntPtr> enumerator = EditorWindows().GetEnumerator(); try { while (enumerator.MoveNext()) { System.IntPtr current = enumerator.Current; if (!val.Contains(current)) { _editorWindow = current; return; } } } finally { ((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } Thread.Sleep(50); } } finally { Interlocked.Exchange(ref _editorOpening, 0); } } public static void Start() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) lock (StartGate) { if (_listener != null) { return; } for (int i = 53187; i < 53197; i++) { try { TcpListener val = new TcpListener(IPAddress.Loopback, i); val.Start(); _listener = val; _port = i; } catch (SocketException) { continue; } break; } if (_listener == null) { throw new IOException("本地编辑器端口暂时不可用"); } _running = true; TcpListener activeListener = _listener; new Thread((ThreadStart)delegate { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown while (_running && _listener == activeListener) { try { TcpClient val2 = activeListener.AcceptTcpClient(); ThreadPool.QueueUserWorkItem(new WaitCallback(HandleClient), (object)val2); } catch (System.Exception ex2) { if (!_running || _listener != activeListener) { break; } Plugin.Logger.LogWarning((object)("[Appearance] 编辑器连接: " + ex2.Message)); Thread.Sleep(500); } } }) { IsBackground = true, Name = "FrozenRA Appearance Editor" }.Start(); Plugin.Logger.LogInfo((object)"[Appearance] F5 打开外观编辑器 · F6 显示/隐藏 · F7 导出 B30 到所选文件夹"); } } private static void HandleClient(object state) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown Handle((TcpClient)state); } public static void Stop() { lock (StartGate) { _running = false; TcpListener listener = _listener; if (listener != null) { listener.Stop(); } _listener = null; } } private static Request ReadRequest(Stream stream) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) List<byte> val = new List<byte>(); int num; while ((num = stream.ReadByte()) >= 0) { val.Add((byte)num); if (val.Count > 16384) { throw new InvalidDataException("请求头过长"); } int count = val.Count; if (count >= 4 && val[count - 4] == 13 && val[count - 3] == 10 && val[count - 2] == 13 && val[count - 1] == 10) { break; } } string[] array = Encoding.ASCII.GetString(val.ToArray()).Split("\r\n", (StringSplitOptions)0); string[] array2 = array[0].Split(' ', (StringSplitOptions)0); if (array2.Length < 2) { throw new InvalidDataException("请求格式错误"); } Request request = new Request { Method = array2[0], Path = array2[1] }; System.Collections.Generic.IEnumerator<string> enumerator = Enumerable.Skip<string>((System.Collections.Generic.IEnumerable<string>)array, 1).GetEnumerator(); try { while (((System.Collections.IEnumerator)enumerator).MoveNext()) { string current = enumerator.Current; int num2 = current.IndexOf(':'); if (num2 > 0) { Dictionary<string, string> headers = request.Headers; string text = current.Substring(0, num2).Trim(); string text2 = current; int num3 = num2 + 1; headers[text] = text2.Substring(num3, text2.Length - num3).Trim(); } } } finally { ((System.IDisposable)enumerator)?.Dispose(); } string text3 = default(string); int num4 = (request.Headers.TryGetValue("Content-Length", ref text3) ? int.Parse(text3) : 0); if (num4 < 0 || num4 > 67108864) { throw new InvalidDataException("图片数据过大"); } request.Body = new byte[num4]; int num5; for (int i = 0; i < num4; i += num5) { num5 = stream.Read(request.Body, i, num4 - i); if (num5 == 0) { throw new EndOfStreamException(); } } return request; } private static void Respond(NetworkStream stream, int code, string type, byte[] bytes) { string text = code switch { 404 => "Not Found", 403 => "Forbidden", 200 => "OK", _ => "Bad Request", }; byte[] bytes2 = Encoding.ASCII.GetBytes($"HTTP/1.1 {code} {text}\r\nContent-Type: {type}\r\nContent-Length: {bytes.Length}\r\nCache-Control: no-store\r\nX-Content-Type-Options: nosniff\r\nConnection: close\r\n\r\n"); ((Stream)stream).Write(System.ReadOnlySpan<byte>.op_Implicit(bytes2)); ((Stream)stream).Write(System.ReadOnlySpan<byte>.op_Implicit(bytes)); } private static void RespondFile(NetworkStream stream, string type, string file, bool cache = false) { FileStream val = File.OpenRead(file); try { string text = (cache ? "private, max-age=3600" : "no-store"); byte[] bytes = Encoding.ASCII.GetBytes($"HTTP/1.1 200 OK\r\nContent-Type: {type}\r\nContent-Length: {((Stream)val).Length}\r\nCache-Control: {text}\r\nX-Content-Type-Options: nosniff\r\nConnection: close\r\n\r\n"); ((Stream)stream).Write(System.ReadOnlySpan<byte>.op_Implicit(bytes)); ((Stream)val).CopyTo((Stream)(object)stream, 65536); } finally { ((System.IDisposable)val)?.Dispose(); } } private static void Json(NetworkStream stream, object value) { Respond(stream, 200, "application/json; charset=utf-8", JsonSerializer.SerializeToUtf8Bytes<object>(value, NetworkJsonOptions)); } private static string String(JsonElement root, string key, string fallback = null) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Invalid comparison between Unknown and I4 JsonElement val = default(JsonElement); if (!((JsonElement)(ref root)).TryGetProperty(key, ref val) || (int)((JsonElement)(ref val)).ValueKind != 3) { return fallback; } return ((JsonElement)(ref val)).GetString(); } private static string Query(string query, string key, string fallback) { string[] array = query.Split('&', (StringSplitOptions)0); foreach (string text in array) { if (text.StartsWith(key + "=", (StringComparison)4)) { string text2 = text; int num = key.Length + 1; return Uri.UnescapeDataString(text2.Substring(num, text2.Length - num)); } } return fallback; } private static AppearanceTheme ThemeSnapshot() { lock (Gate) { return JsonSerializer.Deserialize<AppearanceTheme>(System.ReadOnlySpan<byte>.op_Implicit(JsonSerializer.SerializeToUtf8Bytes<AppearanceTheme>(AppearanceStore.Current, NetworkJsonOptions)), AppearanceStore.JsonOptions); } } public static string EditorDocument() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown Stream manifestResourceStream = typeof(AppearanceHost).Assembly.GetManifestResourceStream("FrozenRA.Editor.html.gz"); try { if (manifestResourceStream != null) { GZipStream val = new GZipStream(manifestResourceStream, (CompressionMode)0); try { StreamReader val2 = new StreamReader((Stream)(object)val, Encoding.UTF8); try { return ((TextReader)val2).ReadToEnd(); } finally { ((System.IDisposable)val2)?.Dispose(); } } finally { ((System.IDisposable)val)?.Dispose(); } } return File.ReadAllText(Path.Combine(Path.GetDirectoryName(typeof(AppearanceHost).Assembly.Location), "FrozenRAEditor.html")); } finally { ((System.IDisposable)manifestResourceStream)?.Dispose(); } } private unsafe static void Handle(TcpClient client) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_03ae: Unknown result type (might be due to invalid IL or missing references) //IL_03b5: Unknown result type (might be due to invalid IL or missing references) //IL_03bb: Unknown result type (might be due to invalid IL or missing references) //IL_0591: Unknown result type (might be due to invalid IL or missing references) //IL_0598: Unknown result type (might be due to invalid IL or missing references) //IL_059e: Unknown result type (might be due to invalid IL or missing references) //IL_03c6: Unknown result type (might be due to invalid IL or missing references) //IL_040b: Unknown result type (might be due to invalid IL or missing references) //IL_0421: Unknown result type (might be due to invalid IL or missing references) //IL_0627: Unknown result type (might be due to invalid IL or missing references) //IL_062e: Unknown result type (might be due to invalid IL or missing references) //IL_0634: Unknown result type (might be due to invalid IL or missing references) //IL_05a9: Unknown result type (might be due to invalid IL or missing references) //IL_0701: Unknown result type (might be due to invalid IL or missing references) //IL_0708: Unknown result type (might be due to invalid IL or missing references) //IL_070e: Unknown result type (might be due to invalid IL or missing references) //IL_0853: Unknown result type (might be due to invalid IL or missing references) //IL_085a: Unknown result type (might be due to invalid IL or missing references) //IL_0860: Unknown result type (might be due to invalid IL or missing references) //IL_0719: Unknown result type (might be due to invalid IL or missing references) //IL_071e: Unknown result type (might be due to invalid IL or missing references) //IL_0674: Unknown result type (might be due to invalid IL or missing references) //IL_0506: Unknown result type (might be due to invalid IL or missing references) //IL_0490: Unknown result type (might be due to invalid IL or missing references) //IL_086b: Unknown result type (might be due to invalid IL or missing references) //IL_0870: Unknown result type (might be due to invalid IL or missing references) //IL_0753: Unknown result type (might be due to invalid IL or missing references) //IL_0cf2: Unknown result type (might be due to invalid IL or missing references) //IL_0cf9: Unknown result type (might be due to invalid IL or missing references) //IL_0cff: Unknown result type (might be due to invalid IL or missing references) //IL_08ae: Unknown result type (might be due to invalid IL or missing references) //IL_08b3: Unknown result type (might be due to invalid IL or missing references) //IL_08cd: Unknown result type (might be due to invalid IL or missing references) //IL_0a9a: Unknown result type (might be due to invalid IL or missing references) //IL_0a9f: Unknown result type (might be due to invalid IL or missing references) //IL_0aa3: Unknown result type (might be due to invalid IL or missing references) //IL_0aa8: Unknown result type (might be due to invalid IL or missing references) //IL_0795: Unknown result type (might be due to invalid IL or missing references) //IL_0d4d: Unknown result type (might be due to invalid IL or missing references) //IL_0d52: Unknown result type (might be due to invalid IL or missing references) //IL_0d5b: Unknown result type (might be due to invalid IL or missing references) //IL_0d60: Unknown result type (might be due to invalid IL or missing references) //IL_0d7c: Unknown result type (might be due to invalid IL or missing references) //IL_092f: Unknown result type (might be due to invalid IL or missing references) //IL_0935: Invalid comparison between Unknown and I4 //IL_07d0: Unknown result type (might be due to invalid IL or missing references) //IL_0dfd: Unknown result type (might be due to invalid IL or missing references) //IL_0ab1: Unknown result type (might be due to invalid IL or missing references) //IL_0ab6: Unknown result type (might be due to invalid IL or missing references) //IL_093a: Unknown result type (might be due to invalid IL or missing references) //IL_0af0: Unknown result type (might be due to invalid IL or missing references) //IL_0b17: Unknown result type (might be due to invalid IL or missing references) //IL_097a: Unknown result type (might be due to invalid IL or missing references) //IL_09a0: Unknown result type (might be due to invalid IL or missing references) //IL_09b6: Unknown result type (might be due to invalid IL or missing references) //IL_0c09: Unknown result type (might be due to invalid IL or missing references) //IL_0a2b: Unknown result type (might be due to invalid IL or missing references) //IL_0a60: Unknown result type (might be due to invalid IL or missing references) try { client.ReceiveTimeout = 20000; client.SendTimeout = 20000; NetworkStream stream = client.GetStream(); BufferedStream val = new BufferedStream((Stream)(object)stream, 8192); try { Request request = ReadRequest((Stream)(object)val); Uri val2 = new Uri("http://127.0.0.1:" + _port + request.Path); string text = Uri.UnescapeDataString(val2.AbsolutePath); string text2 = val2.Query.TrimStart('?'); string text3 = default(string); if ((!request.Headers.TryGetValue("X-InFalsus-Key", ref text3) || !(text3 == Token)) && !Enumerable.Any<string>((System.Collections.Generic.IEnumerable<string>)text2.Split('&', (StringSplitOptions)0), (Func<string, bool>)((string part) => part == "key=" + Token))) { Respond(stream, 403, "text/plain", Encoding.UTF8.GetBytes("请从游戏内按 " + AppearanceStore.Current.Hotkeys.Editor + " 打开编辑器 / Open the editor from the game")); return; } string text4 = default(string); if (request.Headers.TryGetValue("Origin", ref text4) && text4 != "http://127.0.0.1:" + _port) { throw new InvalidDataException("请求来源不匹配"); } if ((text == "/api/records-preview.png" || text == "/api/records-export" || text == "/api/export" || text == "/api/b30-preview.png" || text == "/api/overlay.png" || text == "/api/b30.png") ? true : false) { PresentationState.WaitForIdle(); } if (request.Method == "GET" && text == "/") { string text5 = EditorDocument().Replace("<!--IF_HOST-->", "<script>window.IF_HOST=" + JsonSerializer.Serialize(new { key = Token, port = _port }, (JsonSerializerOptions)null) + ";</script>"); Respond(stream, 200, "text/html; charset=utf-8", Encoding.UTF8.GetBytes(text5)); return; } if (request.Method == "GET" && text == "/api/state") { RatingSnapshot ratingSnapshot = new RatingSnapshot(); System.Collections.Generic.IEnumerable<ChartRating> enumerable = Enumerable.Take<ChartRating>((System.Collections.Generic.IEnumerable<ChartRating>)ratingSnapshot.Charts, 39); Json(stream, new { gameplay = PresentationState.Gameplay, theme = AppearanceStore.Current, exportDirectory = B30ExportFiles.ResolveDirectory(AppearanceStore.Current.ExportDirectory), defaultExportDirectory = B30ExportFiles.DefaultDirectory, rating = ratingSnapshot.Rating, ratingDisplay = RatingDeltaEffect.FormatRating(ratingSnapshot.Rating), delta = ratingSnapshot.Delta, name = ratingSnapshot.Name, charts = Enumerable.Select(enumerable, (ChartRating c, int i) => new { rank = i + 1, songId = c.SongId, title = SongInfoCache.GetName(c.SongId), artist = SongInfoCache.GetArtist(c.SongId), baseName = SongInfoCache.GetBaseName(c.SongId), rating = c.Rating, score = c.Score, difficulty = c.Difficulty, difficultyColor = ChartBadge.DifficultyColor(c.Difficulty), constant = c.Constant, dive = c.PaceValue, grade = ChartBadge.Grade(c.Score), clear = ChartBadge.Clear(c), isFC = c.IsFC, isPM = c.IsPM }) }); return; } if (request.Method == "GET" && text == "/api/records") { RatingSnapshot ratingSnapshot2 = new RatingSnapshot(); AppearanceTheme appearanceTheme = ThemeSnapshot(); CompletionView view = CompletionRecords.Create(Query(text2, "level", "10.8"), ratingSnapshot2.Charts, appearanceTheme, appearanceTheme.State.Name, ratingSnapshot2.Rating, Query(text2, "pack", ""), Query(text2, "difficulty", "all")); Json(stream, new { view = view, appearanceGeneration = AppearanceStore.Generation, exportDirectory = B30ExportFiles.ResolveDirectory(appearanceTheme.ExportDirectory) }); return; } if (request.Method == "POST" && (text == "/api/records-preview.png" || text == "/api/records-export")) { JsonDocument val3 = JsonDocument.Parse(ReadOnlyMemory<byte>.op_Implicit(request.Body), default(JsonDocumentOptions)); try { string selected = CompletionRecords.NormalizeLevel(String(val3.RootElement, "level", "10.8")); RatingSnapshot ratingSnapshot3 = new RatingSnapshot(); AppearanceTheme appearanceTheme2 = ThemeSnapshot(); CompletionView completionView = CompletionRecords.Create(selected, ratingSnapshot3.Charts, appearanceTheme2, appearanceTheme2.State.Name, ratingSnapshot3.Rating, String(val3.RootElement, "pack", ""), String(val3.RootElement, "difficulty", "all")); if (text == "/api/records-preview.png") { string file = Path.Combine(AppearanceStore.Root, "appearance", "preview-records.png"); lock (PreviewFileGate) { if (!AppearanceRenderer.CompletionTable(file, completionView, appearanceTheme2, preview: true)) { throw new IOException("完成表预览生成失败"); } RespondFile(stream, "image/png", file); return; } } string text6 = B30ExportFiles.ResolveDirectory(appearanceTheme2.ExportDirectory); Directory.CreateDirectory(text6); string text7; lock (RecordsExportGate) { text7 = CompletionRecords.NewExportPath(completionView, text6); if (!AppearanceRenderer.CompletionTable(text7, completionView, appearanceTheme2, preview: false)) { throw new IOException("完成表导出失败"); } } Plugin.Logger.LogInfo((object)("[Records] 已导出 " + text7)); Json(stream, new { ok = true, file = text7, level = completionView.Level, pack = completionView.PackId, difficulty = completionView.Difficulty, counts = completionView.Counts }); return; } finally { ((System.IDisposable)val3)?.Dispose(); } } if (request.Method == "POST" && text == "/api/export-directory/browse") { JsonDocument val4 = JsonDocument.Parse(ReadOnlyMemory<byte>.op_Implicit(request.Body), default(JsonDocumentOptions)); try { string text8 = ExportFolderPicker.Browse(B30ExportFiles.ResolveDirectory(String(val4.RootElement, "directory", AppearanceStore.Current.ExportDirectory))); Json(stream, new { cancelled = (text8 == null), directory = text8 }); return; } finally { ((System.IDisposable)val4)?.Dispose(); } } if (request.Method == "POST" && text == "/api/export-directory") { JsonDocument val5 = JsonDocument.Parse(ReadOnlyMemory<byte>.op_Implicit(request.Body), default(JsonDocumentOptions)); try { lock (Gate) { AppearanceTheme appearanceTheme3 = JsonSerializer.Deserialize<AppearanceTheme>(System.ReadOnlySpan<byte>.op_Implicit(JsonSerializer.SerializeToUtf8Bytes<AppearanceTheme>(AppearanceStore.Current, NetworkJsonOptions)), AppearanceStore.JsonOptions); appearanceTheme3.ExportDirectory = B30ExportFiles.NormalizeDirectory(String(val5.RootElement, "directory", "")); AppearanceStore.Save(appearanceTheme3); Json(stream, new { ok = true, exportDirectory = B30ExportFiles.ResolveDirectory(appearanceTheme3.ExportDirectory), defaultExportDirectory = B30ExportFiles.DefaultDirectory }); return; } } finally { ((System.IDisposable)val5)?.Dispose(); } } if (request.Method == "POST" && text == "/api/preferences") { JsonDocument val6 = JsonDocument.Parse(ReadOnlyMemory<byte>.op_Implicit(request.Body), default(JsonDocumentOptions)); try { JsonElement rootElement = val6.RootElement; lock (Gate) { AppearanceTheme appearanceTheme4 = JsonSerializer.Deserialize<AppearanceTheme>(System.ReadOnlySpan<byte>.op_Implicit(JsonSerializer.SerializeToUtf8Bytes<AppearanceTheme>(AppearanceStore.Current, NetworkJsonOptions)), AppearanceStore.JsonOptions); string text9 = String(rootElement, "interfaceLanguage", appearanceTheme4.InterfaceLanguage); if ((!(text9 == "zh") && !(text9 == "ja")) || 1 == 0) { throw new InvalidDataException("请选择中文或日本語"); } appearanceTheme4.InterfaceLanguage = text9; JsonElement val7 = default(JsonElement); if (((JsonElement)(ref rootElement)).TryGetProperty("hotkeys", ref val7)) { appearanceTheme4.Hotkeys = JsonSerializer.Deserialize<HotkeySettings>(((JsonElement)(ref val7)).GetRawText(), AppearanceStore.JsonOptions) ?? throw new InvalidDataException("缺少快捷键设置"); } HotkeyBinding.Validate(appearanceTheme4.Hotkeys); AppearanceStore.Save(appearanceTheme4); Json(stream, new { ok = true, interfaceLanguage = appearanceTheme4.InterfaceLanguage, hotkeys = appearanceTheme4.Hotkeys }); return; } } finally { ((System.IDisposable)val6)?.Dispose(); } } JsonElement val9; if (request.Method == "POST" && text == "/api/theme") { JsonDocument val8 = JsonDocument.Parse(ReadOnlyMemory<byte>.op_Implicit(request.Body), default(JsonDocumentOptions)); try { JsonElement rootElement2 = val8.RootElement; lock (Gate) { AppearanceTheme appearanceTheme5 = JsonSerializer.Deserialize<AppearanceTheme>(System.ReadOnlySpan<byte>.op_Implicit(JsonSerializer.SerializeToUtf8Bytes<AppearanceTheme>(AppearanceStore.Current, NetworkJsonOptions)), AppearanceStore.JsonOptions); val9 = ((JsonElement)(ref rootElement2)).GetProperty("state"); appearanceTheme5.State = JsonSerializer.Deserialize<AppearanceState>(((JsonElement)(ref val9)).GetRawText(), AppearanceStore.JsonOptions); appearanceTheme5.Preset = String(rootElement2, "preset", "poster"); JsonElement val10 = default(JsonElement); string[] array; if (((JsonElement)(ref rootElement2)).TryGetProperty("resources", ref val10)) { array = new string[2] { "art", "bg" }; JsonElement root = default(JsonElement); AppearanceResource appearanceResource = default(AppearanceResource); JsonElement val11 = default(JsonElement); foreach (string text10 in array) { if (!((JsonElement)(ref val10)).TryGetProperty(text10, ref root) || (int)((JsonElement)(ref root)).ValueKind != 1) { continue; } string text11 = String(root, "id"); if (!string.IsNullOrWhiteSpace(text11) && (!appearanceTheme5.Images.TryGetValue(text10, ref appearanceResource) || !(appearanceResource.Id == text11))) { string text12 = String(root, "src"); if (text12 == null || !text12.StartsWith("data:")) { throw new InvalidDataException("图片尚未导入,请重新选择文件"); } string text13 = SaveData(text12, "image", text10); string text14 = String(root, "originalSrc", text12); string originalFile = (text14.StartsWith("data:") ? SaveData(text14, "image", text10 + "-original") : (appearanceResource?.OriginalFile ?? text13)); appearanceTheme5.Images[text10] = new AppearanceResource { Id = text11, File = text13, OriginalFile = originalFile, Name = String(root, "name", "已导入图片"), Crop = (((JsonElement)(ref root)).TryGetProperty("crop", ref val11) ? new JsonElement?(((JsonElement)(ref val11)).Clone()) : ((JsonElement?)null)) }; } } } JsonElement val12 = default(JsonElement); if (((JsonElement)(ref rootElement2)).TryGetProperty("fonts", ref val12)) { ObjectEnumerator val13 = ((JsonElement)(ref val12)).EnumerateObject(); ObjectEnumerator enumerator = ((ObjectEnumerator)(ref val13)).GetEnumerator(); try { while (((ObjectEnumerator)(ref enumerator)).MoveNext()) { JsonProperty current = ((ObjectEnumerator)(ref enumerator)).Current; if (!appearanceTheme5.Fonts.ContainsKey(((JsonProperty)(ref current)).Name)) { appearanceTheme5.Fonts[((JsonProperty)(ref current)).Name] = new AppearanceResource { Id = ((JsonProperty)(ref current)).Name, File = SaveData(String(((JsonProperty)(ref current)).Value, "src"), "font", "font"), Name = String(((JsonProperty)(ref current)).Value, "name", "自定义字体") }; } } } finally { ((System.IDisposable)(*(ObjectEnumerator*)(&enumerator))/*cast due to .constrained prefix*/).Dispose(); } } array = new string[2] { "art", "bg" }; AppearanceResource appearanceResource2 = default(AppearanceResource); string text16 = default(string); foreach (string text15 in array) { if (((text15 == "art") ? appearanceTheme5.State.Art : appearanceTheme5.State.Bg) == "custom" && (!appearanceTheme5.Images.TryGetValue(text15, ref appearanceResource2) || !appearanceTheme5.State.ImageIds.TryGetValue(text15, ref text16) || appearanceResource2.Id != text16)) { throw new InvalidDataException("请重新导入" + ((text15 == "art") ? "人物图片" : "背景图片")); } } AppearanceStore.Save(appearanceTheme5); Plugin.PlayerName = appearanceTheme5.State.Name; File.WriteAllText(Path.Combine(AppearanceStore.Root, "name.txt"), Plugin.PlayerName); } Plugin.Logger.LogInfo((object)"[Appearance] 外观已保存并应用"); Json(stream, new { ok = true, theme = AppearanceStore.Current }); return; } finally { ((System.IDisposable)val8)?.Dispose(); } } if (request.Method == "POST" && text == "/api/export") { Plugin.ExportB30Image(); Json(stream, new { ok = true }); return; } if (request.Method == "POST" && text == "/api/b30-preview.png") { JsonDocument val14 = JsonDocument.Parse(ReadOnlyMemory<byte>.op_Implicit(request.Body), default(JsonDocumentOptions)); try { AppearanceTheme appearanceTheme6; lock (Gate) { appearanceTheme6 = JsonSerializer.Deserialize<AppearanceTheme>(System.ReadOnlySpan<byte>.op_Implicit(JsonSerializer.SerializeToUtf8Bytes<AppearanceTheme>(AppearanceStore.Current, NetworkJsonOptions)), AppearanceStore.JsonOptions); } val9 = val14.RootElement; val9 = ((JsonElement)(ref val9)).GetProperty("state"); appearanceTheme6.State = JsonSerializer.Deserialize<AppearanceState>(((JsonElement)(ref val9)).GetRawText(), AppearanceStore.JsonOptions); appearanceTheme6.Preset = String(val14.RootElement, "preset", appearanceTheme6.Preset); AppearanceStore.Validate(appearanceTheme6); RatingSnapshot ratingSnapshot4 = new RatingSnapshot(); string file2 = Path.Combine(AppearanceStore.Root, "appearance", "preview-b30-fast.png"); lock (PreviewFileGate) { if (!AppearanceRenderer.PreviewB30(file2, appearanceTheme6.State.Name, ratingSnapshot4.Rating, ratingSnapshot4.Charts, appearanceTheme6)) { throw new IOException("B30 预览生成失败"); } RespondFile(stream, "image/png", file2); return; } } finally { ((System.IDisposable)val14)?.Dispose(); } } if (request.Method == "GET" && (text == "/api/overlay.png" || text == "/api/b30.png")) { string file3 = Path.Combine(AppearanceStore.Root, "appearance", text.EndsWith("overlay.png") ? "preview-overlay.png" : "preview-b30.png"); lock (text.EndsWith("overlay.png") ? OverlayFileGate : B30FileGate) { RatingSnapshot ratingSnapshot5 = new RatingSnapshot(); if (text.EndsWith("overlay.png")) { AppearanceRenderer.SaveOverlay(file3, AppearanceStore.Current.State.Name, RatingDeltaEffect.FormatRating(ratingSnapshot5.Rating), Enumerable.Contains<string>((System.Collections.Generic.IEnumerable<string>)text2.Split('&', (StringSplitOptions)0), "deltaPreview=1") ? "+0.020" : ratingSnapshot5.Delta); } else { AppearanceRenderer.ExportB30(file3, AppearanceStore.Current.State.Name, ratingSnapshot5.Rating, ratingSnapshot5.Charts, AppearanceStore.Current.State.B30Jackets); } RespondFile(stream, "image/png", file3); return; } } if (request.Method == "GET" && text.StartsWith("/assets/")) { string text17 = text; string text18 = AppearanceStore.SafePath(text17.Substring(8, text17.Length - 8)); string text19 = Path.GetExtension(text18).ToLowerInvariant(); if (text19 == ".png") { text17 = "image/png"; } else { string text20; if ((text19 == ".jpg" || text19 == ".jpeg") ? true : false) { text20 = "image/jpeg"; } else { string text21; if (text19 == ".webp") { text21 = "image/webp"; } else { bool flag = ((text19 == ".ttf" || text19 == ".otf") ? true : false); text21 = (flag ? "font/ttf" : null); } text20 = text21; } text17 = text20; } string text22 = text17; if (text22 == null || !File.Exists(text18)) { Respond(stream, 404, "text/plain", System.Array.Empty<byte>()); } else { RespondFile(stream, text22, text18, cache: true); } } else { Respond(stream, 404, "text/plain", System.Array.Empty<byte>()); } } catch (System.Exception ex) { Plugin.Logger.LogWarning((object)("[Appearance] 编辑器请求失败: " + ex.Message)); try { Respond(stream, 400, "application/json; charset=utf-8", Encoding.UTF8.GetBytes(JsonSerializer.Serialize(new { error = ex.Message }, (JsonSerializerOptions)null))); } catch { } } finally { ((System.IDisposable)val)?.Dispose(); } } finally { ((System.IDisposable)client)?.Dispose(); } } private static string SaveData(string data, string kind, string label) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_0247: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(data) || !data.StartsWith("data:")) { throw new InvalidDataException("文件数据缺失"); } int num = data.IndexOf(','); if (num < 0 || !data.Substring(0, num).Contains(";base64")) { throw new InvalidDataException("文件编码无效"); } System.ReadOnlySpan<char> readOnlySpan = MemoryExtensions.AsSpan(data, num + 1); byte[] array; int num2 = default(int); string text2; bool num3; checked { array = new byte[unchecked(checked(readOnlySpan.Length + 3) / 4) * 3]; if (!Convert.TryFromBase64Chars(readOnlySpan, System.Span<byte>.op_Implicit(array), ref num2)) { throw new FormatException("Invalid Base64 data"); } if (num2 == 0 || num2 > 41943040) { throw new InvalidDataException("文件大小无效"); } if (kind == "font") { bool flag = num2 < 4; if (!flag) { bool flag2 = array[0] == 0 && array[1] == 1 && array[2] == 0 && array[3] == 0; if (!flag2) { string text = Encoding.ASCII.GetString(array, 0, 4); bool flag3 = ((text == "OTTO" || text == "ttcf") ? true : false); flag2 = flag3; } flag = !flag2; } if (flag) { throw new InvalidDataException("请选择有效的 TTF / OTF 字体"); } text2 = ((Encoding.ASCII.GetString(array, 0, 4) == "OTTO") ? ".otf" : ".ttf"); goto IL_024d; } string text3 = data.Substring(0, num); text2 = (text3.Contains("image/png") ? ".png" : (text3.Contains("image/jpeg") ? ".jpg" : (text3.Contains("image/webp") ? ".webp" : null))); if (text2 == null) { throw new InvalidDataException("请选择 PNG / JPG / WebP 图片"); } if (!(text2 == ".png")) { if (!(text2 == ".jpg")) { if (num2 > 12 && Encoding.ASCII.GetString(array, 0, 4) == "RIFF") { num3 = Encoding.ASCII.GetString(array, 8, 4) == "WEBP"; goto IL_0240; } } else if (num2 > 3 && array[0] == 255) { num3 = array[1] == 216; goto IL_0240; } } else if (num2 > 8 && array[0] == 137 && array[1] == 80 && array[2] == 78) { num3 = array[3] == 71; goto IL_0240; } goto IL_0242; } IL_0242: throw new InvalidDataException("图片内容与格式不匹配"); IL_0240: if (!num3) { goto IL_0242; } goto IL_024d; IL_024d: System.Span<byte> span = MemoryExtensions.AsSpan<byte>(array, 0, num2); string text4 = Convert.ToHexString(SHA256.HashData(System.Span<byte>.op_Implicit(span))).Substring(0, 20).ToLowerInvariant(); string text5 = string.Concat(new string[5] { "appearance/assets/", label, "-", text4, text2 }); string text6 = AppearanceStore.SafePath(text5); if (!File.Exists(text6)) { FileStream val = File.Create(text6); try { ((Stream)val).Write(System.Span<byte>.op_Implicit(span)); } finally { ((System.IDisposable)val)?.Dispose(); } } return text5; } } public sealed class AppearanceLayer { [field: CompilerGenerated] public double X { [CompilerGenerated] get; [CompilerGenerated] set; } [field: CompilerGenerated] public double Y { [CompilerGenerated] get; [CompilerGenerated] set; } [field: CompilerGenerated] public double Scale { [CompilerGenerated] get; [CompilerGenerated] set; } = 100.0; [field: CompilerGenerated] public double Opacity { [CompilerGenerated] get; [CompilerGenerated] set; } = 100.0; [field: CompilerGenerated] public bool Visible { [CompilerGenerated] get; [CompilerGenerated] set; } = true; [field: CompilerGenerated] public bool Mirror { [CompilerGenerated] get; [CompilerGenerated] set; } [field: CompilerGenerated] public string Font { [CompilerGenerated] get; [CompilerGenerated] set; } = "geometric"; [field: CompilerGenerated] public string Color { [CompilerGenerated] get; [CompilerGenerated] set; } = "#F1EDF9"; [field: CompilerGenerated] public double FontSize { [CompilerGenerated] get; [CompilerGenerated] set; } } public sealed class AppearanceCanvas { [field: CompilerGenerated] public int Width { [CompilerGenerated] get; [CompilerGenerated] set; } [field: CompilerGenerated] public int Height { [CompilerGenerated] get; [CompilerGenerated] set; } [field: CompilerGenerated] public int Columns { [CompilerGenerated] get; [CompilerGenerated] set; } = 5; [field: CompilerGenerated] public int Gap { [CompilerGenerated] get; [CompilerGenerated] set; } = 18; [field: CompilerGenerated] public string Anchor { [CompilerGenerated] get; [CompilerGenerated] set; } = "top-center"; [field: CompilerGenerated] public int ScreenX { [CompilerGenerated] get; [CompilerGenerated] set; } [field: CompilerGenerated] public int ScreenY { [CompilerGenerated] get; [CompilerGenerated] set; } = 24; [field: CompilerGenerated] public double PanelScale { [CompilerGenerated] get; [CompilerGenerated] set; } = 100.0; [field: CompilerGenerated] public bool PreserveFontSize { [CompilerGenerated] get; [CompilerGenerated] set; } = true; [field: CompilerGenerated] public double TextScale { [CompilerGenerated] get; [CompilerGenerated] set; } = 100.0; [field: CompilerGenerated] public string OverlayStyle { [CompilerGenerated] get; [CompilerGenerated] set; } = "infalsus"; [field: CompilerGenerated] public bool IncludeInGameScreenshots { [CompilerGenerated] get; [CompilerGenerated] set; } [field: CompilerGenerated] public string CardFont { [CompilerGenerated] get; [CompilerGenerated] set; } = "geometric"; [field: CompilerGenerated] public string CardColor { [CompilerGenerated] get; [CompilerGenerated] set; } = "#F1EDF9"; [field: CompilerGenerated] public double CardScale { [CompilerGenerated] get; [CompilerGenerated] set; } = 100.0; [field: CompilerGenerated] public bool JacketGrades { [CompilerGenerated] get; [CompilerGenerated] set; } = true; [field: CompilerGenerated] public bool JacketClearStates { [CompilerGenerated] get; [CompilerGenerated] set; } = true; [field: CompilerGenerated] public double JacketGlow { [CompilerGenerated] get; [CompilerGenerated] set; } = 70.0; [field: CompilerGenerated] public double HeaderHeight { [CompilerGenerated] get; [CompilerGenerated] set; } = 17.0; [field: CompilerGenerated] public string BackgroundStyle { [CompilerGenerated] get; [CompilerGenerated] set; } = "existing"; [field: CompilerGenerated] public double ArtistScale { [CompilerGenerated] get; [CompilerGenerated] set; } = 125.0; [field: CompilerGenerated] public double DifficultyScale { [CompilerGenerated] get; [CompilerGenerated] set; } = 125.0; [field: CompilerGenerated] public double ScoreScale { [CompilerGenerated] get; [CompilerGenerated] set; } = 118.0; [field: CompilerGenerated] public string B30Style { [CompilerGenerated] get; [CompilerGenerated] set; } = "infalsus"; [field: CompilerGenerated] public bool ShowOverflow { [CompilerGenerated] get; [CompilerGenerated] set; } = true; [field: CompilerGenerated] public bool ExportTimestamp { [CompilerGenerated] get; [CompilerGenerated] set; } = true; } public sealed class AppearanceB30Layout { [field: CompilerGenerated] public Dictionary<string, AppearanceLayer> Layers { [CompilerGenerated] get; [CompilerGenerated] set; } = new Dictionary<string, AppearanceLayer>(); [field: CompilerGenerated] public AppearanceCanvas Canvas { [CompilerGenerated] get; [CompilerGenerated] set; } = new AppearanceCanvas(); } public sealed class AppearanceState { [field: CompilerGenerated] public bool B30Jackets { [CompilerGenerated] get; [CompilerGenerated] set; } = true; [field: CompilerGenerated] public int B30LayoutRevision { [CompilerGenerated] get; [CompilerGenerated] set; } [field: CompilerGenerated] public string Mode { [CompilerGenerated] get; [CompilerGenerated] set; } = "overlay"; [field: CompilerGenerated] public string Selected { [CompilerGenerated] get; [CompilerGenerated] set; } = "art"; [field: CompilerGenerated] public string Accent { [CompilerGenerated] get; [CompilerGenerated] set; } = "#8979DC"; [field: CompilerGenerated] public bool Outline { [CompilerGenerated] get; [CompilerGenerated] set; } [field: CompilerGenerated] public string Art { [CompilerGenerated] get; [CompilerGenerated] set; } = "mirinae"; [field: CompilerGenerated] public string Bg { [CompilerGenerated] get; [CompilerGenerated] set; } = "gradient"; [field: CompilerGenerated] public string Name { [CompilerGenerated] get; [CompilerGenerated] set; } = "Player"; [field: CompilerGenerated] public string TitleLanguage { [CompilerGenerated] get; [CompilerGenerated] set; } = "ja"; [field: CompilerGenerated] public Dictionary<string, AppearanceB30Layout> B30Layouts { [CompilerGenerated] get; [CompilerGenerated] set; } = new Dictionary<string, AppearanceB30Layout>(); [field: CompilerGenerated] public Dictionary<string, AppearanceLayer> Overlay { [CompilerGenerated] get; [CompilerGenerated] set; } = DefaultLayers(b30: false); [field: CompilerGenerated] public Dictionary<string, AppearanceLayer> B30 { [CompilerGenerated] get; [CompilerGenerated] set; } = DefaultLayers(b30: true); [field: CompilerGenerated] public Dictionary<string, string> ImageIds { [CompilerGenerated] get; [CompilerGenerated] set; } = new Dictionary<string, string> { ["art"] = null, ["bg"] = null }; [field: CompilerGenerated] public Dictionary<string, AppearanceCanvas> Canvas { [CompilerGenerated] get; [CompilerGenerated] set; } = new Dictionary<string, AppearanceCanvas> { ["overlay"] = new AppearanceCanvas { Width = 420, Height = 88 }, ["b30"] = DefaultB30Canvas() }; public static AppearanceCanvas DefaultB30Canvas() { return new AppearanceCanvas { Width = 1800, Height = 3240, Columns = 3, Gap = 44, HeaderHeight = 8.0, B30Style = "arc", BackgroundStyle = "infalsus", CardColor = "#F1F3FF" }; } public static Dictionary<string, AppearanceLayer> DefaultLayers(bool b30) { return new Dictionary<string, AppearanceLayer> { ["bg"] = new AppearanceLayer { X = 50.0, Y = 50.0 }, ["art"] = new AppearanceLayer { X = (b30 ? 3 : 81), Y = (b30 ? 6 : 0) }, ["name"] = new AppearanceLayer { X = (b30 ? 18 : 4), Y = (b30 ? 17 : 9), FontSize = (b30 ? 72 : 24) }, ["rating"] = new AppearanceLayer { X = (b30 ? 68 : 44), Y = 9.0, FontSize = (b30 ? 48 : 38), Font = (b30 ? "geometric" : "oxanium") }, ["delta"] = new AppearanceLayer { X = (b30 ? 6 : 44), Y = (b30 ? 70 : 69), FontSize = ((!b30) ? 16 : 0), Color = "#9BFFD1", Visible = !b30, Font = "oxanium" } }; } } public sealed class AppearanceResource { [field: CompilerGenerated] public string Id { [CompilerGenerated] get; [CompilerGenerated] set; } [field: CompilerGenerated] public string File { [CompilerGenerated] get; [CompilerGenerated] set; } [field: CompilerGenerated] public string OriginalFile { [CompilerGenerated] get; [CompilerGenerated] set; } [field: CompilerGenerated] public string Name { [CompilerGenerated] get; [CompilerGenerated] set; } [field: CompilerGenerated] public JsonElement? Crop { [CompilerGenerated] get; [CompilerGenerated] set; } } public sealed class AppearanceTheme { [field: CompilerGenerated] public int Version { [CompilerGenerated] get; [CompilerGenerated] set; } = 1; [field: CompilerGenerated] public string Preset { [CompilerGenerated] get; [CompilerGenerated] set; } = "poster"; [field: CompilerGenerated] public string ExportDirectory { [CompilerGenerated] get; [CompilerGenerated] set; } = ""; [field: CompilerGenerated] public string InterfaceLanguage { [CompilerGenerated] get; [CompilerGenerated] set; } = "zh"; [field: CompilerGenerated] public HotkeySettings Hotkeys { [CompilerGenerated] get; [CompilerGenerated] set; } = new HotkeySettings(); [field: CompilerGenerated] public AppearanceState State { [CompilerGenerated] get; [CompilerGenerated] set; } = new AppearanceState(); [field: CompilerGenerated] public Dictionary<string, AppearanceResource> Images { [CompilerGenerated] get; [CompilerGenerated] set; } = new Dictionary<string, AppearanceResource>(); [field: CompilerGenerated] public Dictionary<string, AppearanceResource> Fonts { [CompilerGenerated] get; [CompilerGenerated] set; } = new Dictionary<string, AppearanceResource>(); } public static class AppearanceStore { public static readonly JsonSerializerOptions JsonOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, PropertyNameCaseInsensitive = true, WriteIndented = true }; private static int _generation; private static readonly object Gate = new object(); private static System.DateTime _lastWrite; [field: CompilerGenerated] public static string Root { [CompilerGenerated] get; [CompilerGenerated] private set; } public static string ThemePath => Path.Combine(Root, "appearance", "theme.json"); [field: CompilerGenerated] public static AppearanceTheme Current { [CompilerGenerated] get; [CompilerGenerated] private set; } = new AppearanceTheme(); public static int Generation => Volatile.Read(ref _generation); public static ValueTuple<AppearanceTheme, int> Snapshot() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) lock (Gate) { return new ValueTuple<AppearanceTheme, int>(Current, _generation); } } public static void Initialize(string root) { Root = Path.GetFullPath(root); Directory.CreateDirectory(Path.Combine(Root, "appearance", "assets")); AppearanceConfigMigration.Apply(); if (!File.Exists(ThemePath)) { Save(new AppearanceTheme()); } else { Reload(); } } public static string SafePath(string relative) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrWhiteSpace(relative)) { return null; } if (Path.IsPathRooted(relative)) { throw new InvalidDataException("资源路径必须位于当前模组配置目录"); } string fullPath = Path.GetFullPath(Path.Combine(Root, relative.Replace('/', Path.DirectorySeparatorChar))); if (!fullPath.StartsWith(Root.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar, (StringComparison)5)) { throw new InvalidDataException("资源路径超出模组配置目录"); } return fullPath; } public static void Validate(AppearanceTheme theme) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_06e3: Unknown result type (might be due to invalid IL or missing references) //IL_06e8: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_0760: Unknown result type (might be due to invalid IL or missing references) //IL_0765: Unknown result type (might be due to invalid IL or missing references) //IL_0776: Unknown result type (might be due to invalid IL or missing references) //IL_077b: Unknown result type (might be due to invalid IL or missing references) //IL_078c: Unknown result type (might be due to invalid IL or missing references) //IL_0791: Unknown result type (might be due to invalid IL or missing references) //IL_07a4: Unknown result type (might be due to invalid IL or missing references) //IL_07a9: Unknown result type (might be due to invalid IL or missing references) //IL_0867: Unknown result type (might be due to invalid IL or missing references) //IL_086c: Unknown result type (might be due to invalid IL or missing references) //IL_08b2: Unknown result type (might be due to invalid IL or missing references) //IL_08b7: Unknown result type (might be due to invalid IL or missing references) if (theme == null || theme.Version != 1 || theme.State == null) { throw new InvalidDataException("外观配置格式错误"); } theme.ExportDirectory = B30ExportFiles.NormalizeDirectory(theme.ExportDirectory); theme.InterfaceLanguage = ((theme.InterfaceLanguage == "ja") ? "ja" : "zh"); AppearanceTheme appearanceTheme = theme; if (appearanceTheme.Hotkeys == null) { HotkeySettings hotkeySettings = (appearanceTheme.Hotkeys = new HotkeySettings()); } HotkeyBinding.Validate(theme.Hotkeys); AppearanceState state = theme.State; UpgradeDefaultB30Layout(state); state.TitleLanguage = ((state.TitleLanguage == "en") ? "en" : "ja"); if (!SupportedLayer(state.Selected)) { state.Selected = "rating"; } state.Name = (string.IsNullOrWhiteSpace(state.Name) ? "Player" : state.Name.Substring(0, Math.Min(state.Name.Length, 48))); string[] array = new string[2] { "overlay", "b30" }; AppearanceLayer appearanceLayer = default(AppearanceLayer); AppearanceCanvas appearanceCanvas = default(AppearanceCanvas); AppearanceState appearanceState; foreach (string text in array) { Dictionary<string, AppearanceLayer> val = AppearanceState.DefaultLayers(text == "b30"); Dictionary<string, AppearanceLayer> val2 = ((text == "overlay") ? state.Overlay : state.B30); if (val2 == null) { val2 = val; } RemoveUnsupportedLayers(val2); Enumerator<string, AppearanceLayer> enumerator = val.Keys.GetEnumerator(); try { while (enumerator.MoveNext()) { string current = enumerator.Current; if (!val2.TryGetValue(current, ref appearanceLayer) || appearanceLayer == null) { appearanceLayer = (val2[current] = val[current]); } appearanceLayer.X = Math.Clamp(double.IsFinite(appearanceLayer.X) ? appearanceLayer.X : 0.0, 0.0, 100.0); appearanceLayer.Y = Math.Clamp(double.IsFinite(appearanceLayer.Y) ? appearanceLayer.Y : 0.0, 0.0, 100.0); appearanceLayer.Scale = Math.Clamp(double.IsFinite(appearanceLayer.Scale) ? appearanceLayer.Scale : 100.0, 10.0, 400.0); appearanceLayer.Opacity = Math.Clamp(double.IsFinite(appearanceLayer.Opacity) ? appearanceLayer.Opacity : 100.0, 0.0, 100.0); appearanceLayer.FontSize = Math.Clamp(double.IsFinite(appearanceLayer.FontSize) ? appearanceLayer.FontSize : 0.0, 0.0, 400.0); } } finally { ((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } if (text == "overlay") { state.Overlay = val2; } else { state.B30 = val2; } appearanceState = state; if (appearanceState.Canvas == null) { Dictionary<string, AppearanceCanvas> val3 = (appearanceState.Canvas = new Dictionary<string, AppearanceCanvas>()); } if (!state.Canvas.TryGetValue(text, ref appearanceCanvas) || appearanceCanvas == null) { appearanceCanvas = (state.Canvas[text] = ((text == "overlay") ? new AppearanceCanvas { Width = 420, Height = 88 } : AppearanceState.DefaultB30Canvas())); } appearanceCanvas.Width = Math.Clamp(appearanceCanvas.Width, (text == "overlay") ? 240 : 800, (text == "overlay") ? 2000 : 4096); appearanceCanvas.Height = Math.Clamp(appearanceCanvas.Height, (text == "overlay") ? 80 : 800, (text == "overlay") ? 700 : 4096); appearanceCanvas.PanelScale = Math.Clamp(appearanceCanvas.PanelScale, 25.0, 200.0); appearanceCanvas.TextScale = Math.Clamp(double.IsFinite(appearanceCanvas.TextScale) ? appearanceCanvas.TextScale : 100.0, 50.0, 200.0); appearanceCanvas.Columns = Math.Clamp(appearanceCanvas.Columns, 3, 6); appearanceCanvas.Gap = Math.Clamp(appearanceCanvas.Gap, 4, 60); appearanceCanvas.CardScale = Math.Clamp(appearanceCanvas.CardScale, 60.0, 140.0); appearanceCanvas.JacketGlow = Math.Clamp(double.IsFinite(appearanceCanvas.JacketGlow) ? appearanceCanvas.JacketGlow : 70.0, 0.0, 100.0); appearanceCanvas.B30Style = ((appearanceCanvas.B30Style == "arc") ? "arc" : "infalsus"); appearanceCanvas.HeaderHeight = Math.Clamp(double.IsFinite(appearanceCanvas.HeaderHeight) ? appearanceCanvas.HeaderHeight : 17.0, (double)((appearanceCanvas.B30Style == "arc") ? 6 : 12), 30.0); appearanceCanvas.BackgroundStyle = ((appearanceCanvas.BackgroundStyle == "infalsus") ? "infalsus" : "existing"); appearanceCanvas.OverlayStyle = ((appearanceCanvas.OverlayStyle == "existing") ? "existing" : "infalsus"); appearanceCanvas.ArtistScale = Math.Clamp(double.IsFinite(appearanceCanvas.ArtistScale) ? appearanceCanvas.ArtistScale : 125.0, 75.0, 175.0); appearanceCanvas.DifficultyScale = Math.Clamp(double.IsFinite(appearanceCanvas.DifficultyScale) ? appearanceCanvas.DifficultyScale : 125.0, 75.0, 175.0); appearanceCanvas.ScoreScale = Math.Clamp(double.IsFinite(appearanceCanvas.ScoreScale) ? appearanceCanvas.ScoreScale : 118.0, 75.0, 160.0); appearanceCanvas.ScreenX = Math.Clamp(appearanceCanvas.ScreenX, -3000, 3000); appearanceCanvas.ScreenY = Math.Clamp(appearanceCanvas.ScreenY, -2000, 2000); } appearanceState = state; if (appearanceState.B30Layouts == null) { Dictionary<string, AppearanceB30Layout> val5 = (appearanceState.B30Layouts = new Dictionary<string, AppearanceB30Layout>()); } Enumerator<string, AppearanceB30Layout> enumerator2 = state.B30Layouts.Values.GetEnumerator(); try { while (enumerator2.MoveNext()) { AppearanceB30Layout current2 = enumerator2.Current; if (current2 != null) { RemoveUnsupportedLayers(current2.Layers); } } } finally { ((System.IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose(); } appearanceTheme = theme; if (appearanceTheme.Images == null) { Dictionary<string, AppearanceResource> val7 = (appearanceTheme.Images = new Dictionary<string, AppearanceResource>()); } appearanceTheme = theme; if (appearanceTheme.Fonts == null) { Dictionary<string, AppearanceResource> val7 = (appearanceTheme.Fonts = new Dictionary<string, AppearanceResource>()); } ValueTuple<string, string>[] array2 = new ValueTuple<string, string>[3] { new ValueTuple<string, string>("oxanium", "Oxanium"), new ValueTuple<string, string>("orbitron", "Orbitron"), new ValueTuple<string, string>("rajdhani", "Rajdhani") }; for (int i = 0; i < array2.Length; i++) { ValueTuple<string, string> val10 = array2[i]; string item = val10.Item1; string item2 = val10.Item2; string text2 = "appearance/builtin/fonts/" + item2 + "-Bold.ttf"; if (!theme.Fonts.ContainsKey(item) && File.Exists(SafePath(text2))) { theme.Fonts[item] = new AppearanceResource { Id = item, File = text2, Name = item2 + " · " + ((item == "oxanium") ? "切角数字(推荐)" : ((item == "orbitron") ? "几何数字" : "紧凑数字")) }; } } Enumerator<string, AppearanceResource> enumerator3 = theme.Images.Values.GetEnumerator(); try { while (enumerator3.MoveNext()) { AppearanceResource current3 = enumerator3.Current; SafePath(current3.File); SafePath(current3.OriginalFile); } } finally { ((System.IDisposable)enumerator3/*cast due to .constrained prefix*/).Dispose(); } enumerator3 = theme.Fonts.Values.GetEnumerator(); try { while (enumerator3.MoveNext()) { SafePath(enumerator3.Current.File); } } finally { ((System.IDisposable)enumerator3/*cast due to .constrained prefix*/).Dispose(); } } private static bool SupportedLayer(string key) { if (key == "bg" || key == "art" || key == "name" || key == "rating" || key == "delta") { return true; } return false; } private static void RemoveUnsupportedLayers(Dictionary<string, AppearanceLayer> layers) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) if (layers == null) { return; } Enumerator<string> enumerator = new List<string>((System.Collections.Generic.IEnumerable<string>)layers.Keys).GetEnumerator(); try { while (enumerator.MoveNext()) { string current = enumerator.Current; if (!SupportedLayer(current)) { layers.Remove(current); } } } finally { ((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } } private static void UpgradeDefaultB30Layout(AppearanceState state) { if (state.B30LayoutRevision >= 1) { return; } AppearanceState appearanceState = state; if (appearanceState.B30Layouts == null) { Dictionary<string, AppearanceB30Layout> val = (appearanceState.B30Layouts = new Dictionary<string, AppearanceB30Layout>()); } AppearanceCanvas appearanceCanvas = default(AppearanceCanvas); if (state.Canvas != null && state.Canvas.TryGetValue("b30", ref appearanceCanvas) && appearanceCanvas != null && appearanceCanvas.B30Style == "infalsus" && appearanceCanvas.Width == 2270 && appearanceCanvas.Height == 1830 && appearanceCanvas.Columns == 5 && appearanceCanvas.Gap == 18 && appearanceCanvas.HeaderHeight == 17.0 && state.B30 != null && LegacyLayer("name", 5.0, 17.0, 96.0) && LegacyLayer("rating", 49.0, 17.0, 128.0) && LegacyLayer("art", 82.0, 0.0, 0.0)) { AppearanceB30Layout appearanceB30Layout = Copy(new AppearanceB30Layout { Layers = state.B30, Canvas = state.Canvas["b30"] }); state.B30Layouts["infalsus"] = appearanceB30Layout; AppearanceB30Layout appearanceB30Layout2 = default(AppearanceB30Layout); if (state.B30Layouts.TryGetValue("arc", ref appearanceB30Layout2) && appearanceB30Layout2?.Canvas != null && appearanceB30Layout2.Layers != null) { AppearanceB30Layout appearanceB30Layout3 = Copy(appearanceB30Layout2); state.B30 = appearanceB30Layout3.Layers; state.Canvas["b30"] = appearanceB30Layout3.Canvas; } else { AppearanceB30Layout appearanceB30Layout4 = Copy(appearanceB30Layout); AppearanceCanvas canvas = appearanceB30Layout4.Canvas; canvas.Width = 1800; canvas.Height = 3240; canvas.Columns = 3; canvas.Gap = 44; canvas.HeaderHeight = 8.0; if (canvas.CardColor == "#F1EDF9") { canvas.CardColor = "#F1F3FF"; } AppearanceLayer appearanceLayer = appearanceB30Layout4.Layers["art"]; appearanceLayer.X = 3.0; appearanceLayer.Y = 6.0; AppearanceLayer appearanceLayer2 = appearanceB30Layout4.Layers["name"]; appearanceLayer2.X = 18.0; appearanceLayer2.Y = 17.0; appearanceLayer2.FontSize = 72.0; AppearanceLayer appearanceLayer3 = appearanceB30Layout4.Layers["rating"]; appearanceLayer3.X = 68.0; appearanceLayer3.Y = 9.0; appearanceLayer3.FontSize = 48.0; state.B30 = appearanceB30Layout4.Layers; state.Canvas["b30"] = canvas; } state.Canvas["b30"].B30Style = "arc"; } state.B30LayoutRevision = 1; [CompilerGenerated] static AppearanceB30Layout Copy(AppearanceB30Layout value) { return JsonSerializer.Deserialize<AppearanceB30Layout>(JsonSerializer.Serialize<AppearanceB30Layout>(value, JsonOptions), JsonOptions); } [CompilerGenerated] bool LegacyLayer(string key, double x, double y, double size) { AppearanceLayer appearanceLayer4 = default(AppearanceLayer); if (state.B30.TryGetValue(key, ref appearanceLayer4) && appearanceLayer4 != null && appearanceLayer4.X == x && appearanceLayer4.Y == y && appearanceLayer4.FontSize == size) { return appearanceLayer4.Scale == 100.0; } return false; } } public static void Save(AppearanceTheme theme) { lock (Gate) { Validate(theme); string text = JsonSerializer.Serialize<AppearanceTheme>(theme, JsonOptions); string text2 = ThemePath + ".tmp"; File.WriteAllText(text2, text); if (File.Exists(ThemePath)) { File.Copy(ThemePath, ThemePath + ".previous", true); } File.Move(text2, ThemePath, true); Current = theme; _lastWrite = File.GetLastWriteTimeUtc(ThemePath); Interlocked.Increment(ref _generation); } } public static void Reload() { lock (Gate) { AppearanceTheme appearanceTheme = JsonSerializer.Deserialize<AppearanceTheme>(File.ReadAllText(ThemePath), JsonOptions); Validate(appearanceTheme); Current = appearanceTheme; _lastWrite = File.GetLastWriteTimeUtc(ThemePath); Interlocked.Increment(ref _generation); } } public static void Poll() { if (File.Exists(ThemePath) && File.GetLastWriteTimeUtc(ThemePath) != _lastWrite) { try { Reload(); } catch (System.Exception ex) { Plugin.Logger.LogWarning((object)("[Appearance] 配置读取失败,沿用上一次有效样式: " + ex.Message)); _lastWrite = File.GetLastWriteTimeUtc(ThemePath); } } } public static string ImageFile(string target, AppearanceTheme theme) { string text = ((target == "art") ? theme.State.Art : theme.State.Bg); if (text == "gradient") { return null; } if (text == "custom") { AppearanceResource appearanceResource = default(AppearanceResource); if (!theme.Images.TryGetValue(target, ref appearanceResource)) { return null; } return SafePath(appearanceResource.File); } return SafePath("jacket/" + text + ".png"); } } public static class AppearanceRenderer { private sealed class PreviewImageEntry { public System.IntPtr Image; public long Stamp; public long Used; public int Bytes; public int Limit; } private sealed class RenderContext { public readonly object Gate; public readonly Dictionary<string, System.IntPtr> Images; public readonly Dictionary<string, ValueTuple<System.IntPtr, System.IntPtr>> Families; public readonly Dictionary<System.IntPtr, Dictionary<char, bool>> Glyphs; public readonly HashSet<string> PrivateFonts; public readonly Dictionary<string, PreviewImageEntry> Previews = new Dictionary<string, PreviewImageEntry>((IEqualityComparer<string>)(object)StringComparer.OrdinalIgnoreCase); public readonly Dictionary<System.IntPtr, ValueTuple<uint, uint>> OriginalSizes = new Dictionary<System.IntPtr, ValueTuple<uint, uint>>(); public int Generation = -1; public int PreviewBytes; public long UseCounter; public string Resources; public AppearanceTheme Theme; public bool Preview; public int SourceImageLimit; public RenderContext() : this(new object(), new Dictionary<string, System.IntPtr>((IEqualityComparer<string>)(object)StringComparer.OrdinalIgnoreCase), new Dictionary<string, ValueTuple<System.IntPtr, System.IntPtr>>(), new Dictionary<System.IntPtr, Dictionary<char, bool>>(), new HashSet<string>((IEqualityComparer<string>)(object)StringComparer.OrdinalIgnoreCase)) { } public RenderContext(object gate, Dictionary<string, System.IntPtr> images, Dictionary<string, ValueTuple<System.IntPtr, System.IntPtr>> families, Dictionary<System.IntPtr, Dictionary<char, bool>> glyphs, HashSet<string> fonts) { Gate = gate; Images = images; Families = families; Glyphs = glyphs; PrivateFonts = fonts; } } private readonly struct RenderScope : System.IDisposable { private readonly RenderContext previous; public RenderScope(RenderContext value) { previous = _context; _context = value; } public void Dispose() { _context = previous; } } private sealed class PreviewFile { [field: CompilerGenerated] public string File { [CompilerGenerated] get; [CompilerGenerated] set; } [field: CompilerGenerated] public uint Width { [CompilerGenerated] get; [CompilerGenerated] set; } [field: CompilerGenerated] public uint Height { [CompilerGenerated] get; [CompilerGenerated] set; } } private sealed class CachedExport { public string Key; public byte[] Png; } private struct StartupInput { public uint Version; public System.IntPtr Callback; public int SuppressThread; public int SuppressCodecs; } private struct Rect { public float X; public float Y; public float W; public float H; public Rect(float x, float y, float w, float h) { X = x; Y = y; W = w; H = h; } } private struct Point { public float X; public float Y; public Point(float x, float y) { X = x; Y = y; } } private struct Matrix { public float M00; public float M01; public float M02; public float M03; public float M04; public float M10; public float M11; public float M12; public float M13; public float M14; public float M20; public float M21; public float M22; public float M23; public float M24; public float M30; public float M31; public float M32; public float M33; public float M34; public float M40; public float M41; public float M42; public float M43; public float M44; } private static readonly object StartupGate = new object(); private static readonly object FontRegistrationGate = new object(); private static readonly Dictionary<string, int> FontRegistrations = new Dictionary<string, int>((IEqualityComparer<string>)(object)StringComparer.OrdinalIgnoreCase); [ThreadStatic] private static RenderContext _context; private const int PreviewImageBudget = 8388608; private static readonly object PreviewDiskGate = new object(); private const int PixelArgb = 2498570; private static readonly object Gate = new object(); private static readonly Dictionary<string, System.IntPtr> Images = new Dictionary<string, System.IntPtr>((IEqualityComparer<string>)(object)StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, ValueTuple<System.IntPtr, System.IntPtr>> Families = new Dictionary<string, ValueTuple<System.IntPtr, System.IntPtr>>(); private static readonly Dictionary<System.IntPtr, Dictionary<char, bool>> Glyphs = new Dictionary<System.IntPtr, Dictionary<char, bool>>(); private static readonly HashSet<string> PrivateGdiFonts = new HashSet<string>((IEqualityComparer<string>)(object)StringComparer.OrdinalIgnoreCase); private static readonly RenderContext OverlayContext = new RenderContext(Gate, Images, Families, Glyphs, PrivateGdiFonts); private static readonly RenderContext ExportContext = new RenderContext(); private static readonly RenderContext PreviewContext = new RenderContext { Preview = true }; private static readonly Dictionary<bool, CachedExport> Exports = new Dictionary<bool, CachedExport>(); private const int ExportCacheBudget = 8388608; private static readonly Guid Png = new Guid("557cf406-1a04-11d3-9a73-0000f81ef32e"); private static System.IntPtr _token; private static RenderContext Context => _context ?? OverlayContext; private static string ChartTitle(string id, AppearanceTheme theme) { return SongLocalization.Title(id, SongInfoCache.GetBaseName(id), SongInfoCache.GetName(id), theme.State.TitleLanguage); } private static string ChartArtist(string id, AppearanceTheme theme) { return SongLocalization.Artist(id, SongInfoCache.GetBaseName(id), SongInfoCache.GetArtist(id), theme.State.TitleLanguage); } [DllImport("gdiplus.dll", ExactSpelling = true)] private static extern int GdipScaleWorldTransform(System.IntPtr graphics, float sx, float sy, int order); [DllImport("gdiplus.dll", ExactSpelling = true)] private static extern int GdipDrawImageRectI(System.IntPtr graphics, System.IntPtr image, int x, int y, int width, int height); [DllImport("gdiplus.dll", ExactSpelling = true)] private static extern int GdipCloneBitmapAreaI(int x, int y, int width, int height, int format, System.IntPtr source, out System.IntPtr clone); private static string ResourceKey(AppearanceTheme theme) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) StringBuilder val = new StringBuilder(AppearanceStore.Root).Append('|').Append(theme.State.Art).Append('|') .Append(theme.State.Bg); Enumerator<string, AppearanceResource> enumerator = theme.Images.GetEnumerator(); try { while (enumerator.MoveNext()) { KeyValuePair<string, AppearanceResource> current = enumerator.Current; val.Append('|').Append(current.Key).Append(':') .Append(current.Value.File); } } finally { ((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } enumerator = theme.Fonts.GetEnumerator(); try { while (enumerator.MoveNext()) { KeyValuePair<string, AppearanceResource> current2 = enumerator.Current; val.Append('|').Append(current2.Key).Append(':') .Append(current2.Value.File); } } finally { ((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } return ((object)val).ToString(); } private static void ClearContext(RenderContext context, bool clearPreviews = true) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) Enumerator<string, System.IntPtr> enumerator = context.Images.Values.GetEnumerator(); try { while (enumerator.MoveNext()) { DisposeBitmap(enumerator.Current); } } finally { ((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } context.Images.Clear(); Enumerator<string, ValueTuple<System.IntPtr, System.IntPtr>> enumerator2 = context.Families.Values.GetEnumerator(); try { while (enumerator2.MoveNext()) { ValueTuple<System.IntPtr, System.IntPtr> current = enumerator2.Current; if (current.Item1 != System.IntPtr.Zero) { GdipDeleteFontFamily(current.Item1); } System.IntPtr collection = current.Item2; if (collection != System.IntPtr.Zero) { GdipDeletePrivateFontCollection(ref collection); } } } finally { ((System.IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose(); } context.Families.Clear(); context.Glyphs.Clear(); Enumerator<string> enumerator3 = context.PrivateFonts.GetEnumerator(); try { while (enumerator3.MoveNext()) { ReleasePrivateFont(enumerator3.Current); } } finally { ((System.IDisposable)enumerator3/*cast due to .constrained prefix*/).Dispose(); } context.PrivateFonts.Clear(); if (clearPreviews) { ClearPreviewImages(context); } } private static void ClearPreviewImages(RenderContext context) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) Enumerator<string, PreviewImageEntry> enumerator = context.Previews.Values.GetEnumerator(); try { while (enumerator.MoveNext()) { DisposeBitmap(enumerator.Current.Image); } } finally { ((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } context.Previews.Clear(); context.OriginalSizes.Clear(); context.PreviewBytes = 0; } private static void RemovePreview(RenderContext context, string file) { PreviewImageEntry previewImageEntry = context.Previews[file]; context.Previews.Remove(file); context.OriginalSizes.Remove(previewImageEntry.Image); context.PreviewBytes -= previewImageEntry.Bytes; DisposeBitmap(previewImageEntry.Image); } private static void RegisterPrivateFont(string file) { lock (FontRegistrationGate) { int num = default(int); if (FontRegistrations.TryGetValue(file, ref num)) { FontRegistrations[file] = num + 1; } else if (AddFontResourceExW(file, 16u, System.IntPtr.Zero) > 0) { FontRegistrations[file] = 1; } } } private static void ReleasePrivateFont(string file) { lock (FontRegistrationGate) { int num = default(int); if (FontRegistrations.TryGetValue(file, ref num)) { if (num > 1) { FontRegistrations[file] = num - 1; return; } FontRegistrations.Remove(file); RemoveFontResourceExW(file, 16u, System.IntPtr.Zero); } } } private static System.IntPtr PreviewImage(string file, int limit = 0) { //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) bool flag = limit > 0; if (limit == 0) { limit = PreviewLimit(file); } RenderContext context = Context; long ticks = File.GetLastWriteTimeUtc(file).Ticks; PreviewImageEntry previewImageEntry = default(PreviewImageEntry); if (context.Previews.TryGetValue(file, ref previewImageEntry)) { if (previewImageEntry.Stamp == ticks && previewImageEntry.Limit == limit) { previewImageEntry.Used = ++context.UseCounter; return previewImageEntry.Image; } RemovePreview(context, file); } string directory = Path.Combine(AppearanceStore.Root, "appearance", "cache", flag ? "completion-thumbnails" : "thumbnails"); PreviewFile previewFile = PreparePreviewImage(file, directory, limit); if (previewFile == null) { return System.IntPtr.Zero; } System.IntPtr intPtr = LoadPreviewBitmap(previewFile.File); if (intPtr == System.IntPtr.Zero) { return System.IntPtr.Zero; } try { GdipGetImageWidth(intPtr, out var w); GdipGetImageHeight(intPtr, out var h); int num = checked((int)(w * h * 4)); while (context.PreviewBytes + num > 8388608 && context.Previews.Count > 0) { string file2 = null; long num2 = 9223372036854775807L; Enumerator<string, PreviewImageEntry> enumerator = context.Previews.GetEnumerator(); try { while (enumerator.MoveNext()) { KeyValuePair<string, PreviewImageEntry> current = enumerator.Current; if (current.Value.Used < num2) { num2 = current.Value.Used; file2 = current.Key; } } } finally { ((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } RemovePreview(context, file2); } context.Previews[file] = new PreviewImageEntry { Image = intPtr, Stamp = ticks, Used = ++context.UseCounter, Bytes = num, Limit = limit }; context.OriginalSizes[intPtr] = new ValueTuple<uint, uint>(previewFile.Width, previewFile.Height); context.PreviewBytes += num; System.IntPtr result = intPtr; intPtr = System.IntPtr.Zero; return result; } finally { DisposeBitmap(intPtr); } } private static System.IntPtr LoadPreviewBitmap(string file) { if (GdipCreateBitmapFromFile(file, out var bitmap) != 0) { return System.IntPtr.Zero; } try { GdipGetImageWidth(bitmap, out var w); GdipGetImageHeight(bitmap, out var h); Check(GdipCloneBitmapAreaI(0, 0, (int)w, (int)h, 2498570, bitmap, out var clone), "preview detach"); return clone; } finally { DisposeBitmap(bitmap); } } private static int PreviewLimit(string file) { if (file.IndexOf(Path.DirectorySeparatorChar + "jacket" + Path.DirectorySeparatorChar, (StringComparison)5) < 0) { return 768; } return 160; } private static PreviewFile PreparePreviewImage(string file, string directory, int limit) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown //IL_022a: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Unknown result type (might be due to invalid IL or missing references) FileInfo val = new FileInfo(file); if (!((FileSystemInfo)val).Exists) { return null; } string text = Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(string.Concat(new string[8] { file, "|", ((FileSystemInfo)val).LastWriteTimeUtc.Ticks.ToString(), "|", val.Length.ToString(), "|", limit.ToString(), "|1" })))).ToLowerInvariant(); string text2 = Path.Combine(directory, "b30-" + text + ".png"); string text3 = text2 + ".json"; lock (PreviewDiskGate) { if (File.Exists(text2) && File.Exists(text3)) { try { PreviewFile previewFile = JsonSerializer.Deserialize<PreviewFile>(File.ReadAllText(text3), (JsonSerializerOptions)null); if (previewFile.Width != 0 && previewFile.Height != 0) { previewFile.File = text2; return previewFile; } } catch (JsonException) { } } Directory.CreateDirectory(directory); lock (StartupGate) { if (_token == System.IntPtr.Zero) { StartupInput input = new StartupInput { Version = 1u }; Check(GdiplusStartup(out _token, ref input, System.IntPtr.Zero), "GDI+ startup"); } } if (GdipCreateBitmapFromFile(file, out var bitmap) != 0) { return null; } System.IntPtr image = System.IntPtr.Zero; try { GdipGetImageWidth(bitmap, out var w); GdipGetImageHeight(bitmap, out var h); double num = Math.Min(1.0, (double)limit / (double)Math.Max(w, h)); int num2 = Math.Max(1, (int)Math.Round((double)w * num)); int num3 = Math.Max(1, (int)Math.Round((double)h * num)); image = Bitmap(num2, num3, out var g); try { Check(GdipDrawImageRectI(g, bitmap, 0, 0, num2, num3), "preview image"); } finally { GdipDeleteGraphics(g); } string text4 = text2 + ".tmp"; Guid encoder = Png; Check(GdipSaveImageToFile(image, text4, ref encoder, System.IntPtr.Zero), "preview cache"); File.Move(text4, text2, true); PreviewFile previewFile2 = new PreviewFile { File = text2, Width = w, Height = h }; File.WriteAllText(text3, JsonSerializer.Serialize<PreviewFile>(previewFile2, (JsonSerializerOptions)null)); TrimPreviewFiles(directory); return previewFile2; } finally { DisposeBitmap(image); DisposeBitmap(bitmap); } } } private static void TrimPreviewFiles(string directory) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) List<FileInfo> val = new List<FileInfo>(); long num = 0L; string[] files = Directory.GetFiles(directory, "b30-*.png"); for (int i = 0; i < files.Length; i++) { FileInfo val2 = new FileInfo(files[i]); val.Add(val2); num += val2.Length; } val.Sort((Comparison<FileInfo>)((FileInfo a, FileInfo b) => ((FileSystemInfo)a).LastWriteTimeUtc.CompareTo(((FileSystemInfo)b).LastWriteTimeUtc))); Enumerator<FileInfo> enumerator = val.GetEnumerator(); try { while (enumerator.MoveNext()) { FileInfo current = enumerator.Current; if (num <= 16777216) { break; } if (((FileSystemInfo)current).Name.Length != 72) { continue; } try { long length = current.Length; ((FileSystemInfo)current).Delete(); num -= length; string text = ((FileSystemInfo)current).FullName + ".json"; if (File.Exists(text)) { File.Delete(text); } } catch (IOException) { } } } finally { ((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } } public static int PreparePreviewCache(string root) { //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) root = Path.GetFullPath(root); string directory = Path.Combine(root, "appearance", "cache", "thumbnails"); HashSet<string> val = new HashSet<string>((IEqualityComparer<string>)(object)StringComparer.OrdinalIgnoreCase); string[] array = new string[2] { "jacket", "appearance/builtin" }; foreach (string text in array) { string text2 = Path.Combine(root, text); if (Directory.Exists(text2)) { string[] files = Directory.GetFiles(text2, "*.png"); foreach (string text3 in files) { val.Add(text3); } } } string text4 = Path.Combine(root, "appearance", "theme.json"); if (File.Exists(text4)) { Enumerator<string, AppearanceResource> enumerator = JsonSerializer.Deserialize<AppearanceTheme>(File.ReadAllText(text4), AppearanceStore.JsonOptions).Images.Values.GetEnumerator(); try { while (enumerator.MoveNext()) { AppearanceResource current = enumerator.Current; string fullPath = Path.GetFullPath(Path.Combine(root, current.File)); if (fullPath.StartsWith(root + Path.DirectorySeparatorChar, (StringComparison)5) && File.Exists(fullPath)) { val.Add(fullPath); } } } finally { ((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } } int num = 0; Enumerator<string> enumerator2 = val.GetEnumerator(); try { while (enumerator2.MoveNext()) { string current2 = enumerator2.Current; if (PreparePreviewImage(current2, directory, PreviewLimit(current2)) != null) { num++; } } return num; } finally { ((System.IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose(); } } public static bool PreviewB30(string file, string name, double rating, List<ChartRating> charts, AppearanceTheme theme, int maximumWidth = 1000) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) return SaveB30(file, name, rating, charts, theme.State.B30Jackets, DateTimeOffset.Now, theme, Math.Clamp(maximumWidth, 400, 1200)); } [DllImport("gdiplus.dll", ExactSpelling = true)] private static extern int GdiplusStartup(out System.IntPtr token, ref StartupInput input, System.IntPtr output); [DllImport("gdiplus.dll", ExactSpelling = true)] private static extern int GdipCreateBitmapFromScan0(int w, int h, int stride, int format, System.IntPtr data, out System.IntPtr bitmap); [DllImport("gdiplus.dll", CharSet = (CharSet)3, ExactSpelling = true)] private static extern int GdipCreateBitmapFromFile(string file, out System.IntPtr bitmap); [DllImport("gdiplus.dll", ExactSpelling = true)] private static extern int GdipDisposeImage(System.IntPtr image); [DllImport("gdiplus.dll", ExactSpelling = true)] private