Decompiled source of Pulse v0.0.6

plugins/io.github.nonperforming.pulse/io.github.nonperforming.pulse.dll

Decompiled 10 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using PulseLib.Cafe.Models;
using PulseLib.Internal;
using PulseLib.Localization;
using PulseLib.Localization.Patches;
using PulseLib.Patches;
using SA.GoogleDoc;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("io.github.nonperforming.pulse")]
[assembly: AssemblyConfiguration("Release-BepInEx5")]
[assembly: AssemblyDescription("Helper library for Rhythm Doctor")]
[assembly: AssemblyFileVersion("0.0.6.0")]
[assembly: AssemblyInformationalVersion("0.0.6+0bd0d3b8264966a50d377aa42bbde266b6ba4226")]
[assembly: AssemblyProduct("Pulse")]
[assembly: AssemblyTitle("io.github.nonperforming.pulse")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.6.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 PulseLib
{
	internal static class Configuration
	{
		internal static ConfigEntry<bool> UseCustomLocalization;

		internal static ConfigEntry<bool> LoadLocalizationFromDisk;

		internal static ConfigEntry<string> CustomLanguage;

		internal static ConfigEntry<bool> LogLocalization;

		internal static ConfigEntry<bool> DumpLocalizationDictionary;

		internal static void Bind(ConfigFile config)
		{
			UseCustomLocalization = config.Bind<bool>("Localization", "UseCustomLocalization", true, "Whether to activate Pulse's localization system or not.");
			LoadLocalizationFromDisk = config.Bind<bool>("Localization", "LoadLocalizationFromDisk", true, "Load localization files from disk.\nLocalization files should be put under `<Game Dir>/Localization/<Language name>/<section>.json`.");
			CustomLanguage = config.Bind<string>("Localization", "CustomLanguage", "", "The custom language chosen.");
			LogLocalization = config.Bind<bool>("Development", "LogLocalization", false, "Log any calls that attempt to get a localized string.");
			DumpLocalizationDictionary = config.Bind<bool>("Development", "DumpLocalizationDictionary", false, "Dump any loaded localization dictionary to disk.\nDumped files will be found under `<Game Dir>/Dumped/<Language name>.json`.");
		}
	}
	public class Events
	{
		private static readonly Lazy<Events> _lazy = new Lazy<Events>(() => new Events());

		public static Events Instance => _lazy.Value;

		public event EventHandler? LevelDeselected;

		private Events()
		{
			Plugin.Logger.LogInfo((object)"Events singleton created");
		}

		public static void RaiseLevelDeselected()
		{
			Instance.LevelDeselected?.Invoke(null, EventArgs.Empty);
		}
	}
	[BepInPlugin("io.github.nonperforming.pulse", "Pulse", "0.0.6")]
	public class Plugin : BaseUnityPlugin
	{
		internal static ManualLogSource Logger;

		private readonly string _localizationPath = Path.Join(Paths.GameRootPath.AsSpan(), "Localization".AsSpan());

		private Harmony _harmony = new Harmony("io.github.nonperforming.pulse");

		private void Awake()
		{
			Logger = ((BaseUnityPlugin)this).Logger;
			Logger.LogInfo((object)"Loading Pulse version 0.0.6");
			Setup();
			Logger.LogInfo((object)"Binding configuration");
			Configuration.Bind(((BaseUnityPlugin)this).Config);
			if (Configuration.LoadLocalizationFromDisk.Value)
			{
				Logger.LogInfo((object)"Discovering localization");
				CustomLocalizationHelper.SearchAndRegisterDirectory(_localizationPath);
			}
			Logger.LogInfo((object)"Creating links to events");
			_harmony.PatchAll(typeof(EventsLink));
			Logger.LogInfo((object)"Applying patches");
			_harmony.PatchAll(typeof(CustomLocalizationPatch));
			_harmony.PatchAll(typeof(MainMenuPatch));
			Logger.LogInfo((object)"Pulse loaded!");
		}

		private void Setup()
		{
			Directory.CreateDirectory(_localizationPath);
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "io.github.nonperforming.pulse";

		public const string PLUGIN_NAME = "Pulse";

		public const string PLUGIN_VERSION = "0.0.6";
	}
}
namespace PulseLib.Patches
{
	[HarmonyPatch(typeof(scnMenu))]
	internal class MainMenuPatch
	{
		[HarmonyPatch("Start")]
		[HarmonyPostfix]
		private static void AddModCountLabelPatch()
		{
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			Plugin.Logger.LogInfo((object)"Creating mod count label");
			GameObject obj = GameObject.Find("/scnMenu/mainMenu/version");
			GameObject obj2 = Object.Instantiate<GameObject>(obj, obj.transform.parent, true);
			((Object)obj2).name = "PULSE - mod count";
			Object.Destroy((Object)(object)obj2.transform.GetChild(0));
			Object.Destroy((Object)(object)obj2.GetComponent<RDVersionText>());
			Text component = obj2.GetComponent<Text>();
			component.fontSize = 5;
			int num = Chainloader.PluginInfos.Count - 1;
			int count = Chainloader.DependencyErrors.Count;
			component.text = string.Format("Pulse v{0} / {1} mods loaded", "0.0.6", num);
			if (count != 0)
			{
				component.text += $" / {count} mods failed to load";
			}
			obj2.transform.localPosition = new Vector3(-167f, -82f, 0f);
		}
	}
}
namespace PulseLib.Localization
{
	public static class CustomLocalizationHelper
	{
		internal static Dictionary<string, Dictionary<string, Dictionary<string, string>>> customLocalizedStrings = new Dictionary<string, Dictionary<string, Dictionary<string, string>>>();

		public static void Register(string language, string section, string key, string localized)
		{
			if (!customLocalizedStrings.ContainsKey(language))
			{
				Plugin.Logger.LogDebug((object)("[Localization] Creating language dictionary for " + language));
				customLocalizedStrings[language] = new Dictionary<string, Dictionary<string, string>>();
			}
			if (!customLocalizedStrings[language].ContainsKey(section))
			{
				Plugin.Logger.LogDebug((object)("[Localization] Creating section dictionary for " + section + " of " + language));
				customLocalizedStrings[language][section] = new Dictionary<string, string>();
			}
			customLocalizedStrings[language][section][key] = localized;
			Plugin.Logger.LogDebug((object)("[Localization] Registered '" + section + "." + key + "' as '" + localized + "' for " + language));
		}

		public static void Register(string language, string section, Dictionary<string, string> localized)
		{
			foreach (var (key, localized2) in localized)
			{
				Register(language, section, key, localized2);
			}
		}

		public unsafe static void Register(LangCode language, LangSection section, Dictionary<string, string> localized)
		{
			Register(((object)(*(LangCode*)(&language))/*cast due to .constrained prefix*/).ToString(), ((object)(*(LangSection*)(&section))/*cast due to .constrained prefix*/).ToString(), localized);
		}

		public unsafe static void Register(string language, LangSection section, Dictionary<string, string> localized)
		{
			Register(language, ((object)(*(LangSection*)(&section))/*cast due to .constrained prefix*/).ToString(), localized);
		}

		public static void Register(Dictionary<LangCode, Dictionary<LangSection, Dictionary<string, string>>> localized)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: 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_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			foreach (var (language, dictionary2) in localized)
			{
				foreach (var (section, localized2) in dictionary2)
				{
					Register(language, section, localized2);
				}
			}
		}

		public static void Register(Dictionary<string, Dictionary<string, Dictionary<string, string>>> localized)
		{
			foreach (KeyValuePair<string, Dictionary<string, Dictionary<string, string>>> item in localized)
			{
				item.Deconstruct(out var key, out var value);
				string language = key;
				foreach (KeyValuePair<string, Dictionary<string, string>> item2 in value)
				{
					item2.Deconstruct(out key, out var value2);
					string section = key;
					Dictionary<string, string> localized2 = value2;
					Register(language, section, localized2);
				}
			}
		}

		public static void Register(string language, Dictionary<string, Dictionary<string, string>> localized)
		{
			foreach (var (section, localized2) in localized)
			{
				Register(language, section, localized2);
			}
		}

		public static void SearchAndRegisterDirectory(string path)
		{
			Plugin.Logger.LogDebug((object)("[Localization] Searching for localization JSONs in " + path));
			Directory.EnumerateDirectories(path, "*", SearchOption.TopDirectoryOnly);
			foreach (string item in Directory.EnumerateDirectories(path, "*", SearchOption.TopDirectoryOnly))
			{
				RegisterDirectory(path, new DirectoryInfo(item).Name);
			}
		}

		public static void RegisterDirectory(string path, string language)
		{
			Plugin.Logger.LogDebug((object)("[Localization] Registering localization JSONs in " + path + " for " + language));
			IEnumerable<string> source = Directory.EnumerateFiles(path, "*.json", SearchOption.AllDirectories);
			if (source.Count() == 1)
			{
				Register(language, (Dictionary<string, Dictionary<string, string>>)Json.Deserialize(File.ReadAllText(source.ElementAt(0))));
				return;
			}
			foreach (string item in Directory.EnumerateFiles(path, "*.json", SearchOption.AllDirectories))
			{
				Register(language, Path.GetFileNameWithoutExtension(item), JsonConvert.DeserializeObject<Dictionary<string, string>>(File.ReadAllText(item)));
			}
		}

		public static string GetLanguage()
		{
			//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)
			if (string.IsNullOrWhiteSpace(Configuration.CustomLanguage.Value))
			{
				return ((object)Localization.Client.CurrentLanguage/*cast due to .constrained prefix*/).ToString();
			}
			return Configuration.CustomLanguage.Value;
		}
	}
}
namespace PulseLib.Localization.Patches
{
	[HarmonyPatch(typeof(LocalizationClient))]
	internal static class CustomLocalizationPatch
	{
		[HarmonyPatch("ExistsLocalizedString")]
		[HarmonyPrefix]
		private static void CheckCustomLocalizationExists(string token, ref bool __result, ref bool __runOriginal)
		{
			if (TryGetCustomLocalization(token, out string _))
			{
				__result = true;
				__runOriginal = false;
			}
		}

		[HarmonyPatch("GetLocalizedString", new Type[]
		{
			typeof(string),
			typeof(LangSection),
			typeof(LangCode)
		})]
		[HarmonyPrefix]
		private static void GetCustomLocalization(string token, LangCode language, ref string __result, ref bool __runOriginal)
		{
			if (TryGetCustomLocalization(token, out string localized))
			{
				__result = localized;
				__runOriginal = false;
				Plugin.Logger.LogDebug((object)("Got custom localization '" + token + "' as '" + localized + "'"));
			}
		}

		private static bool TryGetCustomLocalization(string token, [NotNullWhen(true)] out string? localized)
		{
			string[] array = token.Split('.');
			string section = array[0];
			string key = string.Join(".", array, 1, array.Length - 1);
			return TryGetCustomLocalization(section, key, out localized);
		}

		private static bool TryGetCustomLocalization(string section, string key, [NotNullWhen(true)] out string? localized)
		{
			if (!CustomLocalizationHelper.customLocalizedStrings.TryGetValue(CustomLocalizationHelper.GetLanguage(), out Dictionary<string, Dictionary<string, string>> value) || !value.TryGetValue(section, out var value2))
			{
				localized = null;
				return false;
			}
			return value2.TryGetValue(key, out localized);
		}
	}
}
namespace PulseLib.Internal
{
	[HarmonyPatch]
	internal static class EventsLink
	{
		[HarmonyPatch(typeof(scnLevelSelect), "Update")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> LinkRaiseLevelDeselectedEventPatch(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Expected O, but got Unknown
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Expected O, but got Unknown
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Expected O, but got Unknown
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Expected O, but got Unknown
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Expected O, but got Unknown
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Expected O, but got Unknown
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Expected O, but got Unknown
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Expected O, but got Unknown
			return new CodeMatcher(instructions, (ILGenerator)null).MatchForward(true, (CodeMatch[])(object)new CodeMatch[10]
			{
				new CodeMatch((OpCode?)OpCodes.Ldsfld, (object)AccessTools.Field(typeof(RDInput), "cancelPress"), (string)null),
				new CodeMatch((OpCode?)OpCodes.Brfalse, (object)null, (string)null),
				new CodeMatch((OpCode?)OpCodes.Ldarg_0, (object)null, (string)null),
				new CodeMatch((OpCode?)OpCodes.Ldfld, (object)AccessTools.Field(typeof(scnLevelSelect), "heartMonitor"), (string)null),
				new CodeMatch((OpCode?)OpCodes.Ldfld, (object)AccessTools.Field(typeof(HeartMonitor), "visible"), (string)null),
				new CodeMatch((OpCode?)OpCodes.Brfalse, (object)null, (string)null),
				new CodeMatch((OpCode?)OpCodes.Ldarg_0, (object)null, (string)null),
				new CodeMatch((OpCode?)OpCodes.Ldfld, (object)AccessTools.Field(typeof(scnLevelSelect), "heartMonitor"), (string)null),
				new CodeMatch((OpCode?)OpCodes.Ldfld, (object)AccessTools.Field(typeof(HeartMonitor), "goingToLevel"), (string)null),
				new CodeMatch((OpCode?)OpCodes.Brtrue, (object)null, (string)null)
			}).ThrowIfInvalid("Could not find RDInput.cancelPress && heartMonitor.visible && !heartMonitor.goingToLevel in scnLevelSelect.Update").InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { CodeInstruction.Call(typeof(Events), "RaiseLevelDeselected", (Type[])null, (Type[])null) })
				.InstructionEnumeration();
		}
	}
}
namespace PulseLib.Extensions
{
	public static class LevelExtensions
	{
		public static readonly Level[] AllStandardLevels;

		public static readonly Level[] AllBossLevels;

		public static readonly Level[] AllBonusLevels;

		public static readonly Level[] AllMinigameLevels;

		public static readonly Level[] AllIntermissionLevels;

		static LevelExtensions()
		{
			Level[] array = new Level[47];
			RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			AllStandardLevels = (Level[])(object)array;
			Level[] array2 = new Level[8];
			RuntimeHelpers.InitializeArray(array2, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			AllBossLevels = (Level[])(object)array2;
			Level[] array3 = new Level[3];
			RuntimeHelpers.InitializeArray(array3, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			AllBonusLevels = (Level[])(object)array3;
			AllMinigameLevels = (Level[])(object)new Level[2]
			{
				(Level)17,
				(Level)41
			};
			Level[] array4 = new Level[3];
			RuntimeHelpers.InitializeArray(array4, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			AllIntermissionLevels = (Level[])(object)array4;
		}
	}
	public static class RDStringExtensions
	{
		public static string GetWithSubstitution(string key, params KeyValuePair<string, string>[] substituteWith)
		{
			string text = RDString.Get(key);
			foreach (KeyValuePair<string, string> keyValuePair in substituteWith)
			{
				keyValuePair.Deconstruct(out var key2, out var value);
				string text2 = key2;
				string newValue = value;
				text = text.Replace("[" + text2 + "]", newValue, StringComparison.Ordinal);
			}
			return text;
		}
	}
	public static class scnBaseExtensions
	{
		public static void GoToLevelWithCustomLevelData(CustomLevelData levelData, bool twoPlayer)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Invalid comparison between Unknown and I4
			scnBase.GoToLevelWithExternalPath((twoPlayer && !levelData.isLegacyLevel && (int)levelData.settings.canBePlayedOn == 3 && !string.IsNullOrEmpty(levelData.settings.separate2PLevelFilename)) ? Path.Combine(levelData.path, levelData.settings.separate2PLevelFilename) : Path.Combine(levelData.path, "main.rdlevel"));
		}

		public static void GoToLevelWithImportLevel(ImportLevel importLevel, bool twoPlayer)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Invalid comparison between Unknown and I4
			scnBase.GoToLevelWithExternalPath((twoPlayer && (int)importLevel.settings.canBePlayedOn == 3 && !string.IsNullOrEmpty(importLevel.settings.separate2PLevelFilename)) ? Path.Combine(importLevel.path, importLevel.settings.separate2PLevelFilename) : Path.Combine(importLevel.path, "main.rdlevel"));
		}
	}
}
namespace PulseLib.Cafe
{
	public static class Cafe
	{
		public const string URL = "https://rhythm.cafe/";

		public const string API_URL = "https://rhythm.cafe/api/";

		private static HttpClient _client = new HttpClient
		{
			BaseAddress = new Uri("https://rhythm.cafe/api/"),
			DefaultRequestHeaders = 
			{
				Accept = { MediaTypeWithQualityHeaderValue.Parse("application/json") },
				UserAgent = { ProductInfoHeaderValue.Parse("Pulse/0.0.6") }
			}
		};

		public static async Task<LevelData?> GetLevelData(string levelId)
		{
			using HttpResponseMessage response = await _client.GetAsync("levels/" + levelId + "/");
			if (!response.IsSuccessStatusCode)
			{
				Plugin.Logger.LogError((object)("[Cafe] Could not get level data: " + response.ReasonPhrase));
				return null;
			}
			return JsonConvert.DeserializeObject<LevelData>(await response.Content.ReadAsStringAsync());
		}

		public static async Task<BlendData?> GetDailyBlend()
		{
			using HttpResponseMessage response = await _client.GetAsync("levels/todays-blend/");
			if (!response.IsSuccessStatusCode)
			{
				Plugin.Logger.LogError((object)("[Cafe] Could not get daily blend: " + response.ReasonPhrase));
				return null;
			}
			return JsonConvert.DeserializeObject<BlendData>(await response.Content.ReadAsStringAsync());
		}
	}
}
namespace PulseLib.Cafe.Models
{
	public struct BlendData
	{
		[JsonProperty("blend")]
		public LevelData Blend { get; private set; }
	}
	public struct Club
	{
		[JsonProperty("id")]
		public string Id { get; private set; }

		[JsonProperty("name")]
		public string Name { get; private set; }
	}
	public struct LevelData
	{
		[JsonProperty("id")]
		public string Id { get; private set; }

		[JsonProperty("artist")]
		public string Artist { get; private set; }

		[JsonProperty("artist_tokens")]
		public string[] ArtistTokens { get; private set; }

		[JsonProperty("artist_raw")]
		public string ArtistRaw { get; private set; }

		[JsonProperty("song")]
		public string Song { get; private set; }

		[JsonProperty("song_alt")]
		public string SongAlt { get; private set; }

		[JsonProperty("song_raw")]
		public string SongRaw { get; private set; }

		[JsonProperty("seizure_warning")]
		public bool SeizureWarning { get; private set; }

		[JsonProperty("description")]
		public string Description { get; private set; }

		[JsonProperty("hue")]
		public float Hue { get; private set; }

		[JsonProperty("authors")]
		public string[] Authors { get; private set; }

		[JsonProperty("authors_raw")]
		public string AuthorsRaw { get; private set; }

		[JsonProperty("max_bpm")]
		public long MaxBPM { get; private set; }

		[JsonProperty("min_bpm")]
		public long MinBPM { get; private set; }

		[JsonProperty("difficulty")]
		public LevelDifficulty Difficulty { get; private set; }

		[JsonProperty("single_player")]
		public bool SinglePlayer { get; private set; }

		[JsonProperty("two_player")]
		public bool TwoPlayer { get; private set; }

		[JsonProperty("last_updated")]
		public DateTimeOffset LastUpdated { get; private set; }

		[JsonProperty("tags")]
		public string[] Tags { get; private set; }

		[JsonProperty("sha1")]
		public string SHA1 { get; private set; }

		[JsonProperty("rdlevel_sha1")]
		public string RDLevelSHA1 { get; private set; }

		[JsonProperty("rd_md5")]
		public string RDMD5 { get; private set; }

		[JsonProperty("is_animated")]
		public bool IsAnimated { get; private set; }

		[JsonProperty("rdzip_url")]
		public Uri RDZipUrl { get; private set; }

		[JsonProperty("image_url")]
		public Uri ImageUrl { get; private set; }

		[JsonProperty("thumb_url")]
		public Uri ThumbUrl { get; private set; }

		[JsonProperty("icon_url")]
		public string IconUrl { get; private set; }

		[JsonProperty("submitter")]
		public Submitter Submitter { get; private set; }

		[JsonProperty("club")]
		public Club Club { get; private set; }

		[JsonProperty("approval")]
		public int Approval { get; private set; }

		[JsonProperty("approval_notes_public")]
		public string ApprovalNotesPublic { get; private set; }

		[JsonProperty("is_private")]
		public bool IsPrivate { get; private set; }

		[JsonProperty("prefill_version")]
		public long PrefillVersion { get; private set; }

		[JsonProperty("has_classics")]
		public bool HasClassics { get; private set; }

		[JsonProperty("has_oneshots")]
		public bool HasOneshots { get; private set; }

		[JsonProperty("has_squareshots")]
		public bool HasSquareshots { get; private set; }

		[JsonProperty("has_freezeshots")]
		public bool HasFreezeshots { get; private set; }

		[JsonProperty("has_burnshots")]
		public bool HasBurnshots { get; private set; }

		[JsonProperty("has_holdshots")]
		public bool HasHoldshots { get; private set; }

		[JsonProperty("has_triangleshots")]
		public bool HasTriangleshots { get; private set; }

		[JsonProperty("has_skipshots")]
		public bool HasSkipshots { get; private set; }

		[JsonProperty("has_subdivs")]
		public bool HasSubdivs { get; private set; }

		[JsonProperty("has_synco")]
		public bool HasSynco { get; private set; }

		[JsonProperty("has_freetimes")]
		public bool HasFreetimes { get; private set; }

		[JsonProperty("has_holds")]
		public bool HasHolds { get; private set; }

		[JsonProperty("has_window_dance")]
		public bool HasWindowDance { get; private set; }

		[JsonProperty("has_rdcode")]
		public bool HasRDCode { get; private set; }

		[JsonProperty("has_cpu_rows")]
		public bool HasCpuRows { get; private set; }

		[JsonProperty("total_hits_approx")]
		public long TotalHitsApprox { get; private set; }

		[JsonProperty("is_hidden")]
		public bool IsHidden { get; private set; }
	}
	public struct Submitter
	{
		[JsonProperty("id")]
		public string Id { get; private set; }

		[JsonProperty("displayName")]
		public string DisplayName { get; private set; }
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}