Decompiled source of SideloaderMint v1.1.1
Sirdoggy.SideloaderMint.dll
Decompiled a week ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Serialization; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using FistVR; using HarmonyLib; using Microsoft.CodeAnalysis; using Sirdoggy.SideloaderMint.Addons; using Sirdoggy.SideloaderMint.Addons.Data; using Sirdoggy.SideloaderMint.Addons.Model; using Sirdoggy.SideloaderMint.AssetLoaders; using Sirdoggy.SideloaderMint.AssetLoaders.Implementations; using Sirdoggy.SideloaderMint.Common; using Sirdoggy.SideloaderMint.Common.Structures; using Sirdoggy.SideloaderMint.Common.Validation; using Sirdoggy.SideloaderMint.Model; using Sirdoggy.SideloaderMint.Patchers; using Sirdoggy.SideloaderMint.Patchers.Extensions; using Sirdoggy.SideloaderMint.Patchers.Tags; using UnityEngine; using Valve.Newtonsoft.Json; using Valve.Newtonsoft.Json.Linq; using XUnity.ResourceRedirector; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyCompany("Sirdoggy")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Updated version of Sideloader with new features")] [assembly: AssemblyFileVersion("1.1.1.0")] [assembly: AssemblyInformationalVersion("1.1.1")] [assembly: AssemblyProduct("Sirdoggy.SideloaderMint")] [assembly: AssemblyTitle("SideloaderMint")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.1.1.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] internal sealed class ParamCollectionAttribute : Attribute { } [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 BepInEx { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] internal sealed class BepInAutoPluginAttribute : Attribute { public BepInAutoPluginAttribute(string id = null, string name = null, string version = null) { } } } namespace BepInEx.Preloader.Core.Patching { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] internal sealed class PatcherAutoPluginAttribute : Attribute { public PatcherAutoPluginAttribute(string id = null, string name = null, string version = null) { } } } namespace Sirdoggy.SideloaderMint { internal static class Constants { public const char AssetSegmentDelimiter = ':'; public const char AddonDirectoryNameSegmentDelimiter = '-'; public const string ManifestFileName = "manifest.json"; public const int HarmonyPriority = 300; public static string PluginDirectory => Path.Combine(Paths.PluginPath, "Sirdoggy.SideloaderMint".Replace('.', '-')); } internal static class MainAddonLoader { private const string AddonFolderName = "SideloaderMint"; private static readonly string LegacyAddonsPath = Path.Combine(Paths.BepInExRootPath, "SideloaderMint"); private static readonly GunSoundLoader GunSoundLoader = new GunSoundLoader(); private static readonly GunAudioSetLoader GunAudioSetLoader = new GunAudioSetLoader(); private static readonly AudioClipLoader AudioClipLoader = new AudioClipLoader(); private static readonly IAssetLoader[] AllAssetLoaders = new IAssetLoader[7] { AudioClipLoader, GunSoundLoader, GunAudioSetLoader, new MaterialLoader(), new MeshLoader(), new PrefabLoader(), new TextureLoader() }; internal static void InitializeAssetLoadersAndLoadAddons() { FVRFireArmPatcher.Initialize(GunSoundLoader, GunAudioSetLoader); AudioSourcePatcher.Initialize(AudioClipLoader); Plugin.Log.LogMessage((object)"Loading addons..."); List<string> list = new List<string>(); string[] directories; if (Directory.Exists(LegacyAddonsPath)) { directories = Directory.GetDirectories(LegacyAddonsPath, "*", SearchOption.TopDirectoryOnly); foreach (string text in directories) { if (File.Exists(Path.Combine(text, "manifest.json"))) { list.Add(text); } } } directories = Directory.GetDirectories(Paths.PluginPath, "*", SearchOption.TopDirectoryOnly); for (int i = 0; i < directories.Length; i++) { string text2 = Path.Combine(directories[i], "SideloaderMint"); if (!Directory.Exists(text2)) { continue; } if (File.Exists(Path.Combine(text2, "manifest.json"))) { list.Add(text2); continue; } string[] directories2 = Directory.GetDirectories(text2, "*", SearchOption.TopDirectoryOnly); foreach (string text3 in directories2) { if (File.Exists(Path.Combine(text3, "manifest.json"))) { list.Add(text3); } } } Dictionary<string, Addon> dictionary = new Dictionary<string, Addon>(); int num = 0; foreach (string item in list) { try { Addon addon = new Addon(item, requireUniqueAddonDirectoryName: false); if (addon.Manifest.Disabled) { Plugin.Log.LogInfo((object)("Skipped addon '" + addon.GUID + "' because it's disabled in the manifest.")); num++; } else if (dictionary.ContainsKey(addon.GUID)) { Plugin.Log.LogError((object)("Failed to load addon '" + addon.GUID + "' because an addon with the same GUID was already loaded.")); } else { Plugin.Log.LogInfo((object)("Loaded addon '" + addon.GUID + "'")); dictionary.Add(addon.GUID, addon); } } catch (Exception ex) { Plugin.Log.LogError((object)("Failed to load addon from path: " + item + $"\n- Cause: {ex.GetType()}" + "\n- Message: " + ex.Message)); } } StringBuilder stringBuilder = new StringBuilder().Append($"Loaded {dictionary.Count}/{list.Count} addons!"); if (num > 0) { stringBuilder.Append($" {num} addons were skipped because they're set as disabled in their manifest."); } Plugin.Log.LogInfo((object)stringBuilder.ToString()); try { IAssetLoader[] allAssetLoaders = AllAssetLoaders; foreach (IAssetLoader assetLoader in allAssetLoaders) { Plugin.Log.LogMessage((object)$"Initializing '{assetLoader}'...)'"); assetLoader.Initialize(dictionary.Values); } Plugin.Log.LogInfo((object)"Successfully initialized all asset loaders!"); } catch (Exception ex2) { Plugin.Log.LogFatal((object)("An exception has occured while initializing asset loaders, some of your installed addons will likely not work!" + $"\n- Cause: {ex2.GetType()}" + "\n- Message: " + ex2.Message)); } } } [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInProcess("h3vr.exe")] [BepInPlugin("Sirdoggy.SideloaderMint", "SideloaderMint", "1.1.1")] public class Plugin : BaseUnityPlugin { internal static ManualLogSource Log = null; internal static readonly PluginConfig PluginConfig = new PluginConfig(); public const string Id = "Sirdoggy.SideloaderMint"; public static string Name => "SideloaderMint"; public static string Version => "1.1.1"; internal static void LogVerbose(string message) { if (PluginConfig.VerboseDebugLogs.Value) { Log.LogDebug((object)message); } } private void Awake() { Log = ((BaseUnityPlugin)this).Logger; ResourceRedirection.EnableSyncOverAsyncAssetLoads(); Harmony.CreateAndPatchAll(typeof(AudioSourcePatcher), (string)null); Harmony.CreateAndPatchAll(typeof(FVRFireArmPatcher), (string)null); Harmony.CreateAndPatchAll(typeof(FVRFireArmMagazinePatcher), (string)null); Harmony.CreateAndPatchAll(typeof(ClosedBoltHandlePatcher), (string)null); Harmony.CreateAndPatchAll(typeof(ClosedBoltPatcher), (string)null); Harmony.CreateAndPatchAll(typeof(HandgunSlidePatcher), (string)null); Harmony.CreateAndPatchAll(typeof(TubeFedShotgunBoltPatcher), (string)null); Harmony.CreateAndPatchAll(typeof(TubeFedShotgunHandlePatcher), (string)null); MainAddonLoader.InitializeAssetLoadersAndLoadAddons(); } } internal class PluginConfig { private const string ConfigFileName = "Sirdoggy.SideloaderMint.cfg"; public ConfigEntry<bool> VerboseDebugLogs { get; } public PluginConfig() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown ConfigFile val = new ConfigFile(Path.Combine(Paths.ConfigPath, "Sirdoggy.SideloaderMint.cfg"), true); VerboseDebugLogs = val.Bind<bool>("Developer Settings", "VerboseDebugLogs", false, "Enables verbose debug logs. These will noticeably spam the console."); } } } namespace Sirdoggy.SideloaderMint.Patchers { [HarmonyWrapSafe] [HarmonyPriority(300)] [HarmonyPatch(typeof(AudioSource))] internal static class AudioSourcePatcher { private static AudioClipLoader? _audioClipLoader; internal static void Initialize(AudioClipLoader audioClipLoader) { _audioClipLoader = audioClipLoader; } [HarmonyPrefix] [HarmonyPatch("Play", new Type[] { typeof(ulong) })] private static void PlayPrefix(AudioSource __instance) { if (!Object.op_Implicit((Object)(object)__instance.clip)) { return; } Plugin.Log.LogDebug((object)$"Attempting to replace AudioClip '{__instance.clip}'"); if (_audioClipLoader == null) { Plugin.Log.LogError((object)(string.Format("Did not replace AudioClip '{0}' because '{1}' ", __instance.clip, "AudioSourcePatcher") + "has not been initialized yet.")); return; } if (!_audioClipLoader.LoadedAudioClips.TryGetValue(((Object)__instance.clip).name, out AudioClipLoader.AddonEntry value)) { Plugin.LogVerbose($"Did not replace AudioClip '{__instance.clip}' because it has no " + "replacement sounds."); return; } try { int num = new Random().Next(value.Replacements.Length); ReplacementAsset replacement = value.Replacements[num]; AudioClip clip = value.Addon.LoadAudioClip(replacement, ((Object)__instance.clip).name); __instance.clip = clip; float[] volumeRange = value.VolumeRange; float[] pitchRange = value.PitchRange; if (volumeRange != null) { float volume = Random.Range(volumeRange[0], volumeRange[1]); __instance.volume = volume; } if (pitchRange != null) { float pitch = Random.Range(pitchRange[0], pitchRange[1]); __instance.pitch = pitch; } Plugin.Log.LogDebug((object)$"Successfully replaced AudioClip '{__instance.clip}'"); } catch (Exception ex) { Plugin.Log.LogError((object)($"Failed to load AudioClip '{__instance.clip}' from mod '{value.Addon.GUID}'" + $"\n- Cause: {ex.GetType()}" + "\n- Message: " + ex.Message)); } } } [HarmonyWrapSafe] [HarmonyPriority(300)] [HarmonyPatch(typeof(ClosedBoltHandle))] internal static class ClosedBoltHandlePatcher { private class UpdateHandleSharedState { public readonly bool BoltCanBeSlappedInPrefix; public UpdateHandleSharedState(bool boltCanBeSlappedInPrefix) { BoltCanBeSlappedInPrefix = boltCanBeSlappedInPrefix; base..ctor(); } } [HarmonyPostfix] [HarmonyPatch("UpdateInteraction")] private static void UpdateInteractionPostfix(ClosedBoltHandle __instance) { FirearmTag component = ((Component)__instance.Weapon).gameObject.GetComponent<FirearmTag>(); if (Object.op_Implicit((Object)(object)component)) { string itemID = ((FVRPhysicalObject)__instance.Weapon).ObjectWrapper.ItemID; Plugin.LogVerbose($"[UpdateInteractionPostfix] Updating tag for firearm '{itemID}' ({component})"); if (__instance.Weapon.IsBoltHeldOpen()) { Plugin.LogVerbose("[UpdateInteractionPostfix] Bolt of firearm '" + itemID + "' is held open"); return; } component.OnManualCharge(); Plugin.LogVerbose($"[UpdateInteractionPostfix] Successfully updated tag for firearm '{itemID}' ({component})"); } } [HarmonyPrefix] [HarmonyPatch("UpdateHandle")] private static void UpdateHandlePrefix(ClosedBoltHandle __instance, ref UpdateHandleSharedState? __state) { if (__instance != null && __instance.m_hasRotCatch && __instance.IsSlappable && __instance.m_isAtLockAngle) { __state = new UpdateHandleSharedState(boltCanBeSlappedInPrefix: true); } } [HarmonyPostfix] [HarmonyPatch("UpdateHandle")] private static void UpdateHandlePostfix(ClosedBoltHandle __instance, ref UpdateHandleSharedState? __state) { if (__state != null) { FirearmTag component = ((Component)__instance.Weapon).gameObject.GetComponent<FirearmTag>(); if (Object.op_Implicit((Object)(object)component) && __state.BoltCanBeSlappedInPrefix && !__instance.m_isAtLockAngle) { component.OnHandleSlap(); } } } } [HarmonyWrapSafe] [HarmonyPriority(300)] [HarmonyPatch(typeof(ClosedBolt))] internal static class ClosedBoltPatcher { [HarmonyPostfix] [HarmonyPatch("UpdateInteraction")] private static void UpdateInteractionPostfix(ClosedBolt __instance) { FirearmTag component = ((Component)__instance.Weapon).gameObject.GetComponent<FirearmTag>(); if (Object.op_Implicit((Object)(object)component)) { string itemID = ((FVRPhysicalObject)__instance.Weapon).ObjectWrapper.ItemID; Plugin.LogVerbose($"[UpdateInteractionPostfix] Updating tag for firearm '{itemID}' ({component})"); if (__instance.Weapon.IsBoltHeldOpen()) { Plugin.LogVerbose("[UpdateInteractionPostfix] Bolt of firearm '" + itemID + "' is held open"); return; } component.OnManualCharge(); Plugin.LogVerbose($"[UpdateInteractionPostfix] Successfully updated tag for firearm '{itemID}' ({component})"); } } } [HarmonyWrapSafe] [HarmonyPriority(300)] [HarmonyPatch(typeof(HandgunSlide))] internal static class HandgunSlidePatcher { [HarmonyPostfix] [HarmonyPatch("EndInteraction")] private static void EndInteractionPostfix(HandgunSlide __instance) { FirearmTag component = ((Component)__instance.Handgun).gameObject.GetComponent<FirearmTag>(); if (Object.op_Implicit((Object)(object)component)) { string itemID = ((FVRPhysicalObject)__instance.Handgun).ObjectWrapper.ItemID; Plugin.LogVerbose($"[EndInteractionPostfix] Updating tag for handgun '{itemID}' ({component})"); if (__instance.Handgun.IsSlideHeldOpen()) { Plugin.LogVerbose("[EndInteractionPostfix] Slide of handgun '" + itemID + "' is being held open"); return; } component.OnManualCharge(); Plugin.LogVerbose($"[EndInteractionPostfix] Successfully updated tag for handgun '{itemID}' ({component})"); } } } [HarmonyWrapSafe] [HarmonyPriority(300)] [HarmonyPatch(typeof(TubeFedShotgunBolt))] internal static class TubeFedShotgunBoltPatcher { [HarmonyPostfix] [HarmonyPatch("UpdateBolt")] private static void UpdateInteractionPostfix(TubeFedShotgunBolt __instance) { FirearmTag component = ((Component)__instance.Shotgun).gameObject.GetComponent<FirearmTag>(); if (Object.op_Implicit((Object)(object)component) && ((FVRInteractiveObject)__instance).IsHeld && !__instance.Shotgun.IsTubeFedShotgunBoltHeldOpen()) { component.OnManualCharge(); } } } [HarmonyWrapSafe] [HarmonyPriority(300)] [HarmonyPatch(typeof(TubeFedShotgunHandle))] internal static class TubeFedShotgunHandlePatcher { [HarmonyPostfix] [HarmonyPatch("UpdateInteraction")] private static void UpdateInteractionPostfix(TubeFedShotgunHandle __instance) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Invalid comparison between Unknown and I4 FirearmTag component = ((Component)__instance.Shotgun).gameObject.GetComponent<FirearmTag>(); if (Object.op_Implicit((Object)(object)component)) { string itemID = ((FVRPhysicalObject)__instance.Shotgun).ObjectWrapper.ItemID; Plugin.LogVerbose($"[UpdateInteractionPostfix] Updating tag for shotgun '{itemID}' ({component})"); if ((int)__instance.Shotgun.Mode == 1) { Plugin.LogVerbose("[UpdateInteractionPostfix] Shotgun '" + itemID + "' is in automatic mode"); return; } component.OnManualCharge(); Plugin.LogVerbose("[UpdateInteractionPostfix] Successfully updated tag for " + $"shotgun '{itemID}' ({component})"); } } } [HarmonyWrapSafe] [HarmonyPriority(300)] [HarmonyPatch(typeof(FVRFireArmMagazine))] internal static class FVRFireArmMagazinePatcher { [HarmonyPostfix] [HarmonyPatch("Awake")] private static void AwakePostfix(FVRFireArmMagazine __instance) { if (Object.op_Implicit((Object)(object)((FVRPhysicalObject)__instance).ObjectWrapper)) { string itemID = ((FVRPhysicalObject)__instance).ObjectWrapper.ItemID; if (itemID != null) { Plugin.Log.LogMessage((object)("A firearm magazine with ItemID '" + itemID + "' has spawned.")); } } } } [HarmonyWrapSafe] [HarmonyPriority(300)] [HarmonyPatch(typeof(FVRFireArm))] internal static class FVRFireArmPatcher { private class HandleMagOverrideSharedState { public FVRFireArmMagazine FirearmMagReference { get; } public bool MagUsesOverride { get; } public AudioEvent? FirearmMagInClip { get; } public AudioEvent? FirearmMagOutClip { get; } public HandleMagOverrideSharedState(FVRFireArmMagazine firearmMagReference, bool magUsesOverride, AudioEvent? firearmMagInClip, AudioEvent? firearmMagOutClip) { FirearmMagReference = firearmMagReference; MagUsesOverride = magUsesOverride; FirearmMagInClip = firearmMagInClip; FirearmMagOutClip = firearmMagOutClip; base..ctor(); } } private class PlayAudioEventSharedState { public AudioEvent? OriginalAudioEvent { get; } public GunEvent.Value Type { get; } public PlayAudioEventSharedState(GunEvent.Value type, AudioEvent? originalAudioEvent) { OriginalAudioEvent = originalAudioEvent; Type = type; base..ctor(); } } private static GunSoundLoader? _gunSoundLoader; private static GunAudioSetLoader? _gunAudioSetLoader; private static readonly string ReplacementAudioClipName = Guid.NewGuid().ToString(); internal static void Initialize(GunSoundLoader gunSoundLoader, GunAudioSetLoader gunAudioSetLoader) { _gunSoundLoader = gunSoundLoader; _gunAudioSetLoader = gunAudioSetLoader; } [HarmonyPrefix] [HarmonyPatch("Fire")] private static void FirePrefix(FVRFireArm __instance) { FirearmTag component = ((Component)__instance).gameObject.GetComponent<FirearmTag>(); if (!Object.op_Implicit((Object)(object)component)) { return; } string itemID = ((FVRPhysicalObject)__instance).ObjectWrapper.ItemID; Plugin.LogVerbose($"[FirePrefix] Updating tag for firearm '{itemID}' ({component})"); ClosedBoltWeapon val = (ClosedBoltWeapon)(object)((__instance is ClosedBoltWeapon) ? __instance : null); if (val != null && val.WillBoltBeHeldOpenAfterNextShot()) { Plugin.LogVerbose("[FirePrefix] Bolt of firearm '" + itemID + "' will be held open"); return; } Handgun val2 = (Handgun)(object)((__instance is Handgun) ? __instance : null); if (val2 != null && val2.WillSlideBeHeldOpenAfterNextShot()) { Plugin.LogVerbose("[FirePrefix] Slide of handgun '" + itemID + "' will be held open"); return; } TubeFedShotgun val3 = (TubeFedShotgun)(object)((__instance is TubeFedShotgun) ? __instance : null); if (val3 != null && val3.WillTubeFedShotgunBoltBeHeldOpenAfterNextShot()) { Plugin.LogVerbose("[FirePrefix] Bolt of shotgun '" + itemID + "' will be held open, or shotgunis in pump mode"); return; } component.OnShot(); Plugin.LogVerbose($"[FirePrefix] Successfully updated tag for firearm '{itemID}' ({component})"); } [HarmonyPrefix] [HarmonyPatch(typeof(FVRFireArm), "PlayAudioEvent")] private static void PlayAudioEventPrefix(FVRFireArm __instance, FirearmAudioEventType eType, ref PlayAudioEventSharedState? __state) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) PlayAudioEventSharedPrefix(__instance, eType, ref __state); } [HarmonyPostfix] [HarmonyPatch(typeof(FVRFireArm), "PlayAudioEvent")] private static void PlayAudioEventPostfix(FVRFireArm __instance, ref PlayAudioEventSharedState? __state) { PlayAudioEventSharedPostfix(__instance, ref __state); } [HarmonyPrefix] [HarmonyPatch("LoadMag")] private static void LoadMagPrefix(FVRFireArmMagazine mag, FVRFireArm __instance, ref HandleMagOverrideSharedState? __state) { HandleMagOverrideSharedPrefix(mag, __instance, ref __state, GunEvent.Value.MagazineIn); } [HarmonyPrefix] [HarmonyPatch("EjectMag")] private static void EjectMagPrefix(FVRFireArm __instance, ref HandleMagOverrideSharedState? __state) { HandleMagOverrideSharedPrefix(__instance.Magazine, __instance, ref __state, GunEvent.Value.MagazineOut); } [HarmonyPostfix] [HarmonyPatch("LoadMag")] private static void LoadMagPostfix(FVRFireArmMagazine mag, FVRFireArm __instance, HandleMagOverrideSharedState? __state) { HandleMagOverrideSharedPostfix(__instance, __state); } [HarmonyPostfix] [HarmonyPatch("EjectMag")] private static void EjectMagPostfix(FVRFireArm __instance, HandleMagOverrideSharedState? __state) { HandleMagOverrideSharedPostfix(__instance, __state); } private static void HandleMagOverrideSharedPrefix(FVRFireArmMagazine magazine, FVRFireArm firearm, ref HandleMagOverrideSharedState? state, GunEvent.Value gunEvent) { if (!Object.op_Implicit((Object)(object)magazine) || !Object.op_Implicit((Object)(object)((FVRPhysicalObject)magazine).ObjectWrapper)) { return; } string itemID = ((FVRPhysicalObject)magazine).ObjectWrapper.ItemID; if (itemID == null) { Plugin.Log.LogWarning((object)"[HandleMagOverrideSharedPrefix] Did not replace audio for magazine because its itemID is null."); } else { if (!Object.op_Implicit((Object)(object)((FVRPhysicalObject)firearm).ObjectWrapper)) { return; } string itemID2 = ((FVRPhysicalObject)firearm).ObjectWrapper.ItemID; if (itemID2 == null) { Plugin.Log.LogWarning((object)("[HandleMagOverrideSharedPrefix] Did not replace audio for magazine with ItemID '" + itemID + "' because its parent firearm has a null ItemID.")); return; } if (_gunSoundLoader == null || _gunAudioSetLoader == null) { Plugin.Log.LogError((object)"[HandleMagOverrideSharedPrefix] Failed to replace audio for magazine because 'FVRFireArmPatcher' has not been initialized yet."); return; } if (Object.op_Implicit((Object)(object)firearm.AudioClipSet)) { string name = ((Object)firearm.AudioClipSet).name; state = new HandleMagOverrideSharedState(magazine, magazine.UsesOverrideInOut, firearm.AudioClipSet.MagazineIn.DeepCopy(), firearm.AudioClipSet.MagazineOut.DeepCopy()); string text = string.Join(':'.ToString(), itemID2, gunEvent.ToString(), itemID); string text2 = string.Join(':'.ToString(), name, gunEvent.ToString(), itemID); Plugin.LogVerbose("[HandleMagOverrideSharedPrefix] Attempting to replace firearm magazine audio for '" + text + "' and '" + text2 + "'"); AssetTree<AddonAsset>.AssetNode assetNode = _gunAudioSetLoader.AssetTree?.Find(new AssetTree<AddonAsset>.FindBehaviour.GetMostSpecificThatMatchesQueryAndIsAtLeastTheSameLevel(), name, gunEvent.ToString(), itemID).FirstOrDefault(); AssetTree<AddonAsset>.AssetNode assetNode2 = _gunSoundLoader.AssetTree?.Find(new AssetTree<AddonAsset>.FindBehaviour.GetMostSpecificThatMatchesQueryAndIsAtLeastTheSameLevel(), itemID2, gunEvent.ToString(), itemID).FirstOrDefault(); try { int num; if (assetNode != null) { AddonAsset asset = assetNode.Asset; if (asset != null) { AddonManifest.AssetMapping assetMapping = asset.AssetMapping; if (assetMapping != null) { ReplacementAsset[] replacements = assetMapping.Replacements; if (replacements != null) { num = ((replacements.Length > 0) ? 1 : 0); goto IL_024e; } } } } num = 0; goto IL_024e; IL_0283: int num2; bool flag = (byte)num2 != 0; if (num == 0 && !flag) { Plugin.LogVerbose("[HandleMagOverrideSharedPrefix] Did not replace audio for '" + text + "' and '" + text2 + "' because they have no replacement sounds."); return; } Plugin.LogVerbose("[HandleMagOverrideSharedPrefix] Replacing audio for '" + text + "' and '" + text2 + "'"); magazine.UsesOverrideInOut = false; if (flag) { AddonAsset asset2 = assetNode2.Asset; firearm.AudioClipSet.ReplaceAudioEventByGunEventFromAddonAsset(asset2, gunEvent); } else { AddonAsset asset3 = assetNode.Asset; firearm.AudioClipSet.ReplaceAudioEventByGunEventFromAddonAsset(asset3, gunEvent); } return; IL_024e: if (assetNode2 != null) { AddonAsset asset = assetNode2.Asset; if (asset != null) { AddonManifest.AssetMapping assetMapping = asset.AssetMapping; if (assetMapping != null) { ReplacementAsset[] replacements = assetMapping.Replacements; if (replacements != null) { num2 = ((replacements.Length > 0) ? 1 : 0); goto IL_0283; } } } } num2 = 0; goto IL_0283; } catch (Exception ex) { Plugin.Log.LogError((object)("[HandleMagOverrideSharedPrefix] Failed to replace audio for '" + text + "' and '" + text2 + $"\n- Cause: {ex.GetType()}" + "\n- Message: " + ex.Message)); return; } } Plugin.Log.LogWarning((object)("[HandleMagOverrideSharedPrefix] Did not replace audio for magazine with ItemID '" + itemID + "' because the AudioClipSet of the parent '" + itemID2 + "' is null.")); } } private static void HandleMagOverrideSharedPostfix(FVRFireArm firearm, HandleMagOverrideSharedState? state) { if (state != null && Object.op_Implicit((Object)(object)state.FirearmMagReference)) { Plugin.LogVerbose("[HandleMagOverrideSharedPostfix] Setting UsesOverrideInOut back " + $"to: {state.MagUsesOverride}"); state.FirearmMagReference.UsesOverrideInOut = state.MagUsesOverride; firearm.AudioClipSet.MagazineIn = state.FirearmMagInClip; firearm.AudioClipSet.MagazineOut = state.FirearmMagOutClip; } } private static void PlayAudioEventSharedPrefix(FVRFireArm firearm, FirearmAudioEventType fvrAudioEventType, ref PlayAudioEventSharedState? state) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Invalid comparison between Unknown and I4 GunEvent.Value? value = (((int)fvrAudioEventType == 0) ? new GunEvent.Value?(GunEvent.Value.BoltSlideForward) : (((int)fvrAudioEventType != 11) ? null : new GunEvent.Value?(GunEvent.Value.HandleForward))); GunEvent.Value? value2 = value; if (!value2.HasValue || !Object.op_Implicit((Object)(object)((FVRPhysicalObject)firearm).ObjectWrapper)) { return; } string itemID = ((FVRPhysicalObject)firearm).ObjectWrapper.ItemID; if (itemID == null) { Plugin.Log.LogWarning((object)"[PlayAudioEventSharedPrefix] Did not replace audio for firearm because its ItemID is null."); return; } if (_gunSoundLoader == null || _gunAudioSetLoader == null) { Plugin.Log.LogError((object)"[PlayAudioEventSharedPrefix] Failed to replace audio for magazine because 'FVRFireArmPatcher' has not been initialized yet."); return; } if (!Object.op_Implicit((Object)(object)firearm.AudioClipSet)) { Plugin.Log.LogWarning((object)("[PlayAudioEventSharedPrefix] Did not replace slide audio for firearm with ItemID '" + itemID + "' because the AudioClipSet of the instance is null.")); return; } FirearmTag component = ((Component)firearm).GetComponent<FirearmTag>(); if (!Object.op_Implicit((Object)(object)component)) { Plugin.Log.LogError((object)("[PlayAudioEventSharedPrefix] Did not replace slide audio for firearm with ItemID '" + itemID + "' because FirearmTag is null.")); return; } AudioEvent val = (((int)fvrAudioEventType == 0) ? component.GetLatestSlideForwardCircumstanceAudioEvent() : (((int)fvrAudioEventType != 11) ? null : component.GetLatestHandleForwardCircumstanceAudioEvent())); AudioEvent val2 = val; if (val2 == null) { Plugin.LogVerbose("[PlayAudioEventSharedPrefix] Did not replace audio for firearm with ItemID '" + itemID + "' because there is no available circumstance override."); return; } AudioEvent audioEventBy = firearm.AudioClipSet.GetAudioEventBy(value2.Value); state = new PlayAudioEventSharedState(value2.Value, audioEventBy.DeepCopy()); firearm.AudioClipSet.ReplaceAudioEventBy(value2.Value, val2); } private static void PlayAudioEventSharedPostfix(FVRFireArm firearm, ref PlayAudioEventSharedState? state) { if (state?.OriginalAudioEvent != null) { Plugin.LogVerbose($"[PlayAudioEventSharedPostfix] Setting {state.Type} back to default clip."); firearm.AudioClipSet.ReplaceAudioEventBy(state.Type, state.OriginalAudioEvent); } } [HarmonyPostfix] [HarmonyPatch("Awake")] private static void AwakePostfix(FVRFireArm __instance) { if (!Object.op_Implicit((Object)(object)((FVRPhysicalObject)__instance).ObjectWrapper)) { return; } string itemID = ((FVRPhysicalObject)__instance).ObjectWrapper.ItemID; if (itemID == null) { Plugin.Log.LogWarning((object)"Did not replace audio for firearm because its ItemID is null."); return; } if (_gunSoundLoader == null || _gunAudioSetLoader == null) { Plugin.Log.LogError((object)("Failed to replace audio for firearm with ItemID '" + itemID + "' because 'FVRFireArmPatcher' has not been initialized yet.")); return; } if (!Object.op_Implicit((Object)(object)__instance.AudioClipSet)) { Plugin.Log.LogWarning((object)("Did not replace audio for firearm with ItemID '" + itemID + "' because the AudioClipSet of the instance is null.")); return; } string name = ((Object)__instance.AudioClipSet).name; Plugin.Log.LogMessage((object)("Attempting to replace audio for firearm with ItemID '" + itemID + "' and audio set '" + name + "'")); FVRFirearmAudioSet val = (__instance.AudioClipSet = __instance.AudioClipSet.DeepCopy()); FirearmTag firearmTag = ((Component)__instance).GetComponent<FirearmTag>(); if (!Object.op_Implicit((Object)(object)firearmTag)) { firearmTag = ((Component)__instance).gameObject.AddComponent<FirearmTag>(); ((Object)firearmTag).hideFlags = (HideFlags)61; } GunEvent.Value[] all = GunEvent.All; for (int i = 0; i < all.Length; i++) { GunEvent.Value gunEvent = all[i]; string text = itemID + ":" + gunEvent; string text2 = name + ":" + gunEvent; Plugin.LogVerbose("Attempting to replace firearm audio for '" + text + "' and '" + text2 + "'"); AssetTree<AddonAsset>.AssetNode assetNode = _gunAudioSetLoader.AssetTree?.Find(new AssetTree<AddonAsset>.FindBehaviour.GetMostSpecificThatMatchesQuery(), name, gunEvent.ToString()).FirstOrDefault(); AssetTree<AddonAsset>.AssetNode assetNode2 = _gunSoundLoader.AssetTree?.Find(new AssetTree<AddonAsset>.FindBehaviour.GetMostSpecificThatMatchesQuery(), itemID, gunEvent.ToString()).FirstOrDefault(); try { int num; if (assetNode != null) { AddonAsset asset = assetNode.Asset; if (asset != null) { AddonManifest.AssetMapping assetMapping = asset.AssetMapping; if (assetMapping != null) { ReplacementAsset[] replacements = assetMapping.Replacements; if (replacements != null) { num = ((replacements.Length > 0) ? 1 : 0); goto IL_021e; } } } } num = 0; goto IL_021e; IL_0253: int num2; bool flag = (byte)num2 != 0; if (num == 0 && !flag) { Plugin.LogVerbose("Did not replace audio for '" + text + "' and '" + text2 + "' because they have no replacement sounds."); } else if (flag) { AddonAsset asset2 = assetNode2.Asset; val.ReplaceAudioEventByGunEventFromAddonAsset(asset2, gunEvent); } else { AddonAsset asset3 = assetNode.Asset; val.ReplaceAudioEventByGunEventFromAddonAsset(asset3, gunEvent); } goto end_IL_01e9; IL_021e: if (assetNode2 != null) { AddonAsset asset = assetNode2.Asset; if (asset != null) { AddonManifest.AssetMapping assetMapping = asset.AssetMapping; if (assetMapping != null) { ReplacementAsset[] replacements = assetMapping.Replacements; if (replacements != null) { num2 = ((replacements.Length > 0) ? 1 : 0); goto IL_0253; } } } } num2 = 0; goto IL_0253; end_IL_01e9:; } catch (Exception ex) { Plugin.Log.LogError((object)("Failed to replace audio for '" + text + "' and '" + text2 + "'" + $"\n- Cause: {ex.GetType()}" + "\n- Message: " + ex.Message)); } } SaveSlideForwardOverrideAudioEventsInFirearmTag(itemID, name, firearmTag, val); SaveHandleForwardOverrideAudioEventsInFirearmTag(itemID, name, firearmTag, val); Plugin.LogVerbose($"FirearmTag initialization finished ({firearmTag})"); } private static void SaveSlideForwardOverrideAudioEventsInFirearmTag(string itemID, string gunAudioSetName, FirearmTag firearmTag, FVRFirearmAudioSet firearmAudioSet) { if (_gunAudioSetLoader == null || _gunSoundLoader == null) { return; } BoltSlideForwardCircumstance.Value[] all = BoltSlideForwardCircumstance.All; foreach (BoltSlideForwardCircumstance.Value value in all) { Plugin.LogVerbose("[SaveSlideForwardOverrideAudioEventsInFirearmTag] " + $"Attempting to find sound replacements for '{value}' for " + "'" + itemID + "' and '" + gunAudioSetName + "'"); string[] targetPath = value.GetTargetPath(); List<string> list = new List<string>(2) { gunAudioSetName, "BoltSlideForward" }; List<string> list2 = new List<string>(2) { itemID, "BoltSlideForward" }; list.AddRange(targetPath); list2.AddRange(targetPath); string text = StringUtils.JoinValues(list, ':'.ToString()); string text2 = StringUtils.JoinValues(list2, ':'.ToString()); Plugin.LogVerbose("[SaveSlideForwardOverrideAudioEventsInFirearmTag] Attempting to load '" + text + "' and '" + text2 + "'"); AssetTree<AddonAsset>.AssetNode assetNode = _gunAudioSetLoader.AssetTree?.Find(new AssetTree<AddonAsset>.FindBehaviour.GetMostSpecificThatMatchesQueryAndSpecifications(3, 4), list.ToArray()).FirstOrDefault(); AssetTree<AddonAsset>.AssetNode assetNode2 = _gunSoundLoader.AssetTree?.Find(new AssetTree<AddonAsset>.FindBehaviour.GetMostSpecificThatMatchesQueryAndSpecifications(3, 4), list2.ToArray()).FirstOrDefault(); try { int num; if (assetNode != null) { AddonAsset asset = assetNode.Asset; if (asset != null) { AddonManifest.AssetMapping assetMapping = asset.AssetMapping; if (assetMapping != null) { ReplacementAsset[] replacements = assetMapping.Replacements; if (replacements != null) { num = ((replacements.Length > 0) ? 1 : 0); goto IL_019b; } } } } num = 0; goto IL_019b; IL_01d0: int num2; bool flag = (byte)num2 != 0; if (num == 0 && !flag) { Plugin.LogVerbose("[SaveSlideForwardOverrideAudioEventsInFirearmTag] Could not find '" + text + "' and '" + text2 + "'"); continue; } Plugin.LogVerbose("[SaveSlideForwardOverrideAudioEventsInFirearmTag] Saving '" + text + "' and '" + text2 + "' in firearm tag"); AudioEvent val = firearmAudioSet.BoltSlideForward.DeepCopy(); if (val != null) { AddonAsset asset = ((!flag) ? assetNode.Asset : assetNode2.Asset); AddonAsset addonAsset = asset; val.ReplaceClipsInAudioEvent(addonAsset); firearmTag.SaveBoltSlideForwardCircumstanceAudioEvent(val, value); } goto end_IL_0166; IL_019b: if (assetNode2 != null) { AddonAsset asset = assetNode2.Asset; if (asset != null) { AddonManifest.AssetMapping assetMapping = asset.AssetMapping; if (assetMapping != null) { ReplacementAsset[] replacements = assetMapping.Replacements; if (replacements != null) { num2 = ((replacements.Length > 0) ? 1 : 0); goto IL_01d0; } } } } num2 = 0; goto IL_01d0; end_IL_0166:; } catch (Exception ex) { Plugin.Log.LogError((object)("[SaveSlideForwardOverrideAudioEventsInFirearmTag] Failed to replace audio for '" + text + "' and '" + text2 + $"\n- Cause: {ex.GetType()}" + "\n- Message: " + ex.Message)); } } } private static void SaveHandleForwardOverrideAudioEventsInFirearmTag(string itemID, string gunAudioSetName, FirearmTag firearmTag, FVRFirearmAudioSet firearmAudioSet) { if (_gunAudioSetLoader == null || _gunSoundLoader == null) { return; } HandleForwardCircumstance.Value[] all = HandleForwardCircumstance.All; foreach (HandleForwardCircumstance.Value value in all) { Plugin.LogVerbose("[SaveHandleForwardOverrideAudioEventsInFirearmTag] " + $"Attempting to find sound replacements for '{value}' for " + "'" + itemID + "' and '" + gunAudioSetName + "'"); string[] targetPath = value.GetTargetPath(); List<string> list = new List<string>(2) { gunAudioSetName, "HandleForward" }; List<string> list2 = new List<string>(2) { itemID, "HandleForward" }; list.AddRange(targetPath); list2.AddRange(targetPath); string text = StringUtils.JoinValues(list, ':'.ToString()); string text2 = StringUtils.JoinValues(list2, ':'.ToString()); Plugin.LogVerbose("[SaveHandleForwardOverrideAudioEventsInFirearmTag] Attempting to load '" + text + "' and '" + text2 + "'"); AssetTree<AddonAsset>.AssetNode assetNode = _gunAudioSetLoader.AssetTree?.Find(new AssetTree<AddonAsset>.FindBehaviour.GetMostSpecificThatMatchesQueryAndSpecifications(3, 4), list.ToArray()).FirstOrDefault(); AssetTree<AddonAsset>.AssetNode assetNode2 = _gunSoundLoader.AssetTree?.Find(new AssetTree<AddonAsset>.FindBehaviour.GetMostSpecificThatMatchesQueryAndSpecifications(3, 4), list2.ToArray()).FirstOrDefault(); try { int num; if (assetNode != null) { AddonAsset asset = assetNode.Asset; if (asset != null) { AddonManifest.AssetMapping assetMapping = asset.AssetMapping; if (assetMapping != null) { ReplacementAsset[] replacements = assetMapping.Replacements; if (replacements != null) { num = ((replacements.Length > 0) ? 1 : 0); goto IL_019b; } } } } num = 0; goto IL_019b; IL_01d0: int num2; bool flag = (byte)num2 != 0; if (num == 0 && !flag) { Plugin.LogVerbose("[SaveHandleForwardOverrideAudioEventsInFirearmTag] Could not find '" + text + "' and '" + text2 + "'"); continue; } Plugin.LogVerbose("[SaveHandleForwardOverrideAudioEventsInFirearmTag] Saving '" + text + "' and '" + text2 + "' in firearm tag"); AudioEvent val = firearmAudioSet.HandleForward.DeepCopy(); if (val != null) { AddonAsset asset = ((!flag) ? assetNode.Asset : assetNode2.Asset); AddonAsset addonAsset = asset; val.ReplaceClipsInAudioEvent(addonAsset); firearmTag.SaveHandleForwardCircumstanceAudioEvent(val, value); } goto end_IL_0166; IL_019b: if (assetNode2 != null) { AddonAsset asset = assetNode2.Asset; if (asset != null) { AddonManifest.AssetMapping assetMapping = asset.AssetMapping; if (assetMapping != null) { ReplacementAsset[] replacements = assetMapping.Replacements; if (replacements != null) { num2 = ((replacements.Length > 0) ? 1 : 0); goto IL_01d0; } } } } num2 = 0; goto IL_01d0; end_IL_0166:; } catch (Exception ex) { Plugin.Log.LogError((object)("[SaveHandleForwardOverrideAudioEventsInFirearmTag] Failed to replace audio for '" + text + "' and '" + text2 + $"\n- Cause: {ex.GetType()}" + "\n- Message: " + ex.Message)); } } } private static void ReplaceAudioEventByGunEventFromAddonAsset(this FVRFirearmAudioSet fvrFirearmAudioSet, AddonAsset addonAsset, GunEvent.Value gunEvent) { fvrFirearmAudioSet.GetAudioEventBy(gunEvent).ReplaceClipsInAudioEvent(addonAsset); } private static void ReplaceClipsInAudioEvent(this AudioEvent audioEvent, AddonAsset addonAsset) { //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0088: 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) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) AddonAsset addonAsset2 = addonAsset; List<AudioClip> clips = addonAsset2.AssetMapping.Replacements.Select((ReplacementAsset replacement) => addonAsset2.SourceAddon.LoadAudioClip(replacement, ReplacementAudioClipName)).ToList(); audioEvent.Clips = clips; float[] array = (addonAsset2.AssetMapping as AddonManifest.AssetMapping.Audio)?.VolumeRange; float[] array2 = (addonAsset2.AssetMapping as AddonManifest.AssetMapping.Audio)?.PitchRange; if (array != null) { Vector2 volumeRange = default(Vector2); ((Vector2)(ref volumeRange))..ctor(array[0], array[1]); audioEvent.VolumeRange = volumeRange; } if (array2 != null) { Vector2 pitchRange = default(Vector2); ((Vector2)(ref pitchRange))..ctor(array2[0], array2[1]); audioEvent.PitchRange = pitchRange; } } } } namespace Sirdoggy.SideloaderMint.Patchers.Tags { internal class FirearmTag : MonoBehaviour { private const double CircumstanceValidityTimeSpanMillis = 250.0; private AudioEvent? _boltSlideForwardAfterManualChargeAudioEvent; private AudioEvent? _boltSlideForwardAfterHandleSlapAudioEvent; private AudioEvent? _boltSlideForwardAfterShotAudioEvent; private AudioEvent? _handleForwardAfterHandleSlapAudioEvent; private float? _lastManualChargeTime; private float? _lastHandleSlapTimeSlideForward; private float? _lastHandleSlapTimeHandleForward; private float? _lastShotTime; public void SaveBoltSlideForwardCircumstanceAudioEvent(AudioEvent audioEvent, BoltSlideForwardCircumstance.Value circumstance) { switch (circumstance) { case BoltSlideForwardCircumstance.Value.Shot: _boltSlideForwardAfterShotAudioEvent = audioEvent; break; case BoltSlideForwardCircumstance.Value.HandleSlap: _boltSlideForwardAfterHandleSlapAudioEvent = audioEvent; break; case BoltSlideForwardCircumstance.Value.ManualCharge: _boltSlideForwardAfterManualChargeAudioEvent = audioEvent; break; } } public void SaveHandleForwardCircumstanceAudioEvent(AudioEvent audioEvent, HandleForwardCircumstance.Value circumstance) { if (circumstance == HandleForwardCircumstance.Value.HandleSlap) { _handleForwardAfterHandleSlapAudioEvent = audioEvent; } } public void OnManualCharge() { _lastManualChargeTime = Time.time; _lastHandleSlapTimeHandleForward = null; } public void OnHandleSlap() { _lastHandleSlapTimeSlideForward = Time.time; _lastHandleSlapTimeHandleForward = Time.time; } public void OnShot() { _lastShotTime = Time.time; } public AudioEvent? GetLatestSlideForwardCircumstanceAudioEvent() { List<KeyValuePair<AudioEvent, float?>> list = (from pair in new List<KeyValuePair<AudioEvent, float?>> { new KeyValuePair<AudioEvent, float?>(_boltSlideForwardAfterManualChargeAudioEvent, _lastManualChargeTime), new KeyValuePair<AudioEvent, float?>(_boltSlideForwardAfterHandleSlapAudioEvent, _lastHandleSlapTimeSlideForward), new KeyValuePair<AudioEvent, float?>(_boltSlideForwardAfterShotAudioEvent, _lastShotTime) } where IsValidLastTime(pair.Value) orderby pair.Value descending select pair).ToList(); _lastManualChargeTime = null; _lastHandleSlapTimeSlideForward = null; _lastShotTime = null; if (list.Count == 0) { return null; } return list.First().Key; } public AudioEvent? GetLatestHandleForwardCircumstanceAudioEvent() { List<KeyValuePair<AudioEvent, float?>> list = (from pair in new List<KeyValuePair<AudioEvent, float?>> { new KeyValuePair<AudioEvent, float?>(_handleForwardAfterHandleSlapAudioEvent, _lastHandleSlapTimeHandleForward) } where IsValidLastTime(pair.Value) orderby pair.Value descending select pair).ToList(); _lastHandleSlapTimeHandleForward = null; if (list.Count == 0) { return null; } return list.First().Key; } private static bool IsValidLastTime(float? time) { if (!time.HasValue) { return false; } return (double?)((Time.time - time) * 1000f) < 250.0; } public override string ToString() { return StringUtils.FormatTypeProperties("FirearmTag", new <>z__ReadOnlyArray<string>(new string[4] { string.Format("{0}: {1}", "_lastManualChargeTime", _lastManualChargeTime), string.Format("{0}: {1}", "_lastHandleSlapTimeSlideForward", _lastHandleSlapTimeSlideForward), string.Format("{0}: {1}", "_lastShotTime", _lastShotTime), string.Format("{0}: {1}", "_lastHandleSlapTimeHandleForward", _lastHandleSlapTimeHandleForward) })); } } } namespace Sirdoggy.SideloaderMint.Patchers.Extensions { internal static class AudioEventExtensions { public static AudioEvent? DeepCopy(this AudioEvent? original) { //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_0035: 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_003c: 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_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: 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_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Expected O, but got Unknown if (original == null) { return null; } AudioEvent val = (AudioEvent)FormatterServices.GetUninitializedObject(typeof(AudioEvent)); val.Clips = ((original.Clips != null) ? original.Clips.ToList() : null); val.VolumeRange = original.VolumeRange; val.PitchRange = original.PitchRange; val.ClipLengthRange = original.ClipLengthRange; return val; } } internal static class BoltStateExtensions { public static bool IsBoltHeldOpen(this ClosedBoltWeapon firearm) { if (!firearm.Bolt.IsBoltLocked() && !firearm.IsBoltCatchButtonHeld) { if (firearm.Bolt.HasLastRoundBoltHoldOpen && Object.op_Implicit((Object)(object)((FVRFireArm)firearm).Magazine) && !((FVRFireArm)firearm).Magazine.HasARound()) { return !firearm.Chamber.IsFull; } return false; } return true; } public static bool WillBoltBeHeldOpenAfterNextShot(this ClosedBoltWeapon firearm) { if (!firearm.IsBoltCatchButtonHeld) { if (firearm.Bolt.HasLastRoundBoltHoldOpen && Object.op_Implicit((Object)(object)((FVRFireArm)firearm).Magazine)) { return !((FVRFireArm)firearm).Magazine.HasARound(); } return false; } return true; } public static bool IsSlideHeldOpen(this Handgun handgun) { if ((handgun == null || !handgun.DoesSafetyLockSlide || !handgun.IsSafetyEngaged) && !handgun.IsSLideLockMechanismEngaged) { return handgun.IsSlideCatchEngaged(); } return true; } public static bool WillSlideBeHeldOpenAfterNextShot(this Handgun handgun) { if (handgun.HasSlideRelease && Object.op_Implicit((Object)(object)((FVRFireArm)handgun).Magazine)) { return !((FVRFireArm)handgun).Magazine.HasARound(); } return false; } public static bool IsTubeFedShotgunBoltHeldOpen(this TubeFedShotgun shotgun) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 if ((int)shotgun.Mode == 1) { if (!shotgun.Bolt.m_isBoltLocked) { if (shotgun.Bolt.HasLastRoundBoltHoldOpen && Object.op_Implicit((Object)(object)((FVRFireArm)shotgun).Magazine) && !((FVRFireArm)shotgun).Magazine.HasARound()) { return !shotgun.Chamber.IsFull; } return false; } return true; } return false; } public static bool WillTubeFedShotgunBoltBeHeldOpenAfterNextShot(this TubeFedShotgun shotgun) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 if ((int)shotgun.Mode == 1 && shotgun.Bolt.HasLastRoundBoltHoldOpen && Object.op_Implicit((Object)(object)((FVRFireArm)shotgun).Magazine)) { return !((FVRFireArm)shotgun).Magazine.HasARound(); } return false; } } internal static class FVRFirearmAudioSetExtensions { public static FVRFirearmAudioSet DeepCopy(this FVRFirearmAudioSet original) { //IL_0013: 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) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: 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_009f: Unknown result type (might be due to invalid IL or missing references) FVRFirearmAudioSet obj = ScriptableObject.CreateInstance<FVRFirearmAudioSet>(); ((Object)obj).name = ((Object)original).name; ((Object)obj).hideFlags = ((Object)original).hideFlags; obj.Loudness_Primary = original.Loudness_Primary; obj.Loudness_Suppressed = original.Loudness_Suppressed; obj.Loudness_OperationMult = original.Loudness_OperationMult; obj.FTP = original.FTP; obj.Shots_Main = original.Shots_Main.DeepCopy(); obj.Shots_Suppressed = original.Shots_Suppressed.DeepCopy(); obj.Shots_LowPressure = original.Shots_LowPressure.DeepCopy(); obj.TailPitchMod_Main = original.TailPitchMod_Main; obj.TailPitchMod_Suppressed = original.TailPitchMod_Suppressed; obj.TailPitchMod_LowPressure = original.TailPitchMod_LowPressure; obj.TailConcurrentLimit = original.TailConcurrentLimit; obj.UsesTail_Main = original.UsesTail_Main; obj.UsesTail_Suppressed = original.UsesTail_Suppressed; obj.UsesLowPressureSet = original.UsesLowPressureSet; obj.BoltRelease = original.BoltRelease.DeepCopy(); obj.BoltSlideBack = original.BoltSlideBack.DeepCopy(); obj.BoltSlideBackHeld = original.BoltSlideBackHeld.DeepCopy(); obj.BoltSlideBackLocked = original.BoltSlideBackLocked.DeepCopy(); obj.BoltSlideForward = original.BoltSlideForward.DeepCopy(); obj.BoltSlideForwardHeld = original.BoltSlideForwardHeld.DeepCopy(); obj.BreachOpen = original.BreachOpen.DeepCopy(); obj.BreachClose = original.BreachClose.DeepCopy(); obj.BeltBulletSet = original.BeltBulletSet.DeepCopy(); obj.CatchOnSear = original.CatchOnSear.DeepCopy(); obj.ChamberManual = original.ChamberManual.DeepCopy(); obj.FireSelector = original.FireSelector.DeepCopy(); obj.HammerHit = original.HammerHit.DeepCopy(); obj.HandleBack = original.HandleBack.DeepCopy(); obj.HandleBackEmpty = original.HandleBackEmpty.DeepCopy(); obj.HandleForward = original.HandleForward.DeepCopy(); obj.HandleForwardEmpty = original.HandleForwardEmpty.DeepCopy(); obj.HandleUp = original.HandleUp.DeepCopy(); obj.HandleDown = original.HandleDown.DeepCopy(); obj.HandleGrab = original.HandleGrab.DeepCopy(); obj.MagazineIn = original.MagazineIn.DeepCopy(); obj.MagazineOut = original.MagazineOut.DeepCopy(); obj.MagazineInsertRound = original.MagazineInsertRound.DeepCopy(); obj.MagazineEjectRound = original.MagazineEjectRound.DeepCopy(); obj.Prefire = original.Prefire.DeepCopy(); obj.Safety = original.Safety.DeepCopy(); obj.TriggerReset = original.TriggerReset.DeepCopy(); obj.TopCoverRelease = original.TopCoverRelease.DeepCopy(); obj.TopCoverUp = original.TopCoverUp.DeepCopy(); obj.TopCoverDown = original.TopCoverDown.DeepCopy(); obj.StockOpen = original.StockOpen.DeepCopy(); obj.StockClosed = original.StockClosed.DeepCopy(); obj.BipodOpen = original.BipodOpen.DeepCopy(); obj.BipodClosed = original.BipodClosed.DeepCopy(); obj.BeltGrab = original.BeltGrab.DeepCopy(); obj.BeltRelease = original.BeltRelease.DeepCopy(); obj.BeltSeat = original.BeltSeat.DeepCopy(); obj.BeltSettle = original.BeltSettle.DeepCopy(); obj.BeltSettlingLimit = original.BeltSettlingLimit; return obj; } public static AudioEvent GetAudioEventBy(this FVRFirearmAudioSet clipSet, GunEvent.Value gunEvent) { return (AudioEvent)(gunEvent switch { GunEvent.Value.Shot => clipSet.Shots_Main, GunEvent.Value.ShotSuppressed => clipSet.Shots_Suppressed, GunEvent.Value.ShotLowPressure => clipSet.Shots_LowPressure, GunEvent.Value.BoltRelease => clipSet.BoltRelease, GunEvent.Value.BoltSlideBack => clipSet.BoltSlideBack, GunEvent.Value.BoltSlideBackHeld => clipSet.BoltSlideBackHeld, GunEvent.Value.BoltSlideBackLocked => clipSet.BoltSlideBackLocked, GunEvent.Value.BoltSlideForward => clipSet.BoltSlideForward, GunEvent.Value.BoltSlideForwardHeld => clipSet.BoltSlideForwardHeld, GunEvent.Value.BreachOpen => clipSet.BreachOpen, GunEvent.Value.BreachClose => clipSet.BreachClose, GunEvent.Value.BeltBulletSet => clipSet.BeltBulletSet, GunEvent.Value.CatchOnSear => clipSet.CatchOnSear, GunEvent.Value.ChamberManual => clipSet.ChamberManual, GunEvent.Value.FireSelector => clipSet.FireSelector, GunEvent.Value.HammerHit => clipSet.HammerHit, GunEvent.Value.HandleBack => clipSet.HandleBack, GunEvent.Value.HandleBackEmpty => clipSet.HandleBackEmpty, GunEvent.Value.HandleForward => clipSet.HandleForward, GunEvent.Value.HandleForwardEmpty => clipSet.HandleForwardEmpty, GunEvent.Value.HandleUp => clipSet.HandleUp, GunEvent.Value.HandleDown => clipSet.HandleDown, GunEvent.Value.HandleGrab => clipSet.HandleGrab, GunEvent.Value.MagazineOut => clipSet.MagazineOut, GunEvent.Value.MagazineIn => clipSet.MagazineIn, GunEvent.Value.MagazineInsertRound => clipSet.MagazineInsertRound, GunEvent.Value.MagazineEjectRound => clipSet.MagazineEjectRound, GunEvent.Value.Prefire => clipSet.Prefire, GunEvent.Value.Safety => clipSet.Safety, GunEvent.Value.TriggerReset => clipSet.TriggerReset, GunEvent.Value.TopCoverRelease => clipSet.TopCoverRelease, GunEvent.Value.TopCoverUp => clipSet.TopCoverUp, GunEvent.Value.TopCoverDown => clipSet.TopCoverDown, GunEvent.Value.StockOpen => clipSet.StockOpen, GunEvent.Value.StockClosed => clipSet.StockClosed, GunEvent.Value.BipodOpen => clipSet.BipodOpen, GunEvent.Value.BipodClosed => clipSet.BipodClosed, GunEvent.Value.BeltGrab => clipSet.BeltGrab, GunEvent.Value.BeltRelease => clipSet.BeltRelease, GunEvent.Value.BeltSeat => clipSet.BeltSeat, GunEvent.Value.BeltSettle => clipSet.BeltSettle, _ => throw new NotSupportedException("Unsupported GunEvent"), }); } public static void ReplaceAudioEventBy(this FVRFirearmAudioSet clipSet, GunEvent.Value gunEvent, AudioEvent audioEvent) { switch (gunEvent) { case GunEvent.Value.BoltSlideForward: clipSet.BoltSlideForward = audioEvent; break; case GunEvent.Value.HandleForward: clipSet.HandleForward = audioEvent; break; default: throw new NotSupportedException("Unsupported GunEvent"); } } } internal static class FVRFirearmMagazineAudioSetExtensions { public static FVRFirearmMagazineAudioSet DeepCopy(this FVRFirearmMagazineAudioSet original) { FVRFirearmMagazineAudioSet obj = ScriptableObject.CreateInstance<FVRFirearmMagazineAudioSet>(); obj.MagazineIn = original.MagazineIn.DeepCopy(); obj.MagazineOut = original.MagazineOut.DeepCopy(); obj.MagazineInsertRound = original.MagazineInsertRound.DeepCopy(); obj.MagazineEjectRound = original.MagazineEjectRound.DeepCopy(); return obj; } } } namespace Sirdoggy.SideloaderMint.Model { internal static class BoltSlideForwardCircumstance { public enum Value { ManualCharge, HandleSlap, Shot } public static Value[] All => Enum.GetValues(typeof(Value)).Cast<Value>().ToArray(); public static string[] GetTargetPath(this Value value) { return value switch { Value.Shot => new string[1] { "AfterShot" }, Value.HandleSlap => new string[2] { "AfterManualCharge", "AfterHKSlap" }, Value.ManualCharge => new string[1] { "AfterManualCharge" }, _ => throw new NotSupportedException("Unknown BoltSlideForwardCircumstance value"), }; } } internal static class GunEvent { public enum Value { Shot, ShotSuppressed, ShotLowPressure, BoltRelease, BoltSlideBack, BoltSlideBackHeld, BoltSlideBackLocked, BoltSlideForward, BoltSlideForwardHeld, BreachOpen, BreachClose, BeltBulletSet, CatchOnSear, ChamberManual, FireSelector, HammerHit, HandleBack, HandleBackEmpty, HandleForward, HandleForwardEmpty, HandleUp, HandleDown, HandleGrab, MagazineIn, MagazineOut, MagazineInsertRound, MagazineEjectRound, Prefire, Safety, TriggerReset, TopCoverRelease, TopCoverUp, TopCoverDown, StockOpen, StockClosed, BipodOpen, BipodClosed, BeltGrab, BeltRelease, BeltSeat, BeltSettle } public static Value[] All => Enum.GetValues(typeof(Value)).Cast<Value>().ToArray(); } internal static class HandleForwardCircumstance { public enum Value { HandleSlap } public static Value[] All => Enum.GetValues(typeof(Value)).Cast<Value>().ToArray(); public static string[] GetTargetPath(this Value value) { if (value == Value.HandleSlap) { return new string[1] { "AfterHKSlap" }; } throw new NotSupportedException("Unknown BoltSlideForwardCircumstance value"); } } } namespace Sirdoggy.SideloaderMint.Common { internal static class StreamExtensions { public static void CopyTo(this Stream from, Stream to) { byte[] array = new byte[4096]; int count; while ((count = from.Read(array, 0, array.Length)) > 0) { to.Write(array, 0, count); } } } internal static class StringUtils { public static string FormatTypeProperties<T>(string nameOfType, [ParamCollection] IEnumerable<T> properties) { if (properties is string) { return $"{nameOfType}({properties})"; } return nameOfType + "(" + JoinValues(properties) + ")"; } public static string JoinValues<T>(IEnumerable<T> values, string seperator = ", ") { string[] value = values.Select((T element) => element?.ToString()).ToArray(); return string.Join(seperator, value); } } public class WavUtility { private class BitReader { public int SizeOf { get; set; } public Func<BinaryReader, float> Reader { get; set; } public int MaxValue { get; set; } } private static readonly Dictionary<int, BitReader> readers = new Dictionary<int, BitReader> { [8] = new BitReader { MaxValue = 127, SizeOf = 1, Reader = (BinaryReader br) => br.ReadSByte() }, [16] = new BitReader { MaxValue = 32767, SizeOf = 2, Reader = (BinaryReader br) => br.ReadInt16() }, [24] = new BitReader { MaxValue = int.MaxValue, SizeOf = 3, Reader = delegate(BinaryReader br) { byte[] array = new byte[4]; br.Read(array, 1, 3); return BitConverter.ToInt32(array, 0); } }, [32] = new BitReader { MaxValue = int.MaxValue, SizeOf = 4, Reader = (BinaryReader br) => br.ReadInt32() } }; public static AudioClip ToAudioClip(BinaryReader br, string name = "wav") { br.BaseStream.Position = 16L; int num = br.ReadInt32(); ushort num2 = br.ReadUInt16(); string arg = FormatCode(num2); if (num2 != 1 && num2 != 65534) { throw new Exception($"Detected format code '{arg}' ({num2}), but only PCM and WaveFormatExtensable uncompressed formats are currently supported."); } ushort num3 = br.ReadUInt16(); int num4 = br.ReadInt32(); br.ReadInt32(); br.ReadUInt16(); ushort key = br.ReadUInt16(); br.BaseStream.Position = 20 + num + 4; int dataSize = br.ReadInt32(); if (readers.TryGetValue(key, out BitReader value)) { float[] array = ReadAudioClipSamples(br, value, dataSize); AudioClip obj = AudioClip.Create(name, array.Length, (int)num3, num4, false); obj.SetData(array, 0); return obj; } throw new Exception(key + " bit depth is not supported."); } private static float[] ReadAudioClipSamples(BinaryReader br, BitReader reader, int dataSize) { float[] array = new float[dataSize / reader.SizeOf]; for (int i = 0; i < array.Length; i++) { array[i] = reader.Reader(br) / (float)reader.MaxValue; } return array; } private static string FormatCode(ushort code) { switch (code) { case 1: return "PCM"; case 2: return "ADPCM"; case 3: return "IEEE"; case 7: return "μ-law"; case 65534: return "WaveFormatExtensable"; default: Debug.LogWarning((object)("Unknown wav code format:" + code)); return ""; } } } } namespace Sirdoggy.SideloaderMint.Common.Validation { internal static class PathValidator { public static void Validate(string basePath, string relativePath) { string fullPath = Path.GetFullPath(basePath); if (!Path.GetFullPath(Path.Combine(fullPath, relativePath)).StartsWith(fullPath, StringComparison.OrdinalIgnoreCase)) { throw new UnauthorizedAccessException("Path '" + relativePath + "' is not allowed to access resource outside of its base path '" + basePath + "'"); } } } internal static class StrictDeserializer { private static readonly JsonSerializer Serializer = JsonSerializer.Create(new JsonSerializerSettings { MissingMemberHandling = (MissingMemberHandling)1 }); public static T Deserialize<T>(JObject jObject) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) JsonReader val = ((JToken)jObject).CreateReader(); T val2 = Serializer.Deserialize<T>(val); if (val2 == null) { throw new JsonSerializationException("Deserialization failed"); } return val2; } } } namespace Sirdoggy.SideloaderMint.Common.Structures { public class AssetTree<TAsset> { public abstract class FindBehaviour { public sealed class GetMostSpecificThatMatchesQueryOrCollectAllWhenReachedLastQuerySegment : FindBehaviour { } public sealed class GetMostSpecificThatMatchesQuery : FindBehaviour { } public sealed class GetMostSpecificThatMatchesQueryAndIsAtLeastTheSameLevel : FindBehaviour { } public sealed class GetMostSpecificThatMatchesQueryAndSpecifications : FindBehaviour { public int MinLevel { get; } public int MaxLevel { get; } public GetMostSpecificThatMatchesQueryAndSpecifications(int minLevel, int maxLevel) { MinLevel = minLevel; MaxLevel = maxLevel; base..ctor(); } } } public interface INode { int Level { get; } } public abstract class TraversableNode : INode { public abstract int Level { get; } public List<INode> Children { get; } = new List<INode>(); protected abstract string ReadableName { get; } protected abstract string[] ReadableProperties { get; } public override string ToString() { string value = StringUtils.FormatTypeProperties(ReadableName, ReadableProperties); StringBuilder stringBuilder = new StringBuilder().Append(' ', Level * 2 + 2).Append((Level == 0) ? " " : "└─").Append(value); foreach (INode child in Children) { stringBuilder.Append($"\n{child}"); } return stringBuilder.ToString(); } } public sealed class RootNode : TraversableNode { public override int Level => 0; protected override string ReadableName => "RootNode"; protected override string[] ReadableProperties => new string[1] { string.Format("{0}: {1}", "Level", Level) }; } public sealed class IntermediateNode : TraversableNode { public override int Level { get; } public string? TargetSegment { get; } protected override string ReadableName => "IntermediateNode"; protected override string[] ReadableProperties => new string[2] { string.Format("{0}: {1}", "Level", Level), "TargetSegment: " + (TargetSegment ?? "*") }; public IntermediateNode(int level, string? targetSegment) { Level = level; TargetSegment = targetSegment; base..ctor(); } } public sealed class AssetNode : INode { public int Level { get; } public TAsset Asset { get; } public string? FinalTargetSegment { get; } public AssetNode(int level, TAsset asset, string? finalTargetSegment) { Level = level; Asset = asset; FinalTargetSegment = finalTargetSegment; base..ctor(); } public override string ToString() { string value = StringUtils.FormatTypeProperties("AssetNode", new <>z__ReadOnlyArray<string>(new string[3] { string.Format("{0}: {1}", "Level", Level), string.Format("{0}: {1}", "Asset", Asset), "FinalTargetSegment: " + (FinalTargetSegment ?? "null") })); return new StringBuilder().Append(' ', Level * 2 + 2).Append("└─").Append(value) .ToString(); } } [CompilerGenerated] private int <maxIntermediateLevels>P; private readonly RootNode _rootNode; public AssetTree(int maxIntermediateLevels) { <maxIntermediateLevels>P = maxIntermediateLevels; _rootNode = new RootNode(); base..ctor(); } public void AddAssetToTree(string target, TAsset asset, bool parseAsterisksAsWildcards = true) { string[] array = (from part in target.Split(new char[1] { ':' }) select part.Trim() into part select (part.Length != 0) ? part : null).Reverse().SkipWhile((string part) => part == null).Reverse() .ToArray(); if (parseAsterisksAsWildcards) { array = array.Select((string part) => (!(part == "*")) ? part : null).ToArray(); } if (array.Length > <maxIntermediateLevels>P) { throw new ArgumentException("Can't add asset to AssetTree because target '" + target + "' consists " + $"of more than {<maxIntermediateLevels>P} segments."); } TraversableNode traversableNode = _rootNode; string[] array2 = array; foreach (string segment in array2) { IntermediateNode intermediateNode = traversableNode.Children.Where((INode node) => node is IntermediateNode).Cast<IntermediateNode>().FirstOrDefault((IntermediateNode node) => node.TargetSegment == segment); if (intermediateNode == null) { intermediateNode = new IntermediateNode(traversableNode.Level + 1, segment); traversableNode.Children.Add(intermediateNode); } traversableNode = intermediateNode; } AssetNode item = new AssetNode(traversableNode.Level + 1, asset, (array.Length == <maxIntermediateLevels>P && <maxIntermediateLevels>P > 0) ? array[<maxIntermediateLevels>P - 1] : null); traversableNode.Children.Add(item); } public AssetNode[] Find(FindBehaviour behaviour, params string[] segmentedQuery) { string[] segmentedQuery2 = segmentedQuery; FindBehaviour behaviour2 = behaviour; AssetNode[] array = SearchIntermediateNodesInNode(_rootNode); if (array.Length != 0) { return array; } if (behaviour2 is FindBehaviour.GetMostSpecificThatMatchesQueryAndIsAtLeastTheSameLevel && segmentedQuery2.Length != 0) { return new AssetNode[0]; } return _rootNode.Children.Where((INode node) => node is AssetNode).Cast<AssetNode>().ToArray(); static void CollectAllChildNodesFromCurrent(TraversableNode currentNode, List<AssetNode> collectedAssetNodes) { foreach (INode child in currentNode.Children) { if (!(child is AssetNode item)) { if (child is TraversableNode currentNode2) { CollectAllChildNodesFromCurrent(currentNode2, collectedAssetNodes); } } else { collectedAssetNodes.Add(item); } } } AssetNode[] FindInIntermediateLevel(IntermediateNode nodeToSearch) { string text = ((nodeToSearch.Level <= segmentedQuery2.Length) ? segmentedQuery2[nodeToSearch.Level - 1] : null); if (nodeToSearch.TargetSegment != null && nodeToSearch.TargetSegment != text) { return new AssetNode[0]; } if (behaviour2 is FindBehaviour.GetMostSpecificThatMatchesQueryOrCollectAllWhenReachedLastQuerySegment && nodeToSearch.Level >= segmentedQuery2.Length) { List<AssetNode> list = new List<AssetNode>(); CollectAllChildNodesFromCurrent(nodeToSearch, list); return list.ToArray(); } AssetNode[] array2 = SearchIntermediateNodesInNode(nodeToSearch); if (array2.Length != 0) { return array2; } if (behaviour2 is FindBehaviour.GetMostSpecificThatMatchesQueryAndSpecifications getMostSpecificThatMatchesQueryAndSpecifications && (nodeToSearch.Level < getMostSpecificThatMatchesQueryAndSpecifications.MinLevel || nodeToSearch.Level > getMostSpecificThatMatchesQueryAndSpecifications.MaxLevel)) { return new AssetNode[0]; } if (behaviour2 is FindBehaviour.GetMostSpecificThatMatchesQueryAndIsAtLeastTheSameLevel && nodeToSearch.Level <= segmentedQuery2.Length - 1) { return new AssetNode[0]; } return nodeToSearch.Children.Where((INode node) => node is AssetNode).Cast<AssetNode>().ToArray(); } AssetNode[] SearchIntermediateNodesInNode(TraversableNode nodeToSearch) { foreach (IntermediateNode item2 in from IntermediateNode node in nodeToSearch.Children.Where((INode node) => node is IntermediateNode) where node.TargetSegment != null select node) { AssetNode[] array3 = FindInIntermediateLevel(item2); if (array3.Length != 0) { return array3; } } foreach (IntermediateNode item3 in from IntermediateNode node in nodeToSearch.Children.Where((INode node) => node is IntermediateNode) where node.TargetSegment == null select node) { AssetNode[] array4 = FindInIntermediateLevel(item3); if (array4.Length != 0) { return array4; } } return new AssetNode[0]; } } public override string ToString() { return string.Format("AssetTree({0}: {1})", "maxIntermediateLevels", <maxIntermediateLevels>P) + $"\n{_rootNode}"; } } } namespace Sirdoggy.SideloaderMint.Common.Coroutines { internal class CoroutineRunner : MonoBehaviour { private static CoroutineRunner _instance; public static Coroutine Run(IEnumerator coroutine) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown if (!Object.op_Implicit((Object)(object)_instance)) { GameObject val = new GameObject("CoroutineRunner") { hideFlags = (HideFlags)61 }; Object.DontDestroyOnLoad((Object)val); _instance = val.AddComponent<CoroutineRunner>(); } return ((MonoBehaviour)_instance).StartCoroutine(coroutine); } } } namespace Sirdoggy.SideloaderMint.AssetLoaders { internal class AddonAsset { public AddonManifest.AssetMapping AssetMapping { get; } public Addon SourceAddon { get; } public AddonAsset(AddonManifest.AssetMapping assetMapping, Addon addon) { AssetMapping = assetMapping; SourceAddon = addon; base..ctor(); } } internal interface IAssetLoader { void Initialize(IEnumerable<Addon> addons); } } namespace Sirdoggy.SideloaderMint.AssetLoaders.Implementations { internal class AudioClipLoader : IAssetLoader { internal class AddonEntry { public Addon Addon { get; } public ReplacementAsset[] Replacements { get; } public float[]? VolumeRange { get; } public float[]? PitchRange { get; } public AddonEntry(Addon addon, ReplacementAsset[] replacements, float[]? volumeRange, float[]? pitchRange) { Addon = addon; Replacements = replacements; VolumeRange = volumeRange; PitchRange = pitchRange; base..ctor(); } } internal readonly Dictionary<string, AddonEntry> LoadedAudioClips = new Dictionary<string, AddonEntry>(StringComparer.OrdinalIgnoreCase); public void Initialize(IEnumerable<Addon> addons) { foreach (Addon addon in addons) { foreach (AddonManifest.AssetMapping item in addon.Manifest.AssetMappings.Where((AddonManifest.AssetMapping assetMapping) => assetMapping.AssetType == AddonManifest.AssetType.AudioClip)) { float[] volumeRange = (item as AddonManifest.AssetMapping.Audio)?.VolumeRange; float[] pitchRange = (item as AddonManifest.AssetMapping.Audio)?.PitchRange; AddonEntry value = new AddonEntry(addon, item.Replacements, volumeRange, pitchRange); string[] targets = item.Targets; foreach (string text in targets) { if (LoadedAudioClips.TryGetValue(text, out AddonEntry value2)) { Plugin.Log.LogWarning((object)("[" + addon.GUID + "] AudioClip " + text + " is already replaced by '" + value2.Addon.GUID + "', skipping.")); } else { LoadedAudioClips[text] = value; } } } } } } internal abstract class BaseAssetTreeLoader : IAssetLoader { protected abstract AddonManifest.AssetType SupportedAssetType { get; } protected abstract int TargetSegmentsCount { get; } internal AssetTree<AddonAsset>? AssetTree { get; private set; } public virtual void Initialize(IEnumerable<Addon> addons) { AssetTree = new AssetTree<AddonAsset>(TargetSegmentsCount); foreach (Addon addon in addons) { AddAssetsFromAddon(addon); } if (Plugin.PluginConfig.VerboseDebugLogs.Value) { Plugin.Log.LogInfo((object)AssetTree); } } private void AddAssetsFromAddon(Addon addon) { Addon addon2 = addon; foreach (AddonManifest.AssetMapping item in addon2.Manifest.AssetMappings.Where((AddonManifest.AssetMapping mapping) => mapping.AssetType == SupportedAssetType)) { try { if (item.Replacements.Length == 0) { throw new ArgumentException("Asset mapping in '" + addon2.GUID + "' for target " + $"'{item.Targets}' has no asset paths."); } string assetPath; using IEnumerator<ReplacementAsset> enumerator2 = (from replacement in item.Replacements where replacement is ReplacementAsset.LoadFromAddon where !addon2.FileExists(addon2.GetAssetPath(replacement, out assetPath)) select replacement).GetEnumerator(); if (enumerator2.MoveNext()) { ReplacementAsset current2 = enumerator2.Current; throw new FileNotFoundException($"[{addon2.GUID}] Asset '{current2}' of type '{SupportedAssetType}' could " + "not be resolved."); } } catch (Exception ex) { Plugin.Log.LogError((object)("Failed to add assets from addon: " + addon2.GUID + $"\n- Cause: {ex.GetType()}" + "\n- Message: " + ex.Message)); continue; } AddonAsset asset = new AddonAsset(item, addon2); string[] targets = item.Targets; foreach (string target in targets) { AssetTree?.AddAssetToTree(target, asset, addon2.Manifest.UsesAsterisksAsWildcardsInTargetField()); } } } } internal class GunAudioSetLoader : BaseAssetTreeLoader { protected override AddonManifest.AssetType SupportedAssetType => AddonManifest.AssetType.GunAudioSet; protected override int TargetSegmentsCount => 4; } internal class GunSoundLoader : BaseAssetTreeLoader { protected override AddonManifest.AssetType SupportedAssetType => AddonManifest.AssetType.GunSound; protected override int TargetSegmentsCount => 4; } internal class MaterialLoader : BaseAssetTreeLoader { private static readonly string[] MaterialPathSchema = new string[2] { "prefabPath", "materialName" }; protected override AddonManifest.AssetType SupportedAssetType => AddonManifest.AssetType.Material; protected override int TargetSegmentsCount { get; } = MaterialPathSchema.Length; public override void Initialize(IEnumerable<Addon> addons) { base.Initialize(addons); ResourceRedirection.RegisterAssetLoadedHook((HookBehaviour)2, 100, (Action<AssetLoadedContext>)PatchLoadedAsset); } private void PatchLoadedAsset(AssetLoadedContext ctx) { Object[] assets = ctx.Assets; foreach (Object val in assets) { string uniqueFileSystemAssetPath = ctx.GetUniqueFileSystemAssetPath(val); GameObject val2 = (GameObject)(object)((val is GameObject) ? val : null); if (val2 != null) { ReplaceMaterials(val2, uniqueFileSystemAssetPath); } } } private void ReplaceMaterials(GameObject go, string path) { MeshRenderer[] componentsInChildren = go.GetComponentsInChildren<MeshRenderer>(); foreach (MeshRenderer val in componentsInChildren) { Material[] materials = ((Renderer)val).materials; if (materials == null) { continue; } for (int j = 0; j < materials.Length; j++) { string text = ((Object)materials[j]).name.Replace(" (Instance)", ""); Plugin.LogVerbose("Material: " + string.Join(":", path, text)); AssetTree<AddonAsset>.AssetNode assetNode = base.AssetTree?.Find(new AssetTree<AddonAsset>.FindBehaviour.GetMostSpecificThatMatchesQueryOrCollectAllWhenReachedLastQuerySegment(), path, text).FirstOrDefault(); if (assetNode != null) { materials[j] = assetNode.Asset.SourceAddon.LoadMaterial(assetNode.Asset.AssetMapping.Replacements.First()); } } ((Renderer)val).materials = materials; } } } internal class MeshLoader : BaseAssetTreeLoader { private static readonly string[] MeshPathSchema = new string[3] { "prefabPath", "meshContainerName", "meshName" }; protected override AddonManifest.AssetType SupportedAssetType => AddonManifest.AssetType.Mesh; protected override int TargetSegmentsCount { get; } = MeshPathSchema.Length; public override void Initialize(IEnumerable<Addon> addons) { base.Initialize(addons); ResourceRedirection.RegisterAssetLoadedHook((HookBehaviour)2, (Action<AssetLoadedContext>)PatchLoadedAsset); } private void PatchLoadedAsset(AssetLoadedContext ctx) { Object[] assets = ctx.Assets; foreach (Object val in assets) { string uniqueFileSystemAssetPath = ctx.GetUniqueFileSystemAssetPath(val); GameObject val2 = (GameObject)(object)((val is GameObject) ? val : null); if (val2 != null) { ReplaceMeshes(val2, uniqueFileSystemAssetPath); } } } private void ReplaceMeshes(GameObject go, string path) { MeshFilter[] componentsInChildren = go.GetComponentsInChildren<MeshFilter>(); foreach (MeshFilter val in componentsInChildren) { string name = ((Object)val).name; string text = ((Object)val.mesh).name.Replace(" Instance", ""); Plugin.LogVerbose("Mesh: " + string.Join(":", path, name, text)); AssetTree<AddonAsset>.AssetNode assetNode = base.AssetTree?.Find(new AssetTree<AddonAsset>.FindBehaviour.GetMostSpecificThatMatchesQueryOrCollectAllWhenReachedLastQuerySegment(), path, name, text).FirstOrDefault(); if (assetNode != null) { val.mesh = assetNode.Asset.SourceAddon.LoadMesh(assetNode.Asset.AssetMapping.Replacements.First()); } } } } internal class PrefabLoader : IAssetLoader { private readonly Dictionary<string, Addon> _prefabReplacements = new Dictionary<string, Addon>(); public void Initialize(IEnumerable<Addon> addons) { foreach (Addon addon in addons) { RegisterPrefabReplacements(addon); } ResourceRedirection.RegisterAsyncAndSyncAssetLoadingHook(500, (Action<IAssetLoadingContext>)ReplacePrefab); } private void ReplacePrefab(IAssetLoadingContext ctx) { string text = (ctx.GetNormalizedAssetBundlePath() + "\\" + ctx.Parameters.Name).ToLowerInvariant(); if (_prefabReplacements.TryGetValue(text, out Addon value)) { ctx.Asset = (Object)(object)value.LoadPrefab(text); bool? flag = false; ctx.Complete(true, (bool?)true, flag); } } private void RegisterPrefabReplacements(Addon addon) { foreach (AddonManifest.AssetMapping item in addon.Manifest.AssetMappings.Where((AddonManifest.AssetMapping assetMapping) => assetMapping.AssetType == AddonManifest.AssetType.Prefab)) { if (!addon.FileExists(addon.GetAssetPath(item.Replacements.First(), out string _))) { Plugin.Log.LogWarning((object)($"[{addon.GUID}] Asset '{item.Replacements.First()}' of type " + $"'{AddonManifest.AssetType.Prefab}' does not exist in the mod, skipping.")); } string[] targets = item.Targets; foreach (string text in targets) { if (_prefabReplacements.TryGetValue(text, out Addon value)) { Plugin.Log.LogWarning((object)("[" + addon.GUID + "] prefab " + text + " is already being replaced by '" + value.GUID + "', skipping.")); } else { _prefabReplacements[text] = addon; } } } } } internal class TextureLoader : BaseAssetTreeLoader { private static readonly string[] TexturePathSchema = new string[4] { "prefabPath", "materialName", "textureName", "materialParameter" }; protected override AddonManifest.AssetType SupportedAssetType => AddonManifest.AssetType.Texture; protected override int TargetSegmentsCount { get; } = TexturePathSchema.Length; public override void Initialize(IEnumerable<Addon> addons) { base.Initialize(addons); ResourceRedirection.RegisterAssetLoadedHook((HookBehaviour)2, (Action<AssetLoadedContext>)PatchLoadedAsset); ResourceRedirection.RegisterResourceLoadedHook((HookBehaviour)2, (Action<ResourceLoadedContext>)PatchLoadedResource); } private void PatchLoadedResource(ResourceLoadedContext ctx) { Plugin.LogVerbose($"Loaded resource to load resource {ctx.Parameters.Path}, {ctx.Parameters.Type}"); Object[] assets = ctx.Assets; foreach (Object val in assets) { string uniqueFileSystemAssetPath = ctx.GetUniqueFileSystemAssetPath(val); ItemSpawnerID val2 = (ItemSpawnerID)(object)((val is ItemSpawnerID) ? val : null); if (val2 != null) { ReplaceItemSpawnerIcon(val2, uniqueFileSystemAssetPath); } } } private void ReplaceItemSpawnerIcon(ItemSpawnerID itemSpawnerId, string path) { //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)itemSpawnerId.Sprite)) { Plugin.LogVerbose("ItemSpawnerID Icon: " + string.Join(":", path, ((Object)itemSpawnerId.Sprite).name, ((Object)itemSpawnerId.Sprite.texture).name)); AssetTree<AddonAsset>.AssetNode assetNode = base.AssetTree?.Find(new AssetTree<AddonAsset>.FindBehaviour.GetMostSpecificThatMatchesQueryOrCollectAllWhenReachedLastQuerySegment(), path, ((Object)itemSpawnerId.Sprite).name, ((Object)itemSpawnerId.Sprite.texture).name).FirstOrDefault(); if (assetNode != null) { Sprite sprite = Sprite.Create(assetNode.Asset.SourceAddon.LoadTexture(assetNode.Asset.AssetMapping.Replacements.First()), itemSpawnerId.Sprite.rect, itemSpawnerId.Sprite.pivot, itemSpawnerId.Sprite.pixelsPerUnit, 0u, (SpriteMeshType)1, itemSpawnerId.Sprite.border); itemSpawnerId.Sprite = sprite; } } } private void PatchLoadedAsset(AssetLoadedContext ctx) { Object[] assets = ctx.Assets; foreach (Object val in assets) { string uniqueFileSystemAssetPath = ctx.GetUniqueFileSystemAssetPath(val); GameObject val2 = (GameObject)(object)((val is GameObject) ? val : null); if (val2 != null) { ReplaceTextures(val2, uniqueFileSystemAssetPath); } } } private void ReplaceTextures(GameObject go, string path) { MeshRenderer[] componentsInChildren = go.GetComponentsInChildren<MeshRenderer>(); foreach (MeshRenderer val in componentsInChildren) { Material[] materials = ((Renderer)val).materials; if (materials == null) { continue; } Material[] array = materials; foreach (Material val2 in array) { string text = ((Object)val2).name.Replace("(Instance)", "").Trim(); string text2 = (Object.op_Implicit((Object)(object)val2.mainTexture) ? ((Object)val2.mainTexture).name : "NULL"); Plugin.LogVerbose("Texture: " + string.Join(":", path, text, text2)); AssetTree<AddonAsset>.AssetNode[] array2 = base.AssetTree?.Find(new AssetTree<AddonAsset>.FindBehaviour.GetMostSpecificThatMatchesQueryOrCollectAllWhenReachedLastQuerySegment(), path, text, text2); if (array2 == null || array2.Length == 0) { continue; } AssetTree<AddonAsset>.AssetNode[] array3 = array2; foreach (AssetTree<AddonAsset>.AssetNode assetNode in array3) { Texture2D val3 = assetNode.Asset.SourceAddon.LoadTexture(assetNode.Asset.AssetMapping.Replacements.First()); if (assetNode.FinalTargetSegment == null) { val2.mainTexture = (Texture)(object)val3; } else { val2.SetTexture(assetNode.FinalTargetSegment, (Texture)(object)val3); } } } ((Renderer)val).materials = materials; } } } } namespace Sirdoggy.SideloaderMint.Addons { internal class Addon { private const string MuteAudioClipPathValue = "_mute"; private const string SilenceSoundFileRelativePath = "assets/silence.wav"; private readonly Dictionary<string, AssetBundle> _assetBundles = new Dictionary<string, AssetBundle>(); private readonly Dictionary<string, AudioClip> _audioClips = new Dictionary<string, AudioClip>(); private readonly Dictionary<string, Material> _materials = new Dictionary<string, Material>(); private readonly Dictionary<string, Mesh> _meshes = new Dictionary<string, Mesh>(); private readonly Dictionary<string, Texture2D> _textures = new Dictionary<string, Texture2D>(); public AddonManifest Manifest { get; } public string BasePath { get; } public string GUID => BasePath.Replace(Paths.BepInExRootPath, "") ?? ""; public Addon(string basePath, bool requireUniqueAddonDirectoryName) { if (!Directory.Exists(basePath)) { throw new DirectoryNotFoundException("Could not resolve directory."); } if (requireUniqueAddonDirectoryName) { string[] array = new DirectoryInfo(basePath).Name.Split(new char[1] { '-' }); if (array.Length != 2) { throw new InvalidDirectoryNameException("Addon directory name does not match naming convention. Directory name should match: {AddonAuthor}" + $"{'-'}{{AddonName}}. " + "The directory name must contain only one " + $"'{'-'}' character."); } string text = array[0].Trim(); string text2 = array[1].Trim(); if (text.Length == 0) { throw new InvalidDirectoryNameException("Addon directory name does not match naming convention. Author name cannot be blank."); } if (text2.Length == 0) { throw new InvalidDirectoryNameException("Addon directory name does not match naming convention. Addon name cannot be blank."); } } string path = Path.Combine(basePath, "manifest.json"); if (!File.Exists(path)) { throw new FileNotFoundException("The manifest file is missing. The full file name should be 'manifest.json'."); } AddonManifestParser.ParsingResult parsingResult = AddonManifestParser.ParseJson(File.ReadAllText(path)); if (!(parsingResult is AddonManifestParser.ParsingResult.InvalidManifest invalidManifest)) { if (parsingResult is AddonManifestParser.ParsingResult.Success success) { AddonManifest manifest = success.Manifest; AddonManifest addonManifest = manifest; AddonManifest.AssetMapping[] assetMappings = addonManifest.AssetMappings; for (int i = 0; i < assetMappings.Length; i++) { foreach (ReplacementAsset item in assetMappings[i].Replacements.Where((ReplacementAsset asset) => asset is ReplacementAsset.LoadFromAddon)) { PathValidator.Validate(basePath, (item as ReplacementAsset.LoadFromAddon).RelativePath); } } Manifest = addonManifest; BasePath = basePath; return; } throw new NotSupportedException("Unknown validation result"); } throw new InvalidManifestException("The manifest is not valid. Errors:\n" + StringUtils.JoinValues(invalidManifest.ValidationErrors, "\n")); } public GameObject? LoadPrefab(string target) { if (FileExists(GetAssetPath(target, out string _))) { return this.LoadAssetBundleAsset<GameObject>(target, (IDictionary<string, GameObject>)null); } Plugin.Log.LogWarning((object)("[" + GUID + "] no prefab defined at '" + target + "'")); return null; } public Texture2D LoadTexture(ReplacementAsset replacement) { //IL_005c: 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_0063: Expected O, but got Unknown //IL_0068: Expected O, but got Unknown string relativePath = ((replacement as ReplacementAsset.LoadFromAddon) ?? throw new NotSupportedException("Only ReplacementAsset.LoadFromAddon is supported.")).RelativePath; if (!FileExists(relativePath)) { throw new FileNotFoundException("Tried to load non-existent texture '" + relativePath + "' from mod " + GUID); } if (_textures.TryGetValue(relativePath, out Texture2D value)) { return value; } Dictionary<string, Texture2D> textures = _textures; Texture2D val = new Texture2D(1, 1, (TextureFormat)5, false); Texture2D val2 = val; textures[relativePath] = val; value = val2; value.LoadImage(LoadBytes(relativePath)); return value; } public Material LoadMaterial(ReplacementAsset replacement) { return this.LoadAssetBundleAsset<Material>(replacement, (IDictionary<string, Material>)_materials); } public Mesh LoadMesh(ReplacementAsset replacement) { return this.LoadAssetBundleAsset<Mesh>(replacement, (IDictionary<string, Mesh>)_meshes); } public AudioClip LoadAudioClip(ReplacementAsset replacement, string name) { string text = null; string text2 = null; if (!(replacement is ReplacementAsset.LoadFromAddon loadFromAddon)) { if (replacement is ReplacementAsset.Silence) { text = Constants.PluginDirectory; text2 = "assets/silence.wav"; } } else { text = BasePath; text2 = loadFromAddon.RelativePath; } if (!File.Exists(Path.Combine(text, text2))) { throw new FileNotFoundException("Tried to load non-existent audio clip '" + text2 + "' from mod '" + GUID + "'"); } if (_audioClips.TryGetValue(text2, out AudioClip value)) { return value; } long size; using Stream from = GetInputStream(text, text2, out size); using MemoryStream memoryStream = new MemoryStream(); StreamExtensions.CopyTo(from, memoryStream); using BinaryReader br = new BinaryReader(memoryStream); return _audioClips[text2] = WavUtility.ToAudioClip(br, name); } public AssetBundle LoadAssetBundle(string path, out string assetPath) { string assetPath2 = GetAssetPath(path, out assetPath); if (!_assetBundles.TryGetValue(assetPath2, out AssetBundle value)) { return _assetBundles[assetPath2] = AssetBundle.LoadFromMemory(LoadBytes(assetPath2)); } return value; } public T LoadAssetBundleAsset<T>(ReplacementAsset replacement, IDictionary<string, T> assetCache = null) where T : Object { string relativePath = ((replacement as ReplacementAsset.LoadFromAddon) ?? throw new NotSupportedException("Only ReplacementAsset.LoadFromAddon is supported.")).RelativePath; return LoadAssetBundleAsset(relativePath, assetCache); } public T LoadAssetBundleAsset<T>(string path, IDictionary<string, T> assetCache = null) where T : Object { Plugin.LogVerbose("Loading asset from " + path); if (assetCache != null && assetCache.TryGetValue(path, out T value)) { return value; } value = LoadAssetBundle(path, out string assetPath).LoadAsset<T>(assetPath); if (assetCache != null) { assetCache[path] = value; } return value; } public string GetAssetPath(ReplacementAsset replacement, out string assetPath) { string relativePath = ((replacement as ReplacementAsset.LoadFromAddon) ?? throw new NotSupportedException("Only ReplacementAsset.LoadFromAddon is supported.")).RelativePath; return GetAssetPath(relativePath, out assetPath); } public string GetAssetPath(string path, out string assetPath) { assetPath = null; string[] array = path.Split(new char[1] { ':' }, StringSplitOptions.RemoveEmptyEntries); if (array.Length == 0) { return null; } assetPath = ((array.Length >= 2) ? array[^1] : null); return array[0]; } private Stream GetInputStream(string basePath, string relativePath, out long size) { if (!File.Exists(Path.Combine(basePath, relativePath))) { throw new FileNotFoundException("'" + relativePath + "' does not exist in " + GUID); } FileStream fileStream = File.OpenRead(Path.Combine(basePath, relativePath)); size = fileStream.Length; return fileStream; } private byte[] LoadBytes(string relativePath) { long size; using Stream stream = GetInputStream(BasePath, relativePath, out size); byte[] array = new byte[size]; stream.Read(array, 0, array.Length); return array; } public bool FileExists(string relativePath) { if (!File.Exists(Path.Combine(BasePath, relativePath))) { return relativePath == "_mute"; } return true; } } internal class InvalidManifestException : Exception { public InvalidManifestException(string message) : base(message) { } } internal class InvalidDirectoryNameException : Exception { public InvalidDirectoryNameException(string message) : base(message) { } } internal static class AddonsConstants { public const string SilenceAssetPath = "_mute"; } } namespace Sirdoggy.SideloaderMint.Addons.Model { internal class AddonManifest { public class AssetMapping { public sealed class Audio : AssetMapping { public float[]? VolumeRange { get; } public float[]? PitchRange { get; } public Audio(AssetType assetType, string[] targets, ReplacementAsset[] replacements, float[]? volumeRange, float[]? pitchRange) { VolumeRange = volumeRange; PitchRange = pitchRange; base..ctor(assetType, targets, replacements); } } public AssetType AssetType { get; } public string[] Targets { get; } public ReplacementAsset[] Replacements { get; } public AssetMapping(AssetType assetType, string[] targets, ReplacementAsset[] replacements) { AssetType = assetType; Targets = targets; Replacements = replacements; base..ctor(); } } public enum AssetType { Texture, Prefab, Mesh, Material, AudioClip, GunSound, GunAudioSet, Unknown } public string ManifestRevision { get; } public AssetMapping[] AssetMappings { get; } public bool Disabled { get; } public AddonManifest(string manifestRevision, AssetMapping[] assetMappings, bool disabled) { ManifestRevision = manifestRevision; AssetMappings = assetMappings; Disabled = disabled; base..ctor(); } public bool UsesAsterisksAsWildcardsInTargetField() { return ManifestRevision != "1"; } } internal abstract class ReplacementAsset { public sealed class LoadFromAddon : ReplacementAsset { public string RelativePath { get; } public LoadFromAddon(string relativePath) { RelativePath = relativePath; base..ctor(); } public override string ToString() { return RelativePath; } } public sealed class Silence : ReplacementAsset { } public static ReplacementAsset Create(string relativePath) { if (string.Equals(relativePath, "_mute", StringComparison.OrdinalIgnoreCase)) { return new Silence(); } return new LoadFromAddon(relativePath); } } } namespace Sirdoggy.SideloaderMint.Addons.Data { internal static class AddonManifestMapper { public static AddonManifest ToDomain(this AddonManifestRevision1Dto dto) { return new AddonManifest(dto.ManifestRevision ?? throw new NullReferenceException(), (dto.AssetMappings ?? new AddonManifestRevision1Dto.AssetMappingDto[0]).Select((AddonManifestRevision1Dto.AssetMappingDto assetMappingDto) => assetMappingDto.ToDomain()).ToArray(), disabled: false); } public static AddonManifest.AssetMapping ToDomain(this AddonManifestRevision1Dto.AssetMappingDto dto) { return new AddonManifest.AssetMapping(dto.Type.ToDomain(), new string[1] { dto.Target ?? throw new NullReferenceException() }, new ReplacementAsset[1] { new ReplacementAsset.LoadFromAddon(dto.Path ?? throw new NullReferenceException()) }); } public static AddonManifest.AssetType ToDomain(this AddonManifestRevision1Dto.AssetTypeDto? dto) { return dto switch { AddonManifestRevision1Dto.AssetTypeDto.Texture => AddonManifest.AssetType.Texture, AddonManifestRevision1Dto.AssetTypeDto.Prefab => AddonManifest.AssetType.Prefab, AddonManifestRevision1Dto.AssetTypeDto.Mesh => AddonManifest.AssetType.Mesh, AddonManifestRevision1Dto.AssetTypeDto.Material => AddonManifest.AssetType.Material, AddonManifestRevision1Dto.AssetTypeDto.AudioClip => AddonManifest.AssetType.AudioClip, _ => AddonManifest.AssetType.Unknown, }; } public static AddonManifest ToDomain(this AddonManifestRevision2Dto dto) { return new AddonManifest(dto.ManifestRevision ?? throw new NullReferenceException(), disabled: dto.Disabled.GetValueOrDefault(), assetMappings: (from assetMappingDto in dto.AssetMappings ?? new AddonManifestRevision2Dto.AssetMappingDto[0] where ((!assetMappingDto.Disabled) ?? true) ? true : false select assetMappingDto.ToDomain()).ToArray()); } public static AddonManifest.AssetMapping ToDomain(this AddonManifestRevision2Dto.AssetMappingDto dto) { List<string> list = dto.Targets?.ToList() ?? new List<string>(); if (dto.Target != null) { list.Add(dto.Target); } List<string> list2 = dto.Paths?.ToList() ?? new List<string>(); if (dto.Path != null) { list2.Add(dto.Path); } ReplacementAsset[] replacements = list2.Select(ReplacementAsset.Create).ToArray(); AddonManifestRevision2Dto.AssetTypeDto? type = dto.Type; if (type.HasValue) { AddonManifestRevision2Dto.AssetTypeDto valueOrDefault = type.GetValueOrDefault(); if ((uint)(valueOrDefault - 4) <= 2u) { return new AddonManifest.AssetMapping.Audio(dto.Type.ToDomain(), list.ToArray(), replacements, dto.SoundVolume, dto.SoundPitch); } } return new AddonManifest.AssetMapping(dto.Type.ToDomain(), list.ToArray(), replacements); } public static AddonManifest.AssetType ToDomain(this AddonManifestRevision2Dto.AssetTypeDto? dto) { return dto switch { AddonManifestRevision2Dto.AssetTypeDto.Texture => AddonManifest.AssetType.Texture, AddonMani