Decompiled source of FoundFootage v0.4.4

BepInEx/plugins/jp.assasans.cw.FoundFootage.dll

Decompiled 6 months 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.Net;
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.PropertyVariants;
using UnityEngine.Networking;
using UnityEngine.UI;
using Zorro.Core;
using Zorro.Core.Serizalization;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("jp.assasans.cw.FoundFootage")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("0.4.4.0")]
[assembly: AssemblyInformationalVersion("0.4.4")]
[assembly: AssemblyProduct("Found Footage")]
[assembly: AssemblyTitle("jp.assasans.cw.FoundFootage")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.4.4.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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 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.GetError={request.GetError()} request.error={request.error} downloadHandler.GetErrorMsg={request.downloadHandler.GetErrorMsg()} downloadHandler.error={request.downloadHandler.error}")));
				}
				else
				{
					callback(Result<int, Exception>.NewOk(0));
				}
			}
			finally
			{
				((IDisposable)request)?.Dispose();
			}
		}

		public async Task TraceConfig(MonoBehaviour target, ConfigTrace trace)
		{
			ConfigTrace trace2 = trace;
			try
			{
				await UnityShit.CoroutineToTask(target, (Action<Result<int, Exception>> callback) => TraceConfig_Unity(trace2, callback));
			}
			catch (Exception ex)
			{
				Exception exception = ex;
				FoundFootagePlugin.Logger.LogError((object)("An error occurred while tracing config: " + exception.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;
		}
	}
	public class ConfigTrace
	{
		[SerializeField]
		public string secretUserId;

		[SerializeField]
		public string values;
	}
	public enum FoundVideoSearchParam
	{
		LANGUAGE,
		DAY,
		PLAYERS_COUNT
	}
	[ContentWarningPlugin("jp.assasans.cw.FoundFootage", "0.4.4", false)]
	[BepInPlugin("jp.assasans.cw.FoundFootage", "Found Footage", "0.4.4")]
	[BepInDependency("RugbugRedfern.MyceliumNetworking", "1.0.10")]
	public class FoundFootagePlugin : BaseUnityPlugin
	{
		public const uint ModId = 551035154u;

		public static FoundFootagePlugin Instance { get; private set; }

		internal static ManualLogSource Logger { 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_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Expected O, but got Unknown
			Instance = this;
			Logger = ((BaseUnityPlugin)this).Logger;
			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>();
			BepInPlugin metadata = MetadataHelper.GetMetadata((object)this);
			string text = Path.Combine(Paths.BepInExRootPath, "persistent-config");
			string text2 = Utility.CombinePaths(new string[2]
			{
				text,
				metadata.GUID + ".cfg"
			});
			string text3 = Utility.CombinePaths(new string[2]
			{
				text,
				metadata.GUID + ".privatecfg"
			});
			if (File.Exists(text2))
			{
				File.Move(text2, text3);
				Logger.LogInfo((object)"Migrated persistent config to .privatecfg");
			}
			PersistentConfig = new ConfigFile(text3, false, 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 = ((BaseUnityPlugin)this).Config.Bind<int>("Internal", "ConfigVersion", 1, "");
			VotingEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Voting", "VotingEnabled", true, "Enable voting after watching another team's video.");
			SendPositionEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Voting", "SendPositionEnabled", true, "Send camera position on death to spawn camera at same position for other teams.");
			SendContentBufferEnabled = ((BaseUnityPlugin)this).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 = ((BaseUnityPlugin)this).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 = ((BaseUnityPlugin)this).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 = ((BaseUnityPlugin)this).Config.Bind<float>("Chances", "SpawnChance", 0.3f, "Chance that another team's camera will be spawned (0 to disable).");
			DeathUploadChance = ((BaseUnityPlugin)this).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 = ((BaseUnityPlugin)this).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 = ((BaseUnityPlugin)this).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;
				((BaseUnityPlugin)this).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
			{
				try
				{
					VersionChecker checker = new VersionChecker();
					string remote = await checker.GetVersion((MonoBehaviour)(object)this);
					if (remote == VersionChecker.IncompatibleVersion && ServerUrl.Value == "https://foundfootage-server.assasans.dev")
					{
						ServerUrl.Value = (string)((ConfigEntryBase)ServerUrl).DefaultValue;
						Logger.LogInfo((object)("Updated server URL to " + ServerUrl.Value));
						remote = await checker.GetVersion((MonoBehaviour)(object)this);
					}
					string local = "0.4.4";
					if (checker.IsCompatibleWith(remote, local))
					{
						Logger.LogInfo((object)("Local version " + local + " is compatible with remote " + remote));
					}
					else
					{
						Logger.LogError((object)("Local version " + local + " is NOT compatible with remote " + remote));
						MessageBoxUtils.Show("Content Warning - Found Footage", "Incompatible mod version! Local: " + local + ", remote: " + remote + "\nYou can play the game, but beware of undefined behaviour. If possible, please update the mod.", 16u);
					}
				}
				catch (Exception ex)
				{
					Exception exception = ex;
					Logger.LogError((object)exception);
					throw;
				}
			});
		}

		private void TraceConfigIfNeeded()
		{
			if (!ConfigTraceEnabled.Value)
			{
				return;
			}
			Task.Run(async delegate
			{
				try
				{
					ConfigTracer tracer = new ConfigTracer();
					ConfigTrace 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> entry) => $"{entry.Key}={entry.Value}"))
					};
					await tracer.TraceConfig((MonoBehaviour)(object)this, trace);
				}
				catch (Exception ex)
				{
					Exception exception = ex;
					Logger.LogError((object)exception);
				}
			});
		}

		private ContentEventFrame CreateContentEventForgiving(BinaryDeserializer deserializer)
		{
			//IL_0003: 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_0099: 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_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			ContentEventFrame val = default(ContentEventFrame);
			val.seenAmount = deserializer.ReadFloat();
			val.time = deserializer.ReadFloat();
			val.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_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_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			return new BufferedContent
			{
				score = deserializer.ReadFloat(),
				frame = CreateContentEventForgiving(deserializer)
			};
		}

		private ContentBuffer? CreateContentBufferForgiving(BinaryDeserializer deserializer)
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: 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 BufferedContent {i}: {arg}");
					return null;
				}
			}
			return new ContentBuffer
			{
				buffer = list
			};
		}

		private void CreateFakeContentBuffer(VideoHandle handle, GetSignedVideoResponse response)
		{
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Expected O, but got Unknown
			//IL_011e: 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_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			string guidString2 = guidString;
			Logger.LogInfo((object)"CreateFakeVideo!");
			VideoHandle handle = new VideoHandle(Guid.Parse(guidString2));
			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");
			}
			new Thread((ThreadStart)delegate
			{
				//IL_0061: Unknown result type (might be due to invalid IL or missing references)
				Logger.LogInfo((object)("Downloading video " + guidString2 + " -> " + response.url));
				byte[] result = VideoCameraPatch.DownloadFakeVideo((MonoBehaviour)(object)this, response.url).GetAwaiter().GetResult();
				Logger.LogInfo((object)"VideoCameraPatch.CreateFakeVideo");
				VideoCameraPatch.CreateFakeVideo(handle, result);
			}).Start();
		}
	}
	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
	{
		private 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[] bytes = File.ReadAllBytes(filePath);
			FoundFootagePlugin.Logger.LogInfo((object)"UploadVideo_Unity IMultipartFormSection");
			List<IMultipartFormSection> formData = new List<IMultipartFormSection> { (IMultipartFormSection)new MultipartFormFileSection("file", bytes, "fullRecording.mp4", "application/octet-stream") };
			FoundFootagePlugin.Logger.LogInfo((object)"UploadVideo_Unity MultipartFormFileSection done");
			foreach (KeyValuePair<string, string> property in properties)
			{
				property.Deconstruct(out var key2, out var value2);
				string key = key2;
				string value = value2;
				string realValue = value;
				if (value == "")
				{
					realValue = "null";
				}
				FoundFootagePlugin.Logger.LogInfo((object)("UploadVideo_Unity MultipartFormDataSection " + key + ": " + realValue));
				formData.Add((IMultipartFormSection)new MultipartFormDataSection(key, realValue));
			}
			FoundFootagePlugin.Logger.LogInfo((object)"UploadVideo_Unity GenerateBoundary");
			byte[] boundary = UnityWebRequest.GenerateBoundary();
			FoundFootagePlugin.Logger.LogInfo((object)"UploadVideo_Unity SerializeFormSections");
			byte[] formSections = UnityWebRequest.SerializeFormSections(formData, boundary);
			FoundFootagePlugin.Logger.LogInfo((object)"UploadVideo_Unity Put");
			UnityWebRequest request = UnityWebRequest.Put(FoundFootagePlugin.Instance.ServerUrl.Value + "/videos?local=0.4.4", formSections);
			try
			{
				request.SetRequestHeader("Content-Type", "multipart/form-data; boundary=\"" + Encoding.UTF8.GetString(boundary) + "\"");
				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.GetError={request.GetError()} request.error={request.error} downloadHandler.GetErrorMsg={request.downloadHandler.GetErrorMsg()} 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)
		{
			string filePath2 = filePath;
			Dictionary<string, string> properties2 = properties;
			try
			{
				FoundFootagePlugin.Logger.LogInfo((object)"UploadVideo start");
				string response = await UnityShit.CoroutineToTask(target, (Action<Result<string, Exception>> callback) => UploadVideo_Unity(filePath2, properties2, callback));
				FoundFootagePlugin.Logger.LogInfo((object)("Upload success: " + response));
			}
			catch (Exception ex)
			{
				Exception exception = ex;
				FoundFootagePlugin.Logger.LogError((object)$"An error occurred while uploading video: {exception}");
				throw;
			}
		}
	}
	public class LikeInteractable : Interactable
	{
		private CameraRecording? _recording;

		private bool _enabled = true;

		private void Start()
		{
			base.hoverText = "LIKE THE VIDEO";
		}

		public override bool IsValid(Player player)
		{
			return (Object)(object)_recording != (Object)null && _enabled;
		}

		public override void Interact(Player player)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			FoundFootagePlugin.Logger.LogInfo((object)$"LIKE INTERACTED {_recording}");
			if (!FoundFootagePlugin.Instance.ClientToServerId.TryGetValue(_recording.videoHandle, out string videoId))
			{
				HelmetText.Instance.SetHelmetText("Cannot vote: outdated host", 3f);
			}
			else
			{
				if (FoundFootagePlugin.Instance.SentVotes.Contains(_recording.videoHandle))
				{
					return;
				}
				new Thread((ThreadStart)delegate
				{
					//IL_0022: Unknown result type (might be due to invalid IL or missing references)
					_enabled = false;
					FoundFootagePlugin.Instance.SentVotes.Add(_recording.videoHandle);
					FoundFootagePlugin.Logger.LogInfo((object)("Sending vote for " + videoId + "..."));
					try
					{
						VoteUtils.SendVote((MonoBehaviour)(object)this, videoId, VoteType.Like).GetAwaiter().GetResult();
						HelmetText.Instance.SetHelmetText("Thank you for voting", 3f);
					}
					catch (WebException)
					{
						HelmetText.Instance.SetHelmetText("You have already voted", 3f);
					}
					FoundFootagePlugin.Logger.LogInfo((object)"Sent vote");
				}).Start();
			}
		}

		public void SetRecording(CameraRecording recording)
		{
			_recording = recording;
		}
	}
	public class DislikeInteractable : Interactable
	{
		private CameraRecording? _recording;

		private bool _enabled = true;

		private void Start()
		{
			base.hoverText = "DISLIKE THE VIDEO";
		}

		public override bool IsValid(Player player)
		{
			return (Object)(object)_recording != (Object)null && _enabled;
		}

		public override void Interact(Player player)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			FoundFootagePlugin.Logger.LogInfo((object)$"DISLIKE INTERACTED {_recording}");
			if (!FoundFootagePlugin.Instance.ClientToServerId.TryGetValue(_recording.videoHandle, out string videoId))
			{
				HelmetText.Instance.SetHelmetText("Cannot vote: outdated host", 3f);
			}
			else
			{
				if (FoundFootagePlugin.Instance.SentVotes.Contains(_recording.videoHandle))
				{
					return;
				}
				new Thread((ThreadStart)delegate
				{
					//IL_0022: Unknown result type (might be due to invalid IL or missing references)
					_enabled = false;
					FoundFootagePlugin.Instance.SentVotes.Add(_recording.videoHandle);
					FoundFootagePlugin.Logger.LogInfo((object)("Sending vote for " + videoId + "..."));
					try
					{
						VoteUtils.SendVote((MonoBehaviour)(object)this, videoId, VoteType.Dislike).GetAwaiter().GetResult();
						HelmetText.Instance.SetHelmetText("Thank you for voting", 3f);
					}
					catch (WebException)
					{
						HelmetText.Instance.SetHelmetText("You have already voted", 3f);
					}
					FoundFootagePlugin.Logger.LogInfo((object)"Sent vote");
				}).Start();
				HelmetText.Instance.SetHelmetText("Thank you for voting", 3f);
			}
		}

		public void SetRecording(CameraRecording recording)
		{
			_recording = recording;
		}
	}
	public enum VoteType
	{
		Like = 1,
		Dislike
	}
	public static class VoteUtils
	{
		private static IEnumerator SendVote_Unity(string videoId, VoteType type, Action<Result<string, Exception>> callback)
		{
			List<IMultipartFormSection> formData = new List<IMultipartFormSection>
			{
				(IMultipartFormSection)new MultipartFormDataSection("video_id", videoId),
				(IMultipartFormSection)new MultipartFormDataSection("user_id", FoundFootagePlugin.Instance.UserId.Value),
				(IMultipartFormSection)new MultipartFormDataSection("lobby_id", PhotonNetwork.CurrentRoom.Name)
			};
			List<IMultipartFormSection> list = formData;
			if (1 == 0)
			{
			}
			string text = type switch
			{
				VoteType.Like => "like", 
				VoteType.Dislike => "dislike", 
				_ => throw new ArgumentOutOfRangeException("type", type, null), 
			};
			if (1 == 0)
			{
			}
			list.Add((IMultipartFormSection)new MultipartFormDataSection("vote_type", text));
			byte[] boundary = UnityWebRequest.GenerateBoundary();
			UnityWebRequest request = UnityWebRequest.Post(FoundFootagePlugin.Instance.ServerUrl.Value + "/video/vote?local=0.4.4", formData, boundary);
			try
			{
				yield return request.SendWebRequest();
				if ((int)request.result != 1)
				{
					callback(Result<string, Exception>.NewError(new Exception($"request.GetError={request.GetError()} request.error={request.error} downloadHandler.GetErrorMsg={request.downloadHandler.GetErrorMsg()} downloadHandler.error={request.downloadHandler.error}")));
				}
				else
				{
					callback(Result<string, Exception>.NewOk(request.downloadHandler.text));
				}
			}
			finally
			{
				((IDisposable)request)?.Dispose();
			}
		}

		public static async Task SendVote(MonoBehaviour target, string videoId, VoteType type)
		{
			string videoId2 = videoId;
			try
			{
				string response = await UnityShit.CoroutineToTask(target, (Action<Result<string, Exception>> callback) => SendVote_Unity(videoId2, type, callback));
				FoundFootagePlugin.Logger.LogInfo((object)("Vote sent successfully: " + response));
			}
			catch (Exception ex)
			{
				Exception exception = ex;
				FoundFootagePlugin.Logger.LogError((object)$"An error occurred while sending vote: {exception}");
				throw;
			}
		}
	}
	public class MainThreadDispatcher : MonoBehaviour
	{
		private static MainThreadDispatcher _instance;

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

		public static MainThreadDispatcher Instance => _instance;

		private void Awake()
		{
			if (Object.op_Implicit((Object)(object)_instance) && (Object)(object)_instance != (Object)(object)this)
			{
				Object.Destroy((Object)(object)((Component)this).gameObject);
				return;
			}
			_instance = this;
			Object.DontDestroyOnLoad((Object)(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)
		{
			string content2 = content;
			string title2 = title;
			bool success = false;
			EnumWindows(EnumWindowsCallback, delegate(IntPtr handle)
			{
				success = true;
				MessageBox(handle, content2, title2, type);
			});
			if (!success)
			{
				MessageBox(IntPtr.Zero, content2, title2, type);
			}
		}
	}
	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 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 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.GetError={request.GetError()} request.error={request.error} downloadHandler.GetErrorMsg={request.downloadHandler.GetErrorMsg()} 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)
			{
				Exception exception = ex;
				FoundFootagePlugin.Logger.LogError((object)("An error occurred while fetching version: " + exception.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 static class PluginInfo
	{
		public const string PLUGIN_GUID = "jp.assasans.cw.FoundFootage";

		public const string PLUGIN_NAME = "Found Footage";

		public const string PLUGIN_VERSION = "0.4.4";
	}
}
namespace FoundFootage.Patches
{
	[HarmonyPatch(typeof(ContentBuffer))]
	internal static class ContentBufferPatch
	{
		[HarmonyPostfix]
		[HarmonyPatch("GenerateComments")]
		internal static void GenerateComments(ContentBuffer __instance, ref List<Comment> __result)
		{
			//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_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: 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_0048: 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_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_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			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;
			}
			Dictionary<VideoHandle, CameraRecording> recordings = RecordingsHandler.GetRecordings();
			foreach (var (val3, recording) in recordings)
			{
				FoundFootagePlugin.Logger.LogInfo((object)$"Check {val3}");
				if (!GuidUtils.IsLocal(val3.id))
				{
					if (FoundFootagePlugin.Instance.Random.NextDouble() <= (double)value)
					{
						PhotonGameLobbyHandlerPatch.UploadRecording((MonoBehaviour)(object)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_0054: Unknown result type (might be due to invalid IL or missing references)
				//IL_0056: 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_010e: 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)
				Dictionary<VideoHandle, CameraRecording> recordings = RecordingsHandler.GetRecordings();
				Dictionary<Guid, VideoCamera> dictionary = (Dictionary<Guid, VideoCamera>)AccessTools.DeclaredField(typeof(CameraHandler), "m_cameras").GetValue(Singleton<CameraHandler>.Instance);
				foreach (KeyValuePair<VideoHandle, CameraRecording> item in recordings)
				{
					var (videoID, recording) = (KeyValuePair<VideoHandle, CameraRecording>)(ref item);
					FoundFootagePlugin.Logger.LogInfo((object)$"Check {videoID}");
					if (!GuidUtils.IsLocal(videoID.id))
					{
						if (FoundFootagePlugin.Instance.Random.NextDouble() <= (double)chance)
						{
							VideoCamera val3 = ((IEnumerable<VideoCamera>)dictionary.Values).SingleOrDefault((Func<VideoCamera, bool>)delegate(VideoCamera camera)
							{
								//IL_001b: Unknown result type (might be due to invalid IL or missing references)
								//IL_0021: Expected O, but got Unknown
								VideoInfoEntry val4 = (VideoInfoEntry)AccessTools.DeclaredField(typeof(VideoCamera), "m_recorderInfoEntry").GetValue(camera);
								return val4.videoID.id == videoID.id;
							});
							Vector3? position = ((!((Object)(object)val3 != (Object)null)) ? null : (((Object)(object)((Component)val3).transform != (Object)null) ? new Vector3?(((Component)val3).transform.position) : null));
							UploadRecording((MonoBehaviour)(object)FoundFootagePlugin.Instance, videoID, recording, "death", position);
						}
						else
						{
							FoundFootagePlugin.Logger.LogInfo((object)"Do not extracting");
						}
					}
				}
				FoundFootagePlugin.Logger.LogInfo((object)"CheckForAllDead shitted");
			}));
		}

		public static void UploadRecording(MonoBehaviour target, VideoHandle videoID, CameraRecording recording, string reason, Vector3? position)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			CameraRecording recording2 = recording;
			MonoBehaviour target2 = target;
			string reason2 = reason;
			if (FoundFootagePlugin.Instance.SentVideos.Contains(videoID))
			{
				FoundFootagePlugin.Logger.LogWarning((object)$"Not uploading duplicate video {videoID}");
				return;
			}
			FoundFootagePlugin.Instance.SentVideos.Add(videoID);
			byte[] contentBuffer = (FoundFootagePlugin.Instance.SendContentBufferEnabled.Value ? SerializeContentBuffer(recording2) : null);
			new Thread((ThreadStart)delegate
			{
				//IL_02b9: Unknown result type (might be due to invalid IL or missing references)
				//IL_002e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0301: 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_018e: Unknown result type (might be due to invalid IL or missing references)
				//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
				//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
				try
				{
					bool flag = false;
					for (int i = 0; i < 30; i++)
					{
						flag |= RetrievableSingleton<RecordingsHandler>.Instance.ExtractRecording(recording2);
						if (flag)
						{
							break;
						}
						FoundFootagePlugin.Logger.LogWarning((object)$"Failed to extract {videoID}, retrying...");
						Thread.Sleep(5000);
					}
					if (flag)
					{
						string text = Path.Combine(recording2.GetDirectory(), "fullRecording.webm");
						FoundFootagePlugin.Logger.LogInfo((object)$"Extracted {videoID}: {text}");
						for (int j = 0; j < 10; j++)
						{
							try
							{
								ManualLogSource logger = FoundFootagePlugin.Logger;
								byte[] array = contentBuffer;
								logger.LogInfo((object)$"Content buffer: {((array != null) ? array.Length : 0)} bytes");
								HttpUtils.UploadVideo(target2, text, new Dictionary<string, string>
								{
									["video_id"] = recording2.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"] = reason2,
									["content_buffer"] = ((contentBuffer != null) ? Convert.ToBase64String(contentBuffer) : "")
								}).GetAwaiter().GetResult();
								FoundFootagePlugin.Logger.LogInfo((object)"Uploaded video!");
								break;
							}
							catch (IOException ex)
							{
								FoundFootagePlugin.Logger.LogWarning((object)ex);
								FoundFootagePlugin.Logger.LogWarning((object)$"Failed to upload {videoID}, retrying...");
								Thread.Sleep(5000);
							}
						}
					}
					else
					{
						FoundFootagePlugin.Logger.LogError((object)$"Failed to extract {videoID}");
					}
				}
				catch (Exception ex2)
				{
					FoundFootagePlugin.Logger.LogError((object)ex2);
				}
			}).Start();
		}

		private static byte[] SerializeContentBuffer(CameraRecording recording)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Expected O, but got Unknown
			//IL_009d: 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)
			//IL_0057: 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)2);
			val.Serialize(val3);
			return val3.buffer.ToArray();
		}
	}
	[HarmonyPatch(typeof(ArtifactSpawner))]
	internal static class RoundArtifactSpawnerPatch
	{
		[HarmonyPostfix]
		[HarmonyPatch("Start")]
		internal static void Awake(ArtifactSpawner __instance)
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Expected O, but got Unknown
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Expected O, but got Unknown
			//IL_00ae: 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)
			//IL_00ed: 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_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: 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)(object)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
	{
		[HarmonyPostfix]
		[HarmonyPatch("PlayVideo")]
		public static void PlayVideo(UploadCompleteUI __instance, IPlayableVideo playableVideo)
		{
			if (!FoundFootagePlugin.Instance.VotingEnabled.Value)
			{
				FoundFootagePlugin.Logger.LogInfo((object)"Voting is disabled, not patching UploadCompleteUI.PlayVideo");
				return;
			}
			CameraRecording val = (CameraRecording)(object)((playableVideo is CameraRecording) ? playableVideo : null);
			if (val != null)
			{
				FoundFootagePlugin.Logger.LogInfo((object)"UploadCompleteUI.PlayVideo is CameraRecording");
				bool flag = GuidUtils.IsLocal(val.videoHandle.id);
				UploadVideoStationPatch.LikeUi.SetActive(flag);
				UploadVideoStationPatch.DislikeUi.SetActive(flag);
				UploadVideoStationPatch.LikeInteractable.SetActive(flag);
				UploadVideoStationPatch.DislikeInteractable.SetActive(flag);
				if (flag)
				{
					Transform parent = ((Component)__instance).gameObject.transform.parent.parent.parent;
					GameObject gameObject = ((Component)parent.Find("SaveVideoToDesktopInteractable")).gameObject;
					gameObject.GetComponentInChildren<LikeInteractable>().SetRecording(val);
					gameObject.GetComponentInChildren<DislikeInteractable>().SetRecording(val);
					FoundFootagePlugin.Logger.LogInfo((object)"UploadCompleteUI.PlayVideo completed");
				}
			}
			else
			{
				FoundFootagePlugin.Logger.LogWarning((object)"UploadCompleteUI.PlayVideo not CameraRecording");
			}
		}
	}
	[HarmonyPatch(typeof(UploadVideoStation))]
	internal static class UploadVideoStationPatch
	{
		public static GameObject? LikeUi;

		public static GameObject? DislikeUi;

		public static GameObject? LikeInteractable;

		public static GameObject? DislikeInteractable;

		[HarmonyPostfix]
		[HarmonyPatch("Awake")]
		public static void Awake(UploadVideoStation __instance)
		{
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			if (!FoundFootagePlugin.Instance.VotingEnabled.Value)
			{
				FoundFootagePlugin.Logger.LogInfo((object)"Voting is disabled, not patching UploadVideoStation.Awake");
				return;
			}
			FoundFootagePlugin.Logger.LogError((object)$"Root: {((Component)__instance).gameObject}");
			GameObject videoDone = ((Component)((Component)__instance.m_uploadCompleteUI).gameObject.transform.Find("VIDEO/VideoDone")).gameObject;
			GameObject saveVideo = ((Component)videoDone.transform.Find("SaveVideo")).gameObject;
			FoundFootagePlugin.Logger.LogError((object)$"SaveVideo: {saveVideo}");
			GameObject interactableRoot = ((Component)((Component)__instance).gameObject.transform.Find("SaveVideoToDesktopInteractable")).gameObject;
			BoxCollider[] componentsInChildren = interactableRoot.GetComponentsInChildren<BoxCollider>();
			foreach (BoxCollider val in componentsInChildren)
			{
				val.size = Vector3.zero;
			}
			GameObject replayInteractable = ((Component)interactableRoot.transform.Find("ReplayInt")).gameObject;
			FoundFootagePlugin.Logger.LogError((object)$"ReplayInt: {replayInteractable}");
			((MonoBehaviour)__instance).StartCoroutine(PhotonGameLobbyHandlerPatch.WaitThen(5f, delegate
			{
				//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
				//IL_015c: Unknown result type (might be due to invalid IL or missing references)
				//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
				//IL_0239: Unknown result type (might be due to invalid IL or missing references)
				Sprite sprite = null;
				Sprite sprite2 = null;
				Object[] array = Resources.FindObjectsOfTypeAll<Object>();
				Object[] array2 = array;
				foreach (Object val2 in array2)
				{
					Sprite val3 = (Sprite)(object)((val2 is Sprite) ? val2 : null);
					if (val3 != null)
					{
						if (val2.name == "Emoji-Great--Streamline-Ultimate")
						{
							FoundFootagePlugin.Logger.LogInfo((object)$"Found like sprite: {val3}");
							sprite = val3;
						}
						if (val2.name == "Skull-1--Streamline-Ultimate (1)")
						{
							FoundFootagePlugin.Logger.LogInfo((object)$"Found dislike sprite: {val3}");
							sprite2 = val3;
						}
					}
				}
				GameObject val4 = Object.Instantiate<GameObject>(saveVideo, videoDone.transform);
				((Object)val4).name = "Like";
				val4.transform.localPosition = new Vector3(-178.82f, -178.82f, 0f);
				Object.Destroy((Object)(object)val4.GetComponentInChildren<GameObjectLocalizer>());
				((TMP_Text)val4.GetComponentInChildren<TextMeshProUGUI>()).SetText("LIKE", true);
				val4.GetComponentInChildren<Image>().sprite = sprite;
				LikeUi = val4;
				GameObject val5 = Object.Instantiate<GameObject>(saveVideo, videoDone.transform);
				((Object)val5).name = "Dislike";
				val5.transform.localPosition = new Vector3(0f, -178.82f, 0f);
				Object.Destroy((Object)(object)val5.GetComponentInChildren<GameObjectLocalizer>());
				((TMP_Text)val5.GetComponentInChildren<TextMeshProUGUI>()).SetText("DISLIKE", true);
				val5.GetComponentInChildren<Image>().sprite = sprite2;
				DislikeUi = val5;
				GameObject val6 = Object.Instantiate<GameObject>(replayInteractable, interactableRoot.transform);
				((Object)val6).name = "LikeInt";
				val6.transform.localPosition = new Vector3(0f, -0.5772f, 0f);
				Object.Destroy((Object)(object)val6.GetComponent<ReplayVideoInteractable>());
				val6.AddComponent<LikeInteractable>();
				LikeInteractable = val6;
				GameObject val7 = Object.Instantiate<GameObject>(replayInteractable, interactableRoot.transform);
				((Object)val7).name = "DisikeInt";
				val7.transform.localPosition = new Vector3(1.0727f, -0.5772f, 0f);
				Object.Destroy((Object)(object)val7.GetComponent<ReplayVideoInteractable>());
				val7.AddComponent<DislikeInteractable>();
				DislikeInteractable = val7;
			}));
		}
	}
	[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_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: 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_0163: Expected O, but got Unknown
			//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
			FoundFootagePlugin.Logger.LogInfo((object)"InitCamera shitting from player");
			VideoInfoEntry entry = default(VideoInfoEntry);
			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();
				MeshRenderer val = (MeshRenderer)typeof(VideoCamera).GetField("m_cameraScreen", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(__instance);
				((Renderer)val).enabled = false;
				FoundFootagePlugin.Logger.LogInfo((object)"Check playerView");
				if ((Object)(object)playerView == (Object)null)
				{
					return;
				}
				FoundFootagePlugin.Logger.LogInfo((object)"Check ProcessedFakeVideos");
				if (!FoundFootagePlugin.Instance.ProcessedFakeVideos.Contains(entry.videoID))
				{
					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)
					};
					new Thread((ThreadStart)delegate
					{
						GetSignedVideoResponse result = DownloadFakeVideoSignedUrl((MonoBehaviour)(object)FoundFootagePlugin.Instance, requestParams).GetAwaiter().GetResult();
						FoundFootagePlugin.Logger.LogInfo((object)("Signed URL got successfully (video ID: " + result.videoId + "): " + result.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)result)
						});
					}).Start();
				}
				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.GetError={request.GetError()} request.error={request.error} downloadHandler.GetErrorMsg={request.downloadHandler.GetErrorMsg()} downloadHandler.error={request.downloadHandler.error}")));
				}
				else
				{
					GetSignedVideoResponse responseParams = JsonUtility.FromJson<GetSignedVideoResponse>(request.downloadHandler.text);
					callback(Result<GetSignedVideoResponse, Exception>.NewOk(responseParams));
				}
			}
			finally
			{
				((IDisposable)request)?.Dispose();
			}
		}

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

		private 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.GetError={request.GetError()} request.error={request.error} downloadHandler.GetErrorMsg={request.downloadHandler.GetErrorMsg()} 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)
		{
			string signedUrl2 = signedUrl;
			try
			{
				return await UnityShit.CoroutineToTask(target, (Action<Result<byte[], Exception>> callback) => DownloadFakeVideo_Unity(signedUrl2, callback));
			}
			catch (Exception ex)
			{
				Exception exception = ex;
				FoundFootagePlugin.Logger.LogError((object)$"An error occurred while downloading the file: {exception}");
				throw;
			}
		}

		public static void CreateFakeVideo(VideoHandle handle, byte[] content)
		{
			//IL_006a: 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_007c: Expected O, but got Unknown
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Expected O, but got Unknown
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Expected O, but got Unknown
			//IL_01a7: 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)
			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);
			string path = Path.Combine(clipDirectory, "output.webm");
			using (FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None))
			{
				fileStream.Write(content.AsSpan());
			}
			string path2 = Path.Combine(clipDirectory, "../fullRecording.webm");
			using (FileStream fileStream2 = new FileStream(path2, 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 = "Found camera";
			}
		}
	}
}