diff options
author | tylermurphy534 <tylermurphy534@gmail.com> | 2022-11-06 15:12:42 -0500 |
---|---|---|
committer | tylermurphy534 <tylermurphy534@gmail.com> | 2022-11-06 15:12:42 -0500 |
commit | eb84bb298d2b95aec7b2ae12cbf25ac64f25379a (patch) | |
tree | efd616a157df06ab661c6d56651853431ac6b08b /VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime | |
download | unityprojects-eb84bb298d2b95aec7b2ae12cbf25ac64f25379a.tar.gz unityprojects-eb84bb298d2b95aec7b2ae12cbf25ac64f25379a.tar.bz2 unityprojects-eb84bb298d2b95aec7b2ae12cbf25ac64f25379a.zip |
move to self host
Diffstat (limited to 'VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime')
40 files changed, 2034 insertions, 0 deletions
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi.meta new file mode 100644 index 00000000..96e1993a --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 41b228f88e2afce4fa7152bf11eda080 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi.meta new file mode 100644 index 00000000..0b38e67b --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c4ba92a5dc8e6c14bb9b8dfce6851a99 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Event.cs b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Event.cs new file mode 100644 index 00000000..110564e7 --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Event.cs @@ -0,0 +1,17 @@ +namespace PortMidi +{ + public class Event + { + public Event(PmEvent pmEvent) + { + this.Status = PortMidiMarshal.Pm_MessageStatus(pmEvent.message); + this.Data1 = PortMidiMarshal.Pm_MessageData1(pmEvent.message); + this.Data2 = PortMidiMarshal.Pm_MessageData2(pmEvent.message); + } + + public long Timestamp { get; set; } + public long Status { get; set; } + public long Data1 { get; set; } + public long Data2 { get; set; } + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Event.cs.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Event.cs.meta new file mode 100644 index 00000000..d02de3b3 --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Event.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 61b00e22036b5cc49a565aaec91ca8cb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiDeviceInfo.cs b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiDeviceInfo.cs new file mode 100644 index 00000000..001816fe --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiDeviceInfo.cs @@ -0,0 +1,34 @@ +using System; +using System.Runtime.InteropServices; + +namespace PortMidi +{ + public struct MidiDeviceInfo + { + PmDeviceInfo info; + + internal MidiDeviceInfo(int id, IntPtr ptr) + { + ID = id; + this.info = (PmDeviceInfo) Marshal.PtrToStructure(ptr, typeof(PmDeviceInfo)); + } + + public int ID { get; set; } + + public string Interface => Marshal.PtrToStringAnsi(info.Interface); + + public string Name => Marshal.PtrToStringAnsi(info.Name); + + public bool IsInput => info.Input != 0; + + public bool IsOutput => info.Output != 0; + + public bool IsOpened => info.Opened != 0; + + public override string ToString() + { + return + $"{Interface} - {Name} ({(IsInput ? (IsOutput ? "I/O" : "Input") : (IsOutput ? "Output" : "N/A"))} {(IsOpened ? "open" : String.Empty)})"; + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiDeviceInfo.cs.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiDeviceInfo.cs.meta new file mode 100644 index 00000000..60628362 --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiDeviceInfo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bbb88a986ea685d41a9eac3aa3dd8c60 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiDeviceManager.cs b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiDeviceManager.cs new file mode 100644 index 00000000..4628afc1 --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiDeviceManager.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using PmDeviceID = System.Int32; +using PortMidiStream = System.IntPtr; +using PmError = PortMidi.MidiErrorType; + +namespace PortMidi +{ + public static class MidiDeviceManager + { + private const int DefaultBufferSize = 1024; + + static MidiDeviceManager() + { + PortMidiMarshal.Pm_Initialize(); + AppDomain.CurrentDomain.DomainUnload += delegate { PortMidiMarshal.Pm_Terminate(); }; + } + + public static int DeviceCount => PortMidiMarshal.Pm_CountDevices(); + + public static int DefaultInputDeviceId => PortMidiMarshal.Pm_GetDefaultInputDeviceID(); + + public static int DefaultOutputDeviceId => PortMidiMarshal.Pm_GetDefaultOutputDeviceID(); + + public static IEnumerable<MidiDeviceInfo> AllDevices + { + get + { + for (var i = 0; i < DeviceCount; i++) + { + yield return GetDeviceInfo(i); + } + } + } + + private static MidiDeviceInfo GetDeviceInfo(PmDeviceID id) + { + return new MidiDeviceInfo(id, PortMidiMarshal.Pm_GetDeviceInfo(id)); + } + + public static MidiInput OpenInput(PmDeviceID inputDevice) + { + return OpenInput(inputDevice, DefaultBufferSize); + } + + private static MidiInput OpenInput(PmDeviceID inputDevice, int bufferSize) + { + PortMidiStream stream = default; + var e = PortMidiMarshal.Pm_OpenInput(out stream, inputDevice, IntPtr.Zero, bufferSize, null, IntPtr.Zero); + if (e != PmError.NoError) + { + throw new MidiException(e, $"Failed to open MIDI input device {e}"); + } + + return new MidiInput(stream, inputDevice); + } + + public static MidiOutput OpenOutput(PmDeviceID outputDevice) + { + PortMidiStream stream; + var e = PortMidiMarshal.Pm_OpenOutput(out stream, outputDevice, IntPtr.Zero, 0, null, IntPtr.Zero, 0); + if (e != PmError.NoError) + { + throw new MidiException(e, $"Failed to open MIDI output device {e}"); + } + return new MidiOutput(stream, outputDevice, 0); + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiDeviceManager.cs.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiDeviceManager.cs.meta new file mode 100644 index 00000000..3874e4b5 --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiDeviceManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 02b0549f0815d8a4982133c0a99880cf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiErrorType.cs b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiErrorType.cs new file mode 100644 index 00000000..e03dfdcf --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiErrorType.cs @@ -0,0 +1,18 @@ +namespace PortMidi +{ + public enum MidiErrorType + { + NoError = 0, + NoData = 0, + GotData = 1, + HostError = -10000, + InvalidDeviceId, + InsufficientMemory, + BufferTooSmall, + BufferOverflow, + BadPointer, + BadData, + InternalError, + BufferMaxSize, + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiErrorType.cs.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiErrorType.cs.meta new file mode 100644 index 00000000..15768ae2 --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiErrorType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6b1e1637c177e0c43af16fefddc0826c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiEvent.cs b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiEvent.cs new file mode 100644 index 00000000..b304cefd --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiEvent.cs @@ -0,0 +1,31 @@ +using System; +using System.Runtime.InteropServices; + +namespace PortMidi +{ + [StructLayout(LayoutKind.Sequential)] + public struct MidiEvent + { + MidiMessage msg; + Int32 ts; + [NonSerialized] byte[] sysex; + + public MidiMessage Message + { + get => msg; + set => msg = value; + } + + public Int32 Timestamp + { + get => ts; + set => ts = value; + } + + public byte[] SysEx + { + get => sysex; + set => sysex = value; + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiEvent.cs.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiEvent.cs.meta new file mode 100644 index 00000000..cb252310 --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiEvent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b8f59aea3b1948f4ebb0d7835b69bbb7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiException.cs b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiException.cs new file mode 100644 index 00000000..551297b2 --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiException.cs @@ -0,0 +1,22 @@ +using System; + +namespace PortMidi +{ + public class MidiException : Exception + { + MidiErrorType error_type; + + public MidiException(MidiErrorType errorType, string message) + : this(errorType, message, null) + { + } + + public MidiException(MidiErrorType errorType, string message, Exception innerException) + : base(message, innerException) + { + error_type = errorType; + } + + public MidiErrorType ErrorType => error_type; + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiException.cs.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiException.cs.meta new file mode 100644 index 00000000..82da420f --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiException.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a9aa39367a5a6014e8ae35c4c0fc0fd0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiFilter.cs b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiFilter.cs new file mode 100644 index 00000000..d270806a --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiFilter.cs @@ -0,0 +1,30 @@ +using System; + +namespace PortMidi +{ + [Flags] + public enum MidiFilter : int + { + Active = 1 << 0x0E, + SysEx = 1, + Clock = 1 << 0x08, + Play = ((1 << 0x0A) | (1 << 0x0C) | (1 << 0x0B)), + Tick = (1 << 0x09), + FD = (1 << 0x0D), + Undefined = FD, + Reset = (1 << 0x0F), + RealTime = (Active | SysEx | Clock | Play | Undefined | Reset | Tick), + Note = ((1 << 0x19) | (1 << 0x18)), + CAF = (1 << 0x1D), + PAF = (1 << 0x1A), + AF = (CAF | PAF), + Program = (1 << 0x1C), + Control = (1 << 0x1B), + PitchBend = (1 << 0x1E), + MTC = (1 << 0x01), + SongPosition = (1 << 0x02), + SongSelect = (1 << 0x03), + Tune = (1 << 0x06), + SystemCommon = (MTC | SongPosition | SongSelect | Tune) + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiFilter.cs.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiFilter.cs.meta new file mode 100644 index 00000000..f553d7f5 --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiFilter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9ddf13cca3c55164692f303b16a5fb2d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiInput.cs b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiInput.cs new file mode 100644 index 00000000..dbb35623 --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiInput.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace PortMidi +{ + public class MidiInput : MidiStream + { + public MidiInput(IntPtr stream, Int32 inputDevice) + : base(stream, inputDevice) + { + } + + public bool HasData => PortMidiMarshal.Pm_Poll(stream) == MidiErrorType.GotData; + + public int Read(byte[] buffer, int index, int length) + { + var gch = GCHandle.Alloc(buffer); + try + { + var ptr = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, index); + int size = PortMidiMarshal.Pm_Read(stream, ptr, length); + if (size < 0) + { + throw new MidiException((MidiErrorType) size, + PortMidiMarshal.Pm_GetErrorText((MidiErrorType) size)); + } + return size * 4; + } + finally + { + gch.Free(); + } + } + + public Event ReadEvent(byte[] buffer, int index, int length) + { + var gch = GCHandle.Alloc(buffer); + try + { + var ptr = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, index); + int size = PortMidiMarshal.Pm_Read(stream, ptr, length); + if (size < 0) + { + throw new MidiException((MidiErrorType) size, + PortMidiMarshal.Pm_GetErrorText((MidiErrorType) size)); + } + + return new Event(Marshal.PtrToStructure<PmEvent>(ptr)); + } + finally + { + gch.Free(); + } + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiInput.cs.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiInput.cs.meta new file mode 100644 index 00000000..c46ce4b1 --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiInput.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 324be2ea40d796c49b74c60b8c2e5c97 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiMessage.cs b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiMessage.cs new file mode 100644 index 00000000..a7b7c9c4 --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiMessage.cs @@ -0,0 +1,20 @@ +using System; + +namespace PortMidi +{ + public struct MidiMessage + { + private int v; + public MidiMessage(int value) + { + v = value; + } + + public MidiMessage(int status, int data1, int data2) + { + v = ((data2 << 16) & 0xFF0000) | ((data1 << 8) & 0xFF00) | (status & 0xFF); + } + + public Int32 Value => v; + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiMessage.cs.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiMessage.cs.meta new file mode 100644 index 00000000..02f67375 --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiMessage.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c18643a5c49be3649ade32f7c9b19e99 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiOutput.cs b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiOutput.cs new file mode 100644 index 00000000..fe56081c --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiOutput.cs @@ -0,0 +1,67 @@ +using System; +using System.Runtime.InteropServices; + +namespace PortMidi +{ + public class MidiOutput : MidiStream + { + public MidiOutput(IntPtr stream, Int32 outputDevice, int latency) + : base(stream, outputDevice) + { + } + + public void Write(MidiEvent midiEvent) + { + if (midiEvent.SysEx != null) + { + WriteSysEx(midiEvent.Timestamp, midiEvent.SysEx); + } + else + { + Write(midiEvent.Timestamp, midiEvent.Message); + } + } + + private void Write(Int32 when, MidiMessage msg) + { + var ret = PortMidiMarshal.Pm_WriteShort(stream, when, msg); + if (ret != MidiErrorType.NoError) + { + throw new MidiException(ret, + $"Failed to write message {msg.Value} : {PortMidiMarshal.Pm_GetErrorText((MidiErrorType) ret)}"); + } + } + + private void WriteSysEx(Int32 when, byte[] sysEx) + { + var ret = PortMidiMarshal.Pm_WriteSysEx(stream, when, sysEx); + if (ret != MidiErrorType.NoError) + throw new MidiException(ret, + $"Failed to write sysEx message : {PortMidiMarshal.Pm_GetErrorText((MidiErrorType) ret)}"); + } + + public void Write(MidiEvent[] buffer) + { + Write(buffer, 0, buffer.Length); + } + + private void Write(MidiEvent[] buffer, int index, int length) + { + var gch = GCHandle.Alloc(buffer); + try + { + var ptr = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, index); + var ret = PortMidiMarshal.Pm_Write(stream, ptr, length); + if (ret != MidiErrorType.NoError) + { + throw new MidiException(ret, + $"Failed to write messages : {PortMidiMarshal.Pm_GetErrorText((MidiErrorType) ret)}"); + } + } + finally + { + gch.Free(); + } + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiOutput.cs.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiOutput.cs.meta new file mode 100644 index 00000000..cc6a9a22 --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiOutput.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a806da9b51653af47b83c3c10a6eed65 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiStream.cs b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiStream.cs new file mode 100644 index 00000000..e4d80447 --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiStream.cs @@ -0,0 +1,41 @@ +using System; + +namespace PortMidi +{ + public abstract class MidiStream : IDisposable + { + internal IntPtr stream; + internal Int32 device; + + protected MidiStream(IntPtr stream, Int32 deviceID) + { + this.stream = stream; + this.device = deviceID; + } + + public void Abort() + { + PortMidiMarshal.Pm_Abort(stream); + } + + public void Close() + { + Dispose(); + } + + public void Dispose() + { + PortMidiMarshal.Pm_Close(stream); + } + + public void SetFilter(MidiFilter filters) + { + PortMidiMarshal.Pm_SetFilter(stream, filters); + } + + public void SetChannelMask(int mask) + { + PortMidiMarshal.Pm_SetChannelMask(stream, mask); + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiStream.cs.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiStream.cs.meta new file mode 100644 index 00000000..a16d7e5c --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiStream.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f858f0a52902aaf4298247159434e1ae +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiTimeProcDelegate.cs b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiTimeProcDelegate.cs new file mode 100644 index 00000000..c0a21dc2 --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiTimeProcDelegate.cs @@ -0,0 +1,7 @@ +using System; +using PmTimestamp = System.Int32; + +namespace PortMidi +{ + public delegate PmTimestamp MidiTimeProcDelegate(IntPtr timeInfo); +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiTimeProcDelegate.cs.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiTimeProcDelegate.cs.meta new file mode 100644 index 00000000..79fb10f5 --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiTimeProcDelegate.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 63050b27ae9f6ab4cb3837f915f15067 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Plugins.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Plugins.meta new file mode 100644 index 00000000..9828731f --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Plugins.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 743cfca463c6cc94fabfda636f4eb439 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Plugins/Windows.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Plugins/Windows.meta new file mode 100644 index 00000000..fe5fb2cb --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Plugins/Windows.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: bc4d155a8e6f6f144871b6ee03542b7c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Plugins/Windows/portmidi.dll b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Plugins/Windows/portmidi.dll Binary files differnew file mode 100644 index 00000000..ba5be4e5 --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Plugins/Windows/portmidi.dll diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Plugins/Windows/portmidi.dll.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Plugins/Windows/portmidi.dll.meta new file mode 100644 index 00000000..36f81d69 --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Plugins/Windows/portmidi.dll.meta @@ -0,0 +1,96 @@ +fileFormatVersion: 2 +guid: 372656c38e5c12f48b819e12fa08ac7b +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + '': Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 0 + Exclude Linux: 0 + Exclude Linux64: 0 + Exclude LinuxUniversal: 0 + Exclude OSXUniversal: 0 + Exclude WebGL: 1 + Exclude Win: 0 + Exclude Win64: 0 + - first: + Android: Android + second: + enabled: 0 + settings: + CPU: ARMv7 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 1 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Facebook: Win + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Facebook: Win64 + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Standalone: Linux + second: + enabled: 1 + settings: + CPU: x86 + - first: + Standalone: Linux64 + second: + enabled: 1 + settings: + CPU: x86_64 + - first: + Standalone: LinuxUniversal + second: + enabled: 1 + settings: {} + - first: + Standalone: OSXUniversal + second: + enabled: 1 + settings: + CPU: AnyCPU + - first: + Standalone: Win + second: + enabled: 1 + settings: + CPU: AnyCPU + - first: + Standalone: Win64 + second: + enabled: 1 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PmDeviceInfo.cs b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PmDeviceInfo.cs new file mode 100644 index 00000000..e2ecccdd --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PmDeviceInfo.cs @@ -0,0 +1,21 @@ +using System; +using System.Runtime.InteropServices; + +namespace PortMidi +{ + [StructLayout(LayoutKind.Sequential)] + internal struct PmDeviceInfo + { + public int StructVersion; + public IntPtr Interface; + public IntPtr Name; + public int Input; + public int Output; + public int Opened; + + public override string ToString() + { + return $"{StructVersion}, {Interface}, {Name}, {Input}, {Output}, {Opened}"; + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PmDeviceInfo.cs.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PmDeviceInfo.cs.meta new file mode 100644 index 00000000..048fa702 --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PmDeviceInfo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e20f952b815e0fd4c89e244be795b605 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PmEvent.cs b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PmEvent.cs new file mode 100644 index 00000000..b5d68bce --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PmEvent.cs @@ -0,0 +1,11 @@ +using PmMessage = System.Int32; +using PmTimestamp = System.Int32; + +namespace PortMidi +{ + public struct PmEvent + { + public PmMessage message; + public PmTimestamp timestamp; + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PmEvent.cs.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PmEvent.cs.meta new file mode 100644 index 00000000..d8ec66ee --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PmEvent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 36b7824d7ad3b3f4fbb5ed6d2a5766ec +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PortMidiMarshal.cs b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PortMidiMarshal.cs new file mode 100644 index 00000000..58f02e11 --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PortMidiMarshal.cs @@ -0,0 +1,105 @@ +using System; +using System.Runtime.InteropServices; + +namespace PortMidi +{ + internal class PortMidiMarshal + { + private const int Hdrlength = 50; + private const uint PmHostErrorMsgLen = 256; + const int PmNoDevice = -1; + + [DllImport("portmidi")] + public static extern MidiErrorType Pm_Initialize(); + + [DllImport("portmidi")] + public static extern IntPtr Pm_GetDeviceInfo(Int32 id); + + [DllImport("portmidi")] + public static extern int Pm_CountDevices(); + + [DllImport("portmidi")] + public static extern Int32 Pm_GetDefaultInputDeviceID(); + + [DllImport("portmidi")] + public static extern Int32 Pm_GetDefaultOutputDeviceID(); + + [DllImport("portmidi")] + public static extern MidiErrorType Pm_OpenInput( + out IntPtr stream, + Int32 inputDevice, + IntPtr inputDriverInfo, + int bufferSize, + MidiTimeProcDelegate timeProc, + IntPtr timeInfo); + + [DllImport("portmidi")] + public static extern MidiErrorType Pm_OpenOutput( + out IntPtr stream, + Int32 outputDevice, + IntPtr outputDriverInfo, + int bufferSize, + MidiTimeProcDelegate time_proc, + IntPtr time_info, + int latency); + + [DllImport("portmidi")] + public static extern MidiErrorType Pm_SetFilter(IntPtr stream, MidiFilter filters); + + [DllImport("portmidi")] + public static extern MidiErrorType Pm_SetChannelMask(IntPtr stream, int mask); + + [DllImport("portmidi")] + public static extern MidiErrorType Pm_Poll(IntPtr stream); + + [DllImport("portmidi")] + public static extern int Pm_Read(IntPtr stream, IntPtr buffer, int length); + + [DllImport("portmidi")] + public static extern MidiErrorType Pm_Write(IntPtr stream, IntPtr buffer, int length); + + [DllImport("portmidi")] + public static extern MidiErrorType Pm_WriteSysEx(IntPtr stream, Int32 when, byte[] msg); + + [DllImport("portmidi")] + public static extern MidiErrorType Pm_WriteShort(IntPtr stream, Int32 when, MidiMessage msg); + + [DllImport("portmidi")] + public static extern MidiErrorType Pm_Abort(IntPtr stream); + + [DllImport("portmidi")] + public static extern MidiErrorType Pm_Close(IntPtr stream); + + [DllImport("portmidi")] + public static extern MidiErrorType Pm_Terminate(); + + [DllImport("portmidi")] + public static extern int Pm_HasHostError(IntPtr stream); + + [DllImport("portmidi")] + public static extern string Pm_GetErrorText(MidiErrorType errnum); + + [DllImport("portmidi")] + public static extern void Pm_GetHostErrorText(IntPtr msg, uint len); + + public static int Pm_Channel(int channel) + { + return 1 << channel; + } + + public static int Pm_MessageStatus(int msg) + { + return msg & 0xFF; + } + + public static int Pm_MessageData1(int msg) + { + return (msg >> 8) & 0xFF; + } + + public static int Pm_MessageData2(int msg) + { + return (msg >> 16) & 0xFF; + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PortMidiMarshal.cs.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PortMidiMarshal.cs.meta new file mode 100644 index 00000000..09c36972 --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PortMidiMarshal.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5d4b9d0d9dfad1c4894df5c2a5530696 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/VRCPortMidi.cs b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/VRCPortMidi.cs new file mode 100644 index 00000000..e4a6200b --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/VRCPortMidi.cs @@ -0,0 +1,116 @@ +#if (UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN) && !UNITY_ANDROID +using System; +using System.Collections.Generic; +using System.Linq; +#if VRC_CLIENT +using VRC.Core; +#endif +using PortMidi; +using UnityEngine; +using VRC.SDK3.Midi; + + +namespace VRC.SDKBase.Midi +{ + public class VRCPortMidiInput: IVRCMidiInput + { + + private MidiInput _input; + private byte[] _data; + private MidiDeviceInfo _info; + + public bool OpenDevice(string name = null) + { + try + { + if (!string.IsNullOrWhiteSpace(name)) + { + _info = MidiDeviceManager.AllDevices.FirstOrDefault(device => + device.IsInput && device.Name.ToLower().Contains(name.ToLower())); + } + else + { + _info = MidiDeviceManager.AllDevices.FirstOrDefault(device => + device.ID == MidiDeviceManager.DefaultInputDeviceId); + } + + _input = MidiDeviceManager.OpenInput(_info.ID); + _input.SetFilter(MidiFilter.Active | MidiFilter.SysEx | MidiFilter.Clock | MidiFilter.Play | MidiFilter.Tick | MidiFilter.Undefined | MidiFilter.Reset | MidiFilter.RealTime | MidiFilter.AF | MidiFilter.Program | MidiFilter.PitchBend | MidiFilter.SystemCommon); + _data = new byte[1024]; + return true; + } + catch (Exception e) + { + #if VRC_CLIENT + VRC.Core.Logger.LogError($"Error opening Default Device: {e.Message}"); + #else + Debug.Log($"Error opening Default Device: {e.Message}"); + #endif + return false; + } + } + + public void Close() + { + if (_input != null) + { + _input.Close(); + } + } + + public void Update() + { + if (_input == null) return; + + if (_input.HasData) + { + // Portmidi reports 4 bytes per event but the buffer has 8 bytes so we multiply count by 2 + int count = (_input.Read(_data, 0, _data.Length)) * 2; + for (int i = 0; i < count; i+=8) + { + ConvertAndSend(_data[i], _data[i + 1], _data[i + 2]); + } + } + } + + public IEnumerable<string> GetDeviceNames() + { + return MidiDeviceManager.AllDevices.Select(d => d.Name); + } + + private void ConvertAndSend(byte status, byte data1, byte data2) + { + var command = status & 0xF0; // mask off all but top 4 bits + + if (command >= 0x80 && command <= 0xE0) { + // it's a voice message + // find the channel by masking off all but the low 4 bits + var channel = status & 0x0F; + + if (command == VRCMidiHandler.STATUS_NOTE_ON || command == VRCMidiHandler.STATUS_NOTE_OFF || command == VRCMidiHandler.STATUS_CONTROL_CHANGE) + { + OnMidiVoiceMessage?.Invoke(this, new MidiVoiceEventArgs(command, channel, data1, data2)); + } + else + { +#if VRC_CLIENT + VRC.Core.Logger.Log($"command:{command} channel:{channel} data1:{data1} data2:{data2}"); +#else + Debug.Log($"command:{command} channel:{channel} data1:{data1} data2:{data2}"); +#endif + } + + } + else + { + // it's a system message, ignore for now + OnMidiRawMessage?.Invoke(this, new MidiRawEventArgs(status, data1, data2)); + } + } + + public string Name => _info.Name; + public event MidiVoiceMessageDelegate OnMidiVoiceMessage; + public event MidiRawMessageDelegate OnMidiRawMessage; + } +} +#endif
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/VRCPortMidi.cs.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/VRCPortMidi.cs.meta new file mode 100644 index 00000000..156b4665 --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/VRCPortMidi.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: fb33369bfa554472b08fc6828450a2c4 +timeCreated: 1607454401
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/UnityEventFilter.cs b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/UnityEventFilter.cs new file mode 100644 index 00000000..79228fad --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/UnityEventFilter.cs @@ -0,0 +1,1069 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using JetBrains.Annotations; +using UnityEngine; +using UnityEngine.Events; +using UnityEngine.EventSystems; +using UnityEngine.UI; +using UnityEngine.Video; +#if VRC_SDK_VRCSDK3 +using VRC.SDK3.Components; +using TMPro; +#endif +#if UDON +using VRC.Udon; + +#endif +#if !VRC_CLIENT && UNITY_EDITOR && VRC_SDK_VRCSDK3 +using UnityEditor; +using UnityEngine.SceneManagement; +#endif + +namespace VRC.Core +{ + public static class UnityEventFilter + { + // These types are will always be prohibited even if they are derived from an allowed type. + private static readonly HashSet<Type> _prohibitedUIEventTargetTypes = new HashSet<Type> + { + #if VRC_CLIENT + typeof(RenderHeads.Media.AVProVideo.MediaPlayer), + #endif + #if VRC_SDK_VRCSDK3 + typeof(VRCUrlInputField), + #endif + typeof(VideoPlayer) + }; + + private static readonly Lazy<Dictionary<Type, AllowedMethodFilter>> _allowedUnityEventTargetTypes = + new Lazy<Dictionary<Type, AllowedMethodFilter>>(GetRuntimeUnityEventTargetAccessFilterDictionary); + + private static Dictionary<Type, AllowedMethodFilter> AllowedUnityEventTargetTypes => _allowedUnityEventTargetTypes.Value; + + private static readonly Lazy<int> _debugLevel = new Lazy<int>(InitializeLogging); + private static int DebugLevel => _debugLevel.Value; + + // Builds a HashSet of allowed types, and their derived types, and removes explicitly prohibited types. + private static Dictionary<Type, AllowedMethodFilter> GetRuntimeUnityEventTargetAccessFilterDictionary() + { + Dictionary<Type, AllowedMethodFilter> accessFilterDictionary = new Dictionary<Type, AllowedMethodFilter>(_initialTargetAccessFilters); + AddDerivedTypes(accessFilterDictionary); + RemoveProhibitedTypes(accessFilterDictionary); + + #if VERBOSE_EVENT_SANITIZATION_LOGGING + StringBuilder stringBuilder = new StringBuilder(); + foreach(KeyValuePair<Type, AllowedMethodFilter> entry in accessFilterDictionary) + { + stringBuilder.AppendLine(entry.Key.FullName); + AllowedMethodFilter targetMethodAccessFilter = entry.Value; + foreach(string targetMethod in targetMethodAccessFilter.GetTargetMethodNames()) + { + stringBuilder.AppendLine($" {targetMethod}"); + } + + stringBuilder.AppendLine(); + } + + VerboseLog(stringBuilder.ToString()); + #endif + + return accessFilterDictionary; + } + + #if !VRC_CLIENT && UNITY_EDITOR && VRC_SDK_VRCSDK3 + [RuntimeInitializeOnLoadMethod] + private static void SetupPlayMode() + { + EditorApplication.playModeStateChanged += RunFilteringOnPlayModeEntry; + } + + private static void RunFilteringOnPlayModeEntry(PlayModeStateChange playModeStateChange) + { + switch(playModeStateChange) + { + case PlayModeStateChange.EnteredPlayMode: + { + for(int sceneIndex = 0; sceneIndex < SceneManager.sceneCount; sceneIndex++) + { + Scene currentScene = SceneManager.GetSceneAt(sceneIndex); + List<GameObject> rootGameObjects = new List<GameObject>(); + currentScene.GetRootGameObjects(rootGameObjects); + + FilterEvents(rootGameObjects); + } + + break; + } + case PlayModeStateChange.EnteredEditMode: + case PlayModeStateChange.ExitingEditMode: + case PlayModeStateChange.ExitingPlayMode: + { + return; + } + default: + { + throw new ArgumentOutOfRangeException(nameof(playModeStateChange), playModeStateChange, null); + } + } + } + #endif + + private static int InitializeLogging() + { + int hashCode = typeof(UnityEventFilter).GetHashCode(); + Logger.DescribeDebugLevel(hashCode, "UnityEventFilter", Logger.Color.red); + Logger.AddDebugLevel(hashCode); + return hashCode; + } + + [PublicAPI] + public static void FilterEvents(GameObject gameObject) + { + FilterUIEvents(gameObject); + FilterEventTriggerEvents(gameObject); + FilterAnimatorEvents(gameObject); + } + + [PublicAPI] + public static void FilterEvents(List<GameObject> gameObjects) + { + FilterUIEvents(gameObjects); + FilterEventTriggerEvents(gameObjects); + FilterAnimatorEvents(gameObjects); + } + + [PublicAPI] + public static void FilterUIEvents(GameObject gameObject) + { + List<UIBehaviour> uiBehaviours = new List<UIBehaviour>(); + gameObject.GetComponentsInChildren(true, uiBehaviours); + + FilterUIBehaviourEvents(uiBehaviours); + } + + [PublicAPI] + public static void FilterUIEvents(List<GameObject> gameObjects) + { + HashSet<UIBehaviour> uiBehaviours = new HashSet<UIBehaviour>(); + List<UIBehaviour> uiBehavioursWorkingList = new List<UIBehaviour>(); + foreach(GameObject gameObject in gameObjects) + { + gameObject.GetComponentsInChildren(true, uiBehavioursWorkingList); + uiBehaviours.UnionWith(uiBehavioursWorkingList); + } + + FilterUIBehaviourEvents(uiBehaviours); + } + + [PublicAPI] + public static void FilterEventTriggerEvents(GameObject gameObject) + { + List<EventTrigger> eventTriggers = new List<EventTrigger>(); + gameObject.GetComponentsInChildren(true, eventTriggers); + + FilterEventTriggerEvents(eventTriggers); + } + + [PublicAPI] + public static void FilterEventTriggerEvents(List<GameObject> gameObjects) + { + HashSet<EventTrigger> eventTriggers = new HashSet<EventTrigger>(); + List<EventTrigger> eventTriggerWorkingList = new List<EventTrigger>(); + foreach(GameObject gameObject in gameObjects) + { + gameObject.GetComponentsInChildren(true, eventTriggerWorkingList); + eventTriggers.UnionWith(eventTriggerWorkingList); + } + + FilterEventTriggerEvents(eventTriggers); + } + + [PublicAPI] + public static void FilterAnimatorEvents(GameObject gameObject) + { + List<Animator> animators = new List<Animator>(); + gameObject.GetComponentsInChildren(true, animators); + + FilterAnimatorEvents(animators); + } + + [PublicAPI] + public static void FilterAnimatorEvents(List<GameObject> gameObjects) + { + HashSet<Animator> animators = new HashSet<Animator>(); + List<Animator> animatorsWorkingList = new List<Animator>(); + foreach(GameObject gameObject in gameObjects) + { + gameObject.GetComponentsInChildren(true, animatorsWorkingList); + animators.UnionWith(animatorsWorkingList); + } + + FilterAnimatorEvents(animators); + } + + private static void FilterUIBehaviourEvents(IEnumerable<UIBehaviour> uiBehaviours) + { + Dictionary<Type, List<UIBehaviour>> uiBehavioursByType = new Dictionary<Type, List<UIBehaviour>>(); + foreach(UIBehaviour uiBehaviour in uiBehaviours) + { + if(uiBehaviour == null) + { + continue; + } + + Type uiBehaviourType = uiBehaviour.GetType(); + if(!uiBehavioursByType.TryGetValue(uiBehaviourType, out List<UIBehaviour> uiBehavioursOfType)) + { + uiBehavioursByType.Add(uiBehaviourType, new List<UIBehaviour> {uiBehaviour}); + continue; + } + + uiBehavioursOfType.Add(uiBehaviour); + } + + foreach(KeyValuePair<Type, List<UIBehaviour>> uiBehavioursOfTypeKvp in uiBehavioursByType) + { + Type uiBehaviourType = uiBehavioursOfTypeKvp.Key; + FieldInfo[] fieldInfos = uiBehaviourType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy); + List<FieldInfo> unityEventFieldInfos = new List<FieldInfo>(); + foreach(FieldInfo fieldInfo in fieldInfos) + { + if(typeof(UnityEventBase).IsAssignableFrom(fieldInfo.FieldType)) + { + unityEventFieldInfos.Add(fieldInfo); + } + } + + if(unityEventFieldInfos.Count <= 0) + { + continue; + } + + FieldInfo persistentCallsGroupFieldInfo = typeof(UnityEventBase).GetField("m_PersistentCalls", BindingFlags.Instance | BindingFlags.NonPublic); + if(persistentCallsGroupFieldInfo == null) + { + VerboseLog($"Could not find 'm_PersistentCalls' on UnityEventBase."); + return; + } + + foreach(UIBehaviour uiBehaviour in uiBehavioursOfTypeKvp.Value) + { + VerboseLog($"Checking '{uiBehaviour.name} for UI Events.", uiBehaviour); + foreach(FieldInfo unityEventFieldInfo in unityEventFieldInfos) + { + VerboseLog($"Checking field '{unityEventFieldInfo.Name}' on '{uiBehaviour.name}.", uiBehaviour); + UnityEventBase unityEventBase = unityEventFieldInfo.GetValue(uiBehaviour) as UnityEventBase; + if(unityEventBase == null) + { + VerboseLog($"Null '{unityEventFieldInfo.Name}' UnityEvent on {uiBehaviour.name}.", uiBehaviour); + continue; + } + + int numEventListeners = unityEventBase.GetPersistentEventCount(); + VerboseLog($"There are '{numEventListeners}' on event '{unityEventFieldInfo.Name}' on '{uiBehaviour.name}.", uiBehaviour); + for(int index = 0; index < numEventListeners; index++) + { + string persistentMethodName = unityEventBase.GetPersistentMethodName(index); + + UnityEngine.Object persistentTarget = unityEventBase.GetPersistentTarget(index); + if(persistentTarget == null) + { + VerboseLog($"The target for listener '{index}' on event '{unityEventFieldInfo.Name}' on '{uiBehaviour.name} is null.", uiBehaviour); + continue; + } + + if(IsTargetPermitted(persistentTarget, persistentMethodName)) + { + VerboseLog( + $"Allowing event '{unityEventFieldInfo.Name}' on '{uiBehaviour.name}' to call '{persistentMethodName}' on target '{persistentTarget.name}'.", + uiBehaviour); + + continue; + } + + LogRemoval( + $"Events on '{uiBehaviour.name}' were removed because one of them targeted a prohibited type '{persistentTarget.GetType().Name}', method '{persistentMethodName}' or object '{persistentTarget.name}'.", + uiBehaviour); + + unityEventFieldInfo.SetValue(uiBehaviour, Activator.CreateInstance(unityEventBase.GetType())); + break; + } + } + } + } + } + + private static void FilterEventTriggerEvents(IEnumerable<EventTrigger> eventTriggers) + { + FieldInfo persistentCallsGroupFieldInfo = typeof(UnityEventBase).GetField("m_PersistentCalls", BindingFlags.Instance | BindingFlags.NonPublic); + if(persistentCallsGroupFieldInfo == null) + { + VerboseLog($"Could not find 'm_PersistentCalls' on UnityEventBase."); + return; + } + + foreach(EventTrigger eventTrigger in eventTriggers) + { + VerboseLog($"Checking '{eventTrigger.name} for Unity Events.", eventTrigger); + + List<EventTrigger.Entry> triggers = eventTrigger.triggers; + if(triggers.Count <= 0) + { + continue; + } + + for(int i = triggers.Count - 1; i >= 0; i--) + { + EventTrigger.Entry entry = triggers[i]; + UnityEventBase unityEventBase = entry.callback; + if(unityEventBase == null) + { + VerboseLog($"Null '{entry.eventID}' UnityEvent on {eventTrigger.name}.", eventTrigger); + continue; + } + + int numEventListeners = unityEventBase.GetPersistentEventCount(); + VerboseLog($"There are '{numEventListeners}' on event '{entry.eventID}' on '{eventTrigger.name}.", eventTrigger); + for(int index = 0; index < numEventListeners; index++) + { + string persistentMethodName = unityEventBase.GetPersistentMethodName(index); + + UnityEngine.Object persistentTarget = unityEventBase.GetPersistentTarget(index); + if(persistentTarget == null) + { + VerboseLog($"The target for listener '{index}' on event '{entry.eventID}' on '{eventTrigger.name} is null.", eventTrigger); + continue; + } + + if(IsTargetPermitted(persistentTarget, persistentMethodName)) + { + VerboseLog( + $"Allowing event '{entry.eventID}' on '{eventTrigger.name}' to call '{persistentMethodName}' on target '{persistentTarget.name}'.", + eventTrigger); + + continue; + } + + LogRemoval( + $"Events on '{eventTrigger.name}' were removed because one of them targeted a prohibited type '{persistentTarget.GetType().Name}', method '{persistentMethodName}' or object '{persistentTarget.name}'.", + eventTrigger); + + triggers.RemoveAt(i); + break; + } + } + } + } + + private static void FilterAnimatorEvents(IEnumerable<Animator> animators) + { + foreach(Animator animator in animators) + { + if(animator == null) + { + continue; + } + + RuntimeAnimatorController animatorController = animator.runtimeAnimatorController; + if(animatorController == null) + { + return; + } + + foreach(AnimationClip animationClip in animatorController.animationClips) + { + if(animationClip == null) + { + continue; + } + + foreach(AnimationEvent animationEvent in animationClip.events) + { + if(animationEvent == null) + { + continue; + } + + string animationEventFunctionName = animationEvent.functionName; + if(_allowedAnimationEventFunctionNames.Contains(animationEventFunctionName)) + { + continue; + } + + animationClip.events = null; + LogRemoval( + $"Removed AnimationEvents from AnimationClip used by the Animator on '{animator.gameObject}' because the event targets '{animationEventFunctionName}' which is not allowed."); + + break; + } + } + } + } + + [Conditional("VERBOSE_EVENT_SANITIZATION_LOGGING")] + private static void VerboseLog(string message, UnityEngine.Object target = null) + { + Logger.LogWarning(message, DebugLevel, target); + } + + private static void LogRemoval(string message, UnityEngine.Object target = null) + { + Logger.LogWarning(message, DebugLevel, target); + } + + private static bool IsTargetPermitted(UnityEngine.Object target, string targetMethod) + { + // Block anything blacklisted by Udon to prevent UnityEvents from being used to bypass the blacklist. + // NOTE: This will only block events targeting objects that are blacklisted before the UnityEventSanitizer is run. + // If objects are added to the blacklist after scene loading has finished it will be necessary to re-run the UnityEventSanitizer. + #if UDON + if(UdonManager.Instance.IsBlacklisted(target)) + { + return false; + } + #endif + + Type persistentTargetType = target.GetType(); + if(!AllowedUnityEventTargetTypes.TryGetValue(persistentTargetType, out AllowedMethodFilter accessFilter)) + { + return false; + } + + return accessFilter.IsTargetMethodAllowed(targetMethod); + } + + // Adds types derived from whitelisted types. + private static void AddDerivedTypes(Dictionary<Type, AllowedMethodFilter> accessFilterDictionary) + { + Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); + foreach(Assembly assembly in assemblies) + { + foreach(Type type in assembly.GetTypes()) + { + if(accessFilterDictionary.ContainsKey(type)) + { + continue; + } + + if(!typeof(Component).IsAssignableFrom(type)) + { + continue; + } + + Type currentType = type; + while(currentType != typeof(object) && currentType != null) + { + if(accessFilterDictionary.TryGetValue(currentType, out AllowedMethodFilter accessFilter)) + { + accessFilterDictionary.Add(type, accessFilter); + break; + } + + currentType = currentType.BaseType; + } + } + } + } + + // Removes prohibited types and types derived from them. + private static void RemoveProhibitedTypes(Dictionary<Type, AllowedMethodFilter> accessFilterDictionary) + { + foreach(Type prohibitedType in _prohibitedUIEventTargetTypes) + { + foreach(Type accessFilterType in accessFilterDictionary.Keys.ToArray()) + { + if(prohibitedType.IsAssignableFrom(accessFilterType)) + { + accessFilterDictionary.Remove(accessFilterType); + } + } + } + } + + private static readonly Dictionary<Type, AllowedMethodFilter> _initialTargetAccessFilters = new Dictionary<Type, AllowedMethodFilter> + { + { + typeof(GameObject), new AllowedMethodFilter( + new List<string> + { + nameof(GameObject.SetActive) + }, + new List<string>()) + }, + { + typeof(AudioSource), + new AllowedMethodFilter( + new List<string> + { + nameof(AudioSource.Pause), + nameof(AudioSource.Play), + nameof(AudioSource.PlayDelayed), + nameof(AudioSource.PlayOneShot), + nameof(AudioSource.Stop), + nameof(AudioSource.UnPause) + }, + new List<string> + { + nameof(AudioSource.bypassEffects), + nameof(AudioSource.bypassListenerEffects), + nameof(AudioSource.bypassReverbZones), + nameof(AudioSource.dopplerLevel), + nameof(AudioSource.enabled), + nameof(AudioSource.loop), + nameof(AudioSource.maxDistance), + nameof(AudioSource.rolloffMode), + nameof(AudioSource.minDistance), + nameof(AudioSource.mute), + nameof(AudioSource.pitch), + nameof(AudioSource.playOnAwake), + nameof(AudioSource.priority), + nameof(AudioSource.spatialize), + nameof(AudioSource.spread), + nameof(AudioSource.time), + nameof(AudioSource.volume) + } + ) + }, + { + typeof(AudioDistortionFilter), new AllowedMethodFilter( + new List<string>(), + new List<string> + { + nameof(AudioDistortionFilter.distortionLevel), + nameof(AudioDistortionFilter.enabled) + }) + }, + { + typeof(AudioEchoFilter), new AllowedMethodFilter( + new List<string>(), + new List<string> + { + nameof(AudioEchoFilter.decayRatio), + nameof(AudioEchoFilter.delay), + nameof(AudioEchoFilter.dryMix), + nameof(AudioEchoFilter.enabled), + nameof(AudioEchoFilter.wetMix) + }) + }, + { + typeof(AudioHighPassFilter), new AllowedMethodFilter( + new List<string>(), + new List<string> + { + nameof(AudioHighPassFilter.cutoffFrequency), + nameof(AudioHighPassFilter.enabled), + nameof(AudioHighPassFilter.highpassResonanceQ) + }) + }, + { + typeof(AudioLowPassFilter), new AllowedMethodFilter( + new List<string>(), + new List<string> + { + nameof(AudioLowPassFilter.cutoffFrequency), + nameof(AudioLowPassFilter.enabled), + nameof(AudioLowPassFilter.lowpassResonanceQ) + }) + }, + { + typeof(AudioReverbFilter), new AllowedMethodFilter( + new List<string>(), + new List<string> + { + nameof(AudioReverbFilter.decayHFRatio), + nameof(AudioReverbFilter.decayTime), + nameof(AudioReverbFilter.density), + nameof(AudioReverbFilter.diffusion), + nameof(AudioReverbFilter.dryLevel), + nameof(AudioReverbFilter.enabled), + nameof(AudioReverbFilter.hfReference), + nameof(AudioReverbFilter.reflectionsDelay), + nameof(AudioReverbFilter.reflectionsLevel), + nameof(AudioReverbFilter.reverbDelay), + nameof(AudioReverbFilter.reverbLevel), + nameof(AudioReverbFilter.room), + nameof(AudioReverbFilter.roomHF), + nameof(AudioReverbFilter.roomLF) + }) + }, + { + typeof(AudioReverbZone), new AllowedMethodFilter( + new List<string>(), + new List<string> + { + nameof(AudioReverbZone.decayHFRatio), + nameof(AudioReverbZone.decayTime), + nameof(AudioReverbZone.density), + nameof(AudioReverbZone.diffusion), + nameof(AudioReverbZone.enabled), + nameof(AudioReverbZone.HFReference), + nameof(AudioReverbZone.LFReference), + nameof(AudioReverbZone.maxDistance), + nameof(AudioReverbZone.minDistance), + nameof(AudioReverbZone.reflections), + nameof(AudioReverbZone.reflectionsDelay), + nameof(AudioReverbZone.room), + nameof(AudioReverbZone.roomHF), + nameof(AudioReverbZone.roomLF) + }) + }, + #if UDON + { + typeof(UdonBehaviour), new AllowedMethodFilter( + new List<string> + { + nameof(UdonBehaviour.RunProgram), + nameof(UdonBehaviour.SendCustomEvent), + nameof(UdonBehaviour.Interact), + }, + new List<string>() + { + nameof(UdonBehaviour.enabled) + }) + }, + #endif + { + typeof(MeshRenderer), new AllowedMethodFilter( + new List<string>(), + new List<string> + { + nameof(MeshRenderer.shadowCastingMode), + nameof(MeshRenderer.enabled), + nameof(MeshRenderer.probeAnchor), + nameof(MeshRenderer.probeAnchor), + nameof(MeshRenderer.receiveShadows), + nameof(MeshRenderer.lightProbeUsage) + }) + }, + { + typeof(Collider), new AllowedMethodFilter( + new List<string>(), + new List<string> + { + nameof(Collider.enabled), + nameof(Collider.isTrigger) + }) + }, + { + typeof(SkinnedMeshRenderer), new AllowedMethodFilter( + new List<string>(), + new List<string> + { + nameof(SkinnedMeshRenderer.allowOcclusionWhenDynamic), + nameof(SkinnedMeshRenderer.shadowCastingMode), + nameof(SkinnedMeshRenderer.enabled), + nameof(SkinnedMeshRenderer.lightProbeProxyVolumeOverride), + nameof(SkinnedMeshRenderer.motionVectorGenerationMode), + nameof(SkinnedMeshRenderer.probeAnchor), + nameof(SkinnedMeshRenderer.receiveShadows), + nameof(SkinnedMeshRenderer.rootBone), + nameof(SkinnedMeshRenderer.skinnedMotionVectors), + nameof(SkinnedMeshRenderer.updateWhenOffscreen), + nameof(SkinnedMeshRenderer.lightProbeUsage) + }) + }, + { + typeof(Light), new AllowedMethodFilter( + new List<string> + { + nameof(Light.Reset) + }, + new List<string> + { + nameof(Light.bounceIntensity), + nameof(Light.colorTemperature), + nameof(Light.cookie), + nameof(Light.enabled), + nameof(Light.intensity), + nameof(Light.range), + nameof(Light.shadowBias), + nameof(Light.shadowNearPlane), + nameof(Light.shadowNormalBias), + nameof(Light.shadowStrength), + nameof(Light.spotAngle) + }) + }, + { + typeof(ParticleSystem), new AllowedMethodFilter( + new List<string> + { + nameof(ParticleSystem.Clear), + nameof(ParticleSystem.Emit), + nameof(ParticleSystem.Pause), + nameof(ParticleSystem.Pause), + nameof(ParticleSystem.Play), + nameof(ParticleSystem.Simulate), + nameof(ParticleSystem.Stop), + nameof(ParticleSystem.Stop), + nameof(ParticleSystem.TriggerSubEmitter) + }, + new List<string> + { + nameof(ParticleSystem.time), + nameof(ParticleSystem.useAutoRandomSeed) + }) + }, + { + typeof(ParticleSystemForceField), new AllowedMethodFilter( + new List<string>(), + new List<string> + { + nameof(ParticleSystemForceField.endRange), + nameof(ParticleSystemForceField.gravityFocus), + nameof(ParticleSystemForceField.length), + nameof(ParticleSystemForceField.multiplyDragByParticleSize), + nameof(ParticleSystemForceField.multiplyDragByParticleVelocity), + nameof(ParticleSystemForceField.startRange) + }) + }, + { + typeof(Projector), new AllowedMethodFilter( + new List<string>(), + new List<string> + { + nameof(Projector.aspectRatio), + nameof(Projector.enabled), + nameof(Projector.nearClipPlane), + nameof(Projector.farClipPlane), + nameof(Projector.fieldOfView), + nameof(Projector.orthographic), + nameof(Projector.orthographicSize) + }) + }, + { + typeof(LineRenderer), new AllowedMethodFilter( + new List<string>(), + new List<string> + { + nameof(LineRenderer.allowOcclusionWhenDynamic), + nameof(LineRenderer.shadowCastingMode), + nameof(LineRenderer.enabled), + nameof(LineRenderer.endWidth), + nameof(LineRenderer.loop), + nameof(LineRenderer.motionVectorGenerationMode), + nameof(LineRenderer.numCapVertices), + nameof(LineRenderer.numCornerVertices), + nameof(LineRenderer.probeAnchor), + nameof(LineRenderer.receiveShadows), + nameof(LineRenderer.shadowBias), + nameof(LineRenderer.startWidth), + nameof(LineRenderer.lightProbeUsage), + nameof(LineRenderer.useWorldSpace), + nameof(LineRenderer.widthMultiplier) + }) + }, + { + typeof(TrailRenderer), new AllowedMethodFilter( + new List<string> + { + nameof(TrailRenderer.Clear) + }, + new List<string> + { + nameof(TrailRenderer.allowOcclusionWhenDynamic), + nameof(TrailRenderer.autodestruct), + nameof(TrailRenderer.shadowCastingMode), + nameof(TrailRenderer.enabled), + nameof(TrailRenderer.emitting), + nameof(TrailRenderer.endWidth), + nameof(TrailRenderer.motionVectorGenerationMode), + nameof(TrailRenderer.numCapVertices), + nameof(TrailRenderer.numCornerVertices), + nameof(TrailRenderer.probeAnchor), + nameof(TrailRenderer.receiveShadows), + nameof(TrailRenderer.shadowBias), + nameof(TrailRenderer.startWidth), + nameof(TrailRenderer.lightProbeUsage), + nameof(TrailRenderer.widthMultiplier) + }) + }, + { + typeof(Animator), new AllowedMethodFilter( + new List<string> + { + nameof(Animator.Play), + nameof(Animator.PlayInFixedTime), + nameof(Animator.Rebind), + nameof(Animator.SetBool), + nameof(Animator.SetFloat), + nameof(Animator.SetInteger), + nameof(Animator.SetTrigger), + nameof(Animator.ResetTrigger) + }, + new List<string> + { + nameof(Animator.speed), + nameof(Animator.enabled) + }) + }, + { + typeof(Text), new AllowedMethodFilter( + new List<string>(), + new List<string> + { + nameof(Text.alignByGeometry), + nameof(Text.enabled), + nameof(Text.fontSize), + nameof(Text.lineSpacing), + nameof(Text.maskable), + nameof(Text.raycastTarget), + nameof(Text.resizeTextForBestFit), + nameof(Text.resizeTextMaxSize), + nameof(Text.resizeTextMinSize), + nameof(Text.supportRichText), + nameof(Text.text) + }) + }, + { + typeof(Image), new AllowedMethodFilter( + new List<string>(), + new List<string> + { + nameof(Image.alphaHitTestMinimumThreshold), + nameof(Image.enabled), + nameof(Image.fillAmount), + nameof(Image.fillCenter), + nameof(Image.fillClockwise), + nameof(Image.fillOrigin), + nameof(Image.maskable), + nameof(Image.preserveAspect), + nameof(Image.raycastTarget), + nameof(Image.useSpriteMesh) + }) + }, + { + typeof(RawImage), new AllowedMethodFilter( + new List<string>(), + new List<string> + { + nameof(RawImage.enabled), + nameof(RawImage.maskable), + nameof(RawImage.raycastTarget) + }) + }, + { + typeof(InputField), new AllowedMethodFilter( + new List<string> + { + "Append", + nameof(InputField.ForceLabelUpdate) + }, + new List<string> + { + nameof(InputField.caretBlinkRate), + nameof(InputField.caretPosition), + nameof(InputField.caretWidth), + nameof(InputField.characterLimit), + nameof(InputField.customCaretColor), + nameof(InputField.enabled), + nameof(InputField.interactable), + nameof(InputField.readOnly), + nameof(InputField.selectionAnchorPosition), + nameof(InputField.text), + nameof(InputField.textComponent), + nameof(InputField.selectionFocusPosition) + }) + }, + { + typeof(Dropdown), new AllowedMethodFilter( + new List<string>(), + new List<string> + { + nameof(Dropdown.captionText), + nameof(Dropdown.enabled), + nameof(Dropdown.interactable), + nameof(Dropdown.itemText), + nameof(Dropdown.targetGraphic), + nameof(Dropdown.template), + nameof(Dropdown.value) + }) + }, + { + typeof(Slider), new AllowedMethodFilter( + new List<string>(), + new List<string> + { + nameof(Slider.enabled), + nameof(Slider.fillRect), + nameof(Slider.handleRect), + nameof(Slider.interactable), + nameof(Slider.maxValue), + nameof(Slider.minValue), + nameof(Slider.normalizedValue), + nameof(Slider.targetGraphic), + nameof(Slider.value), + nameof(Slider.wholeNumbers) + }) + }, + { + typeof(Toggle), new AllowedMethodFilter( + new List<string>(), + new List<string> + { + nameof(Toggle.enabled), + nameof(Toggle.group), + nameof(Toggle.interactable), + nameof(Toggle.isOn), + nameof(Toggle.targetGraphic) + }) + }, + { + typeof(Scrollbar), new AllowedMethodFilter( + new List<string>(), + new List<string> + { + nameof(Scrollbar.enabled), + nameof(Scrollbar.handleRect), + nameof(Scrollbar.interactable), + nameof(Scrollbar.numberOfSteps), + nameof(Scrollbar.size), + nameof(Scrollbar.targetGraphic), + nameof(Scrollbar.value) + }) + }, + { + typeof(ScrollRect), new AllowedMethodFilter( + new List<string>(), + new List<string> + { + nameof(ScrollRect.content), + nameof(ScrollRect.decelerationRate), + nameof(ScrollRect.elasticity), + nameof(ScrollRect.enabled), + nameof(ScrollRect.horizontal), + nameof(ScrollRect.horizontalNormalizedPosition), + nameof(ScrollRect.horizontalScrollbar), + nameof(ScrollRect.horizontalScrollbarSpacing), + nameof(ScrollRect.inertia), + nameof(ScrollRect.scrollSensitivity), + nameof(ScrollRect.vertical), + nameof(ScrollRect.verticalNormalizedPosition), + nameof(ScrollRect.verticalScrollbar), + nameof(ScrollRect.verticalScrollbarSpacing), + nameof(ScrollRect.viewport) + }) + }, + { + typeof(Button), new AllowedMethodFilter( + new List<string>(), + new List<string> + { + nameof(Button.enabled), + nameof(Button.interactable), + nameof(Button.targetGraphic) + }) + }, + { + typeof(Mask), new AllowedMethodFilter( + new List<string>(), + new List<string> + { + nameof(Mask.enabled), + nameof(Mask.showMaskGraphic) + }) + }, + { + typeof(RectMask2D), new AllowedMethodFilter( + new List<string>(), + new List<string> + { + nameof(RectMask2D.enabled) + }) + }, + { + typeof(Selectable), new AllowedMethodFilter( + new List<string>(), + new List<string> + { + nameof(Selectable.enabled), + nameof(Selectable.interactable), + nameof(Selectable.targetGraphic) + }) + }, + { + typeof(ToggleGroup), new AllowedMethodFilter( + new List<string>(), + new List<string> + { + nameof(ToggleGroup.allowSwitchOff), + nameof(ToggleGroup.enabled) + }) + }, + #if VRC_SDK_VRCSDK3 // only access Cinemachine and TMPro after install + { + typeof(TextMeshPro), new AllowedMethodFilter( + new List<string>(), + new List<string> + { + nameof(TextMeshPro.text), + }) + }, + { + typeof(TextMeshProUGUI), new AllowedMethodFilter( + new List<string>(), + new List<string> + { + nameof(TextMeshProUGUI.text), + }) + }, + { + typeof(Cinemachine.CinemachineVirtualCamera), new AllowedMethodFilter( + new List<string>() + { + nameof(Cinemachine.CinemachineVirtualCamera.Priority), + }, + new List<string>()) + }, + #endif + }; + + private static readonly HashSet<string> _allowedAnimationEventFunctionNames = new HashSet<string> + { + "RunProgram", + "SendCustomEvent", + "Play", + "Pause", + "Stop", + "PlayInFixedTime", + "Rebind", + "SetBool", + "SetFloat", + "SetInteger", + "SetTrigger", + "ResetTrigger", + "SetActive" + }; + + private class AllowedMethodFilter + { + private readonly HashSet<string> _allowedTargets; + + [PublicAPI] + public AllowedMethodFilter(List<string> allowedTargetMethodNames, List<string> allowedTargetPropertyNames) + { + _allowedTargets = new HashSet<string>(); + _allowedTargets.UnionWith(allowedTargetMethodNames); + foreach(string allowedTargetProperty in allowedTargetPropertyNames) + { + _allowedTargets.Add($"get_{allowedTargetProperty}"); + _allowedTargets.Add($"set_{allowedTargetProperty}"); + } + } + + public bool IsTargetMethodAllowed(string targetMethodName) + { + return _allowedTargets.Contains(targetMethodName); + } + + #if VERBOSE_EVENT_SANITIZATION_LOGGING + public List<string> GetTargetMethodNames() + { + return _allowedTargets.ToList(); + } + #endif + } + } +} diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/UnityEventFilter.cs.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/UnityEventFilter.cs.meta new file mode 100644 index 00000000..5137d5fe --- /dev/null +++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/UnityEventFilter.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 87f13d6a98e54fb78ca47650a29029a5 +timeCreated: 1594690552
\ No newline at end of file |