diff --git a/Assets/LUXPiano.meta b/Assets/LUXPiano.meta
new file mode 100644
index 0000000..2a4df58
--- /dev/null
+++ b/Assets/LUXPiano.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: f3012d801cb94df46953c3c3552d01ee
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/LUXPiano/Art.meta b/Assets/LUXPiano/Art.meta
new file mode 100644
index 0000000..c4308b0
--- /dev/null
+++ b/Assets/LUXPiano/Art.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: b7e15f73887071743b53ca20adbfd493
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/LUXPiano/Art/Black Key.mat b/Assets/LUXPiano/Art/Black Key.mat
new file mode 100644
index 0000000..f40c3d8
--- /dev/null
+++ b/Assets/LUXPiano/Art/Black Key.mat
@@ -0,0 +1,76 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+ serializedVersion: 6
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_Name: Black Key
+ m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
+ m_ShaderKeywords:
+ m_LightmapFlags: 4
+ m_EnableInstancingVariants: 0
+ m_DoubleSidedGI: 0
+ m_CustomRenderQueue: -1
+ stringTagMap: {}
+ disabledShaderPasses: []
+ m_SavedProperties:
+ serializedVersion: 3
+ m_TexEnvs:
+ - _BumpMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailAlbedoMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailMask:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailNormalMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _EmissionMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _MainTex:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _MetallicGlossMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _OcclusionMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _ParallaxMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ m_Floats:
+ - _BumpScale: 1
+ - _Cutoff: 0.5
+ - _DetailNormalMapScale: 1
+ - _DstBlend: 0
+ - _GlossMapScale: 1
+ - _Glossiness: 0.5
+ - _GlossyReflections: 1
+ - _Metallic: 0
+ - _Mode: 0
+ - _OcclusionStrength: 1
+ - _Parallax: 0.02
+ - _SmoothnessTextureChannel: 0
+ - _SpecularHighlights: 1
+ - _SrcBlend: 1
+ - _UVSec: 0
+ - _ZWrite: 1
+ m_Colors:
+ - _Color: {r: 0.122641504, g: 0.122641504, b: 0.122641504, a: 1}
+ - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
diff --git a/Assets/LUXPiano/Art/Black Key.mat.meta b/Assets/LUXPiano/Art/Black Key.mat.meta
new file mode 100644
index 0000000..e45e184
--- /dev/null
+++ b/Assets/LUXPiano/Art/Black Key.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: d6d5818e4a8fcec48a4cf66657ee4b35
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 2100000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/LUXPiano/Art/White Key.mat b/Assets/LUXPiano/Art/White Key.mat
new file mode 100644
index 0000000..0a429f5
--- /dev/null
+++ b/Assets/LUXPiano/Art/White Key.mat
@@ -0,0 +1,76 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+ serializedVersion: 6
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_Name: White Key
+ m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
+ m_ShaderKeywords:
+ m_LightmapFlags: 4
+ m_EnableInstancingVariants: 0
+ m_DoubleSidedGI: 0
+ m_CustomRenderQueue: -1
+ stringTagMap: {}
+ disabledShaderPasses: []
+ m_SavedProperties:
+ serializedVersion: 3
+ m_TexEnvs:
+ - _BumpMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailAlbedoMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailMask:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailNormalMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _EmissionMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _MainTex:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _MetallicGlossMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _OcclusionMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _ParallaxMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ m_Floats:
+ - _BumpScale: 1
+ - _Cutoff: 0.5
+ - _DetailNormalMapScale: 1
+ - _DstBlend: 0
+ - _GlossMapScale: 1
+ - _Glossiness: 0.5
+ - _GlossyReflections: 1
+ - _Metallic: 0
+ - _Mode: 0
+ - _OcclusionStrength: 1
+ - _Parallax: 0.02
+ - _SmoothnessTextureChannel: 0
+ - _SpecularHighlights: 1
+ - _SrcBlend: 1
+ - _UVSec: 0
+ - _ZWrite: 1
+ m_Colors:
+ - _Color: {r: 1, g: 1, b: 1, a: 1}
+ - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
diff --git a/Assets/LUXPiano/Art/White Key.mat.meta b/Assets/LUXPiano/Art/White Key.mat.meta
new file mode 100644
index 0000000..d435b0d
--- /dev/null
+++ b/Assets/LUXPiano/Art/White Key.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 10a49312282010d4381356c509b81eb3
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 2100000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MidiJack.meta b/Assets/LUXPiano/MidiJack.meta
similarity index 100%
rename from Assets/MidiJack.meta
rename to Assets/LUXPiano/MidiJack.meta
diff --git a/Assets/MidiJack/Editor.meta b/Assets/LUXPiano/MidiJack/Editor.meta
similarity index 100%
rename from Assets/MidiJack/Editor.meta
rename to Assets/LUXPiano/MidiJack/Editor.meta
diff --git a/Assets/MidiJack/Editor/MidiJackWindow.cs b/Assets/LUXPiano/MidiJack/Editor/MidiJackWindow.cs
similarity index 100%
rename from Assets/MidiJack/Editor/MidiJackWindow.cs
rename to Assets/LUXPiano/MidiJack/Editor/MidiJackWindow.cs
diff --git a/Assets/MidiJack/Editor/MidiJackWindow.cs.meta b/Assets/LUXPiano/MidiJack/Editor/MidiJackWindow.cs.meta
similarity index 100%
rename from Assets/MidiJack/Editor/MidiJackWindow.cs.meta
rename to Assets/LUXPiano/MidiJack/Editor/MidiJackWindow.cs.meta
diff --git a/Assets/MidiJack/Midi.cs b/Assets/LUXPiano/MidiJack/Midi.cs
similarity index 100%
rename from Assets/MidiJack/Midi.cs
rename to Assets/LUXPiano/MidiJack/Midi.cs
diff --git a/Assets/MidiJack/Midi.cs.meta b/Assets/LUXPiano/MidiJack/Midi.cs.meta
similarity index 100%
rename from Assets/MidiJack/Midi.cs.meta
rename to Assets/LUXPiano/MidiJack/Midi.cs.meta
diff --git a/Assets/MidiJack/MidiDriver.cs b/Assets/LUXPiano/MidiJack/MidiDriver.cs
similarity index 100%
rename from Assets/MidiJack/MidiDriver.cs
rename to Assets/LUXPiano/MidiJack/MidiDriver.cs
diff --git a/Assets/MidiJack/MidiDriver.cs.meta b/Assets/LUXPiano/MidiJack/MidiDriver.cs.meta
similarity index 100%
rename from Assets/MidiJack/MidiDriver.cs.meta
rename to Assets/LUXPiano/MidiJack/MidiDriver.cs.meta
diff --git a/Assets/MidiJack/MidiMaster.cs b/Assets/LUXPiano/MidiJack/MidiMaster.cs
similarity index 100%
rename from Assets/MidiJack/MidiMaster.cs
rename to Assets/LUXPiano/MidiJack/MidiMaster.cs
diff --git a/Assets/MidiJack/MidiMaster.cs.meta b/Assets/LUXPiano/MidiJack/MidiMaster.cs.meta
similarity index 100%
rename from Assets/MidiJack/MidiMaster.cs.meta
rename to Assets/LUXPiano/MidiJack/MidiMaster.cs.meta
diff --git a/Assets/MidiJack/MidiStateUpdater.cs b/Assets/LUXPiano/MidiJack/MidiStateUpdater.cs
similarity index 100%
rename from Assets/MidiJack/MidiStateUpdater.cs
rename to Assets/LUXPiano/MidiJack/MidiStateUpdater.cs
diff --git a/Assets/MidiJack/MidiStateUpdater.cs.meta b/Assets/LUXPiano/MidiJack/MidiStateUpdater.cs.meta
similarity index 100%
rename from Assets/MidiJack/MidiStateUpdater.cs.meta
rename to Assets/LUXPiano/MidiJack/MidiStateUpdater.cs.meta
diff --git a/Assets/MidiJack/Plugins.meta b/Assets/LUXPiano/MidiJack/Plugins.meta
similarity index 100%
rename from Assets/MidiJack/Plugins.meta
rename to Assets/LUXPiano/MidiJack/Plugins.meta
diff --git a/Assets/MidiJack/Plugins/MidiJackPlugin.bundle.meta b/Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle.meta
similarity index 100%
rename from Assets/MidiJack/Plugins/MidiJackPlugin.bundle.meta
rename to Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle.meta
diff --git a/Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents.meta b/Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents.meta
similarity index 100%
rename from Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents.meta
rename to Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents.meta
diff --git a/Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Info.plist b/Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Info.plist
similarity index 100%
rename from Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Info.plist
rename to Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Info.plist
diff --git a/Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Info.plist.meta b/Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Info.plist.meta
similarity index 100%
rename from Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Info.plist.meta
rename to Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Info.plist.meta
diff --git a/Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/MacOS.meta b/Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/MacOS.meta
similarity index 100%
rename from Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/MacOS.meta
rename to Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/MacOS.meta
diff --git a/Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/MacOS/MidiJackPlugin b/Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/MacOS/MidiJackPlugin
similarity index 100%
rename from Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/MacOS/MidiJackPlugin
rename to Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/MacOS/MidiJackPlugin
diff --git a/Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/MacOS/MidiJackPlugin.meta b/Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/MacOS/MidiJackPlugin.meta
similarity index 100%
rename from Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/MacOS/MidiJackPlugin.meta
rename to Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/MacOS/MidiJackPlugin.meta
diff --git a/Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Resources.meta b/Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Resources.meta
similarity index 100%
rename from Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Resources.meta
rename to Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Resources.meta
diff --git a/Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Resources/en.lproj.meta b/Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Resources/en.lproj.meta
similarity index 100%
rename from Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Resources/en.lproj.meta
rename to Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Resources/en.lproj.meta
diff --git a/Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Resources/en.lproj/InfoPlist.strings b/Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Resources/en.lproj/InfoPlist.strings
similarity index 100%
rename from Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Resources/en.lproj/InfoPlist.strings
rename to Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Resources/en.lproj/InfoPlist.strings
diff --git a/Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Resources/en.lproj/InfoPlist.strings.meta b/Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Resources/en.lproj/InfoPlist.strings.meta
similarity index 100%
rename from Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Resources/en.lproj/InfoPlist.strings.meta
rename to Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Resources/en.lproj/InfoPlist.strings.meta
diff --git a/Assets/MidiJack/Plugins/x64.meta b/Assets/LUXPiano/MidiJack/Plugins/x64.meta
similarity index 100%
rename from Assets/MidiJack/Plugins/x64.meta
rename to Assets/LUXPiano/MidiJack/Plugins/x64.meta
diff --git a/Assets/MidiJack/Plugins/x64/MidiJackPlugin.dll b/Assets/LUXPiano/MidiJack/Plugins/x64/MidiJackPlugin.dll
similarity index 100%
rename from Assets/MidiJack/Plugins/x64/MidiJackPlugin.dll
rename to Assets/LUXPiano/MidiJack/Plugins/x64/MidiJackPlugin.dll
diff --git a/Assets/MidiJack/Plugins/x64/MidiJackPlugin.dll.meta b/Assets/LUXPiano/MidiJack/Plugins/x64/MidiJackPlugin.dll.meta
similarity index 100%
rename from Assets/MidiJack/Plugins/x64/MidiJackPlugin.dll.meta
rename to Assets/LUXPiano/MidiJack/Plugins/x64/MidiJackPlugin.dll.meta
diff --git a/Assets/MidiJack/Plugins/x86.meta b/Assets/LUXPiano/MidiJack/Plugins/x86.meta
similarity index 100%
rename from Assets/MidiJack/Plugins/x86.meta
rename to Assets/LUXPiano/MidiJack/Plugins/x86.meta
diff --git a/Assets/MidiJack/Plugins/x86/MidiJackPlugin.dll b/Assets/LUXPiano/MidiJack/Plugins/x86/MidiJackPlugin.dll
similarity index 100%
rename from Assets/MidiJack/Plugins/x86/MidiJackPlugin.dll
rename to Assets/LUXPiano/MidiJack/Plugins/x86/MidiJackPlugin.dll
diff --git a/Assets/MidiJack/Plugins/x86/MidiJackPlugin.dll.meta b/Assets/LUXPiano/MidiJack/Plugins/x86/MidiJackPlugin.dll.meta
similarity index 100%
rename from Assets/MidiJack/Plugins/x86/MidiJackPlugin.dll.meta
rename to Assets/LUXPiano/MidiJack/Plugins/x86/MidiJackPlugin.dll.meta
diff --git a/Assets/LUXPiano/Plugins.meta b/Assets/LUXPiano/Plugins.meta
new file mode 100644
index 0000000..611e67c
--- /dev/null
+++ b/Assets/LUXPiano/Plugins.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 74ab43bd66b4741499c17f218d56c5f3
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/LUXPiano/Plugins/Editor.meta b/Assets/LUXPiano/Plugins/Editor.meta
new file mode 100644
index 0000000..0d148fe
--- /dev/null
+++ b/Assets/LUXPiano/Plugins/Editor.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 95004c6a58042874e98835292d89675a
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/LUXPiano/Plugins/Editor/JetBrains.meta b/Assets/LUXPiano/Plugins/Editor/JetBrains.meta
new file mode 100644
index 0000000..b4cab5e
--- /dev/null
+++ b/Assets/LUXPiano/Plugins/Editor/JetBrains.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 61a19c79d2c13dc41b1c275ab74ef889
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/LUXPiano/Plugins/Editor/JetBrains/Unity3DRider.cs b/Assets/LUXPiano/Plugins/Editor/JetBrains/Unity3DRider.cs
new file mode 100644
index 0000000..6c6fed0
--- /dev/null
+++ b/Assets/LUXPiano/Plugins/Editor/JetBrains/Unity3DRider.cs
@@ -0,0 +1,1363 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Version: 2.1.3.5034
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+using Application = UnityEngine.Application;
+using Debug = UnityEngine.Debug;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Text.RegularExpressions;
+using System.Text;
+using System.Xml.Linq;
+using System;
+using UnityEditor;
+using UnityEngine;
+
+namespace Plugins.Editor.JetBrains
+{
+ public class RiderAssetPostprocessor : AssetPostprocessor
+ {
+ public static void OnGeneratedCSProjectFiles()
+ {
+ if (!RiderPlugin.Enabled)
+ return;
+ var currentDirectory = Directory.GetCurrentDirectory();
+ var projectFiles = Directory.GetFiles(currentDirectory, "*.csproj");
+
+ foreach (var file in projectFiles)
+ {
+ UpgradeProjectFile(file);
+ }
+
+ var slnFile = RiderPlugin.SlnFile;
+ if (string.IsNullOrEmpty(slnFile))
+ return;
+
+ RiderPlugin.Log(RiderPlugin.LoggingLevel.Verbose, string.Format("Post-processing {0}", slnFile));
+ string slnAllText = File.ReadAllText(slnFile);
+ const string unityProjectGuid = @"Project(""{E097FAD1-6243-4DAD-9C02-E9B9EFC3FFC1}"")";
+ if (!slnAllText.Contains(unityProjectGuid))
+ {
+ string matchGUID = @"Project\(\""\{[A-Z0-9]{8}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{12}\}\""\)";
+ // Unity may put a random guid, unityProjectGuid will help VSTU recognize Rider-generated projects
+ slnAllText = Regex.Replace(slnAllText, matchGUID, unityProjectGuid);
+ }
+
+ var lines = slnAllText.Split(new[] {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries);
+ var sb = new StringBuilder();
+ foreach (var line in lines)
+ {
+ if (line.StartsWith("Project("))
+ {
+ MatchCollection mc = Regex.Matches(line, "\"([^\"]*)\"");
+ //RiderPlugin.Log(RiderPlugin.LoggingLevel.Info, "mc[1]: "+mc[1].Value);
+ //RiderPlugin.Log(RiderPlugin.LoggingLevel.Info, "mc[2]: "+mc[2].Value);
+ var to = GetFileNameWithoutExtension(mc[2].Value.Substring(1, mc[2].Value.Length-1)); // remove quotes
+ //RiderPlugin.Log(RiderPlugin.LoggingLevel.Info, "to:" + to);
+ //RiderPlugin.Log(RiderPlugin.LoggingLevel.Info, line);
+ var newLine = line.Substring(0, mc[1].Index + 1) + to + line.Substring(mc[1].Index + mc[1].Value.Length - 1);
+ sb.Append(newLine);
+ //RiderPlugin.Log(RiderPlugin.LoggingLevel.Info, newLine);
+ }
+ else
+ {
+ sb.Append(line);
+ }
+ sb.Append(Environment.NewLine);
+ }
+ File.WriteAllText(slnFile, sb.ToString());
+ }
+
+ private static string GetFileNameWithoutExtension(string path)
+ {
+ if (string.IsNullOrEmpty(path))
+ return null;
+ int length;
+ return (length = path.LastIndexOf('.')) == -1 ? path : path.Substring(0, length);
+ }
+
+ private static void UpgradeProjectFile(string projectFile)
+ {
+ RiderPlugin.Log(RiderPlugin.LoggingLevel.Verbose, string.Format("Post-processing {0}", projectFile));
+ var doc = XDocument.Load(projectFile);
+ var projectContentElement = doc.Root;
+ XNamespace xmlns = projectContentElement.Name.NamespaceName; // do not use var
+
+ FixTargetFrameworkVersion(projectContentElement, xmlns);
+ FixSystemXml(projectContentElement, xmlns);
+ SetLangVersion(projectContentElement, xmlns);
+ // Unity_5_6_OR_NEWER switched to nunit 3.5
+#if UNITY_5_6_OR_NEWER
+ ChangeNunitReference(projectContentElement, xmlns);
+#endif
+
+#if !UNITY_2017_1_OR_NEWER // Unity 2017.1 and later has this features by itself
+ SetManuallyDefinedComilingSettings(projectFile, projectContentElement, xmlns);
+#endif
+ SetXCodeDllReference("UnityEditor.iOS.Extensions.Xcode.dll", xmlns, projectContentElement);
+ SetXCodeDllReference("UnityEditor.iOS.Extensions.Common.dll", xmlns, projectContentElement);
+ ApplyManualCompilingSettingsReferences(projectContentElement, xmlns);
+ doc.Save(projectFile);
+ }
+
+ private static void FixSystemXml(XElement projectContentElement, XNamespace xmlns)
+ {
+ var el = projectContentElement
+ .Elements(xmlns+"ItemGroup")
+ .Elements(xmlns+"Reference")
+ .FirstOrDefault(a => a.Attribute("Include").Value=="System.XML");
+ if (el != null)
+ {
+ el.Attribute("Include").Value = "System.Xml";
+ }
+ }
+
+ private static void ChangeNunitReference(XElement projectContentElement, XNamespace xmlns)
+ {
+ var el = projectContentElement
+ .Elements(xmlns+"ItemGroup")
+ .Elements(xmlns+"Reference")
+ .FirstOrDefault(a => a.Attribute("Include").Value=="nunit.framework");
+ if (el != null)
+ {
+ var hintPath = el.Elements(xmlns + "HintPath").FirstOrDefault();
+ if (hintPath != null)
+ {
+ var path = Path.GetFullPath("Library/resharper-unity-libs/nunit3.5.0/nunit.framework.dll");
+ if (new FileInfo(path).Exists)
+ hintPath.Value = path;
+ }
+ }
+ }
+
+ private static readonly string PROJECT_MANUAL_CONFIG_ABSOLUTE_FILE_PATH = Path.GetFullPath("Assets/mcs.rsp");
+#if !UNITY_2017_1_OR_NEWER // Unity 2017.1 and later has this features by itself
+ private const string UNITY_PLAYER_PROJECT_NAME = "Assembly-CSharp.csproj";
+ private const string UNITY_EDITOR_PROJECT_NAME = "Assembly-CSharp-Editor.csproj";
+ private const string UNITY_UNSAFE_KEYWORD = "-unsafe";
+ private const string UNITY_DEFINE_KEYWORD = "-define:";
+ private static readonly string PLAYER_PROJECT_MANUAL_CONFIG_ABSOLUTE_FILE_PATH = Path.GetFullPath("Assets/smcs.rsp");
+ private static readonly string EDITOR_PROJECT_MANUAL_CONFIG_ABSOLUTE_FILE_PATH = Path.GetFullPath("Assets/gmcs.rsp");
+
+ private static void SetManuallyDefinedComilingSettings(string projectFile, XElement projectContentElement, XNamespace xmlns)
+ {
+ string configPath = null;
+
+ if (IsPlayerProjectFile(projectFile) || IsEditorProjectFile(projectFile))
+ {
+ //Prefer mcs.rsp if it exists
+ if (File.Exists(PROJECT_MANUAL_CONFIG_ABSOLUTE_FILE_PATH))
+ {
+ configPath = PROJECT_MANUAL_CONFIG_ABSOLUTE_FILE_PATH;
+ }
+ else
+ {
+ if (IsPlayerProjectFile(projectFile))
+ configPath = PLAYER_PROJECT_MANUAL_CONFIG_ABSOLUTE_FILE_PATH;
+ else if (IsEditorProjectFile(projectFile))
+ configPath = EDITOR_PROJECT_MANUAL_CONFIG_ABSOLUTE_FILE_PATH;
+ }
+ }
+
+ if(!string.IsNullOrEmpty(configPath))
+ ApplyManualCompilingSettings(configPath
+ , projectContentElement
+ , xmlns);
+ }
+
+ private static void ApplyManualCompilingSettings(string configFilePath, XElement projectContentElement, XNamespace xmlns)
+ {
+ if (File.Exists(configFilePath))
+ {
+ var configText = File.ReadAllText(configFilePath);
+ if (configText.Contains(UNITY_UNSAFE_KEYWORD))
+ {
+ // Add AllowUnsafeBlocks to the .csproj. Unity doesn't generate it (although VSTU does).
+ // Strictly necessary to compile unsafe code
+ ApplyAllowUnsafeBlocks(projectContentElement, xmlns);
+ }
+ if (configText.Contains(UNITY_DEFINE_KEYWORD))
+ {
+ // defines could be
+ // 1) -define:DEFINE1,DEFINE2
+ // 2) -define:DEFINE1;DEFINE2
+ // 3) -define:DEFINE1 -define:DEFINE2
+ // 4) -define:DEFINE1,DEFINE2;DEFINE3
+ // tested on "-define:DEF1;DEF2 -define:DEF3,DEF4;DEFFFF \n -define:DEF5"
+ // result: DEF1, DEF2, DEF3, DEF4, DEFFFF, DEF5
+
+ var definesList = new List();
+ var compileFlags = configText.Split(' ', '\n');
+ foreach (var flag in compileFlags)
+ {
+ var f = flag.Trim();
+ if (f.Contains(UNITY_DEFINE_KEYWORD))
+ {
+ var defineEndPos = f.IndexOf(UNITY_DEFINE_KEYWORD) + UNITY_DEFINE_KEYWORD.Length;
+ var definesSubString = f.Substring(defineEndPos,f.Length - defineEndPos);
+ definesSubString = definesSubString.Replace(";", ",");
+ definesList.AddRange(definesSubString.Split(','));
+ }
+ }
+
+ ApplyCustomDefines(definesList.ToArray(), projectContentElement, xmlns);
+ }
+ }
+ }
+
+ private static void ApplyCustomDefines(string[] customDefines, XElement projectContentElement, XNamespace xmlns)
+ {
+ var definesString = string.Join(";", customDefines);
+
+ var DefineConstants = projectContentElement
+ .Elements(xmlns+"PropertyGroup")
+ .Elements(xmlns+"DefineConstants")
+ .FirstOrDefault(definesConsts=> !string.IsNullOrEmpty(definesConsts.Value));
+
+ if (DefineConstants != null)
+ {
+ DefineConstants.SetValue(DefineConstants.Value + ";" + definesString);
+ }
+ }
+
+ private static void ApplyAllowUnsafeBlocks(XElement projectContentElement, XNamespace xmlns)
+ {
+ projectContentElement.AddFirst(
+ new XElement(xmlns + "PropertyGroup", new XElement(xmlns + "AllowUnsafeBlocks", true)));
+ }
+
+ private static bool IsPlayerProjectFile(string projectFile)
+ {
+ return Path.GetFileName(projectFile) == UNITY_PLAYER_PROJECT_NAME;
+ }
+
+ private static bool IsEditorProjectFile(string projectFile)
+ {
+ return Path.GetFileName(projectFile) == UNITY_EDITOR_PROJECT_NAME;
+ }
+#endif
+ private static void SetXCodeDllReference(string name, XNamespace xmlns, XElement projectContentElement)
+ {
+ string unityAppBaseFolder = Path.GetDirectoryName(EditorApplication.applicationPath);
+
+ var xcodeDllPath = Path.Combine(unityAppBaseFolder, Path.Combine("Data/PlaybackEngines/iOSSupport", name));
+ if (!File.Exists(xcodeDllPath))
+ xcodeDllPath = Path.Combine(unityAppBaseFolder, Path.Combine("PlaybackEngines/iOSSupport", name));
+
+ if (File.Exists(xcodeDllPath))
+ {
+ var itemGroup = new XElement(xmlns + "ItemGroup");
+ var reference = new XElement(xmlns + "Reference");
+ reference.Add(new XAttribute("Include", Path.GetFileNameWithoutExtension(xcodeDllPath)));
+ reference.Add(new XElement(xmlns + "HintPath", xcodeDllPath));
+ itemGroup.Add(reference);
+ projectContentElement.Add(itemGroup);
+ }
+ }
+
+ private const string UNITY_REFERENCE_KEYWORD = "-r:";
+ ///
+ /// Handles custom references -r: in "mcs.rsp"
+ ///
+ ///
+ ///
+ private static void ApplyManualCompilingSettingsReferences(XElement projectContentElement, XNamespace xmlns)
+ {
+ if (!File.Exists(PROJECT_MANUAL_CONFIG_ABSOLUTE_FILE_PATH))
+ return;
+
+ var configFilePath = PROJECT_MANUAL_CONFIG_ABSOLUTE_FILE_PATH;
+
+ if (File.Exists(configFilePath))
+ {
+ var configText = File.ReadAllText(configFilePath);
+ if (configText.Contains(UNITY_REFERENCE_KEYWORD))
+ {
+ var referenceList = new List();
+ var compileFlags = configText.Split(' ', '\n');
+ foreach (var flag in compileFlags)
+ {
+ var f = flag.Trim();
+ if (f.Contains(UNITY_REFERENCE_KEYWORD))
+ {
+ var defineEndPos = f.IndexOf(UNITY_REFERENCE_KEYWORD) + UNITY_REFERENCE_KEYWORD.Length;
+ var definesSubString = f.Substring(defineEndPos,f.Length - defineEndPos);
+ definesSubString = definesSubString.Replace(";", ",");
+ referenceList.AddRange(definesSubString.Split(','));
+ }
+ }
+
+ foreach (var referenceName in referenceList)
+ {
+ ApplyCustomReference(referenceName, projectContentElement, xmlns);
+ }
+ }
+ }
+ }
+
+ private static void ApplyCustomReference(string name, XElement projectContentElement, XNamespace xmlns)
+ {
+ var itemGroup = new XElement(xmlns + "ItemGroup");
+ var reference = new XElement(xmlns + "Reference");
+ reference.Add(new XAttribute("Include", Path.GetFileNameWithoutExtension(name)));
+ itemGroup.Add(reference);
+ projectContentElement.Add(itemGroup);
+ }
+
+ // Set appropriate version
+ private static void FixTargetFrameworkVersion(XElement projectElement, XNamespace xmlns)
+ {
+ var targetFrameworkVersion = projectElement.Elements(xmlns + "PropertyGroup")
+ .Elements(xmlns + "TargetFrameworkVersion")
+ .FirstOrDefault(); // Processing csproj files, which are not Unity-generated #56
+ if (targetFrameworkVersion != null)
+ {
+ int scriptingRuntime = 0; // legacy runtime
+ try
+ {
+ var property = typeof(EditorApplication).GetProperty("scriptingRuntimeVersion");
+ scriptingRuntime = (int)property.GetValue(null, null);
+ if (scriptingRuntime>0)
+ RiderPlugin.Log(RiderPlugin.LoggingLevel.Verbose, "Latest runtime detected.");
+ }
+ catch(Exception){}
+
+ if (scriptingRuntime>0)
+ targetFrameworkVersion.SetValue("v"+RiderPlugin.TargetFrameworkVersion);
+ else
+ targetFrameworkVersion.SetValue("v"+RiderPlugin.TargetFrameworkVersionOldMono);
+ }
+ }
+
+ private static void SetLangVersion(XElement projectElement, XNamespace xmlns)
+ {
+ // Add LangVersion to the .csproj. Unity doesn't generate it (although VSTU does).
+ // Not strictly necessary, as the Unity plugin for Rider will work it out, but setting
+ // it makes Rider work if it's not installed.
+ var langVersion = projectElement.Elements(xmlns + "PropertyGroup").Elements(xmlns + "LangVersion")
+ .FirstOrDefault(); // Processing csproj files, which are not Unity-generated #56
+ if (langVersion != null)
+ {
+ langVersion.SetValue(GetLanguageLevel());
+ }
+ else
+ {
+ projectElement.AddFirst(new XElement(xmlns + "PropertyGroup",
+ new XElement(xmlns + "LangVersion", GetLanguageLevel())));
+ }
+ }
+
+ private static string GetLanguageLevel()
+ {
+ // https://bitbucket.org/alexzzzz/unity-c-5.0-and-6.0-integration/src
+ if (Directory.Exists(Path.GetFullPath("CSharp70Support")))
+ return "7";
+ if (Directory.Exists(Path.GetFullPath("CSharp60Support")))
+ return "6";
+
+ // Unity 5.5 supports C# 6, but only when targeting .NET 4.6. The enum doesn't exist pre Unity 5.5
+#if !UNITY_5_6_OR_NEWER
+ if ((int)PlayerSettings.apiCompatibilityLevel >= 3)
+ #else
+ if ((int) PlayerSettings.GetApiCompatibilityLevel(EditorUserBuildSettings.selectedBuildTargetGroup) >= 3)
+#endif
+ return "6";
+
+ return "4";
+ }
+
+ private static Type ourPdb2MdbDriver;
+ private static Type Pdb2MdbDriver
+ {
+ get
+ {
+ if (ourPdb2MdbDriver != null)
+ return ourPdb2MdbDriver;
+ Assembly assembly;
+ try
+ {
+ var path = Path.GetFullPath(@"Library\resharper-unity-libs\pdb2mdb.exe");
+ var bytes = File.ReadAllBytes(path);
+ assembly = Assembly.Load(bytes);
+ }
+ catch (Exception)
+ {
+ RiderPlugin.Log(RiderPlugin.LoggingLevel.Verbose, "Loading pdb2mdb failed.");
+ assembly = null;
+ }
+
+ if (assembly == null)
+ return null;
+ var type = assembly.GetType("Pdb2Mdb.Driver");
+ if (type == null)
+ return null;
+ return ourPdb2MdbDriver = type;
+ }
+ }
+
+ public static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromPath)
+ {
+ if (!RiderPlugin.Enabled)
+ return;
+ var toBeConverted = importedAssets.Where(a =>
+ a.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) &&
+ importedAssets.Any(a1 => a1 == Path.ChangeExtension(a, ".pdb")) &&
+ importedAssets.All(b => b != Path.ChangeExtension(a, ".dll.mdb")))
+ .ToArray();
+ foreach (var asset in toBeConverted)
+ {
+ var pdb = Path.ChangeExtension(asset, ".pdb");
+ if (!IsPortablePdb(pdb))
+ ConvertSymbolsForAssembly(asset);
+ else
+ RiderPlugin.Log(RiderPlugin.LoggingLevel.Verbose, string.Format("mdb generation for Portable pdb is not supported. {0}", pdb));
+ }
+ }
+
+ private static void ConvertSymbolsForAssembly(string asset)
+ {
+ if (Pdb2MdbDriver == null)
+ {
+ RiderPlugin.Log(RiderPlugin.LoggingLevel.Verbose, "FailedToConvertDebugSymbolsNoPdb2mdb.");
+ return;
+ }
+
+ var method = Pdb2MdbDriver.GetMethod("Main", BindingFlags.Static | BindingFlags.NonPublic);
+ if (method == null)
+ {
+ RiderPlugin.Log(RiderPlugin.LoggingLevel.Verbose, "WarningFailedToConvertDebugSymbolsPdb2mdbMainIsNull.");
+ return;
+ }
+
+ var strArray = new[] { Path.GetFullPath(asset) };
+ method.Invoke(null, new object[] { strArray });
+ }
+
+ //https://github.com/xamarin/xamarin-android/commit/4e30546f
+ const uint ppdb_signature = 0x424a5342;
+ public static bool IsPortablePdb(string filename)
+ {
+ try
+ {
+ using (var fs = new FileStream(filename, FileMode.Open, FileAccess.Read))
+ {
+ using (var br = new BinaryReader(fs))
+ {
+ return br.ReadUInt32() == ppdb_signature;
+ }
+ }
+ }
+ catch
+ {
+ return false;
+ }
+ }
+ }
+}
+
+namespace Plugins.Editor.JetBrains
+{
+ [InitializeOnLoad]
+ public static class RiderPlugin
+ {
+ private static bool Initialized;
+ internal static string SlnFile;
+
+ public static void Log(LoggingLevel level, string initialText)
+ {
+ if (level < SelectedLoggingLevel) return;
+
+ var text = "[Rider] "+DateTime.Now.ToString("HH:mm:ss:ff")+" [" + level + "] " + initialText;
+
+ switch (level)
+ {
+ case LoggingLevel.Warning:
+ Debug.LogWarning(text);
+ break;
+ default:
+ Debug.Log(text);
+ break;
+ }
+ }
+
+ private static string GetDefaultApp()
+ {
+ var allFoundPaths = GetAllRiderPaths().Select(a=>new FileInfo(a).FullName).ToArray();
+ var externalEditor = GetExternalScriptEditor();
+ if (!string.IsNullOrEmpty(externalEditor))
+ {
+ var alreadySetPath = new FileInfo(externalEditor).FullName;
+ if (RiderPathExist(alreadySetPath))
+ {
+ if (!allFoundPaths.Any() || allFoundPaths.Any() && allFoundPaths.Contains(alreadySetPath))
+ {
+ RiderPath = alreadySetPath;
+ return alreadySetPath;
+ }
+ }
+ }
+ if (!string.IsNullOrEmpty(RiderPath) && allFoundPaths.Contains(new FileInfo(RiderPath).FullName)) {}
+ else
+ RiderPath = allFoundPaths.FirstOrDefault();
+
+ return RiderPath;
+ }
+
+ private static string[] GetAllRiderPaths()
+ {
+ switch (SystemInfoRiderPlugin.operatingSystemFamily)
+ {
+ case OperatingSystemFamily.Windows:
+ string[] folders =
+ {
+ @"C:\ProgramData\Microsoft\Windows\Start Menu\Programs\JetBrains", Path.Combine(
+ Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
+ @"Microsoft\Windows\Start Menu\Programs\JetBrains Toolbox")
+ };
+
+ var newPathLnks = folders.Select(b => new DirectoryInfo(b)).Where(a => a.Exists)
+ .SelectMany(c => c.GetFiles("*Rider*.lnk")).ToArray();
+ if (newPathLnks.Any())
+ {
+ var newPaths = newPathLnks
+ .Select(newPathLnk => new FileInfo(ShortcutResolver.Resolve(newPathLnk.FullName)))
+ .Where(fi => File.Exists(fi.FullName))
+ .ToArray()
+ .OrderByDescending(fi => FileVersionInfo.GetVersionInfo(fi.FullName).ProductVersion)
+ .Select(a => a.FullName).ToArray();
+
+ return newPaths;
+ }
+ break;
+
+ case OperatingSystemFamily.MacOSX:
+ // "/Applications/*Rider*.app"
+ //"~/Applications/JetBrains Toolbox/*Rider*.app"
+ string[] foldersMac =
+ {
+ "/Applications", Path.Combine(Environment.GetEnvironmentVariable("HOME"), "Applications/JetBrains Toolbox")
+ };
+ var newPathsMac = foldersMac.Select(b => new DirectoryInfo(b)).Where(a => a.Exists)
+ .SelectMany(c => c.GetDirectories("*Rider*.app"))
+ .Select(a => a.FullName).ToArray();
+ return newPathsMac;
+ }
+ return new string[0];
+ }
+
+ private static string GetTargetFrameworkVersionDefault(string defaultValue)
+ {
+ if (SystemInfoRiderPlugin.operatingSystemFamily == OperatingSystemFamily.Windows)
+ {
+ var dir = new DirectoryInfo(@"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework");
+ if (dir.Exists)
+ {
+ var availableVersions = dir.GetDirectories("v*").Select(a => a.Name.Substring(1))
+ .Where(v => TryCatch(v, s => { })).ToArray();
+ if (availableVersions.Any() && !availableVersions.Contains(defaultValue))
+ {
+ defaultValue = availableVersions.OrderBy(a => new Version(a)).Last();
+ }
+ }
+ }
+ return defaultValue;
+ }
+
+
+ public static string TargetFrameworkVersion
+ {
+ get
+ {
+ return EditorPrefs.GetString("Rider_TargetFrameworkVersion", GetTargetFrameworkVersionDefault("4.6"));
+ }
+ set
+ {
+ TryCatch(value, val =>
+ {
+ EditorPrefs.SetString("Rider_TargetFrameworkVersion", val);
+ });
+ }
+ }
+
+ public static string TargetFrameworkVersionOldMono
+ {
+ get
+ {
+ return EditorPrefs.GetString("Rider_TargetFrameworkVersionOldMono", GetTargetFrameworkVersionDefault("3.5"));
+ }
+ set
+ {
+ TryCatch(value, val =>
+ {
+ EditorPrefs.SetString("Rider_TargetFrameworkVersionOldMono", val);
+ });
+ }
+ }
+
+ private static bool TryCatch(string value, Action action)
+ {
+ try
+ {
+ new Version(value); // mono 2.6 doesn't support Version.TryParse
+ action(value);
+ return true;
+ }
+ catch (ArgumentException)
+ {
+ } // can't put loggin here because ot fire on every symbol
+ catch (FormatException)
+ {
+ }
+ return false;
+ }
+
+ public static string RiderPath
+ {
+ get { return EditorPrefs.GetString("Rider_RiderPath", GetAllRiderPaths().FirstOrDefault()); }
+ set { EditorPrefs.SetString("Rider_RiderPath", value); }
+ }
+
+ public enum LoggingLevel
+ {
+ Verbose = 0,
+ Info = 1,
+ Warning = 2
+ }
+
+ public static LoggingLevel SelectedLoggingLevel
+ {
+ get { return (LoggingLevel) EditorPrefs.GetInt("Rider_SelectedLoggingLevel", 1); }
+ set { EditorPrefs.SetInt("Rider_SelectedLoggingLevel", (int) value); }
+ }
+
+ public static bool RiderInitializedOnce
+ {
+ get { return EditorPrefs.GetBool("RiderInitializedOnce", false); }
+ set { EditorPrefs.SetBool("RiderInitializedOnce", value); }
+ }
+
+ internal static bool Enabled
+ {
+ get
+ {
+ var defaultApp = GetExternalScriptEditor();
+ return !string.IsNullOrEmpty(defaultApp) && Path.GetFileName(defaultApp).ToLower().Contains("rider");
+ }
+ }
+
+ static RiderPlugin()
+ {
+ var riderPath = GetDefaultApp();
+ if (!RiderPathExist(riderPath))
+ return;
+
+ AddRiderToRecentlyUsedScriptApp(riderPath, "RecentlyUsedScriptApp");
+ if (!RiderInitializedOnce)
+ {
+ SetExternalScriptEditor(riderPath);
+ RiderInitializedOnce = true;
+ }
+ if (Enabled)
+ {
+ InitRiderPlugin();
+ }
+ }
+
+ private static void InitRiderPlugin()
+ {
+ var projectDirectory = Directory.GetParent(Application.dataPath).FullName;
+
+ var projectName = Path.GetFileName(projectDirectory);
+ SlnFile = Path.GetFullPath(string.Format("{0}.sln", projectName));
+
+ InitializeEditorInstanceJson();
+
+ RiderAssetPostprocessor.OnGeneratedCSProjectFiles();
+
+ Log(LoggingLevel.Info, "Rider plugin initialized. You may change the amount of Rider Debug output via Edit -> Preferences -> Rider -> Logging Level");
+ Initialized = true;
+ }
+
+ private static void AddRiderToRecentlyUsedScriptApp(string userAppPath, string recentAppsKey)
+ {
+ for (int index = 0; index < 10; ++index)
+ {
+ string path = EditorPrefs.GetString(recentAppsKey + (object) index);
+ if (File.Exists(path) && Path.GetFileName(path).ToLower().Contains("rider"))
+ return;
+ }
+ EditorPrefs.SetString(recentAppsKey + 9, userAppPath);
+ }
+
+ private static string GetExternalScriptEditor()
+ {
+ return EditorPrefs.GetString("kScriptsDefaultApp");
+ }
+
+ private static void SetExternalScriptEditor(string path)
+ {
+ EditorPrefs.SetString("kScriptsDefaultApp", path);
+ }
+
+ private static bool RiderPathExist(string path)
+ {
+ if (string.IsNullOrEmpty(path))
+ return false;
+ // windows or mac
+ var fileInfo = new FileInfo(path);
+ if (!fileInfo.Name.ToLower().Contains("rider"))
+ return false;
+ var directoryInfo = new DirectoryInfo(path);
+ return fileInfo.Exists || (SystemInfoRiderPlugin.operatingSystemFamily == OperatingSystemFamily.MacOSX &&
+ directoryInfo.Exists);
+ }
+
+ ///
+ /// Creates and deletes Library/EditorInstance.json containing info about unity instance
+ ///
+ private static void InitializeEditorInstanceJson()
+ {
+ Log(LoggingLevel.Verbose, "Writing Library/EditorInstance.json");
+
+ var editorInstanceJsonPath = Path.GetFullPath("Library/EditorInstance.json");
+
+ File.WriteAllText(editorInstanceJsonPath, string.Format(@"{{
+ ""process_id"": {0},
+ ""version"": ""{1}"",
+ ""app_path"": ""{2}"",
+ ""app_contents_path"": ""{3}"",
+ ""attach_allowed"": ""{4}""
+}}", Process.GetCurrentProcess().Id, Application.unityVersion,
+ EditorApplication.applicationPath,
+ EditorApplication.applicationContentsPath,
+ EditorPrefs.GetBool("AllowAttachedDebuggingOfEditor", true)
+ ));
+
+ AppDomain.CurrentDomain.DomainUnload += (sender, args) =>
+ {
+ Log(LoggingLevel.Verbose, "Deleting Library/EditorInstance.json");
+ File.Delete(editorInstanceJsonPath);
+ };
+ }
+
+ ///
+ /// Asset Open Callback (from Unity)
+ ///
+ ///
+ /// Called when Unity is about to open an asset.
+ ///
+ [UnityEditor.Callbacks.OnOpenAssetAttribute()]
+ static bool OnOpenedAsset(int instanceID, int line)
+ {
+ if (Enabled)
+ {
+ if (!Initialized)
+ {
+ // make sure the plugin was initialized first.
+ // this can happen in case "Rider" was set as the default scripting app only after this plugin was imported.
+ InitRiderPlugin();
+ }
+
+ // determine asset that has been double clicked in the project view
+ var selected = EditorUtility.InstanceIDToObject(instanceID);
+
+ var assetFilePath = Path.GetFullPath(AssetDatabase.GetAssetPath(selected));
+ if (!(selected.GetType().ToString() == "UnityEditor.MonoScript" ||
+ selected.GetType().ToString() == "UnityEngine.Shader" ||
+ (selected.GetType().ToString() == "UnityEngine.TextAsset" &&
+#if UNITY_5 || UNITY_5_5_OR_NEWER
+ EditorSettings.projectGenerationUserExtensions.Contains(Path.GetExtension(assetFilePath).Substring(1))
+#else
+ EditorSettings.externalVersionControl.Contains(Path.GetExtension(assetFilePath).Substring(1))
+#endif
+ )))
+ return false;
+
+ SyncSolution(); // added to handle opening file, which was just recently created.
+ if (DetectPortAndOpenFile(line, assetFilePath, SystemInfoRiderPlugin.operatingSystemFamily == OperatingSystemFamily.Windows))
+ return true;
+ var args = string.Format("{0}{1}{0} --line {2} {0}{3}{0}", "\"", SlnFile, line, assetFilePath);
+ return CallRider(args);
+ }
+
+ return false;
+ }
+
+
+ private static bool DetectPortAndOpenFile(int line, string filePath, bool isWindows)
+ {
+ if (SystemInfoRiderPlugin.operatingSystemFamily == OperatingSystemFamily.Windows)
+ {
+ var process = GetRiderProcess();
+ if (process == null)
+ return false;
+ }
+
+ var ports = Enumerable.Range(63342, 20);
+ var res = ports.Any(port =>
+ {
+ var aboutUrl = string.Format("http://localhost:{0}/api/about/", port);
+ var aboutUri = new Uri(aboutUrl);
+
+ using (var client = new WebClient())
+ {
+ client.Headers.Add("origin", string.Format("http://localhost:{0}", port));
+ client.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
+
+ try
+ {
+ var responce = CallHttpApi(aboutUri, client);
+ if (responce.ToLower().Contains("rider"))
+ {
+ return HttpOpenFile(line, filePath, isWindows, port, client);
+ }
+ }
+ catch (Exception e)
+ {
+ Log(LoggingLevel.Verbose, string.Format("Exception in DetectPortAndOpenFile: {0}", e));
+ }
+ }
+ return false;
+ });
+ return res;
+ }
+
+ private static bool HttpOpenFile(int line, string filePath, bool isWindows, int port, WebClient client)
+ {
+ var url = string.Format("http://localhost:{0}/api/file?file={1}{2}", port, filePath,
+ line < 0
+ ? "&p=0"
+ : "&line=" + line); // &p is needed to workaround https://youtrack.jetbrains.com/issue/IDEA-172350
+ if (isWindows)
+ url = string.Format(@"http://localhost:{0}/api/file/{1}{2}", port, filePath, line < 0 ? "" : ":" + line);
+
+ var uri = new Uri(url);
+ Log(LoggingLevel.Verbose, string.Format("HttpRequestOpenFile({0})", uri.AbsoluteUri));
+
+ CallHttpApi(uri, client);
+ ActivateWindow();
+ return true;
+ }
+
+ private static string CallHttpApi(Uri uri, WebClient client)
+ {
+ var responseString = client.DownloadString(uri.AbsoluteUri);
+ Log(LoggingLevel.Verbose, string.Format("CallHttpApi {0} response: {1}", uri.AbsoluteUri, responseString));
+ return responseString;
+ }
+
+ private static bool CallRider(string args)
+ {
+ var defaultApp = GetDefaultApp();
+ if (!RiderPathExist(defaultApp))
+ {
+ return false;
+ }
+
+ var proc = new Process();
+ if (SystemInfoRiderPlugin.operatingSystemFamily == OperatingSystemFamily.MacOSX)
+ {
+ proc.StartInfo.FileName = "open";
+ proc.StartInfo.Arguments = string.Format("-n {0}{1}{0} --args {2}", "\"", "/" + defaultApp, args);
+ Log(LoggingLevel.Verbose, string.Format("{0} {1}", proc.StartInfo.FileName, proc.StartInfo.Arguments));
+ }
+ else
+ {
+ proc.StartInfo.FileName = defaultApp;
+ proc.StartInfo.Arguments = args;
+ Log(LoggingLevel.Verbose, string.Format("{2}{0}{2}" + " {1}", proc.StartInfo.FileName, proc.StartInfo.Arguments, "\""));
+ }
+
+ proc.StartInfo.UseShellExecute = false;
+ proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
+ proc.StartInfo.CreateNoWindow = true;
+ proc.StartInfo.RedirectStandardOutput = true;
+ proc.Start();
+
+ ActivateWindow();
+ return true;
+ }
+
+ private static void ActivateWindow()
+ {
+ if (SystemInfoRiderPlugin.operatingSystemFamily == OperatingSystemFamily.Windows)
+ {
+ try
+ {
+ var process = GetRiderProcess();
+ if (process != null)
+ {
+ // Collect top level windows
+ var topLevelWindows = User32Dll.GetTopLevelWindowHandles();
+ // Get process main window title
+ var windowHandle = topLevelWindows.FirstOrDefault(hwnd => User32Dll.GetWindowProcessId(hwnd) == process.Id);
+ Log(LoggingLevel.Info, string.Format("ActivateWindow: {0} {1}", process.Id, windowHandle));
+ if (windowHandle != IntPtr.Zero)
+ {
+ //User32Dll.ShowWindow(windowHandle, 9); //SW_RESTORE = 9
+ User32Dll.SetForegroundWindow(windowHandle);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ Log(LoggingLevel.Warning, "Exception on ActivateWindow: " + e);
+ }
+ }
+ }
+
+ private static Process GetRiderProcess()
+ {
+ var process = Process.GetProcesses().FirstOrDefault(p =>
+ {
+ string processName;
+ try
+ {
+ processName =
+ p.ProcessName; // some processes like kaspersky antivirus throw exception on attempt to get ProcessName
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+
+ return !p.HasExited && processName.ToLower().Contains("rider");
+ });
+ return process;
+ }
+
+ // The default "Open C# Project" menu item will use the external script editor to load the .sln
+ // file, but unless Unity knows the external script editor can properly load solutions, it will
+ // also launch MonoDevelop (or the OS registered app for .sln files). This menu item side steps
+ // that issue, and opens the solution in Rider without opening MonoDevelop as well.
+ // Unity 2017.1 and later recognise Rider as an app that can load solutions, so this menu isn't
+ // needed in newer versions.
+ [MenuItem("Assets/Open C# Project in Rider", false, 1000)]
+ static void MenuOpenProject()
+ {
+ // Force the project files to be sync
+ SyncSolution();
+
+ // Load Project
+ CallRider(string.Format("{0}{1}{0}", "\"", SlnFile));
+ }
+
+ [MenuItem("Assets/Open C# Project in Rider", true, 1000)]
+ static bool ValidateMenuOpenProject()
+ {
+ return Enabled;
+ }
+
+ ///
+ /// Force Unity To Write Project File
+ ///
+ private static void SyncSolution()
+ {
+ System.Type T = System.Type.GetType("UnityEditor.SyncVS,UnityEditor");
+ System.Reflection.MethodInfo SyncSolution = T.GetMethod("SyncSolution",
+ System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
+ SyncSolution.Invoke(null, null);
+ }
+
+ ///
+ /// JetBrains Rider Integration Preferences Item
+ ///
+ ///
+ /// Contains all 3 toggles: Enable/Disable; Debug On/Off; Writing Launch File On/Off
+ ///
+ [PreferenceItem("Rider")]
+ static void RiderPreferencesItem()
+ {
+ EditorGUILayout.BeginVertical();
+ EditorGUI.BeginChangeCheck();
+
+ var alternatives = GetAllRiderPaths();
+ if (alternatives.Any())
+ {
+ int index = Array.IndexOf(alternatives, RiderPath);
+ var alts = alternatives.Select(s => s.Replace("/", ":"))
+ .ToArray(); // hack around https://fogbugz.unity3d.com/default.asp?940857_tirhinhe3144t4vn
+ RiderPath = alternatives[EditorGUILayout.Popup("Rider executable:", index == -1 ? 0 : index, alts)];
+ if (EditorGUILayout.Toggle(new GUIContent("Rider is default editor"), Enabled))
+ {
+ SetExternalScriptEditor(RiderPath);
+ EditorGUILayout.HelpBox("Unckecking will restore default external editor.", MessageType.None);
+ }
+ else
+ {
+ SetExternalScriptEditor(string.Empty);
+ EditorGUILayout.HelpBox("Checking will set Rider as default external editor", MessageType.None);
+ }
+ }
+
+ GUILayout.BeginVertical();
+ string status = "TargetFrameworkVersion for Runtime";
+ EditorGUILayout.TextArea(status, EditorStyles.boldLabel);
+ var help = @"TargetFramework >= 4.5 is recommended.";
+ TargetFrameworkVersion =
+ EditorGUILayout.TextField(
+ new GUIContent("NET 4.6",
+ help), TargetFrameworkVersion);
+ EditorGUILayout.HelpBox(help, MessageType.None);
+ var helpOldMono = @"TargetFramework = 3.5 is recommended.
+ - With 4.5 Rider may show ambiguous references in UniRx.";
+
+ TargetFrameworkVersionOldMono =
+ EditorGUILayout.TextField(
+ new GUIContent("NET 3.5",
+ helpOldMono), TargetFrameworkVersionOldMono);
+ EditorGUILayout.HelpBox(helpOldMono, MessageType.None);
+
+ GUILayout.EndVertical();
+
+ EditorGUI.EndChangeCheck();
+
+ EditorGUI.BeginChangeCheck();
+
+ var loggingMsg =
+ @"Sets the amount of Rider Debug output. If you are about to report an issue, please select Verbose logging level and attach Unity console output to the issue.";
+ SelectedLoggingLevel = (LoggingLevel) EditorGUILayout.EnumPopup(new GUIContent("Logging Level", loggingMsg), SelectedLoggingLevel);
+ EditorGUILayout.HelpBox(loggingMsg, MessageType.None);
+
+ EditorGUI.EndChangeCheck();
+
+ var url = "https://github.com/JetBrains/resharper-unity";
+ LinkButton(url, url);
+
+/* if (GUILayout.Button("reset RiderInitializedOnce = false"))
+ {
+ RiderInitializedOnce = false;
+ }*/
+
+ EditorGUILayout.EndVertical();
+ }
+
+ private static void LinkButton(string caption, string url)
+ {
+ var style = GUI.skin.label;
+ style.richText = true;
+ caption = string.Format("{0}", caption);
+
+ bool bClicked = GUILayout.Button(caption, style);
+
+ var rect = GUILayoutUtility.GetLastRect();
+ rect.width = style.CalcSize(new GUIContent(caption)).x;
+ EditorGUIUtility.AddCursorRect(rect, MouseCursor.Link);
+
+ if (bClicked)
+ Application.OpenURL(url);
+ }
+
+ #region SystemInfoRiderPlugin
+
+ private static class SystemInfoRiderPlugin
+ {
+ public static OperatingSystemFamily operatingSystemFamily
+ {
+ get
+ {
+#if UNITY_5_5_OR_NEWER
+return SystemInfo.operatingSystemFamily;
+#else
+ if (SystemInfo.operatingSystem.StartsWith("Mac", StringComparison.InvariantCultureIgnoreCase))
+ {
+ return OperatingSystemFamily.MacOSX;
+ }
+ if (SystemInfo.operatingSystem.StartsWith("Win", StringComparison.InvariantCultureIgnoreCase))
+ {
+ return OperatingSystemFamily.Windows;
+ }
+ if (SystemInfo.operatingSystem.StartsWith("Lin", StringComparison.InvariantCultureIgnoreCase))
+ {
+ return OperatingSystemFamily.Linux;
+ }
+ return OperatingSystemFamily.Other;
+#endif
+ }
+ }
+ }
+#if !UNITY_5_5_OR_NEWER
+ enum OperatingSystemFamily
+ {
+ Other,
+ MacOSX,
+ Windows,
+ Linux,
+ }
+#endif
+ #endregion
+
+ static class User32Dll
+ {
+
+ ///
+ /// Gets the ID of the process that owns the window.
+ /// Note that creating a wrapper for that is very expensive because it causes an enumeration of all the system processes to happen.
+ ///
+ public static int GetWindowProcessId(IntPtr hwnd)
+ {
+ uint dwProcessId;
+ GetWindowThreadProcessId(hwnd, out dwProcessId);
+ return unchecked((int) dwProcessId);
+ }
+
+ ///
+ /// Lists the handles of all the top-level windows currently available in the system.
+ ///
+ public static List GetTopLevelWindowHandles()
+ {
+ var retval = new List();
+ EnumWindowsProc callback = (hwnd, param) =>
+ {
+ retval.Add(hwnd);
+ return 1;
+ };
+ EnumWindows(Marshal.GetFunctionPointerForDelegate(callback), IntPtr.Zero);
+ GC.KeepAlive(callback);
+ return retval;
+ }
+
+ public delegate Int32 EnumWindowsProc(IntPtr hwnd, IntPtr lParam);
+
+ [DllImport("user32.dll", CharSet = CharSet.Unicode, PreserveSig = true, SetLastError = true,
+ ExactSpelling = true)]
+ public static extern Int32 EnumWindows(IntPtr lpEnumFunc, IntPtr lParam);
+
+ [DllImport("user32.dll", SetLastError = true)]
+ static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
+
+ [DllImport("user32.dll", CharSet = CharSet.Unicode, PreserveSig = true, SetLastError = true,
+ ExactSpelling = true)]
+ public static extern Int32 SetForegroundWindow(IntPtr hWnd);
+
+ [DllImport("user32.dll", CharSet = CharSet.Unicode, PreserveSig = true, SetLastError = true,
+ ExactSpelling = true)]
+ public static extern UInt32 ShowWindow(IntPtr hWnd, Int32 nCmdShow);
+ }
+
+ static class ShortcutResolver
+ {
+ #region Signitures imported from http://pinvoke.net
+
+ [DllImport("shfolder.dll", CharSet = CharSet.Auto)]
+ internal static extern int SHGetFolderPath(IntPtr hwndOwner, int nFolder, IntPtr hToken, int dwFlags, StringBuilder lpszPath);
+
+ [Flags()]
+ enum SLGP_FLAGS
+ {
+ /// Retrieves the standard short (8.3 format) file name
+ SLGP_SHORTPATH = 0x1,
+
+ /// Retrieves the Universal Naming Convention (UNC) path name of the file
+ SLGP_UNCPRIORITY = 0x2,
+
+ /// Retrieves the raw path name. A raw path is something that might not exist and may include environment variables that need to be expanded
+ SLGP_RAWPATH = 0x4
+ }
+
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
+ struct WIN32_FIND_DATAW
+ {
+ public uint dwFileAttributes;
+ public long ftCreationTime;
+ public long ftLastAccessTime;
+ public long ftLastWriteTime;
+ public uint nFileSizeHigh;
+ public uint nFileSizeLow;
+ public uint dwReserved0;
+ public uint dwReserved1;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string cFileName;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] public string cAlternateFileName;
+ }
+
+ [Flags()]
+ enum SLR_FLAGS
+ {
+ ///
+ /// Do not display a dialog box if the link cannot be resolved. When SLR_NO_UI is set,
+ /// the high-order word of fFlags can be set to a time-out value that specifies the
+ /// maximum amount of time to be spent resolving the link. The function returns if the
+ /// link cannot be resolved within the time-out duration. If the high-order word is set
+ /// to zero, the time-out duration will be set to the default value of 3,000 milliseconds
+ /// (3 seconds). To specify a value, set the high word of fFlags to the desired time-out
+ /// duration, in milliseconds.
+ ///
+ SLR_NO_UI = 0x1,
+
+ /// Obsolete and no longer used
+ SLR_ANY_MATCH = 0x2,
+
+ /// If the link object has changed, update its path and list of identifiers.
+ /// If SLR_UPDATE is set, you do not need to call IPersistFile::IsDirty to determine
+ /// whether or not the link object has changed.
+ SLR_UPDATE = 0x4,
+
+ /// Do not update the link information
+ SLR_NOUPDATE = 0x8,
+
+ /// Do not execute the search heuristics
+ SLR_NOSEARCH = 0x10,
+
+ /// Do not use distributed link tracking
+ SLR_NOTRACK = 0x20,
+
+ /// Disable distributed link tracking. By default, distributed link tracking tracks
+ /// removable media across multiple devices based on the volume name. It also uses the
+ /// Universal Naming Convention (UNC) path to track remote file systems whose drive letter
+ /// has changed. Setting SLR_NOLINKINFO disables both types of tracking.
+ SLR_NOLINKINFO = 0x40,
+
+ /// Call the Microsoft Windows Installer
+ SLR_INVOKE_MSI = 0x80
+ }
+
+
+ /// The IShellLink interface allows Shell links to be created, modified, and resolved
+ [ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214F9-0000-0000-C000-000000000046")]
+ interface IShellLinkW
+ {
+ /// Retrieves the path and file name of a Shell link object
+ [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)]
+ void GetPath([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, out WIN32_FIND_DATAW pfd, SLGP_FLAGS fFlags);
+
+ /// Retrieves the list of item identifiers for a Shell link object
+ [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)]
+ void GetIDList(out IntPtr ppidl);
+
+ /// Sets the pointer to an item identifier list (PIDL) for a Shell link object.
+ [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)]
+ void SetIDList(IntPtr pidl);
+
+ /// Retrieves the description string for a Shell link object
+ [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)]
+ void GetDescription([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName);
+
+ /// Sets the description for a Shell link object. The description can be any application-defined string
+ [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)]
+ void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName);
+
+ /// Retrieves the name of the working directory for a Shell link object
+ [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)]
+ void GetWorkingDirectory([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath);
+
+ /// Sets the name of the working directory for a Shell link object
+ [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)]
+ void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir);
+
+ /// Retrieves the command-line arguments associated with a Shell link object
+ [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)]
+ void GetArguments([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath);
+
+ /// Sets the command-line arguments for a Shell link object
+ [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)]
+ void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs);
+
+ /// Retrieves the hot key for a Shell link object
+ [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)]
+ void GetHotkey(out short pwHotkey);
+
+ /// Sets a hot key for a Shell link object
+ [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)]
+ void SetHotkey(short wHotkey);
+
+ /// Retrieves the show command for a Shell link object
+ [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)]
+ void GetShowCmd(out int piShowCmd);
+
+ /// Sets the show command for a Shell link object. The show command sets the initial show state of the window.
+ [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)]
+ void SetShowCmd(int iShowCmd);
+
+ /// Retrieves the location (path and index) of the icon for a Shell link object
+ [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)]
+ void GetIconLocation([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, int cchIconPath, out int piIcon);
+
+ /// Sets the location (path and index) of the icon for a Shell link object
+ [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)]
+ void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon);
+
+ /// Sets the relative path to the Shell link object
+ [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)]
+ void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, int dwReserved);
+
+ /// Attempts to find the target of a Shell link, even if it has been moved or renamed
+ [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)]
+ void Resolve(IntPtr hwnd, SLR_FLAGS fFlags);
+
+ /// Sets the path and file name of a Shell link object
+ [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)]
+ void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile);
+ }
+
+ [ComImport, Guid("0000010c-0000-0000-c000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IPersist
+ {
+ [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)]
+ void GetClassID(out Guid pClassID);
+ }
+
+
+ [ComImport, Guid("0000010b-0000-0000-C000-000000000046"),
+ InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IPersistFile : IPersist
+ {
+ [MethodImpl(MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)]
+ new void GetClassID(out Guid pClassID);
+
+ [MethodImpl(MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)]
+ int IsDirty();
+
+ [MethodImpl(MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)]
+ void Load([In, MarshalAs(UnmanagedType.LPWStr)] string pszFileName, uint dwMode);
+
+ [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)]
+ void Save([In, MarshalAs(UnmanagedType.LPWStr)] string pszFileName, [In, MarshalAs(UnmanagedType.Bool)] bool fRemember);
+
+ [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)]
+ void SaveCompleted([In, MarshalAs(UnmanagedType.LPWStr)] string pszFileName);
+
+ [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)]
+ void GetCurFile([In, MarshalAs(UnmanagedType.LPWStr)] string ppszFileName);
+ }
+
+ const uint STGM_READ = 0;
+ const int MAX_PATH = 260;
+
+ // CLSID_ShellLink from ShlGuid.h
+ [
+ ComImport(),
+ Guid("00021401-0000-0000-C000-000000000046")
+ ]
+ public class ShellLink
+ {
+ }
+
+ #endregion
+
+ public static string Resolve(string filename)
+ {
+ ShellLink link = new ShellLink();
+ ((IPersistFile) link).Load(filename, STGM_READ);
+ // If I can get hold of the hwnd call resolve first. This handles moved and renamed files.
+ // ((IShellLinkW)link).Resolve(hwnd, 0)
+ StringBuilder sb = new StringBuilder(MAX_PATH);
+ WIN32_FIND_DATAW data = new WIN32_FIND_DATAW();
+ ((IShellLinkW) link).GetPath(sb, sb.Capacity, out data, 0);
+ return sb.ToString();
+ }
+ }
+ }
+}
+
+// Developed using JetBrains Rider =)
diff --git a/Assets/LUXPiano/Plugins/Editor/JetBrains/Unity3DRider.cs.meta b/Assets/LUXPiano/Plugins/Editor/JetBrains/Unity3DRider.cs.meta
new file mode 100644
index 0000000..632830b
--- /dev/null
+++ b/Assets/LUXPiano/Plugins/Editor/JetBrains/Unity3DRider.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 1abb4305d6e736848a2adccf4fc0d572
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/LUXPiano/Prefabs.meta b/Assets/LUXPiano/Prefabs.meta
new file mode 100644
index 0000000..dff65f6
--- /dev/null
+++ b/Assets/LUXPiano/Prefabs.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 414407c70752f284ea4b510eac98e7f8
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/LUXPiano/Prefabs/LUXKey (Sharp).prefab b/Assets/LUXPiano/Prefabs/LUXKey (Sharp).prefab
new file mode 100644
index 0000000..4c9a9e0
--- /dev/null
+++ b/Assets/LUXPiano/Prefabs/LUXKey (Sharp).prefab
@@ -0,0 +1,142 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1001 &100100000
+Prefab:
+ m_ObjectHideFlags: 1
+ serializedVersion: 2
+ m_Modification:
+ m_TransformParent: {fileID: 0}
+ m_Modifications: []
+ m_RemovedComponents: []
+ m_ParentPrefab: {fileID: 0}
+ m_RootGameObject: {fileID: 1510759480481440}
+ m_IsPrefabParent: 1
+--- !u!1 &1510759480481440
+GameObject:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ serializedVersion: 5
+ m_Component:
+ - component: {fileID: 4735443396113658}
+ - component: {fileID: 114350742191996926}
+ m_Layer: 0
+ m_Name: LUXKey (Sharp)
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!1 &1702084857732414
+GameObject:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ serializedVersion: 5
+ m_Component:
+ - component: {fileID: 4430957499096772}
+ - component: {fileID: 33275080848273190}
+ - component: {fileID: 23951006577316942}
+ - component: {fileID: 65020162253031530}
+ m_Layer: 0
+ m_Name: Cube
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &4430957499096772
+Transform:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1702084857732414}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: -0.01, y: 0.0088, z: -0.06}
+ m_LocalScale: {x: 0.015, y: 0.02, z: 0.12}
+ m_Children: []
+ m_Father: {fileID: 4735443396113658}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!4 &4735443396113658
+Transform:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1510759480481440}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0.01, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children:
+ - {fileID: 4430957499096772}
+ m_Father: {fileID: 0}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!23 &23951006577316942
+MeshRenderer:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1702084857732414}
+ m_Enabled: 1
+ m_CastShadows: 1
+ m_ReceiveShadows: 1
+ m_DynamicOccludee: 1
+ m_MotionVectors: 1
+ m_LightProbeUsage: 1
+ m_ReflectionProbeUsage: 1
+ m_RenderingLayerMask: 4294967295
+ m_Materials:
+ - {fileID: 2100000, guid: d6d5818e4a8fcec48a4cf66657ee4b35, type: 2}
+ m_StaticBatchInfo:
+ firstSubMesh: 0
+ subMeshCount: 0
+ m_StaticBatchRoot: {fileID: 0}
+ m_ProbeAnchor: {fileID: 0}
+ m_LightProbeVolumeOverride: {fileID: 0}
+ m_ScaleInLightmap: 1
+ m_PreserveUVs: 0
+ m_IgnoreNormalsForChartDetection: 0
+ m_ImportantGI: 0
+ m_StitchLightmapSeams: 0
+ m_SelectedEditorRenderState: 3
+ m_MinimumChartSize: 4
+ m_AutoUVMaxDistance: 0.5
+ m_AutoUVMaxAngle: 89
+ m_LightmapParameters: {fileID: 0}
+ m_SortingLayerID: 0
+ m_SortingLayer: 0
+ m_SortingOrder: 0
+--- !u!33 &33275080848273190
+MeshFilter:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1702084857732414}
+ m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!65 &65020162253031530
+BoxCollider:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1702084857732414}
+ m_Material: {fileID: 0}
+ m_IsTrigger: 0
+ m_Enabled: 1
+ serializedVersion: 2
+ m_Size: {x: 1, y: 1, z: 1}
+ m_Center: {x: 0, y: 0, z: 0}
+--- !u!114 &114350742191996926
+MonoBehaviour:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1510759480481440}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: d8b41fbaae706404aaabf9d10d07f453, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ thisWidth: 0
+ pressTraversalDegrees: {x: -3.5, y: 0, z: 0}
+ isPressed: 0
diff --git a/Assets/LUXPiano/Prefabs/LUXKey (Sharp).prefab.meta b/Assets/LUXPiano/Prefabs/LUXKey (Sharp).prefab.meta
new file mode 100644
index 0000000..5d4c94c
--- /dev/null
+++ b/Assets/LUXPiano/Prefabs/LUXKey (Sharp).prefab.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 634a1eebb2a2dbe48b124f7fd80ff3f2
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 100100000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/LUXPiano/Prefabs/LUXKey.prefab b/Assets/LUXPiano/Prefabs/LUXKey.prefab
new file mode 100644
index 0000000..0bfd167
--- /dev/null
+++ b/Assets/LUXPiano/Prefabs/LUXKey.prefab
@@ -0,0 +1,142 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1001 &100100000
+Prefab:
+ m_ObjectHideFlags: 1
+ serializedVersion: 2
+ m_Modification:
+ m_TransformParent: {fileID: 0}
+ m_Modifications: []
+ m_RemovedComponents: []
+ m_ParentPrefab: {fileID: 0}
+ m_RootGameObject: {fileID: 1510759480481440}
+ m_IsPrefabParent: 1
+--- !u!1 &1510759480481440
+GameObject:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ serializedVersion: 5
+ m_Component:
+ - component: {fileID: 4735443396113658}
+ - component: {fileID: 114350742191996926}
+ m_Layer: 0
+ m_Name: LUXKey
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!1 &1702084857732414
+GameObject:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ serializedVersion: 5
+ m_Component:
+ - component: {fileID: 4430957499096772}
+ - component: {fileID: 33275080848273190}
+ - component: {fileID: 23951006577316942}
+ - component: {fileID: 65020162253031530}
+ m_Layer: 0
+ m_Name: Cube
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &4430957499096772
+Transform:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1702084857732414}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: -0.08}
+ m_LocalScale: {x: 0.02, y: 0.02, z: 0.16}
+ m_Children: []
+ m_Father: {fileID: 4735443396113658}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!4 &4735443396113658
+Transform:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1510759480481440}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children:
+ - {fileID: 4430957499096772}
+ m_Father: {fileID: 0}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!23 &23951006577316942
+MeshRenderer:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1702084857732414}
+ m_Enabled: 1
+ m_CastShadows: 1
+ m_ReceiveShadows: 1
+ m_DynamicOccludee: 1
+ m_MotionVectors: 1
+ m_LightProbeUsage: 1
+ m_ReflectionProbeUsage: 1
+ m_RenderingLayerMask: 4294967295
+ m_Materials:
+ - {fileID: 2100000, guid: 10a49312282010d4381356c509b81eb3, type: 2}
+ m_StaticBatchInfo:
+ firstSubMesh: 0
+ subMeshCount: 0
+ m_StaticBatchRoot: {fileID: 0}
+ m_ProbeAnchor: {fileID: 0}
+ m_LightProbeVolumeOverride: {fileID: 0}
+ m_ScaleInLightmap: 1
+ m_PreserveUVs: 0
+ m_IgnoreNormalsForChartDetection: 0
+ m_ImportantGI: 0
+ m_StitchLightmapSeams: 0
+ m_SelectedEditorRenderState: 3
+ m_MinimumChartSize: 4
+ m_AutoUVMaxDistance: 0.5
+ m_AutoUVMaxAngle: 89
+ m_LightmapParameters: {fileID: 0}
+ m_SortingLayerID: 0
+ m_SortingLayer: 0
+ m_SortingOrder: 0
+--- !u!33 &33275080848273190
+MeshFilter:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1702084857732414}
+ m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!65 &65020162253031530
+BoxCollider:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1702084857732414}
+ m_Material: {fileID: 0}
+ m_IsTrigger: 0
+ m_Enabled: 1
+ serializedVersion: 2
+ m_Size: {x: 1, y: 1, z: 1}
+ m_Center: {x: 0, y: 0, z: 0}
+--- !u!114 &114350742191996926
+MonoBehaviour:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1510759480481440}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: d8b41fbaae706404aaabf9d10d07f453, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ thisWidth: 0.021
+ pressTraversalDegrees: {x: -3.5, y: 0, z: 0}
+ isPressed: 0
diff --git a/Assets/LUXPiano/Prefabs/LUXKey.prefab.meta b/Assets/LUXPiano/Prefabs/LUXKey.prefab.meta
new file mode 100644
index 0000000..a8d99e7
--- /dev/null
+++ b/Assets/LUXPiano/Prefabs/LUXKey.prefab.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 6ca8c4fefd0d74a45b81aa29f12d62af
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 100100000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/LUXPiano/Prefabs/LUXPiano.prefab b/Assets/LUXPiano/Prefabs/LUXPiano.prefab
new file mode 100644
index 0000000..68e7444
--- /dev/null
+++ b/Assets/LUXPiano/Prefabs/LUXPiano.prefab
@@ -0,0 +1,105 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1001 &100100000
+Prefab:
+ m_ObjectHideFlags: 1
+ serializedVersion: 2
+ m_Modification:
+ m_TransformParent: {fileID: 0}
+ m_Modifications: []
+ m_RemovedComponents: []
+ m_ParentPrefab: {fileID: 0}
+ m_RootGameObject: {fileID: 1126565012880550}
+ m_IsPrefabParent: 1
+--- !u!1 &1114720910048750
+GameObject:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ serializedVersion: 5
+ m_Component:
+ - component: {fileID: 4215781214681260}
+ - component: {fileID: 114262336673180654}
+ m_Layer: 0
+ m_Name: Keys
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!1 &1126565012880550
+GameObject:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ serializedVersion: 5
+ m_Component:
+ - component: {fileID: 4743298813287564}
+ - component: {fileID: 114532666944635662}
+ m_Layer: 0
+ m_Name: LUXPiano
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &4215781214681260
+Transform:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1114720910048750}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children: []
+ m_Father: {fileID: 4743298813287564}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!4 &4743298813287564
+Transform:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1126565012880550}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children:
+ - {fileID: 4215781214681260}
+ m_Father: {fileID: 0}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &114262336673180654
+MonoBehaviour:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1114720910048750}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: a79ff9d0da3ab8d4eb2cd0859ed0f175, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ keyPrefab: {fileID: 114350742191996926, guid: 6ca8c4fefd0d74a45b81aa29f12d62af,
+ type: 2}
+ keyPrefabSharp: {fileID: 114350742191996926, guid: 634a1eebb2a2dbe48b124f7fd80ff3f2,
+ type: 2}
+ spawnKeysOnEnable: 1
+ startNote: 21
+ endNote: 108
+--- !u!114 &114532666944635662
+MonoBehaviour:
+ m_ObjectHideFlags: 1
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 100100000}
+ m_GameObject: {fileID: 1126565012880550}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 0df5f5e10f781924d8cb1bb0a4074b30, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ logEvents: 0
+ resizeKeysToRange: 1
+ minKey: 2147483647
+ maxKey: -2147483648
diff --git a/Assets/LUXPiano/Prefabs/LUXPiano.prefab.meta b/Assets/LUXPiano/Prefabs/LUXPiano.prefab.meta
new file mode 100644
index 0000000..b8729cc
--- /dev/null
+++ b/Assets/LUXPiano/Prefabs/LUXPiano.prefab.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 4924853a019ee5a4dbd90e8ef957eff9
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 100100000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/LUXPiano/Scripts.meta b/Assets/LUXPiano/Scripts.meta
new file mode 100644
index 0000000..19d61bd
--- /dev/null
+++ b/Assets/LUXPiano/Scripts.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 6cc6e59a13c08a542974e0cc0e25b5fc
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/LUXPiano/Scripts/Key.cs b/Assets/LUXPiano/Scripts/Key.cs
new file mode 100644
index 0000000..bf5003e
--- /dev/null
+++ b/Assets/LUXPiano/Scripts/Key.cs
@@ -0,0 +1,60 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace LUX
+{
+ public class Key : MonoBehaviour
+ {
+ [Tooltip("Used for spacing against other keys")]
+ public float thisWidth;
+
+ [Tooltip("How many degrees should the key move when pressed?")]
+ public Vector3 pressTraversalDegrees = new Vector3(-3.5f, 0, 0);
+
+ [Tooltip("Is the key currently being held?")] [ReadOnly]
+ public bool isPressed = false;
+
+ // Store the rotation vectors locally:
+ protected Vector3 staticRotation;
+
+ public int note { get; protected set; }
+
+ public void SetNote(int to)
+ {
+ note = to;
+ gameObject.name = "LUX.Key (" + ((Note) Notes.GetNoteFromOctive(note)).ToString() + ")";
+
+ // Set the rotation here because this is after instantiation/setup. Keypress could still happen this tick.
+ staticRotation = transform.localEulerAngles;
+ }
+
+ public void OnPressed(float velocity)
+ {
+ //Debug.Log(gameObject.name + " Pressed, velocity: "+velocity);
+ if (isPressed)
+ {
+ // Debug.LogWarning("Key can't be double pressed.", gameObject);
+ // nb: This can happen if you have more than one keyboard plugged in so COULD happen. let's just ignore.
+ return;
+ }
+
+ isPressed = true;
+ transform.localEulerAngles = staticRotation + pressTraversalDegrees;
+ }
+
+ public void OnReleased()
+ {
+ //Debug.Log(gameObject.name + " Released");
+ if (!isPressed)
+ {
+ //Debug.LogWarning("Key can't be double unpressed.");
+ // nb: This can happen if you have more than one keyboard plugged in so COULD happen. let's just ignore.
+ return;
+ }
+
+ isPressed = false;
+ transform.localEulerAngles = staticRotation;
+ }
+ }
+}
diff --git a/Assets/LUXPiano/Scripts/Key.cs.meta b/Assets/LUXPiano/Scripts/Key.cs.meta
new file mode 100644
index 0000000..47f26f9
--- /dev/null
+++ b/Assets/LUXPiano/Scripts/Key.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d8b41fbaae706404aaabf9d10d07f453
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/LUXPiano/Scripts/Keys.cs b/Assets/LUXPiano/Scripts/Keys.cs
new file mode 100644
index 0000000..b582d71
--- /dev/null
+++ b/Assets/LUXPiano/Scripts/Keys.cs
@@ -0,0 +1,130 @@
+using System.Collections;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using UnityEngine;
+
+namespace LUX
+{
+ public class Keys : MonoBehaviour
+ {
+ [Tooltip("Prefab for spawning piano keys")]
+ public Key keyPrefab;
+ public Key keyPrefabSharp;
+
+ public bool spawnKeysOnEnable = true;
+
+ [Tooltip("MIDI specified note numbers for keyboard layout. eg: Middle C is 60")]
+ // http://computermusicresource.com/midikeys.html
+ public int startNote = 21;
+ public int endNote = 108;
+
+ public int numKeys
+ {
+ get
+ {
+ if (startNote > endNote)
+ {
+ return 0;
+ }
+ return endNote - startNote + 1;
+ }
+ }
+
+ protected Dictionary keys = new Dictionary();
+
+ protected void OnEnable()
+ {
+ if (spawnKeysOnEnable)
+ {
+ CreateKeys();
+ }
+ }
+
+ ///
+ /// Destroys all keys that we've previously created.
+ ///
+ public void DestroyKeys()
+ {
+ foreach (KeyValuePair entry in keys)
+ {
+ Destroy(entry.Value.gameObject);
+ }
+
+ keys.Clear();
+ }
+
+ ///
+ /// Creates a whole new set of keys.
+ ///
+ public void CreateKeys()
+ {
+ // clear out the old:
+ DestroyKeys();
+
+ if (numKeys <= 0)
+ {
+ Debug.LogError("Can't make <= 0 keys.");
+ return;
+ }
+
+ int note = startNote;
+ Vector3 spawnPosition = transform.position;
+ Key lastKey = null;
+ while (note <= endNote)
+ {
+ if (lastKey != null)
+ {
+ // black keys have a thisWidth of zero.
+ spawnPosition += transform.right * lastKey.thisWidth;
+ }
+ lastKey = CreateKey(note, spawnPosition, transform.rotation);
+ note++;
+ }
+ }
+
+ private Key CreateKey(int note, Vector3 position, Quaternion rotation)
+ {
+ Key prefab;
+ if (Notes.IsSharp(note))
+ {
+ prefab = keyPrefabSharp;
+ }
+ else
+ {
+ prefab = keyPrefab;
+ }
+
+ Key key = Instantiate(prefab, position, rotation);
+ key.transform.SetParent(transform, true);
+ key.SetNote(note);
+ keys.Add(note, key);
+ return key;
+ }
+
+ public void OnKeyPressed(int note, float velocity)
+ {
+ if (keys.ContainsKey(note))
+ {
+ OnKeyPressed(keys[note], velocity);
+ }
+ }
+
+ public void OnKeyPressed(Key key, float velocity)
+ {
+ key.OnPressed(velocity);
+ }
+
+ public void OnKeyReleased(int note)
+ {
+ if (keys.ContainsKey(note))
+ {
+ OnKeyReleased(keys[note]);
+ }
+ }
+
+ public void OnKeyReleased(Key key)
+ {
+ key.OnReleased();
+ }
+ }
+}
diff --git a/Assets/LUXPiano/Scripts/Keys.cs.meta b/Assets/LUXPiano/Scripts/Keys.cs.meta
new file mode 100644
index 0000000..f8179cb
--- /dev/null
+++ b/Assets/LUXPiano/Scripts/Keys.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a79ff9d0da3ab8d4eb2cd0859ed0f175
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/LUXPiano/Scripts/MidiInput.cs b/Assets/LUXPiano/Scripts/MidiInput.cs
new file mode 100644
index 0000000..766d9d1
--- /dev/null
+++ b/Assets/LUXPiano/Scripts/MidiInput.cs
@@ -0,0 +1,80 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using MidiJack;
+using UnityEngine;
+
+namespace LUX
+{
+ public class MidiInput : MonoBehaviour
+ {
+ public bool logEvents = false;
+ public bool resizeKeysToRange = false;
+
+ protected Keys luxKeys;
+ [ReadOnly] public int minKey = int.MaxValue;
+ [ReadOnly] public int maxKey = int.MinValue;
+
+ protected void OnEnable()
+ {
+ MidiMaster.noteOnDelegate += OnNoteOn;
+ MidiMaster.noteOffDelegate += OnNoteOff;
+ luxKeys = GetComponentInChildren();
+ }
+
+ protected void OnDisable()
+ {
+ MidiMaster.noteOnDelegate -= OnNoteOn;
+ MidiMaster.noteOffDelegate -= OnNoteOff;
+ }
+
+ private void OnNoteOn(MidiChannel channel, int note, float velocity)
+ {
+ Log("[NoteOn] channel: " + channel.ToString() + " note: " + note + " velocity: " + velocity);
+ CheckNoteRange(note);
+ luxKeys.OnKeyPressed(note, velocity);
+ }
+
+ private void OnNoteOff(MidiChannel channel, int note)
+ {
+ Log("[NoteOff] channel: " + channel.ToString() + " note: " + note);
+ CheckNoteRange(note);
+ luxKeys.OnKeyReleased(note);
+ }
+
+ private void CheckNoteRange(int note)
+ {
+ bool rangeChanged = false;
+ if (note < minKey)
+ {
+ minKey = note;
+ rangeChanged = true;
+ }
+
+ if (note > maxKey)
+ {
+ maxKey = note;
+ rangeChanged = true;
+ }
+
+ if (rangeChanged)
+ {
+ if (resizeKeysToRange)
+ {
+ luxKeys.startNote = minKey;
+ luxKeys.endNote = maxKey;
+ luxKeys.CreateKeys();
+ }
+ }
+ }
+
+ protected void Log(string message)
+ {
+ if (logEvents)
+ {
+ Debug.Log(message);
+ }
+ }
+
+ }
+}
diff --git a/Assets/LUXPiano/Scripts/MidiInput.cs.meta b/Assets/LUXPiano/Scripts/MidiInput.cs.meta
new file mode 100644
index 0000000..6a8175c
--- /dev/null
+++ b/Assets/LUXPiano/Scripts/MidiInput.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0df5f5e10f781924d8cb1bb0a4074b30
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/LUXPiano/Scripts/Notes.cs b/Assets/LUXPiano/Scripts/Notes.cs
new file mode 100644
index 0000000..206c664
--- /dev/null
+++ b/Assets/LUXPiano/Scripts/Notes.cs
@@ -0,0 +1,65 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace LUX
+{
+ public enum Note : int
+ {
+ C = 0,
+ Cs = 1,
+ D = 2,
+ Ds = 3,
+ E = 4,
+ F = 5,
+ Fs = 6,
+ G = 7,
+ Gs = 8,
+ A = 9,
+ As = 10,
+ B = 11
+ }
+
+ public class Notes
+ {
+ public static bool IsSharp(int note)
+ {
+ if (IsNote(note, Note.Cs) ||
+ IsNote(note, Note.Ds) ||
+ IsNote(note, Note.Fs) ||
+ IsNote(note, Note.Gs) ||
+ IsNote(note, Note.As))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ public static bool IsNotSharp(int note)
+ {
+ return !IsSharp(note);
+ }
+
+ public static bool IsNote(int note, Note comparitive)
+ {
+ note = GetNoteFromOctive(note);
+ int compareValue = (int) comparitive;
+ if (compareValue == note)
+ {
+ return true;
+ }
+ return false;
+ }
+
+ public static int GetNoteFromOctive(int note)
+ {
+ while (note >= 12)
+ {
+ note -= 12;
+ }
+
+ return note;
+ }
+ }
+}
diff --git a/Assets/LUXPiano/Scripts/Notes.cs.meta b/Assets/LUXPiano/Scripts/Notes.cs.meta
new file mode 100644
index 0000000..91c793d
--- /dev/null
+++ b/Assets/LUXPiano/Scripts/Notes.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 87177767b4ad192468fcd7517303145e
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/LUXPiano/Scripts/ReadOnlyAttribute.cs b/Assets/LUXPiano/Scripts/ReadOnlyAttribute.cs
new file mode 100644
index 0000000..8fccb68
--- /dev/null
+++ b/Assets/LUXPiano/Scripts/ReadOnlyAttribute.cs
@@ -0,0 +1,33 @@
+using UnityEngine;
+#if UNITY_EDITOR
+using UnityEditor;
+#endif
+
+namespace LUX
+{
+ public class ReadOnly : PropertyAttribute
+ {
+
+ }
+
+#if UNITY_EDITOR
+ [CustomPropertyDrawer(typeof(ReadOnly))]
+ public class ReadOnlyDrawer : PropertyDrawer
+ {
+ public override float GetPropertyHeight(SerializedProperty property,
+ GUIContent label)
+ {
+ return EditorGUI.GetPropertyHeight(property, label, true);
+ }
+
+ public override void OnGUI(Rect position,
+ SerializedProperty property,
+ GUIContent label)
+ {
+ GUI.enabled = false;
+ EditorGUI.PropertyField(position, property, label, true);
+ GUI.enabled = true;
+ }
+ }
+#endif
+}
\ No newline at end of file
diff --git a/Assets/LUXPiano/Scripts/ReadOnlyAttribute.cs.meta b/Assets/LUXPiano/Scripts/ReadOnlyAttribute.cs.meta
new file mode 100644
index 0000000..7231659
--- /dev/null
+++ b/Assets/LUXPiano/Scripts/ReadOnlyAttribute.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 170b28fee6effce4091e9ab67d61f14b
+timeCreated: 1467488107
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Piano.cs b/Assets/Piano.cs
new file mode 100644
index 0000000..946221c
--- /dev/null
+++ b/Assets/Piano.cs
@@ -0,0 +1,16 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+public class Piano : MonoBehaviour {
+
+ // Use this for initialization
+ void Start () {
+
+ }
+
+ // Update is called once per frame
+ void Update () {
+
+ }
+}
diff --git a/Assets/Piano.cs.meta b/Assets/Piano.cs.meta
new file mode 100644
index 0000000..0b71e5f
--- /dev/null
+++ b/Assets/Piano.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: b0bd47d019258fd41a80b6321ed368df
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scenes/SampleScene.unity b/Assets/Scenes/Example Scene.unity
similarity index 77%
rename from Assets/Scenes/SampleScene.unity
rename to Assets/Scenes/Example Scene.unity
index 6dcbeae..9a14acd 100644
--- a/Assets/Scenes/SampleScene.unity
+++ b/Assets/Scenes/Example Scene.unity
@@ -179,13 +179,13 @@ Transform:
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 282840810}
- m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
- m_LocalPosition: {x: 0, y: 1, z: -10}
+ m_LocalRotation: {x: 0.20551528, y: -0, z: -0, w: 0.97865397}
+ m_LocalPosition: {x: 0, y: 17.39, z: -27.44}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 0
- m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_LocalEulerAnglesHint: {x: 23.719002, y: 0, z: 0}
--- !u!1 &1526980365
GameObject:
m_ObjectHideFlags: 0
@@ -251,3 +251,45 @@ Transform:
m_Father: {fileID: 0}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}
+--- !u!1001 &1779513520
+Prefab:
+ m_ObjectHideFlags: 0
+ serializedVersion: 2
+ m_Modification:
+ m_TransformParent: {fileID: 0}
+ m_Modifications:
+ - target: {fileID: 4743298813287564, guid: 4924853a019ee5a4dbd90e8ef957eff9, type: 2}
+ propertyPath: m_LocalPosition.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 4743298813287564, guid: 4924853a019ee5a4dbd90e8ef957eff9, type: 2}
+ propertyPath: m_LocalPosition.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 4743298813287564, guid: 4924853a019ee5a4dbd90e8ef957eff9, type: 2}
+ propertyPath: m_LocalPosition.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 4743298813287564, guid: 4924853a019ee5a4dbd90e8ef957eff9, type: 2}
+ propertyPath: m_LocalRotation.x
+ value: -0
+ objectReference: {fileID: 0}
+ - target: {fileID: 4743298813287564, guid: 4924853a019ee5a4dbd90e8ef957eff9, type: 2}
+ propertyPath: m_LocalRotation.y
+ value: -0
+ objectReference: {fileID: 0}
+ - target: {fileID: 4743298813287564, guid: 4924853a019ee5a4dbd90e8ef957eff9, type: 2}
+ propertyPath: m_LocalRotation.z
+ value: -0
+ objectReference: {fileID: 0}
+ - target: {fileID: 4743298813287564, guid: 4924853a019ee5a4dbd90e8ef957eff9, type: 2}
+ propertyPath: m_LocalRotation.w
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 4743298813287564, guid: 4924853a019ee5a4dbd90e8ef957eff9, type: 2}
+ propertyPath: m_RootOrder
+ value: 2
+ objectReference: {fileID: 0}
+ m_RemovedComponents: []
+ m_ParentPrefab: {fileID: 100100000, guid: 4924853a019ee5a4dbd90e8ef957eff9, type: 2}
+ m_IsPrefabParent: 0
diff --git a/Assets/Scenes/SampleScene.unity.meta b/Assets/Scenes/Example Scene.unity.meta
similarity index 100%
rename from Assets/Scenes/SampleScene.unity.meta
rename to Assets/Scenes/Example Scene.unity.meta
diff --git a/project.sln.DotSettings b/project.sln.DotSettings
new file mode 100644
index 0000000..fd88f79
--- /dev/null
+++ b/project.sln.DotSettings
@@ -0,0 +1,80 @@
+
+ False
+ True
+ DO_NOT_SHOW
+ DO_NOT_SHOW
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ DO_NOT_SHOW
+ DO_NOT_SHOW
+ WARNING
+
+
+ Required
+ Required
+ Required
+
+
+ Required
+ 3
+ 0
+ END_OF_LINE
+ END_OF_LINE
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ END_OF_LINE
+ END_OF_LINE
+ TOGETHER_SAME_LINE
+
+
+ NO_INDENT
+ Tab
+ True
+ True
+ True
+ True
+ True
+ True
+ END_OF_LINE
+ END_OF_LINE
+ True
+ True
+ END_OF_LINE
+ NEVER
+ END_OF_LINE
+ False
+ UseExplicitType
+ UseExplicitType
+ UseExplicitType
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
\ No newline at end of file