Decompiled source of FoundFootage Fixed v0.5.0

plugins/jp.assasans.cw.FoundFootage.dll

Decompiled 14 hours ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using FoundFootage.Patches;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using MyceliumNetworking;
using Photon.Pun;
using TMPro;
using Unity.Collections;
using UnityEngine;
using UnityEngine.Localization;
using UnityEngine.Localization.PropertyVariants;
using UnityEngine.Localization.Settings;
using UnityEngine.Networking;
using UnityEngine.UI;
using Zorro.Core;
using Zorro.Core.Serizalization;

[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: 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.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 FoundFootage
{
	public class ConfigTrace
	{
		[SerializeField]
		public string secretUserId;

		[SerializeField]
		public string values;
	}
	public class ConfigTracer
	{
		private IEnumerator TraceConfig_Unity(ConfigTrace trace, Action<Result<int, Exception>> callback)
		{
			FoundFootagePlugin.Logger.LogInfo((object)("Trace: " + JsonUtility.ToJson((object)trace)));
			UnityWebRequest request = UnityWebRequest.Post(FoundFootagePlugin.Instance.ServerUrl.Value + "/trace-config?local=0.4.4", JsonUtility.ToJson((object)trace), "application/json");
			try
			{
				yield return request.SendWebRequest();
				if ((int)request.result != 1)
				{
					callback(Result<int, Exception>.NewError(new Exception("request.error=" + request.error + " downloadHandler.error=" + request.downloadHandler.error)));
				}
				else
				{
					callback(Result<int, Exception>.NewOk(0));
				}
			}
			finally
			{
				((IDisposable)request)?.Dispose();
			}
		}

		public async Task TraceConfig(MonoBehaviour target, ConfigTrace trace)
		{
			try
			{
				await UnityShit.CoroutineToTask(target, (Action<Result<int, Exception>> callback) => TraceConfig_Unity(trace, callback));
			}
			catch (Exception ex)
			{
				FoundFootagePlugin.Logger.LogError((object)("An error occurred while tracing config: " + ex.Message));
			}
		}

		public bool IsCompatibleWith(string compatible, string actual)
		{
			string[] array = compatible.Split('(');
			if (array[0].Trim() == actual)
			{
				return true;
			}
			if (array[1].Contains(actual + "-compatible"))
			{
				return true;
			}
			return false;
		}
	}
	[ContentWarningPlugin("jp.assasans.cw.FoundFootage", "0.5.0", false)]
	[BepInPlugin("jp.assasans.cw.FoundFootage", "Found Footage", "0.5.0")]
	[BepInDependency("RugbugRedfern.MyceliumNetworking", "1.0.10")]
	public class FoundFootageBootstrapPlugin : BaseUnityPlugin
	{
		internal static ManualLogSource Logger { get; private set; }

		internal static BepInPlugin Metadata { get; private set; }

		private void Awake()
		{
			//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)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected O, but got Unknown
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			Logger = ((BaseUnityPlugin)this).Logger;
			Metadata = MetadataHelper.GetMetadata((object)this);
			GameObject val = new GameObject("FoundFootage Plugin")
			{
				hideFlags = (HideFlags)61
			};
			val.AddComponent<FoundFootagePlugin>();
			Object.DontDestroyOnLoad((Object)val);
		}
	}
	public class FoundFootagePlugin : MonoBehaviour
	{
		public const uint ModId = 551035154u;

		public static FoundFootagePlugin Instance { get; private set; }

		internal static ManualLogSource Logger => FoundFootageBootstrapPlugin.Logger;

		public ConfigFile Config { get; private set; }

		internal Random Random { get; private set; }

		internal List<VideoHandle> FakeVideos { get; private set; }

		internal List<VideoHandle> ProcessedFakeVideos { get; private set; }

		internal List<VideoHandle> SentVideos { get; private set; }

		internal List<VideoHandle> SentVotes { get; private set; }

		internal Dictionary<VideoHandle, string> ClientToServerId { get; private set; }

		internal Dictionary<VideoHandle, ContentBuffer> FakeContentBuffers { get; private set; }

		internal MainThreadDispatcher Dispatcher { get; private set; }

		internal ConfigFile PersistentConfig { get; private set; }

		internal ConfigEntry<string>? ServerUrl { get; private set; }

		internal ConfigEntry<string>? UserId { get; private set; }

		internal ConfigEntry<string>? SecretUserId { get; private set; }

		internal ConfigEntry<bool>? WarningShown { get; private set; }

		internal ConfigEntry<bool>? ConfigTraceEnabled { get; private set; }

		internal ConfigEntry<int>? ConfigVersion { get; private set; }

		internal ConfigEntry<bool>? VotingEnabled { get; private set; }

		internal ConfigEntry<bool>? SendPositionEnabled { get; private set; }

		internal ConfigEntry<bool>? SendContentBufferEnabled { get; private set; }

		internal ConfigEntry<float>? FoundVideoScoreMultiplier { get; private set; }

		internal ConfigEntry<string>? FoundVideoSearchParams { get; private set; }

		internal IEnumerable<FoundVideoSearchParam> FoundVideoSearchParamsParsed => (from name in FoundVideoSearchParams.Value.TrimStart('[').TrimEnd(']').Split(",")
			select name.Trim()).Select(Enum.Parse<FoundVideoSearchParam>);

		internal ConfigEntry<float>? SpawnChance { get; private set; }

		internal ConfigEntry<float>? DeathUploadChance { get; private set; }

		internal ConfigEntry<float>? PassUploadChance { get; private set; }

		internal ConfigEntry<float>? DeathVideoChance { get; private set; }

		private void Awake()
		{
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Expected O, but got Unknown
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Expected O, but got Unknown
			Instance = this;
			Random = new Random();
			FakeVideos = new List<VideoHandle>();
			ProcessedFakeVideos = new List<VideoHandle>();
			SentVideos = new List<VideoHandle>();
			SentVotes = new List<VideoHandle>();
			ClientToServerId = new Dictionary<VideoHandle, string>();
			FakeContentBuffers = new Dictionary<VideoHandle, ContentBuffer>();
			string text = Path.Combine(Paths.BepInExRootPath, "persistent-config");
			string text2 = Utility.CombinePaths(new string[2]
			{
				text,
				FoundFootageBootstrapPlugin.Metadata.GUID + ".cfg"
			});
			string text3 = Utility.CombinePaths(new string[2]
			{
				text,
				FoundFootageBootstrapPlugin.Metadata.GUID + ".privatecfg"
			});
			if (File.Exists(text2))
			{
				File.Move(text2, text3);
				Logger.LogInfo((object)"Migrated persistent config to .privatecfg");
			}
			Config = new ConfigFile(Utility.CombinePaths(new string[2]
			{
				Paths.ConfigPath,
				FoundFootageBootstrapPlugin.Metadata.GUID + ".cfg"
			}), false, FoundFootageBootstrapPlugin.Metadata);
			PersistentConfig = new ConfigFile(text3, false, FoundFootageBootstrapPlugin.Metadata);
			ServerUrl = PersistentConfig.Bind<string>("Internal", "ServerUrl", "https://foundfootage-server.assasans.dev", "Base URL of the server hosting the videos.\n### DATA REQUEST OR REMOVAL ###\nIf you want to request your data or have it removed:\nsend an email to the address available on the \"/info\" endpoint (e.g. https://server.local/info).\nNote that your email must include your Secret User ID (see below).");
			UserId = PersistentConfig.Bind<string>("Internal", "UserId", "", "Random identifier associated with uploaded recordings and votes.");
			SecretUserId = PersistentConfig.Bind<string>("Internal", "SecretUserId", "", "Random identifier associated with Public User ID. Can be used to request or remove your data.");
			if (UserId.Value == (string)((ConfigEntryBase)UserId).DefaultValue)
			{
				UserId.Value = Guid.NewGuid().ToString();
				PersistentConfig.Save();
				Logger.LogInfo((object)("Generated user UUID: " + UserId.Value));
			}
			if (SecretUserId.Value == (string)((ConfigEntryBase)SecretUserId).DefaultValue)
			{
				SecretUserId.Value = Guid.NewGuid().ToString();
				PersistentConfig.Save();
				Logger.LogInfo((object)("Generated secret user UUID: " + new string('*', UserId.Value.Length)));
			}
			WarningShown = PersistentConfig.Bind<bool>("Internal", "WarningShown", false, "");
			ConfigTraceEnabled = PersistentConfig.Bind<bool>("Internal", "ConfigTraceEnabled", true, "Controls whether the mod's config is sent to the server when the game starts.\nThis is used to improve defaults and see how experimental features are used.");
			ConfigVersion = Config.Bind<int>("Internal", "ConfigVersion", 1, "");
			VotingEnabled = Config.Bind<bool>("Voting", "VotingEnabled", true, "Enable voting after watching another team's video.");
			SendPositionEnabled = Config.Bind<bool>("Voting", "SendPositionEnabled", true, "Send camera position on death to spawn camera at same position for other teams.");
			SendContentBufferEnabled = Config.Bind<bool>("Voting", "SendContentBufferEnabled", true, "Send recording content buffer (scoring data) to allow your videos to give views when viewed by other teams.");
			FoundVideoScoreMultiplier = Config.Bind<float>("Voting", "FoundVideoScoreMultiplier", 0.5f, "Controls how much of the original video score you receive. (1 - original score, 0 to disable views for fake videos).");
			FoundVideoSearchParams = Config.Bind<string>("Voting", "FoundVideoSearchParams", "[" + string.Join(", ", new List<string> { "LANGUAGE", "DAY" }) + "]", "Controls which criteria are used to retrieve found videos.\nServer is not required to respect this setting and may return arbitrary videos.\nAvailable values: [LANGUAGE, DAY, PLAYERS_COUNT]\nSet to [] to get completely random videos.");
			SpawnChance = Config.Bind<float>("Chances", "SpawnChance", 0.3f, "Chance that another team's camera will be spawned (0 to disable).");
			DeathUploadChance = Config.Bind<float>("Chances", "DeathUploadChance", 1f, "Chance that the camera video will be uploaded when all team members die (1 - always, 0 to disable).");
			PassUploadChance = Config.Bind<float>("Chances", "PassUploadChance", 0.25f, "Chance that the camera video will be uploaded when the team returns to surface (1 - always, 0 to disable).");
			DeathVideoChance = Config.Bind<float>("Chances", "DeathVideoChance", 0.75f, "Chance that the found camera will contain video of a dead team, instead of a survived one (1 - always, 0 - never).");
			Logger.LogInfo((object)"Plugin jp.assasans.cw.FoundFootage is loaded!");
			Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), "jp.assasans.cw.FoundFootage");
			Logger.LogInfo((object)"Plugin jp.assasans.cw.FoundFootage patched!");
			Logger.LogInfo((object)("Server URL: " + Instance.ServerUrl?.Value));
			MyceliumNetwork.RegisterNetworkObject((object)this, 551035154u, 0);
			MigrateConfig();
			ShowWarningIfNeeded();
			CheckVersion();
			TraceConfigIfNeeded();
			Dispatcher = ((Component)this).gameObject.AddComponent<MainThreadDispatcher>();
		}

		private void MigrateConfig()
		{
			if (ConfigVersion.Value < 2)
			{
				ConfigVersion.Value = 2;
				if (PunExtensions.AlmostEquals(PassUploadChance.Value, 1f, 0.01f))
				{
					PassUploadChance.Value = (float)((ConfigEntryBase)PassUploadChance).DefaultValue;
					Logger.LogInfo((object)$"Set PassUploadChance to {PassUploadChance.Value}");
				}
				Logger.LogInfo((object)$"Migrated to config version {ConfigVersion.Value}");
			}
		}

		private void ShowWarningIfNeeded()
		{
			if (!WarningShown.Value)
			{
				WarningShown.Value = true;
				Config.Save();
				Task.Run(delegate
				{
					Thread.Sleep(1000);
					MessageBoxUtils.Show("Content Warning - Found Footage", "READ THIS IMPORTANT INFORMATION!\n\nThis mod sends your in-game camera footage to the server along with audio, depending on the mod's configuration. ANYONE CAN WATCH YOUR VIDEOS LATER!\nYou can request your data or have it removed, see configuration file for more information.\n\nThis message will only be shown once.", 48u);
				});
			}
		}

		private void CheckVersion()
		{
			Task.Run(async delegate
			{
				_ = 1;
				try
				{
					VersionChecker checker = new VersionChecker();
					string text = await checker.GetVersion((MonoBehaviour)this);
					if (text == VersionChecker.IncompatibleVersion && ServerUrl.Value == "https://foundfootage-server.assasans.dev")
					{
						ServerUrl.Value = (string)((ConfigEntryBase)ServerUrl).DefaultValue;
						Logger.LogInfo((object)("Updated server URL to " + ServerUrl.Value));
						text = await checker.GetVersion((MonoBehaviour)this);
					}
					string text2 = "0.4.4";
					if (checker.IsCompatibleWith(text, text2))
					{
						Logger.LogInfo((object)("Local version " + text2 + " is compatible with remote " + text));
					}
					else
					{
						Logger.LogError((object)("Local version " + text2 + " is NOT compatible with remote " + text));
						MessageBoxUtils.Show("Content Warning - Found Footage", "Incompatible mod version! Local: " + text2 + ", remote: " + text + "\nYou can play the game, but beware of undefined behaviour. If possible, please update the mod.", 16u);
					}
				}
				catch (Exception ex)
				{
					Logger.LogError((object)ex);
					throw;
				}
			});
		}

		private void TraceConfigIfNeeded()
		{
			if (!ConfigTraceEnabled.Value)
			{
				return;
			}
			Task.Run(async delegate
			{
				try
				{
					await new ConfigTracer().TraceConfig(trace: new ConfigTrace
					{
						secretUserId = SecretUserId.Value,
						values = string.Join("\n", new Dictionary<string, object>
						{
							["SpawnChance"] = SpawnChance.Value,
							["DeathUploadChance"] = DeathUploadChance.Value,
							["PassUploadChance"] = PassUploadChance.Value,
							["DeathVideoChance"] = DeathVideoChance.Value,
							["VotingEnabled"] = VotingEnabled.Value,
							["SendPositionEnabled"] = SendPositionEnabled.Value,
							["SendContentBufferEnabled"] = SendContentBufferEnabled.Value,
							["FoundVideoScoreMultiplier"] = FoundVideoScoreMultiplier.Value,
							["FoundVideoSearchParams"] = FoundVideoSearchParams.Value
						}.Select<KeyValuePair<string, object>, string>((KeyValuePair<string, object> entry) => $"{entry.Key}={entry.Value}"))
					}, target: (MonoBehaviour)this);
				}
				catch (Exception ex)
				{
					Logger.LogError((object)ex);
				}
			});
		}

		private ContentEventFrame CreateContentEventForgiving(BinaryDeserializer deserializer)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: 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_0065: Unknown result type (might be due to invalid IL or missing references)
			ContentEventFrame val = new ContentEventFrame
			{
				seenAmount = deserializer.ReadFloat(),
				time = deserializer.ReadFloat(),
				contentEvent = ContentEventIDMapper.GetContentEvent(deserializer.ReadUShort())
			};
			int position = deserializer.position;
			val.contentEvent.Deserialize(deserializer);
			int num = deserializer.position - position;
			int num2 = deserializer.ReadInt();
			if (num != num2)
			{
				Logger.LogError((object)$"Size mismatch on content event: {((object)val.contentEvent).GetType().Name}. Expected: {num2}, Read: {num}");
			}
			return val;
		}

		private BufferedContent CreateBufferedContentForgiving(BinaryDeserializer deserializer)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			return new BufferedContent
			{
				score = deserializer.ReadFloat(),
				frame = CreateContentEventForgiving(deserializer)
			};
		}

		private ContentBuffer? CreateContentBufferForgiving(BinaryDeserializer deserializer)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			int num = deserializer.ReadInt();
			List<BufferedContent> list = new List<BufferedContent>();
			for (int i = 0; i < num; i++)
			{
				try
				{
					list.Add(CreateBufferedContentForgiving(deserializer));
				}
				catch (Exception arg)
				{
					Logger.LogError((object)$"Failed to read ContentBuffer.BufferedContent {i}: {arg}");
					return null;
				}
			}
			return new ContentBuffer
			{
				buffer = list
			};
		}

		private void CreateFakeContentBuffer(VideoHandle handle, GetSignedVideoResponse response)
		{
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Expected O, but got Unknown
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Logger.LogInfo((object)"CreateFakeContentBuffer!");
				if (response.contentBuffer == null)
				{
					return;
				}
				Logger.LogInfo((object)"Saving remote content buffer...");
				Logger.LogInfo((object)(response.contentBuffer ?? ""));
				byte[] array = Convert.FromBase64String(response.contentBuffer);
				Logger.LogInfo((object)$"{array.Length} bytes");
				BinaryDeserializer val = new BinaryDeserializer(array, (Allocator)2);
				try
				{
					Logger.LogInfo((object)"CreateContentBufferForgiving(deserializer)");
					ContentBuffer val2 = CreateContentBufferForgiving(val);
					if (val2 == null)
					{
						return;
					}
					Logger.LogInfo((object)$"Multiply score by {FoundVideoScoreMultiplier.Value}");
					foreach (BufferedContent item in val2.buffer)
					{
						item.score *= FoundVideoScoreMultiplier.Value;
					}
					FakeContentBuffers.Add(handle, val2);
				}
				finally
				{
					((IDisposable)val)?.Dispose();
				}
			}
			catch (Exception arg)
			{
				Logger.LogError((object)$"CreateFakeContentBuffer: {arg}");
			}
		}

		[CustomRPC]
		public void CreateFakeVideo(string guidString, string responseJson)
		{
			//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_0040: Unknown result type (might be due to invalid IL or missing references)
			Logger.LogInfo((object)"CreateFakeVideo!");
			VideoHandle handle = new VideoHandle(Guid.Parse(guidString));
			GetSignedVideoResponse response = JsonUtility.FromJson<GetSignedVideoResponse>(responseJson);
			ClientToServerId.Add(handle, response.videoId);
			if (!PunExtensions.AlmostEquals(FoundVideoScoreMultiplier.Value, 0f, 0.01f))
			{
				Dispatcher.Dispatch(delegate
				{
					//IL_0007: Unknown result type (might be due to invalid IL or missing references)
					CreateFakeContentBuffer(handle, response);
				});
			}
			else
			{
				Logger.LogInfo((object)"Remote content buffer is disabled in config");
			}
			Logger.LogInfo((object)("Downloading video " + guidString + " -> " + response.url));
			((MonoBehaviour)this).StartCoroutine(VideoCameraPatch.DownloadFakeVideo_Unity(response.url, delegate(Result<byte[], Exception> result)
			{
				//IL_003a: Unknown result type (might be due to invalid IL or missing references)
				if (result.Error != null)
				{
					Logger.LogError((object)$"An error occurred while getting signed URL: {result.Error}");
				}
				else
				{
					byte[] ok = result.Ok;
					Logger.LogInfo((object)"VideoCameraPatch.CreateFakeVideo");
					VideoCameraPatch.CreateFakeVideo(handle, ok);
				}
			}));
		}
	}
	public enum FoundVideoSearchParam
	{
		LANGUAGE,
		DAY,
		PLAYERS_COUNT
	}
	public class GetSignedVideoRequest
	{
		[SerializeField]
		public int day;

		[SerializeField]
		public int playerCount;

		[SerializeField]
		public string? reason;

		[SerializeField]
		public string? language;
	}
	public class GetSignedVideoResponse
	{
		[SerializeField]
		public string url;

		[SerializeField]
		public string videoId;

		[SerializeField]
		public string? position;

		[SerializeField]
		public string? contentBuffer;
	}
	public static class GuidUtils
	{
		public static Guid MakeLocal(Guid guid)
		{
			FieldInfo? field = typeof(Guid).GetField("_a", BindingFlags.Instance | BindingFlags.NonPublic);
			object obj = guid;
			field.SetValue(obj, int.MaxValue);
			return (Guid)obj;
		}

		public static bool IsLocal(Guid guid)
		{
			return (int)typeof(Guid).GetField("_a", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(guid) == int.MaxValue;
		}
	}
	public static class HttpUtils
	{
		internal static IEnumerator UploadVideo_Unity(string filePath, Dictionary<string, string> properties, Action<Result<string, Exception>> callback)
		{
			FoundFootagePlugin.Logger.LogInfo((object)"UploadVideo_Unity start");
			FoundFootagePlugin.Logger.LogInfo((object)"UploadVideo_Unity ReadAllBytes");
			byte[] array = File.ReadAllBytes(filePath);
			FoundFootagePlugin.Logger.LogInfo((object)"UploadVideo_Unity IMultipartFormSection");
			List<IMultipartFormSection> list = new List<IMultipartFormSection> { (IMultipartFormSection)new MultipartFormFileSection("file", array, "fullRecording.mp4", "application/octet-stream") };
			FoundFootagePlugin.Logger.LogInfo((object)"UploadVideo_Unity MultipartFormFileSection done");
			foreach (var (text3, text4) in properties)
			{
				string text5;
				if ((text5 = text4) == "")
				{
					text5 = "null";
				}
				FoundFootagePlugin.Logger.LogInfo((object)("UploadVideo_Unity MultipartFormDataSection " + text3 + ": " + text5));
				list.Add((IMultipartFormSection)new MultipartFormDataSection(text3, text5));
			}
			FoundFootagePlugin.Logger.LogInfo((object)"UploadVideo_Unity GenerateBoundary");
			byte[] array2 = UnityWebRequest.GenerateBoundary();
			FoundFootagePlugin.Logger.LogInfo((object)"UploadVideo_Unity SerializeFormSections");
			byte[] array3 = UnityWebRequest.SerializeFormSections(list, array2);
			FoundFootagePlugin.Logger.LogInfo((object)"UploadVideo_Unity Put");
			UnityWebRequest request = UnityWebRequest.Put(FoundFootagePlugin.Instance.ServerUrl.Value + "/videos?local=0.4.4", array3);
			try
			{
				request.SetRequestHeader("Content-Type", "multipart/form-data; boundary=\"" + Encoding.UTF8.GetString(array2) + "\"");
				FoundFootagePlugin.Logger.LogInfo((object)"UploadVideo_Unity waiting for response...");
				yield return request.SendWebRequest();
				FoundFootagePlugin.Logger.LogInfo((object)"UploadVideo_Unity got response");
				if ((int)request.result != 1)
				{
					callback(Result<string, Exception>.NewError(new Exception("request.error=" + request.error + " downloadHandler.error=" + request.downloadHandler.error)));
				}
				else
				{
					callback(Result<string, Exception>.NewOk(request.downloadHandler.text));
				}
			}
			finally
			{
				((IDisposable)request)?.Dispose();
			}
		}

		public static async Task UploadVideo(MonoBehaviour target, string filePath, Dictionary<string, string> properties)
		{
			try
			{
				FoundFootagePlugin.Logger.LogInfo((object)"UploadVideo start");
				string text = await UnityShit.CoroutineToTask(target, (Action<Result<string, Exception>> callback) => UploadVideo_Unity(filePath, properties, callback));
				FoundFootagePlugin.Logger.LogInfo((object)("Upload success: " + text));
			}
			catch (Exception arg)
			{
				FoundFootagePlugin.Logger.LogError((object)$"An error occurred while uploading video: {arg}");
				throw;
			}
		}
	}
	internal static class Lang
	{
		internal static bool IsRussian
		{
			get
			{
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				try
				{
					Locale selectedLocale = LocalizationSettings.SelectedLocale;
					object obj;
					if (selectedLocale == null)
					{
						obj = null;
					}
					else
					{
						LocaleIdentifier identifier = selectedLocale.Identifier;
						obj = ((LocaleIdentifier)(ref identifier)).Code;
					}
					return ((string)obj)?.StartsWith("ru") ?? false;
				}
				catch
				{
					return false;
				}
			}
		}

		internal static string Get(string en, string ru)
		{
			if (!IsRussian)
			{
				return en;
			}
			return ru;
		}
	}
	public class MainThreadDispatcher : MonoBehaviour
	{
		private static MainThreadDispatcher _instance;

		private readonly ConcurrentQueue<Action> mainThreadActions = new ConcurrentQueue<Action>();

		public static MainThreadDispatcher Instance => _instance;

		private void Awake()
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: 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
			if ((Object)(object)_instance != (Object)null && (Object)(object)_instance != (Object)(object)this)
			{
				Object.Destroy((Object)((Component)this).gameObject);
				return;
			}
			_instance = this;
			Object.DontDestroyOnLoad((Object)((Component)this).gameObject);
		}

		public void Dispatch(Action action)
		{
			mainThreadActions.Enqueue(action);
		}

		private void Update()
		{
			Action result;
			while (mainThreadActions.TryDequeue(out result))
			{
				result?.Invoke();
			}
		}
	}
	internal static class MessageBoxUtils
	{
		private delegate void WindowCallback(IntPtr hWnd);

		private delegate bool EnumWindowsProc(IntPtr hWnd, WindowCallback callback);

		[DllImport("user32.dll", CharSet = CharSet.Auto)]
		private static extern int MessageBox(IntPtr hWnd, string text, string caption, uint type);

		[DllImport("user32.dll")]
		private static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);

		[DllImport("user32.dll")]
		private static extern int GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

		[DllImport("user32.dll")]
		private static extern bool IsWindowVisible(IntPtr hWnd);

		[DllImport("user32.dll")]
		private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, WindowCallback lParam);

		private static bool EnumWindowsCallback(IntPtr handle, WindowCallback callback)
		{
			GetWindowThreadProcessId(handle, out var lpdwProcessId);
			if (lpdwProcessId == Process.GetCurrentProcess().Id)
			{
				StringBuilder stringBuilder = new StringBuilder(256);
				if (IsWindowVisible(handle) && GetWindowText(handle, stringBuilder, stringBuilder.Capacity) > 0)
				{
					callback(handle);
					return false;
				}
			}
			return true;
		}

		internal static void Show(string title, string content, uint type)
		{
			bool success = false;
			EnumWindows(EnumWindowsCallback, delegate(IntPtr handle)
			{
				success = true;
				MessageBox(handle, content, title, type);
			});
			if (!success)
			{
				MessageBox(IntPtr.Zero, content, title, type);
			}
		}
	}
	public class Result<T, E>
	{
		public T? Ok { get; set; }

		public E? Error { get; set; }

		private Result(T? ok, E? error)
		{
			Ok = ok;
			Error = error;
		}

		public static Result<T, E> NewOk(T value)
		{
			return new Result<T, E>(value, default(E));
		}

		public static Result<T, E> NewError(E value)
		{
			return new Result<T, E>(default(T), value);
		}
	}
	public static class UnityShit
	{
		public static Task<T> CoroutineToTask<T>(MonoBehaviour target, Func<Action<Result<T, Exception>>, IEnumerator> block)
		{
			TaskCompletionSource<T> source = new TaskCompletionSource<T>();
			target.StartCoroutine(block(delegate(Result<T, Exception> result)
			{
				if (result.Ok != null)
				{
					source.SetResult(result.Ok);
				}
				else if (result.Error != null)
				{
					source.SetException(result.Error);
				}
			}));
			return source.Task;
		}
	}
	public class VersionChecker
	{
		public static readonly string IncompatibleVersion = "0.0.0 (incompatible)";

		private IEnumerator GetVersion_Unity(Action<Result<string, Exception>> callback)
		{
			UnityWebRequest request = UnityWebRequest.Get(FoundFootagePlugin.Instance.ServerUrl.Value + "/version?local=0.4.4");
			try
			{
				yield return request.SendWebRequest();
				if ((int)request.result != 1)
				{
					callback(Result<string, Exception>.NewError(new Exception("request.error=" + request.error + " downloadHandler.error=" + request.downloadHandler.error)));
				}
				else
				{
					callback(Result<string, Exception>.NewOk(request.downloadHandler.text));
				}
			}
			finally
			{
				((IDisposable)request)?.Dispose();
			}
		}

		public async Task<string> GetVersion(MonoBehaviour target)
		{
			try
			{
				return await UnityShit.CoroutineToTask<string>(target, GetVersion_Unity);
			}
			catch (Exception ex)
			{
				FoundFootagePlugin.Logger.LogError((object)("An error occurred while fetching version: " + ex.Message));
				return IncompatibleVersion;
			}
		}

		public bool IsCompatibleWith(string compatible, string actual)
		{
			string[] array = compatible.Split('(');
			if (array[0].Trim() == actual)
			{
				return true;
			}
			if (array[1].Contains(actual + "-compatible"))
			{
				return true;
			}
			return false;
		}
	}
	public class VoteInteractable : Interactable
	{
		public VoteType Type;

		private CameraRecording? _recording;

		private bool _sending;

		private void Start()
		{
			((Interactable)this).hoverText = ((Type == VoteType.Like) ? Lang.Get("LIKE THE VIDEO", "ЛАЙКНУТЬ ВИДЕО") : Lang.Get("DISLIKE THE VIDEO", "ДИЗЛАЙКНУТЬ ВИДЕО"));
		}

		public override bool IsValid(Player player)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_recording != (Object)null && !_sending)
			{
				return !FoundFootagePlugin.Instance.SentVotes.Contains(_recording.videoHandle);
			}
			return false;
		}

		public override void Interact(Player player)
		{
			//IL_0042: 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_0082: Unknown result type (might be due to invalid IL or missing references)
			FoundFootagePlugin.Logger.LogInfo((object)$"{Type} interacted {_recording}");
			if (!FoundFootagePlugin.Instance.ClientToServerId.TryGetValue(_recording.videoHandle, out string value))
			{
				HelmetText.Instance.SetHelmetText(Lang.Get("Cannot vote: outdated host", "Нельзя проголосовать: у хоста старая версия"), 3f);
				return;
			}
			_sending = true;
			VideoHandle handle = _recording.videoHandle;
			FoundFootagePlugin.Logger.LogInfo((object)("Sending vote for " + value + "..."));
			((MonoBehaviour)FoundFootagePlugin.Instance).StartCoroutine(VoteUtils.SendVote_Unity(value, Type, delegate(Result<string, Exception> result)
			{
				//IL_0091: Unknown result type (might be due to invalid IL or missing references)
				_sending = false;
				if (result.Error != null)
				{
					FoundFootagePlugin.Logger.LogError((object)$"An error occurred while sending vote: {result.Error}");
					HelmetText.Instance.SetHelmetText(Lang.Get("You have already voted", "Вы уже голосовали"), 3f);
				}
				else
				{
					FoundFootagePlugin.Logger.LogInfo((object)("Vote sent successfully: " + result.Ok));
					HelmetText.Instance.SetHelmetText(Lang.Get("Thank you for voting", "Спасибо за голос"), 3f);
				}
				FoundFootagePlugin.Instance.SentVotes.Add(handle);
			}));
		}

		public void SetRecording(CameraRecording recording)
		{
			_recording = recording;
		}
	}
	public enum VoteType
	{
		Like = 1,
		Dislike
	}
	public static class VoteUtils
	{
		internal static IEnumerator SendVote_Unity(string videoId, VoteType type, Action<Result<string, Exception>> callback)
		{
			List<IMultipartFormSection> list = new List<IMultipartFormSection>();
			list.Add((IMultipartFormSection)new MultipartFormDataSection("video_id", videoId));
			list.Add((IMultipartFormSection)new MultipartFormDataSection("user_id", FoundFootagePlugin.Instance.UserId.Value));
			list.Add((IMultipartFormSection)new MultipartFormDataSection("lobby_id", PhotonNetwork.CurrentRoom.Name));
			List<IMultipartFormSection> list2 = list;
			list2.Add((IMultipartFormSection)new MultipartFormDataSection("vote_type", type switch
			{
				VoteType.Like => "like", 
				VoteType.Dislike => "dislike", 
				_ => throw new ArgumentOutOfRangeException("type", type, null), 
			}));
			List<IMultipartFormSection> list3 = list;
			byte[] array = UnityWebRequest.GenerateBoundary();
			UnityWebRequest request = UnityWebRequest.Post(FoundFootagePlugin.Instance.ServerUrl.Value + "/video/vote?local=0.4.4", list3, array);
			try
			{
				yield return request.SendWebRequest();
				if ((int)request.result != 1)
				{
					callback(Result<string, Exception>.NewError(new Exception("request.error=" + request.error + " downloadHandler.error=" + request.downloadHandler.error)));
				}
				else
				{
					callback(Result<string, Exception>.NewOk(request.downloadHandler.text));
				}
			}
			finally
			{
				((IDisposable)request)?.Dispose();
			}
		}
	}
}
namespace FoundFootage.Patches
{
	[HarmonyPatch(typeof(ContentBuffer))]
	internal static class ContentBufferPatch
	{
		[HarmonyPostfix]
		[HarmonyPatch("GenerateComments")]
		internal static void GenerateComments(ContentBuffer __instance, ref List<Comment> __result)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: 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)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			if (__instance.buffer.Count == 0)
			{
				__result.Add(new Comment("bruh you stole this from another team")
				{
					Likes = 0,
					Time = 0f,
					Face = 0,
					FaceColor = 0
				});
			}
		}
	}
	[HarmonyPatch(typeof(ExtractVideoMachine))]
	internal static class ExtractVideoMachinePatch
	{
		[HarmonyPostfix]
		[HarmonyPatch("RPC_Success")]
		internal static void RPC_Success(ExtractVideoMachine __instance)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Expected O, but got Unknown
			FoundFootagePlugin.Logger.LogInfo((object)"RPC_Success shitted");
			if (!PhotonNetwork.IsMasterClient)
			{
				return;
			}
			float value = FoundFootagePlugin.Instance.PassUploadChance.Value;
			if (PunExtensions.AlmostEquals(value, 0f, 0.01f))
			{
				return;
			}
			foreach (var (val3, recording) in RecordingsHandler.GetRecordings())
			{
				FoundFootagePlugin.Logger.LogInfo((object)$"Check {val3}");
				if (!GuidUtils.IsLocal(val3.id))
				{
					if (FoundFootagePlugin.Instance.Random.NextDouble() <= (double)value)
					{
						PhotonGameLobbyHandlerPatch.UploadRecording((MonoBehaviour)FoundFootagePlugin.Instance, val3, recording, "extract", null);
					}
					else
					{
						FoundFootagePlugin.Logger.LogInfo((object)"Do not uploading extracted");
					}
				}
			}
		}
	}
	[HarmonyPatch(typeof(PhotonGameLobbyHandler))]
	internal static class PhotonGameLobbyHandlerPatch
	{
		public static IEnumerator WaitThen(float t, Action a)
		{
			yield return (object)new WaitForSecondsRealtime(t);
			a();
		}

		[HarmonyPostfix]
		[HarmonyPatch("ReturnToSurface")]
		internal static void ReturnToSurface(PhotonGameLobbyHandler __instance)
		{
			FoundFootagePlugin.Logger.LogInfo((object)"ReturnToSurface enter");
			if (!(from pl in Object.FindObjectsOfType<Player>()
				where !pl.ai
				select pl).All((Player player) => player.data.dead))
			{
				return;
			}
			FoundFootagePlugin.Logger.LogInfo((object)"CheckForAllDead true");
			float chance = FoundFootagePlugin.Instance.DeathUploadChance.Value;
			if (PunExtensions.AlmostEquals(chance, 0f, 0.01f))
			{
				return;
			}
			((MonoBehaviour)__instance).StartCoroutine(WaitThen(5f, delegate
			{
				//IL_002c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0031: Unknown result type (might be due to invalid IL or missing references)
				//IL_0041: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b8: 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)
				Dictionary<VideoHandle, CameraRecording> recordings = RecordingsHandler.GetRecordings();
				Dictionary<Guid, VideoCamera> cameras = Singleton<CameraHandler>.Instance.m_cameras;
				foreach (KeyValuePair<VideoHandle, CameraRecording> item in recordings)
				{
					VideoHandle videoID = item.Key;
					FoundFootagePlugin.Logger.LogInfo((object)$"Check {videoID}");
					if (!GuidUtils.IsLocal(videoID.id))
					{
						if (FoundFootagePlugin.Instance.Random.NextDouble() <= (double)chance)
						{
							VideoCamera val = ((IEnumerable<VideoCamera>)cameras.Values).FirstOrDefault((Func<VideoCamera, bool>)((VideoCamera c) => (Object)(object)c != (Object)null && c.m_recorderInfoEntry != null && c.m_recorderInfoEntry.videoID.id == videoID.id));
							Vector3? position = (((Object)(object)val != (Object)null) ? new Vector3?(((Component)val).transform.position) : ((Vector3?)null));
							UploadRecording((MonoBehaviour)(object)FoundFootagePlugin.Instance, videoID, item.Value, "death", position);
						}
						else
						{
							FoundFootagePlugin.Logger.LogInfo((object)"Do not extracting");
						}
					}
				}
				FoundFootagePlugin.Logger.LogInfo((object)"CheckForAllDead done");
			}));
		}

		public static void UploadRecording(MonoBehaviour target, VideoHandle videoID, CameraRecording recording, string reason, Vector3? position)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: 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_011b: 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_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			if (FoundFootagePlugin.Instance.SentVideos.Contains(videoID))
			{
				FoundFootagePlugin.Logger.LogWarning((object)$"Not uploading duplicate video {videoID}");
				return;
			}
			FoundFootagePlugin.Instance.SentVideos.Add(videoID);
			byte[] array = (FoundFootagePlugin.Instance.SendContentBufferEnabled.Value ? SerializeContentBuffer(recording) : null);
			FoundFootagePlugin.Logger.LogInfo((object)$"Content buffer: {((array != null) ? array.Length : 0)} bytes");
			Dictionary<string, string> properties = new Dictionary<string, string>
			{
				["video_id"] = recording.videoHandle.id.ToString(),
				["user_id"] = FoundFootagePlugin.Instance.UserId.Value,
				["secret_user_id"] = FoundFootagePlugin.Instance.SecretUserId.Value,
				["lobby_id"] = PhotonNetwork.CurrentRoom.Name,
				["language"] = CultureInfo.InstalledUICulture.TwoLetterISOLanguageName,
				["position"] = (position.HasValue ? $"{position.Value.x}@{position.Value.y}@{position.Value.z}" : ""),
				["version"] = "0.4.4",
				["day"] = SurfaceNetworkHandler.RoomStats.CurrentDay.ToString(),
				["player_count"] = Object.FindObjectsOfType<Player>().Count((Player pl) => !pl.ai).ToString(),
				["reason"] = reason,
				["content_buffer"] = ((array != null) ? Convert.ToBase64String(array) : "")
			};
			target.StartCoroutine(ExtractAndUpload(videoID, recording, properties));
		}

		private static IEnumerator ExtractAndUpload(VideoHandle videoID, CameraRecording recording, Dictionary<string, string> properties)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			bool extracted = false;
			for (int i = 0; i < 30; i++)
			{
				if (extracted)
				{
					break;
				}
				if (i > 0)
				{
					FoundFootagePlugin.Logger.LogWarning((object)$"Failed to extract {videoID}, retrying...");
					yield return (object)new WaitForSecondsRealtime(5f);
				}
				yield return RetrievableSingleton<RecordingsHandler>.Instance.ExtractRecording(recording, (Action<bool>)delegate(bool ok)
				{
					extracted = ok;
				});
			}
			if (!extracted)
			{
				FoundFootagePlugin.Logger.LogError((object)$"Failed to extract {videoID}");
				yield break;
			}
			string path = Path.Combine(recording.GetDirectory(), "fullRecording.webm");
			FoundFootagePlugin.Logger.LogInfo((object)$"Extracted {videoID}: {path}");
			for (int i = 0; i < 10; i++)
			{
				Result<string, Exception> result = null;
				yield return HttpUtils.UploadVideo_Unity(path, properties, delegate(Result<string, Exception> r)
				{
					result = r;
				});
				if (result?.Error == null)
				{
					FoundFootagePlugin.Logger.LogInfo((object)("Uploaded video! " + result?.Ok));
					break;
				}
				FoundFootagePlugin.Logger.LogWarning((object)$"Failed to upload {videoID}, retrying: {result.Error.Message}");
				yield return (object)new WaitForSecondsRealtime(5f);
			}
		}

		private static byte[] SerializeContentBuffer(CameraRecording recording)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Expected O, but got Unknown
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			ContentBuffer val = new ContentBuffer();
			ContentBuffer val2 = default(ContentBuffer);
			foreach (Clip allClip in recording.GetAllClips())
			{
				if (allClip.Valid)
				{
					if (allClip.TryGetContentBuffer(ref val2))
					{
						val.AddBuffer(val2);
					}
					else
					{
						FoundFootagePlugin.Logger.LogWarning((object)$"No content buffer found for clip: {allClip.clipID}");
					}
				}
			}
			BinarySerializer val3 = new BinarySerializer(512, (Allocator)4);
			val.Serialize(val3);
			byte[] result = val3.buffer.ToArray();
			val3.Dispose();
			return result;
		}
	}
	[HarmonyPatch(typeof(ArtifactSpawner))]
	internal static class RoundArtifactSpawnerPatch
	{
		[HarmonyPostfix]
		[HarmonyPatch("Start")]
		internal static void Awake(ArtifactSpawner __instance)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: 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_0085: Expected O, but got Unknown
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Expected O, but got Unknown
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Expected O, but got Unknown
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: 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)
			FoundFootagePlugin.Logger.LogInfo((object)"Start shitted");
			if (!PhotonNetwork.IsMasterClient)
			{
				FoundFootagePlugin.Logger.LogWarning((object)"Not a master client");
			}
			else if (FoundFootagePlugin.Instance.Random.NextDouble() <= (double)FoundFootagePlugin.Instance.SpawnChance.Value)
			{
				FoundFootagePlugin.Logger.LogInfo((object)"[RoundArtifactSpawnerPatch] Spawning found camera!");
				Vector3 val = ((Component)__instance).transform.position + Vector3.up * __instance.spawnAbovePatrolPoint;
				ItemInstanceData val2 = new ItemInstanceData(Guid.NewGuid());
				VideoInfoEntry val3 = new VideoInfoEntry();
				val3.videoID = new VideoHandle(GuidUtils.MakeLocal(Guid.NewGuid()));
				val3.maxTime = 1f;
				val3.timeLeft = 0f;
				((ItemDataEntry)val3).SetDirty();
				val2.AddDataEntry((ItemDataEntry)val3);
				FoundFootagePlugin.Instance.FakeVideos.Add(val3.videoID);
				FoundFootagePlugin.Logger.LogInfo((object)$"[RoundArtifactSpawnerPatch] added entry {val3.videoID.id}");
				PickupHandler.CreatePickup((byte)1, val2, val, Random.rotation);
				FoundFootagePlugin.Logger.LogInfo((object)$"[RoundArtifactSpawnerPatch] Spawned found camera at {val}!");
			}
		}
	}
	[HarmonyPatch(typeof(UploadCompleteUI))]
	internal static class UploadCompleteUIPatch
	{
		private static Sprite? _likeSprite;

		private static Sprite? _dislikeSprite;

		[HarmonyPostfix]
		[HarmonyPatch("Awake")]
		public static void Awake(UploadCompleteUI __instance)
		{
			if (!FoundFootagePlugin.Instance.VotingEnabled.Value)
			{
				FoundFootagePlugin.Logger.LogInfo((object)"Voting is disabled, no vote buttons");
				return;
			}
			try
			{
				ReplayVideoInteractable componentInChildren = ((Component)__instance).GetComponentInChildren<ReplayVideoInteractable>(true);
				if ((Object)(object)componentInChildren == (Object)null)
				{
					FoundFootagePlugin.Logger.LogWarning((object)("No replay button on " + ((Object)__instance).name + ", no vote buttons"));
					return;
				}
				Transform parent = ((Component)componentInChildren).transform.parent;
				GameObject val = CreateButton(parent, "FoundFootageLike", VoteType.Like, Lang.Get("LIKE", "ЛАЙК"));
				GameObject obj = CreateButton(parent, "FoundFootageDislike", VoteType.Dislike, Lang.Get("DISLIKE", "ДИЗЛАЙК"));
				val.transform.SetSiblingIndex(parent.GetSiblingIndex() + 1);
				obj.transform.SetSiblingIndex(val.transform.GetSiblingIndex() + 1);
			}
			catch (Exception arg)
			{
				FoundFootagePlugin.Logger.LogError((object)$"Failed to add vote buttons: {arg}");
			}
		}

		private static GameObject CreateButton(Transform template, string name, VoteType type, string label)
		{
			GameObject val = Object.Instantiate<GameObject>(((Component)template).gameObject, template.parent);
			((Object)val).name = name;
			GameObjectLocalizer[] componentsInChildren = val.GetComponentsInChildren<GameObjectLocalizer>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren[i]);
			}
			((TMP_Text)val.GetComponentInChildren<TextMeshProUGUI>(true)).SetText(label);
			ReplayVideoInteractable componentInChildren = val.GetComponentInChildren<ReplayVideoInteractable>(true);
			GameObject gameObject = ((Component)componentInChildren).gameObject;
			Object.DestroyImmediate((Object)(object)componentInChildren);
			gameObject.AddComponent<VoteInteractable>().Type = type;
			val.SetActive(false);
			return val;
		}

		[HarmonyPostfix]
		[HarmonyPatch("PlayVideos")]
		public static void PlayVideos(UploadCompleteUI __instance, CameraRecording playableVideo)
		{
			bool flag = GuidUtils.IsLocal(playableVideo.videoHandle.id);
			FindSprites();
			VoteInteractable[] componentsInChildren = ((Component)__instance).GetComponentsInChildren<VoteInteractable>(true);
			foreach (VoteInteractable voteInteractable in componentsInChildren)
			{
				GameObject gameObject = ((Component)((Component)voteInteractable).transform.parent).gameObject;
				gameObject.SetActive(flag);
				if (flag)
				{
					voteInteractable.SetRecording(playableVideo);
					Sprite val = ((voteInteractable.Type == VoteType.Like) ? _likeSprite : _dislikeSprite);
					if ((Object)(object)val != (Object)null)
					{
						gameObject.GetComponentInChildren<Image>(true).sprite = val;
					}
				}
			}
		}

		private static void FindSprites()
		{
			if ((Object)(object)_likeSprite != (Object)null && (Object)(object)_dislikeSprite != (Object)null)
			{
				return;
			}
			Sprite[] array = Resources.FindObjectsOfTypeAll<Sprite>();
			foreach (Sprite val in array)
			{
				if (((Object)val).name == "Emoji-Great--Streamline-Ultimate")
				{
					_likeSprite = val;
				}
				else if (((Object)val).name == "Skull-1--Streamline-Ultimate (1)")
				{
					_dislikeSprite = val;
				}
			}
		}
	}
	[HarmonyPatch(typeof(VideoCamera))]
	internal static class VideoCameraPatch
	{
		[HarmonyTranspiler]
		[HarmonyPatch("Update")]
		public static IEnumerable<CodeInstruction> SuppressUpdateObjective(IEnumerable<CodeInstruction> instructions)
		{
			bool found = false;
			bool runAtNext = false;
			foreach (CodeInstruction instruction in instructions)
			{
				yield return instruction;
				if (CodeInstructionExtensions.Is(instruction, OpCodes.Isinst, (MemberInfo)typeof(FilmSomethingScaryObjective)))
				{
					runAtNext = true;
				}
				else if (runAtNext)
				{
					found = true;
					runAtNext = false;
					yield return new CodeInstruction(OpCodes.Ldarg_0, (object)null);
					yield return CodeInstruction.LoadField(typeof(VideoCamera), "m_recorderInfoEntry", false);
					yield return CodeInstruction.LoadField(typeof(VideoInfoEntry), "videoID", false);
					yield return CodeInstruction.LoadField(typeof(VideoHandle), "id", false);
					yield return CodeInstruction.Call(typeof(GuidUtils), "IsLocal", new Type[1] { typeof(Guid) }, (Type[])null);
					yield return new CodeInstruction(OpCodes.Brtrue_S, instruction.operand);
				}
			}
			if (!found)
			{
				FoundFootagePlugin.Logger.LogError((object)"Cannot find PhotonGameLobbyHandler.SetCurrentObjective in VideoCamera.Update");
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch("ConfigItem")]
		public static void InitCamera(VideoCamera __instance, ItemInstanceData data, PhotonView? playerView)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: 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_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Expected O, but got Unknown
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			FoundFootagePlugin.Logger.LogInfo((object)"InitCamera shitting from player");
			VideoInfoEntry entry = null;
			if (data.TryGetEntry<VideoInfoEntry>(ref entry))
			{
				FoundFootagePlugin.Logger.LogInfo((object)$"Handling {entry.videoID}");
				if (entry.videoID.id == VideoHandle.Invalid.id)
				{
					return;
				}
				FoundFootagePlugin.Logger.LogInfo((object)"Check IsLocal");
				if (!GuidUtils.IsLocal(entry.videoID.id))
				{
					return;
				}
				FoundFootagePlugin.Logger.LogInfo((object)"Check FakeVideos");
				if (!FoundFootagePlugin.Instance.FakeVideos.Contains(entry.videoID))
				{
					return;
				}
				entry.maxTime = 1f;
				entry.timeLeft = 0f;
				((ItemDataEntry)entry).SetDirty();
				((Renderer)(MeshRenderer)typeof(VideoCamera).GetField("m_cameraScreen", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(__instance)).enabled = false;
				FoundFootagePlugin.Logger.LogInfo((object)"Check playerView");
				if ((Object)playerView == (Object)null)
				{
					return;
				}
				FoundFootagePlugin.Logger.LogInfo((object)"Check ProcessedFakeVideos");
				if (FoundFootagePlugin.Instance.ProcessedFakeVideos.Contains(entry.videoID))
				{
					return;
				}
				FoundFootagePlugin.Instance.ProcessedFakeVideos.Add(entry.videoID);
				FoundFootagePlugin.Logger.LogInfo((object)"CreateFakeVideo start");
				List<FoundVideoSearchParam> list = FoundFootagePlugin.Instance.FoundVideoSearchParamsParsed.ToList();
				FoundFootagePlugin.Logger.LogInfo((object)("FoundVideoSearchParamsParsed: " + string.Join(", ", list)));
				GetSignedVideoRequest requestParams = new GetSignedVideoRequest
				{
					day = (list.Contains(FoundVideoSearchParam.DAY) ? SurfaceNetworkHandler.RoomStats.CurrentDay : 0),
					playerCount = (list.Contains(FoundVideoSearchParam.PLAYERS_COUNT) ? Object.FindObjectsOfType<Player>().Count((Player pl) => !pl.ai) : 0),
					reason = ((new Random().NextDouble() < (double)FoundFootagePlugin.Instance.DeathVideoChance.Value) ? "death" : "extract"),
					language = (list.Contains(FoundVideoSearchParam.LANGUAGE) ? CultureInfo.InstalledUICulture.TwoLetterISOLanguageName : null)
				};
				((MonoBehaviour)FoundFootagePlugin.Instance).StartCoroutine(DownloadFakeVideoSignedUrl_Unity(requestParams, delegate(Result<GetSignedVideoResponse, Exception> result)
				{
					if (result.Error != null)
					{
						FoundFootagePlugin.Logger.LogError((object)$"An error occurred while getting signed URL: {result.Error}");
					}
					else
					{
						GetSignedVideoResponse ok = result.Ok;
						FoundFootagePlugin.Logger.LogInfo((object)("Signed URL got successfully (video ID: " + ok.videoId + "): " + ok.url));
						FoundFootagePlugin.Logger.LogInfo((object)"Sending signed URL over Mycelium...");
						MyceliumNetwork.RPC(551035154u, "CreateFakeVideo", (ReliableType)1, new object[2]
						{
							entry.videoID.id.ToString(),
							JsonUtility.ToJson((object)ok)
						});
					}
				}));
				return;
			}
			FoundFootagePlugin.Logger.LogError((object)"No VideoInfoEntry found");
			throw new Exception();
		}

		private static IEnumerator DownloadFakeVideoSignedUrl_Unity(GetSignedVideoRequest requestParams, Action<Result<GetSignedVideoResponse, Exception>> callback)
		{
			UnityWebRequest request = UnityWebRequest.Post(FoundFootagePlugin.Instance.ServerUrl.Value + "/v3/video/signed?local=0.4.4", JsonUtility.ToJson((object)requestParams), "application/json");
			try
			{
				yield return request.SendWebRequest();
				if ((int)request.result != 1)
				{
					callback(Result<GetSignedVideoResponse, Exception>.NewError(new Exception("request.error=" + request.error + " downloadHandler.error=" + request.downloadHandler.error)));
					yield break;
				}
				GetSignedVideoResponse value = JsonUtility.FromJson<GetSignedVideoResponse>(request.downloadHandler.text);
				callback(Result<GetSignedVideoResponse, Exception>.NewOk(value));
			}
			finally
			{
				((IDisposable)request)?.Dispose();
			}
		}

		public static async Task<GetSignedVideoResponse> DownloadFakeVideoSignedUrl(MonoBehaviour target, GetSignedVideoRequest requestParams)
		{
			try
			{
				return await UnityShit.CoroutineToTask(target, (Action<Result<GetSignedVideoResponse, Exception>> callback) => DownloadFakeVideoSignedUrl_Unity(requestParams, callback));
			}
			catch (Exception arg)
			{
				FoundFootagePlugin.Logger.LogError((object)$"An error occurred while getting signed URL: {arg}");
				throw;
			}
		}

		public static IEnumerator DownloadFakeVideo_Unity(string signedUrl, Action<Result<byte[], Exception>> callback)
		{
			UnityWebRequest request = UnityWebRequest.Get(signedUrl);
			try
			{
				yield return request.SendWebRequest();
				if ((int)request.result != 1)
				{
					callback(Result<byte[], Exception>.NewError(new Exception("request.error=" + request.error + " downloadHandler.error=" + request.downloadHandler.error)));
				}
				else
				{
					callback(Result<byte[], Exception>.NewOk(request.downloadHandler.data.ToArray()));
				}
			}
			finally
			{
				((IDisposable)request)?.Dispose();
			}
		}

		public static async Task<byte[]> DownloadFakeVideo(MonoBehaviour target, string signedUrl)
		{
			try
			{
				return await UnityShit.CoroutineToTask(target, (Action<Result<byte[], Exception>> callback) => DownloadFakeVideo_Unity(signedUrl, callback));
			}
			catch (Exception arg)
			{
				FoundFootagePlugin.Logger.LogError((object)$"An error occurred while downloading the file: {arg}");
				throw;
			}
		}

		public static void CreateFakeVideo(VideoHandle handle, byte[] content)
		{
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Expected O, but got Unknown
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Expected O, but got Unknown
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Expected O, but got Unknown
			//IL_0186: 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)
			FoundFootagePlugin.Logger.LogInfo((object)"m_recordings");
			Dictionary<VideoHandle, CameraRecording> dictionary = (Dictionary<VideoHandle, CameraRecording>)typeof(RecordingsHandler).GetField("m_recordings", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(RetrievableSingleton<RecordingsHandler>.Instance);
			FoundFootagePlugin.Logger.LogInfo((object)"CreateNewRecording");
			CameraRecording val = (CameraRecording)typeof(RecordingsHandler).GetMethod("CreateNewRecording", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(RetrievableSingleton<RecordingsHandler>.Instance, new object[1] { handle });
			FoundFootagePlugin.Logger.LogInfo((object)$"recording: {val}");
			Clip val2 = new Clip(new ClipID(default(Guid)), true, PhotonNetwork.LocalPlayer.ActorNumber, val);
			val2.isRecording = false;
			val2.encoded = true;
			val2.local = true;
			if (FoundFootagePlugin.Instance.FakeContentBuffers.TryGetValue(handle, out ContentBuffer value))
			{
				val2.SetContentBufffer(value);
				FoundFootagePlugin.Logger.LogInfo((object)"Set fake content buffer from remote");
			}
			else
			{
				val2.SetContentBufffer(new ContentBuffer());
			}
			val2.SetValid(true);
			val.AddNewClip(val2);
			string clipDirectory = val2.GetClipDirectory();
			Directory.CreateDirectory(clipDirectory);
			using (FileStream fileStream = new FileStream(Path.Combine(clipDirectory, "output.webm"), FileMode.Create, FileAccess.Write, FileShare.None))
			{
				fileStream.Write(content.AsSpan());
			}
			using (FileStream fileStream2 = new FileStream(Path.Combine(clipDirectory, "../fullRecording.webm"), FileMode.Create, FileAccess.Write, FileShare.None))
			{
				fileStream2.Write(content.AsSpan());
			}
			if (!dictionary.ContainsKey(handle))
			{
				FoundFootagePlugin.Logger.LogInfo((object)"m_recordings.Add()");
				dictionary.Add(handle, val);
			}
			FoundFootagePlugin.Logger.LogInfo((object)"InitCamera shitted");
		}
	}
	[HarmonyPatch(typeof(VideoInfoEntry))]
	internal static class VideoInfoEntryPatch
	{
		[HarmonyPostfix]
		[HarmonyPatch("GetString")]
		public static void GetString(VideoInfoEntry __instance, ref string __result)
		{
			if (GuidUtils.IsLocal(__instance.videoID.id))
			{
				__result = Lang.Get("Found camera", "Найденная камера");
			}
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}