Decompiled source of PeakMapBrowser v0.1.1
PeakMapBrowser.dll
Decompiled 4 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.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Steamworks; using UnityEngine; using UnityEngine.Networking; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace PeakMapBrowser { internal sealed class MapsResponse { public bool success; public List<MapEntry> data; public PaginationInfo pagination; public string error_code; public string error; public string error_message; } internal sealed class ModVersionsResponse { public bool success; public List<ModVersionEntry> data; public string error_code; public string error; public string error_message; } internal sealed class AuthResponse { public bool success; public string access_token; public string refresh_token; public long expires_at; public int expires_in; public AccountUser user; public string error; public string error_message; } internal sealed class AccountUser { public string id; public string email; public string nickname; } internal sealed class LikeResponse { public bool success; public bool liked; public int likes; public string error; public string error_message; } internal sealed class BasicResponse { public bool success; public string message; public string error; public string error_message; } internal sealed class PeakMapSession { public string access_token; public string refresh_token; public long expires_at; public string user_id; public string email; public string nickname; public string guest_id; public bool HasUser => !string.IsNullOrEmpty(access_token) && !string.IsNullOrEmpty(refresh_token); public bool HasRefreshToken => !string.IsNullOrEmpty(refresh_token); public string DisplayName { get { if (!string.IsNullOrWhiteSpace(nickname)) { return nickname; } if (!string.IsNullOrWhiteSpace(email)) { return email; } return string.Empty; } } } internal sealed class MapEntry { public string id; public string name; public string author; public string mod_version; public string description; public int downloads; public int likes; public string created_at; public string updated_at; public int revision; public bool liked_by_me; public string image_url; public string thumbnail_url; public string json_file_url; public string download_url; } internal sealed class PaginationInfo { public int page; public int page_size; public int total; public int total_pages; public bool has_next; public bool has_prev; } internal sealed class ModVersionEntry { public string id; public string version_name; public string created_at; } internal static class MapImageCache { private const long MaxCacheBytes = 268435456L; private const int MaxCacheFiles = 300; private static readonly object Sync = new object(); private static string CacheDirectory => Path.Combine(Application.persistentDataPath, "PeakMapBrowser", "ImageCache"); public static string GetExistingPath(string url) { if (string.IsNullOrEmpty(url)) { return null; } lock (Sync) { try { string cachePath = GetCachePath(url); if (!File.Exists(cachePath) || new FileInfo(cachePath).Length == 0) { return null; } Touch(cachePath); return cachePath; } catch { return null; } } } public static void Remove(string url) { if (string.IsNullOrEmpty(url)) { return; } lock (Sync) { try { string cachePath = GetCachePath(url); if (File.Exists(cachePath)) { File.Delete(cachePath); } } catch { } } } public static void Save(string url, byte[] bytes) { if (string.IsNullOrEmpty(url) || bytes == null || bytes.Length == 0) { return; } lock (Sync) { try { Directory.CreateDirectory(CacheDirectory); string cachePath = GetCachePath(url); if (File.Exists(cachePath)) { Touch(cachePath); return; } string text = cachePath + ".tmp-" + Guid.NewGuid().ToString("N"); try { File.WriteAllBytes(text, bytes); File.Move(text, cachePath); } finally { if (File.Exists(text)) { File.Delete(text); } } PruneCache(); } catch { } } } private static string GetCachePath(string url) { Directory.CreateDirectory(CacheDirectory); return Path.Combine(CacheDirectory, "image-" + Hash(url) + ".cache"); } private static string Hash(string value) { using SHA256 sHA = SHA256.Create(); byte[] array = sHA.ComputeHash(Encoding.UTF8.GetBytes(value)); StringBuilder stringBuilder = new StringBuilder(array.Length * 2); for (int i = 0; i < array.Length; i++) { stringBuilder.Append(array[i].ToString("x2")); } return stringBuilder.ToString(); } private static void Touch(string path) { try { File.SetLastAccessTimeUtc(path, DateTime.UtcNow); } catch { } } private static void PruneCache() { string[] files = Directory.GetFiles(CacheDirectory, "*.cache", SearchOption.TopDirectoryOnly); Array.Sort(files, (string a, string b) => File.GetLastAccessTimeUtc(a).CompareTo(File.GetLastAccessTimeUtc(b))); long num = 0L; for (int num2 = 0; num2 < files.Length; num2++) { num += new FileInfo(files[num2]).Length; } int num3 = Mathf.Max(0, files.Length - 300); for (int num4 = 0; num4 < files.Length && (num > 268435456 || num4 < num3); num4++) { try { long length = new FileInfo(files[num4]).Length; File.Delete(files[num4]); num -= length; } catch { } } } } internal static class MapSaveService { public static string SavePath => Path.Combine(Application.persistentDataPath, "TerrainCustomiser", "Map Saves"); public static string CoverPath => Path.Combine(SavePath, "Covers"); public static string BackupPath => Path.Combine(SavePath, "Backups"); private static string IndexPath => Path.Combine(Application.persistentDataPath, "PeakMapBrowser", "map-index.json"); public static string PicturesPath => Environment.GetFolderPath(Environment.SpecialFolder.MyPictures); public static string[] GetLocalJsonFiles() { EnsureSaveDirectory(); string[] files = Directory.GetFiles(SavePath, "*.json"); Array.Sort(files, (string a, string b) => File.GetLastWriteTimeUtc(b).CompareTo(File.GetLastWriteTimeUtc(a))); return files; } public static string[] GetLocalImageFiles() { EnsureSaveDirectory(); string[] imageRootPaths = GetImageRootPaths(); List<string> list = new List<string>(); for (int i = 0; i < imageRootPaths.Length; i++) { if (!Directory.Exists(imageRootPaths[i])) { continue; } string[] files = Directory.GetFiles(imageRootPaths[i], "*.*", SearchOption.TopDirectoryOnly); for (int j = 0; j < files.Length; j++) { if (IsSupportedImage(files[j])) { list.Add(files[j]); } } } list.Sort((string a, string b) => File.GetLastWriteTimeUtc(b).CompareTo(File.GetLastWriteTimeUtc(a))); return list.ToArray(); } public static void EnsureSaveDirectory() { Directory.CreateDirectory(SavePath); Directory.CreateDirectory(CoverPath); Directory.CreateDirectory(BackupPath); } public static string[] GetImageRootPaths() { EnsureSaveDirectory(); if (string.IsNullOrEmpty(PicturesPath)) { return new string[2] { SavePath, CoverPath }; } return new string[3] { SavePath, CoverPath, PicturesPath }; } public static string GetImageRootLabel(string root) { string a = NormalizePath(root); if (string.Equals(a, NormalizePath(SavePath), StringComparison.OrdinalIgnoreCase)) { return "Map Saves"; } if (string.Equals(a, NormalizePath(CoverPath), StringComparison.OrdinalIgnoreCase)) { return "Covers"; } if (!string.IsNullOrEmpty(PicturesPath) && string.Equals(a, NormalizePath(PicturesPath), StringComparison.OrdinalIgnoreCase)) { return "Pictures"; } return DisplayName(root); } public static bool TryNormalizeWhitelistedDirectory(string path, out string normalized) { normalized = null; if (string.IsNullOrEmpty(path) || IsNetworkPath(path)) { return false; } string text; try { text = NormalizePath(path); } catch { return false; } if (!Directory.Exists(text) || IsHiddenOrSystem(text)) { return false; } string[] imageRootPaths = GetImageRootPaths(); for (int i = 0; i < imageRootPaths.Length; i++) { if (IsInsideRoot(text, imageRootPaths[i])) { normalized = text; return true; } } return false; } public static bool TryNormalizeWhitelistedImage(string path, out string normalized) { normalized = null; if (string.IsNullOrEmpty(path) || IsNetworkPath(path) || !IsSupportedImage(path)) { return false; } string text; try { text = NormalizePath(path); } catch { return false; } if (!File.Exists(text)) { return false; } string directoryName = Path.GetDirectoryName(text); if (!TryNormalizeWhitelistedDirectory(directoryName, out var _)) { return false; } normalized = text; return true; } public static string[] GetChildDirectories(string directory) { if (!TryNormalizeWhitelistedDirectory(directory, out var normalized)) { return new string[0]; } string[] directories = Directory.GetDirectories(normalized, "*", SearchOption.TopDirectoryOnly); List<string> list = new List<string>(); for (int i = 0; i < directories.Length; i++) { if (TryNormalizeWhitelistedDirectory(directories[i], out var normalized2)) { list.Add(normalized2); } } list.Sort(StringComparer.OrdinalIgnoreCase); return list.ToArray(); } public static string[] GetImageFilesInDirectory(string directory) { if (!TryNormalizeWhitelistedDirectory(directory, out var normalized)) { return new string[0]; } string[] files = Directory.GetFiles(normalized, "*.*", SearchOption.TopDirectoryOnly); List<string> list = new List<string>(); for (int i = 0; i < files.Length; i++) { if (TryNormalizeWhitelistedImage(files[i], out var normalized2)) { list.Add(normalized2); } } list.Sort((string a, string b) => File.GetLastWriteTimeUtc(b).CompareTo(File.GetLastWriteTimeUtc(a))); return list.ToArray(); } public static string SaveDownloadedMap(MapEntry map, byte[] bytes) { return SaveDownloadedMap(map, bytes, allowOverwriteLocalChanges: false); } public static string SaveDownloadedMap(MapEntry map, byte[] bytes, bool allowOverwriteLocalChanges) { if (map == null) { throw new ArgumentNullException("map"); } if (bytes == null) { throw new ArgumentNullException("bytes"); } EnsureSaveDirectory(); MapDownloadInfo downloadInfo = GetDownloadInfo(map); if (downloadInfo.Status == MapDownloadStatus.UpToDate) { return downloadInfo.Path; } if (downloadInfo.Status == MapDownloadStatus.LocalModified && !allowOverwriteLocalChanges) { throw new MapSaveException(MapDownloadStatus.LocalModified, "LOCAL_MODIFIED"); } string value = (string.IsNullOrWhiteSpace(map.name) ? "peak-map" : map.name.Trim()); string text = SanitizeFileName(value); if (string.IsNullOrEmpty(text)) { text = "peak-map"; } string text2 = FindRecord(map.id)?.path; if (string.IsNullOrEmpty(text2) || !IsInsideRoot(text2, SavePath)) { text2 = Path.Combine(SavePath, text + ".json"); int num = 2; while (File.Exists(text2)) { text2 = Path.Combine(SavePath, text + "-" + num + ".json"); num++; } } string text3 = text2 + ".tmp-" + Guid.NewGuid().ToString("N"); try { File.WriteAllBytes(text3, bytes); if (File.Exists(text2)) { string destFileName = Path.Combine(BackupPath, Path.GetFileNameWithoutExtension(text2) + "-" + DateTime.UtcNow.ToString("yyyyMMddHHmmssfff") + ".json"); File.Copy(text2, destFileName, overwrite: false); File.Replace(text3, text2, null); } else { File.Move(text3, text2); } SaveRecord(map, text2, ComputeSha256(bytes)); } finally { if (File.Exists(text3)) { File.Delete(text3); } } return text2; } public static MapDownloadInfo GetDownloadInfo(MapEntry map) { MapDownloadInfo mapDownloadInfo = new MapDownloadInfo(); if (map == null || string.IsNullOrEmpty(map.id)) { mapDownloadInfo.Status = MapDownloadStatus.New; return mapDownloadInfo; } LocalMapRecord localMapRecord = FindRecord(map.id); if (localMapRecord == null || string.IsNullOrEmpty(localMapRecord.path) || !File.Exists(localMapRecord.path)) { mapDownloadInfo.Status = MapDownloadStatus.New; mapDownloadInfo.CurrentRevision = map.revision; return mapDownloadInfo; } mapDownloadInfo.Path = localMapRecord.path; mapDownloadInfo.LocalRevision = localMapRecord.revision; mapDownloadInfo.CurrentRevision = map.revision; mapDownloadInfo.LocalHash = localMapRecord.sha256; string a = ComputeSha256(localMapRecord.path); if (!string.Equals(a, localMapRecord.sha256, StringComparison.OrdinalIgnoreCase)) { mapDownloadInfo.Status = MapDownloadStatus.LocalModified; } else if (map.revision > localMapRecord.revision) { mapDownloadInfo.Status = MapDownloadStatus.UpdateAvailable; } else { mapDownloadInfo.Status = MapDownloadStatus.UpToDate; } return mapDownloadInfo; } private static LocalMapRecord FindRecord(string mapId) { if (string.IsNullOrEmpty(mapId)) { return null; } LocalMapIndex localMapIndex = LoadIndex(); for (int i = 0; i < localMapIndex.maps.Count; i++) { if (string.Equals(localMapIndex.maps[i].map_id, mapId, StringComparison.Ordinal)) { return localMapIndex.maps[i]; } } return null; } private static void SaveRecord(MapEntry map, string path, string sha256) { LocalMapIndex localMapIndex = LoadIndex(); LocalMapRecord localMapRecord = null; for (int i = 0; i < localMapIndex.maps.Count; i++) { if (string.Equals(localMapIndex.maps[i].map_id, map.id, StringComparison.Ordinal)) { localMapRecord = localMapIndex.maps[i]; break; } } if (localMapRecord == null) { localMapRecord = new LocalMapRecord(); localMapRecord.map_id = map.id; localMapIndex.maps.Add(localMapRecord); } localMapRecord.path = path; localMapRecord.name = map.name ?? string.Empty; localMapRecord.revision = map.revision; localMapRecord.sha256 = sha256; SaveIndex(localMapIndex); } private static LocalMapIndex LoadIndex() { try { if (!File.Exists(IndexPath)) { return new LocalMapIndex(); } LocalMapIndex localMapIndex = JsonConvert.DeserializeObject<LocalMapIndex>(File.ReadAllText(IndexPath)); return localMapIndex ?? new LocalMapIndex(); } catch { return new LocalMapIndex(); } } private static void SaveIndex(LocalMapIndex index) { string directoryName = Path.GetDirectoryName(IndexPath); Directory.CreateDirectory(directoryName); string text = IndexPath + ".tmp-" + Guid.NewGuid().ToString("N"); try { File.WriteAllText(text, JsonConvert.SerializeObject((object)index, (Formatting)1), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); if (File.Exists(IndexPath)) { File.Replace(text, IndexPath, null); } else { File.Move(text, IndexPath); } } finally { if (File.Exists(text)) { File.Delete(text); } } } private static string ComputeSha256(string path) { using FileStream inputStream = File.OpenRead(path); using SHA256 sHA = SHA256.Create(); return ToHex(sHA.ComputeHash(inputStream)); } private static string ComputeSha256(byte[] bytes) { using SHA256 sHA = SHA256.Create(); return ToHex(sHA.ComputeHash(bytes)); } private static string ToHex(byte[] bytes) { StringBuilder stringBuilder = new StringBuilder(bytes.Length * 2); for (int i = 0; i < bytes.Length; i++) { stringBuilder.Append(bytes[i].ToString("x2")); } return stringBuilder.ToString(); } public static string SanitizeFileName(string value) { char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); StringBuilder stringBuilder = new StringBuilder(value.Length); foreach (char c in value) { bool flag = false; for (int j = 0; j < invalidFileNameChars.Length; j++) { if (c == invalidFileNameChars[j]) { flag = true; break; } } if (!flag) { stringBuilder.Append(c); } } return stringBuilder.ToString().Trim(); } public static string DisplayName(string path) { return string.IsNullOrEmpty(path) ? string.Empty : Path.GetFileName(path); } public static bool IsSupportedImage(string path) { string text = Path.GetExtension(path).ToLowerInvariant(); int result; switch (text) { default: result = ((text == ".gif") ? 1 : 0); break; case ".png": case ".jpg": case ".jpeg": case ".webp": result = 1; break; } return (byte)result != 0; } private static bool IsHiddenOrSystem(string path) { try { FileAttributes attributes = File.GetAttributes(path); return (attributes & FileAttributes.Hidden) != FileAttributes.None || (attributes & FileAttributes.System) != 0; } catch { return true; } } private static bool IsNetworkPath(string path) { return path.StartsWith("\\\\", StringComparison.Ordinal) || path.StartsWith("//", StringComparison.Ordinal); } private static bool IsInsideRoot(string candidate, string root) { string text = NormalizePath(candidate); string text2 = NormalizePath(root); if (string.Equals(text, text2, StringComparison.OrdinalIgnoreCase)) { return true; } string text3 = text2.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); char directorySeparatorChar = Path.DirectorySeparatorChar; string value = text3 + directorySeparatorChar; return text.StartsWith(value, StringComparison.OrdinalIgnoreCase); } private static string NormalizePath(string path) { return Path.GetFullPath(path).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); } public static string FindMatchingImage(string jsonPath, string[] imageFiles) { if (string.IsNullOrEmpty(jsonPath) || imageFiles == null || imageFiles.Length == 0) { return null; } string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(jsonPath); for (int i = 0; i < imageFiles.Length; i++) { string fileNameWithoutExtension2 = Path.GetFileNameWithoutExtension(imageFiles[i]); if (string.Equals(fileNameWithoutExtension, fileNameWithoutExtension2, StringComparison.OrdinalIgnoreCase)) { return imageFiles[i]; } } return null; } } internal enum MapDownloadStatus { New, UpToDate, UpdateAvailable, LocalModified } internal sealed class MapDownloadInfo { public MapDownloadStatus Status; public string Path; public int LocalRevision; public int CurrentRevision; public string LocalHash; } internal sealed class LocalMapIndex { public List<LocalMapRecord> maps = new List<LocalMapRecord>(); } internal sealed class LocalMapRecord { public string map_id; public string path; public string name; public int revision; public string sha256; } internal sealed class MapSaveException : Exception { public readonly MapDownloadStatus Status; public MapSaveException(MapDownloadStatus status, string message) : base(message) { Status = status; } } internal sealed class PeakMapApiClient { private readonly MonoBehaviour _runner; private readonly ManualLogSource _log; private readonly string _baseUrl; private string _language; public PeakMapSession Session { get; private set; } public bool IsSignedIn => Session != null && Session.HasUser; public bool ShouldRefreshSession { get { if (Session == null || !Session.HasRefreshToken) { return false; } if (string.IsNullOrEmpty(Session.access_token)) { return true; } if (Session.expires_at <= 0) { return false; } long num = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); return Session.expires_at - num < 300; } } public PeakMapApiClient(MonoBehaviour runner, ManualLogSource log, string baseUrl, string language) { _runner = runner; _log = log; _baseUrl = (baseUrl ?? "https://peakmap.top").TrimEnd(new char[1] { '/' }); _language = (string.Equals(language, "en", StringComparison.OrdinalIgnoreCase) ? "en" : "zh"); Session = PeakMapSessionStore.Load(log); } public void SetLanguage(string language) { _language = (string.Equals(language, "en", StringComparison.OrdinalIgnoreCase) ? "en" : "zh"); } public void SignOut() { PeakMapSessionStore.ClearUser(Session, _log); } public void SignOut(Action<bool, string> done) { _runner.StartCoroutine(SignOutRoutine(done)); } public void SignIn(string email, string password, Action<AuthResponse, string> done) { _runner.StartCoroutine(SignInRoutine(email, password, done)); } public void RefreshSession(Action<bool, string> done) { _runner.StartCoroutine(RefreshSessionRoutine(done)); } public void FetchMaps(int page, int pageSize, string query, string sort, string modVersion, Action<MapsResponse, string> done) { _runner.StartCoroutine(FetchMapsRoutine(page, pageSize, query, sort, modVersion, done)); } public void FetchAccountMaps(Action<MapsResponse, string> done) { _runner.StartCoroutine(FetchAccountMapsRoutine(done)); } public void FetchModVersions(Action<ModVersionsResponse, string> done) { _runner.StartCoroutine(FetchModVersionsRoutine(done)); } public void ToggleLike(MapEntry map, Action<LikeResponse, string> done) { _runner.StartCoroutine(ToggleLikeRoutine(map, done)); } public void DownloadMap(MapEntry map, Action<string, string> done) { DownloadMap(map, allowOverwriteLocalChanges: false, done); } public void DownloadMap(MapEntry map, bool allowOverwriteLocalChanges, Action<string, string> done) { _runner.StartCoroutine(DownloadMapRoutine(map, allowOverwriteLocalChanges, done)); } public MapDownloadInfo GetDownloadInfo(MapEntry map) { return MapSaveService.GetDownloadInfo(map); } public void UploadMap(string mapName, string author, string version, string description, string jsonPath, string imagePath, Action<string> done) { _runner.StartCoroutine(UploadMapRoutine(mapName, author, version, description, jsonPath, imagePath, done)); } public void UpdateMap(string mapId, string mapName, string author, string version, string description, string jsonPath, string imagePath, bool removeImage, Action<string> done) { _runner.StartCoroutine(UpdateMapRoutine(mapId, mapName, author, version, description, jsonPath, imagePath, removeImage, done)); } public void DeleteMap(string mapId, Action<string> done) { _runner.StartCoroutine(DeleteMapRoutine(mapId, done)); } public void DownloadTexture(string url, Action<Texture2D> done) { string existingPath = MapImageCache.GetExistingPath(url); if (!string.IsNullOrEmpty(existingPath)) { _runner.StartCoroutine(LoadCachedTextureRoutine(url, existingPath, done)); } else { _runner.StartCoroutine(DownloadTextureRoutine(url, done)); } } private IEnumerator SignInRoutine(string email, string password, Action<AuthResponse, string> done) { UnityWebRequest request = JsonRequest(json: JsonConvert.SerializeObject((object)new { email = ((email == null) ? string.Empty : email.Trim()), password = (password ?? string.Empty) }), url: _baseUrl + "/api/auth/sign-in", method: "POST"); try { yield return request.SendWebRequest(); AuthResponse response = Parse<AuthResponse>(Body(request)); if (HasError(request) || response == null || !response.success) { done(response, ResponseError(response, Text("登录失败", "Sign in failed"))); yield break; } ApplyAuthResponse(response); done(response, null); } finally { ((IDisposable)request)?.Dispose(); } } private IEnumerator RefreshSessionRoutine(Action<bool, string> done) { if (Session == null || string.IsNullOrEmpty(Session.refresh_token)) { done(arg1: false, Text("没有可刷新的登录状态", "No refreshable session")); yield break; } UnityWebRequest request = JsonRequest(json: JsonConvert.SerializeObject((object)new { Session.refresh_token }), url: _baseUrl + "/api/auth/refresh", method: "POST"); try { yield return request.SendWebRequest(); AuthResponse response = Parse<AuthResponse>(Body(request)); if (HasError(request) || response == null || !response.success) { PeakMapSessionStore.ClearUser(Session, _log); done(arg1: false, ResponseError(response, Text("登录状态已过期", "Session expired"))); yield break; } ApplyAuthResponse(response); done(arg1: true, null); } finally { ((IDisposable)request)?.Dispose(); } } private IEnumerator SignOutRoutine(Action<bool, string> done) { bool serverRevoked = false; string error = null; if (Session != null && !string.IsNullOrEmpty(Session.access_token)) { UnityWebRequest request = JsonRequest(_baseUrl + "/api/auth/sign-out", "POST", "{}"); try { ApplySessionHeaders(request); yield return request.SendWebRequest(); BasicResponse response = Parse<BasicResponse>(Body(request)); serverRevoked = !HasError(request) && response != null && response.success; if (!serverRevoked) { error = ResponseError(response, Text("服务端退出登录失败", "Server sign-out failed")); } } finally { ((IDisposable)request)?.Dispose(); } } else { serverRevoked = true; } PeakMapSessionStore.ClearUser(Session, _log); done(serverRevoked, error); } private IEnumerator FetchMapsRoutine(int page, int pageSize, string query, string sort, string modVersion, Action<MapsResponse, string> done) { string url = _baseUrl + "/api/maps?page=" + page + "&page_size=" + pageSize + "&sort=" + Escape(sort) + "&lang=" + _language; if (!string.IsNullOrWhiteSpace(query)) { url = url + "&q=" + Escape(query.Trim()); } if (!string.IsNullOrWhiteSpace(modVersion)) { url = url + "&mod_version=" + Escape(modVersion.Trim()); } UnityWebRequest request = UnityWebRequest.Get(url); try { ApplySessionHeaders(request); yield return request.SendWebRequest(); CaptureGuestCookie(request); if (HasError(request)) { done(null, Text("获取地图列表失败: ", "Failed to fetch maps: ") + ErrorText(request)); yield break; } MapsResponse response = Parse<MapsResponse>(Body(request)); if (response == null || !response.success) { done(response, ResponseError(response, Text("获取地图列表失败", "Failed to fetch maps"))); yield break; } done(response, null); } finally { ((IDisposable)request)?.Dispose(); } } private IEnumerator FetchAccountMapsRoutine(Action<MapsResponse, string> done) { if (!IsSignedIn) { done(null, Text("请先登录", "Please sign in first")); yield break; } UnityWebRequest request = UnityWebRequest.Get(_baseUrl + "/api/account/maps"); try { ApplySessionHeaders(request); yield return request.SendWebRequest(); if (HasError(request)) { MapsResponse failed = Parse<MapsResponse>(Body(request)); done(failed, ResponseError(failed, Text("获取我的地图失败", "Failed to fetch my maps"))); yield break; } MapsResponse response = Parse<MapsResponse>(Body(request)); if (response == null || !response.success) { done(response, ResponseError(response, Text("获取我的地图失败", "Failed to fetch my maps"))); yield break; } done(response, null); } finally { ((IDisposable)request)?.Dispose(); } } private IEnumerator FetchModVersionsRoutine(Action<ModVersionsResponse, string> done) { UnityWebRequest request = UnityWebRequest.Get(_baseUrl + "/api/mod-versions?lang=" + _language); try { yield return request.SendWebRequest(); if (HasError(request)) { done(null, Text("获取 MOD 版本失败: ", "Failed to fetch MOD versions: ") + ErrorText(request)); yield break; } ModVersionsResponse response = Parse<ModVersionsResponse>(Body(request)); if (response == null || !response.success) { done(response, ResponseError(response, Text("获取 MOD 版本失败", "Failed to fetch MOD versions"))); yield break; } done(response, null); } finally { ((IDisposable)request)?.Dispose(); } } private IEnumerator ToggleLikeRoutine(MapEntry map, Action<LikeResponse, string> done) { if (map == null || string.IsNullOrEmpty(map.id)) { done(null, Text("地图缺少 ID", "Map is missing an ID")); yield break; } UnityWebRequest request = JsonRequest(_baseUrl + "/api/maps/" + Escape(map.id) + "/like", "POST", "{}"); try { ApplySessionHeaders(request); yield return request.SendWebRequest(); CaptureGuestCookie(request); LikeResponse response = Parse<LikeResponse>(Body(request)); if (HasError(request) || response == null || !response.success) { done(response, ResponseError(response, Text("点赞失败", "Like failed"))); yield break; } map.liked_by_me = response.liked; map.likes = response.likes; done(response, null); } finally { ((IDisposable)request)?.Dispose(); } } private IEnumerator DownloadMapRoutine(MapEntry map, bool allowOverwriteLocalChanges, Action<string, string> done) { if (map == null || string.IsNullOrEmpty(map.download_url)) { done(null, Text("地图缺少下载地址", "Map is missing a download URL")); yield break; } MapDownloadInfo localInfo = MapSaveService.GetDownloadInfo(map); if (localInfo.Status == MapDownloadStatus.UpToDate) { done(null, Text("地图已经是最新版本", "This map is already up to date")); yield break; } if ((localInfo.Status == MapDownloadStatus.LocalModified || localInfo.Status == MapDownloadStatus.UpdateAvailable) && !allowOverwriteLocalChanges) { done(null, (localInfo.Status == MapDownloadStatus.LocalModified) ? Text("本地 JSON 已被修改,请确认覆盖", "The local JSON was modified; confirm overwrite") : Text("发现地图新版本,请确认更新", "A newer map version is available; confirm update")); yield break; } UnityWebRequest request = UnityWebRequest.Get(map.download_url); try { yield return request.SendWebRequest(); if (HasError(request)) { done(null, Text("下载失败: ", "Download failed: ") + ErrorText(request)); yield break; } try { string saved = MapSaveService.SaveDownloadedMap(map, request.downloadHandler.data, allowOverwriteLocalChanges); done(saved, null); } catch (MapSaveException ex) { done(null, (ex.Status == MapDownloadStatus.LocalModified) ? Text("本地 JSON 已被修改,请确认覆盖", "The local JSON was modified; confirm overwrite") : (Text("保存失败: ", "Save failed: ") + ex.Message)); } catch (Exception ex2) { done(null, Text("保存失败: ", "Save failed: ") + ex2.Message); } } finally { ((IDisposable)request)?.Dispose(); } } private IEnumerator UploadMapRoutine(string mapName, string author, string version, string description, string jsonPath, string imagePath, Action<string> done) { List<IMultipartFormSection> form; string error = BuildMapForm(mapName, author, version, description, jsonPath, imagePath, jsonOptional: false, out form); if (!string.IsNullOrEmpty(error)) { done(error); yield break; } UnityWebRequest request = UnityWebRequest.Post(_baseUrl + "/api/upload", form); try { ApplySessionHeaders(request); _log.LogInfo((object)("Sending upload request to " + _baseUrl + "/api/upload")); yield return request.SendWebRequest(); if (HasError(request)) { string body = Body(request); _log.LogWarning((object)("Upload request failed. Code=" + request.responseCode + ", error=" + request.error + ", body=" + body)); done(Text("上传失败: ", "Upload failed: ") + ((!string.IsNullOrEmpty(body)) ? ExtractError(body) : ErrorText(request))); yield break; } _log.LogInfo((object)("Upload request succeeded. Code=" + request.responseCode)); done(null); } finally { ((IDisposable)request)?.Dispose(); } } private IEnumerator UpdateMapRoutine(string mapId, string mapName, string author, string version, string description, string jsonPath, string imagePath, bool removeImage, Action<string> done) { if (!IsSignedIn) { done(Text("请先登录", "Please sign in first")); yield break; } if (string.IsNullOrEmpty(mapId)) { done(Text("地图缺少 ID", "Map is missing an ID")); yield break; } List<IMultipartFormSection> form; string error = BuildMapForm(mapName, author, version, description, jsonPath, imagePath, jsonOptional: true, out form); if (!string.IsNullOrEmpty(error)) { done(error); yield break; } form.Add((IMultipartFormSection)new MultipartFormDataSection("remove_image", removeImage ? "true" : "false")); UnityWebRequest request = UnityWebRequest.Post(_baseUrl + "/api/maps/" + Escape(mapId), form); try { request.method = "PUT"; ApplySessionHeaders(request); yield return request.SendWebRequest(); if (HasError(request)) { done(Text("保存失败: ", "Save failed: ") + ExtractError(Body(request), ErrorText(request))); yield break; } done(null); } finally { ((IDisposable)request)?.Dispose(); } } private IEnumerator DeleteMapRoutine(string mapId, Action<string> done) { if (!IsSignedIn) { done(Text("请先登录", "Please sign in first")); yield break; } if (string.IsNullOrEmpty(mapId)) { done(Text("地图缺少 ID", "Map is missing an ID")); yield break; } UnityWebRequest request = UnityWebRequest.Delete(_baseUrl + "/api/maps/" + Escape(mapId)); try { ApplySessionHeaders(request); yield return request.SendWebRequest(); if (HasError(request)) { done(Text("删除失败: ", "Delete failed: ") + ExtractError(Body(request), ErrorText(request))); yield break; } done(null); } finally { ((IDisposable)request)?.Dispose(); } } private IEnumerator DownloadTextureRoutine(string url, Action<Texture2D> done) { if (string.IsNullOrEmpty(url)) { done(null); yield break; } UnityWebRequest request = UnityWebRequestTexture.GetTexture(url); try { yield return request.SendWebRequest(); if (HasError(request)) { _log.LogWarning((object)("Thumbnail failed: " + request.error)); done(null); yield break; } MapImageCache.Save(url, request.downloadHandler.data); done(DownloadHandlerTexture.GetContent(request)); } finally { ((IDisposable)request)?.Dispose(); } } private IEnumerator LoadCachedTextureRoutine(string url, string path, Action<Texture2D> done) { string fileUrl; try { fileUrl = new Uri(path).AbsoluteUri; } catch { MapImageCache.Remove(url); fileUrl = null; } if (string.IsNullOrEmpty(fileUrl)) { yield return _runner.StartCoroutine(DownloadTextureRoutine(url, done)); yield break; } UnityWebRequest request = UnityWebRequestTexture.GetTexture(fileUrl); try { yield return request.SendWebRequest(); if (HasError(request)) { _log.LogWarning((object)("Cached thumbnail failed, downloading again: " + request.error)); MapImageCache.Remove(url); yield return _runner.StartCoroutine(DownloadTextureRoutine(url, done)); yield break; } done(DownloadHandlerTexture.GetContent(request)); } finally { ((IDisposable)request)?.Dispose(); } } private string BuildMapForm(string mapName, string author, string version, string description, string jsonPath, string imagePath, bool jsonOptional, out List<IMultipartFormSection> form) { //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Expected O, but got Unknown //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Expected O, but got Unknown //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Expected O, but got Unknown //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Expected O, but got Unknown //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Expected O, but got Unknown //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Expected O, but got Unknown form = new List<IMultipartFormSection>(); if (string.IsNullOrWhiteSpace(mapName) || string.IsNullOrWhiteSpace(author) || string.IsNullOrWhiteSpace(version)) { return Text("地图名称、作者和版本不能为空", "Map name, author, and version are required"); } if (!jsonOptional && (string.IsNullOrEmpty(jsonPath) || !File.Exists(jsonPath))) { return Text("请选择本地 JSON 地图文件", "Please select a local JSON map file"); } form.Add((IMultipartFormSection)new MultipartFormDataSection("name", mapName.Trim())); form.Add((IMultipartFormSection)new MultipartFormDataSection("author", author.Trim())); form.Add((IMultipartFormSection)new MultipartFormDataSection("mod_version", version.Trim())); form.Add((IMultipartFormSection)new MultipartFormDataSection("description", (description == null) ? string.Empty : description.Trim())); if (!string.IsNullOrEmpty(jsonPath) && File.Exists(jsonPath)) { form.Add((IMultipartFormSection)new MultipartFormFileSection("json_file", File.ReadAllBytes(jsonPath), Path.GetFileName(jsonPath), "application/json")); } if (!string.IsNullOrEmpty(imagePath) && File.Exists(imagePath)) { object obj; switch (Path.GetExtension(imagePath).ToLowerInvariant()) { default: obj = "image/png"; break; case ".gif": obj = "image/gif"; break; case ".webp": obj = "image/webp"; break; case ".jpg": case ".jpeg": obj = "image/jpeg"; break; } string text = (string)obj; form.Add((IMultipartFormSection)new MultipartFormFileSection("image_file", File.ReadAllBytes(imagePath), Path.GetFileName(imagePath), text)); } return null; } private UnityWebRequest JsonRequest(string url, string method, string json) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown byte[] bytes = Encoding.UTF8.GetBytes(json ?? "{}"); UnityWebRequest val = new UnityWebRequest(url, method); val.uploadHandler = (UploadHandler)new UploadHandlerRaw(bytes); val.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer(); val.SetRequestHeader("Content-Type", "application/json; charset=utf-8"); val.SetRequestHeader("Accept", "application/json"); return val; } private void ApplySessionHeaders(UnityWebRequest request) { if (request != null && Session != null) { if (!string.IsNullOrEmpty(Session.access_token)) { request.SetRequestHeader("Authorization", "Bearer " + Session.access_token); } if (!string.IsNullOrEmpty(Session.guest_id)) { request.SetRequestHeader("Cookie", "peak_guest_id=" + Session.guest_id); } } } private void CaptureGuestCookie(UnityWebRequest request) { if (request == null || Session == null) { return; } string responseHeader = request.GetResponseHeader("Set-Cookie"); if (string.IsNullOrEmpty(responseHeader)) { return; } int num = responseHeader.IndexOf("peak_guest_id=", StringComparison.OrdinalIgnoreCase); if (num >= 0) { num += "peak_guest_id=".Length; int num2 = responseHeader.IndexOf(';', num); string text = ((num2 >= 0) ? responseHeader.Substring(num, num2 - num) : responseHeader.Substring(num)); if (!string.IsNullOrEmpty(text) && !string.Equals(Session.guest_id, text, StringComparison.Ordinal)) { Session.guest_id = text; PeakMapSessionStore.Save(Session, _log); } } } private void ApplyAuthResponse(AuthResponse response) { if (Session == null) { Session = new PeakMapSession(); } Session.access_token = response.access_token; Session.refresh_token = response.refresh_token; Session.expires_at = response.expires_at; if (response.user != null) { Session.user_id = response.user.id; Session.email = response.user.email; Session.nickname = response.user.nickname; } PeakMapSessionStore.Save(Session, _log); } private T Parse<T>(string json) where T : class { try { return JsonConvert.DeserializeObject<T>(json); } catch (Exception ex) { _log.LogWarning((object)("API JSON parse failed: " + ex.Message)); return null; } } private static bool HasError(UnityWebRequest request) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Invalid comparison between Unknown and I4 return (int)request.result != 1; } private static string Body(UnityWebRequest request) { return (request != null && request.downloadHandler != null) ? request.downloadHandler.text : string.Empty; } private static string ErrorText(UnityWebRequest request) { return (request != null && !string.IsNullOrEmpty(request.error)) ? request.error : ("HTTP " + ((request != null) ? request.responseCode.ToString() : "0")); } private static string ResponseError(MapsResponse response, string fallback) { return (response != null && !string.IsNullOrEmpty(response.error_message)) ? response.error_message : ((response != null && !string.IsNullOrEmpty(response.error)) ? response.error : fallback); } private static string ResponseError(ModVersionsResponse response, string fallback) { return (response != null && !string.IsNullOrEmpty(response.error_message)) ? response.error_message : ((response != null && !string.IsNullOrEmpty(response.error)) ? response.error : fallback); } private static string ResponseError(AuthResponse response, string fallback) { return (response != null && !string.IsNullOrEmpty(response.error_message)) ? response.error_message : ((response != null && !string.IsNullOrEmpty(response.error)) ? response.error : fallback); } private static string ResponseError(BasicResponse response, string fallback) { return (response != null && !string.IsNullOrEmpty(response.error_message)) ? response.error_message : ((response != null && !string.IsNullOrEmpty(response.error)) ? response.error : fallback); } private static string ResponseError(LikeResponse response, string fallback) { return (response != null && !string.IsNullOrEmpty(response.error_message)) ? response.error_message : ((response != null && !string.IsNullOrEmpty(response.error)) ? response.error : fallback); } private string ExtractError(string json) { return ExtractError(json, Text("服务器错误", "Server error")); } private string ExtractError(string json, string fallback) { BasicResponse basicResponse = Parse<BasicResponse>(json); if (basicResponse != null) { if (!string.IsNullOrEmpty(basicResponse.error_message)) { return basicResponse.error_message; } if (!string.IsNullOrEmpty(basicResponse.error)) { return basicResponse.error; } } return string.IsNullOrEmpty(json) ? fallback : json; } private static string Escape(string value) { return UnityWebRequest.EscapeURL(value ?? string.Empty); } private string Text(string zh, string en) { return string.Equals(_language, "en", StringComparison.OrdinalIgnoreCase) ? en : zh; } } internal static class PeakMapLanguage { public static string Resolve(string mode, ManualLogSource log) { return ResolveDetailed(mode, log).Language; } public static PeakMapLanguageResult ResolveDetailed(string mode, ManualLogSource log) { //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_00b6: Unknown result type (might be due to invalid IL or missing references) string text = NormalizeMode(mode); if (text == "zh" || text == "en") { return new PeakMapLanguageResult(text, "Config", text, text); } if (TryGetGameLocalizedTextLanguage(out var language)) { return new PeakMapLanguageResult(IsChineseGameLanguage(language) ? "zh" : "en", "Game.LocalizedText.CURRENT_LANGUAGE", language, text); } if (TryGetUnityLocalizationCode(out var code, out var source)) { return new PeakMapLanguageResult(IsChineseCode(code) ? "zh" : "en", source, code, text); } string rawValue = ((object)Application.systemLanguage/*cast due to .constrained prefix*/).ToString(); return new PeakMapLanguageResult(IsChineseSystemLanguage(Application.systemLanguage) ? "zh" : "en", "SystemLanguageFallback", rawValue, text); } public static string NormalizeMode(string mode) { if (string.IsNullOrWhiteSpace(mode)) { return "auto"; } string text = mode.Trim().ToLowerInvariant(); switch (text) { default: if (!(text == "chinese")) { if (text == "en" || text == "en-us" || text == "english") { return "en"; } return "auto"; } goto case "zh"; case "zh": case "cn": case "zh-cn": return "zh"; } } private static bool TryGetUnityLocalizationCode(out string code, out string source) { code = null; source = null; try { Type type = Type.GetType("UnityEngine.Localization.Settings.LocalizationSettings, Unity.Localization"); if (type == null) { return false; } PropertyInfo property = type.GetProperty("SelectedLocale", BindingFlags.Static | BindingFlags.Public); object locale = ((property != null) ? property.GetValue(null, null) : null); if (TryExtractLocaleCode(locale, out code)) { source = "UnityLocalization.SelectedLocale"; return true; } PropertyInfo property2 = type.GetProperty("SelectedLocaleAsync", BindingFlags.Static | BindingFlags.Public); object selectedLocaleAsync = ((property2 != null) ? property2.GetValue(null, null) : null); if (TryExtractAsyncLocaleCode(selectedLocaleAsync, out code)) { source = "UnityLocalization.SelectedLocaleAsync"; return true; } MethodInfo method = type.GetMethod("GetSelectedLocale", BindingFlags.Static | BindingFlags.Public); object locale2 = ((method != null) ? method.Invoke(null, null) : null); if (TryExtractLocaleCode(locale2, out code)) { source = "UnityLocalization.GetSelectedLocale"; return true; } } catch { return false; } return false; } private static bool TryGetGameLocalizedTextLanguage(out string language) { language = null; try { Type type = FindType("LocalizedText", "Assembly-CSharp"); if (type == null) { return false; } FieldInfo field = type.GetField("CURRENT_LANGUAGE", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); object obj = ((field != null) ? field.GetValue(null) : null); if (obj == null) { return false; } language = obj.ToString(); if (string.IsNullOrWhiteSpace(language)) { language = Convert.ToInt32(obj).ToString(); } return !string.IsNullOrWhiteSpace(language); } catch { return false; } } private static bool TryExtractAsyncLocaleCode(object selectedLocaleAsync, out string code) { code = null; if (selectedLocaleAsync == null) { return false; } try { PropertyInfo property = selectedLocaleAsync.GetType().GetProperty("IsDone", BindingFlags.Instance | BindingFlags.Public); object obj = ((property != null) ? property.GetValue(selectedLocaleAsync, null) : null); if (obj is bool && !(bool)obj) { return false; } PropertyInfo property2 = selectedLocaleAsync.GetType().GetProperty("Result", BindingFlags.Instance | BindingFlags.Public); object locale = ((property2 != null) ? property2.GetValue(selectedLocaleAsync, null) : null); return TryExtractLocaleCode(locale, out code); } catch { return false; } } private static bool TryExtractLocaleCode(object locale, out string code) { code = null; if (locale == null) { return false; } try { PropertyInfo property = locale.GetType().GetProperty("Identifier", BindingFlags.Instance | BindingFlags.Public); object obj = ((property != null) ? property.GetValue(locale, null) : null); if (obj != null) { PropertyInfo property2 = obj.GetType().GetProperty("Code", BindingFlags.Instance | BindingFlags.Public); object obj2 = ((property2 != null) ? property2.GetValue(obj, null) : null); if (obj2 != null && !string.IsNullOrWhiteSpace(obj2.ToString())) { code = obj2.ToString(); return true; } } PropertyInfo property3 = locale.GetType().GetProperty("LocaleName", BindingFlags.Instance | BindingFlags.Public); object obj3 = ((property3 != null) ? property3.GetValue(locale, null) : null); if (obj3 != null && !string.IsNullOrWhiteSpace(obj3.ToString())) { code = obj3.ToString(); return true; } string text = locale.ToString(); if (!string.IsNullOrWhiteSpace(text)) { code = text; return true; } } catch { return false; } return false; } private static Type FindType(string typeName, string assemblyName) { Type type = Type.GetType(typeName + ", " + assemblyName); if (type != null) { return type; } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { AssemblyName name = assemblies[i].GetName(); if (string.Equals(name.Name, assemblyName, StringComparison.OrdinalIgnoreCase)) { Type type2 = assemblies[i].GetType(typeName, throwOnError: false); if (type2 != null) { return type2; } } } return null; } private static bool IsChineseCode(string code) { return !string.IsNullOrWhiteSpace(code) && code.Trim().StartsWith("zh", StringComparison.OrdinalIgnoreCase); } private static bool IsChineseGameLanguage(string language) { if (string.IsNullOrWhiteSpace(language)) { return false; } string a = language.Trim(); return string.Equals(a, "SimplifiedChinese", StringComparison.OrdinalIgnoreCase) || string.Equals(a, "Chinese", StringComparison.OrdinalIgnoreCase) || string.Equals(a, "ChineseSimplified", StringComparison.OrdinalIgnoreCase) || string.Equals(a, "9", StringComparison.OrdinalIgnoreCase); } private static bool IsChineseSystemLanguage(SystemLanguage language) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Invalid comparison between Unknown and I4 //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Invalid comparison between Unknown and I4 return (int)language == 6 || (int)language == 40 || (int)language == 41; } } internal sealed class PeakMapLanguageResult { public readonly string Language; public readonly string Source; public readonly string RawValue; public readonly string Mode; public PeakMapLanguageResult(string language, string source, string rawValue, string mode) { Language = language; Source = source; RawValue = rawValue; Mode = mode; } } internal static class PeakMapSessionStore { private sealed class PersistedSession { public int version; public string protected_refresh_token; public string user_id; public string email; public string nickname; public string guest_id; } private static readonly byte[] Entropy = Encoding.UTF8.GetBytes("com.wuyachiyu.peakmapbrowser.session.v2"); private static string DirectoryPath => Path.Combine(Application.persistentDataPath, "PeakMapBrowser"); private static string FilePath => Path.Combine(DirectoryPath, "session.json"); public static PeakMapSession Load(ManualLogSource log) { try { if (!File.Exists(FilePath)) { return new PeakMapSession(); } string text = File.ReadAllText(FilePath); PersistedSession persistedSession = JsonConvert.DeserializeObject<PersistedSession>(text); if (persistedSession != null && persistedSession.version >= 2) { PeakMapSession peakMapSession = new PeakMapSession { user_id = persistedSession.user_id, email = persistedSession.email, nickname = persistedSession.nickname, guest_id = persistedSession.guest_id }; if (!string.IsNullOrEmpty(persistedSession.protected_refresh_token)) { peakMapSession.refresh_token = Unprotect(persistedSession.protected_refresh_token); } return peakMapSession; } PeakMapSession peakMapSession2 = JsonConvert.DeserializeObject<PeakMapSession>(text); if (peakMapSession2 != null) { Save(peakMapSession2, log); return peakMapSession2; } return new PeakMapSession(); } catch (Exception ex) { log.LogWarning((object)("Failed to load PeakMapBrowser session: " + ex.Message)); return new PeakMapSession(); } } public static void Save(PeakMapSession session, ManualLogSource log) { try { Directory.CreateDirectory(DirectoryPath); PeakMapSession peakMapSession = session ?? new PeakMapSession(); PersistedSession persistedSession = new PersistedSession { version = 2, protected_refresh_token = (string.IsNullOrEmpty(peakMapSession.refresh_token) ? string.Empty : Protect(peakMapSession.refresh_token)), user_id = (peakMapSession.user_id ?? string.Empty), email = (peakMapSession.email ?? string.Empty), nickname = (peakMapSession.nickname ?? string.Empty), guest_id = (peakMapSession.guest_id ?? string.Empty) }; string text = FilePath + ".tmp-" + Guid.NewGuid().ToString("N"); try { File.WriteAllText(text, JsonConvert.SerializeObject((object)persistedSession, (Formatting)1), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); if (File.Exists(FilePath)) { File.Replace(text, FilePath, null); } else { File.Move(text, FilePath); } } finally { if (File.Exists(text)) { File.Delete(text); } } } catch (Exception ex) { log.LogWarning((object)("Failed to save PeakMapBrowser session: " + ex.Message)); } } public static void ClearUser(PeakMapSession session, ManualLogSource log) { if (session == null) { session = new PeakMapSession(); } session.access_token = string.Empty; session.refresh_token = string.Empty; session.expires_at = 0L; session.user_id = string.Empty; session.email = string.Empty; session.nickname = string.Empty; Save(session, log); } private static string Protect(string value) { byte[] bytes = Encoding.UTF8.GetBytes(value ?? string.Empty); byte[] inArray = ProtectedData.Protect(bytes, Entropy, (DataProtectionScope)0); return Convert.ToBase64String(inArray); } private static string Unprotect(string value) { byte[] array = Convert.FromBase64String(value); byte[] bytes = ProtectedData.Unprotect(array, Entropy, (DataProtectionScope)0); return Encoding.UTF8.GetString(bytes); } } internal sealed class PeakMapWindow { private readonly MonoBehaviour _runner; private readonly ManualLogSource _log; private readonly PeakMapApiClient _api; private readonly int _pageSize; private readonly Dictionary<string, Texture2D> _thumbnails = new Dictionary<string, Texture2D>(); private readonly Dictionary<string, MapDownloadInfo> _downloadInfoCache = new Dictionary<string, MapDownloadInfo>(); private bool _visible; private bool _cursorCaptured; private bool _previousCursorVisible; private CursorLockMode _previousCursorLockMode; private GameObject _inputBlocker; private bool _stylesReady; private bool _loadingMaps; private bool _loadingVersions; private bool _downloading; private bool _uploading; private bool _liking; private bool _uploadOpen; private bool _loginOpen; private bool _accountOpen; private bool _communityDetailOpen; private bool _downloadConfirmOpen; private bool _loadingAccountMaps; private bool _savingAccountMap; private bool _deletingAccountMap; private bool _refreshingSession; private bool _loadedOnce; private int _topLayerOpenedFrame = -1; private float _nextSessionRefreshCheckTime; private Rect _windowRect; private Vector2 _mapScroll; private Vector2 _uploadSaveScroll; private Vector2 _detailScroll; private Vector2 _accountScroll; private Vector2 _accountDescriptionScroll; private List<MapEntry> _maps = new List<MapEntry>(); private List<MapEntry> _accountMaps = new List<MapEntry>(); private List<ModVersionEntry> _versions = new List<ModVersionEntry>(); private PaginationInfo _pagination; private int _selectedMapIndex; private int _selectedAccountMapIndex = -1; private int _page = 1; private string _query = string.Empty; private string _sort = "newest"; private string _versionFilter = string.Empty; private string _languageMode; private string _language; private string _languageSource; private string _languageRawValue; private string _toggleKeyLabel; private float _nextLanguageCheckTime; private string _status = string.Empty; private string _toast = string.Empty; private float _toastUntil; private float _downloadInfoCacheUntil; private string[] _localSaves = new string[0]; private readonly List<int> _filteredLocalSaveIndexes = new List<int>(); private int _selectedLocalSave; private string _localSaveFilter = string.Empty; private string _localSaveError = string.Empty; private float _nextLocalSaveScanTime; private int _selectedUploadVersion; private bool _uploadVersionDropdownOpen; private Vector2 _uploadVersionScroll; private bool _uploadSaveDropdownOpen; private string[] _localImages = new string[0]; private readonly List<int> _filteredLocalImageIndexes = new List<int>(); private int _selectedLocalImage = -1; private string _localImageFilter = string.Empty; private string _localImageError = string.Empty; private bool _uploadImageDropdownOpen; private Vector2 _uploadImageScroll; private bool _imagePickerOpen; private string[] _imageRootPaths = new string[0]; private int _imagePickerRootIndex; private string _imagePickerDirectory = string.Empty; private string[] _imagePickerDirs = new string[0]; private string[] _imagePickerFiles = new string[0]; private string _imagePickerSelected = string.Empty; private string _imagePickerError = string.Empty; private Vector2 _imagePickerScroll; private string _uploadName = string.Empty; private string _uploadAuthor = string.Empty; private string _uploadDescription = string.Empty; private string _loginEmail = string.Empty; private string _loginPassword = string.Empty; private string _editName = string.Empty; private string _editAuthor = string.Empty; private string _editVersion = string.Empty; private string _editDescription = string.Empty; private bool _editReplaceJson; private bool _accountJsonDropdownOpen; private bool _accountVersionDropdownOpen; private Vector2 _accountVersionScroll; private bool _editReplaceImage; private bool _editRemoveImage; private string _deleteConfirmMapId = string.Empty; private string _downloadConfirmMapId = string.Empty; private GUIStyle _rootStyle; private GUIStyle _panelStyle; private GUIStyle _panelStrongStyle; private GUIStyle _detailMetaStyle; private GUIStyle _detailDescriptionStyle; private GUIStyle _cardStyle; private GUIStyle _buttonStyle; private GUIStyle _primaryButtonStyle; private GUIStyle _iconButtonStyle; private GUIStyle _inputStyle; private GUIStyle _textAreaStyle; private GUIStyle _titleStyle; private GUIStyle _h2Style; private GUIStyle _labelStyle; private GUIStyle _statLabelStyle; private GUIStyle _statValueStyle; private GUIStyle _cardStatsStyle; private GUIStyle _mutedStyle; private GUIStyle _cardDescStyle; private GUIStyle _detailTextStyle; private GUIStyle _detailTitleStyle; private GUIStyle _tinyStyle; private GUIStyle _badgeStyle; private GUIStyle _apiBadgeStyle; private GUIStyle _pagePillStyle; private GUIStyle _toastStyle; private GUIStyle _thumbStyle; private GUIStyle _sidebarButtonStyle; private GUIStyle _sidebarSelectedStyle; private GUIStyle _statBoxStyle; private GUIStyle _modalBackdropStyle; private GUIStyle _dangerStyle; private Texture2D _placeholderThumb; private Font _uiFont; private string SelectedLocalSavePath => (_selectedLocalSave >= 0 && _selectedLocalSave < _localSaves.Length) ? _localSaves[_selectedLocalSave] : null; private string SelectedLocalImagePath => (_selectedLocalImage >= 0 && _selectedLocalImage < _localImages.Length) ? _localImages[_selectedLocalImage] : null; private MapEntry SelectedMap => (_maps != null && _maps.Count > 0 && _selectedMapIndex >= 0 && _selectedMapIndex < _maps.Count) ? _maps[_selectedMapIndex] : null; private MapEntry SelectedAccountMap => (_accountMaps != null && _accountMaps.Count > 0 && _selectedAccountMapIndex >= 0 && _selectedAccountMapIndex < _accountMaps.Count) ? _accountMaps[_selectedAccountMapIndex] : null; public PeakMapWindow(MonoBehaviour runner, ManualLogSource log, string apiBaseUrl, string language, int pageSize, string toggleKeyLabel) { //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Unknown result type (might be due to invalid IL or missing references) _runner = runner; _log = log; _toggleKeyLabel = (string.IsNullOrWhiteSpace(toggleKeyLabel) ? "/" : toggleKeyLabel); _languageMode = language; PeakMapLanguageResult peakMapLanguageResult = PeakMapLanguage.ResolveDetailed(language, log); _language = peakMapLanguageResult.Language; _languageSource = peakMapLanguageResult.Source; _languageRawValue = peakMapLanguageResult.RawValue; _api = new PeakMapApiClient(runner, log, apiBaseUrl, _language); _pageSize = pageSize; _windowRect = new Rect(0f, 0f, 960f, 640f); _status = T("按 " + _toggleKeyLabel + " 打开或关闭地图库", "Press " + _toggleKeyLabel + " to open or close the map browser"); _log.LogInfo((object)("PEAK Map Browser language resolved: lang=" + _language + ", mode=" + peakMapLanguageResult.Mode + ", source=" + _languageSource + ", raw=" + _languageRawValue)); } public void Toggle() { SetVisible(!_visible); if (_visible && !_loadedOnce) { RefreshAll(); } } private void SetVisible(bool visible) { if (_visible != visible) { _visible = visible; if (visible) { _log.LogInfo((object)"Map browser opened."); CaptureCursor(); EnsureInputBlocker(); return; } _log.LogInfo((object)"Map browser closed."); RestoreCursor(); SetInputBlockerActive(active: false); _uploadOpen = false; _imagePickerOpen = false; _loginOpen = false; _accountOpen = false; _communityDetailOpen = false; _uploadVersionDropdownOpen = false; _uploadImageDropdownOpen = false; _uploadSaveDropdownOpen = false; } } private void CaptureCursor() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) if (!_cursorCaptured) { _previousCursorVisible = Cursor.visible; _previousCursorLockMode = Cursor.lockState; _cursorCaptured = true; } Cursor.visible = true; Cursor.lockState = (CursorLockMode)0; } private void RestoreCursor() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) if (_cursorCaptured) { Cursor.visible = _previousCursorVisible; Cursor.lockState = _previousCursorLockMode; _cursorCaptured = false; } } private void EnsureInputBlocker() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: 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_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_inputBlocker == (Object)null) { _inputBlocker = new GameObject("PeakMapBrowser_InputBlocker"); Object.DontDestroyOnLoad((Object)(object)_inputBlocker); Canvas val = _inputBlocker.AddComponent<Canvas>(); val.renderMode = (RenderMode)0; val.sortingOrder = 32767; _inputBlocker.AddComponent<GraphicRaycaster>(); GameObject val2 = new GameObject("Blocker"); val2.transform.SetParent(_inputBlocker.transform, false); Image val3 = val2.AddComponent<Image>(); ((Graphic)val3).color = new Color(0f, 0f, 0f, 0f); ((Graphic)val3).raycastTarget = true; RectTransform rectTransform = ((Graphic)val3).rectTransform; rectTransform.anchorMin = Vector2.zero; rectTransform.anchorMax = Vector2.one; rectTransform.offsetMin = Vector2.zero; rectTransform.offsetMax = Vector2.zero; } SetInputBlockerActive(active: true); } private void SetInputBlockerActive(bool active) { if ((Object)(object)_inputBlocker != (Object)null && _inputBlocker.activeSelf != active) { _inputBlocker.SetActive(active); } } public void Update() { RefreshLanguageIfNeeded(); RefreshSessionIfNeeded(); if (_visible) { CaptureCursor(); EnsureInputBlocker(); Input.ResetInputAxes(); } } private void RefreshLanguageIfNeeded() { if (Time.unscaledTime < _nextLanguageCheckTime) { return; } _nextLanguageCheckTime = Time.unscaledTime + 2f; PeakMapLanguageResult peakMapLanguageResult = PeakMapLanguage.ResolveDetailed(_languageMode, _log); bool flag = !string.Equals(peakMapLanguageResult.Language, _language, StringComparison.OrdinalIgnoreCase); bool flag2 = !string.Equals(peakMapLanguageResult.Source, _languageSource, StringComparison.Ordinal) || !string.Equals(peakMapLanguageResult.RawValue, _languageRawValue, StringComparison.Ordinal); if (flag || flag2) { string language = _language; string languageSource = _languageSource; string languageRawValue = _languageRawValue; _language = peakMapLanguageResult.Language; _languageSource = peakMapLanguageResult.Source; _languageRawValue = peakMapLanguageResult.RawValue; if (flag) { _api.SetLanguage(_language); _status = T("语言已切换为中文", "Language switched to English"); ShowToast(_status); } _log.LogInfo((object)("PEAK Map Browser language resolved: lang=" + _language + ", mode=" + peakMapLanguageResult.Mode + ", source=" + _languageSource + ", raw=" + _languageRawValue + " (previous lang=" + language + ", source=" + languageSource + ", raw=" + languageRawValue + ")")); } } public void Dispose() { SetVisible(visible: false); if ((Object)(object)_inputBlocker != (Object)null) { Object.Destroy((Object)(object)_inputBlocker); _inputBlocker = null; } } public void Draw() { //IL_021f: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Unknown result type (might be due to invalid IL or missing references) //IL_022d: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) if (!_visible) { return; } Color color = GUI.color; Color contentColor = GUI.contentColor; Color backgroundColor = GUI.backgroundColor; bool enabled = GUI.enabled; int depth = GUI.depth; GUI.color = Color.white; GUI.contentColor = Color.white; GUI.backgroundColor = Color.white; GUI.enabled = true; try { EnsureStyles(); CenterWindow(); GUI.depth = -100; DrawDimBackground(); DrawSolidBackground(_windowRect, new Color(0.01f, 0.016f, 0.012f, 1f)); GUI.Box(_windowRect, GUIContent.none, _rootStyle); bool flag = _communityDetailOpen || _downloadConfirmOpen; bool enabled2 = GUI.enabled; if (flag) { GUI.enabled = false; } if (!_uploadOpen && !_imagePickerOpen && !_loginOpen) { DrawHeader(); DrawSidebar(); if (_accountOpen) { DrawAccountPage(); } else { DrawContent(); } DrawFooter(); } GUI.enabled = enabled2; if (_communityDetailOpen && !_downloadConfirmOpen && !_imagePickerOpen && !_loginOpen && !_uploadOpen) { DrawCommunityDetailModal(); } if (_downloadConfirmOpen && !_imagePickerOpen && !_loginOpen && !_uploadOpen) { DrawDownloadConfirmModal(); } if (_uploadOpen && !_imagePickerOpen) { DrawUploadModal(); } if (_loginOpen && !_imagePickerOpen) { DrawLoginModal(); } if (_imagePickerOpen) { DrawImagePickerModal(); } DrawToast(); ConsumeOverlayEvents(); } finally { GUI.color = color; GUI.contentColor = contentColor; GUI.backgroundColor = backgroundColor; GUI.enabled = enabled; GUI.depth = depth; } } private void ConsumeOverlayEvents() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Invalid comparison between Unknown and I4 //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Invalid comparison between Unknown and I4 //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Invalid comparison between Unknown and I4 Event current = Event.current; if (current != null && ((int)current.type == 0 || (int)current.type == 1 || (int)current.type == 3 || (int)current.type == 6)) { current.Use(); } } private void BlockTopLayerInputThisFrame() { _topLayerOpenedFrame = Time.frameCount; } private bool IsTopLayerInputBlocked() { return _topLayerOpenedFrame == Time.frameCount; } private void CenterWindow() { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Round(Mathf.Min(1160f, (float)Screen.width - 24f)); float num2 = Mathf.Round(Mathf.Min(720f, (float)Screen.height - 24f)); _windowRect = new Rect(Mathf.Round(((float)Screen.width - num) * 0.5f), Mathf.Round(((float)Screen.height - num2) * 0.5f), num, num2); } private void DrawDimBackground() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) Color color = GUI.color; GUI.color = new Color(0f, 0f, 0f, 0.42f); GUI.DrawTexture(new Rect(0f, 0f, (float)Screen.width, (float)Screen.height), (Texture)(object)Texture2D.whiteTexture); GUI.color = color; } private void DrawSolidBackground(Rect rect, Color color) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) Color color2 = GUI.color; GUI.color = color; GUI.DrawTexture(rect, (Texture)(object)Texture2D.whiteTexture, (ScaleMode)0); GUI.color = color2; } private void DrawHeader() { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_0120: 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_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_024f: Unknown result type (might be due to invalid IL or missing references) //IL_02a1: Unknown result type (might be due to invalid IL or missing references) //IL_02cf: Unknown result type (might be due to invalid IL or missing references) //IL_02d5: Invalid comparison between Unknown and I4 //IL_02d9: Unknown result type (might be due to invalid IL or missing references) //IL_02e0: Invalid comparison between Unknown and I4 Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref _windowRect)).x, ((Rect)(ref _windowRect)).y, ((Rect)(ref _windowRect)).width, 64f); GUI.Box(val, GUIContent.none, _panelStrongStyle); Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(((Rect)(ref val)).x + 22f, ((Rect)(ref val)).y + 17f, 30f, 30f); GUI.Box(val2, GUIContent.none, _badgeStyle); GUI.Label(new Rect(((Rect)(ref val2)).x + 8f, ((Rect)(ref val2)).y - 2f, 22f, 28f), "/", _titleStyle); GUI.Label(new Rect(((Rect)(ref val)).x + 62f, ((Rect)(ref val)).y + 11f, 190f, 16f), T("远征数据库", "EXPEDITION DATABASE"), _tinyStyle); GUI.Label(new Rect(((Rect)(ref val)).x + 62f, ((Rect)(ref val)).y + 27f, 210f, 30f), T("PEAK 地图库", "PEAK Maps"), _titleStyle); float num = ((Rect)(ref val)).y + 17f; float num2 = 38f; float num3 = 118f; float num4 = 112f; float num5 = 38f; float num6 = 8f; float num7 = ((Rect)(ref val)).xMax - num2 - num4 - num3 - num5 - num6 * 4f - 22f; if (GUI.Button(new Rect(num7, num, num3, 34f), T("上传地图", "Upload"), _primaryButtonStyle)) { OpenUpload(); } num7 += num3 + num6; if (GUI.Button(new Rect(num7, num, num5, 34f), "↻", _iconButtonStyle)) { RefreshAll(); } num7 += num5 + num6; string text = (_api.IsSignedIn ? ShortAccountName(_api.Session.DisplayName) : T("登录", "Sign in")); if (GUI.Button(new Rect(num7, num, num4, 34f), text, _buttonStyle)) { if (_api.IsSignedIn) { OpenAccount(); } else { OpenLogin(); } } num7 += num4 + num6; if (GUI.Button(new Rect(num7, num, num2, 34f), "×", _iconButtonStyle)) { SetVisible(visible: false); } Event current = Event.current; if ((int)current.type == 4 && (int)current.keyCode == 13 && GUI.GetNameOfFocusedControl() == "PeakMapSearch") { _page = 1; FetchMaps(); current.Use(); } } private void DrawSidebar() { //IL_003a: 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_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_021c: Unknown result type (might be due to invalid IL or missing references) //IL_0285: Unknown result type (might be due to invalid IL or missing references) Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref _windowRect)).x, ((Rect)(ref _windowRect)).y + 64f, 230f, ((Rect)(ref _windowRect)).height - 92f); GUI.Box(val, GUIContent.none, _panelStyle); GUI.Label(new Rect(((Rect)(ref val)).x + 24f, ((Rect)(ref val)).y + 22f, 160f, 16f), T("导航", "NAVIGATION"), _tinyStyle); float num = ((Rect)(ref val)).y + 58f; DrawNavButton(new Rect(((Rect)(ref val)).x + 22f, num, ((Rect)(ref val)).width - 44f, 42f), T("⌂ 主页", "⌂ Home"), !_accountOpen, delegate { _accountOpen = false; }); num += 50f; DrawNavButton(new Rect(((Rect)(ref val)).x + 22f, num, ((Rect)(ref val)).width - 44f, 42f), T("▣ 我的地图", "▣ My Maps"), _accountOpen, delegate { if (_api.IsSignedIn) { OpenAccount(); } else { OpenLogin(); } }); num += 50f; DrawNavButton(new Rect(((Rect)(ref val)).x + 22f, num, ((Rect)(ref val)).width - 44f, 42f), T("◎ 社区地图", "◎ Database"), !_accountOpen, delegate { _accountOpen = false; }); num += 50f; DrawNavButton(new Rect(((Rect)(ref val)).x + 22f, num, ((Rect)(ref val)).width - 44f, 42f), T("⚙ 设置", "⚙ Settings"), selected: false, OpenAccountWebsite); if (GUI.Button(new Rect(((Rect)(ref val)).x + 22f, ((Rect)(ref val)).yMax - 84f, ((Rect)(ref val)).width - 44f, 40f), T("上传地图", "Upload Map"), _primaryButtonStyle)) { OpenUpload(); } if (_api.IsSignedIn && GUI.Button(new Rect(((Rect)(ref val)).x + 22f, ((Rect)(ref val)).yMax - 38f, ((Rect)(ref val)).width - 44f, 30f), T("退出登录", "Logout"), _buttonStyle)) { _accountOpen = false; _api.SignOut(delegate(bool serverRevoked, string error) { _status = (string.IsNullOrEmpty(error) ? T("已退出登录", "Signed out") : T("已退出本地登录,但服务端撤销失败", "Signed out locally, but server revocation failed")); ShowToast(_status); FetchMaps(); }); } } private void DrawNavButton(Rect rect, string label, bool selected, Action clicked) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (GUI.Button(rect, label, selected ? _sidebarSelectedStyle : _sidebarButtonStyle)) { clicked(); } } private void DrawFooter() { //IL_0034: 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) //IL_00af: Unknown result type (might be due to invalid IL or missing references) Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref _windowRect)).x, ((Rect)(ref _windowRect)).yMax - 28f, ((Rect)(ref _windowRect)).width, 28f); GUI.Box(val, GUIContent.none, _panelStrongStyle); GUI.Label(new Rect(((Rect)(ref val)).x + 16f, ((Rect)(ref val)).y + 7f, 360f, 16f), T("● 在线 © 2024 PEAK 地图库", "● ONLINE © 2024 PEAK MAP DATABASE"), _tinyStyle); GUI.Label(new Rect(((Rect)(ref val)).xMax - 360f, ((Rect)(ref val)).y + 7f, 340f, 16f), "API: peakmap.top", _mutedStyle); } private void DrawContent() { //IL_004d: Unknown result type (might be due to invalid IL or missing references) Rect rect = default(Rect); ((Rect)(ref rect))..ctor(((Rect)(ref _windowRect)).x + 248f, ((Rect)(ref _windowRect)).y + 84f, ((Rect)(ref _windowRect)).width - 270f, ((Rect)(ref _windowRect)).height - 128f); DrawMapList(rect); } private void DrawMapList(Rect rect) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_0229: Unknown result type (might be due to invalid IL or missing references) //IL_02c0: Unknown result type (might be due to invalid IL or missing references) //IL_033e: Unknown result type (might be due to invalid IL or missing references) //IL_03f7: Unknown result type (might be due to invalid IL or missing references) //IL_03c1: Unknown result type (might be due to invalid IL or missing references) //IL_037f: Unknown result type (might be due to invalid IL or missing references) GUI.Box(rect, GUIContent.none, _panelStyle); string text = ((_pagination != null) ? _pagination.total.ToString() : ((_maps != null) ? _maps.Count.ToString() : "0")); GUI.Label(new Rect(((Rect)(ref rect)).x + 18f, ((Rect)(ref rect)).y + 16f, 260f, 16f), T("共 " + text + " 张地图", "TOTAL " + text + " MAPS FOUND"), _tinyStyle); GUI.Label(new Rect(((Rect)(ref rect)).x + 18f, ((Rect)(ref rect)).y + 32f, 260f, 34f), T("社区地图", "Community Maps"), _titleStyle); GUI.Label(new Rect(((Rect)(ref rect)).xMax - 142f, ((Rect)(ref rect)).y + 18f, 124f, 26f), "API: peakmap.top", _apiBadgeStyle); float num = ((Rect)(ref rect)).y + 72f; float num2 = 8f; float num3 = Mathf.Max(180f, ((Rect)(ref rect)).width - 36f - 96f - 92f - 104f - num2 * 3f); GUI.SetNextControlName("PeakMapSearch"); string text2 = GUI.TextField(new Rect(((Rect)(ref rect)).x + 18f, num, num3, 34f), _query, _inputStyle); if (text2 != _query) { _query = text2; } float num4 = ((Rect)(ref rect)).x + 18f + num3 + num2; if (GUI.Button(new Rect(num4, num, 96f, 34f), ShortVersion(_versionFilter), _buttonStyle)) { CycleVersionFilter(); } num4 += 96f + num2; if (GUI.Button(new Rect(num4, num, 92f, 34f), (_sort == "downloads") ? T("下载量", "Popular") : T("最新", "Newest"), _buttonStyle)) { _sort = ((_sort == "downloads") ? "newest" : "downloads"); _page = 1; FetchMaps(); } num4 += 92f + num2; if (GUI.Button(new Rect(num4, num, 104f, 34f), T("搜索", "Search"), _primaryButtonStyle)) { _page = 1; FetchMaps(); } Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref rect)).x + 18f, ((Rect)(ref rect)).y + 118f, ((Rect)(ref rect)).width - 36f, ((Rect)(ref rect)).height - 174f); if (_loadingMaps) { GUI.Label(val, T("正在从 peakmap.top 获取地图列表...", "Fetching maps from peakmap.top..."), _labelStyle); } else if (_maps == null || _maps.Count == 0) { GUI.Label(val, string.IsNullOrEmpty(_query) ? T("暂无地图。", "No maps yet.") : T("没有找到匹配的地图。", "No matching maps found."), _labelStyle); } else { DrawCards(val); } DrawPagination(new Rect(((Rect)(ref rect)).x + 18f, ((Rect)(ref rect)).yMax - 46f, ((Rect)(ref rect)).width - 36f, 34f)); } private void DrawCards(Rect rect) { //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) int num = ((!(((Rect)(ref rect)).width > 560f)) ? 1 : 2); float num2 = 14f; float num3 = (((Rect)(ref rect)).width - (float)(num - 1) * num2 - 10f) / (float)num; float num4 = 290f; int num5 = Mathf.CeilToInt((float)_maps.Count / (float)num); Rect val = default(Rect); ((Rect)(ref val))..ctor(0f, 0f, ((Rect)(ref rect)).width - 18f, (float)num5 * (num4 + num2)); _mapScroll = GUI.BeginScrollView(rect, _mapScroll, val, false, true); Rect rect2 = default(Rect); for (int i = 0; i < _maps.Count; i++) { int num6 = i % num; int num7 = i / num; ((Rect)(ref rect2))..ctor((float)num6 * (num3 + num2), (float)num7 * (num4 + num2), num3, num4); DrawCard(rect2, i, _maps[i]); } GUI.EndScrollView(); } private void DrawCard(Rect rect, int index, MapEntry map) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: 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_02f7: Unknown result type (might be due to invalid IL or missing references) //IL_0335: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_036c: Unknown result type (might be due to invalid IL or missing references) //IL_037a: Unknown result type (might be due to invalid IL or missing references) //IL_038d: Unknown result type (might be due to invalid IL or missing references) bool flag = index == _selectedMapIndex; GUI.Box(rect, GUIContent.none, _cardStyle); if (flag) { DrawCardSelectionOutline(rect); } Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref rect)).x + 1f, ((Rect)(ref rect)).y + 1f, ((Rect)(ref rect)).width - 2f, ((Rect)(ref rect)).height - 2f); Rect rect2 = default(Rect); ((Rect)(ref rect2))..ctor(((Rect)(ref val)).x, ((Rect)(ref val)).y, ((Rect)(ref val)).width, 130f); DrawThumbnail(rect2, map); GUI.Label(new Rect(((Rect)(ref rect2)).x + 10f, ((Rect)(ref rect2)).y + 10f, 86f, 22f), ShortVersion(map.mod_version), _badgeStyle); if (map.revision > 1) { GUI.Label(new Rect(((Rect)(ref rect2)).xMax - 78f, ((Rect)(ref rect2)).y + 10f, 66f, 22f), "v" + map.revision, _badgeStyle); } float num = ((Rect)(ref val)).y + 146f; GUI.Label(new Rect(((Rect)(ref val)).x + 14f, num, ((Rect)(ref val)).width - 28f, 30f), CleanUiText(Safe(map.name, T("未命名地图", "Untitled map"))), _h2Style); GUI.Label(new Rect(((Rect)(ref val)).x + 14f, num + 34f, ((Rect)(ref val)).width - 28f, 18f), "♙ " + CleanUiText(Safe(map.author, T("未知", "Unknown"))), _mutedStyle); float num2 = ((Rect)(ref val)).yMax - 42f; float num3 = Mathf.Max(28f, num2 - (num + 58f) - 6f); string text = CompactCardDescription(CleanUiText(Safe(map.description, T("没有描述", "No description"))), ((Rect)(ref val)).width - 28f); GUI.Label(new Rect(((Rect)(ref val)).x + 14f, num + 58f, ((Rect)(ref val)).width - 28f, num3), text, _cardDescStyle); string text2 = T("下载 " + map.downloads + " 点赞 " + map.likes, "Downloads " + map.downloads + " Likes " + map.likes); GUI.Label(new Rect(((Rect)(ref val)).x + 14f, num2, ((Rect)(ref val)).width - 104f, 26f), text2, _cardStatsStyle); Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(((Rect)(ref val)).xMax - 82f, ((Rect)(ref val)).yMax - 46f, 68f, 34f); if (GUI.Button(val2, DownloadButtonLabel(map), _primaryButtonStyle)) { SelectMap(index); DownloadSelected(); } if (GUI.enabled && (int)Event.current.type == 0 && ((Rect)(ref rect)).Contains(Event.current.mousePosition) && !((Rect)(ref val2)).Contains(Event.current.mousePosition)) { SelectMap(index); _communityDetailOpen = true; BlockTopLayerInputThisFrame(); Event.current.Use(); } } private void DrawCardSelectionOutline(Rect rect) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: 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_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: 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) Color color = GUI.color; GUI.color = new Color(0.2f, 0.86f, 0.46f, 1f); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width, 2f), (Texture)(object)Texture2D.whiteTexture); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).yMax - 2f, ((Rect)(ref rect)).width, 2f), (Texture)(object)Texture2D.whiteTexture); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, 2f, ((Rect)(ref rect)).height), (Texture)(object)Texture2D.whiteTexture); GUI.DrawTexture(new Rect(((Rect)(ref rect)).xMax - 2f, ((Rect)(ref rect)).y, 2f, ((Rect)(ref rect)).height), (Texture)(object)Texture2D.whiteTexture); GUI.color = color; } private void DrawThumbnail(Rect rect, MapEntry map) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) DrawThumbnail(rect, map, (ScaleMode)1); } private void DrawThumbnail(Rect rect, MapEntry map, ScaleMode scaleMode) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) Texture2D thumbnail = GetThumbnail(map); GUI.DrawTexture(rect, (Texture)(object)(((Object)(object)thumbnail != (Object)null) ? thumbnail : _placeholderThumb), scaleMode); Color color = GUI.color; GUI.color = new Color(0f, 0f, 0f, 0.2f); GUI.DrawTexture(rect, (Texture)(object)Texture2D.whiteTexture); GUI.color = new Color(0f, 0f, 0f, 0.5f); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).yMax - 34f, ((Rect)(ref rect)).width, 34f), (Texture)(object)Texture2D.whiteTexture); GUI.color = color; } private Texture2D GetThumbnail(MapEntry map) { string url = ((!string.IsNullOrEmpty(map.thumbnail_url)) ? map.thumbnail_url : map.image_url); if (string.IsNullOrEmpty(url)) { return _placeholderThumb; } if (_thumbnails.TryGetValue(url, out var value)) { return ((Object)(object)value != (Object)null) ? value : _placeholderThumb; } _thumbnails[url] = null; _api.DownloadTexture(url, delegate(Texture2D loaded) { if ((Object)(object)loaded != (Object)null) { _thumbnails[url] = loaded; } }); return _placeholderThumb; } private void DrawDetail(Rect rect) { //IL_0001: 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_00df: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_022d: Unknown result type (might be due to invalid IL or missing references) //IL_02a3: 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_0314: Unknown result type (might be due to invalid IL or missing references) //IL_0385: Unknown result type (might be due to invalid IL or missing references) GUI.Box(rect, GUIContent.none, _panelStyle); MapEntry selectedMap = SelectedMap; if (selectedMap == null) { GUI.Label(new Rect(((Rect)(ref rect)).x + 16f, ((Rect)(ref rect)).y + 16f, ((Rect)(ref rect)).width - 32f, 40f), T("选择一张地图查看详情", "Select a map to view details"), _labelStyle); return; } Rect rect2 = default(Rect); ((Rect)(ref rect2))..ctor(((Rect)(ref rect)).x + 14f, ((Rect)(ref rect)).y + 14f, ((Rect)(ref rect)).width - 28f, 150f); DrawThumbnail(rect2, selectedMap); GUI.Label(new Rect(((Rect)(ref rect2)).x + 12f, ((Rect)(ref rect2)).yMax - 58f, ((Rect)(ref rect2)).width - 24f, 16f), T("当前地图", "SELECTED MAP"), _tinyStyle); GUI.Label(new Rect(((Rect)(ref rect2)).x + 12f, ((Rect)(ref rect2)).yMax - 40f, ((Rect)(ref rect2)).width - 24f, 34f), CleanUiText(Safe(selectedMap.name, T("未命名地图", "Untitled map"))), _detailTitleStyle); float num = ((Rect)(ref rect2)).yMax + 16f; DrawDetailMeta(new Rect(((Rect)(ref rect)).x + 14f, num, ((Rect)(ref rect)).width - 28f, 88f), selectedMap); num += 102f; GUI.Label(new Rect(((Rect)(ref rect)).x + 16f, num, ((Rect)(ref rect)).width - 32f, 18f), T("描述", "DESCRIPTION"), _tinyStyle); num += 24f; float num2 = ((Rect)(ref rect)).yMax - 48f; Rect rect3 = default(Rect); ((Rect)(ref rect3))..ctor(((Rect)(ref rect)).x + 14f, num, ((Rect)(ref rect)).width - 28f, Mathf.Max(110f, num2 - num - 12f)); DrawScrollableDescription(rect3, FormatDetailDescription(CleanUiText(Safe(selectedMap.description, T("没有描述", "No description"))))); float num3 = 8f; float num4 = (((Rect)(ref rect)).width - 32f - num3 * 2f) / 3f; GUI.enabled = !_downloading; if (GUI.Button(new Rect(((Rect)(ref rect)).x + 16f, num2, num4, 36f), _downloading ? T("下载中", "Loading") : DownloadButtonLabel(selectedMap), _primaryButtonStyle)) { DownloadSelected(); } GUI.enabled = true; GUI.enabled = !_liking; if (GUI.Button(new Rect(((Rect)(ref rect)).x + 16f + num4 + num3, num2, num4, 36f), selectedMap.liked_by_me ? T("已赞", "Liked") : T("点赞", "Like"), _buttonStyle)) { ToggleLikeSelected(); } GUI.enabled = true; if (GUI.Button(new Rect(((Rect)(ref rect)).x + 16f + (num4 + num3) * 2f, num2, num4, 36f), T("刷新", "Refresh"), _buttonStyle)) { RefreshAll(); } } private void DrawCommunityDetailModal() { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_0230: Unknown result type (might be due to invalid IL or missing references) //IL_0288: Unknown result type (might be due to invalid IL or missing references) //IL_02db: Unknown result type (might be due to invalid IL or missing references) //IL_0324: Unknown result type (might be due to invalid IL or missing references) //IL_0375: Unknown result type (might be due to invalid IL or missing references) //IL_03dc: Unknown result type (might be due to invalid IL or missing references) //IL_044f: Unknown result type (might be due to invalid IL or missing references) //IL_04b2: Unknown result type (might be due to invalid IL or missing references) //IL_0521: Unknown result type (might be due to invalid IL or missing references) //IL_058b: Unknown result type (might be due to invalid IL or missing references) MapEntry selectedMap = SelectedMap; if (selectedMap == null) { _communityDetailOpen = false; return; } GUI.depth = -101; GUI.Box(new Rect(0f, 0f, (float)Screen.width, (float)Screen.height), GUIContent.none, _modalBackdropStyle); float num = Mathf.Min(820f, (float)Screen.width - 32f); float num2 = Mathf.Min(680f, (float)Screen.height - 26f); Rect val = default(Rect); ((Rect)(ref val))..ctor(Mathf.Round(((float)Screen.width - num) * 0.5f), Mathf.Round(((float)Screen.height - num2) * 0.5f), num, num2); GUI.Box(val, GUIContent.none, _rootStyle); Rect rect = default(Rect); ((Rect)(ref rect))..ctor(((Rect)(ref val)).x + 14f, ((Rect)(ref val)).y + 14f, ((Rect)(ref val)).width - 28f, 238f); DrawThumbnail(rect, selectedMap, (ScaleMode)2); GUI.Label(new Rect(((Rect)(ref rect)).x + 14f, ((Rect)(ref rect)).y + 12f, 118f, 24f), ShortVersion(selectedMap.mod_version), _badgeStyle); if (GUI.Button(new Rect(((Rect)(ref val)).xMax - 52f, ((Rect)(ref val)).y + 18f, 34f, 34f), "×", _iconButtonStyle)) { _communityDetailOpen = false; } float num3 = ((Rect)(ref rect)).yMax + 16f; GUI.Label(new Rect(((Rect)(ref val)).x + 24f, num3, ((Rect)(ref val)).width - 48f, 38f), CleanUiText(Safe(selectedMap.name, T("未命名地图", "Untitled map"))), _titleStyle); num3 += 48f; float num4 = 10f; float num5 = (((Rect)(ref val)).width - 48f - num4 * 3f) / 4f; DrawStatBox(new Rect(((Rect)(ref val)).x + 24f, num3, num5, 64f), T("作者", "AUTHOR"), CleanUiText(Safe(selectedMap.author, T("未知", "Unknown")))); DrawStatBox(new Rect(((Rect)(ref val)).x + 24f + num5 + num4, num3, num5, 64f), T("MOD 版本", "MOD VERSION"), CleanUiText(Safe(selectedMap.mod_version, "-"))); DrawStatBox(new Rect(((Rect)(ref val)).x + 24f + (num5 + num4) * 2f, num3, num5, 64f), T("下载次数", "DOWNLOADS"), selectedMap.downloads.ToString()); DrawStatBox(new Rect(((Rect)(ref val)).x + 24f + (num5 + num4) * 3f, num3, num5, 64f), T("点赞", "LIKES"), selectedMap.likes.ToString()); num3 += 78f; GUI.Label(new Rect(((Rect)(ref val)