[
  {
    "path": ".angulardoc.json",
    "content": "{\n  \"repoId\": \"132e1921-3d2f-40c3-9aad-ca5bf9141f7d\",\n  \"lastSync\": 0\n}"
  },
  {
    "path": ".gitattributes",
    "content": "###############################################################################\n# Set default behavior to automatically normalize line endings.\n###############################################################################\n* text=auto\n\n###############################################################################\n# Set default behavior for command prompt diff.\n#\n# This is need for earlier builds of msysgit that does not have it on by\n# default for csharp files.\n# Note: This is only used by command line\n###############################################################################\n#*.cs     diff=csharp\n\n###############################################################################\n# Set the merge driver for project and solution files\n#\n# Merging from the command prompt will add diff markers to the files if there\n# are conflicts (Merging from VS is not affected by the settings below, in VS\n# the diff markers are never inserted). Diff markers may cause the following \n# file extensions to fail to load in VS. An alternative would be to treat\n# these files as binary and thus will always conflict and require user\n# intervention with every merge. To do so, just uncomment the entries below\n###############################################################################\n#*.sln       merge=binary\n#*.csproj    merge=binary\n#*.vbproj    merge=binary\n#*.vcxproj   merge=binary\n#*.vcproj    merge=binary\n#*.dbproj    merge=binary\n#*.fsproj    merge=binary\n#*.lsproj    merge=binary\n#*.wixproj   merge=binary\n#*.modelproj merge=binary\n#*.sqlproj   merge=binary\n#*.wwaproj   merge=binary\n\n###############################################################################\n# behavior for image files\n#\n# image files are treated as binary by default.\n###############################################################################\n#*.jpg   binary\n#*.png   binary\n#*.gif   binary\n\n###############################################################################\n# diff behavior for common document formats\n# \n# Convert binary document formats to text before diffing them. This feature\n# is only available from the command line. Turn it on by uncommenting the \n# entries below.\n###############################################################################\n#*.doc   diff=astextplain\n#*.DOC   diff=astextplain\n#*.docx  diff=astextplain\n#*.DOCX  diff=astextplain\n#*.dot   diff=astextplain\n#*.DOT   diff=astextplain\n#*.pdf   diff=astextplain\n#*.PDF   diff=astextplain\n#*.rtf   diff=astextplain\n#*.RTF   diff=astextplain\n"
  },
  {
    "path": ".gitignore",
    "content": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n\n# User-specific files\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# User-specific files (MonoDevelop/Xamarin Studio)\n*.userprefs\n\n# Build results\n[Dd]ebug/\n[Dd]ebugPublic/\n[Rr]elease/\n[Rr]eleases/\nx64/\nx86/\nbld/\n[Bb]in/\n[Oo]bj/\n[Ll]og/\n\n# Visual Studio 2015 cache/options directory\n.vs/\n# Uncomment if you have tasks that create the project's static files in wwwroot\n#wwwroot/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n# NUNIT\n*.VisualState.xml\nTestResult.xml\n\n# Build Results of an ATL Project\n[Dd]ebugPS/\n[Rr]eleasePS/\ndlldata.c\n\n# DNX\nproject.lock.json\nproject.fragment.lock.json\nartifacts/\n\n*_i.c\n*_p.c\n*_i.h\n*.ilk\n*.meta\n*.obj\n*.pch\n*.pdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.svclog\n*.scc\n\n# Chutzpah Test files\n_Chutzpah*\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opendb\n*.opensdf\n*.sdf\n*.cachefile\n*.VC.db\n*.VC.VC.opendb\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n*.sap\n\n# TFS 2012 Local Workspace\n$tf/\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n*.DotSettings.user\n\n# JustCode is a .NET coding add-in\n.JustCode\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# NCrunch\n_NCrunch_*\n.*crunch*.local.xml\nnCrunchTemp_*\n\n# MightyMoose\n*.mm.*\nAutoTest.Net/\n\n# Web workbench (sass)\n.sass-cache/\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.[Pp]ublish.xml\n*.azurePubxml\n# TODO: Comment the next line if you want to checkin your web deploy settings\n# but database connection strings (with potential passwords) will be unencrypted\n#*.pubxml\n*.publishproj\n\n# Microsoft Azure Web App publish settings. Comment the next line if you want to\n# checkin your Azure Web App publish settings, but sensitive information contained\n# in these scripts will be unencrypted\nPublishScripts/\n\n# NuGet Packages\n*.nupkg\n# The packages folder can be ignored because of Package Restore\n**/packages/*\n# except build/, which is used as an MSBuild target.\n!**/packages/build/\n# Uncomment if necessary however generally it will be regenerated when needed\n#!**/packages/repositories.config\n# NuGet v3's project.json files produces more ignoreable files\n*.nuget.props\n*.nuget.targets\n\n# Microsoft Azure Build Output\ncsx/\n*.build.csdef\n\n# Microsoft Azure Emulator\necf/\nrcf/\n\n# Windows Store app package directories and files\nAppPackages/\nBundleArtifacts/\nPackage.StoreAssociation.xml\n_pkginfo.txt\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!*.[Cc]ache/\n\n# Others\nClientBin/\n~$*\n*~\n*.dbmdl\n*.dbproj.schemaview\n*.jfm\n*.pfx\n*.publishsettings\nnode_modules/\norleans.codegen.cs\n\n# Since there are multiple workflows, uncomment next line to ignore bower_components\n# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)\n#bower_components/\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file\n# to a newer Visual Studio version. Backup files are not needed,\n# because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\n\n# SQL Server files\n*.mdf\n*.ldf\n\n# Business Intelligence projects\n*.rdl.data\n*.bim.layout\n*.bim_*.settings\n\n# Microsoft Fakes\nFakesAssemblies/\n\n# GhostDoc plugin setting file\n*.GhostDoc.xml\n\n# Node.js Tools for Visual Studio\n.ntvs_analysis.dat\n\n# Visual Studio 6 build log\n*.plg\n\n# Visual Studio 6 workspace options file\n*.opt\n\n# Visual Studio LightSwitch build output\n**/*.HTMLClient/GeneratedArtifacts\n**/*.DesktopClient/GeneratedArtifacts\n**/*.DesktopClient/ModelManifest.xml\n**/*.Server/GeneratedArtifacts\n**/*.Server/ModelManifest.xml\n_Pvt_Extensions\n\n# Paket dependency manager\n.paket/paket.exe\npaket-files/\n\n# FAKE - F# Make\n.fake/\n\n# JetBrains Rider\n.idea/\n*.sln.iml\n\n# CodeRush\n.cr/\n\n# Python Tools for Visual Studio (PTVS)\n__pycache__/\n*.pyc\n\ncleanup.bat\n\nbin\n*/obj/*\n\nbuild/"
  },
  {
    "path": "BMEngine/BufferByteReader.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace ZenithEngine\n{\n    public class BufferByteReader\n    {\n        long pos;\n        int buffersize;\n        int bufferpos;\n        int maxbufferpos;\n        long streamstart;\n        long streamlen;\n        Stream stream;\n        byte[] buffer;\n        byte[] bufferNext;\n        Task nextReader = null;\n\n        public BufferByteReader(Stream stream, int buffersize, long streamstart, long streamlen)\n        {\n            if (buffersize > streamlen) buffersize = (int)streamlen;\n            this.buffersize = buffersize;\n            this.streamstart = streamstart;\n            this.streamlen = streamlen;\n            this.stream = stream;\n            buffer = new byte[buffersize];\n            bufferNext = new byte[buffersize];\n            UpdateBuffer(pos, true);\n        }\n\n        void UpdateBuffer(long pos, bool first = false)\n        {\n            if (first)\n            {\n                nextReader = Task.Run(() =>\n                {\n                    lock (stream)\n                    {\n                        stream.Position = pos + streamstart;\n                        stream.Read(bufferNext, 0, buffersize);\n                    }\n                });\n            }\n            nextReader.GetAwaiter().GetResult();\n            Buffer.BlockCopy(bufferNext, 0, buffer, 0, buffersize);\n            nextReader = Task.Run(() =>\n            {\n                lock (stream)\n                {\n                    stream.Position = pos + streamstart + buffersize;\n                    stream.Read(bufferNext, 0, buffersize);\n                }\n            });\n            nextReader.GetAwaiter().GetResult();\n            //lock (stream)\n            //{\n            //    stream.Position = pos + streamstart;\n            //    stream.Read(buffer, 0, buffersize);\n            //}\n            maxbufferpos = (int)Math.Min(streamlen - pos + 1, buffersize);\n        }\n\n        public long Location => pos;\n\n        public int Pushback = -1;\n\n        public byte Read()\n        {\n            if (Pushback != -1)\n            {\n                byte _b = (byte)Pushback;\n                Pushback = -1;\n                return _b;\n            }\n            byte b = buffer[bufferpos++];\n            if (bufferpos < maxbufferpos) return b;\n            else if (bufferpos >= buffersize)\n            {\n                pos += bufferpos;\n                bufferpos = 0;\n                UpdateBuffer(pos);\n                return b;\n            }\n            else throw new IndexOutOfRangeException();\n        }\n\n        public byte ReadFast()\n        {\n            byte b = buffer[bufferpos++];\n            if (bufferpos < maxbufferpos) return b;\n            else if (bufferpos >= buffersize)\n            {\n                pos += bufferpos;\n                bufferpos = 0;\n                UpdateBuffer(pos);\n                return b;\n            }\n            else throw new IndexOutOfRangeException();\n        }\n\n        public void Reset()\n        {\n            pos = 0;\n            bufferpos = 0;\n            UpdateBuffer(pos, true);\n        }\n\n        public void Skip(int count)\n        {\n            for (int i = 0; i < count; i++)\n            {\n                if(Pushback != -1)\n                {\n                    Pushback = -1;\n                    continue;\n                }\n                bufferpos++;\n                if (bufferpos < maxbufferpos) continue;\n                if (bufferpos >= buffersize)\n                {\n                    pos += bufferpos;\n                    bufferpos = 0;\n                    UpdateBuffer(pos);\n                }\n                else throw new IndexOutOfRangeException();\n            }\n        }\n\n        public void Dispose()\n        {\n            buffer = null;\n        }\n    }\n}\n"
  },
  {
    "path": "BMEngine/FastList.cs",
    "content": "﻿using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace ZenithEngine\n{\n    public class FastList<T> : IEnumerable<T>\n    {\n        private class ListItem\n        {\n            public ListItem Next;\n            public T item;\n        }\n\n        private ListItem root = new ListItem();\n        private ListItem last = null;\n\n        public T First\n        {\n            get\n            {\n                if (root.Next != null) return root.Next.item;\n                else return default(T);\n            }\n        }\n        public class Iterator\n        {\n            FastList<T> _ilist;\n\n            private ListItem prev;\n            private ListItem curr;\n\n            internal Iterator(FastList<T> ll)\n            {\n                _ilist = ll;\n                Reset();\n            }\n\n            public bool MoveNext(out T v)\n            {\n                ListItem ll = curr.Next;\n\n                if (ll == null)\n                {\n                    v = default(T);\n                    _ilist.last = curr;\n                    return false;\n                }\n\n                v = ll.item;\n\n                prev = curr;\n                curr = ll;\n\n                return true;\n            }\n\n            public void Remove()\n            {\n                if (_ilist.last.Equals(curr)) _ilist.last = prev;\n                prev.Next = curr.Next;\n            }\n\n            public void Insert(T item)\n            {\n                var i = new ListItem()\n                {\n                    item = item,\n                    Next = curr\n                };\n                if (prev == null)\n                    _ilist.root.Next = i;\n                else\n                    prev.Next = i;\n                //if (curr.Equals(_ilist.last))\n                //{\n                //    _ilist.last = curr;\n                //}\n            }\n\n            public void Reset()\n            {\n                this.prev = null;\n                this.curr = _ilist.root;\n            }\n        }\n\n        public class FastIterator : IEnumerator<T>\n        {\n            FastList<T> _ilist;\n\n            private ListItem curr;\n\n            internal FastIterator(FastList<T> ll)\n            {\n                _ilist = ll;\n                Reset();\n            }\n\n            public object Current => curr.item;\n\n            T IEnumerator<T>.Current => curr.item;\n\n            public void Dispose()\n            {\n\n            }\n\n            public bool MoveNext()\n            {\n                try\n                {\n                    curr = curr.Next;\n\n                    return curr != null;\n                }\n                catch { return false; }\n            }\n\n            public void Reset()\n            {\n                this.curr = _ilist.root;\n            }\n        }\n\n        public void Add(T item)\n        {\n            ListItem li = new ListItem();\n            li.item = item;\n\n            if (root.Next != null && last != null)\n            {\n                while (last.Next != null) last = last.Next;\n                last.Next = li;\n            }\n            else\n                root.Next = li;\n\n            last = li;\n        }\n\n        public T Pop()\n        {\n            ListItem el = root.Next;\n            root.Next = el.Next;\n            return el.item;\n        }\n\n        public Iterator Iterate()\n        {\n            return new Iterator(this);\n        }\n\n        public bool ZeroLen => root.Next == null;\n\n        public IEnumerator<T> FastIterate()\n        {\n            return new FastIterator(this);\n        }\n\n        public void Unlink()\n        {\n            root.Next = null;\n            last = null;\n        }\n\n        public int Count()\n        {\n            int cnt = 0;\n\n            ListItem li = root.Next;\n            while (li != null)\n            {\n                cnt++;\n                li = li.Next;\n            }\n\n            return cnt;\n        }\n\n        public bool Any()\n        {\n            return root.Next != null;\n        }\n\n        IEnumerator IEnumerable.GetEnumerator()\n        {\n            return FastIterate();\n        }\n\n        public IEnumerator<T> GetEnumerator()\n        {\n            return FastIterate();\n        }\n    }\n}\n"
  },
  {
    "path": "BMEngine/GLTextEngine.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Drawing;\nusing System.Drawing.Drawing2D;\nusing System.Drawing.Text;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing OpenTK.Graphics.OpenGL;\nusing OpenTK.Graphics;\nusing OpenTK;\nusing System.Drawing.Imaging;\nusing Size = System.Drawing.Size;\n\nnamespace ZenithEngine\n{\n    public class GLTextEngine\n    {\n        #region Shaders\n        string textShaderVert = @\"#version 330 compatibility\n\nlayout(location = 0) in vec2 position;\nlayout(location = 1) in vec2 uv;\n\nuniform mat4 viewmat;\nuniform vec4 Col;\n\nout vec2 UV;\nout vec4 Color;\n\nvoid main()\n{\n    gl_Position = viewmat * vec4(position.x, position.y, 0, 1.0f);\n    UV = uv;\n    Color = Col;\n}\n\";\n        string textShaderFrag = @\"#version 330 compatibility\n\nin vec2 UV;\nin vec4 Color;\n\nout vec4 color;\n\nuniform sampler2D textureSampler;\n\nvoid main()\n{\n    float mask = texture2D( textureSampler, UV ).y;\n    color = vec4(1, 1, 1, mask) * Color;\n}\n\";\n        #endregion\n\n        int charMapTex;\n        Size mapCharSize;\n        SizeF[] charSizes;\n\n        int textShader;\n\n        int uniformMatrix;\n        int uniformColor;\n\n        int vertexBufferID;\n        int uvBufferID;\n\n        int quadBufferLength = 2048 * 2;\n        double[] quadVertexbuff;\n        double[] quaduvbuff;\n        int quadBufferPos = 0;\n\n        int indexBufferId;\n        uint[] indexes = new uint[2048 * 4 * 6];\n\n        public void Dispose()\n        {\n            GL.DeleteBuffers(3, new int[] { vertexBufferID });\n            GL.DeleteProgram(textShader);\n            GL.DeleteTexture(charMapTex);\n        }\n\n        public string Font { get; private set; } = \"\";\n        public int FontSize { get; private set; } = -1;\n\n        public GLTextEngine()\n        {\n            int _vertexObj = GL.CreateShader(ShaderType.VertexShader);\n            int _fragObj = GL.CreateShader(ShaderType.FragmentShader);\n            int statusCode;\n            string info;\n\n            GL.ShaderSource(_vertexObj, textShaderVert);\n            GL.CompileShader(_vertexObj);\n            info = GL.GetShaderInfoLog(_vertexObj);\n            GL.GetShader(_vertexObj, ShaderParameter.CompileStatus, out statusCode);\n            if (statusCode != 1) throw new ApplicationException(info);\n\n            GL.ShaderSource(_fragObj, textShaderFrag);\n            GL.CompileShader(_fragObj);\n            info = GL.GetShaderInfoLog(_fragObj);\n            GL.GetShader(_fragObj, ShaderParameter.CompileStatus, out statusCode);\n            if (statusCode != 1) throw new ApplicationException(info);\n\n            textShader = GL.CreateProgram();\n            GL.AttachShader(textShader, _fragObj);\n            GL.AttachShader(textShader, _vertexObj);\n            GL.LinkProgram(textShader);\n\n            quadVertexbuff = new double[quadBufferLength * 8];\n            quaduvbuff = new double[quadBufferLength * 8];\n\n            GL.GenBuffers(1, out vertexBufferID);\n            GL.GenBuffers(1, out uvBufferID);\n            for (uint i = 0; i < indexes.Length / 6; i++)\n            {\n                indexes[i * 6 + 0] = i * 4 + 0;\n                indexes[i * 6 + 1] = i * 4 + 1;\n                indexes[i * 6 + 2] = i * 4 + 3;\n                indexes[i * 6 + 3] = i * 4 + 1;\n                indexes[i * 6 + 4] = i * 4 + 3;\n                indexes[i * 6 + 5] = i * 4 + 2;\n            }\n\n            GL.GenBuffers(1, out indexBufferId);\n            GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexBufferId);\n            GL.BufferData(\n                BufferTarget.ElementArrayBuffer,\n                (IntPtr)(indexes.Length * 4),\n                indexes,\n                BufferUsageHint.StaticDraw);\n\n            uniformMatrix = GL.GetUniformLocation(textShader, \"viewmat\");\n            uniformColor = GL.GetUniformLocation(textShader, \"Col\");\n\n            charMapTex = GL.GenTexture();\n        }\n\n        public void SetFont(string font, int size)\n        {\n            var bitmap = GenerateCharacters(size, font, out mapCharSize, out charSizes);\n            loadImage(bitmap, charMapTex);\n            bitmap.Dispose();\n            Font = font;\n            FontSize = size;\n        }\n\n        public void SetFont(string font, int size, string charmap)\n        {\n            Characters = charmap;\n            SetFont(font, size);\n        }\n\n        public void SetFont(string font, System.Drawing.FontStyle fontStyle, int size)\n        {\n            var bitmap = GenerateCharacters(size, font, fontStyle, out mapCharSize, out charSizes);\n            loadImage(bitmap, charMapTex);\n            bitmap.Dispose();\n            Font = font;\n            FontSize = size;\n        }\n\n        public void SetFont(string font, System.Drawing.FontStyle fontStyle, int size, string charmap)\n        {\n            Characters = charmap;\n            SetFont(font, fontStyle, size);\n        }\n\n        void loadImage(Bitmap image, int texID)\n        {\n            GL.BindTexture(TextureTarget.Texture2D, texID);\n            BitmapData data = image.LockBits(new System.Drawing.Rectangle(0, 0, image.Width, image.Height),\n                ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);\n\n            GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, data.Width, data.Height, 0,\n                OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);\n\n            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);\n            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);\n            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge);\n            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge);\n\n            image.UnlockBits(data);\n        }\n\n        public void Render(string text, Matrix4 transform, Color4 color)\n        {\n            //GL.Enable(EnableCap.Blend);\n            //GL.EnableClientState(ArrayCap.VertexArray);\n            //GL.EnableClientState(ArrayCap.ColorArray);\n            //GL.EnableClientState(ArrayCap.TextureCoordArray);\n            //GL.Enable(EnableCap.Texture2D);\n\n            //GL.EnableVertexAttribArray(0);\n            //GL.EnableVertexAttribArray(1);\n\n            //GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);\n\n            GL.UseProgram(textShader);\n            GL.ActiveTexture(TextureUnit.Texture0);\n            GL.BindTexture(TextureTarget.Texture2D, charMapTex);\n\n            GL.Uniform4(uniformColor, color);\n            GL.UniformMatrix4(uniformMatrix, false, ref transform);\n\n            quadBufferPos = 0;\n            Vector2 curpos = new Vector2(0, 0);\n\n            int charImHeight = (Characters.Length - (Characters.Length % charImWidth)) / charImWidth + 1;\n            double charwidth = 1.0 / charImWidth;\n            double charheight = 1.0 / charImHeight;\n\n            foreach (char c in text)\n            {\n                if (c == '\\n')\n                {\n                    curpos.Y += mapCharSize.Height;\n                    curpos.X = 0;\n                }\n                if (c == ' ')\n                {\n                    curpos.X += mapCharSize.Width / 4.0f;\n                }\n                if (!Characters.Contains(c)) continue;\n                var chari = Characters.IndexOf(c);\n                var sz = charSizes[chari];\n                int charix = chari % charImWidth;\n                int chariy = (chari - charix) / charImWidth;\n                double sx = charwidth * charix;\n                double ex = sx + charSizes[chari].Width / mapCharSize.Width * charwidth;\n                double sy = charheight * chariy;\n                double ey = sy + charSizes[chari].Height / mapCharSize.Height * charheight;\n\n                float padding = mapCharSize.Width / 8f;\n                sz.Width -= padding * 2;\n                Vector2 endpos = curpos + new Vector2(sz.Width, sz.Height);\n\n                int pos = quadBufferPos * 8;\n                quadVertexbuff[pos++] = (curpos.X - padding);\n                quadVertexbuff[pos++] = curpos.Y;\n                quadVertexbuff[pos++] = (curpos.X - padding);\n                quadVertexbuff[pos++] = endpos.Y;\n                quadVertexbuff[pos++] = (endpos.X + padding);\n                quadVertexbuff[pos++] = endpos.Y;\n                quadVertexbuff[pos++] = (endpos.X + padding);\n                quadVertexbuff[pos++] = curpos.Y;\n\n                curpos.X += sz.Width;\n\n                pos = quadBufferPos * 8;\n                quaduvbuff[pos++] = sx;\n                quaduvbuff[pos++] = sy;\n                quaduvbuff[pos++] = sx;\n                quaduvbuff[pos++] = ey;\n                quaduvbuff[pos++] = ex;\n                quaduvbuff[pos++] = ey;\n                quaduvbuff[pos++] = ex;\n                quaduvbuff[pos++] = sy;\n                //quaduvbuff[pos++] = 0;\n                //quaduvbuff[pos++] = 0;\n                //quaduvbuff[pos++] = 1;\n                //quaduvbuff[pos++] = 0;\n                //quaduvbuff[pos++] = 1;\n                //quaduvbuff[pos++] = 1;\n                //quaduvbuff[pos++] = 0;\n                //quaduvbuff[pos++] = 1;\n                quadBufferPos++;\n                FlushQuadBuffer();\n            }\n            FlushQuadBuffer(false);\n\n            //GL.Disable(EnableCap.Blend);\n            //GL.Disable(EnableCap.Texture2D);\n            //GL.DisableClientState(ArrayCap.VertexArray);\n            //GL.DisableClientState(ArrayCap.ColorArray);\n            //GL.DisableClientState(ArrayCap.TextureCoordArray);\n\n            //GL.DisableVertexAttribArray(0);\n            //GL.DisableVertexAttribArray(1);\n        }\n\n        public SizeF GetBoundBox(string text)\n        {\n            Vector2 curpos = new Vector2(0, 0);\n            int rows = 1;\n            float maxWidth = 0;\n            float padding = mapCharSize.Width / 8f;\n            foreach (char c in text)\n            {\n                if (c == '\\n')\n                {\n                    curpos.X = 0;\n                    rows++;\n                }\n                if (!Characters.Contains(c)) continue;\n                var chari = Characters.IndexOf(c);\n                var sz = charSizes[chari];\n                sz.Width -= padding * 2;\n                Vector2 endpos = curpos + new Vector2(sz.Width, sz.Height);\n                curpos.X += sz.Width;\n                if (curpos.X > maxWidth) maxWidth = curpos.X;\n            }\n            return new SizeF(maxWidth + padding * 2, mapCharSize.Height * rows);\n        }\n\n        void FlushQuadBuffer(bool check = true)\n        {\n            if (quadBufferPos < quadBufferLength && check) return;\n            if (quadBufferPos == 0) return;\n            GL.BindBuffer(BufferTarget.ArrayBuffer, vertexBufferID);\n            GL.BufferData(\n                BufferTarget.ArrayBuffer,\n                (IntPtr)(quadVertexbuff.Length * 8),\n                quadVertexbuff,\n                BufferUsageHint.StaticDraw);\n            GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Double, false, 16, 0);\n            GL.BindBuffer(BufferTarget.ArrayBuffer, uvBufferID);\n            GL.BufferData(\n                BufferTarget.ArrayBuffer,\n                (IntPtr)(quaduvbuff.Length * 8),\n                quaduvbuff,\n                BufferUsageHint.StaticDraw);\n            GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Double, false, 16, 0);\n            GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexBufferId);\n            GL.IndexPointer(IndexPointerType.Int, 1, 0);\n            GL.DrawElements(PrimitiveType.Triangles, quadBufferPos * 6, DrawElementsType.UnsignedInt, IntPtr.Zero);\n            quadBufferPos = 0;\n        }\n\n        private string Characters = @\" qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM0123456789µ§½!\"\"#¤%&/()=?^*@£€${[]}\\~¨'-_.:,;<>|°©®±¥\";\n        private const int charImWidth = 20;\n\n        public Bitmap GenerateCharacters(int fontSize, string fontName, out Size charSize, out SizeF[] charSizes)\n        {\n            return GenerateCharacters(fontSize, fontName, System.Drawing.FontStyle.Regular, out charSize, out charSizes);\n        }\n\n        public Bitmap GenerateCharacters(int fontSize, string fontName, System.Drawing.FontStyle fontStyle, out Size charSize, out SizeF[] charSizes)\n        {\n            charSizes = new SizeF[Characters.Length];\n            var characters = new List<Bitmap>();\n            using (var font = new Font(fontName, fontSize, fontStyle))\n            {\n                for (int i = 0; i < Characters.Length; i++)\n                {\n                    var charBmp = GenerateCharacter(font, Characters[i]);\n                    charSizes[i] = GetSize(font, Characters[i]);\n                    characters.Add(charBmp);\n                }\n                charSize = new Size(characters.Max(x => x.Width), characters.Max(x => x.Height));\n\n                var charMap = new Bitmap(charSize.Width * charImWidth, charSize.Height * ((characters.Count - (characters.Count % charImWidth)) / charImWidth + 1));\n                using (var gfx = Graphics.FromImage(charMap))\n                {\n                    gfx.FillRectangle(Brushes.Black, 0, 0, charMap.Width, charMap.Height);\n                    for (int i = 0; i < characters.Count; i++)\n                    {\n                        var c = characters[i];\n                        int x = i % charImWidth;\n                        int y = (i - x) / charImWidth;\n                        gfx.DrawImageUnscaled(c, x * charSize.Width, y * charSize.Height);\n\n                        c.Dispose();\n                    }\n                }\n                return charMap;\n            }\n        }\n\n        private Bitmap GenerateCharacter(Font font, char c)\n        {\n            var size = GetSize(font, c);\n            var bmp = new Bitmap((int)size.Width, (int)size.Height);\n            using (var gfx = Graphics.FromImage(bmp))\n            {\n                gfx.FillRectangle(Brushes.Black, 0, 0, bmp.Width, bmp.Height);\n                gfx.DrawString(c.ToString(), font, Brushes.White, 0, 0);\n            }\n            return bmp;\n        }\n        private SizeF GetSize(Font font, char c)\n        {\n            using (var bmp = new Bitmap(512, 512))\n            {\n                using (var gfx = Graphics.FromImage(bmp))\n                {\n                    return gfx.MeasureString(c.ToString(), font);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "BMEngine/GLUtils.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing OpenTK.Graphics.OpenGL;\n\nnamespace ZenithEngine\n{\n    public static class GLUtils\n    {\n        public static int MakeShaderProgram(string name)\n        {\n            int _vertexObj = GL.CreateShader(ShaderType.VertexShader);\n            int _fragObj = GL.CreateShader(ShaderType.FragmentShader);\n            int statusCode;\n            string info;\n\n            GL.ShaderSource(_vertexObj, File.ReadAllText(\"Shaders\\\\\" + name + \".vert\"));\n            GL.CompileShader(_vertexObj);\n            info = GL.GetShaderInfoLog(_vertexObj);\n            GL.GetShader(_vertexObj, ShaderParameter.CompileStatus, out statusCode);\n            if (statusCode != 1) throw new ApplicationException(info);\n\n            GL.ShaderSource(_fragObj, File.ReadAllText(\"Shaders\\\\\" + name + \".frag\"));\n            GL.CompileShader(_fragObj);\n            info = GL.GetShaderInfoLog(_fragObj);\n            GL.GetShader(_fragObj, ShaderParameter.CompileStatus, out statusCode);\n            if (statusCode != 1) throw new ApplicationException(info);\n\n            int shader = GL.CreateProgram();\n            GL.AttachShader(shader, _fragObj);\n            GL.AttachShader(shader, _vertexObj);\n            GL.LinkProgram(shader);\n            return shader;\n        }\n\n        public static int MakePostShaderProgram(string name)\n        {\n            int _vertexObj = GL.CreateShader(ShaderType.VertexShader);\n            int _fragObj = GL.CreateShader(ShaderType.FragmentShader);\n            int statusCode;\n            string info;\n\n            GL.ShaderSource(_vertexObj, File.ReadAllText(\"Shaders\\\\Post\\\\post.vert\"));\n            GL.CompileShader(_vertexObj);\n            info = GL.GetShaderInfoLog(_vertexObj);\n            GL.GetShader(_vertexObj, ShaderParameter.CompileStatus, out statusCode);\n            if (statusCode != 1) throw new ApplicationException(info);\n\n            GL.ShaderSource(_fragObj, File.ReadAllText(\"Shaders\\\\Post\\\\\" + name + \".frag\"));\n            GL.CompileShader(_fragObj);\n            info = GL.GetShaderInfoLog(_fragObj);\n            GL.GetShader(_fragObj, ShaderParameter.CompileStatus, out statusCode);\n            if (statusCode != 1) throw new ApplicationException(info);\n\n            int shader = GL.CreateProgram();\n            GL.AttachShader(shader, _fragObj);\n            GL.AttachShader(shader, _vertexObj);\n            GL.LinkProgram(shader);\n            return shader;\n        }\n\n        public static void GenFrameBufferTexture(int width, int height, out int fbuffer, out int rtexture)\n        {\n            fbuffer = GL.GenFramebuffer();\n            GL.BindFramebuffer(FramebufferTarget.Framebuffer, fbuffer);\n            rtexture = GL.GenTexture();\n            GL.BindTexture(TextureTarget.Texture2D, rtexture);\n            GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, width, height, 0, PixelFormat.Rgba, PixelType.Byte, (IntPtr)0);\n            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);\n            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);\n            GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, rtexture, 0);\n            if (GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer) != FramebufferErrorCode.FramebufferComplete) throw new Exception();\n        }\n\n        public static void GenFrameBufferTexture3d(int width, int height, out int fbuffer, out int rtexture, out int depthrenderbuffer)\n        {\n            fbuffer = GL.GenFramebuffer();\n            GL.BindFramebuffer(FramebufferTarget.Framebuffer, fbuffer);\n            rtexture = GL.GenTexture();\n            GL.BindTexture(TextureTarget.Texture2D, rtexture);\n            GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, width, height, 0, PixelFormat.Rgba, PixelType.Byte, (IntPtr)0);\n            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);\n            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);\n                depthrenderbuffer = GL.GenRenderbuffer();\n                GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, depthrenderbuffer);\n                GL.RenderbufferStorage(RenderbufferTarget.Renderbuffer, RenderbufferStorage.DepthComponent, width, height);\n                GL.FramebufferRenderbuffer(FramebufferTarget.Framebuffer, FramebufferAttachment.DepthAttachment, RenderbufferTarget.Renderbuffer, depthrenderbuffer);\n\n            GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, rtexture, 0);\n            if (GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer) != FramebufferErrorCode.FramebufferComplete) throw new Exception();\n        }\n    }\n}\n"
  },
  {
    "path": "BMEngine/IPluginRender.cs",
    "content": "﻿using OpenTK.Graphics;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows.Controls;\nusing System.Windows.Media;\n\nnamespace ZenithEngine\n{\n    public interface IPluginRender : IDisposable\n    {\n        string Name { get; }\n        string Description { get; }\n        bool Initialized { get; }\n        ImageSource PreviewImage { get; }\n        bool ManualNoteDelete { get; }\n        double NoteCollectorOffset { get; }\n\n        NoteColor[][] NoteColors { set; }\n        double Tempo { set; }\n        \n        MidiInfo CurrentMidi { set; }\n\n        string LanguageDictName { get; }\n\n        double NoteScreenTime { get; }\n        long LastNoteCount { get; }\n        Control SettingsControl { get; }\n\n        void Init();\n        void RenderFrame(FastList<Note> notes, double midiTime, int finalCompositeBuff);\n        void ReloadTrackColors();\n    }\n}\n"
  },
  {
    "path": "BMEngine/MidiFile.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace ZenithEngine\n{\n    public class MidiFile : IDisposable\n    {\n        Stream MidiFileReader;\n        public ushort division;\n        public int trackcount;\n        public ushort format;\n\n        public int zerothTempo = 500000;\n\n        List<long> trackBeginnings = new List<long>();\n        List<uint> trackLengths = new List<uint>();\n\n        public MidiTrack[] tracks;\n\n        public MidiInfo info;\n\n        public long maxTrackTime;\n        public long noteCount = 0;\n\n        public long currentSyncTime = 0;\n        public double currentFlexSyncTime = 0;\n\n        public FastList<Note> globalDisplayNotes = new FastList<Note>();\n        public FastList<Tempo> globalTempoEvents = new FastList<Tempo>();\n        public FastList<ColorChange> globalColorEvents = new FastList<ColorChange>();\n        public FastList<PlaybackEvent> globalPlaybackEvents = new FastList<PlaybackEvent>();\n\n        public double tempoTickMultiplier = 0;\n\n        public int unendedTracks = 0;\n\n        RenderSettings settings;\n\n        public MidiFile(string filename, RenderSettings settings)\n        {\n            this.settings = settings;\n            MidiFileReader = new StreamReader(filename).BaseStream;\n            ParseHeaderChunk();\n            while (MidiFileReader.Position < MidiFileReader.Length)\n            {\n                ParseTrackChunk();\n            }\n            tracks = new MidiTrack[trackcount];\n\n            Console.WriteLine(\"Loading tracks into memory\");\n            info = new MidiInfo();\n            LoadAndParseAll(true);\n            Console.WriteLine(\"Loaded!\");\n            Console.WriteLine(\"Note count: \" + noteCount);\n            unendedTracks = trackcount;\n\n            info.division = division;\n            info.firstTempo = zerothTempo;\n            info.noteCount = noteCount;\n            info.tickLength = maxTrackTime;\n            info.trackCount = trackcount;\n            tempoTickMultiplier = (double)division / 500000 * 1000;\n        }\n\n        void AssertText(string text)\n        {\n            foreach (char c in text)\n            {\n                if (MidiFileReader.ReadByte() != c)\n                {\n                    throw new Exception(\"Corrupt chunk headers\");\n                }\n            }\n        }\n\n        uint ReadInt32()\n        {\n            uint length = 0;\n            for (int i = 0; i != 4; i++)\n                length = (uint)((length << 8) | (byte)MidiFileReader.ReadByte());\n            return length;\n        }\n\n        ushort ReadInt16()\n        {\n            ushort length = 0;\n            for (int i = 0; i != 2; i++)\n                length = (ushort)((length << 8) | (byte)MidiFileReader.ReadByte());\n            return length;\n        }\n\n        void ParseHeaderChunk()\n        {\n            AssertText(\"MThd\");\n            uint length = ReadInt32();\n            if (length != 6) throw new Exception(\"Header chunk size isn't 6\");\n            format = ReadInt16();\n            ReadInt16();\n            division = ReadInt16();\n            if (format == 2) throw new Exception(\"Midi type 2 not supported\");\n            if (division < 0) throw new Exception(\"Division < 0 not supported\");\n        }\n\n        void ParseTrackChunk()\n        {\n            AssertText(\"MTrk\");\n            uint length = ReadInt32();\n            trackBeginnings.Add(MidiFileReader.Position);\n            trackLengths.Add(length);\n            MidiFileReader.Position += length;\n            trackcount++;\n            Console.WriteLine(\"Track \" + trackcount + \", Size \" + length);\n        }\n\n\n        public bool ParseUpTo(double targetTime)\n        {\n            lock (globalDisplayNotes)\n            {\n                if (settings.timeBasedNotes)\n                    for (; currentFlexSyncTime <= targetTime && settings.running; currentSyncTime++)\n                    {\n                        currentFlexSyncTime += 1 / tempoTickMultiplier;\n                        int ut = 0;\n                        for (int trk = 0; trk < trackcount; trk++)\n                        {\n                            var t = tracks[trk];\n                            if (!t.trackEnded)\n                            {\n                                ut++;\n                                t.Step(currentSyncTime);\n                            }\n                        }\n                        unendedTracks = ut;\n                    }\n                else\n                    for (; currentSyncTime <= targetTime && settings.running; currentSyncTime++)\n                    {\n                        int ut = 0;\n                        for (int trk = 0; trk < trackcount; trk++)\n                        {\n                            var t = tracks[trk];\n                            if (!t.trackEnded)\n                            {\n                                ut++;\n                                t.Step(currentSyncTime);\n                            }\n                        }\n                        unendedTracks = ut;\n                    }\n                foreach (var t in tracks)\n                {\n                    if (!t.trackEnded) return true;\n                }\n                return false;\n            }\n        }\n\n        public void LoadAndParseAll(bool useBufferStream = false)\n        {\n            long[] tracklens = new long[tracks.Length];\n            int p = 0;\n            List<FastList<Tempo>> tempos = new List<FastList<Tempo>>();\n            Parallel.For(0, tracks.Length, (i) =>\n               {\n                   var reader = new BufferByteReader(MidiFileReader, settings.maxTrackBufferSize, trackBeginnings[i], trackLengths[i]);\n                   tracks[i] = new MidiTrack(i, reader, this, settings);\n                   var t = tracks[i];\n                   while (!t.trackEnded)\n                   {\n                       try\n                       {\n                           t.ParseNextEventFast();\n                       }\n                       catch\n                       {\n                           break;\n                       }\n                   }\n                   noteCount += t.noteCount;\n                   tracklens[i] = t.trackTime;\n                   if (t.foundTimeSig != null)\n                       info.timeSig = t.foundTimeSig;\n                   if (t.zerothTempo != -1)\n                   {\n                       zerothTempo = t.zerothTempo;\n                   }\n                   lock (tempos) tempos.Add(t.TempoEvents);\n                   t.Reset();\n                   Console.WriteLine(\"Loaded track \" + p++ + \"/\" + tracks.Length);\n                   GC.Collect();\n               });\n            maxTrackTime = tracklens.Max();\n            Console.WriteLine(\"Processing Tempos\");\n            LinkedList<Tempo> Tempos = new LinkedList<Tempo>();\n            var iters = tempos.Select(t => t.GetEnumerator()).ToArray();\n            bool[] unended = new bool[iters.Length];\n            for (int i = 0; i < iters.Length; i++) unended[i] = iters[i].MoveNext();\n            while (true)\n            {\n                long smallest = 0;\n                bool first = true;\n                int id = 0;\n                for (int i = 0; i < iters.Length; i++)\n                {\n                    if (!unended[i]) continue;\n                    if (first)\n                    {\n                        smallest = iters[i].Current.pos;\n                        id = i;\n                        first = false;\n                        continue;\n                    }\n                    if (iters[i].Current.pos < smallest)\n                    {\n                        smallest = iters[i].Current.pos;\n                        id = i;\n                    }\n                }\n                if (first)\n                {\n                    break;\n                }\n                Tempos.AddLast(iters[id].Current);\n                unended[id] = iters[id].MoveNext();\n            }\n\n            double time = 0;\n            long ticks = maxTrackTime;\n            double multiplier = ((double)500000 / division) / 1000000;\n            long lastt = 0;\n            foreach (var t in Tempos)\n            {\n                var offset = t.pos - lastt;\n                time += offset * multiplier;\n                ticks -= offset;\n                lastt = t.pos;\n                multiplier = ((double)t.tempo / division) / 1000000;\n            }\n\n            time += ticks * multiplier;\n\n            info.secondsLength = time;\n\n            maxTrackTime = tracklens.Max();\n            unendedTracks = trackcount;\n        }\n\n        public void SetZeroColors()\n        {\n            foreach (var t in tracks) t.SetZeroColors();\n        }\n\n        public void Reset()\n        {\n            globalDisplayNotes.Unlink();\n            globalTempoEvents.Unlink();\n            globalColorEvents.Unlink();\n            globalPlaybackEvents.Unlink();\n            currentSyncTime = 0;\n            currentFlexSyncTime = 0;\n            unendedTracks = trackcount;\n            tempoTickMultiplier = (double)division / 500000 * 1000;\n            foreach (var t in tracks) t.Reset();\n        }\n\n        public void Dispose()\n        {\n            foreach (var t in tracks) t.Dispose();\n            MidiFileReader.Dispose();\n        }\n    }\n}\n"
  },
  {
    "path": "BMEngine/MidiInfo.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace ZenithEngine\n{\n    public class MidiInfo\n    {\n        public int division;\n        public int trackCount;\n        public long noteCount;\n        public int firstTempo;\n        public long tickLength;\n        public double secondsLength;\n        public TimeSignature timeSig = new TimeSignature() { numerator = 4, denominator = 4 };\n    }\n}\n"
  },
  {
    "path": "BMEngine/MidiTrack.cs",
    "content": "﻿using OpenTK.Graphics;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace ZenithEngine\n{\n    public class Note\n    {\n        public double start;\n        public double end;\n        public bool hasEnded;\n        public byte channel;\n        public byte key;\n        public byte vel;\n        public bool delete = false;\n        public object meta = null;\n        public int track;\n        public NoteColor color;\n    }\n\n    public class NoteColor\n    {\n        public Color4 left;\n        public Color4 right;\n        public bool isDefault = true;\n    }\n\n    public struct PlaybackEvent\n    {\n        public double pos;\n        public int val;\n    }\n\n    public class Tempo\n    {\n        public long pos;\n        public int tempo;\n    }\n\n    public class ColorChange\n    {\n        public double pos;\n        public Color4 col1;\n        public Color4 col2;\n        public byte channel;\n        public MidiTrack track;\n    }\n\n    public class TimeSignature\n    {\n        public int numerator { get; internal set; }\n        public int denominator { get; internal set; }\n    }\n\n    public class MidiTrack : IDisposable\n    {\n        public int trackID;\n\n        public bool trackEnded = false;\n\n        public long trackTime = 0;\n        public long lastStepTime = 0;\n        public double trackFlexTime = 0;\n        public long noteCount = 0;\n        public int zerothTempo = -1;\n\n        byte channelPrefix = 0;\n\n        MidiFile midi;\n\n        public FastList<Note>[] UnendedNotes = null;\n        public LinkedList<Tempo> Tempos = new LinkedList<Tempo>();\n\n        FastList<Note> globalDisplayNotes;\n        FastList<Tempo> globalTempoEvents;\n        FastList<ColorChange> globalColorEvents;\n        FastList<PlaybackEvent> globalPlaybackEvents;\n\n        public NoteColor[] trkColors;\n        public NoteColor[] zeroTickTrkColors;\n\n        public TimeSignature foundTimeSig = null;\n\n        bool readDelta = false;\n\n        BufferByteReader reader;\n\n        public void Reset()\n        {\n            if (UnendedNotes != null) foreach (var un in UnendedNotes) un.Unlink();\n            reader.Reset();\n            ResetColors();\n            trackTime = 0;\n            lastStepTime = 0;\n            trackFlexTime = 0;\n            trackEnded = false;\n            readDelta = false;\n            channelPrefix = 0;\n            noteCount = 0;\n            UnendedNotes = null;\n        }\n\n        public void ResetColors()\n        {\n            trkColors = new NoteColor[16];\n            for (int i = 0; i < 16; i++)\n            {\n                trkColors[i] = new NoteColor() { left = Color4.Gray, right = Color4.Gray, isDefault = true };\n            }\n        }\n\n        public void SetZeroColors()\n        {\n            for (int i = 0; i < 16; i++)\n            {\n                if (zeroTickTrkColors[i] != null)\n                {\n                    trkColors[i].left = zeroTickTrkColors[i].left;\n                    trkColors[i].right = zeroTickTrkColors[i].right;\n                }\n            }\n        }\n\n        RenderSettings settings;\n        public MidiTrack(int id, BufferByteReader reader, MidiFile file, RenderSettings settings)\n        {\n            this.settings = settings;\n            globalDisplayNotes = file.globalDisplayNotes;\n            globalTempoEvents = file.globalTempoEvents;\n            globalColorEvents = file.globalColorEvents;\n            globalPlaybackEvents = file.globalPlaybackEvents;\n            midi = file;\n            this.reader = reader;\n            trackID = id;\n            ResetColors();\n\n            zeroTickTrkColors = new NoteColor[16];\n            for (int i = 0; i < 16; i++) zeroTickTrkColors[i] = null;\n        }\n\n        long ReadVariableLen()\n        {\n            byte c;\n            int val = 0;\n            for (int i = 0; i < 4; i++)\n            {\n                c = reader.ReadFast();\n                if (c > 0x7F)\n                {\n                    val = (val << 7) | (c & 0x7F);\n                }\n                else\n                {\n                    val = val << 7 | c;\n                    return val;\n                }\n            }\n            return val;\n        }\n\n        public void Step(long time)\n        {\n            timebase = settings.timeBasedNotes;\n            trackFlexTime += (time - lastStepTime) / midi.tempoTickMultiplier;\n            lastStepTime = time;\n            try\n            {\n                if (time >= trackTime)\n                {\n                    if (readDelta)\n                    {\n                        long d = trackTime;\n                        do\n                        {\n                            ParseNextEvent();\n                            if (trackEnded) return;\n                            trackTime += ReadVariableLen();\n                            readDelta = true;\n                        }\n                        while (trackTime == d);\n                    }\n                    else\n                    {\n                        if (trackEnded) return;\n                        trackTime += ReadVariableLen();\n                        readDelta = true;\n                    }\n                }\n            }\n            catch (IndexOutOfRangeException)\n            {\n                EndTrack();\n            }\n        }\n\n        void EndTrack()\n        {\n            trackEnded = true;\n            if (UnendedNotes != null)\n                foreach (var un in UnendedNotes)\n                {\n                    var iter = un.Iterate();\n                    Note n;\n                    while (iter.MoveNext(out n))\n                    {\n                        n.end = trackTime;\n                        n.hasEnded = true;\n                    }\n                    un.Unlink();\n                }\n            UnendedNotes = null;\n        }\n\n        byte prevCommand = 0;\n        bool timebase = false;\n        public void ParseNextEvent()\n        {\n            try\n            {\n                if (!readDelta)\n                {\n                    trackTime += ReadVariableLen();\n                }\n                readDelta = false;\n\n                double time = trackTime;\n                if (timebase)\n                    time = trackFlexTime;\n\n                byte command = reader.ReadFast();\n                if (command < 0x80)\n                {\n                    reader.Pushback = command;\n                    command = prevCommand;\n                }\n                prevCommand = command;\n                byte comm = (byte)(command & 0b11110000);\n                if (comm == 0b10010000)\n                {\n                    byte channel = (byte)(command & 0b00001111);\n                    byte note = reader.Read();\n                    byte vel = reader.ReadFast();\n\n                    if (settings.playbackEnabled && vel > 10)\n                    {\n                        globalPlaybackEvents.Add(new PlaybackEvent()\n                        {\n                            pos = time,\n                            val = command | (note << 8) | (vel << 16)\n                        });\n                    }\n                    if (vel == 0)\n                    {\n                        var l = UnendedNotes[note << 4 | channel];\n                        if (!l.ZeroLen)\n                        {\n                            Note n = l.Pop();\n                            n.end = time;\n                            n.hasEnded = true;\n                        }\n                    }\n                    else\n                    {\n                        Note n = new Note();\n                        n.start = time;\n                        n.key = note;\n                        n.color = trkColors[channel];\n                        n.channel = channel;\n                        n.vel = vel;\n                        n.track = trackID;\n                        if (UnendedNotes == null)\n                        {\n                            UnendedNotes = new FastList<Note>[256 * 16];\n                            for (int i = 0; i < 256 * 16; i++)\n                            {\n                                UnendedNotes[i] = new FastList<Note>();\n                            }\n                        }\n                        UnendedNotes[note << 4 | channel].Add(n);\n                        globalDisplayNotes.Add(n);\n                    }\n                }\n                else if (comm == 0b10000000)\n                {\n                    int channel = command & 0b00001111;\n                    byte note = reader.Read();\n                    byte vel = reader.ReadFast();\n                    var l = UnendedNotes[note << 4 | channel];\n                    if (!l.ZeroLen)\n                    {\n                        try\n                        {\n                            Note n = l.Pop();\n\n                            if (settings.playbackEnabled && n.vel > 10)\n                            {\n                                globalPlaybackEvents.Add(new PlaybackEvent()\n                                {\n                                    pos = time,\n                                    val = command | (note << 8) | (vel << 16)\n                                });\n                            }\n                            n.end = time;\n                            n.hasEnded = true;\n                        }\n                        catch\n                        { }\n                    }\n                }\n                else if (comm == 0b10100000)\n                {\n                    int channel = command & 0b00001111;\n                    byte note = reader.Read();\n                    byte vel = reader.Read();\n                    if (settings.playbackEnabled)\n                    {\n                        globalPlaybackEvents.Add(new PlaybackEvent()\n                        {\n                            pos = time,\n                            val = command | (note << 8) | (vel << 16)\n                        });\n                    }\n                }\n                else if (comm == 0b11000000)\n                {\n                    int channel = command & 0b00001111;\n                    byte program = reader.Read();\n                    if (settings.playbackEnabled)\n                    {\n                        globalPlaybackEvents.Add(new PlaybackEvent()\n                        {\n                            pos = time,\n                            val = command | (program << 8)\n                        });\n                    }\n                }\n                else if (comm == 0b11010000)\n                {\n\n                    int channel = command & 0b00001111;\n                    byte pressure = reader.Read();\n                    if (settings.playbackEnabled)\n                    {\n                        globalPlaybackEvents.Add(new PlaybackEvent()\n                        {\n                            pos = time,\n                            val = command | (pressure << 8)\n                        });\n                    }\n                }\n                else if (comm == 0b11100000)\n                {\n                    int channel = command & 0b00001111;\n                    byte l = reader.Read();\n                    byte m = reader.Read();\n                    if (settings.playbackEnabled)\n                    {\n                        globalPlaybackEvents.Add(new PlaybackEvent()\n                        {\n                            pos = time,\n                            val = command | (l << 8) | (m << 16)\n                        });\n                    }\n                }\n                else if (comm == 0b10110000)\n                {\n                    int channel = command & 0b00001111;\n                    byte cc = reader.Read();\n                    byte vv = reader.Read();\n                    if (settings.playbackEnabled)\n                    {\n                        globalPlaybackEvents.Add(new PlaybackEvent()\n                        {\n                            pos = time,\n                            val = command | (cc << 8) | (vv << 16)\n                        });\n                    }\n                }\n                else if (command == 0b11110000)\n                {\n                    while (reader.Read() != 0b11110111) ;\n                }\n                else if (command == 0b11110100 || command == 0b11110001 || command == 0b11110101 || command == 0b11111001 || command == 0b11111101)\n                {\n                    //printf(\"Undefined\\n\");\n                }\n                else if (command == 0b11110010)\n                {\n                    int channel = command & 0b00001111;\n                    byte ll = reader.Read();\n                    byte mm = reader.Read();\n\n                }\n                else if (command == 0b11110011)\n                {\n                    byte ss = reader.Read();\n                }\n                else if (command == 0b11110110)\n                {\n                }\n                else if (command == 0b11110111)\n                {\n                }\n                else if (command == 0b11111000)\n                {\n                }\n                else if (command == 0b11111010)\n                {\n                }\n                else if (command == 0b11111100)\n                {\n                }\n                else if (command == 0b11111110)\n                {\n                }\n                else if (command == 0xFF)\n                {\n                    command = reader.Read();\n                    if (command == 0x00)\n                    {\n                        if (reader.Read() != 2)\n                        {\n                            throw new Exception(\"Corrupt Track\");\n                        }\n                        reader.Read(); \n                        reader.Read();\n                    }\n                    else if (command == 0x01)\n                    {\n                        int size = (int)ReadVariableLen();\n                        char[] text = new char[size];\n                        for (int i = 0; i < size; i++)\n                        {\n                            text[i] = (char)reader.Read();\n                        }\n                        string str = new string(text);\n                    }\n                    else if (command == 0x02)\n                    {\n                        int size = (int)ReadVariableLen();\n                        char[] text = new char[size];\n                        for (int i = 0; i < size; i++)\n                        {\n                            text[i] = (char)reader.Read();\n                        }\n                        string str = new string(text);\n                    }\n                    else if (command == 0x03)\n                    {\n                        int size = (int)ReadVariableLen();\n                        char[] text = new char[size];\n                        for (int i = 0; i < size; i++)\n                        {\n                            text[i] = (char)reader.Read();\n                        }\n                        string str = new string(text);\n                    }\n                    else if (command == 0x04)\n                    {\n                        int size = (int)ReadVariableLen();\n                        char[] text = new char[size];\n                        for (int i = 0; i < size; i++)\n                        {\n                            text[i] = (char)reader.Read();\n                        }\n                        string str = new string(text);\n                    }\n                    else if (command == 0x05)\n                    {\n                        int size = (int)ReadVariableLen();\n                        char[] text = new char[size];\n                        for (int i = 0; i < size; i++)\n                        {\n                            text[i] = (char)reader.Read();\n                        }\n                        string str = new string(text);\n                    }\n                    else if (command == 0x06)\n                    {\n                        int size = (int)ReadVariableLen();\n                        char[] text = new char[size];\n                        for (int i = 0; i < size; i++)\n                        {\n                            text[i] = (char)reader.Read();\n                        }\n                        string str = new string(text);\n                    }\n                    else if (command == 0x07)\n                    {\n                        int size = (int)ReadVariableLen();\n                        char[] text = new char[size];\n                        for (int i = 0; i < size; i++)\n                        {\n                            text[i] = (char)reader.Read();\n                        }\n                        string str = new string(text);\n                    }\n                    else if (command == 0x08)\n                    {\n                        int size = (int)ReadVariableLen();\n                        char[] text = new char[size];\n                        for (int i = 0; i < size; i++)\n                        {\n                            text[i] = (char)reader.Read();\n                        }\n                        string str = new string(text);\n                    }\n                    else if (command == 0x09)\n                    {\n                        int size = (int)ReadVariableLen();\n                        char[] text = new char[size];\n                        for (int i = 0; i < size; i++)\n                        {\n                            text[i] = (char)reader.Read();\n                        }\n                        string str = new string(text);\n                    }\n                    else if (command == 0x0A)\n                    {\n                        int size = (int)ReadVariableLen();\n                        byte[] data = new byte[size];\n                        for (int i = 0; i < size; i++)\n                        {\n                            data[i] = reader.Read();\n                        }\n                        if (data.Length == 8 || data.Length == 12)\n                        {\n                            if (data[0] == 0x00 &&\n                                data[1] == 0x0F)\n                            {\n                                Color4 col1 = new Color4(data[4], data[5], data[6], data[7]);\n                                Color4 col2;\n                                if (data.Length == 12)\n                                    col2 = new Color4(data[8], data[9], data[10], data[11]);\n                                else col2 = col1;\n                                if (data[2] < 0x10 || data[2] == 0x7F)\n                                {\n                                    var c = new ColorChange() { pos = time, col1 = col1, col2 = col2, channel = data[2], track = this };\n                                    globalColorEvents.Add(c);\n                                }\n                            }\n                        }\n                    }\n                    else if (command == 0x20)\n                    {\n                        command = reader.Read();\n                        if (command != 1)\n                        {\n                            throw new Exception(\"Corrupt Track\");\n                        }\n                        channelPrefix = reader.Read();\n                    }\n                    else if (command == 0x21)\n                    {\n                        command = reader.Read();\n                        if (command != 1)\n                        {\n                            throw new Exception(\"Corrupt Track\");\n                        }\n                        reader.Skip(1);\n                        //TODO:  MIDI port\n                    }\n                    else if (command == 0x2F)\n                    {\n                        command = reader.Read();\n                        if (command != 0)\n                        {\n                            throw new Exception(\"Corrupt Track\");\n                        }\n                        EndTrack();\n                    }\n                    else if (command == 0x51)\n                    {\n                        command = reader.Read();\n                        if (command != 3)\n                        {\n                            throw new Exception(\"Corrupt Track\");\n                        }\n                        int btempo = 0;\n                        for (int i = 0; i != 3; i++)\n                            btempo = (int)((btempo << 8) | reader.Read());\n                        if (!timebase)\n                        {\n                            Tempo t = new Tempo();\n                            t.pos = trackTime;\n                            t.tempo = btempo;\n\n                            lock (globalTempoEvents)\n                            {\n                                globalTempoEvents.Add(t);\n                            }\n                        }\n                        midi.tempoTickMultiplier = ((double)midi.division / btempo) * 1000;\n                    }\n                    else if (command == 0x54)\n                    {\n                        command = reader.Read();\n                        if (command != 5)\n                        {\n                            throw new Exception(\"Corrupt Track\");\n                        }\n                        reader.Skip(4);\n                    }\n                    else if (command == 0x58)\n                    {\n                        command = reader.Read();\n                        if (command != 4)\n                        {\n                            throw new Exception(\"Corrupt Track\");\n                        }\n                        reader.Skip(4);\n                    }\n                    else if (command == 0x59)\n                    {\n                        command = reader.Read();\n                        if (command != 2)\n                        {\n                            throw new Exception(\"Corrupt Track\");\n                        }\n                        reader.Skip(2);\n                        //TODO: Key Signature\n                    }\n                    else if (command == 0x7F)\n                    {\n                        int size = (int)ReadVariableLen();\n                        byte[] data = new byte[size];\n                        for (int i = 0; i < size; i++)\n                        {\n                            data[i] = reader.Read();\n                        }\n                    }\n                    else\n                    {\n                        throw new Exception(\"Corrupt Track\");\n                    }\n                }\n                else\n                {\n                    throw new Exception(\"Corrupt Track\");\n                }\n            }\n            catch (IndexOutOfRangeException)\n            {\n                EndTrack();\n            }\n            catch\n            { }\n        }\n\n        public FastList<Tempo> TempoEvents = new FastList<Tempo>();\n        public void ParseNextEventFast()\n        {\n            try\n            {\n                trackTime += ReadVariableLen();\n                byte command = reader.Read();\n                if (command < 0x80)\n                {\n                    reader.Pushback = command;\n                    command = prevCommand;\n                }\n                prevCommand = command;\n                byte comm = (byte)(command & 0b11110000);\n                if (comm == 0b10010000)\n                {\n                    byte channel = (byte)(command & 0b00001111);\n                    reader.Skip(1);\n                    byte vel = reader.Read();\n                    if (vel != 0)\n                        noteCount++;\n                }\n                else if (comm == 0b10000000)\n                {\n                    int channel = command & 0b00001111;\n                    reader.Skip(2);\n                }\n                else if (comm == 0b10100000)\n                {\n                    int channel = command & 0b00001111;\n                    reader.Skip(2);\n                }\n                else if (comm == 0b11000000)\n                {\n                    int channel = command & 0b00001111;\n                    reader.Skip(1);\n                }\n                else if (comm == 0b11010000)\n                {\n                    int channel = command & 0b00001111;\n                    reader.Skip(1);\n                }\n                else if (comm == 0b11100000)\n                {\n                    int channel = command & 0b00001111;\n                    reader.Skip(2);\n                }\n                else if (comm == 0b10110000)\n                {\n                    int channel = command & 0b00001111;\n                    reader.Skip(2);\n                }\n                else if (command == 0b11110000)\n                {\n                    while (reader.Read() != 0b11110111) ;\n                }\n                else if (command == 0b11110100 || command == 0b11110001 || command == 0b11110101 || command == 0b11111001 || command == 0b11111101)\n                {\n                    //printf(\"Undefined\\n\");\n                }\n                else if (command == 0b11110010)\n                {\n                    int channel = command & 0b00001111;\n                    reader.Skip(2);\n\n                }\n                else if (command == 0b11110011)\n                {\n                    byte ss = reader.Read();\n                }\n                else if (command == 0b11110110)\n                {\n                }\n                else if (command == 0b11110111)\n                {\n                }\n                else if (command == 0b11111000)\n                {\n                }\n                else if (command == 0b11111010)\n                {\n                }\n                else if (command == 0b11111100)\n                {\n                }\n                else if (command == 0b11111110)\n                {\n                }\n                else if (command == 0xFF)\n                {\n                    command = reader.Read();\n                    if (command == 0x00)\n                    {\n                        if (reader.Read() != 2)\n                        {\n                            throw new Exception(\"Corrupt Track\");\n                        }\n                    }\n                    else if (command >= 0x01 &&\n                            command <= 0x0A)\n                    {\n                        int size = (int)ReadVariableLen();\n                        if (command != 0x0A || trackTime != 0)\n                        {\n                            reader.Skip(size);\n                        }\n                        else\n                        {\n                            byte[] data = new byte[size];\n                            for (int i = 0; i < size; i++)\n                            {\n                                data[i] = reader.Read();\n                            }\n                            if (data.Length == 8 || data.Length == 12)\n                            {\n                                if (data[0] == 0x00 &&\n                                    data[1] == 0x0F)\n                                {\n                                    Color4 col1 = new Color4(data[4], data[5], data[6], data[7]);\n                                    Color4 col2;\n                                    if (data.Length == 12)\n                                        col2 = new Color4(data[8], data[9], data[10], data[11]);\n                                    else col2 = col1;\n                                    if (data[2] < 0x10)\n                                    {\n                                        zeroTickTrkColors[data[2]] = new NoteColor() { left = col1, right = col2 };\n                                    }\n                                    else if (data[2] == 0x7F)\n                                    {\n                                        for (int i = 0; i < 16; i++)\n                                            zeroTickTrkColors[i] = new NoteColor() { left = col1, right = col2 };\n                                    }\n                                }\n                            }\n                        }\n                    }\n                    else if (command == 0x20)\n                    {\n                        command = reader.Read();\n                        if (command != 1)\n                        {\n                            throw new Exception(\"Corrupt Track\");\n                        }\n                        channelPrefix = reader.Read();\n                    }\n                    else if (command == 0x21)\n                    {\n                        command = reader.Read();\n                        if (command != 1)\n                        {\n                            throw new Exception(\"Corrupt Track\");\n                        }\n                        reader.Skip(1);\n                        //TODO:  MIDI port\n                    }\n                    else if (command == 0x2F)\n                    {\n                        command = reader.Read();\n                        if (command != 0)\n                        {\n                            throw new Exception(\"Corrupt Track\");\n                        }\n                        EndTrack();\n                    }\n                    else if (command == 0x51)\n                    {\n                        command = reader.Read();\n                        if (command != 3)\n                        {\n                            throw new Exception(\"Corrupt Track\");\n                        }\n                        int btempo = 0;\n                        for (int i = 0; i != 3; i++)\n                            btempo = (int)((btempo << 8) | reader.Read());\n                        if (trackTime == 0)\n                        {\n                            zerothTempo = btempo;\n                        }\n\n                        Tempo t = new Tempo();\n                        t.pos = trackTime;\n                        t.tempo = btempo;\n                        TempoEvents.Add(t);\n                    }\n                    else if (command == 0x54)\n                    {\n                        command = reader.Read();\n                        if (command != 5)\n                        {\n                            throw new Exception(\"Corrupt Track\");\n                        }\n                        reader.Skip(4);\n                    }\n                    else if (command == 0x58)\n                    {\n                        command = reader.Read();\n                        if (command != 4)\n                        {\n                            throw new Exception(\"Corrupt Track\");\n                        }\n                        int nn = reader.ReadFast();\n                        int dd = reader.ReadFast();\n                        dd = (int)Math.Pow(2, dd);\n                        foundTimeSig = new TimeSignature() { numerator = nn, denominator = dd };\n                        reader.Skip(2);\n                    }\n                    else if (command == 0x59)\n                    {\n                        command = reader.Read();\n                        if (command != 2)\n                        {\n                            throw new Exception(\"Corrupt Track\");\n                        }\n                        reader.Skip(2);\n                        //TODO: Key Signature\n                    }\n                    else if (command == 0x7F)\n                    {\n                        int size = (int)ReadVariableLen();\n                        reader.Skip(size);\n                    }\n                    else\n                    {\n                        throw new Exception(\"Corrupt Track\");\n                    }\n                }\n                else\n                {\n                    throw new Exception(\"Corrupt Track\");\n                }\n            }\n            catch (IndexOutOfRangeException)\n            {\n                EndTrack();\n            }\n            catch\n            { }\n        }\n\n        public void Dispose()\n        {\n            reader.Dispose();\n        }\n    }\n}\n"
  },
  {
    "path": "BMEngine/NoteColorPalettePick.xaml",
    "content": "﻿<UserControl x:Class=\"ZenithEngine.NoteColorPalettePick\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" \n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\" \n             xmlns:local=\"clr-namespace:ZenithEngine\"\n             xmlns:ui=\"clr-namespace:ZenithEngine.UI\"\n             mc:Ignorable=\"d\">\n    <UserControl.Resources>\n        <ResourceDictionary Source=\"pack://application:,,,/ZenithEngine;component/UI/Material.xaml\"/>\n    </UserControl.Resources>\n        <DockPanel LastChildFill=\"True\">\n            <Button DockPanel.Dock=\"Top\" x:Name=\"reloadButton\" Height=\"26\" Content=\"{DynamicResource palettes_reload}\" Margin=\"0,0,0,6\" VerticalAlignment=\"Top\" Click=\"ReloadButton_Click\"/>\n            <Button DockPanel.Dock=\"Bottom\" x:Name=\"openPaletteFolder\" Margin=\"0,10,0,0\" Height=\"26\" Content=\"{DynamicResource palettes_openFolder}\" Click=\"openPaletteFolder_Click\"/>\n            <ui:BetterCheckbox x:Name=\"randomiseOrder\" DockPanel.Dock=\"Bottom\" Text=\"{DynamicResource palettes_randomise}\" HorizontalAlignment=\"Left\" Margin=\"0,5,0,0\" VerticalAlignment=\"Top\" CheckToggled=\"randomiseOrder_CheckToggled\" IsChecked=\"True\"/>\n            <ListBox x:Name=\"paletteList\" Margin=\"0\" SelectionChanged=\"PaletteList_SelectionChanged\"/>\n        </DockPanel>\n</UserControl>\n"
  },
  {
    "path": "BMEngine/NoteColorPalettePick.xaml.cs",
    "content": "﻿using OpenTK.Graphics;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Drawing;\nusing System.IO;\nusing System.Linq;\nusing System.Reflection;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\nusing System.Xml;\nusing Brushes = System.Windows.Media.Brushes;\nusing Color = System.Drawing.Color;\nusing Path = System.IO.Path;\n\nnamespace ZenithEngine\n{\n    /// <summary>\n    /// Interaction logic for NoteColorPalettePick.xaml\n    /// </summary>\n    public partial class NoteColorPalettePick : UserControl\n    {\n        string searchPath = \"\";\n        public string SelectedImage { get; private set; } = \"\";\n        bool randomise = true;\n        int selectedIndex = -1;\n        List<Bitmap> images = new List<Bitmap>();\n\n        public event Action PaletteChanged;\n\n        int seed = 0;\n\n        float defS, defV;\n        public NoteColorPalettePick()\n        {\n            InitializeComponent();\n        }\n\n        public void SetPath(string path, float defS = 1, float defV = 1)\n        {\n            this.defS = defS;\n            this.defV = defV;\n            searchPath = path;\n            Reload();\n        }\n\n        public void Reload()\n        {\n            float mult = 0.12345f;\n            if (!Directory.Exists(searchPath)) Directory.CreateDirectory(searchPath);\n            using (Bitmap palette = new Bitmap(16, 8))\n            {\n                for (int i = 0; i < 16 * 8; i++)\n                {\n                    palette.SetPixel(i % 16, (i - i % 16) / 16, (Color)Color4.FromHsv(new OpenTK.Vector4(i * mult % 1, defS, defV, 1)));\n                }\n                palette.Save(Path.Combine(searchPath, \"Random.png\"));\n            }\n            using (Bitmap palette = new Bitmap(32, 8))\n            {\n                for (int i = 0; i < 32 * 8; i++)\n                {\n                    palette.SetPixel(i % 32, (i - i % 32) / 32, (Color)Color4.FromHsv(new OpenTK.Vector4(i * mult % 1, defS, defV, 1)));\n                    i++;\n                    palette.SetPixel(i % 32, (i - i % 32) / 32, (Color)Color4.FromHsv(new OpenTK.Vector4(((i - 1) * mult + 0.166f) % 1, defS, defV, 1)));\n                }\n                palette.Save(Path.Combine(searchPath, \"Random Gradients.png\"));\n            }\n            using (Bitmap palette = new Bitmap(32, 8))\n            {\n                for (int i = 0; i < 32 * 8; i++)\n                {\n                    palette.SetPixel(i % 32, (i - i % 32) / 32, (Color)Color4.FromHsv(new OpenTK.Vector4(i * mult % 1, defS, defV, 0.8f)));\n                    i++;\n                    palette.SetPixel(i % 32, (i - i % 32) / 32, (Color)Color4.FromHsv(new OpenTK.Vector4(((i - 1) * mult + 0.166f) % 1, defS, defV, 0.8f)));\n                }\n                palette.Save(Path.Combine(searchPath, \"Random Alpha Gradients.png\"));\n            }\n            using (Bitmap palette = new Bitmap(16, 8))\n            {\n                for (int i = 0; i < 16 * 8; i++)\n                {\n                    palette.SetPixel(i % 16, (i - i % 16) / 16, (Color)Color4.FromHsv(new OpenTK.Vector4(i * mult % 1, defS, defV, 0.8f)));\n                }\n                palette.Save(Path.Combine(searchPath, \"Random with Alpha.png\"));\n            }\n            var imagePaths = Directory.GetFiles(searchPath).Where(s => s.EndsWith(\".png\")).ToArray();\n\n            paletteList.Items.Clear();\n            foreach (var i in images) i.Dispose();\n            images.Clear();\n\n            Array.Sort(imagePaths, new Comparison<string>((s1, s2) =>\n            {\n                if (s1.Contains(\"Random.png\")) return -1;\n                if (s2.Contains(\"Random.png\")) return 1;\n                else return 0;\n            }));\n\n            foreach (var i in imagePaths)\n            {\n                try\n                {\n                    using (var fs = new System.IO.FileStream(i, System.IO.FileMode.Open))\n                    {\n                        Bitmap img = new Bitmap(fs);\n                        if (!(img.Width == 16 || img.Width == 32) || img.Width < 1) continue;\n                        images.Add(img);\n                        var item = new ListBoxItem() { Content = Path.GetFileNameWithoutExtension(i) };\n                        if (img.Width == 32) item.Foreground = Brushes.Blue;\n                        paletteList.Items.Add(item);\n                    }\n                }\n                catch\n                {\n\n                }\n            }\n            ReadPFAConfig();\n            SelectImage(SelectedImage);\n        }\n\n        void ReadPFAConfig()\n        {\n            try\n            {\n                var appdata = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);\n                var configPath = Path.Combine(appdata, \"Piano From Above/Config.xml\");\n                if (File.Exists(configPath))\n                {\n                    var data = File.ReadAllText(configPath);\n                    XmlDocument doc = new XmlDocument();\n                    doc.LoadXml(data);\n                    var colors = doc.GetElementsByTagName(\"Colors\").Item(0);\n                    Bitmap img = new Bitmap(16, 1);\n                    for (int i = 0; i < 16; i++)\n                    {\n                        var c = colors.ChildNodes.Item(i);\n                        int r = -1;\n                        int g = -1;\n                        int b = -1;\n                        for (int j = 0; j < 3; j++)\n                        {\n                            var attrib = c.Attributes.Item(j);\n                            if (attrib.Name == \"R\") r = Convert.ToInt32(attrib.InnerText);\n                            if (attrib.Name == \"G\") g = Convert.ToInt32(attrib.InnerText);\n                            if (attrib.Name == \"B\") b = Convert.ToInt32(attrib.InnerText);\n                        }\n                        img.SetPixel(i, 0, Color.FromArgb(r, g, b));\n                    }\n                    images.Add(img);\n                    var item = new ListBoxItem() { Content = \"PFA Config Colors\" };\n                    paletteList.Items.Add(item);\n\n                }\n            }\n            catch { }\n        }\n\n        public void SelectImage(string img)\n        {\n            bool set = false;\n            foreach (var i in paletteList.Items)\n            {\n                if ((string)((ListBoxItem)i).Content == img)\n                {\n                    paletteList.SelectedItem = i;\n                    set = true;\n                    break;\n                }\n            }\n            if (!set)\n            {\n                paletteList.SelectedIndex = 0;\n            }\n\n            PaletteChanged?.Invoke();\n        }\n\n        public Color4[] GetColors(int tracks)\n        {\n            Random r = new Random(seed);\n            double[] order = new double[tracks * 16];\n            int[] coords = new int[tracks * 16];\n            for (int i = 0; i < order.Length; i++)\n            {\n                order[i] = r.NextDouble();\n                coords[i] = i;\n            }\n            if (randomise)\n            {\n                Array.Sort(order, coords);\n            }\n            List<Color4> cols = new List<Color4>();\n            var img = images[selectedIndex];\n            for (int i = 0; i < tracks; i++)\n            {\n                for (int j = 0; j < 16; j++)\n                {\n                    int y = coords[i * 16 + j];\n                    int x = y % 16;\n                    y = y - x;\n                    y /= 16;\n                    if (img.Width == 16)\n                    {\n                        cols.Add(img.GetPixel(x, y % img.Height));\n                        cols.Add(img.GetPixel(x, y % img.Height));\n                    }\n                    else\n                    {\n                        cols.Add(img.GetPixel(x * 2, y % img.Height));\n                        cols.Add(img.GetPixel(x * 2 + 1, y % img.Height));\n                    }\n                }\n            }\n            return cols.ToArray();\n        }\n\n        private void ReloadButton_Click(object sender, RoutedEventArgs e)\n        {\n            Reload();\n        }\n\n        private void randomiseOrder_CheckToggled(object sender, RoutedPropertyChangedEventArgs<bool> e)\n        {\n            randomise = (bool)randomiseOrder.IsChecked;\n            if (randomise) seed++;\n            PaletteChanged?.Invoke();\n        }\n\n        private void openPaletteFolder_Click(object sender, RoutedEventArgs e)\n        {\n            if (!searchPath.Contains(\":\\\\\") && !searchPath.Contains(\":/\"))\n                Process.Start(\"explorer.exe\", System.IO.Path.Combine(System.IO.Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), searchPath));\n            else\n                Process.Start(\"explorer.exe\", searchPath);\n        }\n\n        private void PaletteList_SelectionChanged(object sender, SelectionChangedEventArgs e)\n        {\n            if (paletteList.SelectedItem == null) return;\n            try\n            {\n                SelectedImage = (string)((ListBoxItem)paletteList.SelectedItem).Content;\n                selectedIndex = paletteList.SelectedIndex;\n                PaletteChanged?.Invoke();\n            }\n            catch\n            { }\n        }\n    }\n}\n"
  },
  {
    "path": "BMEngine/OpenTK.dll.config",
    "content": "<configuration>\n  <dllmap os=\"linux\" dll=\"opengl32.dll\" target=\"libGL.so.1\"/>\n  <dllmap os=\"linux\" dll=\"glu32.dll\" target=\"libGLU.so.1\"/>\n  <dllmap os=\"linux\" dll=\"openal32.dll\" target=\"libopenal.so.1\"/>\n  <dllmap os=\"linux\" dll=\"alut.dll\" target=\"libalut.so.0\"/>\n  <dllmap os=\"linux\" dll=\"opencl.dll\" target=\"libOpenCL.so\"/>\n  <dllmap os=\"linux\" dll=\"libX11\" target=\"libX11.so.6\"/>\n  <dllmap os=\"linux\" dll=\"libXi\" target=\"libXi.so.6\"/>\n  <dllmap os=\"linux\" dll=\"SDL2.dll\" target=\"libSDL2-2.0.so.0\"/>\n  <dllmap os=\"osx\" dll=\"opengl32.dll\" target=\"/System/Library/Frameworks/OpenGL.framework/OpenGL\"/>\n  <dllmap os=\"osx\" dll=\"openal32.dll\" target=\"/System/Library/Frameworks/OpenAL.framework/OpenAL\" />\n  <dllmap os=\"osx\" dll=\"alut.dll\" target=\"/System/Library/Frameworks/OpenAL.framework/OpenAL\" />\n  <dllmap os=\"osx\" dll=\"libGLES.dll\" target=\"/System/Library/Frameworks/OpenGLES.framework/OpenGLES\" />\n  <dllmap os=\"osx\" dll=\"libGLESv1_CM.dll\" target=\"/System/Library/Frameworks/OpenGLES.framework/OpenGLES\" />\n  <dllmap os=\"osx\" dll=\"libGLESv2.dll\" target=\"/System/Library/Frameworks/OpenGLES.framework/OpenGLES\" />\n  <dllmap os=\"osx\" dll=\"opencl.dll\" target=\"/System/Library/Frameworks/OpenCL.framework/OpenCL\"/>\n  <dllmap os=\"osx\" dll=\"SDL2.dll\" target=\"libSDL2.dylib\"/>\n  <!-- XQuartz compatibility (X11 on Mac) -->\n  <dllmap os=\"osx\" dll=\"libGL.so.1\" target=\"/usr/X11/lib/libGL.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libX11\" target=\"/usr/X11/lib/libX11.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXcursor.so.1\" target=\"/usr/X11/lib/libXcursor.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXi\" target=\"/usr/X11/lib/libXi.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXinerama\" target=\"/usr/X11/lib/libXinerama.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXrandr.so.2\" target=\"/usr/X11/lib/libXrandr.dylib\"/>\n</configuration>\n"
  },
  {
    "path": "BMEngine/PluginUtils.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Drawing;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows.Media.Imaging;\n\nnamespace ZenithEngine\n{\n    public static class PluginUtils\n    {\n        public static BitmapImage BitmapToImageSource(Bitmap bitmap)\n        {\n            using (MemoryStream memory = new MemoryStream())\n            {\n                bitmap.Save(memory, System.Drawing.Imaging.ImageFormat.Bmp);\n                memory.Position = 0;\n                BitmapImage bitmapimage = new BitmapImage();\n                bitmapimage.BeginInit();\n                bitmapimage.StreamSource = memory;\n                bitmapimage.CacheOption = BitmapCacheOption.OnLoad;\n                bitmapimage.EndInit();\n\n                return bitmapimage;\n            }\n        }\n\n    }\n}\n"
  },
  {
    "path": "BMEngine/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// General Information about an assembly is controlled through the following\n// set of attributes. Change these attribute values to modify the information\n// associated with an assembly.\n[assembly: AssemblyTitle(\"ZenithEngine\")]\n[assembly: AssemblyDescription(\"\")]\n[assembly: AssemblyConfiguration(\"\")]\n[assembly: AssemblyCompany(\"\")]\n[assembly: AssemblyProduct(\"ZenithEngine\")]\n[assembly: AssemblyCopyright(\"Copyright ©  2019\")]\n[assembly: AssemblyTrademark(\"\")]\n[assembly: AssemblyCulture(\"\")]\n\n// Setting ComVisible to false makes the types in this assembly not visible\n// to COM components.  If you need to access a type in this assembly from\n// COM, set the ComVisible attribute to true on that type.\n[assembly: ComVisible(false)]\n\n// The following GUID is for the ID of the typelib if this project is exposed to COM\n[assembly: Guid(\"60f2212a-d56c-4776-836f-6a49453cdbc4\")]\n\n// Version information for an assembly consists of the following four values:\n//\n//      Major Version\n//      Minor Version\n//      Build Number\n//      Revision\n//\n// You can specify all the values or you can default the Build and Revision Numbers\n// by using the '*' as shown below:\n// [assembly: AssemblyVersion(\"1.0.*\")]\n[assembly: AssemblyVersion(\"1.0.0.0\")]\n[assembly: AssemblyFileVersion(\"1.0.0.0\")]\n"
  },
  {
    "path": "BMEngine/RenderSettings.cs",
    "content": "﻿using OpenTK.Graphics;\nusing System;\nusing System.Collections.Generic;\nusing System.Drawing;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace ZenithEngine\n{\n    public enum KeyboardRenderers\n    {\n        Legacy,\n        New,\n        Flat\n    }\n\n    public enum NoteRenderers\n    {\n        Shaded,\n        Flat\n    }\n\n    public class RenderSettings\n    {\n        public int fps = 60;\n\n        public int width = 1920;\n        public int height = 1080;\n        public int downscale = 1;\n\n        public bool ffRender = false;\n        public string ffPath = \"\";\n        public bool ffRenderMask = false;\n        public string ffMaskPath = \"\";\n        public bool vsync = true;\n        public double renderSecondsDelay = 0;\n\n        private bool paused = false;\n        public bool forceReRender = true;\n        public double tempoMultiplier = 1;\n\n        public bool includeAudio = false;\n        public string audioPath = \"\";\n\n        public int maxTrackBufferSize = 10000;\n\n        public bool useBitrate = true;\n        public bool CustomFFmpeg = false;\n        public int bitrate = 20000;\n        public int crf = 17;\n        public string crfPreset = \"medium\";\n        public bool ffmpegDebug = false;\n        public string ffoption = \"\";\n\n        public bool showNoteCount = false;\n        public bool showNotesRendered = false;\n        public int fontSize = 50;\n        public string font = \"Arial\";\n\n        public bool running = false;\n\n        public bool playSound = true;\n        public bool playbackEnabled = true;\n\n        public bool realtimePlayback = true;\n\n        public double liveFps = 0;\n\n        public bool timeBasedNotes = false;\n\n        public bool ignoreColorEvents = false;\n\n        public long lastBGChangeTime = -1;\n        public string BGImage = null;\n\n        public event Action PauseToggled;\n        public bool Paused\n        {\n            get => paused;\n            set\n            {\n                paused = value;\n                PauseToggled();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "BMEngine/ScriptedCompile/ExtraUI.cs",
    "content": "﻿using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace ScriptedEngine\n{\n    public abstract class UISetting\n    {\n        public double Padding { get; set; } = 10;\n    }\n    public abstract class UISettingValued<T> : UISetting\n    {\n        private bool enabled = true;\n\n        public event Action<bool> EnableToggled;\n\n        public T Default { get; protected set; }\n\n        public bool Enabled\n        {\n            get => enabled;\n            set\n            {\n                if (enabled != value)\n                {\n                    enabled = value;\n                    EnableToggled?.Invoke(Enabled);\n                }\n            }\n        }\n\n    }\n\n    public class UITabs : UISetting\n    {\n        public Dictionary<string, IEnumerable<UISetting>> Tabs { get; set; } = new Dictionary<string, IEnumerable<UISetting>>();\n\n        public UITabs() { }\n        public UITabs(Dictionary<string, IEnumerable<UISetting>> tabs) { Tabs = tabs; }\n    }\n\n    public class UILabel : UISetting\n    {\n        public double FontSize { get; } = 16;\n        public string Text { get; } = \"\";\n\n        public UILabel(string text) { Text = text; }\n        public UILabel(string text, double fontSize) { Text = text; FontSize = fontSize; }\n        public UILabel(string text, double fontSize, double padding) { Text = text; FontSize = fontSize; Padding = padding; }\n    }\n\n    public class UINumber : UISettingValued<double>\n    {\n        private double value;\n\n        public UINumber(string text, double value, double minimum, double maximum, int decialPoints)\n        {\n            Default = value;\n            Text = text;\n            Value = value;\n            Minimum = minimum;\n            Maximum = maximum;\n            DecialPoints = decialPoints;\n        }\n\n        public UINumber(string text, double value, double minimum, double maximum, int decialPoints, double step) : this(text, value, minimum, maximum, decialPoints)\n        {\n            Step = step;\n        }\n\n        public event Action<double> ValueChanged;\n\n        public double Value\n        {\n            get => value;\n            set\n            {\n                if (this.value != value)\n                {\n                    this.value = value;\n                    ValueChanged?.Invoke(this.value);\n                }\n            }\n        }\n        public string Text { get; }\n        public double Minimum { get; }\n        public double Maximum { get; }\n        public int DecialPoints { get; }\n        public double Step { get; } = 1;\n    }\n\n    public class UINumberSlider : UISettingValued<double>\n    {\n        private double value;\n\n        public UINumberSlider(string text, double value, double minimum, double maximum, double trueMinimum, double trueMaximum, int decialPoints)\n        {\n            Default = value;\n            Text = text;\n            Value = value;\n            Minimum = minimum;\n            Maximum = maximum;\n            TrueMinimum = trueMinimum;\n            TrueMaximum = trueMaximum;\n            DecialPoints = decialPoints;\n        }\n\n        public UINumberSlider(string text, double value, double minimum, double maximum, double trueMinimum, double trueMaximum, int decialPoints, bool logarithmic) : this(text, value, minimum, maximum, trueMinimum, trueMaximum, decialPoints)\n        {\n            Logarithmic = logarithmic;\n        }\n\n        public UINumberSlider(string text, double value, double minimum, double maximum, double trueMinimum, double trueMaximum, int decialPoints, double step, bool logarithmic)\n        {\n            Default = value;\n            Value = value;\n            Text = text;\n            Minimum = minimum;\n            Maximum = maximum;\n            TrueMinimum = trueMinimum;\n            TrueMaximum = trueMaximum;\n            DecialPoints = decialPoints;\n            Step = step;\n            Logarithmic = logarithmic;\n        }\n\n        public UINumberSlider(string text, double value, double minimum, double maximum, double trueMinimum, double trueMaximum, int decialPoints, double step)\n        {\n            Default = value;\n            Value = value;\n            Text = text;\n            Minimum = minimum;\n            Maximum = maximum;\n            TrueMinimum = trueMinimum;\n            TrueMaximum = trueMaximum;\n            DecialPoints = decialPoints;\n            Step = step;\n        }\n\n        public event Action<double> ValueChanged;\n\n        public double Value\n        {\n            get => value;\n            set\n            {\n                if (this.value != value)\n                {\n                    this.value = value;\n                    ValueChanged?.Invoke(this.value);\n                }\n            }\n        }\n        public string Text { get; }\n        public double Minimum { get; }\n        public double Maximum { get; }\n        public double TrueMinimum { get; }\n        public double TrueMaximum { get; }\n        public int DecialPoints { get; }\n        public double Step { get; } = 1;\n        public bool Logarithmic { get; } = false;\n    }\n\n    public class UIDropdown : UISettingValued<int>\n    {\n        private string value;\n        private int index;\n\n        public UIDropdown(string text, string[] options)\n        {\n            Text = text;\n            Options = options;\n            Index = 0;\n            Value = options[0];\n            Default = 0;\n        }\n\n        public UIDropdown(string text, int index, string[] options)\n        {\n            Text = text;\n            Options = options;\n            Index = index;\n            Value = options[index];\n            Default = index;\n        }\n\n\n        public event Action<int> IndexChanged;\n\n        public string Value\n        {\n            get => value;\n            set\n            {\n                if (this.value != value)\n                {\n                    this.value = value;\n                    if (!Options.Contains(value)) throw new Exception(\"Dropdown doesn't contain value '\" + value + \"'\");\n                    Index = Array.IndexOf(Options, value);\n                }\n            }\n        }\n        public string Text { get; }\n        public int Index\n        {\n            get => index;\n            set\n            {\n                if (index != value)\n                {\n                    index = value;\n                    if (index < 0 || index >= Options.Length) throw new Exception(\"Index value of \" + index + \" is outside the rage of the dropdown's \" + Options.Length + \" item count.\");\n                    this.value = Options[index];\n                    IndexChanged?.Invoke(index);\n                }\n            }\n        }\n        public string[] Options { get; }\n    }\n\n    public class UICheckbox : UISettingValued<bool>\n    {\n        private bool value;\n\n        public UICheckbox(string text, bool @checked)\n        {\n            this.Text = text;\n            this.Checked = @checked;\n            Default = @checked;\n        }\n\n        public event Action<bool> ValueChanged;\n\n        public bool Checked\n        {\n            get => value;\n            set\n            {\n                if (this.value != value)\n                {\n                    this.value = value;\n                    ValueChanged?.Invoke(this.value);\n                }\n            }\n        }\n        public string Text { get; }\n    }\n}\n"
  },
  {
    "path": "BMEngine/ScriptedCompile/IO.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Drawing;\nusing System.Linq;\nusing System.Reflection;\nusing System.Text;\nusing System.Threading.Tasks;\nusing OpenTK;\nusing ZenithEngine;\nusing OpenTK.Graphics;\n\nnamespace ScriptedEngine\n{\n    public class Texture\n    {\n        public string path;\n        public Bitmap bitmap;\n        public int texId = -1;\n        public bool linear = false;\n        public bool looped = true;\n\n        public int width;\n        public int height;\n        public double aspectRatio;\n    }\n\n    public enum FontStyle\n    {\n        Regular = 0,\n        Bold = 1,\n        Italic = 2,\n        Underline = 4,\n        Strikeout = 8\n    }\n\n    public enum TextAlignment\n    {\n        Regular = 0,\n        Bold = 1,\n        Italic = 2,\n        Underline = 4,\n        Strikeout = 8\n    }\n\n    public class Font\n    {\n        public int fontPixelSize;\n        public string fontName;\n        public FontStyle fontStyle;\n        public string charMap;\n\n        public GLTextEngine engine;\n    }\n\n    public enum TextureShaders\n    {\n        Normal,\n        Inverted,\n        Hybrid\n    }\n\n    public enum BlendFunc\n    {\n        Mix,\n        Add\n    }\n\n    public class RenderOptions\n    {\n        public int firstKey;\n        public int lastKey;\n        public int renderWidth;\n        public int renderHeight;\n        public double renderAspectRatio;\n        public int renderFPS;\n        public int renderSSAA;\n        public double midiTime;\n        public double noteScreenTime;\n\n        public int midiPPQ;\n        public bool midiTimeBased;\n        public TimeSignature midiTimeSignature;\n        public int midiBarLength;\n    }\n\n    public class KeyLayout\n    {\n        public class pos\n        {\n            public double left;\n            public double right;\n        }\n\n        public KeyLayout()\n        {\n            keys = new pos[257];\n            notes = new pos[257];\n\n            for (int i = 0; i < blackKey.Length; i++) blackKey[i] = Util.IsBlackKey(i);\n            int b = 0;\n            int w = 0;\n            for (int i = 0; i < keyNumber.Length; i++)\n            {\n                if (blackKey[i]) keyNumber[i] = b++;\n                else keyNumber[i] = w++;\n            }\n        }\n\n        public pos[] keys;\n        public pos[] notes;\n        public bool[] blackKey = new bool[257];\n        public int[] keyNumber = new int[257];\n\n        public double blackKeyWidth;\n        public double whiteKeyWidth;\n        public double blackNoteWidth;\n        public double whiteNoteWidth;\n    }\n\n    public class KeyboardOptions\n    {\n        public bool sameWidthNotes = false;\n\n        public double blackKey2setOffset = 0.3;\n        public double blackKey3setOffset = 0.5;\n        public double blackKeyScale = 0.6;\n\n        public double blackNote2setOffset = 0;\n        public double blackNote3setOffset = 0;\n        public double blackNoteScale = 1;\n\n        public double[] advancedBlackKeyOffsets = new double[] { 0, 0, 0, 0, 0 };\n    }\n\n    public static class IO\n    {\n        public static Func<string, bool, bool, Texture> loadTexture;\n        public static Texture LoadTexture(string path)\n        {\n            if (!IsInFunction(\"Load\") && !IsInFunction(\".ctor\")) throw new Exception(\"Can't call LoadTexture outside the load function\");\n            return loadTexture(path, true, false);\n        }\n\n        public static Texture LoadTexture(string path, bool linear)\n        {\n            if (!IsInFunction(\"Load\") && !IsInFunction(\".ctor\")) throw new Exception(\"Can't call LoadTexture outside the load function\");\n            return loadTexture(path, true, linear);\n        }\n\n        public static Texture LoadTexture(string path, bool uvLoop, bool linear)\n        {\n            if (!IsInFunction(\"Load\") && !IsInFunction(\".ctor\")) throw new Exception(\"Can't call LoadTexture outside the load function\");\n            return loadTexture(path, uvLoop, linear);\n        }\n\n        public static Func<int, string, FontStyle, string, Font> loadFont;\n        public static Font LoadFont(int size, string name)\n        {\n            if (!IsInFunction(\"Load\") && !IsInFunction(\".ctor\")) throw new Exception(\"Can't call LoadFont outside the load function\");\n            return loadFont(size, name, FontStyle.Regular, null);\n        }\n        public static Font LoadFont(int size, string name, FontStyle style)\n        {\n            if (!IsInFunction(\"Load\") && !IsInFunction(\".ctor\")) throw new Exception(\"Can't call LoadFont outside the load function\");\n            return loadFont(size, name, style, null);\n        }\n        public static Font LoadFont(int size, string name, string charmap)\n        {\n            if (!IsInFunction(\"Load\") && !IsInFunction(\".ctor\")) throw new Exception(\"Can't call LoadFont outside the load function\");\n            return loadFont(size, name, FontStyle.Regular, charmap);\n        }\n        public static Font LoadFont(int size, string name, FontStyle style, string charmap)\n        {\n            if (!IsInFunction(\"Load\") && !IsInFunction(\".ctor\")) throw new Exception(\"Can't call LoadFont outside the load function\");\n            return loadFont(size, name, style, charmap);\n        }\n\n        public static Action<double, double, double, double, Color4, Color4, Color4, Color4, Texture, double, double, double, double> renderQuad;\n        public static Action<double, double, double, Color4, Font, string> renderText;\n        public static Func<Font, string, double> getTextSize;\n        public static Action<TextureShaders> selectTexShader;\n        public static Action<BlendFunc> setBlendFunc;\n        public static Action forceFlush;\n        public static Action<Vector2d, Vector2d, Vector2d, Vector2d, Color4, Color4, Color4, Color4, Texture, Vector2d, Vector2d, Vector2d, Vector2d> renderShape;\n\n        public static void RenderQuad(double left, double top, double right, double bottom, Color4 col) =>\n            renderQuad(left, top, right, bottom, col, col, col, col, null, 0, 0, 0, 0);\n\n        public static void RenderQuad(double left, double top, double right, double bottom, Color4 col, Texture tex) =>\n            renderQuad(left, top, right, bottom, col, col, col, col, tex, 0, 0, 1, 1);\n\n        public static void RenderQuad(double left, double top, double right, double bottom, Color4 col, Texture tex, double uvLeft, double uvTop, double uvRight, double uvBottom) =>\n            renderQuad(left, top, right, bottom, col, col, col, col, tex, uvLeft, uvTop, uvRight, uvBottom);\n\n        public static void RenderQuad(double left, double top, double right, double bottom, Color4 topLeft, Color4 topRight, Color4 bottomRight, Color4 bottomLeft) =>\n            renderQuad(left, top, right, bottom, topLeft, topRight, bottomRight, bottomLeft, null, 0, 0, 0, 0);\n\n        public static void RenderQuad(double left, double top, double right, double bottom, Color4 topLeft, Color4 topRight, Color4 bottomRight, Color4 bottomLeft, Texture tex) =>\n            renderQuad(left, top, right, bottom, topLeft, topRight, bottomRight, bottomLeft, tex, 0, 0, 1, 1);\n\n        public static void RenderQuad(double left, double top, double right, double bottom, Color4 topLeft, Color4 topRight, Color4 bottomRight, Color4 bottomLeft, Texture tex, double uvLeft, double uvTop, double uvRight, double uvBottom) =>\n            renderQuad(left, top, right, bottom, topLeft, topRight, bottomRight, bottomLeft, tex, uvLeft, uvTop, uvRight, uvBottom);\n\n        public static void RenderShape(Vector2d v1, Vector2d v2, Vector2d v3, Vector2d v4, Color4 col) =>\n            renderShape(v1, v2, v3, v4, col, col, col, col, null, Vector2d.Zero, Vector2d.Zero, Vector2d.Zero, Vector2d.Zero);\n\n        public static void RenderShape(Vector2d v1, Vector2d v2, Vector2d v3, Vector2d v4, Color4 col, Texture tex) =>\n            renderShape(v1, v2, v3, v4, col, col, col, col, tex, new Vector2d(0, 0), new Vector2d(1, 0), new Vector2d(1, 1), new Vector2d(0, 1));\n\n        public static void RenderShape(Vector2d v1, Vector2d v2, Vector2d v3, Vector2d v4, Color4 col, Texture tex, Vector2d uv1, Vector2d uv2, Vector2d uv3, Vector2d uv4) =>\n            renderShape(v1, v2, v3, v4, col, col, col, col, tex, uv1, uv2, uv3, uv4);\n\n        public static void RenderShape(Vector2d v1, Vector2d v2, Vector2d v3, Vector2d v4, Color4 topLeft, Color4 topRight, Color4 bottomRight, Color4 bottomLeft) =>\n            renderShape(v1, v2, v3, v4, topLeft, topRight, bottomRight, bottomLeft, null, new Vector2d(0, 0), new Vector2d(1, 0), new Vector2d(1, 1), new Vector2d(0, 1));\n\n        public static void RenderShape(Vector2d v1, Vector2d v2, Vector2d v3, Vector2d v4, Color4 topLeft, Color4 topRight, Color4 bottomRight, Color4 bottomLeft, Texture tex) =>\n            renderShape(v1, v2, v3, v4, topLeft, topRight, bottomRight, bottomLeft, tex, new Vector2d(0, 0), new Vector2d(1, 0), new Vector2d(1, 1), new Vector2d(0, 1));\n\n        public static void RenderShape(Vector2d v1, Vector2d v2, Vector2d v3, Vector2d v4, Color4 topLeft, Color4 topRight, Color4 bottomRight, Color4 bottomLeft, Texture tex, Vector2d uv1, Vector2d uv2, Vector2d uv3, Vector2d uv4) =>\n            renderShape(v1, v2, v3, v4, topLeft, topRight, bottomRight, bottomLeft, tex, uv1, uv2, uv3, uv4);\n\n        public static void SelectTextureShader(TextureShaders shader) => selectTexShader(shader);\n\n        public static void SetBlendFunc(BlendFunc function) => setBlendFunc(function);\n\n        public static void RenderText(double left, double bottom, double height, Color4 color, Font font, string text) => renderText(left, bottom, height, color, font, text);\n        public static double GetTextWidth(Font font, string text) => getTextSize(font, text);\n        public static double GetTextWidth(Font font, double height, string text) => getTextSize(font, text) * height;\n\n        public static void ForceFlushBuffer() => forceFlush();\n\n        static string[] functions = new string[] { \"Load\", \"Render\", \"RenderInit\", \"RenderDispose\", \".ctor\" };\n\n        static bool IsInFunction(string name)\n        {\n            var funcs = functions.Where(f => f != name).ToArray();\n            StackTrace st = new StackTrace(true);\n            bool has = false;\n            for (int i = 0; i < st.FrameCount; i++)\n            {\n                var m = st.GetFrame(i).GetMethod();\n                if (m.Name == name &&\n                    m.DeclaringType.Name == \"Script\")\n                    has = true;\n                if (funcs.Contains(m.Name) &&\n                    m.DeclaringType.Name == \"Script\")\n                    has = false;\n            }\n            return has;\n        }\n\n        static public void callLoadFunction(dynamic script)\n        {\n            script.Load();\n        }\n    }\n\n    public static class Util\n    {\n        public static KeyLayout GetKeyboardLayout(int firstNote, int lastNote, KeyboardOptions options)\n        {\n            double wdth;\n\n            double[] leftArrayKeys = new double[257];\n            double[] widthArrayKeys = new double[257];\n            double[] leftArrayNotes = new double[257];\n            double[] widthArrayNotes = new double[257];\n\n            var layout = new KeyLayout();\n\n            if (options.sameWidthNotes)\n            {\n                var samewidth = 1.0f / (lastNote - firstNote);\n\n                for (int i = 0; i < 257; i++)\n                {\n                    leftArrayKeys[i] = (i - firstNote) / (double)(lastNote - firstNote);\n                    widthArrayKeys[i] = samewidth;\n                    leftArrayNotes[i] = (i - firstNote) / (double)(lastNote - firstNote);\n                    widthArrayNotes[i] = samewidth;\n                }\n\n                layout.blackKeyWidth = samewidth;\n                layout.whiteKeyWidth = samewidth;\n                layout.blackNoteWidth = samewidth;\n                layout.whiteNoteWidth = samewidth;\n            }\n            else\n            {\n                for (int i = 0; i < 257; i++)\n                {\n                    if (!layout.blackKey[i])\n                    {\n                        leftArrayKeys[i] = layout.keyNumber[i];\n                        leftArrayNotes[i] = layout.keyNumber[i];\n                        widthArrayKeys[i] = 1.0f;\n                        widthArrayNotes[i] = 1.0f;\n                    }\n                    else\n                    {\n                        int _i = i + 1;\n                        wdth = options.blackKeyScale;\n                        int bknum = layout.keyNumber[i] % 5;\n                        double offset = wdth / 2;\n                        if (bknum == 0) offset += wdth / 2 * options.blackKey2setOffset;\n                        if (bknum == 2) offset += wdth / 2 * options.blackKey3setOffset;\n                        if (bknum == 1) offset -= wdth / 2 * options.blackKey2setOffset;\n                        if (bknum == 4) offset -= wdth / 2 * options.blackKey3setOffset;\n\n                        offset -= options.advancedBlackKeyOffsets[layout.keyNumber[i] % 5] * wdth / 2;\n\n                        leftArrayKeys[i] = layout.keyNumber[_i] - offset;\n                        widthArrayKeys[i] = wdth;\n\n                        offset -= wdth / 2 * (1 - options.blackNoteScale);\n                        if (bknum == 0) offset += wdth / 2 * options.blackNote2setOffset;\n                        if (bknum == 2) offset += wdth / 2 * options.blackNote3setOffset;\n                        if (bknum == 1) offset -= wdth / 2 * options.blackNote2setOffset;\n                        if (bknum == 4) offset -= wdth / 2 * options.blackNote3setOffset;\n                        wdth *= options.blackNoteScale;\n\n                        leftArrayNotes[i] = layout.keyNumber[_i] - offset;\n                        widthArrayNotes[i] = wdth;\n                    }\n                }\n                double knmfn = leftArrayKeys[firstNote];\n                double knmln = leftArrayKeys[lastNote - 1] + widthArrayKeys[lastNote - 1];\n                double width = knmln - knmfn;\n\n                for (int i = 0; i < 257; i++)\n                {\n                    leftArrayKeys[i] = (leftArrayKeys[i] - knmfn) / width;\n                    leftArrayNotes[i] = (leftArrayNotes[i] - knmfn) / width;\n                    widthArrayKeys[i] /= width;\n                    widthArrayNotes[i] /= width;\n                }\n\n                layout.blackKeyWidth = options.blackKeyScale / width;\n                layout.whiteKeyWidth = 1 / width;\n                layout.blackNoteWidth = options.blackKeyScale * options.blackNoteScale / width;\n                layout.whiteNoteWidth = 1 / width;\n            }\n\n            for (int i = 0; i < 257; i++)\n            {\n                layout.keys[i] = new KeyLayout.pos() { left = leftArrayKeys[i], right = leftArrayKeys[i] + widthArrayKeys[i] };\n                layout.notes[i] = new KeyLayout.pos() { left = leftArrayNotes[i], right = leftArrayNotes[i] + widthArrayNotes[i] };\n            }\n\n            return layout;\n        }\n\n        public static bool IsBlackKey(int n)\n        {\n            n = n % 12;\n            if (n < 0) n += 12;\n            return n == 1 || n == 3 || n == 6 || n == 8 || n == 10;\n        }\n\n        public static IEnumerable<Note> BlackNotesAbove(IEnumerable<Note> notes)\n        {\n            foreach (var n in notes)\n            {\n                if (!IsBlackKey(n.key)) yield return n;\n            }\n\n            foreach (var n in notes)\n            {\n                if (IsBlackKey(n.key)) yield return n;\n            }\n        }\n\n        public static Color4 BlendColors(Color4 col1, Color4 col2)\n        {\n            float blendfac = col2.A;\n            float revblendfac = 1 - blendfac;\n            return new Color4(\n                col2.R * blendfac + col1.R * revblendfac,\n                col2.G * blendfac + col1.G * revblendfac,\n                col2.B * blendfac + col1.B * revblendfac,\n                col1.A + (1 - col1.A) * blendfac);\n        }\n    }\n}\n"
  },
  {
    "path": "BMEngine/UI/BBinding.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Data;\n\nnamespace ZenithEngine.UI\n{\n    public class BBinding : Binding\n    {\n        public BBinding(DependencyProperty dp, object source) : base()\n        {\n            Path = new PropertyPath(dp);\n            Source = source;\n        }\n    }\n}\n"
  },
  {
    "path": "BMEngine/UI/BetterCheckbox.xaml",
    "content": "﻿<UserControl x:Class=\"ZenithEngine.UI.BetterCheckbox\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" \n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\" \n             xmlns:local=\"clr-namespace:ZenithEngine.UI\"\n             mc:Ignorable=\"d\" Name=\"root\" >\n    <UserControl.Resources>\n        <ResourceDictionary>\n            <ResourceDictionary.MergedDictionaries>\n                <ResourceDictionary Source=\"Material.xaml\"/>\n            </ResourceDictionary.MergedDictionaries>\n        </ResourceDictionary>\n    </UserControl.Resources>\n    <DockPanel MouseDown=\"DockPanel_MouseDown\" Background=\"Transparent\">\n        <Grid Margin=\"4,0,0,0\" VerticalAlignment=\"Center\">\n            <Border Width=\"16\" Height=\"16\" BorderThickness=\"2\" Background=\"Transparent\" CornerRadius=\"3\">\n\n                <Border.Style>\n                    <Style TargetType=\"Border\">\n                        <Setter Property=\"BorderBrush\" Value=\"{StaticResource PrimaryBrush}\"/>\n                        <Style.Triggers>\n                            <Trigger Property=\"IsEnabled\" Value=\"False\">\n                                <Setter Property=\"BorderBrush\" Value=\"#777777\"/>\n                            </Trigger>\n                        </Style.Triggers>\n                    </Style>\n                </Border.Style>\n                <Border Name=\"checkedBox\" Margin=\"2\" CornerRadius=\"1\">\n                    <Border.Style>\n                        <Style TargetType=\"Border\">\n                            <Setter Property=\"Background\" Value=\"{StaticResource PrimaryBrush}\"/>\n                            <Style.Triggers>\n                                <Trigger Property=\"IsEnabled\" Value=\"False\">\n                                    <Setter Property=\"Background\" Value=\"#777777\"/>\n                                </Trigger>\n                            </Style.Triggers>\n                        </Style>\n                    </Border.Style>\n                </Border>\n            </Border>\n            <Grid Name=\"rippleBox\" Margin=\"-7\">\n\n            </Grid>\n        </Grid>\n        <TextBlock FontSize=\"14\" Margin=\"3,-3,0,0\" Text=\"{Binding ElementName=root, Path=Text}\" VerticalAlignment=\"Center\"/>\n    </DockPanel>\n</UserControl>\n"
  },
  {
    "path": "BMEngine/UI/BetterCheckbox.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Animation;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\n\nnamespace ZenithEngine.UI\n{\n    /// <summary>\n    /// Interaction logic for BetterCheckbox.xaml\n    /// </summary>\n    public partial class BetterCheckbox : UserControl\n    {\n        public bool IsChecked\n        {\n            get { return (bool)GetValue(IsCheckedProperty); }\n            set { SetValue(IsCheckedProperty, value); }\n        }\n\n        public static readonly DependencyProperty IsCheckedProperty =\n            DependencyProperty.Register(\"IsChecked\", typeof(bool), typeof(BetterCheckbox), new PropertyMetadata(false, (s, e) => ((BetterCheckbox)s).OnCheckChanged()));\n\n\n        public string Text\n        {\n            get { return (string)GetValue(TextProperty); }\n            set { SetValue(TextProperty, value); }\n        }\n\n        public static readonly DependencyProperty TextProperty =\n            DependencyProperty.Register(\"Text\", typeof(string), typeof(BetterCheckbox), new PropertyMetadata(\"\"));\n\n\n        public static readonly RoutedEvent CheckToggledEvent = EventManager.RegisterRoutedEvent(\n            \"RadioChecked\", RoutingStrategy.Bubble,\n            typeof(RoutedPropertyChangedEventHandler<bool>), typeof(BetterCheckbox));\n\n        public event RoutedPropertyChangedEventHandler<bool> CheckToggled\n        {\n            add { AddHandler(CheckToggledEvent, value); }\n            remove { RemoveHandler(CheckToggledEvent, value); }\n        }\n\n\n        void OnCheckChanged()\n        {\n            RaiseEvent(new RoutedPropertyChangedEventArgs<bool>(!IsChecked, IsChecked, CheckToggledEvent));\n        }\n\n\n        public BetterCheckbox()\n        {\n            InitializeComponent();\n            new InplaceConverter(new[] {\n                new BBinding(IsCheckedProperty, this)\n            }, v => (bool)v[0] ? Visibility.Visible : Visibility.Hidden)\n                .Set(checkedBox, VisibilityProperty);\n        }\n\n        private void DockPanel_MouseDown(object sender, MouseButtonEventArgs e)\n        {\n            IsChecked = !IsChecked;\n\n\n            double ExpandTime = 0.1;\n            double FadeTime = 0.1;\n\n            double o = 0.7;\n\n            var targetWidth = rippleBox.ActualWidth;\n\n            var ellipse = new Ellipse()\n            {\n                Fill = (Brush)Resources[\"PrimaryBrush\"],\n                HorizontalAlignment = HorizontalAlignment.Center,\n                VerticalAlignment = VerticalAlignment.Center,\n                Opacity = o\n            };\n            ellipse.SetBinding(HeightProperty, new Binding(\"Width\") { Source = ellipse });\n\n            Storyboard storyboard = new Storyboard();\n\n            var expand = new DoubleAnimation(10, targetWidth, new Duration(TimeSpan.FromSeconds(ExpandTime + FadeTime)));\n            storyboard.Children.Add(expand);\n            Storyboard.SetTarget(expand, ellipse);\n            Storyboard.SetTargetProperty(expand, new PropertyPath(WidthProperty));\n\n            var opacity = new DoubleAnimation(o, 0, new Duration(TimeSpan.FromSeconds(FadeTime)));\n            opacity.BeginTime = TimeSpan.FromSeconds(ExpandTime);\n            storyboard.Children.Add(opacity);\n            Storyboard.SetTarget(opacity, ellipse);\n            Storyboard.SetTargetProperty(opacity, new PropertyPath(Ellipse.OpacityProperty));\n\n            rippleBox.Children.Add(ellipse);\n\n            storyboard.Begin();\n\n            var waitTime = ExpandTime + FadeTime;\n            Task.Run(() =>\n            {\n                Thread.Sleep(TimeSpan.FromSeconds(waitTime));\n                Dispatcher.Invoke(() =>\n                {\n                    rippleBox.Children.Remove(ellipse);\n                });\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "BMEngine/UI/BetterRadio.xaml",
    "content": "﻿<UserControl x:Class=\"ZenithEngine.UI.BetterRadio\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" \n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\" \n             xmlns:local=\"clr-namespace:ZenithEngine.UI\"\n             mc:Ignorable=\"d\" Name=\"root\">\n    <UserControl.Resources>\n        <ResourceDictionary>\n            <ResourceDictionary.MergedDictionaries>\n                <ResourceDictionary Source=\"Material.xaml\"/>\n            </ResourceDictionary.MergedDictionaries>\n        </ResourceDictionary>\n    </UserControl.Resources>\n    <DockPanel MouseDown=\"DockPanel_MouseDown\" Background=\"Transparent\">\n        <Grid VerticalAlignment=\"Center\">\n            <Border Width=\"16\" Height=\"16\" BorderThickness=\"2\" BorderBrush=\"{StaticResource PrimaryBrush}\" Background=\"Transparent\" CornerRadius=\"10\">\n                <Border Name=\"checkedBox\" Margin=\"2\" CornerRadius=\"10\" Background=\"{StaticResource PrimaryBrush}\">\n                </Border>\n            </Border>\n            <Grid Name=\"rippleBox\" Margin=\"-7\">\n\n            </Grid>\n        </Grid>\n        <TextBlock FontSize=\"14\" Margin=\"3,0,0,0\" Text=\"{Binding ElementName=root, Path=Text}\" VerticalAlignment=\"Center\"/>\n    </DockPanel>\n</UserControl>\n"
  },
  {
    "path": "BMEngine/UI/BetterRadio.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Animation;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\n\nnamespace ZenithEngine.UI\n{\n    /// <summary>\n    /// Interaction logic for BetterRadio.xaml\n    /// </summary>\n    public partial class BetterRadio : UserControl\n    {\n        public bool IsChecked\n        {\n            get { return (bool)GetValue(IsCheckedProperty); }\n            set { SetValue(IsCheckedProperty, value); }\n        }\n\n        public static readonly DependencyProperty IsCheckedProperty =\n            DependencyProperty.Register(\"IsChecked\", typeof(bool), typeof(BetterRadio), new PropertyMetadata(false, (s, e) => ((BetterRadio)s).OnCheckChanged()));\n\n\n        public string Text\n        {\n            get { return (string)GetValue(TextProperty); }\n            set { SetValue(TextProperty, value); }\n        }\n\n        public static readonly DependencyProperty TextProperty =\n            DependencyProperty.Register(\"Text\", typeof(string), typeof(BetterRadio), new PropertyMetadata(\"\"));\n\n\n        public int ParentDepth\n        {\n            get { return (int)GetValue(ParentDepthProperty); }\n            set { SetValue(ParentDepthProperty, value); }\n        }\n\n        public static readonly DependencyProperty ParentDepthProperty =\n            DependencyProperty.Register(\"ParentDepth\", typeof(int), typeof(BetterRadio), new PropertyMetadata(1));\n\n\n        public static readonly RoutedEvent RadioCheckedEvent = EventManager.RegisterRoutedEvent(\n            \"RadioChecked\", RoutingStrategy.Bubble,\n            typeof(RoutedEventHandler), typeof(BetterRadio));\n\n        public event RoutedEventHandler RadioChecked\n        {\n            add { AddHandler(RadioCheckedEvent, value); }\n            remove { RemoveHandler(RadioCheckedEvent, value); }\n        }\n\n\n        void OnCheckChanged()\n        {\n            if (IsChecked)\n            {\n                if (ParentDepth != 0)\n                {\n                    FrameworkElement p = (FrameworkElement)Parent;\n                    for (int i = 1; i < ParentDepth; i++)\n                    {\n                        p = (FrameworkElement)p.Parent;\n                    }\n                    RecursiveUncheck(p);\n                }\n                RaiseEvent(new RoutedEventArgs(RadioCheckedEvent));\n            }\n        }\n\n\n        public BetterRadio()\n        {\n            InitializeComponent();\n            new InplaceConverter(new[] {\n                new BBinding(IsCheckedProperty, this)\n            }, v => (bool)v[0] ? Visibility.Visible : Visibility.Hidden)\n                .Set(checkedBox, VisibilityProperty);\n        }\n\n        void RecursiveUncheck(FrameworkElement p)\n        {\n            if (p is Panel)\n                foreach (var c in ((Panel)p).Children) if(c is FrameworkElement) RecursiveUncheck((FrameworkElement)c);\n            if (p is BetterRadio && p != this) ((BetterRadio)p).IsChecked = false;\n        }\n\n        private void DockPanel_MouseDown(object sender, MouseButtonEventArgs e)\n        {\n            IsChecked = true;\n\n            double ExpandTime = 0.1;\n            double FadeTime = 0.1;\n\n            double o = 0.7;\n\n            var targetWidth = rippleBox.ActualWidth;\n\n            var ellipse = new Ellipse()\n            {\n                Fill = (Brush)Resources[\"PrimaryBrush\"],\n                HorizontalAlignment = HorizontalAlignment.Center,\n                VerticalAlignment = VerticalAlignment.Center,\n                Opacity = o\n            };\n            ellipse.SetBinding(HeightProperty, new Binding(\"Width\") { Source = ellipse });\n\n            Storyboard storyboard = new Storyboard();\n\n            var expand = new DoubleAnimation(10, targetWidth, new Duration(TimeSpan.FromSeconds(ExpandTime + FadeTime)));\n            storyboard.Children.Add(expand);\n            Storyboard.SetTarget(expand, ellipse);\n            Storyboard.SetTargetProperty(expand, new PropertyPath(WidthProperty));\n\n            var opacity = new DoubleAnimation(o, 0, new Duration(TimeSpan.FromSeconds(FadeTime)));\n            opacity.BeginTime = TimeSpan.FromSeconds(ExpandTime);\n            storyboard.Children.Add(opacity);\n            Storyboard.SetTarget(opacity, ellipse);\n            Storyboard.SetTargetProperty(opacity, new PropertyPath(Ellipse.OpacityProperty));\n\n            rippleBox.Children.Add(ellipse);\n\n            storyboard.Begin();\n\n            var waitTime = ExpandTime + FadeTime;\n            Task.Run(() =>\n            {\n                Thread.Sleep(TimeSpan.FromSeconds(waitTime));\n                Dispatcher.Invoke(() =>\n                {\n                    rippleBox.Children.Remove(ellipse);\n                });\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "BMEngine/UI/BetterSlider.xaml",
    "content": "﻿<UserControl x:Class=\"ZenithEngine.UI.BetterSlider\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" \n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\" \n             xmlns:local=\"clr-namespace:ZenithEngine.UI\"\n             mc:Ignorable=\"d\" \n             Name=\"slider\"\n             d:DesignHeight=\"30\" d:DesignWidth=\"800\" Focusable=\"True\">\n    <UserControl.Resources>\n        <ResourceDictionary>\n            <ResourceDictionary.MergedDictionaries>\n                <ResourceDictionary Source=\"Material.xaml\"/>\n            </ResourceDictionary.MergedDictionaries>\n            <local:DoubleMultiplyConverter x:Key=\"DoubleMultiplyConverter\"/>\n            <local:ThicknessConverter x:Key=\"ThicknessConverter\"/>\n        </ResourceDictionary>\n    </UserControl.Resources>\n    <Grid Height=\"14\" Name=\"clickerGrid\" Background=\"Transparent\" MouseEnter=\"ClickerGrid_MouseEnter\" MouseLeave=\"ClickerGrid_MouseLeave\" MouseDown=\"ClickerGrid_MouseDown\" MouseMove=\"ClickerGrid_MouseMove\" MouseUp=\"ClickerGrid_MouseUp\">\n        <Grid Margin=\"5\" VerticalAlignment=\"Center\" Name=\"barGrid\" Height=\"4\" Background=\"#55000000\">\n            <Rectangle HorizontalAlignment=\"Left\" Fill=\"{StaticResource PrimaryBrush}\" Opacity=\"0.8\">\n                <Rectangle.Style>\n                    <Style TargetType=\"Rectangle\">\n                        <Setter Property=\"Fill\" Value=\"{StaticResource PrimaryBrush}\"/>\n                        <Style.Triggers>\n                            <Trigger Property=\"IsEnabled\" Value=\"False\">\n                                <Setter Property=\"Fill\" Value=\"#777777\"/>\n                            </Trigger>\n                        </Style.Triggers>\n                    </Style>\n                </Rectangle.Style>\n                <Rectangle.Width>\n                    <Binding ElementName=\"slider\" Path=\"ScaledValue\"/>\n                </Rectangle.Width>\n            </Rectangle>\n        </Grid>\n        <Grid Name=\"headGrid\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Center\" Height=\"10\" Width=\"10\">\n            <Ellipse Name=\"trueHead\" Width=\"10\" Height=\"10\" HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\">\n                <Ellipse.Style>\n                    <Style TargetType=\"Ellipse\">\n                        <Setter Property=\"Fill\" Value=\"{StaticResource PrimaryBrush}\"/>\n                        <Style.Triggers>\n                            <Trigger Property=\"IsEnabled\" Value=\"False\">\n                                <Setter Property=\"Fill\" Value=\"#777777\"/>\n                            </Trigger>\n                        </Style.Triggers>\n                    </Style>\n                </Ellipse.Style>\n            </Ellipse>\n            <Grid Margin=\"-7\" Name=\"auraGrid\">\n                <Ellipse Name=\"hoverEllipse\" Visibility=\"Hidden\" Fill=\"#44000000\"/>\n            </Grid>\n        </Grid>\n    </Grid>\n</UserControl>\n"
  },
  {
    "path": "BMEngine/UI/BetterSlider.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing System.Linq;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Animation;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\n\nnamespace ZenithEngine.UI\n{\n    /// <summary>\n    /// Interaction logic for BetterSlider.xaml\n    /// </summary>\n    public partial class BetterSlider : UserControl\n    {\n        public double Minimum\n        {\n            get { return (double)GetValue(MinimumProperty); }\n            set { SetValue(MinimumProperty, value); }\n        }\n\n        public static readonly DependencyProperty MinimumProperty =\n            DependencyProperty.Register(\"Minimum\", typeof(double), typeof(BetterSlider), new PropertyMetadata(0.0));\n\n\n        public double Maximum\n        {\n            get { return (double)GetValue(MaximumProperty); }\n            set { SetValue(MaximumProperty, value); }\n        }\n\n        public static readonly DependencyProperty MaximumProperty =\n            DependencyProperty.Register(\"Maximum\", typeof(double), typeof(BetterSlider), new PropertyMetadata(0.0));\n\n\n        static void ValueChangedCallback(DependencyObject s, DependencyPropertyChangedEventArgs e)\n        {\n            var slider = (BetterSlider)s;\n            //slider.ScaledValue = (slider.Value - slider.Minimum) / slider.Maximum * slider.barGrid.ActualWidth;\n        }\n\n        public double Value\n        {\n            get { return (double)GetValue(ValueProperty); }\n            set\n            {\n                var v = value;\n                if (v > Maximum) v = Maximum;\n                if (v < Minimum) v = Minimum;\n                SetValue(ValueProperty, v);\n            }\n        }\n\n        public static readonly DependencyProperty ValueProperty =\n            DependencyProperty.Register(\"Value\", typeof(double), typeof(BetterSlider), new PropertyMetadata(0.0, ValueChangedCallback));\n\n\n        double ScaledValue\n        {\n            get { return (double)GetValue(ScaledValueProperty); }\n            set { SetValue(ScaledValueProperty, value); }\n        }\n\n        static readonly DependencyProperty ScaledValueProperty =\n            DependencyProperty.Register(\"ScaledValue\", typeof(double), typeof(BetterSlider), new PropertyMetadata(0.0));\n\n\n        public event EventHandler<double> UserValueChanged;\n\n\n        public BetterSlider()\n        {\n            InitializeComponent();\n\n            new InplaceConverter(new[]\n            {\n                new BBinding(ValueProperty, this),\n                new BBinding(MinimumProperty, this),\n                new BBinding(MaximumProperty, this),\n                new BBinding(ActualWidthProperty, barGrid),\n            },\n            (values) =>\n            {\n                try\n                {\n                    return ((double)values[0] - (double)values[1]) / ((double)values[2] - (double)values[1]) * (double)values[3];\n                }\n                catch { return 0; }\n            })\n                .Set(this, ScaledValueProperty);\n\n            new InplaceConverter(new[] { new BBinding(ScaledValueProperty, this) },\n            (values) => new Thickness((double)values[0], 0, 0, 0))\n                .Set(headGrid, MarginProperty);\n        }\n\n        private void ClickerGrid_MouseEnter(object sender, MouseEventArgs e)\n        {\n            //hoverEllipse.Visibility = Visibility.Visible;\n        }\n\n        private void ClickerGrid_MouseLeave(object sender, MouseEventArgs e)\n        {\n            //hoverEllipse.Visibility = Visibility.Hidden;\n        }\n\n        private void ClickerGrid_MouseDown(object sender, MouseButtonEventArgs e)\n        {\n            clickerGrid.CaptureMouse();\n            Value = e.GetPosition(barGrid).X / barGrid.ActualWidth * (Maximum - Minimum) + Minimum;\n            UserValueChanged?.Invoke(this, Value);\n            AddRipple();\n            this.Focus();\n        }\n\n        private void ClickerGrid_MouseMove(object sender, MouseEventArgs e)\n        {\n            if (clickerGrid.IsMouseCaptureWithin)\n            {\n                Value = e.GetPosition(barGrid).X / barGrid.ActualWidth * (Maximum - Minimum) + Minimum;\n                UserValueChanged?.Invoke(this, Value);\n            }\n        }\n\n        private void ClickerGrid_MouseUp(object sender, MouseButtonEventArgs e)\n        {\n            clickerGrid.ReleaseMouseCapture();\n        }\n\n        void AddRipple()\n        {\n            double ExpandTime = 0.1;\n            double FadeTime = 0.1;\n\n            double o = 0.7;\n\n            var targetWidth = auraGrid.ActualWidth;\n\n            var ellipse = new Ellipse()\n            {\n                Fill = (Brush)Resources[\"PrimaryBrush\"],\n                HorizontalAlignment = HorizontalAlignment.Center,\n                VerticalAlignment = VerticalAlignment.Center,\n                Opacity = o\n            };\n            ellipse.SetBinding(HeightProperty, new Binding(\"Width\") { Source = ellipse });\n\n            Storyboard storyboard = new Storyboard();\n\n            var expand = new DoubleAnimation(0, targetWidth, new Duration(TimeSpan.FromSeconds(ExpandTime + FadeTime)));\n            storyboard.Children.Add(expand);\n            Storyboard.SetTarget(expand, ellipse);\n            Storyboard.SetTargetProperty(expand, new PropertyPath(WidthProperty));\n\n            var opacity = new DoubleAnimation(o, 0, new Duration(TimeSpan.FromSeconds(FadeTime)));\n            opacity.BeginTime = TimeSpan.FromSeconds(ExpandTime);\n            storyboard.Children.Add(opacity);\n            Storyboard.SetTarget(opacity, ellipse);\n            Storyboard.SetTargetProperty(opacity, new PropertyPath(Ellipse.OpacityProperty));\n\n            auraGrid.Children.Add(ellipse);\n\n            storyboard.Begin();\n\n            var waitTime = ExpandTime + FadeTime;\n            Task.Run(() =>\n            {\n                Thread.Sleep(TimeSpan.FromSeconds(waitTime));\n                Dispatcher.Invoke(() =>\n                {\n                    headGrid.Children.Remove(ellipse);\n                });\n            });\n        }\n    }\n\n    public class DoubleMultiplyConverter : IMultiValueConverter\n    {\n        public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)\n        {\n            double r = 1;\n            foreach (var v in values) r *= (double)v;\n            return r;\n        }\n\n        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)\n        {\n            throw new NotImplementedException();\n        }\n    }\n\n    public class ScaledValueConverter : IMultiValueConverter\n    {\n        public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)\n        {\n            return ((double)values[0] - (double)values[1]) / ((double)values[2] - (double)values[1]) * (double)values[3];\n        }\n\n        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)\n        {\n            throw new NotImplementedException();\n        }\n    }\n\n    public class ThicknessConverter : IValueConverter\n    {\n        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            return new Thickness(\n                    (double)value,\n                    0,\n                    0,\n                    0\n                );\n        }\n\n        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            throw new NotImplementedException();\n        }\n    }\n}\n"
  },
  {
    "path": "BMEngine/UI/Colors.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:local=\"clr-namespace:ZenithEngine.UI\"\n                    xmlns:sys=\"clr-namespace:System;assembly=mscorlib\"\n                    xmlns:theme=\"clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero2\"\n                    xmlns:media=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n    <media:Color x:Key=\"PrimaryColor\" >#2E7D32</media:Color>\n    <SolidColorBrush x:Key=\"PrimaryBrush\" Color=\"{StaticResource PrimaryColor}\"/>\n</ResourceDictionary>"
  },
  {
    "path": "BMEngine/UI/Converters.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows.Data;\n\nnamespace ZenithEngine.UI\n{\n\n    public class AndValueConverter : IMultiValueConverter\n    {\n        public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)\n        {\n            bool b = true;\n            for (int i = 0; i < values.Length; i++) b = b && (bool)values[i];\n\n            return b;\n        }\n\n        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)\n        {\n            throw new NotImplementedException();\n        }\n    }\n\n    public class OrValueConverter : IMultiValueConverter\n    {\n        public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)\n        {\n            bool b = false;\n            for (int i = 0; i < values.Length; i++) b = b || (bool)values[i];\n\n            return b;\n        }\n\n        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)\n        {\n            throw new NotImplementedException();\n        }\n    }\n\n    public class NotValueConverter : IMultiValueConverter\n    {\n        public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)\n        {\n            return !(bool)values[0];\n        }\n\n        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)\n        {\n            throw new NotImplementedException();\n        }\n    }\n}\n"
  },
  {
    "path": "BMEngine/UI/HexColorPicker.xaml",
    "content": "﻿<UserControl x:Class=\"ZenithEngine.UI.HexColorPicker\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" \n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\" \n             xmlns:local=\"clr-namespace:ZenithEngine.UI\"\n             mc:Ignorable=\"d\" KeyDown=\"UserControl_KeyDown\">\n    <Grid>\n        <TextBox FontSize=\"14\" Background=\"Transparent\" BorderThickness=\"0\" Name=\"hexText\" Text=\"FFFFFF\" TextChanged=\"HexText_TextChanged\" LostFocus=\"HexText_LostFocus\"/>\n        <Rectangle Fill=\"Black\" Height=\"1\" VerticalAlignment=\"Bottom\"/>\n    </Grid>\n</UserControl>\n"
  },
  {
    "path": "BMEngine/UI/HexColorPicker.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\n\nnamespace ZenithEngine.UI\n{\n    /// <summary>\n    /// Interaction logic for HexColorPicker.xaml\n    /// </summary>\n    public partial class HexColorPicker : UserControl\n    {\n        public Color Color\n        {\n            get { return (Color)GetValue(ColorProperty); }\n            set { SetValue(ColorProperty, value); }\n        }\n\n        public static readonly DependencyProperty ColorProperty =\n            DependencyProperty.Register(\"Color\", typeof(Color), typeof(HexColorPicker), new PropertyMetadata(Color.FromArgb(255, 255, 255, 255), (s, e) => ((HexColorPicker)s).OnColorPropertyChanged(e)));\n\n\n        public bool UseAlpha\n        {\n            get { return (bool)GetValue(UseAlphaProperty); }\n            set { SetValue(UseAlphaProperty, value); }\n        }\n\n        public static readonly DependencyProperty UseAlphaProperty =\n            DependencyProperty.Register(\"UseAlpha\", typeof(bool), typeof(HexColorPicker), new PropertyMetadata(false));\n\n        public static readonly RoutedEvent ValueChangedEvent = EventManager.RegisterRoutedEvent(\n            \"ValueChanged\", RoutingStrategy.Bubble,\n            typeof(RoutedPropertyChangedEventHandler<Color>), typeof(HexColorPicker));\n\n        public event RoutedPropertyChangedEventHandler<Color> ValueChanged\n        {\n            add { AddHandler(ValueChangedEvent, value); }\n            remove { RemoveHandler(ValueChangedEvent, value); }\n        }\n\n\n        void OnColorPropertyChanged(DependencyPropertyChangedEventArgs e)\n        {\n            SaveString();\n            RaiseEvent(new RoutedPropertyChangedEventArgs<Color>((Color)e.OldValue, (Color)e.NewValue, ValueChangedEvent));\n        }\n\n        string Hexify(byte val)\n        {\n            var s = val.ToString(\"X\");\n            if (s.Length == 1) return \"0\" + s;\n            return s;\n        }\n\n        void SaveString()\n        {\n            string s = \"\";\n            s += Hexify(Color.R);\n            s += Hexify(Color.G);\n            s += Hexify(Color.B);\n            if (UseAlpha && (hexText.Text.Length != 6 || Color.A != 255))\n                s += Hexify(Color.A);\n            if (hexText.Text != s)\n                hexText.Text = s;\n        }\n\n\n        public HexColorPicker()\n        {\n            InitializeComponent();\n\n            new InplaceConverter(\n                new[] { new BBinding(UseAlphaProperty, this) },\n                (e) => (bool)e[0] ? 8 : 6\n            ).Set(hexText, TextBox.MaxLengthProperty);\n        }\n\n        private void HexText_TextChanged(object sender, TextChangedEventArgs e)\n        {\n            if (!((hexText.Text.Length == 6) || (UseAlpha && hexText.Text.Length == 8))) return;\n            try\n            {\n                int col = int.Parse(hexText.Text, System.Globalization.NumberStyles.HexNumber);\n                Color c;\n                if (hexText.Text.Length == 8)\n                    c = Color.FromArgb(\n                        (byte)((col >> 0) & 0xFF),\n                        (byte)((col >> 24) & 0xFF),\n                        (byte)((col >> 16) & 0xFF),\n                        (byte)((col >> 8) & 0xFF)\n                    );\n                else\n                    c = Color.FromArgb(\n                        255,\n                        (byte)((col >> 16) & 0xFF),\n                        (byte)((col >> 8) & 0xFF),\n                        (byte)((col >> 0) & 0xFF)\n                    );\n                Color = c;\n            }\n            catch { }\n        }\n\n        private void HexText_LostFocus(object sender, RoutedEventArgs e)\n        {\n            SaveString();\n        }\n\n        private void UserControl_KeyDown(object sender, KeyEventArgs e)\n        {\n            if (e.Key == Key.Enter)\n            {\n                SaveString();\n                e.Handled = true;\n                Keyboard.ClearFocus();\n\n                FrameworkElement parent = (FrameworkElement)hexText.Parent;\n                while (parent != null && parent is IInputElement && !((IInputElement)parent).Focusable)\n                {\n                    parent = (FrameworkElement)parent.Parent;\n                }\n\n                DependencyObject scope = FocusManager.GetFocusScope(hexText);\n                FocusManager.SetFocusedElement(scope, parent as IInputElement);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "BMEngine/UI/InplaceConverter.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Data;\nusing System.Reflection;\n\nnamespace ZenithEngine.UI\n{\n    public class InplaceConverter : IMultiValueConverter\n    {\n        Func<object[], object> func;\n        Binding[] bindings;\n\n        public InplaceConverter(Binding[] bindings, Func<object[], object> func)\n        {\n            this.bindings = bindings;\n            this.func = func;\n        }\n\n        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)\n        {\n            return func(values);\n        }\n\n        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)\n        {\n            throw new NotImplementedException();\n        }\n\n        public void Set(FrameworkElement o, DependencyProperty p)\n        {\n            var b = new MultiBinding();\n            b.Converter = this;\n            foreach (var _b in bindings) b.Bindings.Add(_b);\n            o.SetBinding(p, b);\n        }\n    }\n}\n"
  },
  {
    "path": "BMEngine/UI/ListBox.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:local=\"clr-namespace:ZenithEngine.UI\"\n                    xmlns:sys=\"clr-namespace:System;assembly=mscorlib\"\n                    xmlns:theme=\"clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero2\"\n                    xmlns:media=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n    <ResourceDictionary.MergedDictionaries>\n        <ResourceDictionary Source=\"Colors.xaml\"/>\n    </ResourceDictionary.MergedDictionaries>\n    <media:Style TargetType=\"{x:Type media:ListBox}\">\n        <media:Setter Property=\"BorderThickness\" Value=\"1\"/>\n        <media:Setter Property=\"media:ScrollViewer.HorizontalScrollBarVisibility\" Value=\"Auto\"/>\n        <media:Setter Property=\"media:ScrollViewer.VerticalScrollBarVisibility\" Value=\"Auto\"/>\n        <media:Setter Property=\"media:ScrollViewer.CanContentScroll\" Value=\"true\"/>\n        <media:Setter Property=\"media:ScrollViewer.PanningMode\" Value=\"Both\"/>\n        <media:Setter Property=\"media:Stylus.IsFlicksEnabled\" Value=\"False\"/>\n        <media:Setter Property=\"VerticalContentAlignment\" Value=\"Center\"/>\n        <media:Setter Property=\"Template\">\n            <media:Setter.Value>\n                <media:ControlTemplate TargetType=\"{x:Type media:ListBox}\">\n                    <media:Border x:Name=\"Bd\" BorderBrush=\"Black\" BorderThickness=\"1\" Background=\"Transparent\" Padding=\"1\" SnapsToDevicePixels=\"true\">\n                        <media:ScrollViewer Focusable=\"false\" Padding=\"{media:TemplateBinding Padding}\">\n                            <media:ItemsPresenter SnapsToDevicePixels=\"{media:TemplateBinding SnapsToDevicePixels}\"/>\n                        </media:ScrollViewer>\n                    </media:Border>\n                </media:ControlTemplate>\n            </media:Setter.Value>\n        </media:Setter>\n    </media:Style>\n    <media:Style TargetType=\"{x:Type media:ListBoxItem}\">\n        <media:Setter Property=\"SnapsToDevicePixels\" Value=\"True\"/>\n        <media:Setter Property=\"Padding\" Value=\"4,1\"/>\n        <media:Setter Property=\"HorizontalContentAlignment\" Value=\"{media:Binding HorizontalContentAlignment, RelativeSource={media:RelativeSource AncestorType={x:Type media:ItemsControl}}}\"/>\n        <media:Setter Property=\"VerticalContentAlignment\" Value=\"{media:Binding VerticalContentAlignment, RelativeSource={media:RelativeSource AncestorType={x:Type media:ItemsControl}}}\"/>\n        <media:Setter Property=\"Background\" Value=\"Transparent\"/>\n        <media:Setter Property=\"BorderBrush\" Value=\"Transparent\"/>\n        <media:Setter Property=\"BorderThickness\" Value=\"1\"/>\n        <media:Setter Property=\"FontSize\" Value=\"14\"/>\n        <media:Setter Property=\"Foreground\" Value=\"White\"/>\n        <media:Setter Property=\"Padding\" Value=\"5\"/>\n        <media:Setter Property=\"Template\">\n            <media:Setter.Value>\n                <media:ControlTemplate TargetType=\"{x:Type media:ListBoxItem}\">\n                    <local:RippleEffectDecorator>\n                        <media:Border x:Name=\"Bd\" Background=\"{media:TemplateBinding Background}\" Padding=\"{media:TemplateBinding Padding}\" SnapsToDevicePixels=\"true\">\n                            <media:ContentPresenter HorizontalAlignment=\"{media:TemplateBinding HorizontalContentAlignment}\" SnapsToDevicePixels=\"{media:TemplateBinding SnapsToDevicePixels}\" VerticalAlignment=\"{media:TemplateBinding VerticalContentAlignment}\"/>\n                        </media:Border>\n                    </local:RippleEffectDecorator>\n                    <media:ControlTemplate.Triggers>\n                        <media:MultiTrigger>\n                            <media:MultiTrigger.Conditions>\n                                <media:Condition Property=\"IsMouseOver\" Value=\"True\"/>\n                            </media:MultiTrigger.Conditions>\n                            <media:Setter Property=\"Background\" TargetName=\"Bd\" Value=\"#11FFFFFF\"/>\n                        </media:MultiTrigger>\n                        <media:MultiTrigger>\n                            <media:MultiTrigger.Conditions>\n                                <media:Condition Property=\"IsSelected\" Value=\"True\"/>\n                            </media:MultiTrigger.Conditions>\n                            <media:Setter Property=\"Background\" TargetName=\"Bd\" Value=\"#33FFFFFF\"/>\n                        </media:MultiTrigger>\n                    </media:ControlTemplate.Triggers>\n                </media:ControlTemplate>\n            </media:Setter.Value>\n        </media:Setter>\n    </media:Style>\n</ResourceDictionary>"
  },
  {
    "path": "BMEngine/UI/Material.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:local=\"clr-namespace:ZenithEngine.UI\"\n                    xmlns:sys=\"clr-namespace:System;assembly=mscorlib\"\n                    xmlns:theme=\"clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero2\"\n                    xmlns:media=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n    <ResourceDictionary.MergedDictionaries>\n        <ResourceDictionary Source=\"Scrollbar.xaml\"/>\n        <ResourceDictionary Source=\"Colors.xaml\"/>\n        <ResourceDictionary Source=\"ListBox.xaml\"/>\n    </ResourceDictionary.MergedDictionaries>\n    <Style TargetType=\"{x:Type TextBlock}\">\n        <Setter Property=\"Control.Foreground\" Value=\"White\"/>\n        <Setter Property=\"Control.FontSize\" Value=\"14\"/>\n    </Style>\n    <Style TargetType=\"{x:Type Label}\">\n        <Setter Property=\"Control.Foreground\" Value=\"White\"/>\n        <Setter Property=\"Control.FontSize\" Value=\"14\"/>\n    </Style>\n    <Style x:Key=\"WindowButton\" TargetType=\"Button\">\n        <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n        <Setter Property=\"Focusable\" Value=\"False\"/>\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"Button\">\n                    <Border Name=\"border\"\n                        BorderThickness=\"1\"\n                        Padding=\"4,2\"\n                        BorderBrush=\"DarkGray\"\n                        CornerRadius=\"3\"\n                        Background=\"{TemplateBinding Background}\">\n                        <Grid >\n                            <ContentPresenter HorizontalAlignment=\"Center\"\n                           VerticalAlignment=\"Center\" Name=\"content\"/>\n                        </Grid>\n                    </Border>\n                    <ControlTemplate.Triggers>\n                        <Trigger Property=\"IsMouseOver\" Value=\"True\">\n                            <Setter TargetName=\"border\" Property=\"BorderBrush\" Value=\"#FF4788c8\" />\n                            <Setter Property=\"Foreground\" Value=\"#FF4788c8\" />\n                        </Trigger>\n                        <Trigger Property=\"IsPressed\" Value=\"True\">\n                        </Trigger>\n                        <Trigger Property=\"IsDefaulted\" Value=\"True\">\n                        </Trigger>\n                        <Trigger Property=\"IsFocused\" Value=\"True\">\n                        </Trigger>\n                    </ControlTemplate.Triggers>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n    <Style x:Key=\"MenuButtonStyle\" TargetType=\"Button\">\n        <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n        <Setter Property=\"Focusable\" Value=\"False\"/>\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"Button\">\n                    <Border Name=\"border\"\n                        Padding=\"4,2\"\n                        Background=\"{TemplateBinding Background}\">\n                        <Grid >\n                            <ContentPresenter HorizontalAlignment=\"Center\"\n                           VerticalAlignment=\"Center\" Name=\"content\"/>\n                        </Grid>\n                    </Border>\n                    <ControlTemplate.Triggers>\n                        <Trigger Property=\"IsMouseOver\" Value=\"True\">\n                            <Setter Property=\"Background\" Value=\"#44FFFFFF\" />\n                        </Trigger>\n                        <Trigger Property=\"IsPressed\" Value=\"True\">\n                        </Trigger>\n                        <Trigger Property=\"IsDefaulted\" Value=\"True\">\n                        </Trigger>\n                        <Trigger Property=\"IsFocused\" Value=\"True\">\n                        </Trigger>\n                    </ControlTemplate.Triggers>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n    <Style x:Key=\"MainTabs\" TargetType=\"TabItem\">\n        <Setter Property=\"Foreground\" Value=\"White\"/>\n        <Setter Property=\"FontSize\" Value=\"14\"/>\n        <Setter Property=\"HorizontalAlignment\" Value=\"Left\"/>\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"TabItem\">\n                    <Grid Name=\"Panel\">\n                        <local:RippleEffectDecorator ExpandTime=\"0.2\" FadeTime=\"0.2\">\n                            <ContentPresenter x:Name=\"ContentSite\"\n                                        VerticalAlignment=\"Center\"\n                                        HorizontalAlignment=\"Center\"\n                                        ContentSource=\"Header\"\n                                        Margin=\"10,2\"/>\n                        </local:RippleEffectDecorator>\n                    </Grid>\n                    <ControlTemplate.Triggers>\n                        <Trigger Property=\"IsSelected\" Value=\"True\">\n                            <Setter TargetName=\"Panel\" Property=\"Background\" Value=\"#33FFFFFF\" />\n                        </Trigger>\n                        <Trigger Property=\"IsSelected\" Value=\"False\">\n                            <Setter TargetName=\"Panel\" Property=\"Background\" Value=\"Transparent\" />\n                        </Trigger>\n                    </ControlTemplate.Triggers>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n    <Style x:Key=\"SubTabItems\" TargetType=\"TabItem\">\n        <Setter Property=\"Foreground\" Value=\"White\"/>\n        <Setter Property=\"FontSize\" Value=\"14\"/>\n        <Setter Property=\"HorizontalAlignment\" Value=\"Left\"/>\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"TabItem\">\n                    <Grid Name=\"Panel\" Height=\"30\">\n                        <local:RippleEffectDecorator ExpandTime=\"0.2\" FadeTime=\"0.2\">\n                            <ContentPresenter x:Name=\"ContentSite\"\n                                        VerticalAlignment=\"Center\"\n                                        HorizontalAlignment=\"Center\"\n                                        ContentSource=\"Header\"\n                                        Margin=\"10,2\"/>\n                        </local:RippleEffectDecorator>\n                    </Grid>\n                    <ControlTemplate.Triggers>\n                        <Trigger Property=\"IsSelected\" Value=\"True\">\n                            <Setter TargetName=\"Panel\" Property=\"Background\" Value=\"#33FFFFFF\" />\n                        </Trigger>\n                        <Trigger Property=\"IsSelected\" Value=\"False\">\n                            <Setter TargetName=\"Panel\" Property=\"Background\" Value=\"Transparent\" />\n                        </Trigger>\n                    </ControlTemplate.Triggers>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n    <Style x:Key=\"SubTabs\" TargetType=\"TabControl\">\n        <Setter Property=\"Background\" Value=\"Transparent\"/>\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type TabControl}\">\n                    <DockPanel LastChildFill=\"True\">\n                        <Border Name=\"titlebar\" Grid.Row=\"0\" DockPanel.Dock=\"Top\" Visibility=\"{Binding ChromeVisibility, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}\" Background=\"#11FFFFFF\">\n                            <DockPanel>\n                                <TabPanel\n                                    Grid.Row=\"0\"\n                                    Panel.ZIndex=\"1\"\n                                    Margin=\"0,0,0,0\"\n                                    IsItemsHost=\"True\"\n                                    Background=\"Transparent\" />\n                            </DockPanel>\n                        </Border>\n                        <Border Grid.Row=\"1\" CornerRadius=\"0, 12, 12, 12\" >\n                            <ContentPresenter ContentSource=\"SelectedContent\" />\n                        </Border>\n                    </DockPanel>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n    <Style TargetType=\"Button\">\n        <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n        <Setter Property=\"Focusable\" Value=\"False\"/>\n        <Setter Property=\"FontSize\" Value=\"14\"/>\n        <Setter Property=\"Foreground\" Value=\"White\"/>\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"ContentControl\">\n                    <Grid Background=\"{TemplateBinding Background}\" Width=\"{TemplateBinding Width}\">\n                        <Border x:Name=\"background\">\n                            <Border.Background>\n                                <SolidColorBrush x:Name=\"bgBrush\" Color=\"{StaticResource PrimaryColor}\"/>\n                            </Border.Background>\n                            <Border.Effect>\n                                <DropShadowEffect x:Name=\"dropShadow\" ShadowDepth=\"0\" Direction=\"0\" BlurRadius=\"5\"/>\n                            </Border.Effect>\n                        </Border>\n                        <local:RippleEffectDecorator ExpandTime=\"0.2\" FadeTime=\"0.2\">\n                            <Border Padding=\"{TemplateBinding Padding}\">\n                                <ContentPresenter HorizontalAlignment=\"Center\"\n                           VerticalAlignment=\"Center\" Name=\"content\"/>\n                            </Border>\n                        </local:RippleEffectDecorator>\n                    </Grid>\n                    <ControlTemplate.Triggers>\n                        <Trigger Value=\"True\" Property=\"IsMouseOver\">\n                            <Trigger.EnterActions>\n                                <BeginStoryboard>\n                                    <Storyboard>\n                                        <ColorAnimation Storyboard.TargetName=\"bgBrush\" Storyboard.TargetProperty=\"Color\" Duration=\"0:0:0.1\" To=\"#43A047\" />\n                                        <DoubleAnimation Storyboard.TargetName=\"dropShadow\" Storyboard.TargetProperty=\"BlurRadius\" Duration=\"0:0:0.1\" To=\"10\" />\n                                    </Storyboard>\n                                </BeginStoryboard>\n                            </Trigger.EnterActions>\n                            <Trigger.ExitActions>\n                                <BeginStoryboard>\n                                    <Storyboard>\n                                        <ColorAnimation Storyboard.TargetName=\"bgBrush\" Storyboard.TargetProperty=\"Color\" Duration=\"0:0:0.1\" To=\"{StaticResource PrimaryColor}\" />\n                                        <DoubleAnimation Storyboard.TargetName=\"dropShadow\" Storyboard.TargetProperty=\"BlurRadius\" Duration=\"0:0:0.1\" To=\"5\" />\n                                    </Storyboard>\n                                </BeginStoryboard>\n                            </Trigger.ExitActions>\n                        </Trigger>\n                        <Trigger Value=\"False\" Property=\"IsEnabled\">\n                            <Trigger.EnterActions>\n                                <BeginStoryboard>\n                                    <Storyboard>\n                                        <ColorAnimation Storyboard.TargetName=\"bgBrush\" Storyboard.TargetProperty=\"Color\" Duration=\"0:0:0.1\" To=\"#222222\" />\n                                        <DoubleAnimation Storyboard.TargetName=\"dropShadow\" Storyboard.TargetProperty=\"BlurRadius\" Duration=\"0:0:0.0\" To=\"3\" />\n                                    </Storyboard>\n                                </BeginStoryboard>\n                            </Trigger.EnterActions>\n                            <Trigger.ExitActions>\n                                <BeginStoryboard>\n                                    <Storyboard>\n                                        <ColorAnimation Storyboard.TargetName=\"bgBrush\" Storyboard.TargetProperty=\"Color\" Duration=\"0:0:0.1\" To=\"{StaticResource PrimaryColor}\" />\n                                        <DoubleAnimation Storyboard.TargetName=\"dropShadow\" Storyboard.TargetProperty=\"BlurRadius\" Duration=\"0:0:0.0\" To=\"5\" />\n                                    </Storyboard>\n                                </BeginStoryboard>\n                            </Trigger.ExitActions>\n                        </Trigger>\n                    </ControlTemplate.Triggers>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n    \n    <Style x:Key=\"ComboBoxToggleButton\" TargetType=\"{x:Type ToggleButton}\">\n        <Setter Property=\"OverridesDefaultStyle\" Value=\"true\"/>\n        <Setter Property=\"IsTabStop\" Value=\"false\"/>\n        <Setter Property=\"Focusable\" Value=\"false\"/>\n        <Setter Property=\"ClickMode\" Value=\"Press\"/>\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type ToggleButton}\">\n                    <Grid>\n                        <Border Background=\"Black\">\n                            <Border.Effect>\n                                <DropShadowEffect x:Name=\"dropShadow\" ShadowDepth=\"0\" BlurRadius=\"5\"/>\n                            </Border.Effect>\n                        </Border>\n                        <Border x:Name=\"templateRoot\" SnapsToDevicePixels=\"true\">\n                            <Border.Background>\n                                <SolidColorBrush x:Name=\"solidBrush\" Color=\"#222222\"/>\n                            </Border.Background>\n                            <Border x:Name=\"splitBorder\" BorderBrush=\"Transparent\" BorderThickness=\"1\" HorizontalAlignment=\"Right\" Margin=\"0\" SnapsToDevicePixels=\"true\" Width=\"{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}\">\n                                <Path x:Name=\"arrow\" Data=\"F1 M -2,0 L 2.667,2.66665 L 7.3334,0 L 7.3334,-1.78168 L 2.6667,0.88501 L-2,-1.78168 L-2,0 Z\" Fill=\"#2E7D32\" HorizontalAlignment=\"Center\" Margin=\"0\" VerticalAlignment=\"Center\"/>\n                            </Border>\n                        </Border>\n                    </Grid>\n                    <ControlTemplate.Triggers>\n                        <DataTrigger Value=\"True\">\n                            <DataTrigger.Binding>\n                                <MultiBinding>\n                                    <MultiBinding.Converter>\n                                        <local:OrValueConverter/>\n                                    </MultiBinding.Converter>\n                                    <Binding RelativeSource=\"{RelativeSource TemplatedParent}\" Path=\"IsMouseOver\" />\n                                </MultiBinding>\n                            </DataTrigger.Binding>\n                            <DataTrigger.EnterActions>\n                                <BeginStoryboard>\n                                    <Storyboard>\n                                        <ColorAnimation Storyboard.TargetName=\"solidBrush\" Storyboard.TargetProperty=\"Color\" Duration=\"0:0:0.1\" To=\"#292929\" />\n                                        <DoubleAnimation Storyboard.TargetName=\"dropShadow\" Storyboard.TargetProperty=\"BlurRadius\" Duration=\"0:0:0.1\" To=\"10\" />\n                                    </Storyboard>\n                                </BeginStoryboard>\n                            </DataTrigger.EnterActions>\n                            <DataTrigger.ExitActions>\n                                <BeginStoryboard>\n                                    <Storyboard>\n                                        <ColorAnimation Storyboard.TargetName=\"solidBrush\" Storyboard.TargetProperty=\"Color\" Duration=\"0:0:0.1\" To=\"#222222\" />\n                                        <DoubleAnimation Storyboard.TargetName=\"dropShadow\" Storyboard.TargetProperty=\"BlurRadius\" Duration=\"0:0:0.1\" To=\"5\" />\n                                    </Storyboard>\n                                </BeginStoryboard>\n                            </DataTrigger.ExitActions>\n                        </DataTrigger>\n                    </ControlTemplate.Triggers>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n    <Style TargetType=\"{x:Type ComboBox}\">\n        <Setter Property=\"FontSize\" Value=\"14\"/>\n        <Setter Property=\"Foreground\" Value=\"White\"/>\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type ComboBox}\">\n                    <Grid x:Name=\"templateRoot\" SnapsToDevicePixels=\"true\">\n                        <Grid.ColumnDefinitions>\n                            <ColumnDefinition Width=\"*\"/>\n                            <ColumnDefinition MinWidth=\"{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}\" Width=\"0\"/>\n                        </Grid.ColumnDefinitions>\n                        <Popup x:Name=\"PART_Popup\" AllowsTransparency=\"true\" Grid.ColumnSpan=\"2\" IsOpen=\"{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}\" Margin=\"1\" PopupAnimation=\"{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}\" Placement=\"Bottom\">\n                            <Grid MinWidth=\"{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=ActualWidth}\">\n                                <Border Background=\"Black\">\n                                    <Border.Effect>\n                                        <DropShadowEffect ShadowDepth=\"0\" BlurRadius=\"5\"/>\n                                    </Border.Effect>\n                                </Border>\n                                <Border x:Name=\"dropDownBorder\">\n                                    <ScrollViewer x:Name=\"DropDownScrollViewer\">\n                                        <Grid x:Name=\"grid\" RenderOptions.ClearTypeHint=\"Enabled\">\n                                            <Canvas x:Name=\"canvas\" HorizontalAlignment=\"Left\" Height=\"0\" VerticalAlignment=\"Top\" Width=\"0\">\n                                                <Rectangle x:Name=\"opaqueRect\" Fill=\"{Binding Background, ElementName=dropDownBorder}\" Height=\"{Binding ActualHeight, ElementName=dropDownBorder}\" Width=\"{Binding ActualWidth, ElementName=dropDownBorder}\"/>\n                                            </Canvas>\n                                            <ItemsPresenter x:Name=\"ItemsPresenter\" KeyboardNavigation.DirectionalNavigation=\"Contained\" SnapsToDevicePixels=\"{TemplateBinding SnapsToDevicePixels}\"/>\n                                        </Grid>\n                                    </ScrollViewer>\n                                </Border>\n                            </Grid>\n                        </Popup>\n                        <ToggleButton x:Name=\"toggleButton\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\" Background=\"{TemplateBinding Background}\" Grid.ColumnSpan=\"2\" IsChecked=\"{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}\" Style=\"{StaticResource ComboBoxToggleButton}\"/>\n                        <ContentPresenter x:Name=\"contentPresenter\" ContentTemplate=\"{TemplateBinding SelectionBoxItemTemplate}\" ContentTemplateSelector=\"{TemplateBinding ItemTemplateSelector}\" Content=\"{TemplateBinding SelectionBoxItem}\" ContentStringFormat=\"{TemplateBinding SelectionBoxItemStringFormat}\" HorizontalAlignment=\"{TemplateBinding HorizontalContentAlignment}\" IsHitTestVisible=\"false\" Margin=\"{TemplateBinding Padding}\" SnapsToDevicePixels=\"{TemplateBinding SnapsToDevicePixels}\" VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\"/>\n                    </Grid>\n                    <ControlTemplate.Triggers>\n                        <Trigger Property=\"HasItems\" Value=\"false\">\n                            <Setter Property=\"Height\" TargetName=\"dropDownBorder\" Value=\"95\"/>\n                        </Trigger>\n                        <MultiTrigger>\n                            <MultiTrigger.Conditions>\n                                <Condition Property=\"IsGrouping\" Value=\"true\"/>\n                                <Condition Property=\"VirtualizingPanel.IsVirtualizingWhenGrouping\" Value=\"false\"/>\n                            </MultiTrigger.Conditions>\n                            <Setter Property=\"ScrollViewer.CanContentScroll\" Value=\"false\"/>\n                        </MultiTrigger>\n                        <Trigger Property=\"ScrollViewer.CanContentScroll\" SourceName=\"DropDownScrollViewer\" Value=\"false\">\n                            <Setter Property=\"Canvas.Top\" TargetName=\"opaqueRect\" Value=\"{Binding VerticalOffset, ElementName=DropDownScrollViewer}\"/>\n                            <Setter Property=\"Canvas.Left\" TargetName=\"opaqueRect\" Value=\"{Binding HorizontalOffset, ElementName=DropDownScrollViewer}\"/>\n                        </Trigger>\n                    </ControlTemplate.Triggers>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n    <Style TargetType=\"{x:Type ComboBoxItem}\">\n        <Setter Property=\"Foreground\" Value=\"White\"/>\n        <Setter Property=\"FontSize\" Value=\"14\"/>\n        <Setter Property=\"Padding\" Value=\"5\"/>\n        <Setter Property=\"Background\" Value=\"#292929\"/>\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type ComboBoxItem}\">\n                    <Border Background=\"{TemplateBinding Background}\" Padding=\"{TemplateBinding Padding}\">\n                        <ContentPresenter/>\n                    </Border>\n                    <ControlTemplate.Triggers>\n                        <Trigger Property=\"IsMouseOver\" Value=\"True\">\n                            <Setter Property=\"Background\" Value=\"#444444\"/>\n                        </Trigger>\n                    </ControlTemplate.Triggers>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n    \n    <Style TargetType=\"{x:Type TextBox}\">\n        <Setter Property=\"Background\" Value=\"#444444\"/>\n        <Setter Property=\"Foreground\" Value=\"White\"/>\n        <Setter Property=\"VerticalContentAlignment\" Value=\"Center\"/>\n        <Setter Property=\"Padding\" Value=\"3\"/>\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"TextBox\">\n                    <Grid>\n                        <Border Background=\"Black\">\n                            <Border.Effect>\n                                <DropShadowEffect x:Name=\"dropShadow\" ShadowDepth=\"0\" BlurRadius=\"5\"/>\n                            </Border.Effect>\n                        </Border>\n                        <Border Name=\"Bd\" BorderBrush=\"{TemplateBinding BorderBrush}\" \n                                             Background=\"{TemplateBinding Background}\" \n                                             SnapsToDevicePixels=\"true\">\n                            <ScrollViewer Name=\"PART_ContentHost\" Background=\"{TemplateBinding Background}\" \n                                          SnapsToDevicePixels=\"{TemplateBinding SnapsToDevicePixels}\" />\n                        </Border>\n                    </Grid>\n                    <ControlTemplate.Triggers>\n                        <Trigger Property=\"IsEnabled\" Value=\"False\">\n                            <Setter Value=\"#333333\" Property=\"Background\" />\n                        </Trigger>\n                        <MultiTrigger>\n                            <MultiTrigger.Conditions>\n                                <Condition Property=\"IsEnabled\" Value=\"True\"/>\n                                <Condition Property=\"IsMouseOver\" Value=\"True\"/>\n                            </MultiTrigger.Conditions>\n                            <MultiTrigger.EnterActions>\n                                <BeginStoryboard>\n                                    <Storyboard>\n                                        <DoubleAnimation Storyboard.TargetName=\"dropShadow\" Storyboard.TargetProperty=\"BlurRadius\" Duration=\"0:0:0.1\" To=\"10\" />\n                                    </Storyboard>\n                                </BeginStoryboard>\n                            </MultiTrigger.EnterActions>\n                            <MultiTrigger.ExitActions>\n                                <BeginStoryboard>\n                                    <Storyboard>\n                                        <DoubleAnimation Storyboard.TargetName=\"dropShadow\" Storyboard.TargetProperty=\"BlurRadius\" Duration=\"0:0:0.1\" To=\"5\" />\n                                    </Storyboard>\n                                </BeginStoryboard>\n                            </MultiTrigger.ExitActions>\n                        </MultiTrigger>\n                    </ControlTemplate.Triggers>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n    <media:Style x:Key=\"FocusVisual\">\n        <media:Setter Property=\"media:Control.Template\">\n            <media:Setter.Value>\n                <media:ControlTemplate>\n                    <media:Rectangle Margin=\"2\" SnapsToDevicePixels=\"true\" Stroke=\"{media:DynamicResource {x:Static media:SystemColors.ControlTextBrushKey}}\" StrokeThickness=\"1\" StrokeDashArray=\"1 2\"/>\n                </media:ControlTemplate>\n            </media:Setter.Value>\n        </media:Setter>\n    </media:Style>\n</ResourceDictionary>"
  },
  {
    "path": "BMEngine/UI/NumberSelect.xaml",
    "content": "﻿<UserControl x:Class=\"ZenithEngine.UI.NumberSelect\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" \n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\" \n             xmlns:local=\"clr-namespace:ZenithEngine.UI\"\n             TextOptions.TextRenderingMode=\"Aliased\"\n             KeyDown=\"UserControl_KeyDown\"\n             mc:Ignorable=\"d\" >\n    <UserControl.Resources>\n        <ResourceDictionary>\n            <ResourceDictionary.MergedDictionaries>\n                <ResourceDictionary Source=\"Material.xaml\"/>\n            </ResourceDictionary.MergedDictionaries>\n        </ResourceDictionary>\n    </UserControl.Resources>\n    <Grid ClipToBounds=\"True\">\n        <Grid.ColumnDefinitions>\n            <ColumnDefinition Width=\"1*\"/>\n            <ColumnDefinition Width=\"25\"/>\n        </Grid.ColumnDefinitions>\n        <Grid Margin=\"0,0,3,0\">\n            <TextBox ClipToBounds=\"True\" x:Name=\"textBox\" Padding=\"0,1,0,0\" TextWrapping=\"Wrap\"\n                 Background=\"Transparent\"\n                 BorderThickness=\"0\"\n                 KeyDown=\"TextBox_KeyDown\"\n                     Foreground=\"White\"\n                     Text=\"0\"\n                     CaretBrush=\"White\"\n                     RenderTransformOrigin=\"0.5,0.5\" TextChanged=\"TextBox_TextChanged\" LostFocus=\"TextBox_LostFocus\" TextInput=\"TextBox_TextInput\" FontSize=\"16\" Margin=\"0,2,0,0\">\n                <TextBox.Style>\n                    <Style TargetType=\"TextBox\">\n                        <Setter Property=\"Background\" Value=\"#666666\"/>\n                        <Setter Property=\"Foreground\" Value=\"White\"/>\n                        <Setter Property=\"VerticalContentAlignment\" Value=\"Center\"/>\n                        <Setter Property=\"Padding\" Value=\"3\"/>\n                        <Setter Property=\"Template\">\n                            <Setter.Value>\n                                <ControlTemplate TargetType=\"TextBox\">\n                                    <Border Name=\"Bd\" BorderBrush=\"{TemplateBinding BorderBrush}\" \n                                             SnapsToDevicePixels=\"true\">\n                                        <ScrollViewer Name=\"PART_ContentHost\" \n                                          SnapsToDevicePixels=\"{TemplateBinding SnapsToDevicePixels}\" />\n                                    </Border>\n                                </ControlTemplate>\n                            </Setter.Value>\n                        </Setter>\n                    </Style>\n                </TextBox.Style>\n                <TextBox.RenderTransform>\n                    <TransformGroup>\n                        <ScaleTransform/>\n                        <SkewTransform AngleX=\"-0.843\"/>\n                        <RotateTransform/>\n                        <TranslateTransform X=\"-0.235\"/>\n                    </TransformGroup>\n                </TextBox.RenderTransform>\n            </TextBox>\n            <Rectangle Fill=\"{StaticResource PrimaryBrush}\" Height=\"1\" VerticalAlignment=\"Bottom\"/>\n        </Grid>\n        <Grid Grid.Column=\"1\" Width=\"25\" Height=\"25\" Grid.ColumnSpan=\"2\" Margin=\"0,0\">\n            <Grid.RowDefinitions>\n                <RowDefinition/>\n                <RowDefinition/>\n            </Grid.RowDefinitions>\n            <Button Name=\"downArrow\" FontSize=\"12\" Grid.Row=\"1\" Padding=\"0,0,0.3,0\" Background=\"#33FFFFFF\" Height=\"Auto\" Margin=\"0\" Click=\"Button_Click_1\">\n                <Button.Content>\n                    <Label Foreground=\"white\" Padding=\"0,0,0,0\" Margin=\"0,-0,0,0\" FontWeight=\"Bold\">﹀</Label>\n                </Button.Content>\n            </Button>\n            <Button Name=\"upArrow\" FontSize=\"12\" Padding=\"0,0,0.3,0\" Background=\"#33FFFFFF\" Height=\"Auto\" Margin=\"0\" Click=\"Button_Click\">\n                <Button.Content>\n                    <Label Foreground=\"white\" Padding=\"0,0,0,0\" Margin=\"0,-6,0,0\" FontWeight=\"Bold\">︿</Label>\n                </Button.Content>\n            </Button>\n        </Grid>\n    </Grid>\n</UserControl>\n"
  },
  {
    "path": "BMEngine/UI/NumberSelect.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\n\nnamespace ZenithEngine.UI\n{\n    /// <summary>\n    /// Interaction logic for NumberSelect.xaml\n    /// </summary>\n    public partial class NumberSelect : UserControl\n    {\n        public decimal Value\n        { get => (decimal)GetValue(ValueProperty); set => SetValue(ValueProperty, value); }\n        public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(\"Value\", typeof(decimal), typeof(NumberSelect), new PropertyMetadata((decimal)0, new PropertyChangedCallback(OnPropertyChange)));\n        public int DecimalPoints\n        { get => (int)GetValue(DecimalPointsProperty); set => SetValue(DecimalPointsProperty, value); }\n        public static readonly DependencyProperty DecimalPointsProperty = DependencyProperty.Register(\"DecimalPoints\", typeof(int), typeof(NumberSelect), new PropertyMetadata((int)0, new PropertyChangedCallback(OnPropertyChange)));\n        public decimal Minimum\n        { get => (decimal)GetValue(MinimumProperty); set => SetValue(MinimumProperty, value); }\n        public static readonly DependencyProperty MinimumProperty = DependencyProperty.Register(\"Minimum\", typeof(decimal), typeof(NumberSelect), new PropertyMetadata((decimal)0, new PropertyChangedCallback(OnPropertyChange)));\n        public decimal Maximum\n        { get => (decimal)GetValue(MaximumProperty); set => SetValue(MaximumProperty, value); }\n        public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register(\"Maximum\", typeof(decimal), typeof(NumberSelect), new PropertyMetadata((decimal)1000, new PropertyChangedCallback(OnPropertyChange)));\n        public decimal Step\n        { get => (decimal)GetValue(StepProperty); set => SetValue(StepProperty, value); }\n        public static readonly DependencyProperty StepProperty = DependencyProperty.Register(\"Step\", typeof(decimal), typeof(NumberSelect), new PropertyMetadata((decimal)1));\n\n        public static readonly RoutedEvent ValueChangedEvent = EventManager.RegisterRoutedEvent(\n            \"ValueChanged\", RoutingStrategy.Bubble,\n            typeof(RoutedPropertyChangedEventHandler<decimal>), typeof(NumberSelect));\n\n        public event RoutedPropertyChangedEventHandler<decimal> ValueChanged\n        {\n            add { AddHandler(ValueChangedEvent, value); }\n            remove { RemoveHandler(ValueChangedEvent, value); }\n        }\n\n        private static void OnPropertyChange(DependencyObject sender, DependencyPropertyChangedEventArgs e)\n        {\n            ((NumberSelect)sender).UpdateValue();\n        }\n\n        public bool TextFocused => textBox.IsFocused;\n\n        string prevText = \"\";\n\n        public NumberSelect()\n        {\n            InitializeComponent();\n\n            prevText = Value.ToString();\n            textBox.Text = prevText;\n\n            upArrow.SetBinding(IsEnabledProperty, new BBinding(IsEnabledProperty, this));\n            downArrow.SetBinding(IsEnabledProperty, new BBinding(IsEnabledProperty, this));\n            textBox.SetBinding(IsEnabledProperty, new BBinding(IsEnabledProperty, this));\n        }\n\n        bool ignoreChange = false;\n        void UpdateValue()\n        {\n            if (!ignoreChange)\n            {\n                try\n                {\n                    decimal old = Value;\n                    decimal d = Decimal.Round(old, DecimalPoints);\n                    if (d < Minimum) d = Minimum;\n                    if (d > Maximum) d = Maximum;\n                    if (d != old)\n                    {\n                        Value = d;\n                    }\n                    try\n                    {\n                        RaiseEvent(new RoutedPropertyChangedEventArgs<decimal>(old, d, ValueChangedEvent));\n                    }\n                    catch { }\n                }\n                catch { }\n                textBox.Text = Value.ToString();\n            }\n            ignoreChange = false;\n        }\n\n        private void TextBox_TextChanged(object sender, TextChangedEventArgs e)\n        {\n            try\n            {\n                decimal _d = Convert.ToDecimal(textBox.Text);\n                decimal d = Decimal.Round(_d, DecimalPoints);\n                if (d < Minimum) d = Minimum;\n                if (d > Maximum) d = Maximum;\n                else\n                {\n                    var old = Value;\n                    if (d != old)\n                    {\n                        ignoreChange = true;\n                        Value = d;\n                        try\n                        {\n                            RaiseEvent(new RoutedPropertyChangedEventArgs<decimal>(old, d, ValueChangedEvent));\n                        }\n                        catch { }\n                    }\n                }\n            }\n            catch\n            {\n                if(textBox.Text != \"\")\n                    textBox.Text = prevText;\n            }\n            prevText = textBox.Text;\n        }\n\n        private void TextBox_LostFocus(object sender, RoutedEventArgs e)\n        {\n            CheckAndSave();\n        }\n\n        void CheckAndSave()\n        {\n            try\n            {\n                decimal _d = Convert.ToDecimal(textBox.Text);\n                decimal d = Decimal.Round(_d, DecimalPoints);\n                if (d < Minimum) d = Minimum;\n                if (d > Maximum) d = Maximum;\n                var old = Value;\n                if (d != old)\n                {\n                    Value = d;\n                    try\n                    {\n                        RaiseEvent(new RoutedPropertyChangedEventArgs<decimal>(old, d, ValueChangedEvent));\n                    }\n                    catch { }\n                }\n            }\n            catch { }\n            textBox.Text = Value.ToString();\n        }\n\n        private void TextBox_TextInput(object sender, TextCompositionEventArgs e)\n        {\n\n        }\n\n        private void Button_Click(object sender, RoutedEventArgs e)\n        {\n            var d = Value + Step;\n            if (d < Minimum) d = Minimum;\n            if (d > Maximum) d = Maximum;\n            var old = Value;\n            Value = d;\n            textBox.Text = Value.ToString();\n            if (old != d)\n                RaiseEvent(new RoutedPropertyChangedEventArgs<decimal>(old, d, ValueChangedEvent));\n        }\n\n        private void Button_Click_1(object sender, RoutedEventArgs e)\n        {\n            var d = Value - Step;\n            if (d < Minimum) d = Minimum;\n            if (d > Maximum) d = Maximum;\n            var old = Value;\n            Value = d;\n            textBox.Text = Value.ToString();\n            if (old != d)\n                RaiseEvent(new RoutedPropertyChangedEventArgs<decimal>(old, d, ValueChangedEvent));\n        }\n\n        private void UserControl_KeyDown(object sender, KeyEventArgs e)\n        {\n            if(e.Key == Key.Enter)\n            {\n                CheckAndSave();\n                e.Handled = true;\n                Keyboard.ClearFocus();\n\n                FrameworkElement parent = (FrameworkElement)textBox.Parent;\n                while (parent != null && parent is IInputElement && !((IInputElement)parent).Focusable)\n                {\n                    parent = (FrameworkElement)parent.Parent;\n                }\n\n                DependencyObject scope = FocusManager.GetFocusScope(textBox);\n                FocusManager.SetFocusedElement(scope, parent as IInputElement);\n            }\n        }\n\n        private void TextBox_KeyDown(object sender, KeyEventArgs e)\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "BMEngine/UI/RippleEffectDecorator.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Animation;\nusing System.Windows.Shapes;\n\nnamespace ZenithEngine.UI\n{\n    public class RippleEffectDecorator : ContentControl\n    {\n        new public double Opacity\n        {\n            get { return (double)GetValue(OpacityProperty); }\n            set { SetValue(OpacityProperty, value); }\n        }\n\n        // Using a DependencyProperty as the backing store for Opacity.  This enables animation, styling, binding, etc...\n        new public static readonly DependencyProperty OpacityProperty =\n            DependencyProperty.Register(\"Opacity\", typeof(double), typeof(RippleEffectDecorator), new PropertyMetadata(0.4));\n\n        public double ExpandTime\n        {\n            get { return (double)GetValue(ExpandTimeProperty); }\n            set { SetValue(ExpandTimeProperty, value); }\n        }\n\n        // Using a DependencyProperty as the backing store for ExpandTime.  This enables animation, styling, binding, etc...\n        public static readonly DependencyProperty ExpandTimeProperty =\n            DependencyProperty.Register(\"ExpandTime\", typeof(double), typeof(RippleEffectDecorator), new PropertyMetadata(0.4));\n\n        public double FadeTime\n        {\n            get { return (double)GetValue(FadeTimeProperty); }\n            set { SetValue(FadeTimeProperty, value); }\n        }\n\n        // Using a DependencyProperty as the backing store for FadeTime.  This enables animation, styling, binding, etc...\n        public static readonly DependencyProperty FadeTimeProperty =\n            DependencyProperty.Register(\"FadeTime\", typeof(double), typeof(RippleEffectDecorator), new PropertyMetadata(0.3));\n\n        public RippleEffectDecorator()\n        {\n        }\n\n        public override void OnApplyTemplate()\n        {\n            base.OnApplyTemplate();\n\n            var parentGrid = new Grid();\n            var grid = new Grid();\n            var content = new ContentControl();\n            grid.Background = Brushes.Transparent;\n            grid.ClipToBounds = true;\n            parentGrid.Children.Add(grid);\n            parentGrid.Children.Add(content);\n\n            var c = Content;\n            this.Content = parentGrid;\n            content.Content = c;\n\n            grid.SetBinding(WidthProperty, new Binding(\"ActualWidth\") { Source = parentGrid });\n            grid.SetBinding(HeightProperty, new Binding(\"ActualHeight\") { Source = parentGrid });\n\n            parentGrid.PreviewMouseDown += (sender, e) =>\n            {\n                var targetWidth = (Math.Max(ActualWidth, ActualHeight) * 2) / ExpandTime * (ExpandTime + FadeTime);\n                var mousePosition = (e as MouseButtonEventArgs).GetPosition(this);\n                var startMargin = new Thickness(mousePosition.X, mousePosition.Y, 0, 0);\n                var endMargin = new Thickness(mousePosition.X - targetWidth / 2, mousePosition.Y - targetWidth / 2, 0, 0);\n\n                var ellipse = new Ellipse()\n                {\n                    Fill = Brushes.White,\n                    HorizontalAlignment = HorizontalAlignment.Left,\n                    VerticalAlignment = VerticalAlignment.Top,\n                    Opacity = Opacity\n                };\n                ellipse.Margin = startMargin;\n                ellipse.SetBinding(HeightProperty, new Binding(\"Width\") { Source = ellipse });\n\n                Storyboard storyboard = new Storyboard();\n\n                var expand = new DoubleAnimation(0, targetWidth, new Duration(TimeSpan.FromSeconds(ExpandTime + FadeTime)));\n                storyboard.Children.Add(expand);\n                Storyboard.SetTarget(expand, ellipse);\n                Storyboard.SetTargetProperty(expand, new PropertyPath(WidthProperty));\n\n                var marginShrink = new ThicknessAnimation(startMargin, endMargin, new Duration(TimeSpan.FromSeconds(ExpandTime + FadeTime)));\n                storyboard.Children.Add(marginShrink);\n                Storyboard.SetTarget(marginShrink, ellipse);\n                Storyboard.SetTargetProperty(marginShrink, new PropertyPath(MarginProperty));\n\n                var opacity = new DoubleAnimation(Opacity, 0, new Duration(TimeSpan.FromSeconds(FadeTime)));\n                opacity.BeginTime = TimeSpan.FromSeconds(ExpandTime);\n                storyboard.Children.Add(opacity);\n                Storyboard.SetTarget(opacity, ellipse);\n                Storyboard.SetTargetProperty(opacity, new PropertyPath(Ellipse.OpacityProperty));\n\n                grid.Children.Add(ellipse);\n\n                storyboard.Begin();\n\n                var waitTime = ExpandTime + FadeTime;\n                Task.Run(() =>\n                {\n                    Thread.Sleep(TimeSpan.FromSeconds(waitTime));\n                    Dispatcher.Invoke(() =>\n                    {\n                        grid.Children.Remove(ellipse);\n                    });\n                });\n                e.Handled = false;\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "BMEngine/UI/Scrollbar.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:local=\"clr-namespace:ZenithEngine.UI\">\n    <Style TargetType=\"{x:Type ScrollBar}\">\n        <Style.Resources>\n            <Style x:Key=\"PageScrollButton\" TargetType=\"{x:Type RepeatButton}\">\n                <Setter Property=\"Template\">\n                    <Setter.Value>\n                        <ControlTemplate TargetType=\"{x:Type RepeatButton}\">\n                            <Rectangle Fill=\"Transparent\"/>\n                        </ControlTemplate>\n                    </Setter.Value>\n                </Setter>\n            </Style>\n            <Style x:Key=\"VerticalThumbStyle\" TargetType=\"{x:Type Thumb}\">\n                <Setter Property=\"Template\">\n                    <Setter.Value>\n                        <ControlTemplate TargetType=\"{x:Type Thumb}\">\n                            <Border Background=\"Transparent\">\n                                <Rectangle x:Name=\"Slider\" Fill=\"#000000\" Width=\"4\" HorizontalAlignment=\"Right\" />\n                            </Border>\n                            <ControlTemplate.Triggers>\n                                <Trigger Property=\"IsMouseOver\" Value=\"True\">\n                                    <Trigger.EnterActions>\n                                        <BeginStoryboard>\n                                            <Storyboard>\n                                                <DoubleAnimation To=\"12\" DecelerationRatio=\"1\" Storyboard.TargetName=\"Slider\" Storyboard.TargetProperty=\"Width\" Duration=\"0:0:0.10\" />\n                                            </Storyboard>\n                                        </BeginStoryboard>\n                                    </Trigger.EnterActions>\n                                    <Trigger.ExitActions>\n                                        <BeginStoryboard>\n                                            <Storyboard>\n                                                <DoubleAnimation To=\"12\" DecelerationRatio=\"1\" Storyboard.TargetName=\"Slider\" Storyboard.TargetProperty=\"Width\" Duration=\"0:0:0.10\" />\n                                                <DoubleAnimation To=\"4\" AccelerationRatio=\"1\" Storyboard.TargetName=\"Slider\" Storyboard.TargetProperty=\"Width\" BeginTime=\"0:0:0.15\" Duration=\"0:0:0.25\" />\n                                            </Storyboard>\n                                        </BeginStoryboard>\n                                    </Trigger.ExitActions>\n                                </Trigger>\n                            </ControlTemplate.Triggers>\n                        </ControlTemplate>\n                    </Setter.Value>\n                </Setter>\n            </Style>\n            <Style x:Key=\"HorizontalThumbStyle\" TargetType=\"{x:Type Thumb}\">\n                <Setter Property=\"Template\">\n                    <Setter.Value>\n                        <ControlTemplate TargetType=\"{x:Type Thumb}\">\n                            <Border Background=\"Transparent\">\n                                <Rectangle x:Name=\"Slider\" Fill=\"#000000\" Height=\"4\" VerticalAlignment=\"Bottom\" />\n                            </Border>\n                            <ControlTemplate.Triggers>\n                                <Trigger Property=\"IsMouseOver\" Value=\"True\">\n                                    <Trigger.EnterActions>\n                                        <BeginStoryboard>\n                                            <Storyboard>\n                                                <DoubleAnimation To=\"12\" DecelerationRatio=\"1\" Storyboard.TargetName=\"Slider\" Storyboard.TargetProperty=\"Height\" Duration=\"0:0:0.1\" />\n                                            </Storyboard>\n                                        </BeginStoryboard>\n                                    </Trigger.EnterActions>\n                                    <Trigger.ExitActions>\n                                        <BeginStoryboard>\n                                            <Storyboard>\n                                                <DoubleAnimation To=\"4\" DecelerationRatio=\"1\" Storyboard.TargetName=\"Slider\" Storyboard.TargetProperty=\"Height\" Duration=\"0:0:0.25\" />\n                                            </Storyboard>\n                                        </BeginStoryboard>\n                                    </Trigger.ExitActions>\n                                </Trigger>\n                            </ControlTemplate.Triggers>\n                        </ControlTemplate>\n                    </Setter.Value>\n                </Setter>\n            </Style>\n        </Style.Resources>\n        <Setter Property=\"Background\" Value=\"Transparent\"/>\n        <Setter Property=\"Width\" Value=\"0\"/>\n        <Setter Property=\"Margin\" Value=\"-17,0,0,0\"/>\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type ScrollBar}\">\n                    <Track x:Name=\"PART_Track\" IsDirectionReversed=\"true\" IsEnabled=\"{TemplateBinding IsMouseOver}\">\n                        <Track.DecreaseRepeatButton>\n                            <RepeatButton Command=\"{x:Static ScrollBar.PageUpCommand}\" Style=\"{StaticResource PageScrollButton}\"/>\n                        </Track.DecreaseRepeatButton>\n                        <Track.IncreaseRepeatButton>\n                            <RepeatButton Command=\"{x:Static ScrollBar.PageDownCommand}\" Style=\"{StaticResource PageScrollButton}\"/>\n                        </Track.IncreaseRepeatButton>\n                        <Track.Thumb>\n                            <Thumb Style=\"{StaticResource VerticalThumbStyle}\" />\n                        </Track.Thumb>\n                    </Track>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n        <Style.Triggers>\n            <Trigger Property=\"Orientation\" Value=\"Horizontal\">\n                <Setter Property=\"Margin\" Value=\"0,-17,0,0\"/>\n                <Setter Property=\"Width\" Value=\"Auto\"/>\n                <Setter Property=\"Height\" Value=\"12\"/>\n                <Setter Property=\"Template\">\n                    <Setter.Value>\n                        <ControlTemplate TargetType=\"{x:Type ScrollBar}\">\n                            <Track x:Name=\"PART_Track\" IsEnabled=\"{TemplateBinding IsMouseOver}\">\n                                <Track.DecreaseRepeatButton>\n                                    <RepeatButton Command=\"{x:Static ScrollBar.PageLeftCommand}\" Style=\"{StaticResource PageScrollButton}\"/>\n                                </Track.DecreaseRepeatButton>\n                                <Track.IncreaseRepeatButton>\n                                    <RepeatButton Command=\"{x:Static ScrollBar.PageRightCommand}\" Style=\"{StaticResource PageScrollButton}\"/>\n                                </Track.IncreaseRepeatButton>\n                                <Track.Thumb>\n                                    <Thumb Style=\"{StaticResource HorizontalThumbStyle}\" />\n                                </Track.Thumb>\n                            </Track>\n                        </ControlTemplate>\n                    </Setter.Value>\n                </Setter>\n            </Trigger>\n        </Style.Triggers>\n    </Style>\n</ResourceDictionary>"
  },
  {
    "path": "BMEngine/UI/ValueSlider.xaml",
    "content": "﻿<UserControl x:Class=\"ZenithEngine.UI.ValueSlider\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" \n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\" \n             xmlns:local=\"clr-namespace:ZenithEngine.UI\"\n             mc:Ignorable=\"d\">\n    <DockPanel LastChildFill=\"True\">\n        <local:NumberSelect ValueChanged=\"Updown_ValueChanged\" DockPanel.Dock=\"Right\" MinWidth=\"80\" x:Name=\"updown\" Value=\"0.2\" Maximum=\"1\" Minimum=\"0\" Margin=\"5,0,0,0\" HorizontalAlignment=\"Left\" />\n        <local:BetterSlider x:Name=\"slider\" VerticalAlignment=\"Bottom\" UserValueChanged=\"Slider_ValueChanged\" Margin=\"0,0,0,3\"></local:BetterSlider>\n    </DockPanel>\n</UserControl>\n"
  },
  {
    "path": "BMEngine/UI/ValueSlider.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\n\nnamespace ZenithEngine.UI\n{\n    /// <summary>\n    /// Interaction logic for ValueSlider.xaml\n    /// </summary>\n    public partial class ValueSlider : UserControl\n    {\n        public double Minimum\n        {\n            get { return (double)GetValue(MinimumProperty); }\n            set { SetValue(MinimumProperty, value); }\n        }\n\n        public static readonly DependencyProperty MinimumProperty =\n            DependencyProperty.Register(\"Minimum\", typeof(double), typeof(ValueSlider), new PropertyMetadata(0.0));\n\n\n        public double Maximum\n        {\n            get { return (double)GetValue(MaximumProperty); }\n            set { SetValue(MaximumProperty, value); }\n        }\n\n        public static readonly DependencyProperty MaximumProperty =\n            DependencyProperty.Register(\"Maximum\", typeof(double), typeof(ValueSlider), new PropertyMetadata(1.0));\n\n\n        public double Value\n        {\n            get { return (double)GetValue(ValueProperty); }\n            set { SetValue(ValueProperty, value); }\n        }\n\n        public static readonly DependencyProperty ValueProperty =\n            DependencyProperty.Register(\"Value\", typeof(double), typeof(ValueSlider), new PropertyMetadata(0.0, (s, e) => (s as ValueSlider).OnValueChange(e)));\n\n\n        public int DecimalPoints\n        {\n            get { return (int)GetValue(DecimalPointsProperty); }\n            set { SetValue(DecimalPointsProperty, value); }\n        }\n\n        public static readonly DependencyProperty DecimalPointsProperty =\n            DependencyProperty.Register(\"DecimalPoints\", typeof(int), typeof(ValueSlider), new PropertyMetadata(0));\n\n\n        public decimal TrueMin\n        {\n            get { return (decimal)GetValue(TrueMinProperty); }\n            set { SetValue(TrueMinProperty, value); }\n        }\n\n        public static readonly DependencyProperty TrueMinProperty =\n            DependencyProperty.Register(\"TrueMin\", typeof(decimal), typeof(ValueSlider), new PropertyMetadata((decimal)0.0));\n\n\n        public decimal TrueMax\n        {\n            get { return (decimal)GetValue(TrueMaxProperty); }\n            set { SetValue(TrueMaxProperty, value); }\n        }\n\n        public static readonly DependencyProperty TrueMaxProperty =\n            DependencyProperty.Register(\"TrueMax\", typeof(decimal), typeof(ValueSlider), new PropertyMetadata((decimal)1.0d));\n\n        public decimal Step\n        { get => (decimal)GetValue(StepProperty); set => SetValue(StepProperty, value); }\n        public static readonly DependencyProperty StepProperty = DependencyProperty.Register(\"Step\", typeof(decimal), typeof(ValueSlider), new PropertyMetadata((decimal)1));\n\n\n        public static readonly RoutedEvent ValueChangedEvent = EventManager.RegisterRoutedEvent(\n                \"ValueChanged\", RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler<double>), typeof(ValueSlider));\n\n        public event RoutedPropertyChangedEventHandler<double> ValueChanged\n        {\n            add { AddHandler(ValueChangedEvent, value); }\n            remove { RemoveHandler(ValueChangedEvent, value); }\n        }\n\n        void OnValueChange(DependencyPropertyChangedEventArgs e)\n        {\n            if (!ignoreslider) slider.Value = nudToSlider(Value);\n            if (!ignorevalue) updown.Value = (decimal)Value;\n            ignoreslider = false;\n            ignorevalue = false;\n            RaiseEvent(new RoutedPropertyChangedEventArgs<double>((double)e.OldValue, (double)e.NewValue, ValueChangedEvent));\n        }\n\n        public Func<double, double> sliderToNud = v => v;\n        public Func<double, double> nudToSlider = v => v;\n\n        public ValueSlider()\n        {\n            InitializeComponent();\n\n            FocusVisualStyle = null;\n\n            slider.SetBinding(BetterSlider.MaximumProperty, new Binding(\"Maximum\") { Source = this });\n            slider.SetBinding(BetterSlider.MinimumProperty, new Binding(\"Minimum\") { Source = this });\n            updown.SetBinding(NumberSelect.MinimumProperty, new Binding(\"TrueMin\") { Source = this });\n            updown.SetBinding(NumberSelect.MaximumProperty, new Binding(\"TrueMax\") { Source = this });\n            updown.SetBinding(NumberSelect.DecimalPointsProperty, new Binding(\"DecimalPoints\") { Source = this });\n            updown.SetBinding(NumberSelect.StepProperty, new Binding(\"Step\") { Source = this });\n        }\n\n        bool ignoreslider = false;\n        bool ignorevalue = false;\n\n        private void Slider_ValueChanged(object sender, double e)\n        {\n            if (IsInitialized)\n            {\n                ignoreslider = true;\n                Value = sliderToNud(slider.Value);\n            }\n        }\n\n        private void Updown_ValueChanged(object sender, RoutedPropertyChangedEventArgs<decimal> e)\n        {\n            if (IsInitialized)\n            {\n                if (!ignorevalue)\n                {\n                    ignorevalue = true;\n                    Value = (double)updown.Value;\n                }\n                ignorevalue = false;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "BMEngine/ZenithEngine.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProjectGuid>{60F2212A-D56C-4776-836F-6A49453CDBC4}</ProjectGuid>\n    <OutputType>Library</OutputType>\n    <AppDesignerFolder>Properties</AppDesignerFolder>\n    <RootNamespace>BMEngine</RootNamespace>\n    <AssemblyName>ZenithEngine</AssemblyName>\n    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>\n    <FileAlignment>512</FileAlignment>\n    <Deterministic>true</Deterministic>\n    <TargetFrameworkProfile />\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Debug|x86'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>bin\\x86\\Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <DebugType>full</DebugType>\n    <PlatformTarget>x86</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Release|x86'\">\n    <OutputPath>bin\\x86\\Release\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <Optimize>true</Optimize>\n    <DebugType>pdbonly</DebugType>\n    <PlatformTarget>x86</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>bin\\Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <DebugType>full</DebugType>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Release|AnyCPU'\">\n    <OutputPath>bin\\Release\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <Optimize>true</Optimize>\n    <DebugType>pdbonly</DebugType>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Debug|x64'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>bin\\x64\\Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <DebugType>full</DebugType>\n    <PlatformTarget>x64</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Release|x64'\">\n    <OutputPath>bin\\x64\\Release\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <Optimize>true</Optimize>\n    <DebugType>pdbonly</DebugType>\n    <PlatformTarget>x64</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"OpenTK, Version=3.1.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\OpenTK.3.1.0\\lib\\net20\\OpenTK.dll</HintPath>\n    </Reference>\n    <Reference Include=\"PresentationCore\" />\n    <Reference Include=\"PresentationFramework\" />\n    <Reference Include=\"PresentationFramework.Aero2\" />\n    <Reference Include=\"SharpCompress, Version=0.24.0.0, Culture=neutral, PublicKeyToken=afb0a02973931d96, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\SharpCompress.0.24.0\\lib\\net45\\SharpCompress.dll</HintPath>\n    </Reference>\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Core\" />\n    <Reference Include=\"System.Drawing\" />\n    <Reference Include=\"System.Xaml\" />\n    <Reference Include=\"System.Xml.Linq\" />\n    <Reference Include=\"System.Data.DataSetExtensions\" />\n    <Reference Include=\"Microsoft.CSharp\" />\n    <Reference Include=\"System.Data\" />\n    <Reference Include=\"System.Net.Http\" />\n    <Reference Include=\"System.Xml\" />\n    <Reference Include=\"WindowsBase\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"BufferByteReader.cs\" />\n    <Compile Include=\"FastList.cs\" />\n    <Compile Include=\"GLTextEngine.cs\" />\n    <Compile Include=\"GLUtils.cs\" />\n    <Compile Include=\"ScriptedCompile\\ExtraUI.cs\" />\n    <Compile Include=\"ScriptedCompile\\IO.cs\" />\n    <Compile Include=\"IPluginRender.cs\" />\n    <Compile Include=\"MidiFile.cs\" />\n    <Compile Include=\"MidiInfo.cs\" />\n    <Compile Include=\"MidiTrack.cs\" />\n    <Compile Include=\"NoteColorPalettePick.xaml.cs\">\n      <DependentUpon>NoteColorPalettePick.xaml</DependentUpon>\n    </Compile>\n    <Compile Include=\"PluginUtils.cs\" />\n    <Compile Include=\"Properties\\AssemblyInfo.cs\" />\n    <Compile Include=\"RenderSettings.cs\" />\n    <Compile Include=\"UI\\BBinding.cs\" />\n    <Compile Include=\"UI\\BetterCheckbox.xaml.cs\">\n      <DependentUpon>BetterCheckbox.xaml</DependentUpon>\n    </Compile>\n    <Compile Include=\"UI\\BetterRadio.xaml.cs\">\n      <DependentUpon>BetterRadio.xaml</DependentUpon>\n    </Compile>\n    <Compile Include=\"UI\\BetterSlider.xaml.cs\">\n      <DependentUpon>BetterSlider.xaml</DependentUpon>\n    </Compile>\n    <Compile Include=\"UI\\Converters.cs\" />\n    <Compile Include=\"UI\\HexColorPicker.xaml.cs\">\n      <DependentUpon>HexColorPicker.xaml</DependentUpon>\n    </Compile>\n    <Compile Include=\"UI\\InplaceConverter.cs\" />\n    <Compile Include=\"UI\\NumberSelect.xaml.cs\">\n      <DependentUpon>NumberSelect.xaml</DependentUpon>\n    </Compile>\n    <Compile Include=\"UI\\RippleEffectDecorator.cs\" />\n    <Compile Include=\"UI\\ValueSlider.xaml.cs\">\n      <DependentUpon>ValueSlider.xaml</DependentUpon>\n    </Compile>\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"OpenTK.dll.config\" />\n    <None Include=\"packages.config\" />\n    <Page Include=\"UI\\BetterCheckbox.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </Page>\n    <Page Include=\"UI\\BetterRadio.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </Page>\n    <Page Include=\"UI\\BetterSlider.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </Page>\n    <Page Include=\"UI\\Colors.xaml\">\n      <SubType>Designer</SubType>\n      <Generator>MSBuild:Compile</Generator>\n    </Page>\n    <Page Include=\"UI\\HexColorPicker.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </Page>\n    <Page Include=\"UI\\ListBox.xaml\">\n      <SubType>Designer</SubType>\n      <Generator>MSBuild:Compile</Generator>\n    </Page>\n    <Page Include=\"UI\\Material.xaml\">\n      <SubType>Designer</SubType>\n      <Generator>MSBuild:Compile</Generator>\n    </Page>\n    <Page Include=\"UI\\NumberSelect.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </Page>\n    <Page Include=\"UI\\Scrollbar.xaml\">\n      <SubType>Designer</SubType>\n      <Generator>MSBuild:Compile</Generator>\n    </Page>\n    <Page Include=\"UI\\ValueSlider.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </Page>\n  </ItemGroup>\n  <ItemGroup>\n    <Page Include=\"NoteColorPalettePick.xaml\">\n      <SubType>Designer</SubType>\n      <Generator>MSBuild:Compile</Generator>\n    </Page>\n  </ItemGroup>\n  <ItemGroup />\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n  <PropertyGroup>\n    <PostBuildEvent>\n    </PostBuildEvent>\n  </PropertyGroup>\n</Project>"
  },
  {
    "path": "BMEngine/packages.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n  <package id=\"OpenTK\" version=\"3.1.0\" targetFramework=\"net461\" />\n  <package id=\"SharpCompress\" version=\"0.24.0\" targetFramework=\"net461\" />\n</packages>"
  },
  {
    "path": "Black-Midi-Render/App.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n  <startup>\n    <supportedRuntime version=\"v4.0\" sku=\".NETFramework,Version=v4.6.1\" />\n  </startup>\n  <runtime>\n    <assemblyBinding xmlns=\"urn:schemas-microsoft-com:asm.v1\">\n      <probing privatePath=\"lib;libs;debug\" />\n      <loadFromRemoteSources enabled=\"true\" />\n      <dependentAssembly>\n        <assemblyIdentity name=\"OpenTK\" publicKeyToken=\"bad199fe84eb3df4\" culture=\"neutral\" />\n        <bindingRedirect oldVersion=\"0.0.0.0-3.0.1.0\" newVersion=\"3.0.1.0\" />\n      </dependentAssembly>\n    </assemblyBinding>\n  </runtime>\n</configuration>"
  },
  {
    "path": "Black-Midi-Render/GLPostbuffer.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing ZenithEngine;\nusing OpenTK.Graphics.OpenGL;\n\nnamespace Zenith_MIDI\n{\n    class GLPostbuffer: IDisposable\n    {\n        public int BufferID { get; private set; }\n        public int TextureID { get; private set; }\n\n        public GLPostbuffer(int width, int height)\n        {\n            int b, t;\n            GLUtils.GenFrameBufferTexture(width, height, out b, out t);\n            BufferID = b;\n            TextureID = t;\n        }\n\n        public void BindBuffer()\n        {\n            GL.BindFramebuffer(FramebufferTarget.Framebuffer, BufferID);\n        }\n\n        public void BindTexture()\n        {\n            GL.BindTexture(TextureTarget.Texture2D, TextureID);\n        }\n\n        public static void UnbindBuffers()\n        {\n            GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);\n        }\n\n        public static void UnbindTextures()\n        {\n            GL.BindTexture(TextureTarget.Texture2D, 0);\n        }\n\n        public void Dispose()\n        {\n            GL.DeleteFramebuffer(BufferID);\n            GL.DeleteTexture(TextureID);\n        }\n    }\n}\n"
  },
  {
    "path": "Black-Midi-Render/GLUtils.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing OpenTK.Graphics.OpenGL;\n\nnamespace Zenith_MIDI\n{\n    static class GLUtils\n    {\n        public static int MakeShaderProgram(string name)\n        {\n            int _vertexObj = GL.CreateShader(ShaderType.VertexShader);\n            int _fragObj = GL.CreateShader(ShaderType.FragmentShader);\n            int statusCode;\n            string info;\n\n            GL.ShaderSource(_vertexObj, File.ReadAllText(\"Shaders\\\\\" + name + \".vert\"));\n            GL.CompileShader(_vertexObj);\n            info = GL.GetShaderInfoLog(_vertexObj);\n            GL.GetShader(_vertexObj, ShaderParameter.CompileStatus, out statusCode);\n            if (statusCode != 1) throw new ApplicationException(info);\n\n            GL.ShaderSource(_fragObj, File.ReadAllText(\"Shaders\\\\\" + name + \".frag\"));\n            GL.CompileShader(_fragObj);\n            info = GL.GetShaderInfoLog(_fragObj);\n            GL.GetShader(_fragObj, ShaderParameter.CompileStatus, out statusCode);\n            if (statusCode != 1) throw new ApplicationException(info);\n\n            int shader = GL.CreateProgram();\n            GL.AttachShader(shader, _fragObj);\n            GL.AttachShader(shader, _vertexObj);\n            GL.LinkProgram(shader);\n            return shader;\n        }\n\n        public static int MakePostShaderProgram(string name)\n        {\n            int _vertexObj = GL.CreateShader(ShaderType.VertexShader);\n            int _fragObj = GL.CreateShader(ShaderType.FragmentShader);\n            int statusCode;\n            string info;\n\n            GL.ShaderSource(_vertexObj, File.ReadAllText(\"Shaders\\\\Post\\\\post.vert\"));\n            GL.CompileShader(_vertexObj);\n            info = GL.GetShaderInfoLog(_vertexObj);\n            GL.GetShader(_vertexObj, ShaderParameter.CompileStatus, out statusCode);\n            if (statusCode != 1) throw new ApplicationException(info);\n\n            GL.ShaderSource(_fragObj, File.ReadAllText(\"Shaders\\\\Post\\\\\" + name + \".frag\"));\n            GL.CompileShader(_fragObj);\n            info = GL.GetShaderInfoLog(_fragObj);\n            GL.GetShader(_fragObj, ShaderParameter.CompileStatus, out statusCode);\n            if (statusCode != 1) throw new ApplicationException(info);\n\n            int shader = GL.CreateProgram();\n            GL.AttachShader(shader, _fragObj);\n            GL.AttachShader(shader, _vertexObj);\n            GL.LinkProgram(shader);\n            return shader;\n        }\n\n        public static void GenFrameBufferTexture(int width, int height, out int fbuffer, out int rtexture)\n        {\n            fbuffer = GL.GenFramebuffer();\n            GL.BindFramebuffer(FramebufferTarget.Framebuffer, fbuffer);\n            rtexture = GL.GenTexture();\n            GL.BindTexture(TextureTarget.Texture2D, rtexture);\n            GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, width, height, 0, PixelFormat.Rgba, PixelType.Byte, (IntPtr)0);\n            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);\n            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);\n            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge);\n            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge);\n            GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, rtexture, 0);\n            if (GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer) != FramebufferErrorCode.FramebufferComplete) throw new Exception();\n        }\n    }\n}\n"
  },
  {
    "path": "Black-Midi-Render/KDMAPI.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nstatic class  KDMAPI\n{\n    public struct MIDIHDR\n    {\n        string lpdata;\n        uint dwBufferLength;\n        uint dwBytesRecorded;\n        IntPtr dwUser;\n        uint dwFlags;\n        IntPtr lpNext;\n        IntPtr reserved;\n        uint dwOffset;\n        IntPtr dwReserved;\n    }\n\n    public enum OMSettingMode\n    {\n        OM_SET = 0x0,\n        OM_GET = 0x1\n    }\n\n    public enum OMSetting\n    {\n        OM_CAPFRAMERATE = 0x10000,\n        OM_DEBUGMMODE = 0x10001,\n        OM_DISABLEFADEOUT = 0x10002,\n        OM_DONTMISSNOTES = 0x10003,\n\n        OM_ENABLESFX = 0x10004,\n        OM_FULLVELOCITY = 0x10005,\n        OM_IGNOREVELOCITYRANGE = 0x10006,\n        OM_IGNOREALLEVENTS = 0x10007,\n        OM_IGNORESYSEX = 0x10008,\n        OM_IGNORESYSRESET = 0x10009,\n        OM_LIMITRANGETO88 = 0x10010,\n        OM_MT32MODE = 0x10011,\n        OM_MONORENDERING = 0x10012,\n        OM_NOTEOFF1 = 0x10013,\n        OM_EVENTPROCWITHAUDIO = 0x10014,\n        OM_SINCINTER = 0x10015,\n        OM_SLEEPSTATES = 0x10016,\n\n        OM_AUDIOBITDEPTH = 0x10017,\n        OM_AUDIOFREQ = 0x10018,\n        OM_CURRENTENGINE = 0x10019,\n        OM_BUFFERLENGTH = 0x10020,\n        OM_MAXRENDERINGTIME = 0x10021,\n        OM_MINIGNOREVELRANGE = 0x10022,\n        OM_MAXIGNOREVELRANGE = 0x10023,\n        OM_OUTPUTVOLUME = 0x10024,\n        OM_TRANSPOSE = 0x10025,\n        OM_MAXVOICES = 0x10026,\n        OM_SINCINTERCONV = 0x10027,\n\n        OM_OVERRIDENOTELENGTH = 0x10028,\n        OM_NOTELENGTH = 0x10029,\n        OM_ENABLEDELAYNOTEOFF = 0x10030,\n        OM_DELAYNOTEOFFVAL = 0x10031\n    }\n\n    public struct DebugInfo\n    {\n        float RenderingTime;\n        int[] ActiveVoices;\n\n        double ASIOInputLatency;\n        double ASIOOutputLatency;\n    }\n    \n    [DllImport(\"OmniMIDI\\\\OmniMIDI\")]\n    public static extern bool ReturnKDMAPIVer(out Int32 Major, out Int32 Minor, out Int32 Build, out Int32 Revision);\n\n    [DllImport(\"OmniMIDI\\\\OmniMIDI\")]\n    public static extern bool IsKDMAPIAvailable();\n\n    [DllImport(\"OmniMIDI\\\\OmniMIDI\")]\n    public static extern int InitializeKDMAPIStream();\n\n    [DllImport(\"OmniMIDI\\\\OmniMIDI\")]\n    public static extern int TerminateKDMAPIStream();\n\n    [DllImport(\"OmniMIDI\\\\OmniMIDI\")]\n    public static extern void ResetKDMAPIStream();\n\n    [DllImport(\"OmniMIDI\\\\OmniMIDI\")]\n    public static extern uint SendCustomEvent(uint eventtype, uint chan, uint param);\n\n    [DllImport(\"OmniMIDI\\\\OmniMIDI\")]\n    public static extern uint SendDirectData(uint dwMsg);\n\n    [DllImport(\"OmniMIDI\\\\OmniMIDI\")]\n    public static extern uint SendDirectDataNoBuf(uint dwMsg);\n\n    [DllImport(\"OmniMIDI\\\\OmniMIDI\")]\n    public static extern uint SendDirectLongData(ref MIDIHDR IIMidiHdr);\n\n    [DllImport(\"OmniMIDI\\\\OmniMIDI\")]\n    public static extern uint SendDirectLongDataNoBuf(ref MIDIHDR IIMidiHdr);\n\n    [DllImport(\"OmniMIDI\\\\OmniMIDI\")]\n    public static extern uint PrepareLongData(ref MIDIHDR IIMidiHdr);\n\n    [DllImport(\"OmniMIDI\\\\OmniMIDI\")]\n    public static extern uint UnprepareLongData(ref MIDIHDR IIMidiHdr);\n\n    [DllImport(\"OmniMIDI\\\\OmniMIDI\")]\n    public static extern bool DriverSettings(OMSetting Setting, OMSettingMode Mode, IntPtr Value, Int32 cbValue);\n\n    [DllImport(\"OmniMIDI\\\\OmniMIDI\")]\n    public static extern void LoadCustomSoundFontsList(ref String Directory);\n\n    [DllImport(\"OmniMIDI\\\\OmniMIDI\")]\n    public static extern DebugInfo GetDriverDebugInfo();\n}"
  },
  {
    "path": "Black-Midi-Render/Languages/en/window.xaml",
    "content": "﻿<ResourceDictionary\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" \n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n    xmlns:local=\"using:MSDNSample\"\n    xmlns:system=\"clr-namespace:System;assembly=mscorlib\">\n\n    <system:String x:Key=\"LanguageName\">English</system:String>\n    \n    <!--Tab Names-->\n    <system:String x:Key=\"generalTab\">General</system:String>\n    <system:String x:Key=\"modulesTab\">Modules</system:String>\n    <system:String x:Key=\"moduleSettingsTab\">Module Settings</system:String>\n    <system:String x:Key=\"renderTab\">Render</system:String>\n\n    <!--Auto Update-->\n    <system:String x:Key=\"updateDownloading\">Downloading update...</system:String>\n    <system:String x:Key=\"updateReady\">Update installed!</system:String>\n    <system:String x:Key=\"clickToRestart\">Click here to restart Zenith</system:String>\n\n    <!--Main Page-->\n    <system:String x:Key=\"load\">Load</system:String>\n    <system:String x:Key=\"unload\">Unload</system:String>\n    <system:String x:Key=\"width\">Width</system:String>\n    <system:String x:Key=\"height\">Height</system:String>\n    <system:String x:Key=\"browse\">Browse</system:String>\n    <system:String x:Key=\"fps\">FPS</system:String>\n    <system:String x:Key=\"presets\">Presets</system:String>\n    <system:String x:Key=\"zenithRenderWarning\">Warning: Zenith Preview renders in the resolution above, then downscales to the window size</system:String>\n    <system:String x:Key=\"superRes\">SSAA</system:String>\n    <system:String x:Key=\"superResWarning\">Increasing SSAA can greatly increase the smoothness of the pixels but takes much more performance</system:String>\n    <system:String x:Key=\"language\">Language</system:String>\n    <system:String x:Key=\"renderResolution\">Render Resolution</system:String>\n    <system:String x:Key=\"previewOptions\">Preview Options</system:String>\n    <system:String x:Key=\"renderOptions\">Render Options</system:String>\n    \n    <!--Plugins Page-->\n    <system:String x:Key=\"reload\">Reload</system:String>\n    <system:String x:Key=\"startPreview\">Start Preview</system:String>\n    <system:String x:Key=\"stop\">Stop</system:String>\n\n    <!--Preview Page-->\n    <system:String x:Key=\"vsync\">V-Sync Enabled</system:String>\n    <system:String x:Key=\"tempo\">Tempo Multiplier</system:String>\n    <system:String x:Key=\"pauseHint\">Right click slider or press space to toggle pause</system:String>\n    <system:String x:Key=\"paused\">Preview Paused</system:String>\n\n    <!--Render Page-->\n    <system:String x:Key=\"savePath\">Save Path</system:String>\n    <system:String x:Key=\"includeAudio\">Include Audio</system:String>\n    <system:String x:Key=\"renderMask\">Render Transparency Mask</system:String>\n    <system:String x:Key=\"bitrate\">Bitrate</system:String>\n    <system:String x:Key=\"crfHint\">FFMPEG crf: 0 = lossless, 51 = worst quality possible</system:String>\n    <system:String x:Key=\"delatStart\">Delay start (seconds)</system:String>\n    <system:String x:Key=\"ffmpegDebug\">ffmpeg debug</system:String>\n    <system:String x:Key=\"startRender\">Start Render</system:String>\n    <system:String x:Key=\"ifUploading\">If uploading the video online, please credit the program</system:String>\n    <system:String x:Key=\"Custom\">Customize FFmpeg Options (for experts, at your own risk)</system:String>\n    \n    <!--NEW 1.5-->\n    <system:String x:Key=\"realtimePlayback\">Realtime Playback</system:String>\n    <system:String x:Key=\"disableKDMAPI\">Disable KDMAPI</system:String>\n    <system:String x:Key=\"enableKDMAPI\">Enable KDMAPI</system:String>\n    <system:String x:Key=\"noteSize\">Note Size</system:String>\n    <system:String x:Key=\"tickBased\">Tick Based</system:String>\n    <system:String x:Key=\"timeBased\">Time Based</system:String>\n    <system:String x:Key=\"ignoreColorEvents\">Ignore Color Events</system:String>\n    <system:String x:Key=\"useBackgroundImage\">Use Background</system:String>\n\n    <!--NEW 1.6-->\n    <system:String x:Key=\"pauseHintNew\">Press space to toggle pause, press enter on the preview window to fullscreen</system:String>\n\n    <!--Palette picker-->\n    <system:String x:Key=\"palettes_reload\">Reload</system:String>\n    <system:String x:Key=\"palettes_randomise\">Randomise color order</system:String>\n    <system:String x:Key=\"palettes_openFolder\">Open Palettes Folder</system:String>\n</ResourceDictionary>"
  },
  {
    "path": "Black-Midi-Render/MainWindow.xaml",
    "content": "﻿<Window x:Class=\"Zenith_MIDI.MainWindow\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:xctk=\"http://schemas.xceed.com/wpf/xaml/toolkit\"\n        xmlns:sys=\"clr-namespace:System;assembly=mscorlib\"\n        xmlns:local=\"clr-namespace:Zenith_MIDI\"\n        xmlns:ui=\"clr-namespace:ZenithEngine.UI;assembly=ZenithEngine\"\n        mc:Ignorable=\"d\"\n        TextOptions.TextFormattingMode=\"Ideal\" \n        TextOptions.TextRenderingMode=\"Auto\" WindowStyle=\"None\"\n        Title=\"Zenith\" Height=\"600\" Width=\"900\" MinWidth=\"780\" MinHeight=\"550\" Closing=\"Window_Closing\"\n        Background=\"#171717\" Name=\"mainWindow\">\n    <WindowChrome.WindowChrome>\n        <WindowChrome CaptionHeight=\"40\"/>\n    </WindowChrome.WindowChrome>\n    <Window.Resources>\n        <ResourceDictionary>\n            <ResourceDictionary.MergedDictionaries>\n                <ResourceDictionary x:Name=\"localization\">\n                    <ResourceDictionary.MergedDictionaries>\n                        <ResourceDictionary Source=\"pack://siteoforigin:,,,/Languages/en/window.xaml\" />\n                    </ResourceDictionary.MergedDictionaries>\n                </ResourceDictionary>\n                <ResourceDictionary Source=\"pack://application:,,,/ZenithEngine;component/UI/Material.xaml\"/>\n                <ResourceDictionary>\n                    <local:AndValueConverter x:Key=\"AndValueConverter\"/>\n                    <local:OrValueConverter x:Key=\"OrValueConverter\"/>\n                    <local:NotValueConverter x:Key=\"NotValueConverter\"/>\n\n                    <sys:Boolean x:Key=\"staticControlsEnabled\" >True</sys:Boolean>\n\n                    <sys:Boolean x:Key=\"previewStartEnabled\" >False</sys:Boolean>\n                    <sys:Boolean x:Key=\"previewStopEnabled\" >False</sys:Boolean>\n                    <sys:Boolean x:Key=\"unloadEnabled\" >False</sys:Boolean>\n                    <sys:Boolean x:Key=\"notPreviewing\" >True</sys:Boolean>\n                    <sys:Boolean x:Key=\"notRendering\" >True</sys:Boolean>\n                    <sys:Boolean x:Key=\"midiLoaded\" >False</sys:Boolean>\n                </ResourceDictionary>\n            </ResourceDictionary.MergedDictionaries>\n        </ResourceDictionary>\n    </Window.Resources>\n    <DockPanel>\n        <Grid>\n            <Grid Name=\"midiLoaded\" IsEnabled=\"{DynamicResource midiLoaded}\"></Grid>\n            <Grid Name=\"midiNotLoaded\">\n                <Grid.IsEnabled>\n                    <MultiBinding Converter=\"{StaticResource NotValueConverter}\">\n                        <Binding ElementName=\"midiLoaded\" Path=\"IsEnabled\"/>\n                    </MultiBinding>\n                </Grid.IsEnabled>\n            </Grid>\n            <Grid Name=\"notRendering\" IsEnabled=\"{DynamicResource notRendering}\"></Grid>\n            <Grid Name=\"notPreviewing\" IsEnabled=\"{DynamicResource notPreviewing}\"></Grid>\n            <Grid Name=\"notPreviewingOrRendering\">\n                <Grid.IsEnabled>\n                    <MultiBinding Converter=\"{StaticResource AndValueConverter}\">\n                        <Binding ElementName=\"notPreviewing\" Path=\"IsEnabled\"/>\n                        <Binding ElementName=\"notRendering\" Path=\"IsEnabled\"/>\n                    </MultiBinding>\n                </Grid.IsEnabled>\n            </Grid>\n            <Grid Name=\"previewStartEnabled\">\n                <Grid.IsEnabled>\n                    <MultiBinding Converter=\"{StaticResource AndValueConverter}\">\n                        <Binding ElementName=\"notPreviewing\" Path=\"IsEnabled\"/>\n                        <Binding ElementName=\"notRendering\" Path=\"IsEnabled\"/>\n                        <Binding ElementName=\"midiLoaded\" Path=\"IsEnabled\"/>\n                    </MultiBinding>\n                </Grid.IsEnabled>\n            </Grid>\n            <Grid Name=\"isRendering\">\n                <Grid.IsEnabled>\n                    <MultiBinding Converter=\"{StaticResource NotValueConverter}\">\n                        <Binding ElementName=\"notRendering\" Path=\"IsEnabled\"/>\n                    </MultiBinding>\n                </Grid.IsEnabled>\n            </Grid>\n            <Grid Name=\"isPreviewing\">\n                <Grid.IsEnabled>\n                    <MultiBinding Converter=\"{StaticResource NotValueConverter}\">\n                        <Binding ElementName=\"notPreviewing\" Path=\"IsEnabled\"/>\n                    </MultiBinding>\n                </Grid.IsEnabled>\n            </Grid>\n            <Grid Name=\"stopEnabled\">\n                <Grid.IsEnabled>\n                    <MultiBinding Converter=\"{StaticResource OrValueConverter}\">\n                        <Binding ElementName=\"isRendering\" Path=\"IsEnabled\"/>\n                        <Binding ElementName=\"isPreviewing\" Path=\"IsEnabled\"/>\n                    </MultiBinding>\n                </Grid.IsEnabled>\n            </Grid>\n            <local:CustomTabs x:Name=\"windowTabs\" Background=\"Transparent\" BorderThickness=\"0\" Margin=\"0,0,0,0\">\n                <local:CustomTabs.Style>\n                    <Style TargetType=\"{x:Type local:CustomTabs}\">\n                        <Setter Property=\"Template\">\n                            <Setter.Value>\n                                <ControlTemplate TargetType=\"{x:Type local:CustomTabs}\">\n                                    <Grid>\n                                        <Grid.RowDefinitions>\n                                            <RowDefinition Height=\"50\"/>\n                                            <RowDefinition Height=\"*\"/>\n                                        </Grid.RowDefinitions>\n                                        <Border Name=\"titlebar\" Grid.Row=\"0\" DockPanel.Dock=\"Top\" Height=\"50\" Visibility=\"{Binding ChromeVisibility, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}\" Background=\"#11FFFFFF\">\n                                            <DockPanel>\n                                                <Image Source=\"pack://application:,,,/Zenith;component/icon.png\" RenderOptions.BitmapScalingMode=\"HighQuality\" Width=\"30\" Height=\"30\" Margin=\"10\"/>\n                                                <Label VerticalAlignment=\"Center\" FontSize=\"20\" Margin=\"-5,0,10,0\" Content=\"{Binding RelativeSource={RelativeSource TemplatedParent}, Path=VersionName}\"/>\n                                                <TabPanel\n                                                     Grid.Row=\"0\"\n                                                     Panel.ZIndex=\"1\"\n                                                     Margin=\"0,0,0,0\"\n                                                     IsItemsHost=\"True\"\n                                                     Background=\"Transparent\" />\n                                                <StackPanel MouseDown=\"updateDownloaded_MouseDown\" Margin=\"20,0,0,0\" VerticalAlignment=\"Center\" Cursor=\"Arrow\" WindowChrome.IsHitTestVisibleInChrome=\"True\">\n                                                    <StackPanel.Style>\n                                                        <Style TargetType=\"StackPanel\">\n                                                            <Setter Property=\"Visibility\" Value=\"Collapsed\"/>\n                                                            <Style.Triggers>\n                                                                <DataTrigger Binding=\"{Binding RelativeSource={RelativeSource TemplatedParent}, Path=UpdaterProgress}\" Value=\"Downloaded\">\n                                                                    <Setter Property=\"Visibility\" Value=\"Visible\"/>\n                                                                </DataTrigger>\n                                                            </Style.Triggers>\n                                                        </Style>\n                                                    </StackPanel.Style>\n                                                    <TextBlock Foreground=\"White\" FontSize=\"14\" Text=\"{DynamicResource updateReady}\"/>\n                                                    <TextBlock Foreground=\"White\" FontSize=\"14\" Text=\"{DynamicResource clickToRestart}\"/>\n                                                </StackPanel>\n                                                <StackPanel Margin=\"20,0,0,0\" VerticalAlignment=\"Center\">\n                                                    <StackPanel.Style>\n                                                        <Style TargetType=\"StackPanel\">\n                                                            <Setter Property=\"Visibility\" Value=\"Collapsed\"/>\n                                                            <Style.Triggers>\n                                                                <DataTrigger Binding=\"{Binding RelativeSource={RelativeSource TemplatedParent}, Path=UpdaterProgress}\" Value=\"Downloading\">\n                                                                    <Setter Property=\"Visibility\" Value=\"Visible\"/>\n                                                                </DataTrigger>\n                                                            </Style.Triggers>\n                                                        </Style>\n                                                    </StackPanel.Style>\n                                                    <TextBlock Foreground=\"White\" FontSize=\"20\" Text=\"{DynamicResource updateDownloading}\"/>\n                                                </StackPanel>\n                                                <DockPanel DockPanel.Dock=\"Right\" VerticalAlignment=\"Stretch\" HorizontalAlignment=\"Right\" Margin=\"0,0,10,0\" Grid.RowSpan=\"2\" Width=\"52\" Panel.ZIndex=\"100\">\n                                                    <Button Style=\"{StaticResource WindowButton}\" Focusable=\"False\" Name=\"ExitButton\" Background=\"Red\" Width=\"20\" Height=\"20\" Margin=\"3\" DockPanel.Dock=\"Right\" WindowChrome.IsHitTestVisibleInChrome=\"True\" Click=\"ExitButton_Click\"></Button>\n                                                    <Button Style=\"{StaticResource WindowButton}\" Focusable=\"False\" Name=\"MinimiseButton\" Background=\"Orange\" Width=\"20\" Height=\"20\" Margin=\"3\" DockPanel.Dock=\"Right\" WindowChrome.IsHitTestVisibleInChrome=\"True\" Click=\"MinimiseButton_Click\"></Button>\n                                                </DockPanel>\n                                            </DockPanel>\n                                        </Border>\n                                        <Border\n                                             Grid.Row=\"1\"\n                                             CornerRadius=\"0, 12, 12, 12\" >\n                                            <ContentPresenter ContentSource=\"SelectedContent\" />\n                                        </Border>\n                                    </Grid>\n                                </ControlTemplate>\n                            </Setter.Value>\n                        </Setter>\n                    </Style>\n                </local:CustomTabs.Style>\n                <TabControl.Resources>\n                    <Style TargetType=\"TabItem\" BasedOn=\"{StaticResource MainTabs}\">\n                        <Setter Property=\"Height\" Value=\"50\"/>\n                        <Setter Property=\"WindowChrome.IsHitTestVisibleInChrome\" Value=\"True\"/>\n                    </Style>\n                </TabControl.Resources>\n                <TabItem Header=\"{DynamicResource generalTab}\" IsEnabled=\"{DynamicResource notRendering}\">\n                    <Grid>\n                        <StackPanel>\n                            <Grid Height=\"30\" Margin=\"10\">\n                                <Grid.ColumnDefinitions>\n                                    <ColumnDefinition/>\n                                    <ColumnDefinition/>\n                                </Grid.ColumnDefinitions>\n                                <Button Margin=\"0,0,3,0\" Content=\"{DynamicResource load}\" Padding=\"30,0,30,0\" x:Name=\"browseMidiButton\" Click=\"LoadButton_Click\" DockPanel.Dock=\"Left\">\n                                    <Button.IsEnabled>\n                                        <MultiBinding Converter=\"{StaticResource AndValueConverter}\">\n                                            <Binding ElementName=\"notPreviewingOrRendering\" Path=\"IsEnabled\"/>\n                                            <Binding ElementName=\"midiNotLoaded\" Path=\"IsEnabled\"/>\n                                        </MultiBinding>\n                                    </Button.IsEnabled>\n                                </Button>\n                                <Button Margin=\"3,0,0,0\" Grid.Column=\"1\" x:Name=\"unloadButton\" Content=\"{DynamicResource unload}\" Padding=\"20,0,20,0\" Click=\"UnloadButton_Click\" DockPanel.Dock=\"Left\">\n                                    <Button.IsEnabled>\n                                        <MultiBinding Converter=\"{StaticResource AndValueConverter}\">\n                                            <Binding ElementName=\"notPreviewingOrRendering\" Path=\"IsEnabled\"/>\n                                            <Binding ElementName=\"midiLoaded\" Path=\"IsEnabled\"/>\n                                        </MultiBinding>\n                                    </Button.IsEnabled>\n                                </Button>\n                            </Grid>\n                            <StackPanel Margin=\"10\">\n                                <Grid>\n                                    <Grid.ColumnDefinitions>\n                                        <ColumnDefinition/>\n                                        <ColumnDefinition/>\n                                    </Grid.ColumnDefinitions>\n                                    <Border Padding=\"0\">\n                                        <StackPanel>\n                                            <Label FontSize=\"16\" HorizontalAlignment=\"Center\" Content=\"{DynamicResource renderResolution}\"/>\n                                            <Grid>\n                                                <Grid.ColumnDefinitions>\n                                                    <ColumnDefinition/>\n                                                    <ColumnDefinition/>\n                                                </Grid.ColumnDefinitions>\n                                                <StackPanel>\n                                                    <DockPanel LastChildFill=\"False\" Margin=\"0,0,0,5\" HorizontalAlignment=\"Left\">\n                                                        <Label Content=\"{DynamicResource width}\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Top\" DockPanel.Dock=\"Left\"/>\n                                                        <ui:NumberSelect x:Name=\"viewWidth\" Maximum=\"15360\" Minimum=\"1\" Width=\"118\" IsEnabled=\"{Binding IsEnabled, ElementName=notPreviewingOrRendering}\"  DockPanel.Dock=\"Left\" Margin=\"10,0,0,0\"  />\n                                                    </DockPanel>\n                                                    <DockPanel LastChildFill=\"False\" HorizontalAlignment=\"Left\">\n                                                        <Label Content=\"{DynamicResource height}\" HorizontalAlignment=\"Left\" DockPanel.Dock=\"Left\" Margin=\"0,0,0,0\"/>\n                                                        <ui:NumberSelect x:Name=\"viewHeight\" Maximum=\"8640\" Minimum=\"1\" Width=\"118\" IsEnabled=\"{Binding IsEnabled, ElementName=notPreviewingOrRendering}\"  DockPanel.Dock=\"Left\" Margin=\"5,0,0,0\"  />\n                                                    </DockPanel>\n                                                </StackPanel>\n                                                <StackPanel Grid.Column=\"1\">\n                                                    <DockPanel LastChildFill=\"False\" Margin=\"0,0,0,5\" HorizontalAlignment=\"Left\">\n                                                        <Label Content=\"{DynamicResource presets}\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Top\"/>\n                                                        <ComboBox IsEnabled=\"{Binding ElementName=notPreviewingOrRendering, Path=IsEnabled}\" x:Name=\"resolutionPreset\" Margin=\"10,0,0,0\" Width=\"110\" SelectionChanged=\"ResolutionPreset_SelectionChanged\">\n                                                            <ComboBoxItem Content=\"720p\"/>\n                                                            <ComboBoxItem Content=\"1080p\"/>\n                                                            <ComboBoxItem Content=\"1440p\"/>\n                                                            <ComboBoxItem Content=\"4k\"/>\n                                                            <ComboBoxItem Content=\"5k\"/>\n                                                            <ComboBoxItem Content=\"8k\"/>\n                                                            <ComboBoxItem Content=\"16k\"/>\n                                                        </ComboBox>\n                                                    </DockPanel>\n                                                    <DockPanel DockPanel.Dock=\"Top\" HorizontalAlignment=\"Left\" LastChildFill=\"False\" Margin=\"0,5\" VerticalAlignment=\"Top\">\n                                                        <Label Content=\"{DynamicResource superRes}\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Top\" DockPanel.Dock=\"Left\"/>\n                                                        <ui:NumberSelect DockPanel.Dock=\"Left\" x:Name=\"SSAAFactor\" Value=\"1\" Maximum=\"4\" Minimum=\"1\" HorizontalAlignment=\"Left\" Width=\"118\" IsEnabled=\"{Binding IsEnabled, ElementName=notPreviewingOrRendering}\" Margin=\"20,0,0,0\"  />\n                                                    </DockPanel>\n                                                </StackPanel>\n                                            </Grid>\n                                        </StackPanel>\n                                    </Border>\n                                    <Border Padding=\"0\" Grid.Column=\"1\">\n                                        <StackPanel>\n                                            <Label FontSize=\"16\" HorizontalAlignment=\"Center\" Content=\"{DynamicResource renderOptions}\"/>\n                                            <DockPanel HorizontalAlignment=\"Center\" Margin=\"0,0,0,10\">\n                                                <DockPanel LastChildFill=\"False\" VerticalAlignment=\"Top\" HorizontalAlignment=\"Left\">\n                                                    <Label Content=\"{DynamicResource fps}\" HorizontalAlignment=\"Left\" RenderTransformOrigin=\"1.078,0.59\" DockPanel.Dock=\"Left\" Margin=\"0,0,0,0\"/>\n                                                    <ui:NumberSelect x:Name=\"viewFps\" Maximum=\"1000\" Minimum=\"1\" Width=\"77\" IsEnabled=\"{Binding IsEnabled, ElementName=notPreviewingOrRendering}\"  DockPanel.Dock=\"Left\" Margin=\"5,0,0,0\"  />\n                                                </DockPanel>\n                                                <DockPanel HorizontalAlignment=\"Center\" LastChildFill=\"False\" Margin=\"20,0,0,0\" VerticalAlignment=\"Top\" >\n                                                    <Label Content=\"{DynamicResource noteSize}\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Top\" DockPanel.Dock=\"Left\"/>\n                                                    <ComboBox x:Name=\"noteSizeStyle\" IsEnabled=\"{Binding ElementName=notPreviewingOrRendering, Path=IsEnabled}\" DockPanel.Dock=\"Left\" Padding=\"7,4,10,4\" Margin=\"5,0,0,0\" SelectedIndex=\"0\" SelectionChanged=\"NoteSizeStyle_SelectionChanged\">\n                                                        <ComboBoxItem Content=\"{DynamicResource tickBased}\"/>\n                                                        <ComboBoxItem Content=\"{DynamicResource timeBased}\"/>\n                                                    </ComboBox>\n                                                </DockPanel>\n                                            </DockPanel>\n                                            <ui:BetterCheckbox Height=\"26\" x:Name=\"ignoreColorEvents\" Text=\"{DynamicResource ignoreColorEvents}\" CheckToggled=\"IgnoreColorEvents_Checked\" HorizontalAlignment=\"Center\"/>\n                                        </StackPanel>\n                                    </Border>\n                                </Grid>\n                                <DockPanel LastChildFill=\"False\" VerticalAlignment=\"Top\">\n                                    <TextBlock DockPanel.Dock=\"Top\" TextWrapping=\"Wrap\" Text=\"{DynamicResource zenithRenderWarning}\" VerticalAlignment=\"Top\" Margin=\"5,5,0,5\" HorizontalAlignment=\"Left\"/>\n                                    <TextBlock DockPanel.Dock=\"Top\" TextWrapping=\"Wrap\" Text=\"{DynamicResource superResWarning}\" VerticalAlignment=\"Top\" Margin=\"5,5,0,5\" HorizontalAlignment=\"Left\"/>\n                                </DockPanel>\n                                <DockPanel LastChildFill=\"True\" Height=\"26\" Margin=\"0,10,0,20\" VerticalAlignment=\"Top\">\n                                    <ui:BetterCheckbox x:Name=\"useBGImage\" Text=\"{DynamicResource useBackgroundImage}\" HorizontalAlignment=\"Left\" DockPanel.Dock=\"Left\" CheckToggled=\"UseBGImage_Checked\" Margin=\"0,4,0,0\"/>\n                                    <Button x:Name=\"browseBG\" IsEnabled=\"{Binding ElementName=useBGImage, Path=IsChecked}\" Content=\"{DynamicResource browse}\" Padding=\"15,1,15,1\" HorizontalAlignment=\"Left\" DockPanel.Dock=\"Left\" Margin=\"5,0,0,0\" Click=\"BrowseBG_Click\"/>\n                                    <TextBox x:Name=\"bgImagePath\" Text=\"\" DockPanel.Dock=\"Right\" TextWrapping=\"Wrap\" VerticalAlignment=\"Stretch\" Margin=\"5,0,0,0\" IsEnabled=\"False\"/>\n                                </DockPanel>\n                                <Label FontSize=\"16\" HorizontalAlignment=\"Center\" Content=\"{DynamicResource previewOptions}\"/>\n                                <DockPanel HorizontalAlignment=\"Center\" Height=\"30\" LastChildFill=\"False\" Margin=\"0,0,0,10\" VerticalAlignment=\"Top\">\n                                    <Label Content=\"{DynamicResource tempo}\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Top\" DockPanel.Dock=\"Left\"/>\n                                    <ui:ValueSlider ValueChanged=\"tempoMultSlider_ValueChanged\" Name=\"tempoMultSlider\" Width=\"396\" Minimum=\"-4\" Maximum=\"6\" TrueMin=\"0.01\" TrueMax=\"1000\" DecimalPoints=\"2\" Value=\"1\" Margin=\"0,0,0,4\"/>\n                                </DockPanel>\n                                <Grid>\n                                    <Grid.ColumnDefinitions>\n                                        <ColumnDefinition/>\n                                        <ColumnDefinition/>\n                                        <ColumnDefinition/>\n                                    </Grid.ColumnDefinitions>\n                                    <ui:BetterCheckbox HorizontalAlignment=\"Center\" x:Name=\"vsyncEnabled\" Text=\"{DynamicResource vsync}\" CheckToggled=\"VsyncEnabled_Checked\" Focusable=\"False\" IsChecked=\"True\"/>\n                                    <ui:BetterCheckbox HorizontalAlignment=\"Center\" Grid.Column=\"1\" x:Name=\"previewPaused\" Text=\"{DynamicResource paused}\" CheckToggled=\"Paused_Checked\"/>\n                                    <ui:BetterCheckbox HorizontalAlignment=\"Center\" Grid.Column=\"2\" Text=\"{DynamicResource realtimePlayback}\" Name=\"realtimePlayback\" CheckToggled=\"Checkbox_Checked\" IsChecked=\"True\"/>\n                                </Grid>\n                                <DockPanel Margin=\"10,10,10,10\">\n                                    <Button HorizontalAlignment=\"Left\" x:Name=\"disableKDMAPI\" Content=\"{DynamicResource disableKDMAPI}\" Padding=\"20,2,20,2\" Click=\"DisableKDMAPI_Click\" />\n                                    <Label Content=\"{DynamicResource pauseHintNew}\" HorizontalAlignment=\"Left\" Margin=\"10,0,0,0\" VerticalAlignment=\"Top\"/>\n                                </DockPanel>\n                            </StackPanel>\n                        </StackPanel>\n                        <DockPanel LastChildFill=\"False\" Margin=\"0,0,10,10\" HorizontalAlignment=\"Right\" Width=\"293\" Height=\"30\" VerticalAlignment=\"Bottom\">\n                            <ComboBox x:Name=\"languageSelect\" DockPanel.Dock=\"Right\" HorizontalAlignment=\"Left\" Margin=\"0\" Width=\"120\" SelectionChanged=\"LanguageSelect_SelectionChanged\"/>\n                            <Label DockPanel.Dock=\"Right\" Content=\"{DynamicResource language}\" VerticalAlignment=\"Top\"/>\n                        </DockPanel>\n                        <DockPanel HorizontalAlignment=\"Left\" LastChildFill=\"False\" Margin=\"10,0,0,10\" Height=\"26\" VerticalAlignment=\"Bottom\">\n                            <Button Content=\"{DynamicResource startPreview}\" HorizontalAlignment=\"Left\" Margin=\"0,0,0,0\" Padding=\"20,0,20,0\" IsEnabled=\"{Binding IsEnabled, ElementName=previewStartEnabled}\" Click=\"StartButton_Click\" DockPanel.Dock=\"Left\" />\n                            <Button Content=\"{DynamicResource stop}\" HorizontalAlignment=\"Left\" Margin=\"10,0,0,0\" Padding=\"20,0,20,0\" IsEnabled=\"{Binding IsEnabled, ElementName=stopEnabled}\" Click=\"StopButton_Click\" DockPanel.Dock=\"Left\" />\n                        </DockPanel>\n                    </Grid>\n                </TabItem>\n                <TabItem Header=\"{DynamicResource modulesTab}\" IsEnabled=\"{DynamicResource notRendering}\">\n                    <DockPanel Margin=\"10\">\n                        <TextBox DockPanel.Dock=\"Bottom\" Name=\"pluginDescription\" Margin=\"0,10,0,0\" MinHeight=\"100\" TextAlignment=\"Center\" Grid.Row=\"1\" TextWrapping=\"Wrap\" IsEnabled=\"False\"/>\n                        <DockPanel>\n                            <DockPanel Width=\"170\">\n                                <Button DockPanel.Dock=\"Top\" Name=\"reloadButton\" Content=\"{DynamicResource reload}\" Margin=\"0,0,0,0\" Height=\"26\" Click=\"ReloadButton_Click\"/>\n                                <ListBox Name=\"pluginsList\" Margin=\"0,10,0,0\" SelectionChanged=\"PluginsList_SelectionChanged\"/>\n                            </DockPanel>\n                            <Image Name=\"previewImage\" Margin=\"10,0,0,0\" Stretch=\"Uniform\"/>\n                        </DockPanel>\n                    </DockPanel>\n                </TabItem>\n                <TabItem Header=\"{DynamicResource moduleSettingsTab}\" IsEnabled=\"{DynamicResource notRendering}\">\n                    <Grid Name=\"pluginsSettings\">\n\n                    </Grid>\n                </TabItem>\n                <TabItem Header=\"{DynamicResource renderTab}\">\n                    <Grid>\n                        <StackPanel Margin=\"10\">\n                            <DockPanel Height=\"26\" LastChildFill=\"True\" Margin=\"0,0,0,0\" VerticalAlignment=\"Top\">\n                                <Label  Margin=\"0,0,0,0\" Content=\"{DynamicResource savePath}\"/>\n                                <Button x:Name=\"browseVideoSaveButton\" Content=\"{DynamicResource browse}\" HorizontalAlignment=\"Left\" Margin=\"10,0,0,0\" Padding=\"20,0,20,0\" IsEnabled=\"{DynamicResource notRendering}\" Click=\"BrowseVideoSaveButton_Click\"/>\n                                <TextBox x:Name=\"videoPath\" Margin=\"10,0,0,0\" IsEnabled=\"False\" TextWrapping=\"Wrap\" Text=\"\"/>\n                            </DockPanel>\n                            <DockPanel Height=\"26\" LastChildFill=\"True\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\">\n                                <ui:BetterCheckbox DockPanel.Dock=\"Left\" x:Name=\"includeAudio\" Text=\"{DynamicResource includeAudio}\" HorizontalAlignment=\"Left\" IsEnabled=\"{DynamicResource notRendering}\"/>\n                                <Button DockPanel.Dock=\"Left\" x:Name=\"browseAudioButton\" Content=\"{DynamicResource browse}\" HorizontalAlignment=\"Left\" Margin=\"10,0,0,0\" Padding=\"20,0,20,0\" Click=\"BrowseAudioButton_Click\" >\n                                    <Button.IsEnabled>\n                                        <MultiBinding Converter=\"{StaticResource AndValueConverter}\">\n                                            <Binding ElementName=\"includeAudio\" Path=\"IsChecked\" />\n                                            <Binding ElementName=\"notRendering\" Path=\"IsEnabled\"/>\n                                        </MultiBinding>\n                                    </Button.IsEnabled>\n                                </Button>\n                                <TextBox DockPanel.Dock=\"Left\" x:Name=\"audioPath\" Margin=\"10,0,0,0\" IsEnabled=\"False\" TextWrapping=\"Wrap\" Text=\"\"/>\n                            </DockPanel>\n                            <DockPanel Height=\"26\" LastChildFill=\"True\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\">\n                                <ui:BetterCheckbox x:Name=\"includeAlpha\" Text=\"{DynamicResource renderMask}\" HorizontalAlignment=\"Left\" IsEnabled=\"{DynamicResource notRendering}\" DockPanel.Dock=\"Left\"/>\n                                <Button x:Name=\"browseAlphaButton\" Content=\"{DynamicResource browse}\" HorizontalAlignment=\"Left\" Padding=\"20,0,20,0\" Click=\"BrowseAlphaButton_Click\" DockPanel.Dock=\"Left\" Margin=\"10,0,0,0\" >\n                                    <Button.IsEnabled>\n                                        <MultiBinding Converter=\"{StaticResource AndValueConverter}\">\n                                            <Binding ElementName=\"includeAlpha\" Path=\"IsChecked\" />\n                                            <Binding ElementName=\"notRendering\" Path=\"IsEnabled\"/>\n                                        </MultiBinding>\n                                    </Button.IsEnabled>\n                                </Button>\n                                <TextBox x:Name=\"alphaPath\" IsEnabled=\"False\" TextWrapping=\"Wrap\" Text=\"\" DockPanel.Dock=\"Right\" Margin=\"10,0,0,0\"/>\n                            </DockPanel>\n                            <DockPanel Height=\"30\" LastChildFill=\"False\" Margin=\"3,30,0,0\" VerticalAlignment=\"Top\">\n                                <ui:BetterRadio IsEnabled=\"{DynamicResource notRendering}\" ParentDepth=\"3\" x:Name=\"bitrateOption\" Text=\"{DynamicResource bitrate}\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Top\" IsChecked=\"True\" DockPanel.Dock=\"Left\" Margin=\"0,6,0,0\"/>\n                                <ui:NumberSelect x:Name=\"bitrate\" Maximum=\"2000000\" Minimum=\"1\" Value=\"20000\" HorizontalAlignment=\"Left\" Width=\"118\" DockPanel.Dock=\"Left\" Margin=\"10,0,0,0\" >\n                                    <ui:NumberSelect.IsEnabled>\n                                        <MultiBinding Converter=\"{StaticResource AndValueConverter}\">\n                                            <Binding ElementName=\"bitrateOption\" Path=\"IsChecked\" />\n                                            <Binding ElementName=\"notRendering\" Path=\"IsEnabled\"/>\n                                        </MultiBinding>\n                                    </ui:NumberSelect.IsEnabled>\n                                </ui:NumberSelect>\n                                <Label Content=\"kbps\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Top\" DockPanel.Dock=\"Left\" Margin=\"5,0,0,0\"/>\n                            </DockPanel>\n                            <DockPanel LastChildFill=\"False\" Margin=\"3,10,0,0\" VerticalAlignment=\"Top\">\n                                <ui:BetterRadio ParentDepth=\"3\" IsEnabled=\"{DynamicResource notRendering}\" x:Name=\"crfOption\" Text=\"crf\" HorizontalAlignment=\"Left\" RenderTransformOrigin=\"5.125,1.133\" DockPanel.Dock=\"Left\"/>\n                                <ui:NumberSelect x:Name=\"crfFactor\" Value=\"17\" Maximum=\"51\" Minimum=\"0\" HorizontalAlignment=\"Left\" Width=\"64\" DockPanel.Dock=\"Left\" Margin=\"10,0,0,0\" >\n                                    <ui:NumberSelect.IsEnabled>\n                                        <MultiBinding Converter=\"{StaticResource AndValueConverter}\">\n                                            <Binding ElementName=\"crfOption\" Path=\"IsChecked\" />\n                                            <Binding ElementName=\"notRendering\" Path=\"IsEnabled\"/>\n                                        </MultiBinding>\n                                    </ui:NumberSelect.IsEnabled>\n                                </ui:NumberSelect>\n                                <ComboBox DockPanel.Dock=\"Left\" x:Name=\"crfPreset\" HorizontalAlignment=\"Left\" Margin=\"10,0,0,0\" Width=\"122\" SelectedIndex=\"5\">\n                                    <ComboBox.IsEnabled>\n                                        <MultiBinding Converter=\"{StaticResource AndValueConverter}\">\n                                            <Binding ElementName=\"crfOption\" Path=\"IsChecked\" />\n                                            <Binding ElementName=\"notRendering\" Path=\"IsEnabled\"/>\n                                        </MultiBinding>\n                                    </ComboBox.IsEnabled>\n                                    <ComboBoxItem Content=\"ultrafast\"/>\n                                    <ComboBoxItem Content=\"superfast\"/>\n                                    <ComboBoxItem Content=\"veryfast\"/>\n                                    <ComboBoxItem Content=\"faster\"/>\n                                    <ComboBoxItem Content=\"fast\"/>\n                                    <ComboBoxItem Content=\"medium\"/>\n                                    <ComboBoxItem Content=\"slow\"/>\n                                    <ComboBoxItem Content=\"slower\"/>\n                                    <ComboBoxItem Content=\"veryslow\"/>\n                                </ComboBox>\n                                <Label Content=\"{DynamicResource crfHint}\" HorizontalAlignment=\"Left\" DockPanel.Dock=\"Left\" Margin=\"10,0,0,0\"/>\n                            </DockPanel>\n                            <DockPanel Height=\"30\" LastChildFill=\"False\" Margin=\"3,10,0,0\" VerticalAlignment=\"Top\">\n                                <ui:BetterRadio IsEnabled=\"{DynamicResource notRendering}\" ParentDepth=\"3\" x:Name=\"FFmpeg\" Text=\"{DynamicResource Custom}\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Top\" IsChecked=\"False\" DockPanel.Dock=\"Left\" Margin=\"0,6,0,0\" />\n                            </DockPanel>\n                            <DockPanel>\n                                <TextBox AcceptsReturn=\"True\" VerticalContentAlignment=\"Top\" x:Name=\"FFmpegOptions\" Margin=\"20,5,0,0\" TextWrapping=\"Wrap\" Height=\"50\" Text=\"FFmpeg options\">\n                                    <TextBox.IsEnabled>\n                                        <MultiBinding Converter=\"{StaticResource AndValueConverter}\">\n                                            <Binding ElementName=\"FFmpeg\" Path=\"IsChecked\" />\n                                            <Binding ElementName=\"notRendering\" Path=\"IsEnabled\"/>\n                                        </MultiBinding>\n                                    </TextBox.IsEnabled>\n                                </TextBox>\n                            </DockPanel>\n                            <DockPanel LastChildFill=\"False\" Margin=\"0,30,0,0\" VerticalAlignment=\"Top\">\n                                <Label Content=\"{DynamicResource delatStart}\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Top\" DockPanel.Dock=\"Left\"/>\n                                <ui:NumberSelect x:Name=\"secondsDelay\" Value=\"0\" Maximum=\"10\" Minimum=\"0\" HorizontalAlignment=\"Left\" Width=\"82\"  IsEnabled=\"{Binding IsEnabled, ElementName=notRendering}\" DockPanel.Dock=\"Left\" />\n                            </DockPanel>\n                            <DockPanel HorizontalAlignment=\"Left\" Height=\"26\" LastChildFill=\"False\" Margin=\"0,10,0,0\" >\n                                <Button x:Name=\"startRenderButton\" Content=\"{DynamicResource startRender}\" HorizontalAlignment=\"Left\" Padding=\"20,0,20,0\" Click=\"StartRenderButton_Click\"  IsEnabled=\"{Binding IsEnabled, ElementName=previewStartEnabled}\" DockPanel.Dock=\"Left\"/>\n                                <Button Content=\"{DynamicResource stop}\" HorizontalAlignment=\"Left\" Padding=\"20,0,20,0\" IsEnabled=\"{Binding IsEnabled, ElementName=stopEnabled}\" Click=\"StopButton_Click\" DockPanel.Dock=\"Left\" Margin=\"10,0,0,0\" />\n                                <ui:BetterCheckbox IsEnabled=\"{DynamicResource notRendering}\" x:Name=\"ffdebug\" Text=\"{DynamicResource ffmpegDebug}\" HorizontalAlignment=\"Left\" DockPanel.Dock=\"Left\" Margin=\"10,0,0,0\"/>\n                            </DockPanel>\n                        </StackPanel>\n                        <DockPanel Margin=\"10\">\n                            <TextBox DockPanel.Dock=\"Bottom\" x:Name=\"creditText\" Text=\"hhhh\" AcceptsReturn=\"True\" IsReadOnly=\"True\" HorizontalAlignment=\"Left\" Margin=\"0,0,0,0\" TextWrapping=\"Wrap\" Width=\"281\" VerticalAlignment=\"Bottom\"/>\n                            <Label Content=\"{DynamicResource ifUploading}\" HorizontalAlignment=\"Left\" Margin=\"0,0,0,0\" VerticalAlignment=\"Bottom\"/>\n                        </DockPanel>\n                    </Grid>\n                </TabItem>\n            </local:CustomTabs>\n        </Grid>\n    </DockPanel>\n</Window>\n"
  },
  {
    "path": "Black-Midi-Render/MainWindow.xaml.cs",
    "content": "﻿using ZenithEngine;\nusing Microsoft.CSharp.RuntimeBinder;\nusing Microsoft.Win32;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Drawing;\nusing System.IO;\nusing System.Linq;\nusing System.Reflection;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\nusing Path = System.IO.Path;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing System.Runtime.InteropServices;\nusing System.Windows.Interop;\nusing ZenithShared;\nusing System.IO;\nusing System.IO.Compression;\n\nnamespace Zenith_MIDI\n{\n    class CurrentRendererPointer\n    {\n        public Queue<IPluginRender> disposeQueue = new Queue<IPluginRender>();\n        public IPluginRender renderer = null;\n    }\n\n    public enum UpdateProgress\n    {\n        NotDownloading,\n        Downloading,\n        Downloaded\n    }\n\n    public partial class MainWindow : Window\n    {\n        #region Chrome Window scary code\n        private static IntPtr WindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)\n        {\n            switch (msg)\n            {\n                case 0x0024:\n                    WmGetMinMaxInfo(hwnd, lParam);\n                    handled = true;\n                    break;\n            }\n            return (IntPtr)0;\n        }\n\n        private static void WmGetMinMaxInfo(IntPtr hwnd, IntPtr lParam)\n        {\n            MINMAXINFO mmi = (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));\n            int MONITOR_DEFAULTTONEAREST = 0x00000002;\n            IntPtr monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);\n            if (monitor != IntPtr.Zero)\n            {\n                MONITORINFO monitorInfo = new MONITORINFO();\n                GetMonitorInfo(monitor, monitorInfo);\n                RECT rcWorkArea = monitorInfo.rcWork;\n                RECT rcMonitorArea = monitorInfo.rcMonitor;\n                mmi.ptMaxPosition.x = Math.Abs(rcWorkArea.left - rcMonitorArea.left);\n                mmi.ptMaxPosition.y = Math.Abs(rcWorkArea.top - rcMonitorArea.top);\n                mmi.ptMaxSize.x = Math.Abs(rcWorkArea.right - rcWorkArea.left);\n                mmi.ptMaxSize.y = Math.Abs(rcWorkArea.bottom - rcWorkArea.top);\n            }\n            Marshal.StructureToPtr(mmi, lParam, true);\n        }\n\n        [StructLayout(LayoutKind.Sequential)]\n        public struct POINT\n        {\n            /// <summary>x coordinate of point.</summary>\n            public int x;\n            /// <summary>y coordinate of point.</summary>\n            public int y;\n            /// <summary>Construct a point of coordinates (x,y).</summary>\n            public POINT(int x, int y)\n            {\n                this.x = x;\n                this.y = y;\n            }\n        }\n\n        [StructLayout(LayoutKind.Sequential)]\n        public struct MINMAXINFO\n        {\n            public POINT ptReserved;\n            public POINT ptMaxSize;\n            public POINT ptMaxPosition;\n            public POINT ptMinTrackSize;\n            public POINT ptMaxTrackSize;\n        };\n\n        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]\n        public class MONITORINFO\n        {\n            public int cbSize = Marshal.SizeOf(typeof(MONITORINFO));\n            public RECT rcMonitor = new RECT();\n            public RECT rcWork = new RECT();\n            public int dwFlags = 0;\n        }\n\n        [StructLayout(LayoutKind.Sequential, Pack = 0)]\n        public struct RECT\n        {\n            public int left;\n            public int top;\n            public int right;\n            public int bottom;\n            public static readonly RECT Empty = new RECT();\n            public int Width { get { return Math.Abs(right - left); } }\n            public int Height { get { return bottom - top; } }\n            public RECT(int left, int top, int right, int bottom)\n            {\n                this.left = left;\n                this.top = top;\n                this.right = right;\n                this.bottom = bottom;\n            }\n            public RECT(RECT rcSrc)\n            {\n                left = rcSrc.left;\n                top = rcSrc.top;\n                right = rcSrc.right;\n                bottom = rcSrc.bottom;\n            }\n            public bool IsEmpty { get { return left >= right || top >= bottom; } }\n            public override string ToString()\n            {\n                if (this == Empty) { return \"RECT {Empty}\"; }\n                return \"RECT { left : \" + left + \" / top : \" + top + \" / right : \" + right + \" / bottom : \" + bottom + \" }\";\n            }\n            public override bool Equals(object obj)\n            {\n                if (!(obj is Rect)) { return false; }\n                return (this == (RECT)obj);\n            }\n            /// <summary>Return the HashCode for this struct (not garanteed to be unique)</summary>\n            public override int GetHashCode() => left.GetHashCode() + top.GetHashCode() + right.GetHashCode() + bottom.GetHashCode();\n            /// <summary> Determine if 2 RECT are equal (deep compare)</summary>\n            public static bool operator ==(RECT rect1, RECT rect2) { return (rect1.left == rect2.left && rect1.top == rect2.top && rect1.right == rect2.right && rect1.bottom == rect2.bottom); }\n            /// <summary> Determine if 2 RECT are different(deep compare)</summary>\n            public static bool operator !=(RECT rect1, RECT rect2) { return !(rect1 == rect2); }\n        }\n\n        [DllImport(\"user32\")]\n        internal static extern bool GetMonitorInfo(IntPtr hMonitor, MONITORINFO lpmi);\n\n        [DllImport(\"User32\")]\n        internal static extern IntPtr MonitorFromWindow(IntPtr handle, int flags);\n        #endregion\n\n        RenderSettings settings;\n        MidiFile midifile = null;\n        string midipath = \"\";\n\n        Control pluginControl = null;\n\n        List<IPluginRender> RenderPlugins = new List<IPluginRender>();\n\n        CurrentRendererPointer renderer = new CurrentRendererPointer();\n\n        List<Dictionary<string, ResourceDictionary>> Languages = new List<Dictionary<string, ResourceDictionary>>();\n\n        bool foundOmniMIDI = true;\n        bool OmniMIDIDisabled = false;\n\n        string defaultPlugin = \"Classic\";\n\n        Settings metaSettings = new Settings();\n\n        void RunLanguageCheck()\n        {\n            string ver;\n            try\n            {\n                ver = ZenithLanguages.GetLatestVersion();\n            }\n            catch { return; }\n            if (ver != metaSettings.LanguagesVersion)\n            {\n                try\n                {\n                    Console.WriteLine(\"Update found for language packs, downloading...\");\n                    var latest = ZenithLanguages.DownloadLatestVersion();\n                    ZenithLanguages.UnpackFromStream(latest);\n                    metaSettings.LanguagesVersion = ver;\n                    metaSettings.SaveConfig();\n                    Console.WriteLine(\"Updated language packs!\");\n                }\n                catch { Console.WriteLine(\"Failed to update language packs\"); }\n            }\n        }\n\n        void RunUpdateCheck()\n        {\n            if (!metaSettings.AutoUpdate) return;\n\n            var requiredInstaller = ZenithUpdates.InstallerVer;\n            if (metaSettings.InstallerVer != requiredInstaller)\n            {\n                Console.WriteLine(\"Important update found for installer, updating...\");\n                ZenithUpdates.UpdateInstaller();\n                metaSettings.InstallerVer = requiredInstaller;\n                metaSettings.SaveConfig();\n            }\n\n            string ver;\n            try\n            {\n                ver = ZenithUpdates.GetLatestVersion();\n            }\n            catch { return; }\n            if (ver != metaSettings.VersionName)\n            {\n                Console.WriteLine(\"Found Update! Current: \" + metaSettings.VersionName + \" Latest: \" + ver);\n                try\n                {\n                    Dispatcher.InvokeAsync(() => windowTabs.UpdaterProgress = UpdateProgress.Downloading).Wait();\n                    Stream data;\n                    if (Environment.Is64BitOperatingSystem) data = ZenithUpdates.DownloadAssetData(ZenithUpdates.DataAssetName64);\n                    else data = ZenithUpdates.DownloadAssetData(ZenithUpdates.DataAssetName32);\n                    var dest = File.OpenWrite(ZenithUpdates.DefaultUpdatePackagePath);\n                    data.CopyTo(dest);\n                    data.Close();\n                    dest.Close();\n                    Dispatcher.InvokeAsync(() => windowTabs.UpdaterProgress = UpdateProgress.Downloaded).Wait();\n                }\n                catch (Exception e)\n                {\n                    Dispatcher.InvokeAsync(() => windowTabs.UpdaterProgress = UpdateProgress.NotDownloading).Wait();\n                    MessageBox.Show(\"Couldn't download and save update package\", \"Update failed\");\n                }\n            }\n        }\n\n        void CheckUpdateDownloaded()\n        {\n            if(metaSettings.PreviousVersion != metaSettings.VersionName)\n            {\n                if (File.Exists(\"settings.json\")) File.Delete(\"settings.json\");\n                metaSettings.PreviousVersion = metaSettings.VersionName;\n                metaSettings.SaveConfig();\n            }\n\n            if (metaSettings.AutoUpdate)\n            {\n                if (File.Exists(ZenithUpdates.DefaultUpdatePackagePath))\n                {\n                    try\n                    {\n                        using (var z = File.OpenRead(ZenithUpdates.DefaultUpdatePackagePath))\n                        using (ZipArchive archive = new ZipArchive(z))\n                        { }\n                        Dispatcher.InvokeAsync(() => windowTabs.UpdaterProgress = UpdateProgress.Downloaded).Wait();\n                        if (!ZenithUpdates.IsAnotherProcessRunning())\n                        {\n                            Process.Start(ZenithUpdates.InstallerPath, \"update -Reopen\");\n                        }\n                    }\n                    catch (Exception) { File.Delete(ZenithUpdates.DefaultUpdatePackagePath); }\n                }\n            }\n        }\n\n        public MainWindow()\n        {\n            CheckUpdateDownloaded();\n\n            InitializeComponent();\n\n            windowTabs.VersionName = metaSettings.VersionName;\n\n            SourceInitialized += (s, e) =>\n            {\n                IntPtr handle = (new WindowInteropHelper(this)).Handle;\n                HwndSource.FromHwnd(handle).AddHook(new HwndSourceHook(WindowProc));\n            };\n\n            tempoMultSlider.nudToSlider = v => Math.Log(v, 2);\n            tempoMultSlider.sliderToNud = v => Math.Pow(2, v);\n\n            bool dontUpdateLanguages = false;\n\n            if (!File.Exists(\"Settings/settings.json\"))\n            {\n                var sett = new JObject();\n                sett.Add(\"defaultBackground\", \"\");\n                sett.Add(\"ignoreKDMAPI\", \"false\");\n                sett.Add(\"defaultPlugin\", \"Classic\");\n                sett.Add(\"ignoreLanguageUpdates\", \"false\");\n                File.WriteAllText(\"Settings/settings.json\", JsonConvert.SerializeObject(sett));\n            }\n\n            {\n                dynamic sett = JsonConvert.DeserializeObject(File.ReadAllText(\"Settings/settings.json\"));\n                if (sett.defaultBackground != \"\")\n                {\n                    try\n                    {\n                        bgImagePath.Text = sett.defaultBackground;\n                        settings.BGImage = bgImagePath.Text;\n                    }\n                    catch\n                    {\n                        settings.BGImage = null;\n                        if (bgImagePath.Text != \"\")\n                            MessageBox.Show(\"Couldn't load default background image\");\n                    }\n                }\n                if ((bool)sett.ignoreKDMAPI) foundOmniMIDI = false;\n                defaultPlugin = (string)sett.defaultPlugin;\n                dontUpdateLanguages = (bool)sett.ignoreLanguageUpdates;\n            }\n\n            Task omnimidiLoader = null;\n            Task languageLoader = null;\n            if (!dontUpdateLanguages) Task.Run(RunLanguageCheck);\n            Task updateLoader = Task.Run(RunUpdateCheck);\n            if (foundOmniMIDI)\n            {\n                omnimidiLoader = Task.Run(() =>\n                {\n                    try\n                    {\n                        KDMAPI.InitializeKDMAPIStream();\n                        Console.WriteLine(\"Loaded KDMAPI!\");\n                    }\n                    catch\n                    {\n                        Console.WriteLine(\"Failed to load KDMAPI, disabling\");\n                        foundOmniMIDI = false;\n                    }\n                });\n            }\n            if (!foundOmniMIDI)\n            {\n                disableKDMAPI.IsEnabled = false;\n            }\n            settings = new RenderSettings();\n            settings.PauseToggled += ToggledPause;\n            InitialiseSettingsValues();\n            creditText.Text = \"Video was rendered with Zenith\\nhttps://arduano.github.io/Zenith-MIDI/start\";\n\n            if(languageLoader != null) languageLoader.Wait();\n\n            var languagePacks = Directory.GetDirectories(\"Languages\");\n            foreach (var language in languagePacks)\n            {\n                var resources = Directory.GetFiles(language).Where((l) => l.EndsWith(\".xaml\")).ToList();\n                if (resources.Count == 0) continue;\n\n                Dictionary<string, ResourceDictionary> fullDict = new Dictionary<string, ResourceDictionary>();\n                foreach (var r in resources)\n                {\n                    ResourceDictionary file = new ResourceDictionary();\n                    file.Source = new Uri(Path.GetFullPath(r), UriKind.RelativeOrAbsolute);\n                    var name = Path.GetFileNameWithoutExtension(r);\n                    fullDict.Add(name, file);\n                }\n                if (!fullDict.ContainsKey(\"window\")) continue;\n                if (fullDict[\"window\"].Contains(\"LanguageName\") && fullDict[\"window\"][\"LanguageName\"].GetType() == typeof(string))\n                    Languages.Add(fullDict);\n            }\n            Languages.Sort(new Comparison<Dictionary<string, ResourceDictionary>>((d1, d2) =>\n            {\n                if ((string)d1[\"window\"][\"LanguageName\"] == \"English\") return -1;\n                if ((string)d2[\"window\"][\"LanguageName\"] == \"English\") return 1;\n                else return 0;\n            }));\n            foreach (var lang in Languages)\n            {\n                var item = new ComboBoxItem() { Content = lang[\"window\"][\"LanguageName\"] };\n                languageSelect.Items.Add(item);\n            }\n            languageSelect.SelectedIndex = 0;\n            if (omnimidiLoader != null)\n                omnimidiLoader.Wait();\n        }\n\n        void ToggledPause()\n        {\n            Dispatcher.Invoke(() =>\n            {\n                if ((bool)previewPaused.IsChecked ^ settings.Paused)\n                {\n                    previewPaused.IsChecked = settings.Paused;\n                }\n            });\n\n        }\n\n        void InitialiseSettingsValues()\n        {\n            viewWidth.Value = settings.width;\n            viewHeight.Value = settings.height;\n            viewFps.Value = settings.fps;\n            vsyncEnabled.IsChecked = settings.vsync;\n            tempoMultSlider.Value = settings.tempoMultiplier;\n\n            ReloadPlugins();\n        }\n\n        Task renderThread = null;\n        RenderWindow win = null;\n        void RunRenderWindow()\n        {\n            bool winStarted = false;\n            Task winthread = new Task(() =>\n            {\n                win = new RenderWindow(renderer, midifile, settings);\n                winStarted = true;\n                win.Run();\n            });\n            winthread.Start();\n            SpinWait.SpinUntil(() => winStarted);\n            double time = 0;\n            int nc = -1;\n            long maxRam = 0;\n            long avgRam = 0;\n            long ramSample = 0;\n            Stopwatch timewatch = new Stopwatch();\n            timewatch.Start();\n            IPluginRender render = null;\n            double lastWinTime = double.NaN;\n            bool tryToParse()\n            {\n                lock (midifile)\n                {\n                    return (midifile.ParseUpTo(\n                        (win.midiTime + win.lastDeltaTimeOnScreen +\n                        (win.tempoFrameStep * 20 * settings.tempoMultiplier * (win.lastMV > 1 ? win.lastMV : 1))))\n                        || nc != 0) && settings.running;\n                }\n            }\n            try\n            {\n                while (tryToParse())\n                {\n                    //SpinWait.SpinUntil(() => lastWinTime != win.midiTime || render != renderer.renderer || !settings.running);\n                    if (!settings.running) break;\n                    Note n;\n                    double cutoffTime = win.midiTime;\n                    bool manualDelete = false;\n                    double noteCollectorOffset = 0;\n                    bool receivedInfo = false;\n                    while (!receivedInfo)\n                        try\n                        {\n                            render = renderer.renderer;\n                            receivedInfo = true;\n                        }\n                        catch\n                        { }\n                    manualDelete = render.ManualNoteDelete;\n                    noteCollectorOffset = render.NoteCollectorOffset;\n                    cutoffTime += noteCollectorOffset;\n                    if (!settings.running) break;\n                    lock (midifile.globalDisplayNotes)\n                    {\n                        var i = midifile.globalDisplayNotes.Iterate();\n                        if (manualDelete)\n                            while (i.MoveNext(out n))\n                            {\n                                if (n.delete)\n                                    i.Remove();\n                                else\n                                    nc++;\n                            }\n                        else\n                            while (i.MoveNext(out n))\n                            {\n                                if (n.hasEnded && n.end < cutoffTime)\n                                    i.Remove();\n                                if (n.start > cutoffTime) break;\n                            }\n                        GC.Collect();\n                    }\n                    try\n                    {\n                        double progress = win.midiTime / midifile.maxTrackTime;\n                        if (settings.timeBasedNotes) progress = win.midiTime / 1000 / midifile.info.secondsLength;\n                        Console.WriteLine(\n                            new TimeSpan(0, 0, (int)(timewatch.ElapsedMilliseconds / 1000)) +\n                            \" \" + Math.Round(progress * 10000) / 100 +\n                            \"%\\tRender FPS: \" + Math.Round(settings.liveFps) +\n                            \"\\tNotes drawn: \" + renderer.renderer.LastNoteCount\n                            );\n                    }\n                    catch\n                    {\n                    }\n                    long ram = Process.GetCurrentProcess().PrivateMemorySize64;\n                    if (maxRam < ram) maxRam = ram;\n                    avgRam = (long)((double)avgRam * ramSample + ram) / (ramSample + 1);\n                    ramSample++;\n                    lastWinTime = win.midiTime;\n                    Stopwatch s = new Stopwatch();\n                    s.Start();\n                    SpinWait.SpinUntil(() =>\n                    (\n                        (s.ElapsedMilliseconds > 1000.0 / settings.fps * 30 && false) ||\n                        (win.midiTime + win.lastDeltaTimeOnScreen +\n                        (win.tempoFrameStep * 10 * settings.tempoMultiplier * (win.lastMV > 1 ? win.lastMV : 1))) > midifile.currentSyncTime ||\n                        lastWinTime != win.midiTime || render != renderer.renderer || !settings.running\n                    )\n                    ); ;\n                }\n            }\n            catch (Exception ex)\n            {\n                MessageBox.Show(\"An error occurred while opeining render window. Please try again.\\n\\n\" + ex.Message + \"\\n\" + ex.StackTrace);\n                settings.running = false;\n            }\n            winthread.GetAwaiter().GetResult();\n            settings.running = false;\n            Console.WriteLine(\"Reset midi file\");\n            midifile.Reset();\n            win.Dispose();\n            win = null;\n            GC.Collect();\n            GC.WaitForFullGCComplete();\n            Console.ForegroundColor = ConsoleColor.Blue;\n            Console.WriteLine(\n                    \"Finished render\\nRAM usage (Private bytes)\\nPeak: \" + Math.Round((double)maxRam / 1000 / 1000 / 1000 * 100) / 100 +\n                    \"GB\\nAvg: \" + Math.Round((double)avgRam / 1000 / 1000 / 1000 * 100) / 100 +\n                    \"GB\\nMinutes to render: \" + Math.Round((double)timewatch.ElapsedMilliseconds / 1000 / 60 * 100) / 100);\n            Console.ResetColor();\n            Dispatcher.Invoke(() =>\n            {\n                Resources[\"notRendering\"] = true;\n                Resources[\"notPreviewing\"] = true;\n            });\n        }\n\n        void ReloadPlugins()\n        {\n            previewImage.Source = null;\n            pluginDescription.Text = \"\";\n            lock (renderer)\n            {\n                foreach (var p in RenderPlugins)\n                {\n                    if (p.Initialized) renderer.disposeQueue.Enqueue(p);\n                }\n                RenderPlugins.Clear();\n                var files = Directory.GetFiles(\"Plugins\");\n                var dlls = files.Where((s) => s.EndsWith(\".dll\"));\n                foreach (var d in dlls)\n                {\n                    try\n                    {\n                        var DLL = Assembly.UnsafeLoadFrom(System.IO.Path.GetFullPath(d));\n                        bool hasClass = false;\n                        var name = System.IO.Path.GetFileName(d);\n                        try\n                        {\n                            foreach (Type type in DLL.GetExportedTypes())\n                            {\n                                if (type.Name == \"Render\")\n                                {\n                                    hasClass = true;\n                                    var instance = (IPluginRender)Activator.CreateInstance(type, new object[] { settings });\n                                    RenderPlugins.Add(instance);\n                                    Console.WriteLine(\"Loaded \" + name);\n                                }\n                            }\n                            if (!hasClass)\n                            {\n                                MessageBox.Show(\"Could not load \" + name + \"\\nDoesn't have render class\");\n                            }\n                        }\n                        catch (RuntimeBinderException)\n                        {\n                            MessageBox.Show(\"Could not load \" + name + \"\\nA binding error occured\");\n                        }\n                        catch (InvalidCastException)\n                        {\n                            MessageBox.Show(\"Could not load \" + name + \"\\nThe Render class was not a compatible with the interface\");\n                        }\n                        catch (Exception e)\n                        {\n                            MessageBox.Show(\"An error occured while binfing \" + name + \"\\n\" + e.Message);\n                        }\n                    }\n                    catch { }\n                }\n\n                pluginsList.Items.Clear();\n                for (int i = 0; i < RenderPlugins.Count; i++)\n                {\n                    pluginsList.Items.Add(new ListBoxItem() { Content = RenderPlugins[i].Name });\n                }\n                if (RenderPlugins.Count != 0)\n                {\n                    SelectRenderer(0);\n                }\n            }\n        }\n\n        void SelectRenderer(int id)\n        {\n            (pluginsSettings as Panel).Children.Clear();\n            pluginControl = null;\n            if (id == -1)\n            {\n                renderer.renderer = null;\n                return;\n            }\n            pluginsList.SelectedIndex = id;\n            lock (renderer)\n            {\n                renderer.renderer = RenderPlugins[id];\n            }\n            previewImage.Source = renderer.renderer.PreviewImage;\n            pluginDescription.Text = renderer.renderer.Description;\n\n            var c = renderer.renderer.SettingsControl;\n            if (c == null) return;\n            if (c.Parent != null)\n                (c.Parent as Panel).Children.Clear();\n            pluginsSettings.Children.Add(c);\n            c.VerticalAlignment = VerticalAlignment.Stretch;\n            c.HorizontalAlignment = HorizontalAlignment.Stretch;\n            c.Width = double.NaN;\n            c.Height = double.NaN;\n            c.Margin = new Thickness(0);\n            pluginControl = c;\n            if (languageSelect.SelectedIndex != -1 && Languages[languageSelect.SelectedIndex].ContainsKey(renderer.renderer.LanguageDictName))\n            {\n                c.Resources.MergedDictionaries[0].MergedDictionaries.Clear();\n                c.Resources.MergedDictionaries[0].MergedDictionaries.Add(Languages[0][renderer.renderer.LanguageDictName]);\n                c.Resources.MergedDictionaries[0].MergedDictionaries.Add(Languages[languageSelect.SelectedIndex][renderer.renderer.LanguageDictName]);\n            }\n        }\n\n        private void LoadButton_Click(object sender, RoutedEventArgs e)\n        {\n            var open = new OpenFileDialog();\n            open.Filter = \"Midi files (*.mid)|*.mid\";\n            if ((bool)open.ShowDialog())\n            {\n                midipath = open.FileName;\n            }\n            else return;\n\n            if (!File.Exists(midipath))\n            {\n                MessageBox.Show(\"Midi file doesn't exist\");\n                return;\n            }\n            try\n            {\n                if (midifile != null) midifile.Dispose();\n                midifile = null;\n                GC.Collect();\n                GC.WaitForFullGCComplete();\n                midifile = new MidiFile(midipath, settings);\n                Resources[\"midiLoaded\"] = true;\n                browseMidiButton.Content = Path.GetFileName(midipath);\n            }\n            catch (Exception ex)\n            {\n                Console.WriteLine(ex.Message + \"\\n\" + ex.StackTrace);\n                MessageBox.Show(ex.Message + \"\\n\" + ex.StackTrace);\n            }\n        }\n\n        private void UnloadButton_Click(object sender, RoutedEventArgs e)\n        {\n            Console.WriteLine(\"Unloading midi\");\n            midifile.Dispose();\n            midifile = null;\n            GC.Collect();\n            GC.WaitForFullGCComplete();\n            Console.WriteLine(\"Unloaded\");\n            Resources[\"midiLoaded\"] = false;\n            browseMidiButton.SetResourceReference(Button.ContentProperty, \"load\");\n        }\n\n        private void StartButton_Click(object sender, RoutedEventArgs e)\n        {\n            if (renderer.renderer == null)\n            {\n                MessageBox.Show(\"No renderer is selected\");\n                return;\n            }\n\n            windowTabs.SelectedIndex = 4;\n\n            settings.realtimePlayback = (bool)realtimePlayback.IsChecked;\n\n            settings.running = true;\n            settings.width = (int)viewWidth.Value * (int)SSAAFactor.Value;\n            settings.height = (int)viewHeight.Value * (int)SSAAFactor.Value;\n            settings.downscale = (int)SSAAFactor.Value;\n            settings.fps = (int)viewFps.Value;\n            settings.ffRender = false;\n            settings.Paused = false;\n            settings.renderSecondsDelay = 0;\n            renderThread = Task.Factory.StartNew(RunRenderWindow, TaskCreationOptions.RunContinuationsAsynchronously | TaskCreationOptions.LongRunning);\n            Resources[\"notPreviewing\"] = false;\n        }\n\n        private void StopButton_Click(object sender, RoutedEventArgs e)\n        {\n            if (settings.running == false)\n            {\n                Resources[\"notRendering\"] = true;\n                Resources[\"notPreviewing\"] = true;\n            }\n            else\n                settings.running = false;\n        }\n\n        private void StartRenderButton_Click(object sender, RoutedEventArgs e)\n        {\n            if (videoPath.Text == \"\")\n            {\n                MessageBox.Show(\"Please specify a destination path\");\n                return;\n            }\n\n            if (renderer.renderer == null)\n            {\n                MessageBox.Show(\"No renderer is selected\");\n                return;\n            }\n\n            if (File.Exists(videoPath.Text))\n            {\n                if (MessageBox.Show(\"Are you sure you want to override \" + Path.GetFileName(videoPath.Text), \"Override\", MessageBoxButton.YesNo) == MessageBoxResult.No)\n                    return;\n            }\n            if (File.Exists(alphaPath.Text))\n            {\n                if (MessageBox.Show(\"Are you sure you want to override \" + Path.GetFileName(alphaPath.Text), \"Override\", MessageBoxButton.YesNo) == MessageBoxResult.No)\n                    return;\n            }\n\n            settings.realtimePlayback = false;\n\n            settings.running = true;\n            settings.width = (int)viewWidth.Value * (int)SSAAFactor.Value;\n            settings.height = (int)viewHeight.Value * (int)SSAAFactor.Value;\n            settings.downscale = (int)SSAAFactor.Value;\n            settings.fps = (int)viewFps.Value;\n            settings.ffRender = true;\n            settings.ffPath = videoPath.Text;\n            settings.renderSecondsDelay = (double)secondsDelay.Value;\n\n            settings.Paused = false;\n            previewPaused.IsChecked = false;\n            settings.tempoMultiplier = 1;\n            tempoMultSlider.Value = 1;\n\n            settings.ffmpegDebug = (bool)ffdebug.IsChecked;\n\n            settings.useBitrate = (bool)bitrateOption.IsChecked;\n            settings.CustomFFmpeg = (bool)FFmpeg.IsChecked;\n            if (settings.useBitrate) settings.bitrate = (int)bitrate.Value;\n            else if (settings.CustomFFmpeg)\n            {\n                settings.ffoption = FFmpegOptions.Text;\n            }\n            else\n            {\n                settings.crf = (int)crfFactor.Value;\n                settings.crfPreset = (string)((ComboBoxItem)crfPreset.SelectedItem).Content;\n            }\n\n            settings.includeAudio = (bool)includeAudio.IsChecked;\n            settings.audioPath = audioPath.Text;\n            settings.ffRenderMask = (bool)includeAlpha.IsChecked;\n            settings.ffMaskPath = alphaPath.Text;\n            renderThread = Task.Factory.StartNew(RunRenderWindow);\n            Resources[\"notPreviewing\"] = false;\n            Resources[\"notRendering\"] = false;\n        }\n\n        private void BrowseVideoSaveButton_Click(object sender, RoutedEventArgs e)\n        {\n            var save = new SaveFileDialog();\n            save.OverwritePrompt = true;\n            save.Filter = \"H.264 video (*.mp4)|*.mp4|All types|*.*\";\n            if ((bool)save.ShowDialog())\n            {\n                videoPath.Text = save.FileName;\n            }\n        }\n\n        private void BrowseAudioButton_Click(object sender, RoutedEventArgs e)\n        {\n            var audio = new OpenFileDialog();\n            audio.Filter = \"Common audio files (*.mp3;*.wav;*.ogg;*.flac)|*.mp3;*.wav;*.ogg;*.flac\";\n            if ((bool)audio.ShowDialog())\n            {\n                audioPath.Text = audio.FileName;\n            }\n        }\n\n        private void BrowseAlphaButton_Click(object sender, RoutedEventArgs e)\n        {\n            var save = new SaveFileDialog();\n            save.OverwritePrompt = true;\n            save.Filter = \"H.264 video (*.mp4)|*.mp4\";\n            if ((bool)save.ShowDialog())\n            {\n                alphaPath.Text = save.FileName;\n            }\n        }\n\n        private void Paused_Checked(object sender, RoutedEventArgs e)\n        {\n            settings.Paused = (bool)previewPaused.IsChecked;\n        }\n\n        private void VsyncEnabled_Checked(object sender, RoutedEventArgs e)\n        {\n            if (settings == null) return;\n            settings.vsync = (bool)vsyncEnabled.IsChecked;\n        }\n\n        private void Grid_KeyDown(object sender, KeyEventArgs e)\n        {\n            if (e.Key == Key.Space)\n            {\n                previewPaused.IsChecked = !settings.Paused;\n                settings.Paused = (bool)previewPaused.IsChecked;\n            }\n        }\n\n        private void ReloadButton_Click(object sender, RoutedEventArgs e)\n        {\n            ReloadPlugins();\n        }\n\n        private void PluginsList_SelectionChanged(object sender, SelectionChangedEventArgs e)\n        {\n            foreach (var p in RenderPlugins)\n            {\n                if (p.Initialized) renderer.disposeQueue.Enqueue(p);\n            }\n            SelectRenderer(pluginsList.SelectedIndex);\n        }\n\n        private void ResolutionPreset_SelectionChanged(object sender, SelectionChangedEventArgs e)\n        {\n            string preset = (string)((ComboBoxItem)resolutionPreset.SelectedItem).Content;\n            switch (preset)\n            {\n                case \"720p\":\n                    viewWidth.Value = 1280;\n                    viewHeight.Value = 720;\n                    break;\n                case \"1080p\":\n                    viewWidth.Value = 1920;\n                    viewHeight.Value = 1080;\n                    break;\n                case \"1440p\":\n                    viewWidth.Value = 2560;\n                    viewHeight.Value = 1440;\n                    break;\n                case \"4k\":\n                    viewWidth.Value = 3840;\n                    viewHeight.Value = 2160;\n                    break;\n                case \"5k\":\n                    viewWidth.Value = 5120;\n                    viewHeight.Value = 2880;\n                    break;\n                case \"8k\":\n                    viewWidth.Value = 7680;\n                    viewHeight.Value = 4320;\n                    break;\n                case \"16k\":\n                    viewWidth.Value = 15360;\n                    viewHeight.Value = 8640;\n                    break;\n                default:\n                    break;\n            }\n        }\n\n        private void LanguageSelect_SelectionChanged(object sender, SelectionChangedEventArgs e)\n        {\n            if (pluginControl != null)\n                lock (renderer)\n                {\n                    ((UserControl)pluginControl).Resources.MergedDictionaries[0].MergedDictionaries.Clear();\n                    ((UserControl)pluginControl).Resources.MergedDictionaries[0].MergedDictionaries.Add(Languages[0][renderer.renderer.LanguageDictName]);\n                    ((UserControl)pluginControl).Resources.MergedDictionaries[0].MergedDictionaries.Add(Languages[languageSelect.SelectedIndex][renderer.renderer.LanguageDictName]);\n                }\n            Resources.MergedDictionaries[0].MergedDictionaries.Clear();\n            Resources.MergedDictionaries[0].MergedDictionaries.Add(Languages[0][\"window\"]);\n            Resources.MergedDictionaries[0].MergedDictionaries.Add(Languages[languageSelect.SelectedIndex][\"window\"]);\n        }\n\n        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)\n        {\n            if (foundOmniMIDI)\n                KDMAPI.TerminateKDMAPIStream();\n        }\n\n        private void Checkbox_Checked(object sender, RoutedEventArgs e)\n        {\n            if (settings == null) return;\n            if (sender == realtimePlayback) settings.realtimePlayback = (bool)realtimePlayback.IsChecked;\n        }\n\n        private void DisableKDMAPI_Click(object sender, RoutedEventArgs e)\n        {\n            if (OmniMIDIDisabled)\n            {\n                disableKDMAPI.Content = Resources[\"disableKDMAPI\"];\n                OmniMIDIDisabled = false;\n                settings.playbackEnabled = true;\n                try\n                {\n                    Console.WriteLine(\"Loading KDMAPI...\");\n                    KDMAPI.InitializeKDMAPIStream();\n                    Console.WriteLine(\"Loaded!\");\n                }\n                catch { }\n            }\n            else\n            {\n                disableKDMAPI.Content = Resources[\"enableKDMAPI\"];\n                OmniMIDIDisabled = true;\n                settings.playbackEnabled = false;\n                try\n                {\n                    Console.WriteLine(\"Unloading KDMAPI\");\n                    KDMAPI.TerminateKDMAPIStream();\n                }\n                catch { }\n            }\n        }\n\n        private void NoteSizeStyle_SelectionChanged(object sender, SelectionChangedEventArgs e)\n        {\n            if (settings == null) return;\n            if (noteSizeStyle.SelectedIndex == 0) settings.timeBasedNotes = false;\n            if (noteSizeStyle.SelectedIndex == 1) settings.timeBasedNotes = true;\n        }\n\n        private void IgnoreColorEvents_Checked(object sender, RoutedEventArgs e)\n        {\n            if (settings == null) return;\n            settings.ignoreColorEvents = (bool)ignoreColorEvents.IsChecked;\n        }\n\n        private void UseBGImage_Checked(object sender, RoutedEventArgs e)\n        {\n            try\n            {\n                if (useBGImage.IsChecked && bgImagePath.Text != \"\")\n                {\n                    settings.BGImage = bgImagePath.Text;\n                }\n                else\n                {\n                    settings.BGImage = null;\n                }\n                settings.lastBGChangeTime = DateTime.Now.Ticks;\n            }\n            catch { }\n        }\n\n        private void BrowseBG_Click(object sender, RoutedEventArgs e)\n        {\n            var open = new OpenFileDialog();\n            open.Filter = \"Image files |*.png;*.bmp;*.jpg;*.jpeg\";\n            if ((bool)open.ShowDialog())\n            {\n                bgImagePath.Text = open.FileName;\n                try\n                {\n                    settings.BGImage = bgImagePath.Text;\n                }\n                catch\n                {\n                    settings.BGImage = null;\n                }\n                settings.lastBGChangeTime = DateTime.Now.Ticks;\n            }\n        }\n\n        private void Grid_PreviewKeyDown(object sender, KeyEventArgs e)\n        {\n            if (e.Key == Key.Space) settings.Paused = !settings.Paused;\n        }\n\n        private void ExitButton_Click(object sender, RoutedEventArgs e)\n        {\n            Close();\n        }\n\n        private void MinimiseButton_Click(object sender, RoutedEventArgs e)\n        {\n            try\n            {\n                WindowStyle = WindowStyle.SingleBorderWindow;\n            }\n            catch { }\n            WindowState = WindowState.Minimized;\n        }\n\n        private void tempoMultSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)\n        {\n            if (settings != null) settings.tempoMultiplier = tempoMultSlider.Value;\n        }\n\n        private void updateDownloaded_MouseDown(object sender, MouseButtonEventArgs e)\n        {\n            ZenithUpdates.KillAllProcesses();\n            Process.Start(ZenithUpdates.InstallerPath, \"update -Reopen\");\n            Close();\n        }\n\n        private void StackPanel_DragOver(object sender, DragEventArgs e)\n        {\n\n        }\n    }\n\n    public class CustomTabs : TabControl\n    {\n        public UpdateProgress UpdaterProgress\n        {\n            get { return (UpdateProgress)GetValue(UpdaterProgressProperty); }\n            set { SetValue(UpdaterProgressProperty, value); }\n        }\n\n        public static readonly DependencyProperty UpdaterProgressProperty =\n            DependencyProperty.Register(\"UpdaterProgress\", typeof(UpdateProgress), typeof(CustomTabs), new PropertyMetadata(UpdateProgress.NotDownloading));\n\n\n        public string VersionName\n        {\n            get { return (string)GetValue(VersionNameProperty); }\n            set { SetValue(VersionNameProperty, value); }\n        }\n\n        public static readonly DependencyProperty VersionNameProperty =\n            DependencyProperty.Register(\"VersionName\", typeof(string), typeof(CustomTabs), new PropertyMetadata(\"\"));\n    }\n\n    public class AndValueConverter : IMultiValueConverter\n    {\n        public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)\n        {\n            bool b = true;\n            for (int i = 0; i < values.Length; i++) b = b && (bool)values[i];\n\n            return b;\n        }\n\n        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)\n        {\n            throw new NotImplementedException();\n        }\n    }\n\n    public class OrValueConverter : IMultiValueConverter\n    {\n        public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)\n        {\n            bool b = false;\n            for (int i = 0; i < values.Length; i++) b = b || (bool)values[i];\n\n            return b;\n        }\n\n        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)\n        {\n            throw new NotImplementedException();\n        }\n    }\n\n    public class NotValueConverter : IMultiValueConverter\n    {\n        public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)\n        {\n            return !(bool)values[0];\n        }\n\n        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)\n        {\n            throw new NotImplementedException();\n        }\n    }\n}\n"
  },
  {
    "path": "Black-Midi-Render/OpenTK.dll.config",
    "content": "<configuration>\n  <dllmap os=\"linux\" dll=\"opengl32.dll\" target=\"libGL.so.1\"/>\n  <dllmap os=\"linux\" dll=\"glu32.dll\" target=\"libGLU.so.1\"/>\n  <dllmap os=\"linux\" dll=\"openal32.dll\" target=\"libopenal.so.1\"/>\n  <dllmap os=\"linux\" dll=\"alut.dll\" target=\"libalut.so.0\"/>\n  <dllmap os=\"linux\" dll=\"opencl.dll\" target=\"libOpenCL.so\"/>\n  <dllmap os=\"linux\" dll=\"libX11\" target=\"libX11.so.6\"/>\n  <dllmap os=\"linux\" dll=\"libXi\" target=\"libXi.so.6\"/>\n  <dllmap os=\"linux\" dll=\"SDL2.dll\" target=\"libSDL2-2.0.so.0\"/>\n  <dllmap os=\"osx\" dll=\"opengl32.dll\" target=\"/System/Library/Frameworks/OpenGL.framework/OpenGL\"/>\n  <dllmap os=\"osx\" dll=\"openal32.dll\" target=\"/System/Library/Frameworks/OpenAL.framework/OpenAL\" />\n  <dllmap os=\"osx\" dll=\"alut.dll\" target=\"/System/Library/Frameworks/OpenAL.framework/OpenAL\" />\n  <dllmap os=\"osx\" dll=\"libGLES.dll\" target=\"/System/Library/Frameworks/OpenGLES.framework/OpenGLES\" />\n  <dllmap os=\"osx\" dll=\"libGLESv1_CM.dll\" target=\"/System/Library/Frameworks/OpenGLES.framework/OpenGLES\" />\n  <dllmap os=\"osx\" dll=\"libGLESv2.dll\" target=\"/System/Library/Frameworks/OpenGLES.framework/OpenGLES\" />\n  <dllmap os=\"osx\" dll=\"opencl.dll\" target=\"/System/Library/Frameworks/OpenCL.framework/OpenCL\"/>\n  <dllmap os=\"osx\" dll=\"SDL2.dll\" target=\"libSDL2.dylib\"/>\n  <!-- XQuartz compatibility (X11 on Mac) -->\n  <dllmap os=\"osx\" dll=\"libGL.so.1\" target=\"/usr/X11/lib/libGL.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libX11\" target=\"/usr/X11/lib/libX11.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXcursor.so.1\" target=\"/usr/X11/lib/libXcursor.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXi\" target=\"/usr/X11/lib/libXi.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXinerama\" target=\"/usr/X11/lib/libXinerama.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXrandr.so.2\" target=\"/usr/X11/lib/libXrandr.dylib\"/>\n</configuration>\n"
  },
  {
    "path": "Black-Midi-Render/Program.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Reflection;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing ZenithShared;\n\nnamespace Zenith_MIDI\n{\n    class Program\n    {\n        [STAThread]\n        static void Main(string[] args)\n        {\n#if !DEBUG\n            try\n            {\n#endif\n            Console.Title = \"Zenith\";\n            Application app = new Application();\n            app.Run(new MainWindow());\n#if !DEBUG\n            }\n            catch (Exception e)\n            {\n                string msg = e.Message + \"\\n\" + e.Data + \"\\n\";\n                msg += e.StackTrace;\n                MessageBox.Show(msg, \"Zenith has crashed!\");\n            }\n#endif\n        }\n    }\n}\n"
  },
  {
    "path": "Black-Midi-Render/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// General Information about an assembly is controlled through the following\n// set of attributes. Change these attribute values to modify the information\n// associated with an assembly.\n[assembly: AssemblyTitle(\"Zenith\")]\n[assembly: AssemblyDescription(\"\")]\n[assembly: AssemblyConfiguration(\"\")]\n[assembly: AssemblyCompany(\"\")]\n[assembly: AssemblyProduct(\"Zenith\")]\n[assembly: AssemblyCopyright(\"Copyright ©  2019\")]\n[assembly: AssemblyTrademark(\"\")]\n[assembly: AssemblyCulture(\"\")]\n\n// Setting ComVisible to false makes the types in this assembly not visible\n// to COM components.  If you need to access a type in this assembly from\n// COM, set the ComVisible attribute to true on that type.\n[assembly: ComVisible(false)]\n\n// The following GUID is for the ID of the typelib if this project is exposed to COM\n[assembly: Guid(\"273da7bd-c89a-4574-97de-af425529faae\")]\n\n// Version information for an assembly consists of the following four values:\n//\n//      Major Version\n//      Minor Version\n//      Build Number\n//      Revision\n//\n// You can specify all the values or you can default the Build and Revision Numbers\n// by using the '*' as shown below:\n// [assembly: AssemblyVersion(\"1.0.*\")]\n[assembly: AssemblyVersion(\"1.0.0.0\")]\n[assembly: AssemblyFileVersion(\"1.0.0.0\")]\n"
  },
  {
    "path": "Black-Midi-Render/Properties/Resources.Designer.cs",
    "content": "﻿//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace Zenith_MIDI.Properties {\n    using System;\n    \n    \n    /// <summary>\n    ///   A strongly-typed resource class, for looking up localized strings, etc.\n    /// </summary>\n    // This class was auto-generated by the StronglyTypedResourceBuilder\n    // class via a tool like ResGen or Visual Studio.\n    // To add or remove a member, edit your .ResX file then rerun ResGen\n    // with the /str option, or rebuild your VS project.\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"System.Resources.Tools.StronglyTypedResourceBuilder\", \"15.0.0.0\")]\n    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]\n    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]\n    internal class Resources {\n        \n        private static global::System.Resources.ResourceManager resourceMan;\n        \n        private static global::System.Globalization.CultureInfo resourceCulture;\n        \n        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute(\"Microsoft.Performance\", \"CA1811:AvoidUncalledPrivateCode\")]\n        internal Resources() {\n        }\n        \n        /// <summary>\n        ///   Returns the cached ResourceManager instance used by this class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Resources.ResourceManager ResourceManager {\n            get {\n                if (object.ReferenceEquals(resourceMan, null)) {\n                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager(\"Zenith_MIDI.Properties.Resources\", typeof(Resources).Assembly);\n                    resourceMan = temp;\n                }\n                return resourceMan;\n            }\n        }\n        \n        /// <summary>\n        ///   Overrides the current thread's CurrentUICulture property for all\n        ///   resource lookups using this strongly typed resource class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Globalization.CultureInfo Culture {\n            get {\n                return resourceCulture;\n            }\n            set {\n                resourceCulture = value;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Black-Midi-Render/Properties/Resources.resx",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The primary goals of this format is to allow a simple XML format \n    that is mostly human readable. The generation and parsing of the \n    various data types are done through the TypeConverter classes \n    associated with the data types.\n    \n    Example:\n    \n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n                \n    There are any number of \"resheader\" rows that contain simple \n    name/value pairs.\n    \n    Each data row contains a name, and value. The row also contains a \n    type or mimetype. Type corresponds to a .NET class that support \n    text/value conversion through the TypeConverter architecture. \n    Classes that don't support this are serialized and stored with the \n    mimetype set.\n    \n    The mimetype is used for serialized objects, and tells the \n    ResXResourceReader how to depersist the object. This is currently not \n    extensible. For a given mimetype the value must be set accordingly:\n    \n    Note - application/x-microsoft.net.object.binary.base64 is the format \n    that the ResXResourceWriter will generate, however the reader can \n    read any of the formats listed below.\n    \n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n    \n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array \n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n</root>"
  },
  {
    "path": "Black-Midi-Render/RenderWindow.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Diagnostics;\nusing System.Drawing;\nusing System.IO;\nusing System.IO.Pipes;\nusing System.Linq;\nusing System.Reflection;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing System.Windows.Forms;\nusing ZenithEngine;\nusing OpenTK;\nusing OpenTK.Graphics;\nusing OpenTK.Graphics.OpenGL;\nusing OpenTK.Input;\nusing System.Drawing.Drawing2D;\nusing System.Drawing.Text;\nusing System.Text.RegularExpressions;\nusing System.Drawing.Imaging;\nusing PixelFormat = OpenTK.Graphics.OpenGL.PixelFormat;\n\nnamespace Zenith_MIDI\n{\n    interface INoteRender : IDisposable\n    {\n        long Render(FastList<Note> notes, double midiTime);\n    }\n\n    interface IKeyboardRender : IDisposable\n    {\n        void Render();\n    }\n\n    class RenderWindow : GameWindow\n    {\n        #region Shaders\n        string postShaderVert = @\"#version 330 compatibility\n\nin vec3 position;\nout vec2 UV;\n\nvoid main()\n{\n    gl_Position = vec4(position.x * 2 - 1, position.y * 2 - 1, position.z * 2 - 1, 1.0f);\n\t//color = glColor;\n    UV = vec2(position.x, position.y);\n}\n\";\n        string postShaderFlipVert = @\"#version 330 compatibility\n\nin vec3 position;\nout vec2 UV;\n\nvoid main()\n{\n    gl_Position = vec4(position.x * 2 - 1, -(position.y * 2 - 1), position.z * 2 - 1, 1.0f);\n\t//color = glColor;\n    UV = vec2(position.x, position.y);\n}\n\";\n        string postShaderFrag = @\"#version 330 compatibility\n\nin vec2 UV;\n\nout vec4 color;\n\nuniform sampler2D TextureSampler;\n\nvoid main()\n{\n    color = texture2D( TextureSampler, UV );\n    color.a = sqrt(color.a);\n    color.rgb /= color.a;\n}\n\";\n        string postShaderFragDownscale = @\"#version 330 compatibility\n\nin vec2 UV;\n\nout vec4 color;\n\nuniform sampler2D TextureSampler;\nuniform vec2 res;\nuniform int factor;\n\nvoid main()\n{\n    color = vec4(0, 0, 0, 0);\n    float stepX = 1 / res.x / factor;\n    float stepY = 1 / res.y / factor;\n    for(int i = 0; i < factor; i += 1){\n        for(int j = 0; j < factor; j += 1){\n            color += texture2D(TextureSampler, UV + vec2(i * stepX, j * stepY));\n        }\n    }\n    color /= factor * factor;\n}\n\";\n\n        string postShaderFragAlphaMask = @\"#version 330 compatibility\n\nin vec2 UV;\n\nout vec4 color;\n\nuniform sampler2D myTextureSampler;\n\nvoid main()\n{\n    color = texture2D( myTextureSampler, UV );\n    color.x = color.w;\n    color.y = color.w;\n    color.z = color.w;\n    color.w = 1;\n}\n\";\n        string postShaderFragAlphaMaskColor = @\"#version 330 compatibility\n\nin vec2 UV;\n\nout vec4 color;\n\nuniform sampler2D myTextureSampler;\n\nvoid main()\n{\n    color = texture2D( myTextureSampler, UV );\n    color.x /= color.w;\n    color.y /= color.w;\n    color.z /= color.w;\n    color.w = 1;\n}\n\";\n        string postShaderFragBlackFill = @\"#version 330 compatibility\n\nin vec2 UV;\n\nout vec4 color;\n\nuniform sampler2D myTextureSampler;\n\nvoid main()\n{\n    color = vec4( 0,0,0,1 );\n}\n\";\n\n\n        int MakeShader(string vert, string frag)\n        {\n            int _vertexObj = GL.CreateShader(ShaderType.VertexShader);\n            int _fragObj = GL.CreateShader(ShaderType.FragmentShader);\n            int statusCode;\n            string info;\n\n            GL.ShaderSource(_vertexObj, vert);\n            GL.CompileShader(_vertexObj);\n            info = GL.GetShaderInfoLog(_vertexObj);\n            GL.GetShader(_vertexObj, ShaderParameter.CompileStatus, out statusCode);\n            if (statusCode != 1) throw new ApplicationException(info);\n\n            GL.ShaderSource(_fragObj, frag);\n            GL.CompileShader(_fragObj);\n            info = GL.GetShaderInfoLog(_fragObj);\n            GL.GetShader(_fragObj, ShaderParameter.CompileStatus, out statusCode);\n            if (statusCode != 1) throw new ApplicationException(info);\n\n            int shader = GL.CreateProgram();\n            GL.AttachShader(shader, _fragObj);\n            GL.AttachShader(shader, _vertexObj);\n            GL.LinkProgram(shader);\n            return shader;\n        }\n        #endregion\n\n        void loadImage(Bitmap image, int texID, bool loop, bool linear)\n        {\n            GL.BindTexture(TextureTarget.Texture2D, texID);\n            BitmapData data = image.LockBits(new System.Drawing.Rectangle(0, 0, image.Width, image.Height),\n                ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);\n\n            GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, data.Width, data.Height, 0,\n                OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);\n\n            if (linear)\n            {\n                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);\n                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);\n            }\n            else\n            {\n                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest);\n                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest);\n            }\n            if (loop)\n            {\n                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);\n                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);\n            }\n            else\n            {\n                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge);\n                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge);\n            }\n\n            image.UnlockBits(data);\n        }\n\n        FastList<Note> globalDisplayNotes;\n        FastList<Tempo> globalTempoEvents;\n        FastList<ColorChange> globalColorEvents;\n        FastList<PlaybackEvent> globalPlaybackEvents;\n        MidiFile midi;\n\n        RenderSettings settings;\n\n        public double midiTime = 0;\n        public long frameStartTime = 0;\n        public double tempoFrameStep = 10;\n\n        Process ffmpegvideo = new Process();\n        Process ffmpegmask = new Process();\n        Task lastRenderPush = null;\n        Task lastRenderPushMask = null;\n\n        GLPostbuffer finalCompositeBuff;\n        GLPostbuffer downscaleBuff;\n        GLPostbuffer ffmpegOutputBuff;\n\n        int postShader;\n        int postShaderFlip;\n        int postShaderDownscale;\n        int postShaderMask;\n        int postShaderMaskColor;\n        int postShaderBlackFill;\n\n        int uDownscaleRes;\n        int uDownscaleFac;\n\n        int screenQuadBuffer;\n        int screenQuadIndexBuffer;\n        double[] screenQuadArray = new double[] { 0, 0, 0, 1, 1, 1, 1, 0 };\n        int[] screenQuadArrayIndex = new int[] { 0, 1, 2, 3 };\n\n        byte[] pixels;\n        byte[] pixelsmask;\n\n        Process startNewFF(string path)\n        {\n            Process ffmpeg = new Process();\n            string args = \"-hide_banner\";\n            if (settings.includeAudio)\n            {\n                double fstep = ((double)midi.division / lastTempo) * (1000000 / settings.fps);\n                double offset = -midiTime / fstep / settings.fps;\n                offset = Math.Round(offset * 100) / 100;\n                args = \"\" +\n                    \" -f rawvideo -s \" + settings.width / settings.downscale + \"x\" + settings.height / settings.downscale +\n                    \" -pix_fmt rgb32 -r \" + settings.fps + \" -i -\" +\n                    \" -itsoffset \" + offset.ToString().Replace(\",\", \".\") + \" -i \\\"\" + settings.audioPath + \"\\\"\" + \" -vf vflip -pix_fmt yuv420p \";\n                args += settings.CustomFFmpeg ? \"\" : \"-vcodec libx264 -acodec aac\";\n            }\n            else\n            {\n                args = \"\" +\n                    \" -f rawvideo -s \" + settings.width / settings.downscale + \"x\" + settings.height / settings.downscale +\n                    \" -strict -2\" +\n                    \" -pix_fmt rgb32 -r \" + settings.fps + \" -i -\" +\n                    \" -vf vflip -pix_fmt yuv420p \";\n                args += settings.CustomFFmpeg ? \"\" : \"-vcodec libx264\";\n            }\n            if (settings.useBitrate)\n            {\n                args += \" -b:v \" + settings.bitrate + \"k\" +\n                    \" -maxrate \" + settings.bitrate + \"k\" +\n                    \" -minrate \" + settings.bitrate + \"k\";\n            }\n            else if (settings.CustomFFmpeg)\n            {\n                args += settings.ffoption;\n            }\n            else\n            {\n                args += \" -preset \" + settings.crfPreset + \" -crf \" + settings.crf;\n            }\n            args += \" -y \\\"\" + path + \"\\\"\";\n            ffmpeg.StartInfo = new ProcessStartInfo(\"ffmpeg\", args);\n            ffmpeg.StartInfo.RedirectStandardInput = true;\n            ffmpeg.StartInfo.UseShellExecute = false;\n            ffmpeg.StartInfo.RedirectStandardError = !settings.ffmpegDebug;\n            try\n            {\n                ffmpeg.Start();\n                if (!settings.ffmpegDebug)\n                {\n                    Console.OpenStandardOutput();\n                    Regex messageMatch = new Regex(\"\\\\[.*@.*\\\\]\");\n                    ffmpeg.ErrorDataReceived += (s, e) =>\n                    {\n                        if (e.Data == null) return;\n                        if (e.Data.Contains(\"frame=\"))\n                        {\n                            Console.Write(e.Data);\n                            Console.SetCursorPosition(0, Console.CursorTop);\n                        }\n                        if (e.Data.Contains(\"Conversion failed!\"))\n                        {\n                            Console.ForegroundColor = ConsoleColor.Red;\n                            Console.WriteLine(\"An error occured in FFMPEG, closing!\");\n                            Console.ResetColor();\n                            settings.running = false;\n                        }\n                        if (messageMatch.IsMatch(e.Data))\n                        {\n                            Console.WriteLine(e.Data);\n                        }\n                    };\n                    ffmpeg.BeginErrorReadLine();\n                }\n            }\n            catch (Exception ex)\n            {\n                MessageBox.Show(\"There was an error starting the ffmpeg process\\nNo video will be written\\n(Is ffmpeg.exe in the same folder as this program?)\\n\\n\\\"\" + ex.Message + \"\\\"\");\n                settings.ffRender = false;\n            }\n            return ffmpeg;\n        }\n\n        protected override void OnResize(EventArgs e)\n        {\n            base.OnResize(e);\n        }\n\n        CurrentRendererPointer render;\n        GLTextEngine textEngine;\n        public RenderWindow(CurrentRendererPointer renderer, MidiFile midi, RenderSettings settings) : base(16, 9, new GraphicsMode(new ColorFormat(8, 8, 8, 8)), \"Render\", GameWindowFlags.Default, DisplayDevice.Default)\n        {\n            Width = (int)(DisplayDevice.Default.Width / 1.5);\n            Height = (int)((double)Width / settings.width * settings.height);\n            Location = new Point((DisplayDevice.Default.Width - Width) / 2, (DisplayDevice.Default.Height - Height) / 2);\n            //textEngine = new GLTextEngine();\n            render = renderer;\n            this.settings = settings;\n            lastTempo = midi.zerothTempo;\n            lock (render)\n            {\n                render.renderer.Tempo = 60000000.0 / midi.zerothTempo;\n                midiTime = -render.renderer.NoteScreenTime;\n                if (settings.timeBasedNotes) tempoFrameStep = 1000.0 / settings.fps;\n                else tempoFrameStep = ((double)midi.division / lastTempo) * (1000000 / settings.fps);\n                midiTime -= tempoFrameStep * settings.renderSecondsDelay * settings.fps;\n            }\n\n            globalDisplayNotes = midi.globalDisplayNotes;\n            globalTempoEvents = midi.globalTempoEvents;\n            globalColorEvents = midi.globalColorEvents;\n            globalPlaybackEvents = midi.globalPlaybackEvents;\n            this.midi = midi;\n            if (settings.ffRender)\n            {\n                pixels = new byte[settings.width * settings.height * 4 / settings.downscale / settings.downscale];\n                ffmpegvideo = startNewFF(settings.ffPath);\n                if (settings.ffRenderMask)\n                {\n                    pixelsmask = new byte[settings.width * settings.height * 4 / settings.downscale / settings.downscale];\n                    ffmpegmask = startNewFF(settings.ffMaskPath);\n                }\n            }\n\n            finalCompositeBuff = new GLPostbuffer(settings.width, settings.height);\n            ffmpegOutputBuff = new GLPostbuffer(settings.width / settings.downscale, settings.height / settings.downscale);\n            downscaleBuff = new GLPostbuffer(settings.width / settings.downscale, settings.height / settings.downscale);\n\n            GL.GenBuffers(1, out screenQuadBuffer);\n            GL.GenBuffers(1, out screenQuadIndexBuffer);\n\n            GL.BindBuffer(BufferTarget.ArrayBuffer, screenQuadBuffer);\n            GL.BufferData(\n                BufferTarget.ArrayBuffer,\n                (IntPtr)(screenQuadArray.Length * 8),\n                screenQuadArray,\n                BufferUsageHint.StaticDraw);\n\n            GL.BindBuffer(BufferTarget.ElementArrayBuffer, screenQuadIndexBuffer);\n            GL.BufferData(\n                BufferTarget.ElementArrayBuffer,\n                (IntPtr)(screenQuadArrayIndex.Length * 4),\n                screenQuadArrayIndex,\n                BufferUsageHint.StaticDraw);\n\n            postShader = MakeShader(postShaderVert, postShaderFrag);\n            postShaderFlip = MakeShader(postShaderFlipVert, postShaderFrag);\n            postShaderMask = MakeShader(postShaderVert, postShaderFragAlphaMask);\n            postShaderMaskColor = MakeShader(postShaderVert, postShaderFragAlphaMaskColor);\n            postShaderDownscale = MakeShader(postShaderVert, postShaderFragDownscale);\n            postShaderBlackFill = MakeShader(postShaderVert, postShaderFragBlackFill);\n\n            uDownscaleRes = GL.GetUniformLocation(postShaderDownscale, \"res\");\n            uDownscaleFac = GL.GetUniformLocation(postShaderDownscale, \"factor\");\n        }\n\n        double microsecondsPerTick = 0;\n        bool playbackLoopStarted = false;\n        void PlaybackLoop()\n        {\n            PlaybackEvent pe;\n            int timeJump;\n            long now;\n            playbackLoopStarted = true;\n            if (settings.ffRender) return;\n            if (settings.Paused || !settings.playbackEnabled)\n            {\n                SpinWait.SpinUntil(() => !(settings.Paused || !settings.playbackEnabled));\n            }\n            KDMAPI.ResetKDMAPIStream();\n            KDMAPI.SendDirectData(0x0);\n            while (settings.running)\n            {\n                if (settings.Paused || !settings.playbackEnabled)\n                {\n                    SpinWait.SpinUntil(() => !(settings.Paused || !settings.playbackEnabled));\n                }\n                try\n                {\n                    if (globalPlaybackEvents.ZeroLen) continue;\n                    pe = globalPlaybackEvents.Pop();\n                    now = DateTime.Now.Ticks;\n                    if (now - 10000000 > frameStartTime)\n                    {\n                        SpinWait.SpinUntil(() => now - 10000000 < frameStartTime);\n                    }\n                    timeJump = (int)(((pe.pos - midiTime) * microsecondsPerTick / settings.tempoMultiplier - now + frameStartTime) / 10000);\n                    if (timeJump < -1000)\n                        continue;\n                    if (timeJump > 0)\n                        Thread.Sleep(timeJump);\n                    if (settings.playSound && settings.playbackEnabled)\n                        try\n                        {\n                            KDMAPI.SendDirectData((uint)pe.val);\n                        }\n                        catch { continue; }\n                }\n                catch { continue; }\n            }\n            KDMAPI.ResetKDMAPIStream();\n            KDMAPI.SendDirectData(0x0);\n        }\n\n        double lastTempo;\n        public double lastDeltaTimeOnScreen = 0;\n        public double lastMV = 1;\n\n        int bgTexID = -1;\n        long lastBGChangeTime = -1;\n        protected override void OnRenderFrame(FrameEventArgs e)\n        {\n            Task.Factory.StartNew(() => PlaybackLoop(), TaskCreationOptions.LongRunning);\n            SpinWait.SpinUntil(() => playbackLoopStarted);\n            Stopwatch watch = new Stopwatch();\n            watch.Start();\n            if (!settings.timeBasedNotes) tempoFrameStep = ((double)midi.division / lastTempo) * (1000000.0 / settings.fps);\n            lock (render)\n            {\n                lastDeltaTimeOnScreen = render.renderer.NoteScreenTime;\n                render.renderer.CurrentMidi = midi.info;\n            }\n            int noNoteFrames = 0;\n            long lastNC = 0;\n            bool firstRenderer = true;\n            frameStartTime = DateTime.Now.Ticks;\n            if (settings.timeBasedNotes) microsecondsPerTick = 10000;\n            else microsecondsPerTick = (long)((double)lastTempo / midi.division * 10);\n            while (settings.running && (noNoteFrames < settings.fps * 5 || midi.unendedTracks != 0))\n            {\n                if (!settings.Paused || settings.forceReRender)\n                {\n                    if (settings.lastBGChangeTime != lastBGChangeTime)\n                    {\n                        if (settings.BGImage == null)\n                        {\n                            if (bgTexID != -1) GL.DeleteTexture(bgTexID);\n                            bgTexID = -1;\n                        }\n                        else\n                        {\n                            if (bgTexID == -1) bgTexID = GL.GenTexture();\n                            try\n                            {\n                                loadImage(new Bitmap(settings.BGImage), bgTexID, false, true);\n                            }\n                            catch\n                            {\n                                MessageBox.Show(\"Couldn't load image\");\n                                if (bgTexID != -1) GL.DeleteTexture(bgTexID);\n                                bgTexID = -1;\n                            }\n                        }\n                        lastBGChangeTime = settings.lastBGChangeTime;\n                    }\n\n                    lock (render)\n                    {\n                        try\n                        {\n                            if (render.disposeQueue.Count != 0)\n                                try\n                                {\n                                    while (true)\n                                    {\n                                        var r = render.disposeQueue.Dequeue();\n                                        if (r.Initialized)\n                                        {\n                                            try\n                                            {\n                                                r.Dispose();\n                                            }\n                                            catch { }\n                                            GC.Collect();\n                                        }\n                                    }\n                                }\n                                catch (InvalidOperationException) { }\n                            if (!render.renderer.Initialized)\n                            {\n                                render.renderer.Init();\n                                render.renderer.NoteColors = midi.tracks.Select(t => t.trkColors).ToArray();\n                                render.renderer.ReloadTrackColors();\n                                if (firstRenderer)\n                                {\n                                    firstRenderer = false;\n                                    midi.SetZeroColors();\n                                }\n                                render.renderer.CurrentMidi = midi.info;\n                                lock (globalDisplayNotes)\n                                {\n                                    foreach (Note n in globalDisplayNotes)\n                                    {\n                                        n.meta = null;\n                                    }\n                                }\n                            }\n                            render.renderer.Tempo = 60000000.0 / lastTempo;\n                            lastDeltaTimeOnScreen = render.renderer.NoteScreenTime;\n                            if (settings.timeBasedNotes)\n                                SpinWait.SpinUntil(() => (midi.currentFlexSyncTime > midiTime + lastDeltaTimeOnScreen + tempoFrameStep * settings.tempoMultiplier || midi.unendedTracks == 0) || !settings.running);\n                            else\n                                SpinWait.SpinUntil(() => (midi.currentSyncTime > midiTime + lastDeltaTimeOnScreen + tempoFrameStep * settings.tempoMultiplier || midi.unendedTracks == 0) || !settings.running);\n                            if (!settings.running) break;\n\n                            render.renderer.RenderFrame(globalDisplayNotes, midiTime, finalCompositeBuff.BufferID);\n                            lastNC = render.renderer.LastNoteCount;\n                            if (lastNC == 0 && midi.unendedTracks == 0) noNoteFrames++;\n                            else noNoteFrames = 0;\n                        }\n                        catch (Exception ex)\n                        {\n                            MessageBox.Show(\"The renderer has crashed\\n\" + ex.Message + \"\\n\" + ex.StackTrace);\n                            break;\n                        }\n                    }\n                }\n                double mv = 1;\n                if (settings.realtimePlayback)\n                {\n                    mv = (DateTime.Now.Ticks - frameStartTime) / microsecondsPerTick / tempoFrameStep;\n                    if (mv > settings.fps / 4)\n                        mv = settings.fps / 4;\n                }\n                lastMV = mv;\n                if (!settings.Paused)\n                {\n                    lock (globalTempoEvents)\n                    {\n                        while (globalTempoEvents.First != null && midiTime + (tempoFrameStep * mv * settings.tempoMultiplier) > globalTempoEvents.First.pos)\n                        {\n                            var t = globalTempoEvents.Pop();\n                            if (t.tempo == 0)\n                            {\n                                Console.WriteLine(\"Zero tempo event encountered, ignoring\");\n                                continue;\n                            }\n                            var _t = ((t.pos) - midiTime) / (tempoFrameStep * mv * settings.tempoMultiplier);\n                            mv *= 1 - _t;\n                            if (!settings.timeBasedNotes) tempoFrameStep = ((double)midi.division / t.tempo) * (1000000.0 / settings.fps);\n                            lastTempo = t.tempo;\n                            midiTime = t.pos;\n                        }\n                    }\n                    midiTime += mv * tempoFrameStep * settings.tempoMultiplier;\n                }\n                frameStartTime = DateTime.Now.Ticks;\n                if (settings.timeBasedNotes) microsecondsPerTick = 10000;\n                else microsecondsPerTick = (long)(lastTempo / midi.division * 10);\n\n                while (globalColorEvents.First != null && globalColorEvents.First.pos < midiTime)\n                {\n                    var c = globalColorEvents.Pop();\n                    var track = c.track;\n                    if (!settings.ignoreColorEvents)\n                    {\n                        if (c.channel == 0x7F)\n                        {\n                            for (int i = 0; i < 16; i++)\n                            {\n                                c.track.trkColors[i].left = c.col1;\n                                c.track.trkColors[i].right = c.col2;\n                                c.track.trkColors[i].isDefault = false;\n                            }\n                        }\n                        else\n                        {\n                            c.track.trkColors[c.channel].left = c.col1;\n                            c.track.trkColors[c.channel].right = c.col2;\n                            c.track.trkColors[c.channel].isDefault = false;\n                        }\n                    }\n                }\n\n                downscaleBuff.BindBuffer();\n                GL.Clear(ClearBufferMask.ColorBufferBit);\n                GL.Viewport(0, 0, settings.width / settings.downscale, settings.height / settings.downscale);\n                if (bgTexID != -1)\n                {\n                    GL.UseProgram(postShaderFlip);\n                    GL.BindTexture(TextureTarget.Texture2D, bgTexID);\n                    DrawScreenQuad();\n                }\n\n                if (settings.downscale > 1)\n                {\n                    GL.UseProgram(postShaderDownscale);\n                    GL.Uniform1(uDownscaleFac, (int)settings.downscale);\n                    GL.Uniform2(uDownscaleRes, new Vector2(settings.width / settings.downscale, settings.height / settings.downscale));\n                }\n                else\n                {\n                    GL.UseProgram(postShader);\n                }\n\n                finalCompositeBuff.BindTexture();\n                DrawScreenQuad();\n\n                if (settings.ffRender)\n                {\n                    if (ffmpegvideo.HasExited || (settings.ffRenderMask && ffmpegmask.HasExited))\n                    {\n                        Console.ForegroundColor = ConsoleColor.Red;\n                        Console.WriteLine(\"FFMPEG process closed unexpectedly!\");\n                        Console.WriteLine(\"Use 'ffmpeg debug' for more advanced info.\");\n                        Console.ResetColor();\n                        settings.running = false;\n                    }\n\n                    if (!settings.ffRenderMask)\n                        GL.UseProgram(postShader);\n                    else\n                        GL.UseProgram(postShaderMaskColor);\n                    finalCompositeBuff.BindTexture();\n                    ffmpegOutputBuff.BindBuffer();\n                    GL.Clear(ClearBufferMask.ColorBufferBit);\n                    GL.Viewport(0, 0, settings.width / settings.downscale, settings.height / settings.downscale);\n                    downscaleBuff.BindTexture();\n                    DrawScreenQuad();\n                    IntPtr unmanagedPointer = Marshal.AllocHGlobal(pixels.Length);\n                    GL.ReadPixels(0, 0, settings.width / settings.downscale, settings.height / settings.downscale, PixelFormat.Bgra, PixelType.UnsignedByte, unmanagedPointer);\n                    Marshal.Copy(unmanagedPointer, pixels, 0, pixels.Length);\n\n                    if (lastRenderPush != null) lastRenderPush.GetAwaiter().GetResult();\n                    lastRenderPush = Task.Run(() =>\n                    {\n                        ffmpegvideo.StandardInput.BaseStream.Write(pixels, 0, pixels.Length);\n                    });\n                    Marshal.FreeHGlobal(unmanagedPointer);\n\n                    if (settings.ffRenderMask)\n                    {\n                        if (lastRenderPushMask != null) lastRenderPushMask.GetAwaiter().GetResult();\n                        GL.UseProgram(postShaderMask);\n                        ffmpegOutputBuff.BindBuffer();\n                        GL.Clear(ClearBufferMask.ColorBufferBit);\n                        GL.Viewport(0, 0, settings.width / settings.downscale, settings.height / settings.downscale);\n                        downscaleBuff.BindTexture();\n                        DrawScreenQuad();\n                        unmanagedPointer = Marshal.AllocHGlobal(pixelsmask.Length);\n                        GL.ReadPixels(0, 0, settings.width / settings.downscale, settings.height / settings.downscale, PixelFormat.Bgra, PixelType.UnsignedByte, unmanagedPointer);\n                        Marshal.Copy(unmanagedPointer, pixelsmask, 0, pixelsmask.Length);\n\n                        if (lastRenderPush != null) lastRenderPush.GetAwaiter().GetResult();\n                        lastRenderPush = Task.Run(() =>\n                        {\n                            ffmpegmask.StandardInput.BaseStream.Write(pixelsmask, 0, pixelsmask.Length);\n                        });\n                        Marshal.FreeHGlobal(unmanagedPointer);\n                    }\n                }\n\n                GLPostbuffer.UnbindBuffers();\n                GL.Clear(ClearBufferMask.ColorBufferBit);\n                GL.UseProgram(postShaderBlackFill);\n                DrawScreenQuad();\n                GL.UseProgram(postShader);\n                GL.Viewport(0, 0, Width, Height);\n                downscaleBuff.BindTexture();\n                DrawScreenQuad();\n                GLPostbuffer.UnbindTextures();\n                if (settings.ffRender) VSync = VSyncMode.Off;\n                else if (settings.vsync) VSync = VSyncMode.On;\n                else VSync = VSyncMode.Off;\n                try\n                {\n                    SwapBuffers();\n                }\n                catch\n                {\n                    break;\n                }\n                ProcessEvents();\n                double fr = 10000000.0 / watch.ElapsedTicks;\n                settings.liveFps = (settings.liveFps * 2 + fr) / 3;\n                watch.Reset();\n                watch.Start();\n            }\n            Console.WriteLine(\"Left render loop\");\n            settings.running = false;\n            if (settings.ffRender)\n            {\n                if (lastRenderPush != null) lastRenderPush.GetAwaiter().GetResult();\n                ffmpegvideo.StandardInput.Close();\n                ffmpegvideo.Close();\n                if (settings.ffRenderMask)\n                {\n                    if (lastRenderPushMask != null) lastRenderPushMask.GetAwaiter().GetResult();\n                    ffmpegmask.StandardInput.Close();\n                    ffmpegmask.Close();\n                }\n            }\n            Console.WriteLine(\"Disposing current renderer\");\n            try\n            {\n                render.renderer.Dispose();\n            }\n            catch { }\n            try\n            {\n                Console.WriteLine(\"Disposing of other renderers\");\n                while (render.disposeQueue.Count != 0)\n                {\n                    var r = render.disposeQueue.Dequeue();\n                    try\n                    {\n                        if (r.Initialized) r.Dispose();\n                    }\n                    catch { }\n                }\n            }\n            catch (InvalidOperationException) { }\n            Console.WriteLine(\"Disposed of renderers\");\n\n            globalDisplayNotes = null;\n            globalTempoEvents = null;\n            globalColorEvents = null;\n            pixels = null;\n            pixelsmask = null;\n            if (settings.ffRender)\n            {\n                ffmpegvideo.Dispose();\n                if (settings.ffRenderMask) ffmpegmask.Dispose();\n            }\n            ffmpegvideo = null;\n            ffmpegmask = null;\n\n\n            finalCompositeBuff.Dispose();\n            ffmpegOutputBuff.Dispose();\n            downscaleBuff.Dispose();\n            finalCompositeBuff = null;\n            ffmpegOutputBuff = null;\n            downscaleBuff = null;\n\n            GL.DeleteBuffers(2, new int[] { screenQuadBuffer, screenQuadIndexBuffer });\n\n            GL.DeleteProgram(postShader);\n            GL.DeleteProgram(postShaderMask);\n            GL.DeleteProgram(postShaderMaskColor);\n            GL.DeleteProgram(postShaderDownscale);\n\n            midi = null;\n            render = null;\n            Console.WriteLine(\"Closing window\");\n\n            this.Close();\n        }\n\n        protected override void OnKeyDown(KeyboardKeyEventArgs e)\n        {\n            base.OnKeyDown(e);\n            if (e.Key == Key.Space && !settings.ffRender) settings.Paused = !settings.Paused;\n            if (e.Key == Key.Right && !settings.ffRender)\n            {\n                int skip = 5000;\n                if (e.Modifiers == KeyModifiers.Control) skip = 20000;\n                if (e.Modifiers == KeyModifiers.Shift) skip = 60000;\n                if (settings.timeBasedNotes) midiTime += skip;\n                else\n                {\n                    lock (midi)\n                    {\n                        double timeSkipped = 0;\n                        for (; timeSkipped < skip; midiTime++)\n                        {\n                            midi.ParseUpTo(midiTime);\n                            timeSkipped += 1 / midi.tempoTickMultiplier;\n                        }\n                    }\n                }\n            }\n            if (e.Key == Key.Enter)\n            {\n                if (WindowState != WindowState.Fullscreen)\n                    WindowState = WindowState.Fullscreen;\n                else\n                    WindowState = WindowState.Normal;\n            }\n        }\n\n        protected override void OnClosing(CancelEventArgs e)\n        {\n            base.OnClosing(e);\n\n            settings.running = false;\n        }\n\n        protected override void OnClosed(EventArgs e)\n        {\n            base.OnClosed(e);\n\n            settings.running = false;\n        }\n\n        void DrawScreenQuad()\n        {\n            GL.Enable(EnableCap.Blend);\n            GL.EnableClientState(ArrayCap.VertexArray);\n            GL.Enable(EnableCap.Texture2D);\n            GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);\n\n            GL.BindBuffer(BufferTarget.ArrayBuffer, screenQuadBuffer);\n            GL.VertexPointer(2, VertexPointerType.Double, 16, 0);\n\n            GL.BindBuffer(BufferTarget.ElementArrayBuffer, screenQuadIndexBuffer);\n            GL.IndexPointer(IndexPointerType.Int, 1, 0);\n            GL.DrawElements(PrimitiveType.Quads, 4, DrawElementsType.UnsignedInt, IntPtr.Zero);\n\n            GL.Disable(EnableCap.Blend);\n            GL.DisableClientState(ArrayCap.VertexArray);\n            GL.Disable(EnableCap.Texture2D);\n        }\n    }\n}\n"
  },
  {
    "path": "Black-Midi-Render/Settings.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.IO.Compression;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\n\nnamespace Zenith_MIDI\n{\n    class Settings\n    {\n        public string PreviousVersion = \"\";\n        public string VersionName = \"2.1.5\";\n        public string LanguagesVersion = \"\";\n        public bool AutoUpdate = false;\n        public bool Installed = false;\n        public string SelectedLanguage = \"\";\n        public string InstallerVer = \"\";\n\n        public static readonly string SettingsPath = \"Settings\\\\meta.kvs\";\n\n        public Settings()\n        {\n            if (!File.Exists(SettingsPath))\n            {\n                if(!Directory.Exists(Path.GetDirectoryName(SettingsPath)))\n                    Directory.CreateDirectory(Path.GetDirectoryName(SettingsPath));\n                SaveConfig();\n            }\n            else\n            {\n                var stream = new StreamReader(new GZipStream(File.Open(SettingsPath, FileMode.Open), CompressionMode.Decompress));\n                var text = stream.ReadToEnd();\n                var obj = (dynamic)JsonConvert.DeserializeObject(text);\n                stream.Close();\n                if (obj.version != null) VersionName = obj.version;\n                if (obj.langsVersion != null) LanguagesVersion = obj.langsVersion;\n                if (obj.selectedLang != null) SelectedLanguage = obj.selectedLang;\n                if (obj.autoUpdate != null) AutoUpdate = obj.autoUpdate;\n                if (obj.installed != null) Installed = obj.installed;\n                if (obj.installerVer != null) InstallerVer = obj.installerVer;\n            }\n        }\n\n        public void SaveConfig()\n        {\n            var jobj = new JObject();\n            jobj.Add(\"version\", VersionName);\n            jobj.Add(\"langsVersion\", LanguagesVersion);\n            jobj.Add(\"selectedLang\", SelectedLanguage);\n            jobj.Add(\"autoUpdate\", AutoUpdate);\n            jobj.Add(\"installed\", Installed);\n            jobj.Add(\"installerVer\", InstallerVer);\n\n            var stream = new StreamWriter(new GZipStream(File.Open(SettingsPath, FileMode.Create), CompressionMode.Compress));\n            stream.Write(JsonConvert.SerializeObject(jobj));\n            stream.Close();\n        }\n    }\n}\n"
  },
  {
    "path": "Black-Midi-Render/Zenith.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProjectGuid>{273DA7BD-C89A-4574-97DE-AF425529FAAE}</ProjectGuid>\n    <OutputType>Exe</OutputType>\n    <RootNamespace>Black_Midi_Render</RootNamespace>\n    <AssemblyName>Zenith</AssemblyName>\n    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>\n    <FileAlignment>512</FileAlignment>\n    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>\n    <Deterministic>true</Deterministic>\n    <NuGetPackageImportStamp>\n    </NuGetPackageImportStamp>\n    <TargetFrameworkProfile />\n    <IsWebBootstrapper>false</IsWebBootstrapper>\n    <PublishUrl>publish\\</PublishUrl>\n    <Install>true</Install>\n    <InstallFrom>Disk</InstallFrom>\n    <UpdateEnabled>false</UpdateEnabled>\n    <UpdateMode>Foreground</UpdateMode>\n    <UpdateInterval>7</UpdateInterval>\n    <UpdateIntervalUnits>Days</UpdateIntervalUnits>\n    <UpdatePeriodically>false</UpdatePeriodically>\n    <UpdateRequired>false</UpdateRequired>\n    <MapFileExtensions>true</MapFileExtensions>\n    <ApplicationRevision>0</ApplicationRevision>\n    <ApplicationVersion>1.0.0.%2a</ApplicationVersion>\n    <UseApplicationTrust>false</UseApplicationTrust>\n    <BootstrapperEnabled>true</BootstrapperEnabled>\n  </PropertyGroup>\n  <PropertyGroup>\n    <StartupObject>Zenith_MIDI.Program</StartupObject>\n  </PropertyGroup>\n  <PropertyGroup>\n    <ApplicationIcon>icon.ico</ApplicationIcon>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Debug|x86'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>..\\bin\\x86\\Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n    <DebugType>full</DebugType>\n    <PlatformTarget>x86</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Release|x86'\">\n    <OutputPath>..\\bin\\x86\\Release\\</OutputPath>\n    <DefineConstants>\n    </DefineConstants>\n    <Optimize>true</Optimize>\n    <DebugType>pdbonly</DebugType>\n    <PlatformTarget>x86</PlatformTarget>\n    <GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n    <Prefer32Bit>true</Prefer32Bit>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Debug|x64'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>..\\bin\\x64\\Debug\\</OutputPath>\n    <DefineConstants>TRACE;DEBUG</DefineConstants>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n    <DebugType>full</DebugType>\n    <PlatformTarget>x64</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Release|x64'\">\n    <OutputPath>..\\bin\\x64\\Release\\</OutputPath>\n    <DefineConstants>\n    </DefineConstants>\n    <Optimize>true</Optimize>\n    <DebugType>pdbonly</DebugType>\n    <PlatformTarget>x64</PlatformTarget>\n    <GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n    <Prefer32Bit>true</Prefer32Bit>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>..\\bin\\Any cpu\\Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n    <DebugType>full</DebugType>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Release|AnyCPU'\">\n    <OutputPath>..\\bin\\Any cpu\\Release\\</OutputPath>\n    <DefineConstants>\n    </DefineConstants>\n    <Optimize>true</Optimize>\n    <DebugType>pdbonly</DebugType>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n    <Prefer32Bit>true</Prefer32Bit>\n  </PropertyGroup>\n  <PropertyGroup>\n    <RunPostBuildEvent>Always</RunPostBuildEvent>\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Newtonsoft.Json.12.0.1\\lib\\net45\\Newtonsoft.Json.dll</HintPath>\n    </Reference>\n    <Reference Include=\"OpenTK, Version=3.1.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\OpenTK.3.1.0\\lib\\net20\\OpenTK.dll</HintPath>\n    </Reference>\n    <Reference Include=\"PresentationCore\" />\n    <Reference Include=\"PresentationFramework\" />\n    <Reference Include=\"SharpCompress, Version=0.24.0.0, Culture=neutral, PublicKeyToken=afb0a02973931d96, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\SharpCompress.0.24.0\\lib\\net45\\SharpCompress.dll</HintPath>\n    </Reference>\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Core\" />\n    <Reference Include=\"System.Drawing\" />\n    <Reference Include=\"System.IO.Compression\" />\n    <Reference Include=\"System.Numerics\" />\n    <Reference Include=\"System.Windows.Forms\" />\n    <Reference Include=\"System.Xaml\" />\n    <Reference Include=\"System.Xml.Linq\" />\n    <Reference Include=\"System.Data.DataSetExtensions\" />\n    <Reference Include=\"Microsoft.CSharp\" />\n    <Reference Include=\"System.Data\" />\n    <Reference Include=\"System.Net.Http\" />\n    <Reference Include=\"System.Xml\" />\n    <Reference Include=\"WindowsBase\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"GLPostbuffer.cs\" />\n    <Compile Include=\"GLUtils.cs\" />\n    <Compile Include=\"KDMAPI.cs\" />\n    <Compile Include=\"MainWindow.xaml.cs\">\n      <DependentUpon>MainWindow.xaml</DependentUpon>\n    </Compile>\n    <Compile Include=\"Program.cs\" />\n    <Compile Include=\"Properties\\AssemblyInfo.cs\" />\n    <Compile Include=\"Properties\\Resources.Designer.cs\">\n      <AutoGen>True</AutoGen>\n      <DesignTime>True</DesignTime>\n      <DependentUpon>Resources.resx</DependentUpon>\n    </Compile>\n    <Compile Include=\"RenderWindow.cs\" />\n    <Compile Include=\"Settings.cs\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"App.config\">\n      <SubType>Designer</SubType>\n    </None>\n    <None Include=\"OpenTK.dll.config\" />\n    <None Include=\"packages.config\" />\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Properties\\Resources.resx\">\n      <Generator>ResXFileCodeGenerator</Generator>\n      <LastGenOutput>Resources.Designer.cs</LastGenOutput>\n    </EmbeddedResource>\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\BMEngine\\ZenithEngine.csproj\">\n      <Project>{60f2212a-d56c-4776-836f-6a49453cdbc4}</Project>\n      <Name>ZenithEngine</Name>\n    </ProjectReference>\n    <ProjectReference Include=\"..\\ClassicRender\\ClassicRender.csproj\">\n      <Project>{b733d6f1-78a8-43ca-91a3-28fa3e7025d4}</Project>\n      <Name>ClassicRender</Name>\n    </ProjectReference>\n    <ProjectReference Include=\"..\\FlatRender\\FlatRender.csproj\">\n      <Project>{ae59479c-9d0d-464c-a4d3-8fdd7e1f3786}</Project>\n      <Name>FlatRender</Name>\n    </ProjectReference>\n    <ProjectReference Include=\"..\\MidiTrailRender\\MidiTrailRender.csproj\">\n      <Project>{991e132a-a56d-49ab-bbee-9828359bb307}</Project>\n      <Name>MidiTrailRender</Name>\n    </ProjectReference>\n    <ProjectReference Include=\"..\\NoteCountRender\\NoteCountRender.csproj\">\n      <Project>{5f30ba79-d86b-4fc6-ba76-7cbb9ebba721}</Project>\n      <Name>NoteCountRender</Name>\n    </ProjectReference>\n    <ProjectReference Include=\"..\\PFARender\\PFARender.csproj\">\n      <Project>{84e13869-0d33-4a56-a1a8-0b6f12c76081}</Project>\n      <Name>PFARender</Name>\n    </ProjectReference>\n    <ProjectReference Include=\"..\\ScriptedRenderer\\ScriptedRender.csproj\">\n      <Project>{95f774eb-47a2-40b3-a0ce-e616bbd4d62b}</Project>\n      <Name>ScriptedRender</Name>\n    </ProjectReference>\n    <ProjectReference Include=\"..\\TexturedRender\\TexturedRender.csproj\">\n      <Project>{ecf72523-79ae-4dd1-9421-60b1d8bccb9e}</Project>\n      <Name>TexturedRender</Name>\n    </ProjectReference>\n    <ProjectReference Include=\"..\\ZenithShared\\ZenithShared.csproj\">\n      <Project>{019e4ce1-547f-4a32-b02c-f190943713eb}</Project>\n      <Name>ZenithShared</Name>\n    </ProjectReference>\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Languages\\en\\window.xaml\">\n      <SubType>Designer</SubType>\n      <Generator>MSBuild:Compile</Generator>\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </EmbeddedResource>\n    <Page Include=\"MainWindow.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </Page>\n  </ItemGroup>\n  <ItemGroup>\n    <Content Include=\"icon.ico\" />\n    <Resource Include=\"icon.png\" />\n  </ItemGroup>\n  <ItemGroup>\n    <BootstrapperPackage Include=\".NETFramework,Version=v4.6.1\">\n      <Visible>False</Visible>\n      <ProductName>Microsoft .NET Framework 4.6.1 %28x86 and x64%29</ProductName>\n      <Install>true</Install>\n    </BootstrapperPackage>\n    <BootstrapperPackage Include=\"Microsoft.Net.Framework.3.5.SP1\">\n      <Visible>False</Visible>\n      <ProductName>.NET Framework 3.5 SP1</ProductName>\n      <Install>false</Install>\n    </BootstrapperPackage>\n  </ItemGroup>\n  <ItemGroup>\n    <WCFMetadata Include=\"Connected Services\\\" />\n  </ItemGroup>\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n  <Target Name=\"AfterBuild\">\n    <ItemGroup>\n      <MoveToLibFolder Include=\"$(OutputPath)*.dll\" />\n    </ItemGroup>\n    <ItemGroup>\n      <MoveToDebugFolder Include=\"$(OutputPath)*.pdb ; $(OutputPath)*.xml\" />\n    </ItemGroup>\n    <ItemGroup>\n      <PluginPdbs Include=\"$(OutputPath)Plugins/*.pdb ; $(OutputPath)Plugins/*.xml\" />\n    </ItemGroup>\n    <ItemGroup>\n      <Pluginlib Include=\"$(OutputPath)lib/*Render.dll\" />\n    </ItemGroup>\n    <Move SourceFiles=\"@(MoveToDebugFolder)\" DestinationFolder=\"$(OutputPath)debug\" OverwriteReadOnlyFiles=\"true\" />\n    <Move SourceFiles=\"@(MoveToLibFolder)\" DestinationFolder=\"$(OutputPath)lib\" OverwriteReadOnlyFiles=\"true\" />\n    <Move SourceFiles=\"@(PluginPdbs)\" DestinationFolder=\"$(OutputPath)debug\" OverwriteReadOnlyFiles=\"true\" />\n    <Delete Files=\"@(Pluginlib)\" />\n  </Target>\n  <PropertyGroup>\n    <PostBuildEvent>del Plugins\\MidiUtils.dll\ndel Plugins\\Newtonsoft.Json.dll\ndel Plugins\\OpenTK.dll\ndel Plugins\\Xceed.Wpf.Toolkit.dll\ndel Plugins\\ZenithEngine.dll\ndel Plugins\\SharpCompress.dll\ndel Plugins\\Jint.dll\ndel Plugins\\Languages /s /q\nFOR /D %25%25p IN (\"Plugins\\Languages\") DO rmdir \"%25%25p\" /s /q</PostBuildEvent>\n  </PropertyGroup>\n  <PropertyGroup>\n    <PreBuildEvent>\n    </PreBuildEvent>\n  </PropertyGroup>\n</Project>"
  },
  {
    "path": "Black-Midi-Render/packages.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n  <package id=\"Newtonsoft.Json\" version=\"12.0.1\" targetFramework=\"net461\" />\n  <package id=\"OpenTK\" version=\"3.1.0\" targetFramework=\"net461\" />\n  <package id=\"PrettyBin\" version=\"1.1.0\" targetFramework=\"net461\" />\n  <package id=\"SharpCompress\" version=\"0.24.0\" targetFramework=\"net461\" />\n</packages>"
  },
  {
    "path": "ClassicRender/ClassicRender.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProjectGuid>{B733D6F1-78A8-43CA-91A3-28FA3E7025D4}</ProjectGuid>\n    <OutputType>Library</OutputType>\n    <AppDesignerFolder>Properties</AppDesignerFolder>\n    <RootNamespace>ClassicRender</RootNamespace>\n    <AssemblyName>ClassicRender</AssemblyName>\n    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>\n    <FileAlignment>512</FileAlignment>\n    <Deterministic>true</Deterministic>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>..\\bin\\Any cpu\\Debug\\Plugins\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>..\\bin\\Any cpu\\Release\\Plugins\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <PlatformTarget>x64</PlatformTarget>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Debug|x86'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>..\\bin\\x86\\Debug\\Plugins\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <DebugType>full</DebugType>\n    <PlatformTarget>x86</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Release|x86'\">\n    <OutputPath>..\\bin\\x86\\Release\\Plugins\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <Optimize>true</Optimize>\n    <DebugType>pdbonly</DebugType>\n    <PlatformTarget>x86</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Debug|x64'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>..\\bin\\x64\\Debug\\Plugins\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <DebugType>full</DebugType>\n    <PlatformTarget>x64</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Release|x64'\">\n    <OutputPath>..\\bin\\x64\\Release\\Plugins\\</OutputPath>\n    <DefineConstants>\n    </DefineConstants>\n    <Optimize>true</Optimize>\n    <DebugType>pdbonly</DebugType>\n    <PlatformTarget>x64</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Newtonsoft.Json.12.0.1\\lib\\net45\\Newtonsoft.Json.dll</HintPath>\n    </Reference>\n    <Reference Include=\"OpenTK, Version=3.1.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\OpenTK.3.1.0\\lib\\net20\\OpenTK.dll</HintPath>\n    </Reference>\n    <Reference Include=\"PresentationCore\" />\n    <Reference Include=\"PresentationFramework\" />\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Core\" />\n    <Reference Include=\"System.Drawing\" />\n    <Reference Include=\"System.Xaml\" />\n    <Reference Include=\"System.Xml.Linq\" />\n    <Reference Include=\"System.Data.DataSetExtensions\" />\n    <Reference Include=\"Microsoft.CSharp\" />\n    <Reference Include=\"System.Data\" />\n    <Reference Include=\"System.Net.Http\" />\n    <Reference Include=\"System.Xml\" />\n    <Reference Include=\"WindowsBase\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"Properties\\AssemblyInfo.cs\" />\n    <Compile Include=\"Properties\\Resources.Designer.cs\">\n      <AutoGen>True</AutoGen>\n      <DesignTime>True</DesignTime>\n      <DependentUpon>Resources.resx</DependentUpon>\n    </Compile>\n    <Compile Include=\"Render.cs\" />\n    <Compile Include=\"Settings.cs\" />\n    <Compile Include=\"SettingsCtrl.xaml.cs\">\n      <DependentUpon>SettingsCtrl.xaml</DependentUpon>\n    </Compile>\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"OpenTK.dll.config\" />\n    <None Include=\"packages.config\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\BMEngine\\ZenithEngine.csproj\">\n      <Project>{60F2212A-D56C-4776-836F-6A49453CDBC4}</Project>\n      <Name>ZenithEngine</Name>\n    </ProjectReference>\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Properties\\Resources.resx\">\n      <Generator>ResXFileCodeGenerator</Generator>\n      <LastGenOutput>Resources.Designer.cs</LastGenOutput>\n    </EmbeddedResource>\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"preview.png\" />\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Languages\\en\\classic.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </EmbeddedResource>\n    <Page Include=\"SettingsCtrl.xaml\">\n      <SubType>Designer</SubType>\n      <Generator>MSBuild:Compile</Generator>\n    </Page>\n  </ItemGroup>\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n  <PropertyGroup>\n    <PostBuildEvent>\n    </PostBuildEvent>\n  </PropertyGroup>\n</Project>"
  },
  {
    "path": "ClassicRender/Languages/en/classic.xaml",
    "content": "﻿<ResourceDictionary\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" \n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n    xmlns:local=\"using:MSDNSample\"\n    xmlns:system=\"clr-namespace:System;assembly=mscorlib\">\n    \n    <!--Other settings-->\n    <system:String x:Key=\"firstNote\">First Note</system:String>\n    <system:String x:Key=\"lastNote\">Last Note</system:String>\n    <system:String x:Key=\"pianoHeight\">Piano Height %</system:String>\n    <system:String x:Key=\"noteScreenTime\">Note screen time</system:String>\n    <system:String x:Key=\"sameWidthNotes\">Same Width Notes</system:String>\n    <system:String x:Key=\"save\">Save</system:String>\n    <system:String x:Key=\"loadSaved\">Load Saved</system:String>\n    <system:String x:Key=\"setDefaults\">Defaults</system:String>\n    <system:String x:Key=\"blackNotesAbove\">Draw black notes above (Warning: SLOWER!!)</system:String>\n\n</ResourceDictionary>"
  },
  {
    "path": "ClassicRender/OpenTK.dll.config",
    "content": "<configuration>\n  <dllmap os=\"linux\" dll=\"opengl32.dll\" target=\"libGL.so.1\"/>\n  <dllmap os=\"linux\" dll=\"glu32.dll\" target=\"libGLU.so.1\"/>\n  <dllmap os=\"linux\" dll=\"openal32.dll\" target=\"libopenal.so.1\"/>\n  <dllmap os=\"linux\" dll=\"alut.dll\" target=\"libalut.so.0\"/>\n  <dllmap os=\"linux\" dll=\"opencl.dll\" target=\"libOpenCL.so\"/>\n  <dllmap os=\"linux\" dll=\"libX11\" target=\"libX11.so.6\"/>\n  <dllmap os=\"linux\" dll=\"libXi\" target=\"libXi.so.6\"/>\n  <dllmap os=\"linux\" dll=\"SDL2.dll\" target=\"libSDL2-2.0.so.0\"/>\n  <dllmap os=\"osx\" dll=\"opengl32.dll\" target=\"/System/Library/Frameworks/OpenGL.framework/OpenGL\"/>\n  <dllmap os=\"osx\" dll=\"openal32.dll\" target=\"/System/Library/Frameworks/OpenAL.framework/OpenAL\" />\n  <dllmap os=\"osx\" dll=\"alut.dll\" target=\"/System/Library/Frameworks/OpenAL.framework/OpenAL\" />\n  <dllmap os=\"osx\" dll=\"libGLES.dll\" target=\"/System/Library/Frameworks/OpenGLES.framework/OpenGLES\" />\n  <dllmap os=\"osx\" dll=\"libGLESv1_CM.dll\" target=\"/System/Library/Frameworks/OpenGLES.framework/OpenGLES\" />\n  <dllmap os=\"osx\" dll=\"libGLESv2.dll\" target=\"/System/Library/Frameworks/OpenGLES.framework/OpenGLES\" />\n  <dllmap os=\"osx\" dll=\"opencl.dll\" target=\"/System/Library/Frameworks/OpenCL.framework/OpenCL\"/>\n  <dllmap os=\"osx\" dll=\"SDL2.dll\" target=\"libSDL2.dylib\"/>\n  <!-- XQuartz compatibility (X11 on Mac) -->\n  <dllmap os=\"osx\" dll=\"libGL.so.1\" target=\"/usr/X11/lib/libGL.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libX11\" target=\"/usr/X11/lib/libX11.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXcursor.so.1\" target=\"/usr/X11/lib/libXcursor.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXi\" target=\"/usr/X11/lib/libXi.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXinerama\" target=\"/usr/X11/lib/libXinerama.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXrandr.so.2\" target=\"/usr/X11/lib/libXrandr.dylib\"/>\n</configuration>\n"
  },
  {
    "path": "ClassicRender/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// General Information about an assembly is controlled through the following\n// set of attributes. Change these attribute values to modify the information\n// associated with an assembly.\n[assembly: AssemblyTitle(\"ClassicRender\")]\n[assembly: AssemblyDescription(\"\")]\n[assembly: AssemblyConfiguration(\"\")]\n[assembly: AssemblyCompany(\"\")]\n[assembly: AssemblyProduct(\"ClassicRender\")]\n[assembly: AssemblyCopyright(\"Copyright ©  2019\")]\n[assembly: AssemblyTrademark(\"\")]\n[assembly: AssemblyCulture(\"\")]\n\n// Setting ComVisible to false makes the types in this assembly not visible\n// to COM components.  If you need to access a type in this assembly from\n// COM, set the ComVisible attribute to true on that type.\n[assembly: ComVisible(false)]\n\n// The following GUID is for the ID of the typelib if this project is exposed to COM\n[assembly: Guid(\"b733d6f1-78a8-43ca-91a3-28fa3e7025d4\")]\n\n// Version information for an assembly consists of the following four values:\n//\n//      Major Version\n//      Minor Version\n//      Build Number\n//      Revision\n//\n// You can specify all the values or you can default the Build and Revision Numbers\n// by using the '*' as shown below:\n// [assembly: AssemblyVersion(\"1.0.*\")]\n[assembly: AssemblyVersion(\"1.0.0.0\")]\n[assembly: AssemblyFileVersion(\"1.0.0.0\")]\n"
  },
  {
    "path": "ClassicRender/Properties/Resources.Designer.cs",
    "content": "﻿//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace ClassicRender.Properties {\n    using System;\n    \n    \n    /// <summary>\n    ///   A strongly-typed resource class, for looking up localized strings, etc.\n    /// </summary>\n    // This class was auto-generated by the StronglyTypedResourceBuilder\n    // class via a tool like ResGen or Visual Studio.\n    // To add or remove a member, edit your .ResX file then rerun ResGen\n    // with the /str option, or rebuild your VS project.\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"System.Resources.Tools.StronglyTypedResourceBuilder\", \"15.0.0.0\")]\n    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]\n    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]\n    internal class Resources {\n        \n        private static global::System.Resources.ResourceManager resourceMan;\n        \n        private static global::System.Globalization.CultureInfo resourceCulture;\n        \n        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute(\"Microsoft.Performance\", \"CA1811:AvoidUncalledPrivateCode\")]\n        internal Resources() {\n        }\n        \n        /// <summary>\n        ///   Returns the cached ResourceManager instance used by this class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Resources.ResourceManager ResourceManager {\n            get {\n                if (object.ReferenceEquals(resourceMan, null)) {\n                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager(\"ClassicRender.Properties.Resources\", typeof(Resources).Assembly);\n                    resourceMan = temp;\n                }\n                return resourceMan;\n            }\n        }\n        \n        /// <summary>\n        ///   Overrides the current thread's CurrentUICulture property for all\n        ///   resource lookups using this strongly typed resource class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Globalization.CultureInfo Culture {\n            get {\n                return resourceCulture;\n            }\n            set {\n                resourceCulture = value;\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized resource of type System.Drawing.Bitmap.\n        /// </summary>\n        internal static System.Drawing.Bitmap preview {\n            get {\n                object obj = ResourceManager.GetObject(\"preview\", resourceCulture);\n                return ((System.Drawing.Bitmap)(obj));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "ClassicRender/Properties/Resources.resx",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The primary goals of this format is to allow a simple XML format \n    that is mostly human readable. The generation and parsing of the \n    various data types are done through the TypeConverter classes \n    associated with the data types.\n    \n    Example:\n    \n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n                \n    There are any number of \"resheader\" rows that contain simple \n    name/value pairs.\n    \n    Each data row contains a name, and value. The row also contains a \n    type or mimetype. Type corresponds to a .NET class that support \n    text/value conversion through the TypeConverter architecture. \n    Classes that don't support this are serialized and stored with the \n    mimetype set.\n    \n    The mimetype is used for serialized objects, and tells the \n    ResXResourceReader how to depersist the object. This is currently not \n    extensible. For a given mimetype the value must be set accordingly:\n    \n    Note - application/x-microsoft.net.object.binary.base64 is the format \n    that the ResXResourceWriter will generate, however the reader can \n    read any of the formats listed below.\n    \n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n    \n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array \n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <assembly alias=\"System.Windows.Forms\" name=\"System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" />\n  <data name=\"preview\" type=\"System.Resources.ResXFileRef, System.Windows.Forms\">\n    <value>..\\preview.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>\n  </data>\n</root>"
  },
  {
    "path": "ClassicRender/README.md",
    "content": "# Zenith-Classic-Render\n"
  },
  {
    "path": "ClassicRender/Render.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing OpenTK;\nusing OpenTK.Graphics;\nusing OpenTK.Graphics.OpenGL;\nusing ZenithEngine;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Drawing;\nusing System.Windows.Interop;\nusing System.Windows;\nusing System.IO;\nusing System.Windows.Controls;\nusing System.Diagnostics;\n\nnamespace ClassicRender\n{\n    public class Render : IPluginRender\n    {\n        #region PreviewConvert\n        BitmapImage BitmapToImageSource(Bitmap bitmap)\n        {\n            using (MemoryStream memory = new MemoryStream())\n            {\n                bitmap.Save(memory, System.Drawing.Imaging.ImageFormat.Bmp);\n                memory.Position = 0;\n                BitmapImage bitmapimage = new BitmapImage();\n                bitmapimage.BeginInit();\n                bitmapimage.StreamSource = memory;\n                bitmapimage.CacheOption = BitmapCacheOption.OnLoad;\n                bitmapimage.EndInit();\n\n                return bitmapimage;\n            }\n        }\n        #endregion\n\n        #region Info\n        public string Name { get; } = \"Original\";\n        public string Description { get; } = \"The original graphics renderer with full rendering functionality and vibrant colours\";\n        public bool Initialized { get; private set; } = false;\n        public ImageSource PreviewImage { get; private set; }\n        public string LanguageDictName { get; } = \"classic\";\n\n        public double NoteScreenTime => settings.deltaTimeOnScreen;\n        #endregion\n\n        #region Shaders\n        string noteShaderVert = @\"#version 330 compatibility\n\nlayout(location = 0) in vec3 position;\nlayout(location = 1) in vec4 glColor;\nlayout(location = 2) in vec2 attrib;\n\nout vec4 color;\n\nvoid main()\n{\n    gl_Position = vec4(position.x * 2 - 1, position.y * 2 - 1, 1.0f, 1.0f);\n    color = vec4(glColor.xyz + attrib.x, glColor.w);\n}\n\";\n        string noteShaderFrag = @\"#version 330 compatibility\n \nin vec4 color;\n \nout vec4 outputF;\nlayout(location = 0) out vec4 texOut;\n\nvoid main()\n{\n    outputF = color;\n\ttexOut = outputF;\n}\n\";\n        #endregion\n\n        RenderSettings renderSettings;\n        Settings settings;\n\n        public long LastNoteCount { get; private set; }\n\n        public Control SettingsControl { get; private set; }\n\n        public double Tempo { get; set; }\n\n        public NoteColor[][] NoteColors { get; set; }\n\n        public MidiInfo CurrentMidi { get; set; }\n\n        public bool ManualNoteDelete => false;\n\n        public double NoteCollectorOffset => 0;\n\n        int noteShader;\n\n        int vertexBufferID;\n        int colorBufferID;\n        int attribBufferID;\n\n        int quadBufferLength = 2048 * 64;\n        double[] quadVertexbuff;\n        float[] quadColorbuff;\n        double[] quadAttribbuff;\n        int quadBufferPos = 0;\n\n        int indexBufferId;\n        uint[] indexes;\n\n        bool[] blackKeys = new bool[257];\n        int[] keynum = new int[257];\n\n        public void Dispose()\n        {\n            GL.DeleteBuffers(3, new int[] { vertexBufferID, colorBufferID, attribBufferID });\n            GL.DeleteProgram(noteShader);\n            quadVertexbuff = null;\n            quadColorbuff = null;\n            quadAttribbuff = null;\n            Initialized = false;\n            Console.WriteLine(\"Disposed of ClassicRender\");\n        }\n\n        public Render(RenderSettings settings)\n        {\n            this.settings = new Settings();\n            this.renderSettings = settings;\n            SettingsControl = new SettingsCtrl(this.settings);\n            ((SettingsCtrl)SettingsControl).PaletteChanged += () => { ReloadTrackColors(); };\n            PreviewImage = BitmapToImageSource(Properties.Resources.preview);\n            for (int i = 0; i < blackKeys.Length; i++) blackKeys[i] = isBlackNote(i);\n            int b = 0;\n            int w = 0;\n            for (int i = 0; i < keynum.Length; i++)\n            {\n                if (blackKeys[i]) keynum[i] = b++;\n                else keynum[i] = w++;\n            }\n        }\n\n        public void Init()\n        {\n            int _vertexObj = GL.CreateShader(ShaderType.VertexShader);\n            int _fragObj = GL.CreateShader(ShaderType.FragmentShader);\n            int statusCode;\n            string info;\n\n            GL.ShaderSource(_vertexObj, noteShaderVert);\n            GL.CompileShader(_vertexObj);\n            info = GL.GetShaderInfoLog(_vertexObj);\n            GL.GetShader(_vertexObj, ShaderParameter.CompileStatus, out statusCode);\n            if (statusCode != 1) throw new ApplicationException(info);\n\n            GL.ShaderSource(_fragObj, noteShaderFrag);\n            GL.CompileShader(_fragObj);\n            info = GL.GetShaderInfoLog(_fragObj);\n            GL.GetShader(_fragObj, ShaderParameter.CompileStatus, out statusCode);\n            if (statusCode != 1) throw new ApplicationException(info);\n\n            noteShader = GL.CreateProgram();\n            GL.AttachShader(noteShader, _fragObj);\n            GL.AttachShader(noteShader, _vertexObj);\n            GL.LinkProgram(noteShader);\n\n            quadVertexbuff = new double[quadBufferLength * 8];\n            quadColorbuff = new float[quadBufferLength * 16];\n            quadAttribbuff = new double[quadBufferLength * 8];\n\n            GL.GenBuffers(1, out vertexBufferID);\n            GL.GenBuffers(1, out colorBufferID);\n            GL.GenBuffers(1, out attribBufferID);\n            indexes = new uint[quadBufferLength * 6];\n            for (uint i = 0; i < indexes.Length / 6; i++)\n            {\n                indexes[i * 6 + 0] = i * 4 + 0;\n                indexes[i * 6 + 1] = i * 4 + 1;\n                indexes[i * 6 + 2] = i * 4 + 3;\n                indexes[i * 6 + 3] = i * 4 + 1;\n                indexes[i * 6 + 4] = i * 4 + 3;\n                indexes[i * 6 + 5] = i * 4 + 2;\n            }\n            for (int i = 0; i < quadAttribbuff.Length;)\n            {\n                quadAttribbuff[i++] = -0.1;\n                quadAttribbuff[i++] = 0;\n                quadAttribbuff[i++] = 0.3;\n                quadAttribbuff[i++] = 0;\n                quadAttribbuff[i++] = -0.3;\n                quadAttribbuff[i++] = 0;\n                quadAttribbuff[i++] = 0.3;\n                quadAttribbuff[i++] = 0;\n            }\n            GL.BindBuffer(BufferTarget.ArrayBuffer, attribBufferID);\n            GL.BufferData(\n                BufferTarget.ArrayBuffer,\n                (IntPtr)(quadAttribbuff.Length * 8),\n                quadAttribbuff,\n                BufferUsageHint.StaticDraw);\n\n            GL.GenBuffers(1, out indexBufferId);\n            GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexBufferId);\n            GL.BufferData(\n                BufferTarget.ElementArrayBuffer,\n                (IntPtr)(indexes.Length * 4),\n                indexes,\n                BufferUsageHint.StaticDraw);\n            Initialized = true;\n            Console.WriteLine(\"Initialised ClassicRender\");\n        }\n\n        public void ReloadTrackColors()\n        {\n            if (NoteColors == null) return;\n            var cols = ((SettingsCtrl)SettingsControl).paletteList.GetColors(NoteColors.Length);\n\n            for (int i = 0; i < NoteColors.Length; i++)\n            {\n                for (int j = 0; j < NoteColors[i].Length; j++)\n                {\n                    if (NoteColors[i][j].isDefault)\n                    {\n                        NoteColors[i][j].left = cols[i * 32 + j * 2];\n                        NoteColors[i][j].right = cols[i * 32 + j * 2 + 1];\n                    }\n                }\n            }\n        }\n\n        Color4[] keyColors = new Color4[514];\n        bool[] keyPressed = new bool[256];\n        double[] x1array = new double[257];\n        double[] wdtharray = new double[257];\n\n        public void RenderFrame(FastList<Note> notes, double midiTime, int finalCompositeBuff)\n        {\n            GL.Enable(EnableCap.Blend);\n            GL.EnableClientState(ArrayCap.VertexArray);\n            GL.EnableClientState(ArrayCap.ColorArray);\n            GL.Enable(EnableCap.Texture2D);\n\n            GL.EnableVertexAttribArray(0);\n            GL.EnableVertexAttribArray(1);\n            GL.EnableVertexAttribArray(2);\n\n            GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);\n\n            GL.BindFramebuffer(FramebufferTarget.Framebuffer, finalCompositeBuff);\n            GL.Viewport(0, 0, renderSettings.width, renderSettings.height);\n            GL.Clear(ClearBufferMask.ColorBufferBit);\n\n            GL.UseProgram(noteShader);\n\n            #region Vars\n            long nc = 0;\n            int firstNote = settings.firstNote;\n            int lastNote = settings.lastNote;\n            int kbfirstNote = settings.firstNote;\n            int kblastNote = settings.lastNote;\n            if (blackKeys[firstNote]) kbfirstNote--;\n            if (blackKeys[lastNote - 1]) kblastNote++;\n\n            double deltaTimeOnScreen = NoteScreenTime;\n            double pianoHeight = settings.pianoHeight;\n            bool sameWidth = settings.sameWidthNotes;\n            for (int i = 0; i < 514; i++) keyColors[i] = Color4.Transparent;\n            for (int i = 0; i < 256; i++) keyPressed[i] = false;\n            double wdth;\n            float r, g, b, a, r2, g2, b2, a2, r3, g3, b3, a3;\n            double x1;\n            double x2;\n            double y1;\n            double y2;\n            quadBufferPos = 0;\n            double xx1, xx2, yy1, yy2;\n            double ys1, ys2;\n\n            if (settings.sameWidthNotes)\n            {\n                for (int i = 0; i < 257; i++)\n                {\n                    x1array[i] = (float)(i - firstNote) / (lastNote - firstNote);\n                    wdtharray[i] = 1.0f / (lastNote - firstNote);\n                }\n            }\n            else\n            {\n                double knmfn = keynum[firstNote];\n                double knmln = keynum[lastNote - 1];\n                if (blackKeys[firstNote]) knmfn = keynum[firstNote - 1] + 0.5;\n                if (blackKeys[lastNote - 1]) knmln = keynum[lastNote] - 0.5;\n                for (int i = 0; i < 257; i++)\n                {\n                    if (!blackKeys[i])\n                    {\n                        x1array[i] = (float)(keynum[i] - knmfn) / (knmln - knmfn + 1);\n                        wdtharray[i] = 1.0f / (knmln - knmfn + 1);\n                    }\n                    else\n                    {\n                        int _i = i + 1;\n                        wdth = 0.6f / (knmln - knmfn + 1);\n                        int bknum = keynum[i] % 5;\n                        double offset = wdth / 2;\n                        if (bknum == 0 || bknum == 2)\n                        {\n                            offset *= 1.3;\n                        }\n                        else if (bknum == 1 || bknum == 4)\n                        {\n                            offset *= 0.7;\n                        }\n                        x1array[i] = (float)(keynum[_i] - knmfn) / (knmln - knmfn + 1) - offset;\n                        wdtharray[i] = wdth;\n                    }\n                }\n            }\n            double paddingx = wdtharray[5] * 0.1;\n            double paddingy = paddingx * renderSettings.width / renderSettings.height;\n\n            #endregion\n\n            #region Notes\n            quadBufferPos = 0;\n            double notePosFactor = 1 / deltaTimeOnScreen * (1 - pianoHeight);\n            double renderCutoff = midiTime + deltaTimeOnScreen;\n            for (int noteKey = 0; noteKey < 2; noteKey++)\n            {\n                if (!settings.blackNotesAbove && !settings.sameWidthNotes && noteKey == 1) break;\n                foreach (Note n in notes)\n                {\n                    if ((settings.blackNotesAbove && !settings.sameWidthNotes))\n                    {\n                        if ((noteKey == 0) ^ !blackKeys[n.key]) continue;\n                    }\n                    if (n.end >= midiTime || !n.hasEnded)\n                    {\n                        if (n.start < renderCutoff)\n                        {\n                            unsafe\n                            {\n                                nc++;\n                                int k = n.key;\n                                if (!(k >= firstNote && k < lastNote)) continue;\n                                Color4 coll = n.color.left;\n                                Color4 colr = n.color.right;\n                                if (n.start <= midiTime)\n                                {\n                                    Color4 origcoll = keyColors[k * 2];\n                                    Color4 origcolr = keyColors[k * 2 + 1];\n                                    float blendfac = coll.A;\n                                    float revblendfac = 1 - blendfac;\n                                    keyColors[k * 2] = new Color4(\n                                        coll.R * blendfac + origcoll.R * revblendfac,\n                                        coll.G * blendfac + origcoll.G * revblendfac,\n                                        coll.B * blendfac + origcoll.B * revblendfac,\n                                        1);\n                                    blendfac = colr.A * 0.8f;\n                                    revblendfac = 1 - blendfac;\n                                    keyColors[k * 2 + 1] = new Color4(\n                                        colr.R * blendfac + origcolr.R * revblendfac,\n                                        colr.G * blendfac + origcolr.G * revblendfac,\n                                        colr.B * blendfac + origcolr.B * revblendfac,\n                                        1);\n                                    keyPressed[k] = true;\n                                }\n                                x1 = x1array[k];\n                                wdth = wdtharray[k];\n                                x2 = x1 + wdth;\n                                y1 = 1 - (renderCutoff - n.end) * notePosFactor;\n                                y2 = 1 - (renderCutoff - n.start) * notePosFactor;\n                                if (!n.hasEnded)\n                                    y1 = 1;\n\n                                xx1 = x1 + paddingx;\n                                xx2 = x2 - paddingx;\n                                yy1 = y1 - paddingy;\n                                yy2 = y2 + paddingy;\n\n                                if (yy1 < yy2)\n                                {\n                                    double yyavg = (yy1 + yy2) / 2;\n                                    yy1 = yyavg;\n                                    yy2 = yyavg;\n                                }\n\n                                int pos = quadBufferPos * 8;\n                                quadVertexbuff[pos++] = x2;\n                                quadVertexbuff[pos++] = y2;\n                                quadVertexbuff[pos++] = x2;\n                                quadVertexbuff[pos++] = y1;\n                                quadVertexbuff[pos++] = x1;\n                                quadVertexbuff[pos++] = y1;\n                                quadVertexbuff[pos++] = x1;\n                                quadVertexbuff[pos++] = y2;\n\n                                pos = quadBufferPos * 16;\n                                r = coll.R * 0.5f;\n                                g = coll.G * 0.5f;\n                                b = coll.B * 0.5f;\n                                a = coll.A;\n                                quadColorbuff[pos++] = r;\n                                quadColorbuff[pos++] = g;\n                                quadColorbuff[pos++] = b;\n                                quadColorbuff[pos++] = a;\n                                quadColorbuff[pos++] = r;\n                                quadColorbuff[pos++] = g;\n                                quadColorbuff[pos++] = b;\n                                quadColorbuff[pos++] = a;\n                                r = colr.R * 0.5f;\n                                g = colr.G * 0.5f;\n                                b = colr.B * 0.5f;\n                                a = colr.A;\n                                quadColorbuff[pos++] = r;\n                                quadColorbuff[pos++] = g;\n                                quadColorbuff[pos++] = b;\n                                quadColorbuff[pos++] = a;\n                                quadColorbuff[pos++] = r;\n                                quadColorbuff[pos++] = g;\n                                quadColorbuff[pos++] = b;\n                                quadColorbuff[pos++] = a;\n\n                                pos = quadBufferPos * 8;\n                                quadAttribbuff[pos++] = 0.1;\n                                quadAttribbuff[pos++] = 0;\n                                quadAttribbuff[pos++] = 0.1;\n                                quadAttribbuff[pos++] = 0;\n                                quadAttribbuff[pos++] = -0.3;\n                                quadAttribbuff[pos++] = 0;\n                                quadAttribbuff[pos++] = -0.3;\n                                quadAttribbuff[pos++] = 0;\n\n                                quadBufferPos++;\n                                FlushQuadBuffer();\n\n                                pos = quadBufferPos * 8;\n                                quadVertexbuff[pos++] = xx2;\n                                quadVertexbuff[pos++] = yy2;\n                                quadVertexbuff[pos++] = xx2;\n                                quadVertexbuff[pos++] = yy1;\n                                quadVertexbuff[pos++] = xx1;\n                                quadVertexbuff[pos++] = yy1;\n                                quadVertexbuff[pos++] = xx1;\n                                quadVertexbuff[pos++] = yy2;\n\n                                pos = quadBufferPos * 16;\n                                r = coll.R;\n                                g = coll.G;\n                                b = coll.B;\n                                a = coll.A;\n                                quadColorbuff[pos++] = r;\n                                quadColorbuff[pos++] = g;\n                                quadColorbuff[pos++] = b;\n                                quadColorbuff[pos++] = a;\n                                quadColorbuff[pos++] = r;\n                                quadColorbuff[pos++] = g;\n                                quadColorbuff[pos++] = b;\n                                quadColorbuff[pos++] = a;\n                                r = colr.R;\n                                g = colr.G;\n                                b = colr.B;\n                                a = colr.A;\n                                quadColorbuff[pos++] = r;\n                                quadColorbuff[pos++] = g;\n                                quadColorbuff[pos++] = b;\n                                quadColorbuff[pos++] = a;\n                                quadColorbuff[pos++] = r;\n                                quadColorbuff[pos++] = g;\n                                quadColorbuff[pos++] = b;\n                                quadColorbuff[pos++] = a;\n\n                                pos = quadBufferPos * 8;\n                                quadAttribbuff[pos++] = 0.1;\n                                quadAttribbuff[pos++] = 0;\n                                quadAttribbuff[pos++] = 0.1;\n                                quadAttribbuff[pos++] = 0;\n                                quadAttribbuff[pos++] = -0.3;\n                                quadAttribbuff[pos++] = 0;\n                                quadAttribbuff[pos++] = -0.3;\n                                quadAttribbuff[pos++] = 0;\n\n                                quadBufferPos++;\n                                FlushQuadBuffer();\n                            }\n\n                        }\n                        else break;\n                    }\n                }\n            }\n\n            FlushQuadBuffer(false);\n            quadBufferPos = 0;\n\n            LastNoteCount = nc;\n            #endregion\n\n            #region Keyboard\n            y1 = pianoHeight;\n            y2 = 0;\n            Color4[] origColors = new Color4[257];\n            for (int k = kbfirstNote; k < kblastNote; k++)\n            {\n                if (isBlackNote(k))\n                    origColors[k] = Color4.Black;\n                else\n                    origColors[k] = Color4.White;\n            }\n\n            for (int n = kbfirstNote; n < kblastNote; n++)\n            {\n                x1 = x1array[n];\n                wdth = wdtharray[n];\n                x2 = x1 + wdth;\n\n                if (!blackKeys[n])\n                {\n                    y2 = 0;\n                    if (settings.sameWidthNotes)\n                    {\n                        int _n = n % 12;\n                        if (_n == 0)\n                            x2 += wdth * 0.666;\n                        else if (_n == 2)\n                        {\n                            x1 -= wdth / 3;\n                            x2 += wdth / 3;\n                        }\n                        else if (_n == 4)\n                            x1 -= wdth / 3 * 2;\n                        else if (_n == 5)\n                            x2 += wdth * 0.75;\n                        else if (_n == 7)\n                        {\n                            x1 -= wdth / 4;\n                            x2 += wdth / 2;\n                        }\n                        else if (_n == 9)\n                        {\n                            x1 -= wdth / 2;\n                            x2 += wdth / 4;\n                        }\n                        else if (_n == 11)\n                            x1 -= wdth * 0.75;\n                    }\n                }\n                else continue;\n\n                var coll = keyColors[n * 2];\n                var colr = keyColors[n * 2 + 1];\n                var origcol = origColors[n];\n                float blendfac = coll.A;\n                float revblendfac = 1 - blendfac;\n                coll = new Color4(\n                    coll.R * blendfac + origcol.R * revblendfac,\n                    coll.G * blendfac + origcol.G * revblendfac,\n                    coll.B * blendfac + origcol.B * revblendfac,\n                    1);\n                r = coll.R;\n                g = coll.G;\n                b = coll.B;\n                a = coll.A;\n                blendfac = colr.A;\n                revblendfac = 1 - blendfac;\n                colr = new Color4(\n                    colr.R * blendfac + origcol.R * revblendfac,\n                    colr.G * blendfac + origcol.G * revblendfac,\n                    colr.B * blendfac + origcol.B * revblendfac,\n                    1);\n                r2 = colr.R;\n                g2 = colr.G;\n                b2 = colr.B;\n                a2 = colr.A;\n\n                int pos = quadBufferPos * 8;\n                quadVertexbuff[pos++] = x1;\n                quadVertexbuff[pos++] = y2;\n                quadVertexbuff[pos++] = x2;\n                quadVertexbuff[pos++] = y2;\n                quadVertexbuff[pos++] = x2;\n                quadVertexbuff[pos++] = y1;\n                quadVertexbuff[pos++] = x1;\n                quadVertexbuff[pos++] = y1;\n\n                pos = quadBufferPos * 8;\n                quadAttribbuff[pos++] = 0.0;\n                quadAttribbuff[pos++] = 0;\n                quadAttribbuff[pos++] = -0.1;\n                quadAttribbuff[pos++] = 0;\n                quadAttribbuff[pos++] = 0.0;\n                quadAttribbuff[pos++] = 0;\n                quadAttribbuff[pos++] = -0.3;\n                quadAttribbuff[pos++] = 0;\n\n                pos = quadBufferPos * 16;\n                quadColorbuff[pos++] = r;\n                quadColorbuff[pos++] = g;\n                quadColorbuff[pos++] = b;\n                quadColorbuff[pos++] = a;\n                quadColorbuff[pos++] = r;\n                quadColorbuff[pos++] = g;\n                quadColorbuff[pos++] = b;\n                quadColorbuff[pos++] = a;\n                quadColorbuff[pos++] = r2;\n                quadColorbuff[pos++] = g2;\n                quadColorbuff[pos++] = b2;\n                quadColorbuff[pos++] = a2;\n                quadColorbuff[pos++] = r2;\n                quadColorbuff[pos++] = g2;\n                quadColorbuff[pos++] = b2;\n                quadColorbuff[pos++] = a2;\n                quadBufferPos++;\n                FlushQuadBuffer();\n\n                pos = quadBufferPos * 8;\n                x2 = x1 + wdth / 30;\n                x1 -= wdth / 30;\n                quadVertexbuff[pos++] = x1;\n                quadVertexbuff[pos++] = y2;\n                quadVertexbuff[pos++] = x2;\n                quadVertexbuff[pos++] = y2;\n                quadVertexbuff[pos++] = x2;\n                quadVertexbuff[pos++] = y1;\n                quadVertexbuff[pos++] = x1;\n                quadVertexbuff[pos++] = y1;\n\n                pos = quadBufferPos * 8;\n                quadAttribbuff[pos++] = -0.3;\n                quadAttribbuff[pos++] = 0;\n                quadAttribbuff[pos++] = -0.3;\n                quadAttribbuff[pos++] = 0;\n                quadAttribbuff[pos++] = -0.1;\n                quadAttribbuff[pos++] = 0;\n                quadAttribbuff[pos++] = -0.1;\n                quadAttribbuff[pos++] = 0;\n\n                pos = quadBufferPos * 16;\n                quadColorbuff[pos++] = r;\n                quadColorbuff[pos++] = g;\n                quadColorbuff[pos++] = b;\n                quadColorbuff[pos++] = a;\n                quadColorbuff[pos++] = r;\n                quadColorbuff[pos++] = g;\n                quadColorbuff[pos++] = b;\n                quadColorbuff[pos++] = a;\n                quadColorbuff[pos++] = r2;\n                quadColorbuff[pos++] = g2;\n                quadColorbuff[pos++] = b2;\n                quadColorbuff[pos++] = a2;\n                quadColorbuff[pos++] = r2;\n                quadColorbuff[pos++] = g2;\n                quadColorbuff[pos++] = b2;\n                quadColorbuff[pos++] = a2;\n                quadBufferPos++;\n                FlushQuadBuffer();\n            }\n            for (int n = kbfirstNote; n < kblastNote; n++)\n            {\n                x1 = x1array[n];\n                wdth = wdtharray[n];\n                x2 = x1 + wdth;\n\n                if (blackKeys[n])\n                {\n                    y2 = pianoHeight / 10 * 3.7;\n                }\n                else continue;\n\n                var coll = keyColors[n * 2];\n                var colr = keyColors[n * 2 + 1];\n                var origcol = origColors[n];\n                float blendfac = coll.A;\n                float revblendfac = 1 - blendfac;\n                coll = new Color4(\n                    coll.R * blendfac + origcol.R * revblendfac,\n                    coll.G * blendfac + origcol.G * revblendfac,\n                    coll.B * blendfac + origcol.B * revblendfac,\n                    1);\n                r = coll.R;\n                g = coll.G;\n                b = coll.B;\n                a = coll.A;\n                blendfac = colr.A;\n                revblendfac = 1 - blendfac;\n                colr = new Color4(\n                    colr.R * blendfac + origcol.R * revblendfac,\n                    colr.G * blendfac + origcol.G * revblendfac,\n                    colr.B * blendfac + origcol.B * revblendfac,\n                    1);\n                r2 = colr.R;\n                g2 = colr.G;\n                b2 = colr.B;\n                a2 = colr.A;\n                var colm = new Color4(\n                    (coll.R + colr.R) / 2,\n                    (coll.G + colr.G) / 2,\n                    (coll.B + colr.B) / 2,\n                    (coll.A + colr.A) / 2\n                    );\n                r3 = colm.R;\n                g3 = colm.G;\n                b3 = colm.B;\n                a3 = colm.A;\n\n                xx1 = x1 + wdth / 6;\n                xx2 = x2 - wdth / 6;\n                if (keyPressed[n])\n                    yy1 = y1 + 0.001;\n                else\n                    yy1 = y1 + 0.002;\n                if (keyPressed[n])\n                    yy2 = y2 + 0.005;\n                else\n                    yy2 = y2 + 0.01;\n\n                ys1 = pianoHeight / 7 * 5;\n                ys2 = pianoHeight / 7 * 6;\n\n                //Middle Top\n                int pos = quadBufferPos * 8;\n                quadVertexbuff[pos++] = xx1;\n                quadVertexbuff[pos++] = ys1;\n                quadVertexbuff[pos++] = xx2;\n                quadVertexbuff[pos++] = ys2;\n                quadVertexbuff[pos++] = xx2;\n                quadVertexbuff[pos++] = yy1;\n                quadVertexbuff[pos++] = xx1;\n                quadVertexbuff[pos++] = yy1;\n\n                pos = quadBufferPos * 8;\n                quadAttribbuff[pos++] = 0.3;\n                quadAttribbuff[pos++] = 0;\n                quadAttribbuff[pos++] = 0.3;\n                quadAttribbuff[pos++] = 0;\n                quadAttribbuff[pos++] = 0.2;\n                quadAttribbuff[pos++] = 0;\n                quadAttribbuff[pos++] = 0.4;\n                quadAttribbuff[pos++] = 0;\n\n                pos = quadBufferPos * 16;\n                quadColorbuff[pos++] = r3;\n                quadColorbuff[pos++] = g3;\n                quadColorbuff[pos++] = b3;\n                quadColorbuff[pos++] = a3;\n                quadColorbuff[pos++] = r3;\n                quadColorbuff[pos++] = g3;\n                quadColorbuff[pos++] = b3;\n                quadColorbuff[pos++] = a3;\n                quadColorbuff[pos++] = r2;\n                quadColorbuff[pos++] = g2;\n                quadColorbuff[pos++] = b2;\n                quadColorbuff[pos++] = a2;\n                quadColorbuff[pos++] = r2;\n                quadColorbuff[pos++] = g2;\n                quadColorbuff[pos++] = b2;\n                quadColorbuff[pos++] = a2;\n                quadBufferPos++;\n                FlushQuadBuffer();\n\n                //Middle Bottom\n                pos = quadBufferPos * 8;\n                quadVertexbuff[pos++] = xx1;\n                quadVertexbuff[pos++] = yy2;\n                quadVertexbuff[pos++] = xx2;\n                quadVertexbuff[pos++] = yy2;\n                quadVertexbuff[pos++] = xx2;\n                quadVertexbuff[pos++] = ys2;\n                quadVertexbuff[pos++] = xx1;\n                quadVertexbuff[pos++] = ys1;\n\n                pos = quadBufferPos * 8;\n                quadAttribbuff[pos++] = 0.0;\n                quadAttribbuff[pos++] = 0;\n                quadAttribbuff[pos++] = -0.2;\n                quadAttribbuff[pos++] = 0;\n                quadAttribbuff[pos++] = 0.3;\n                quadAttribbuff[pos++] = 0;\n                quadAttribbuff[pos++] = 0.3;\n                quadAttribbuff[pos++] = 0;\n\n                pos = quadBufferPos * 16;\n                quadColorbuff[pos++] = r;\n                quadColorbuff[pos++] = g;\n                quadColorbuff[pos++] = b;\n                quadColorbuff[pos++] = a;\n                quadColorbuff[pos++] = r;\n                quadColorbuff[pos++] = g;\n                quadColorbuff[pos++] = b;\n                quadColorbuff[pos++] = a;\n                quadColorbuff[pos++] = r3;\n                quadColorbuff[pos++] = g3;\n                quadColorbuff[pos++] = b3;\n                quadColorbuff[pos++] = a3;\n                quadColorbuff[pos++] = r3;\n                quadColorbuff[pos++] = g3;\n                quadColorbuff[pos++] = b3;\n                quadColorbuff[pos++] = a3;\n                quadBufferPos++;\n                FlushQuadBuffer();\n\n                //Bottom\n                pos = quadBufferPos * 8;\n                quadVertexbuff[pos++] = x1;\n                quadVertexbuff[pos++] = y2;\n                quadVertexbuff[pos++] = x2;\n                quadVertexbuff[pos++] = y2;\n                quadVertexbuff[pos++] = xx2;\n                quadVertexbuff[pos++] = yy2;\n                quadVertexbuff[pos++] = xx1;\n                quadVertexbuff[pos++] = yy2;\n\n                pos = quadBufferPos * 8;\n                quadAttribbuff[pos++] = 0.2;\n                quadAttribbuff[pos++] = 0;\n                quadAttribbuff[pos++] = -0.2;\n                quadAttribbuff[pos++] = 0;\n                quadAttribbuff[pos++] = 0.4;\n                quadAttribbuff[pos++] = 0;\n                quadAttribbuff[pos++] = 0.4;\n                quadAttribbuff[pos++] = 0;\n\n                pos = quadBufferPos * 16;\n                quadColorbuff[pos++] = r;\n                quadColorbuff[pos++] = g;\n                quadColorbuff[pos++] = b;\n                quadColorbuff[pos++] = a;\n                quadColorbuff[pos++] = r;\n                quadColorbuff[pos++] = g;\n                quadColorbuff[pos++] = b;\n                quadColorbuff[pos++] = a;\n                quadColorbuff[pos++] = r;\n                quadColorbuff[pos++] = g;\n                quadColorbuff[pos++] = b;\n                quadColorbuff[pos++] = a;\n                quadColorbuff[pos++] = r;\n                quadColorbuff[pos++] = g;\n                quadColorbuff[pos++] = b;\n                quadColorbuff[pos++] = a;\n                quadBufferPos++;\n                FlushQuadBuffer();\n\n                //Left\n                pos = quadBufferPos * 8;\n                quadVertexbuff[pos++] = x1;\n                quadVertexbuff[pos++] = y2;\n                quadVertexbuff[pos++] = xx1;\n                quadVertexbuff[pos++] = yy2;\n                quadVertexbuff[pos++] = xx1;\n                quadVertexbuff[pos++] = yy1;\n                quadVertexbuff[pos++] = x1;\n                quadVertexbuff[pos++] = y1;\n\n                pos = quadBufferPos * 8;\n                quadAttribbuff[pos++] = 0.2;\n                quadAttribbuff[pos++] = 0;\n                quadAttribbuff[pos++] = 0.2;\n                quadAttribbuff[pos++] = 0;\n                quadAttribbuff[pos++] = 0.4;\n                quadAttribbuff[pos++] = 0;\n                quadAttribbuff[pos++] = 0.2;\n                quadAttribbuff[pos++] = 0;\n\n                pos = quadBufferPos * 16;\n                quadColorbuff[pos++] = r;\n                quadColorbuff[pos++] = g;\n                quadColorbuff[pos++] = b;\n                quadColorbuff[pos++] = a;\n                quadColorbuff[pos++] = r;\n                quadColorbuff[pos++] = g;\n                quadColorbuff[pos++] = b;\n                quadColorbuff[pos++] = a;\n                quadColorbuff[pos++] = r2;\n                quadColorbuff[pos++] = g2;\n                quadColorbuff[pos++] = b2;\n                quadColorbuff[pos++] = a2;\n                quadColorbuff[pos++] = r2;\n                quadColorbuff[pos++] = g2;\n                quadColorbuff[pos++] = b2;\n                quadColorbuff[pos++] = a2;\n                quadBufferPos++;\n                FlushQuadBuffer();\n\n\n                //Right\n                pos = quadBufferPos * 8;\n                quadVertexbuff[pos++] = x2;\n                quadVertexbuff[pos++] = y2;\n                quadVertexbuff[pos++] = xx2;\n                quadVertexbuff[pos++] = yy2;\n                quadVertexbuff[pos++] = xx2;\n                quadVertexbuff[pos++] = yy1;\n                quadVertexbuff[pos++] = x2;\n                quadVertexbuff[pos++] = y1;\n\n                pos = quadBufferPos * 8;\n                quadAttribbuff[pos++] = -0.2;\n                quadAttribbuff[pos++] = 0;\n                quadAttribbuff[pos++] = -0.2;\n                quadAttribbuff[pos++] = 0;\n                quadAttribbuff[pos++] = -0.2;\n                quadAttribbuff[pos++] = 0;\n                quadAttribbuff[pos++] = -0.2;\n                quadAttribbuff[pos++] = 0;\n\n                pos = quadBufferPos * 16;\n                quadColorbuff[pos++] = r;\n                quadColorbuff[pos++] = g;\n                quadColorbuff[pos++] = b;\n                quadColorbuff[pos++] = a;\n                quadColorbuff[pos++] = r;\n                quadColorbuff[pos++] = g;\n                quadColorbuff[pos++] = b;\n                quadColorbuff[pos++] = a;\n                quadColorbuff[pos++] = r2;\n                quadColorbuff[pos++] = g2;\n                quadColorbuff[pos++] = b2;\n                quadColorbuff[pos++] = a2;\n                quadColorbuff[pos++] = r2;\n                quadColorbuff[pos++] = g2;\n                quadColorbuff[pos++] = b2;\n                quadColorbuff[pos++] = a2;\n                quadBufferPos++;\n                FlushQuadBuffer();\n            }\n            FlushQuadBuffer(false);\n            #endregion\n\n            GL.Disable(EnableCap.Blend);\n            GL.DisableClientState(ArrayCap.VertexArray);\n            GL.DisableClientState(ArrayCap.ColorArray);\n            GL.Disable(EnableCap.Texture2D);\n\n            GL.DisableVertexAttribArray(0);\n            GL.DisableVertexAttribArray(1);\n            GL.DisableVertexAttribArray(2);\n        }\n\n        void FlushQuadBuffer(bool check = true)\n        {\n            if (quadBufferPos < quadBufferLength && check) return;\n            if (quadBufferPos == 0) return;\n            GL.BindBuffer(BufferTarget.ArrayBuffer, vertexBufferID);\n            GL.BufferData(\n                BufferTarget.ArrayBuffer,\n                (IntPtr)(quadBufferPos * 2 * 8 * 4),\n                quadVertexbuff,\n                BufferUsageHint.StaticDraw);\n            GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Double, false, 16, 0);\n            GL.BindBuffer(BufferTarget.ArrayBuffer, colorBufferID);\n            GL.BufferData(\n                BufferTarget.ArrayBuffer,\n                (IntPtr)(quadBufferPos * 4 * 4 * 4),\n                quadColorbuff,\n                BufferUsageHint.StaticDraw);\n            GL.VertexAttribPointer(1, 4, VertexAttribPointerType.Float, false, 16, 0);\n            GL.BindBuffer(BufferTarget.ArrayBuffer, attribBufferID);\n            GL.BufferData(\n                BufferTarget.ArrayBuffer,\n                (IntPtr)(quadBufferPos * 2 * 8 * 4),\n                quadAttribbuff,\n                BufferUsageHint.StaticDraw);\n            GL.VertexAttribPointer(2, 2, VertexAttribPointerType.Double, false, 16, 0);\n            GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexBufferId);\n            GL.IndexPointer(IndexPointerType.Int, 1, 0);\n            GL.DrawElements(PrimitiveType.Triangles, quadBufferPos * 6, DrawElementsType.UnsignedInt, IntPtr.Zero);\n            quadBufferPos = 0;\n        }\n\n        bool isBlackNote(int n)\n        {\n            n = n % 12;\n            return n == 1 || n == 3 || n == 6 || n == 8 || n == 10;\n        }\n    }\n}\n"
  },
  {
    "path": "ClassicRender/Settings.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace ClassicRender\n{\n    public class Settings\n    {\n        public int firstNote = 0;\n        public int lastNote = 128;\n        public double pianoHeight = 0.16;\n        public double deltaTimeOnScreen = 300;\n        public bool sameWidthNotes = true;\n        public bool blackNotesAbove = true;\n\n        public string palette = \"Random\";\n\n        public float noteBrightness = 1;\n    }\n}\n"
  },
  {
    "path": "ClassicRender/SettingsCtrl.xaml",
    "content": "﻿<UserControl\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" \n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\" \n             xmlns:local=\"clr-namespace:ClassicRender\"\n             xmlns:ui=\"clr-namespace:ZenithEngine.UI;assembly=ZenithEngine\"\n             xmlns:bme=\"clr-namespace:ZenithEngine;assembly=ZenithEngine\"\n             xmlns:xctk=\"http://schemas.xceed.com/wpf/xaml/toolkit\" xmlns:System=\"clr-namespace:System;assembly=mscorlib\" x:Class=\"ClassicRender.SettingsCtrl\"\n             mc:Ignorable=\"d\" Height=\"366.234\" Width=\"753.096\">\n    <UserControl.Resources>\n        <ResourceDictionary>\n            <ResourceDictionary.MergedDictionaries>\n                <ResourceDictionary>\n                    <ResourceDictionary.MergedDictionaries>\n                        <ResourceDictionary Source=\"pack://application:,,,/ZenithEngine;component/UI/Material.xaml\"/>\n                        <ResourceDictionary Source=\"pack://siteoforigin:,,,/Languages/en/classic.xaml\" />\n                    </ResourceDictionary.MergedDictionaries>\n                </ResourceDictionary>\n            </ResourceDictionary.MergedDictionaries>\n        </ResourceDictionary>\n    </UserControl.Resources>\n    <DockPanel>\n        <StackPanel Margin=\"10\">\n            <DockPanel HorizontalAlignment=\"Left\" LastChildFill=\"False\" Margin=\"0,0,0,0\">\n                <Label Content=\"{DynamicResource firstNote}\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Top\" DockPanel.Dock=\"Left\"/>\n                <ui:NumberSelect x:Name=\"firstNote\" Value=\"1\" Maximum=\"254\" Minimum=\"0\" Margin=\"5,0,0,2\" HorizontalAlignment=\"Left\" Width=\"80\" ValueChanged=\"Nud_ValueChanged\"  />\n                <Label Content=\"{DynamicResource lastNote}\" HorizontalAlignment=\"Left\" Margin=\"10,0,0,0\" VerticalAlignment=\"Top\"/>\n                <ui:NumberSelect x:Name=\"lastNote\" Value=\"1\" Maximum=\"255\" Minimum=\"1\" Margin=\"5,0,0,2\" HorizontalAlignment=\"Left\" Width=\"80\" ValueChanged=\"Nud_ValueChanged\"  />\n            </DockPanel>\n            <DockPanel HorizontalAlignment=\"Left\" LastChildFill=\"False\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\">\n                <Label Content=\"{DynamicResource pianoHeight}\" HorizontalAlignment=\"Left\" Margin=\"0\" VerticalAlignment=\"Top\"/>\n                <ui:NumberSelect x:Name=\"pianoHeight\" Value=\"1\" Maximum=\"100\" Minimum=\"1\" Margin=\"6,0,0,2\" HorizontalAlignment=\"Left\" Width=\"80\" ValueChanged=\"Nud_ValueChanged\"  />\n            </DockPanel>\n            <ui:BetterCheckbox x:Name=\"sameWidth\" Text=\"{DynamicResource sameWidthNotes}\" HorizontalAlignment=\"Left\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\" IsChecked=\"True\" CheckToggled=\"SameWidth_Checked\"/>\n            <ui:BetterCheckbox x:Name=\"blackNotesAbove\" Text=\"{DynamicResource blackNotesAbove}\" HorizontalAlignment=\"Left\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\" CheckToggled=\"BlackNotesAbove_Checked\"/>\n            <DockPanel HorizontalAlignment=\"Left\" LastChildFill=\"False\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\" Width=\"528\" >\n                <Label Content=\"{DynamicResource noteScreenTime}\" HorizontalAlignment=\"Left\" Margin=\"0,0,0,0\" VerticalAlignment=\"Top\"/>\n                <ui:ValueSlider x:Name=\"noteDeltaScreenTime\" Maximum=\"12\" DecimalPoints=\"2\" Minimum=\"2\" TrueMin=\"1\" TrueMax=\"100000\" ValueChanged=\"NoteDeltaScreenTime_ValueChanged\" Width=\"305\" VerticalAlignment=\"Top\"/>\n            </DockPanel>\n        </StackPanel>\n        <bme:NoteColorPalettePick x:FieldModifier=\"public\" x:Name=\"paletteList\" Margin=\"0,10,10,10\" HorizontalAlignment=\"Right\" Width=\"184\"/>\n    </DockPanel>\n</UserControl>\n"
  },
  {
    "path": "ClassicRender/SettingsCtrl.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\nusing ZenithEngine;\nusing Newtonsoft.Json;\n\nnamespace ClassicRender\n{\n    /// <summary>\n    /// Interaction logic for SettingsCtrl.xaml\n    /// </summary>\n    public partial class SettingsCtrl : UserControl\n    {\n        Settings settings;\n\n        public event Action PaletteChanged\n        {\n            add { paletteList.PaletteChanged += value; }\n            remove { paletteList.PaletteChanged -= value; }\n        }\n\n        public void SetValues()\n        {\n            firstNote.Value = settings.firstNote;\n            lastNote.Value = settings.lastNote - 1;\n            pianoHeight.Value = (int)(settings.pianoHeight * 100);\n            noteDeltaScreenTime.Value = settings.deltaTimeOnScreen;\n            sameWidth.IsChecked = settings.sameWidthNotes;\n            blackNotesAbove.IsChecked = settings.blackNotesAbove;\n            paletteList.SelectImage(settings.palette);\n        }\n\n        public SettingsCtrl(Settings settings) : base()\n        {\n            InitializeComponent();\n            noteDeltaScreenTime.nudToSlider = v => Math.Log(v, 2);\n            noteDeltaScreenTime.sliderToNud = v => Math.Pow(2, v);\n            this.settings = settings;\n            paletteList.SetPath(\"Plugins\\\\Assets\\\\Palettes\");\n            SetValues();\n        }\n\n        private void Nud_ValueChanged(object sender, RoutedPropertyChangedEventArgs<decimal> e)\n        {\n            if (settings == null) return;\n                if (sender == firstNote) settings.firstNote = (int)firstNote.Value;\n                if (sender == lastNote) settings.lastNote = (int)lastNote.Value + 1;\n                if (sender == pianoHeight) settings.pianoHeight = (double)pianoHeight.Value / 100;\n                if (sender == noteDeltaScreenTime) settings.deltaTimeOnScreen = (int)noteDeltaScreenTime.Value;\n        }\n\n        private void NoteDeltaScreenTime_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)\n        {\n            if (settings == null) return;\n            settings.deltaTimeOnScreen = noteDeltaScreenTime.Value;\n        }\n\n        private void BlackNotesAbove_Checked(object sender, RoutedEventArgs e)\n        {\n            if (settings == null) return;\n                settings.blackNotesAbove = (bool)blackNotesAbove.IsChecked;\n        }\n\n        private void SameWidth_Checked(object sender, RoutedEventArgs e)\n        {\n            if (settings == null) return;\n                settings.sameWidthNotes = (bool)sameWidth.IsChecked;\n                blackNotesAbove.IsEnabled = !settings.sameWidthNotes;\n        }\n    }\n}\n"
  },
  {
    "path": "ClassicRender/packages.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n  <package id=\"Newtonsoft.Json\" version=\"12.0.1\" targetFramework=\"net461\" />\n  <package id=\"OpenTK\" version=\"3.1.0\" targetFramework=\"net461\" />\n</packages>"
  },
  {
    "path": "FlatRender/FlatRender.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProjectGuid>{AE59479C-9D0D-464C-A4D3-8FDD7E1F3786}</ProjectGuid>\n    <OutputType>Library</OutputType>\n    <AppDesignerFolder>Properties</AppDesignerFolder>\n    <RootNamespace>FlatRender</RootNamespace>\n    <AssemblyName>FlatRender</AssemblyName>\n    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>\n    <FileAlignment>512</FileAlignment>\n    <Deterministic>true</Deterministic>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>..\\bin\\Any cpu\\Debug\\Plugins\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>..\\bin\\Any cpu\\Release\\Plugins\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <PlatformTarget>x64</PlatformTarget>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Debug|x86'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>..\\bin\\x86\\Debug\\Plugins\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <DebugType>full</DebugType>\n    <PlatformTarget>x86</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Release|x86'\">\n    <OutputPath>..\\bin\\x86\\Release\\Plugins\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <Optimize>true</Optimize>\n    <DebugType>pdbonly</DebugType>\n    <PlatformTarget>x86</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Debug|x64'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>..\\bin\\x64\\Debug\\Plugins\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <DebugType>full</DebugType>\n    <PlatformTarget>x64</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Release|x64'\">\n    <OutputPath>..\\bin\\x64\\Release\\Plugins\\</OutputPath>\n    <DefineConstants>\n    </DefineConstants>\n    <Optimize>true</Optimize>\n    <DebugType>pdbonly</DebugType>\n    <PlatformTarget>x64</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Newtonsoft.Json.12.0.1\\lib\\net45\\Newtonsoft.Json.dll</HintPath>\n    </Reference>\n    <Reference Include=\"OpenTK, Version=3.1.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\OpenTK.3.1.0\\lib\\net20\\OpenTK.dll</HintPath>\n    </Reference>\n    <Reference Include=\"PresentationCore\" />\n    <Reference Include=\"PresentationFramework\" />\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Core\" />\n    <Reference Include=\"System.Drawing\" />\n    <Reference Include=\"System.Xaml\" />\n    <Reference Include=\"System.Xml.Linq\" />\n    <Reference Include=\"System.Data.DataSetExtensions\" />\n    <Reference Include=\"Microsoft.CSharp\" />\n    <Reference Include=\"System.Data\" />\n    <Reference Include=\"System.Net.Http\" />\n    <Reference Include=\"System.Xml\" />\n    <Reference Include=\"WindowsBase\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"Properties\\AssemblyInfo.cs\" />\n    <Compile Include=\"Properties\\Resources.Designer.cs\">\n      <AutoGen>True</AutoGen>\n      <DesignTime>True</DesignTime>\n      <DependentUpon>Resources.resx</DependentUpon>\n    </Compile>\n    <Compile Include=\"Render.cs\" />\n    <Compile Include=\"Settings.cs\" />\n    <Compile Include=\"SettingsCtrl.xaml.cs\">\n      <DependentUpon>SettingsCtrl.xaml</DependentUpon>\n    </Compile>\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"OpenTK.dll.config\" />\n    <None Include=\"packages.config\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\BMEngine\\ZenithEngine.csproj\">\n      <Project>{60F2212A-D56C-4776-836F-6A49453CDBC4}</Project>\n      <Name>ZenithEngine</Name>\n    </ProjectReference>\n  </ItemGroup>\n  <ItemGroup>\n    <Content Include=\"preview.png\" />\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Properties\\Resources.resx\">\n      <Generator>ResXFileCodeGenerator</Generator>\n      <LastGenOutput>Resources.Designer.cs</LastGenOutput>\n    </EmbeddedResource>\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Languages\\en\\flat.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </EmbeddedResource>\n    <Page Include=\"SettingsCtrl.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </Page>\n  </ItemGroup>\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n  <PropertyGroup>\n    <PostBuildEvent>\n    </PostBuildEvent>\n  </PropertyGroup>\n</Project>"
  },
  {
    "path": "FlatRender/Languages/en/flat.xaml",
    "content": "﻿<ResourceDictionary\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" \n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n    xmlns:local=\"using:MSDNSample\"\n    xmlns:system=\"clr-namespace:System;assembly=mscorlib\">\n\n    <!--Other settings-->\n    <system:String x:Key=\"firstNote\">First Note</system:String>\n    <system:String x:Key=\"lastNote\">Last Note</system:String>\n    <system:String x:Key=\"pianoHeight\">Piano Height %</system:String>\n    <system:String x:Key=\"noteScreenTime\">Note screen time</system:String>\n    <system:String x:Key=\"sameWidthNotes\">Same Width Notes</system:String>\n    <system:String x:Key=\"save\">Save</system:String>\n    <system:String x:Key=\"loadSaved\">Load Saved</system:String>\n    <system:String x:Key=\"setDefaults\">Defaults</system:String>\n\n</ResourceDictionary>"
  },
  {
    "path": "FlatRender/OpenTK.dll.config",
    "content": "<configuration>\n  <dllmap os=\"linux\" dll=\"opengl32.dll\" target=\"libGL.so.1\"/>\n  <dllmap os=\"linux\" dll=\"glu32.dll\" target=\"libGLU.so.1\"/>\n  <dllmap os=\"linux\" dll=\"openal32.dll\" target=\"libopenal.so.1\"/>\n  <dllmap os=\"linux\" dll=\"alut.dll\" target=\"libalut.so.0\"/>\n  <dllmap os=\"linux\" dll=\"opencl.dll\" target=\"libOpenCL.so\"/>\n  <dllmap os=\"linux\" dll=\"libX11\" target=\"libX11.so.6\"/>\n  <dllmap os=\"linux\" dll=\"libXi\" target=\"libXi.so.6\"/>\n  <dllmap os=\"linux\" dll=\"SDL2.dll\" target=\"libSDL2-2.0.so.0\"/>\n  <dllmap os=\"osx\" dll=\"opengl32.dll\" target=\"/System/Library/Frameworks/OpenGL.framework/OpenGL\"/>\n  <dllmap os=\"osx\" dll=\"openal32.dll\" target=\"/System/Library/Frameworks/OpenAL.framework/OpenAL\" />\n  <dllmap os=\"osx\" dll=\"alut.dll\" target=\"/System/Library/Frameworks/OpenAL.framework/OpenAL\" />\n  <dllmap os=\"osx\" dll=\"libGLES.dll\" target=\"/System/Library/Frameworks/OpenGLES.framework/OpenGLES\" />\n  <dllmap os=\"osx\" dll=\"libGLESv1_CM.dll\" target=\"/System/Library/Frameworks/OpenGLES.framework/OpenGLES\" />\n  <dllmap os=\"osx\" dll=\"libGLESv2.dll\" target=\"/System/Library/Frameworks/OpenGLES.framework/OpenGLES\" />\n  <dllmap os=\"osx\" dll=\"opencl.dll\" target=\"/System/Library/Frameworks/OpenCL.framework/OpenCL\"/>\n  <dllmap os=\"osx\" dll=\"SDL2.dll\" target=\"libSDL2.dylib\"/>\n  <!-- XQuartz compatibility (X11 on Mac) -->\n  <dllmap os=\"osx\" dll=\"libGL.so.1\" target=\"/usr/X11/lib/libGL.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libX11\" target=\"/usr/X11/lib/libX11.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXcursor.so.1\" target=\"/usr/X11/lib/libXcursor.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXi\" target=\"/usr/X11/lib/libXi.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXinerama\" target=\"/usr/X11/lib/libXinerama.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXrandr.so.2\" target=\"/usr/X11/lib/libXrandr.dylib\"/>\n</configuration>\n"
  },
  {
    "path": "FlatRender/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// General Information about an assembly is controlled through the following\n// set of attributes. Change these attribute values to modify the information\n// associated with an assembly.\n[assembly: AssemblyTitle(\"FlatRender\")]\n[assembly: AssemblyDescription(\"\")]\n[assembly: AssemblyConfiguration(\"\")]\n[assembly: AssemblyCompany(\"\")]\n[assembly: AssemblyProduct(\"FlatRender\")]\n[assembly: AssemblyCopyright(\"Copyright ©  2019\")]\n[assembly: AssemblyTrademark(\"\")]\n[assembly: AssemblyCulture(\"\")]\n\n// Setting ComVisible to false makes the types in this assembly not visible\n// to COM components.  If you need to access a type in this assembly from\n// COM, set the ComVisible attribute to true on that type.\n[assembly: ComVisible(false)]\n\n// The following GUID is for the ID of the typelib if this project is exposed to COM\n[assembly: Guid(\"ae59479c-9d0d-464c-a4d3-8fdd7e1f3786\")]\n\n// Version information for an assembly consists of the following four values:\n//\n//      Major Version\n//      Minor Version\n//      Build Number\n//      Revision\n//\n// You can specify all the values or you can default the Build and Revision Numbers\n// by using the '*' as shown below:\n// [assembly: AssemblyVersion(\"1.0.*\")]\n[assembly: AssemblyVersion(\"1.0.0.0\")]\n[assembly: AssemblyFileVersion(\"1.0.0.0\")]\n"
  },
  {
    "path": "FlatRender/Properties/Resources.Designer.cs",
    "content": "﻿//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace FlatRender.Properties {\n    using System;\n    \n    \n    /// <summary>\n    ///   A strongly-typed resource class, for looking up localized strings, etc.\n    /// </summary>\n    // This class was auto-generated by the StronglyTypedResourceBuilder\n    // class via a tool like ResGen or Visual Studio.\n    // To add or remove a member, edit your .ResX file then rerun ResGen\n    // with the /str option, or rebuild your VS project.\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"System.Resources.Tools.StronglyTypedResourceBuilder\", \"15.0.0.0\")]\n    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]\n    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]\n    internal class Resources {\n        \n        private static global::System.Resources.ResourceManager resourceMan;\n        \n        private static global::System.Globalization.CultureInfo resourceCulture;\n        \n        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute(\"Microsoft.Performance\", \"CA1811:AvoidUncalledPrivateCode\")]\n        internal Resources() {\n        }\n        \n        /// <summary>\n        ///   Returns the cached ResourceManager instance used by this class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Resources.ResourceManager ResourceManager {\n            get {\n                if (object.ReferenceEquals(resourceMan, null)) {\n                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager(\"FlatRender.Properties.Resources\", typeof(Resources).Assembly);\n                    resourceMan = temp;\n                }\n                return resourceMan;\n            }\n        }\n        \n        /// <summary>\n        ///   Overrides the current thread's CurrentUICulture property for all\n        ///   resource lookups using this strongly typed resource class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Globalization.CultureInfo Culture {\n            get {\n                return resourceCulture;\n            }\n            set {\n                resourceCulture = value;\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized resource of type System.Drawing.Bitmap.\n        /// </summary>\n        internal static System.Drawing.Bitmap preview {\n            get {\n                object obj = ResourceManager.GetObject(\"preview\", resourceCulture);\n                return ((System.Drawing.Bitmap)(obj));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "FlatRender/Properties/Resources.resx",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The primary goals of this format is to allow a simple XML format \n    that is mostly human readable. The generation and parsing of the \n    various data types are done through the TypeConverter classes \n    associated with the data types.\n    \n    Example:\n    \n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n                \n    There are any number of \"resheader\" rows that contain simple \n    name/value pairs.\n    \n    Each data row contains a name, and value. The row also contains a \n    type or mimetype. Type corresponds to a .NET class that support \n    text/value conversion through the TypeConverter architecture. \n    Classes that don't support this are serialized and stored with the \n    mimetype set.\n    \n    The mimetype is used for serialized objects, and tells the \n    ResXResourceReader how to depersist the object. This is currently not \n    extensible. For a given mimetype the value must be set accordingly:\n    \n    Note - application/x-microsoft.net.object.binary.base64 is the format \n    that the ResXResourceWriter will generate, however the reader can \n    read any of the formats listed below.\n    \n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n    \n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array \n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <assembly alias=\"System.Windows.Forms\" name=\"System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" />\n  <data name=\"preview\" type=\"System.Resources.ResXFileRef, System.Windows.Forms\">\n    <value>..\\preview.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>\n  </data>\n</root>"
  },
  {
    "path": "FlatRender/README.md",
    "content": "# Zenith-Flat-Render\n"
  },
  {
    "path": "FlatRender/Render.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing OpenTK;\nusing OpenTK.Graphics;\nusing OpenTK.Graphics.OpenGL;\nusing ZenithEngine;\nusing System.Windows.Media;\nusing System.Drawing;\nusing System.Windows.Interop;\nusing System.Windows;\nusing System.IO;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Controls;\n\nnamespace FlatRender\n{\n    public class Render : IPluginRender\n    {\n        #region PreviewConvert\n        BitmapImage BitmapToImageSource(Bitmap bitmap)\n        {\n            using (MemoryStream memory = new MemoryStream())\n            {\n                bitmap.Save(memory, System.Drawing.Imaging.ImageFormat.Bmp);\n                memory.Position = 0;\n                BitmapImage bitmapimage = new BitmapImage();\n                bitmapimage.BeginInit();\n                bitmapimage.StreamSource = memory;\n                bitmapimage.CacheOption = BitmapCacheOption.OnLoad;\n                bitmapimage.EndInit();\n\n                return bitmapimage;\n            }\n        }\n        #endregion\n\n        #region Info\n        public string Name { get; } = \"Flat\";\n        public string Description { get; } = \"Flat renderer, requested by SquareWaveMidis for his channel\";\n        public bool Initialized { get; private set; } = false;\n        public ImageSource PreviewImage { get; private set; }\n        public string LanguageDictName { get; } = \"flat\";\n        #endregion\n\n        #region Shaders\n        string noteShaderVert = @\"#version 330 compatibility\n\nlayout(location = 0) in vec3 position;\nlayout(location = 1) in vec4 glColor;\n\nout vec4 color;\n\nvoid main()\n{\n    gl_Position = vec4(position.x * 2 - 1, position.y * 2 - 1, 1.0f, 1.0f);\n    color = glColor;\n}\n\";\n        string noteShaderFrag = @\"#version 330 compatibility\n \nin vec4 color;\n \nout vec4 outputF;\nlayout(location = 0) out vec4 texOut;\n\nvoid main()\n{\n    outputF = color;\n\ttexOut = outputF;\n}\n\";\n        #endregion\n\n        RenderSettings renderSettings;\n        Settings settings;\n\n        SettingsCtrl settingsControl;\n\n        public long LastNoteCount { get; private set; }\n\n        public Control SettingsControl { get { return settingsControl; } }\n\n        public double NoteCollectorOffset => 0;\n\n        public bool ManualNoteDelete => false;\n\n        public NoteColor[][] NoteColors { get; set; }\n\n        public double NoteScreenTime => settings.deltaTimeOnScreen;\n\n        public double Tempo { get; set; }\n\n        public MidiInfo CurrentMidi { get; set; }\n\n        int noteShader;\n\n        int vertexBufferID;\n        int colorBufferID;\n\n        int quadBufferLength = 2048 * 2;\n        double[] quadVertexbuff;\n        float[] quadColorbuff;\n        int quadBufferPos = 0;\n\n        int indexBufferId;\n        uint[] indexes = new uint[2048 * 4 * 6];\n\n        bool[] blackKeys = new bool[257];\n        int[] keynum = new int[257];\n\n        public void Dispose()\n        {\n            if (!Initialized) return;\n            GL.DeleteBuffers(3, new int[] { vertexBufferID, colorBufferID });\n            GL.DeleteProgram(noteShader);\n            quadVertexbuff = null;\n            quadColorbuff = null;\n            Initialized = false;\n            Console.WriteLine(\"Disposed of FlatRender\");\n        }\n\n        public Render(RenderSettings settings)\n        {\n            this.renderSettings = settings;\n            this.settings = new Settings();\n            PreviewImage = BitmapToImageSource(Properties.Resources.preview);\n            settingsControl = new SettingsCtrl(this.settings);\n            ((SettingsCtrl)SettingsControl).PaletteChanged += () => { ReloadTrackColors(); };\n            for (int i = 0; i < blackKeys.Length; i++) blackKeys[i] = isBlackNote(i);\n            int b = 0;\n            int w = 0;\n            for (int i = 0; i < keynum.Length; i++)\n            {\n                if (blackKeys[i]) keynum[i] = b++;\n                else keynum[i] = w++;\n            }\n        }\n\n        public void Init()\n        {\n            int _vertexObj = GL.CreateShader(ShaderType.VertexShader);\n            int _fragObj = GL.CreateShader(ShaderType.FragmentShader);\n            int statusCode;\n            string info;\n\n            GL.ShaderSource(_vertexObj, noteShaderVert);\n            GL.CompileShader(_vertexObj);\n            info = GL.GetShaderInfoLog(_vertexObj);\n            GL.GetShader(_vertexObj, ShaderParameter.CompileStatus, out statusCode);\n            if (statusCode != 1) throw new ApplicationException(info);\n\n            GL.ShaderSource(_fragObj, noteShaderFrag);\n            GL.CompileShader(_fragObj);\n            info = GL.GetShaderInfoLog(_fragObj);\n            GL.GetShader(_fragObj, ShaderParameter.CompileStatus, out statusCode);\n            if (statusCode != 1) throw new ApplicationException(info);\n\n            noteShader = GL.CreateProgram();\n            GL.AttachShader(noteShader, _fragObj);\n            GL.AttachShader(noteShader, _vertexObj);\n            GL.LinkProgram(noteShader);\n\n            quadVertexbuff = new double[quadBufferLength * 8];\n            quadColorbuff = new float[quadBufferLength * 16];\n\n            GL.GenBuffers(1, out vertexBufferID);\n            GL.GenBuffers(1, out colorBufferID);\n            for (uint i = 0; i < indexes.Length / 6; i++)\n            {\n                indexes[i * 6 + 0] = i * 4 + 0;\n                indexes[i * 6 + 1] = i * 4 + 1;\n                indexes[i * 6 + 2] = i * 4 + 3;\n                indexes[i * 6 + 3] = i * 4 + 1;\n                indexes[i * 6 + 4] = i * 4 + 3;\n                indexes[i * 6 + 5] = i * 4 + 2;\n            }\n\n            GL.GenBuffers(1, out indexBufferId);\n            GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexBufferId);\n            GL.BufferData(\n                BufferTarget.ElementArrayBuffer,\n                (IntPtr)(indexes.Length * 4),\n                indexes,\n                BufferUsageHint.StaticDraw);\n            Initialized = true;\n            Console.WriteLine(\"Initialised FlatRender\");\n        }\n\n        public void RenderFrame(FastList<Note> notes, double midiTime, int finalCompositeBuff)\n        {\n            GL.Enable(EnableCap.Blend);\n            GL.EnableClientState(ArrayCap.VertexArray);\n            GL.EnableClientState(ArrayCap.ColorArray);\n            GL.Enable(EnableCap.Texture2D);\n\n            GL.EnableVertexAttribArray(0);\n            GL.EnableVertexAttribArray(1);\n\n            GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);\n\n            GL.BindFramebuffer(FramebufferTarget.Framebuffer, finalCompositeBuff);\n            GL.Viewport(0, 0, renderSettings.width, renderSettings.height);\n            GL.Clear(ClearBufferMask.ColorBufferBit);\n            \n            GL.UseProgram(noteShader);\n\n            #region Vars\n            long nc = 0;\n            int firstNote = settings.firstNote;\n            int lastNote = settings.lastNote;\n            int kbfirstNote = settings.firstNote;\n            int kblastNote = settings.lastNote;\n            if (blackKeys[firstNote]) kbfirstNote--;\n            if (blackKeys[lastNote - 1]) kblastNote++;\n\n            double deltaTimeOnScreen = NoteScreenTime;\n            double pianoHeight = settings.pianoHeight;\n            bool sameWidth = settings.sameWidthNotes;\n            Color4[] keyColors = new Color4[514];\n            for (int i = 0; i < 514; i++) keyColors[i] = Color4.Transparent;\n            double wdth;\n            float r, g, b, a;\n            float r2, g2, b2, a2;\n            double x1;\n            double x2;\n            double y1;\n            double y2;\n            quadBufferPos = 0;\n\n            double[] x1array = new double[257];\n            double[] wdtharray = new double[257];\n            if (settings.sameWidthNotes)\n            {\n                for (int i = 0; i < 257; i++)\n                {\n                    x1array[i] = (float)(i - firstNote) / (lastNote - firstNote);\n                    wdtharray[i] = (float)(1.0 / (lastNote - firstNote)) + 0.0005;\n                }\n            }\n            else\n            {\n                double knmfn = keynum[firstNote];\n                double knmln = keynum[lastNote - 1];\n                if (blackKeys[firstNote]) knmfn = keynum[firstNote - 1] + 0.5;\n                if (blackKeys[lastNote - 1]) knmln = keynum[lastNote] - 0.5;\n                for (int i = 0; i < 257; i++)\n                {\n                    if (!blackKeys[i])\n                    {\n                        x1array[i] = (float)(keynum[i] - knmfn) / (knmln - knmfn + 1);\n                        wdtharray[i] = 1.0f / (knmln - knmfn + 1);\n                    }\n                    else\n                    {\n                        int _i = i + 1;\n                        wdth = 0.6f / (knmln - knmfn + 1);\n                        int bknum = keynum[i] % 5;\n                        double offset = wdth / 2;\n                        if (bknum == 0 || bknum == 2)\n                        {\n                            offset *= 1.3;\n                        }\n                        else if (bknum == 1 || bknum == 4)\n                        {\n                            offset *= 0.7;\n                        }\n                        x1array[i] = (float)(keynum[_i] - knmfn) / (knmln - knmfn + 1) - offset;\n                        wdtharray[i] = wdth + 0.0005;\n                    }\n                }\n            }\n            #endregion\n\n            #region Notes\n            quadBufferPos = 0;\n            double notePosFactor = 1 / deltaTimeOnScreen * (1 - pianoHeight);\n            foreach (Note n in notes)\n            {\n                double renderCutoff = midiTime + deltaTimeOnScreen;\n                if (n.end >= midiTime || !n.hasEnded)\n                { \n                    if (n.start < renderCutoff)\n                    {\n                        if (n.key >= firstNote && n.key < lastNote)\n                        {\n                            unsafe\n                            {\n                                nc++;\n                                int k = n.key;\n                                if (!(k >= firstNote && k < lastNote)) continue;\n                                Color4 coll = n.color.left;\n                                Color4 colr = n.color.right;\n                                if (n.start < midiTime)\n                                {\n                                    Color4 origcoll = keyColors[k * 2];\n                                    Color4 origcolr = keyColors[k * 2 + 1];\n                                    float blendfac = coll.A;\n                                    float revblendfac = 1 - blendfac;\n                                    keyColors[k * 2] = new Color4(\n                                        coll.R * blendfac + origcoll.R * revblendfac,\n                                        coll.G * blendfac + origcoll.G * revblendfac,\n                                        coll.B * blendfac + origcoll.B * revblendfac,\n                                        1);\n                                    blendfac = colr.A;\n                                    revblendfac = 1 - blendfac;\n                                    keyColors[k * 2 + 1] = new Color4(\n                                        colr.R * blendfac + origcolr.R * revblendfac,\n                                        colr.G * blendfac + origcolr.G * revblendfac,\n                                        colr.B * blendfac + origcolr.B * revblendfac,\n                                        1);\n                                }\n                                x1 = x1array[k];\n                                wdth = wdtharray[k];\n                                x2 = x1 + wdth;\n                                y1 = 1 - (renderCutoff - n.end) * notePosFactor;\n                                y2 = 1 - (renderCutoff - n.start) * notePosFactor;\n                                if (!n.hasEnded)\n                                    y1 = 1;\n\n                                int pos = quadBufferPos * 8;\n                                quadVertexbuff[pos++] = x2;\n                                quadVertexbuff[pos++] = y2;\n                                quadVertexbuff[pos++] = x2;\n                                quadVertexbuff[pos++] = y1;\n                                quadVertexbuff[pos++] = x1;\n                                quadVertexbuff[pos++] = y1;\n                                quadVertexbuff[pos++] = x1;\n                                quadVertexbuff[pos++] = y2;\n\n                                pos = quadBufferPos * 16;\n                                r = coll.R;\n                                g = coll.G;\n                                b = coll.B;\n                                a = coll.A;\n                                quadColorbuff[pos++] = r;\n                                quadColorbuff[pos++] = g;\n                                quadColorbuff[pos++] = b;\n                                quadColorbuff[pos++] = a;\n                                quadColorbuff[pos++] = r;\n                                quadColorbuff[pos++] = g;\n                                quadColorbuff[pos++] = b;\n                                quadColorbuff[pos++] = a;\n                                r = colr.R;\n                                g = colr.G;\n                                b = colr.B;\n                                a = colr.A;\n                                quadColorbuff[pos++] = r;\n                                quadColorbuff[pos++] = g;\n                                quadColorbuff[pos++] = b;\n                                quadColorbuff[pos++] = a;\n                                quadColorbuff[pos++] = r;\n                                quadColorbuff[pos++] = g;\n                                quadColorbuff[pos++] = b;\n                                quadColorbuff[pos++] = a;\n\n                                quadBufferPos++;\n                            }\n                            FlushQuadBuffer();\n                        }\n                    }\n                    else break;\n                }\n            }\n            FlushQuadBuffer(false);\n            quadBufferPos = 0;\n\n            LastNoteCount = nc;\n            #endregion\n\n            #region Keyboard\n            y1 = pianoHeight;\n            y2 = 0;\n            Color4[] origColors = new Color4[257];\n            for (int k = kbfirstNote; k < kblastNote; k++)\n            {\n                if (isBlackNote(k))\n                    origColors[k] = Color4.Black;\n                else\n                    origColors[k] = Color4.White;\n            }\n\n            for (int n = kbfirstNote; n < kblastNote; n++)\n            {\n                x1 = x1array[n];\n                wdth = wdtharray[n];\n                x2 = x1 + wdth;\n\n                if (!blackKeys[n])\n                {\n                    y2 = 0;\n                    if (settings.sameWidthNotes)\n                    {\n                        int _n = n % 12;\n                        if (_n == 0)\n                            x2 += wdth * 0.666;\n                        else if (_n == 2)\n                        {\n                            x1 -= wdth / 3;\n                            x2 += wdth / 3;\n                        }\n                        else if (_n == 4)\n                            x1 -= wdth / 3 * 2;\n                        else if (_n == 5)\n                            x2 += wdth * 0.75;\n                        else if (_n == 7)\n                        {\n                            x1 -= wdth / 4;\n                            x2 += wdth / 2;\n                        }\n                        else if (_n == 9)\n                        {\n                            x1 -= wdth / 2;\n                            x2 += wdth / 4;\n                        }\n                        else if (_n == 11)\n                            x1 -= wdth * 0.75;\n                    }\n                }\n                else continue;\n\n                var coll = keyColors[n * 2];\n                var colr = keyColors[n * 2 + 1];\n                var origcol = origColors[n];\n                float blendfac = coll.A;\n                float revblendfac = 1 - blendfac;\n                coll = new Color4(\n                    coll.R * blendfac + origcol.R * revblendfac,\n                    coll.G * blendfac + origcol.G * revblendfac,\n                    coll.B * blendfac + origcol.B * revblendfac,\n                    1);\n                r = coll.R;\n                g = coll.G;\n                b = coll.B;\n                a = coll.A;\n                blendfac = colr.A;\n                revblendfac = 1 - blendfac;\n                colr = new Color4(\n                    colr.R * blendfac + origcol.R * revblendfac,\n                    colr.G * blendfac + origcol.G * revblendfac,\n                    colr.B * blendfac + origcol.B * revblendfac,\n                    1);\n                r2 = colr.R;\n                g2 = colr.G;\n                b2 = colr.B;\n                a2 = colr.A;\n\n                int pos = quadBufferPos * 8;\n                quadVertexbuff[pos++] = x1;\n                quadVertexbuff[pos++] = y2;\n                quadVertexbuff[pos++] = x2;\n                quadVertexbuff[pos++] = y2;\n                quadVertexbuff[pos++] = x2;\n                quadVertexbuff[pos++] = y1;\n                quadVertexbuff[pos++] = x1;\n                quadVertexbuff[pos++] = y1;\n\n                pos = quadBufferPos * 16;\n                quadColorbuff[pos++] = r;\n                quadColorbuff[pos++] = g;\n                quadColorbuff[pos++] = b;\n                quadColorbuff[pos++] = a;\n                quadColorbuff[pos++] = r;\n                quadColorbuff[pos++] = g;\n                quadColorbuff[pos++] = b;\n                quadColorbuff[pos++] = a;\n                quadColorbuff[pos++] = r2;\n                quadColorbuff[pos++] = g2;\n                quadColorbuff[pos++] = b2;\n                quadColorbuff[pos++] = a2;\n                quadColorbuff[pos++] = r2;\n                quadColorbuff[pos++] = g2;\n                quadColorbuff[pos++] = b2;\n                quadColorbuff[pos++] = a2;\n                quadBufferPos++;\n                FlushQuadBuffer();\n            }\n            for (int n = kbfirstNote; n < kblastNote; n++)\n            {\n                x1 = x1array[n];\n                wdth = wdtharray[n];\n                x2 = x1 + wdth;\n\n                if (blackKeys[n])\n                {\n                    y2 = pianoHeight / 10 * 3.7;\n                }\n                else continue;\n\n                var coll = keyColors[n * 2];\n                var colr = keyColors[n * 2 + 1];\n                var origcol = origColors[n];\n                float blendfac = coll.A;\n                float revblendfac = 1 - blendfac;\n                coll = new Color4(\n                    coll.R * blendfac + origcol.R * revblendfac,\n                    coll.G * blendfac + origcol.G * revblendfac,\n                    coll.B * blendfac + origcol.B * revblendfac,\n                    1);\n                r = coll.R;\n                g = coll.G;\n                b = coll.B;\n                a = coll.A;\n                blendfac = colr.A;\n                revblendfac = 1 - blendfac;\n                colr = new Color4(\n                    colr.R * blendfac + origcol.R * revblendfac,\n                    colr.G * blendfac + origcol.G * revblendfac,\n                    colr.B * blendfac + origcol.B * revblendfac,\n                    1);\n                r2 = colr.R;\n                g2 = colr.G;\n                b2 = colr.B;\n                a2 = colr.A;\n                \n                int pos = quadBufferPos * 8;\n                quadVertexbuff[pos++] = x1;\n                quadVertexbuff[pos++] = y2;\n                quadVertexbuff[pos++] = x2;\n                quadVertexbuff[pos++] = y2;\n                quadVertexbuff[pos++] = x2;\n                quadVertexbuff[pos++] = y1;\n                quadVertexbuff[pos++] = x1;\n                quadVertexbuff[pos++] = y1;\n\n                pos = quadBufferPos * 16;\n                quadColorbuff[pos++] = r;\n                quadColorbuff[pos++] = g;\n                quadColorbuff[pos++] = b;\n                quadColorbuff[pos++] = a;\n                quadColorbuff[pos++] = r;\n                quadColorbuff[pos++] = g;\n                quadColorbuff[pos++] = b;\n                quadColorbuff[pos++] = a;\n                quadColorbuff[pos++] = r2;\n                quadColorbuff[pos++] = g2;\n                quadColorbuff[pos++] = b2;\n                quadColorbuff[pos++] = a2;\n                quadColorbuff[pos++] = r2;\n                quadColorbuff[pos++] = g2;\n                quadColorbuff[pos++] = b2;\n                quadColorbuff[pos++] = a2;\n                quadBufferPos++;\n                FlushQuadBuffer();\n            }\n            FlushQuadBuffer(false);\n            #endregion\n            \n            GL.Disable(EnableCap.Blend);\n            GL.DisableClientState(ArrayCap.VertexArray);\n            GL.DisableClientState(ArrayCap.ColorArray);\n            GL.Disable(EnableCap.Texture2D);\n\n            GL.DisableVertexAttribArray(0);\n            GL.DisableVertexAttribArray(1);\n        }\n\n        void FlushQuadBuffer(bool check = true)\n        {\n            if (quadBufferPos < quadBufferLength && check) return;\n            if (quadBufferPos == 0) return;\n            GL.BindBuffer(BufferTarget.ArrayBuffer, vertexBufferID);\n            GL.BufferData(\n                BufferTarget.ArrayBuffer,\n                (IntPtr)(quadBufferPos * 8 * 8),\n                quadVertexbuff,\n                BufferUsageHint.StaticDraw);\n            GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Double, false, 16, 0);\n            GL.BindBuffer(BufferTarget.ArrayBuffer, colorBufferID);\n            GL.BufferData(\n                BufferTarget.ArrayBuffer,\n                (IntPtr)(quadBufferPos * 16 * 4),\n                quadColorbuff,\n                BufferUsageHint.StaticDraw);\n            GL.VertexAttribPointer(1, 4, VertexAttribPointerType.Float, false, 16, 0);\n            GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexBufferId);\n            GL.IndexPointer(IndexPointerType.Int, 1, 0);\n            GL.DrawElements(PrimitiveType.Triangles, quadBufferPos * 6, DrawElementsType.UnsignedInt, IntPtr.Zero);\n            quadBufferPos = 0;\n        }\n\n        bool isBlackNote(int n)\n        {\n            n = n % 12;\n            return n == 1 || n == 3 || n == 6 || n == 8 || n == 10;\n        }\n\n        public void ReloadTrackColors()\n        {\n            if (NoteColors == null) return;\n            var cols = ((SettingsCtrl)SettingsControl).paletteList.GetColors(NoteColors.Length);\n\n            for (int i = 0; i < NoteColors.Length; i++)\n            {\n                for (int j = 0; j < NoteColors[i].Length; j++)\n                {\n                    if (NoteColors[i][j].isDefault)\n                    {\n                        NoteColors[i][j].left = cols[i * 32 + j * 2];\n                        NoteColors[i][j].right = cols[i * 32 + j * 2 + 1];\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "FlatRender/Settings.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace FlatRender\n{\n    public class Settings\n    {\n        public int firstNote = 0;\n        public int lastNote = 128;\n        public double pianoHeight = 0.16;\n        public double deltaTimeOnScreen = 300;\n        public bool sameWidthNotes = true;\n\n        public string palette = \"Random\";\n\n        public float noteBrightness = 1;\n    }\n}\n"
  },
  {
    "path": "FlatRender/SettingsCtrl.xaml",
    "content": "﻿<UserControl\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" \n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\" \n             xmlns:ui=\"clr-namespace:ZenithEngine.UI;assembly=ZenithEngine\"\n             xmlns:local=\"clr-namespace:FlatRender\"\n             xmlns:xctk=\"http://schemas.xceed.com/wpf/xaml/toolkit\"\n    xmlns:bme=\"clr-namespace:ZenithEngine;assembly=ZenithEngine\" x:Class=\"FlatRender.SettingsCtrl\"\n             mc:Ignorable=\"d\" Height=\"328.9\" Width=\"651.763\">\n    <UserControl.Resources>\n        <ResourceDictionary>\n            <ResourceDictionary.MergedDictionaries>\n                <ResourceDictionary>\n                    <ResourceDictionary.MergedDictionaries>\n                        <ResourceDictionary Source=\"pack://application:,,,/ZenithEngine;component/UI/Material.xaml\"/>\n                        <ResourceDictionary Source=\"pack://siteoforigin:,,,/Languages/en/flat.xaml\" />\n                    </ResourceDictionary.MergedDictionaries>\n                </ResourceDictionary>\n            </ResourceDictionary.MergedDictionaries>\n        </ResourceDictionary>\n    </UserControl.Resources>\n    <DockPanel>\n        <StackPanel Margin=\"10\">\n            <DockPanel HorizontalAlignment=\"Left\" LastChildFill=\"False\" Margin=\"0,0,0,0\">\n                <Label Content=\"{DynamicResource firstNote}\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Top\" DockPanel.Dock=\"Left\"/>\n                <ui:NumberSelect x:Name=\"firstNote\" Value=\"1\" Maximum=\"254\" Minimum=\"0\" Margin=\"5,0,0,2\" HorizontalAlignment=\"Left\" Width=\"80\" ValueChanged=\"Nud_ValueChanged\"  />\n                <Label Content=\"{DynamicResource lastNote}\" HorizontalAlignment=\"Left\" Margin=\"10,0,0,0\" VerticalAlignment=\"Top\"/>\n                <ui:NumberSelect x:Name=\"lastNote\" Value=\"1\" Maximum=\"255\" Minimum=\"1\" Margin=\"5,0,0,2\" HorizontalAlignment=\"Left\" Width=\"80\" ValueChanged=\"Nud_ValueChanged\"  />\n            </DockPanel>\n            <DockPanel HorizontalAlignment=\"Left\" LastChildFill=\"False\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\">\n                <Label Content=\"{DynamicResource noteScreenTime}\" HorizontalAlignment=\"Left\" Margin=\"0,0,0,0\" VerticalAlignment=\"Top\"/>\n                <ui:ValueSlider x:Name=\"noteDeltaScreenTime\" Maximum=\"12\" DecimalPoints=\"2\" Minimum=\"2\" TrueMin=\"1\" TrueMax=\"100000\" ValueChanged=\"NoteDeltaScreenTime_ValueChanged\" Width=\"305\" VerticalAlignment=\"Top\"/>\n            </DockPanel>\n            <DockPanel HorizontalAlignment=\"Left\" LastChildFill=\"False\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\">\n                <Label Content=\"{DynamicResource pianoHeight}\" HorizontalAlignment=\"Left\" Margin=\"0\" VerticalAlignment=\"Top\"/>\n                <ui:NumberSelect x:Name=\"pianoHeight\" Value=\"1\" Maximum=\"100\" Minimum=\"1\" Margin=\"6,0,0,2\" HorizontalAlignment=\"Left\" Width=\"80\" ValueChanged=\"Nud_ValueChanged\"  />\n            </DockPanel>\n            <ui:BetterCheckbox x:Name=\"sameWidth\" Text=\"{DynamicResource sameWidthNotes}\" HorizontalAlignment=\"Left\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\" IsChecked=\"True\" CheckToggled=\"SameWidth_Checked\"/>\n        </StackPanel>\n        <bme:NoteColorPalettePick x:FieldModifier=\"public\" x:Name=\"paletteList\" Margin=\"0,10,10,10\" HorizontalAlignment=\"Right\" Width=\"184\"/>\n    </DockPanel>\n</UserControl>\n"
  },
  {
    "path": "FlatRender/SettingsCtrl.xaml.cs",
    "content": "﻿using Newtonsoft.Json;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\n\nnamespace FlatRender\n{\n    /// <summary>\n    /// Interaction logic for SettingsCtrl.xaml\n    /// </summary>\n    public partial class SettingsCtrl : UserControl\n    {\n        Settings settings;\n\n        public event Action PaletteChanged\n        {\n            add { paletteList.PaletteChanged += value; }\n            remove { paletteList.PaletteChanged -= value; }\n        }\n\n        public void SetValues()\n        {\n            firstNote.Value = settings.firstNote;\n            lastNote.Value = settings.lastNote - 1;\n            pianoHeight.Value = (int)(settings.pianoHeight * 100);\n            noteDeltaScreenTime.Value = settings.deltaTimeOnScreen;\n            sameWidth.IsChecked = settings.sameWidthNotes;\n            paletteList.SelectImage(settings.palette);\n        }\n\n        public SettingsCtrl(Settings settings) : base()\n        {\n            InitializeComponent();\n            noteDeltaScreenTime.nudToSlider = v => Math.Log(v, 2);\n            noteDeltaScreenTime.sliderToNud = v => Math.Pow(2, v);\n            this.settings = settings;\n            paletteList.SetPath(\"Plugins\\\\Assets\\\\Palettes\");\n            SetValues();\n        }\n\n        private void Nud_ValueChanged(object sender, RoutedPropertyChangedEventArgs<decimal> e)\n        {\n            if (settings == null) return;\n                if (sender == firstNote) settings.firstNote = (int)firstNote.Value;\n                if (sender == lastNote) settings.lastNote = (int)lastNote.Value + 1;\n                if (sender == pianoHeight) settings.pianoHeight = (double)pianoHeight.Value / 100;\n                if (sender == noteDeltaScreenTime) settings.deltaTimeOnScreen = (int)noteDeltaScreenTime.Value;\n        }\n\n        private void NoteDeltaScreenTime_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)\n        {\n            if (settings == null) return;\n            settings.deltaTimeOnScreen = noteDeltaScreenTime.Value;\n        }\n\n        private void SameWidth_Checked(object sender, RoutedEventArgs e)\n        {\n            if (settings == null) return;\n                settings.sameWidthNotes = (bool)sameWidth.IsChecked;\n        }\n    }\n}\n"
  },
  {
    "path": "FlatRender/packages.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n  <package id=\"Newtonsoft.Json\" version=\"12.0.1\" targetFramework=\"net461\" />\n  <package id=\"OpenTK\" version=\"3.1.0\" targetFramework=\"net461\" />\n</packages>"
  },
  {
    "path": "LICENSE",
    "content": "# DON'T BE A DICK PUBLIC LICENSE\n\n> Version 1.1, December 2016\n\n> Copyright (C) 2020 Arduano\n\nEveryone is permitted to copy and distribute verbatim or modified\ncopies of this license document.\n\n> DON'T BE A DICK PUBLIC LICENSE\n> TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n1. Do whatever you like with the original work, just don't be a dick.\n\n   Being a dick includes - but is not limited to - the following instances:\n\n 1a. Outright copyright infringement - Don't just copy this and change the name.\n 1b. Selling the unmodified original with no work done what-so-ever, that's REALLY being a dick.\n 1c. Modifying the original work to contain hidden harmful content. That would make you a PROPER dick.\n\n2. If you become rich through modifications, related works/services, or supporting the original work,\nshare the love. Only a dick would make loads off this work and not buy the original work's\ncreator(s) a pint.\n\n3. Code is provided with no warranty. Using somebody else's code and bitching when it goes wrong makes\nyou a DONKEY dick. Fix the problem yourself. A non-dick would submit the fix back."
  },
  {
    "path": "MidiTrailRender/AuraSelect.xaml",
    "content": "﻿<UserControl\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" \n             xmlns:ui=\"clr-namespace:ZenithEngine.UI;assembly=ZenithEngine\"\n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\" \n             xmlns:local=\"clr-namespace:MIDITrailRender\"\n             xmlns:xctk=\"http://schemas.xceed.com/wpf/xaml/toolkit\" x:Class=\"MIDITrailRender.AuraSelect\"\n             mc:Ignorable=\"d\" Height=\"342.588\" Width=\"518.468\">\n    <UserControl.Resources>\n        <ResourceDictionary>\n            <ResourceDictionary.MergedDictionaries>\n                <ResourceDictionary Source=\"pack://siteoforigin:,,,/Languages/en/miditrail.xaml\" />\n                <ResourceDictionary Source=\"pack://application:,,,/ZenithEngine;component/UI/Material.xaml\"/>\n            </ResourceDictionary.MergedDictionaries>\n        </ResourceDictionary>\n    </UserControl.Resources>\n    <Grid>\n        <Grid.ColumnDefinitions>\n            <ColumnDefinition Width=\"200\"/>\n            <ColumnDefinition/>\n        </Grid.ColumnDefinitions>\n        <Image x:Name=\"imagePreview\" Grid.Column=\"1\" Margin=\"10\" Grid.RowSpan=\"2\"/>\n        <DockPanel LastChildFill=\"True\" Margin=\"10\" >\n            <TextBlock DockPanel.Dock=\"Top\" Text=\"{DynamicResource imageHint}\" Margin=\"0\" TextWrapping=\"Wrap\" VerticalAlignment=\"Top\"/>\n            <ui:BetterCheckbox x:Name=\"auraEnabled\" Text=\"{DynamicResource auraEnabled}\" HorizontalAlignment=\"Left\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\" CheckToggled=\"AuraEnabled_Checked\" DockPanel.Dock=\"Top\"/>\n            <Button x:Name=\"reload\" Content=\"{DynamicResource reload}\" Margin=\"0,10,0,0\" Height=\"26\" VerticalAlignment=\"Top\" Click=\"Reload_Click\" DockPanel.Dock=\"Top\"/>\n            <Button DockPanel.Dock=\"Bottom\" x:Name=\"openFolder\" Content=\"{DynamicResource openAuraFolder}\" Margin=\"0,5,0,0\" Height=\"26\" VerticalAlignment=\"Top\" Click=\"openFolder_Click\"/>\n            <DockPanel DockPanel.Dock=\"Bottom\" HorizontalAlignment=\"Left\" LastChildFill=\"False\" Margin=\"0,0,0,5\" VerticalAlignment=\"Top\">\n                <Label Content=\"{DynamicResource auraStrength}\" HorizontalAlignment=\"Left\" Margin=\"0\" VerticalAlignment=\"Bottom\" DockPanel.Dock=\"Left\"/>\n                <ui:NumberSelect x:Name=\"auraStrength\" Value=\"1\" Maximum=\"3\" Minimum=\"1\" Margin=\"5,0,0,2\" HorizontalAlignment=\"Left\" Width=\"77\" ValueChanged=\"AuraStrength_ValueChanged\"  DockPanel.Dock=\"Left\"  />\n            </DockPanel>\n            <ListBox x:Name=\"imagesList\" Margin=\"0,5\" SelectionChanged=\"ImagesList_SelectionChanged\"/>\n        </DockPanel>\n\n    </Grid>\n</UserControl>\n"
  },
  {
    "path": "MidiTrailRender/AuraSelect.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Drawing;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\nusing Image = System.Drawing.Image;\nusing Path = System.IO.Path;\nusing Color = System.Drawing.Color;\nusing System.Diagnostics;\nusing System.Reflection;\n\nnamespace MIDITrailRender\n{\n    /// <summary>\n    /// Interaction logic for AuraSelect.xaml\n    /// </summary>\n    public partial class AuraSelect : UserControl\n    {\n        #region PreviewConvert\n        BitmapImage BitmapToImageSource(Bitmap bitmap)\n        {\n            using (MemoryStream memory = new MemoryStream())\n            {\n                bitmap.Save(memory, System.Drawing.Imaging.ImageFormat.Bmp);\n                memory.Position = 0;\n                BitmapImage bitmapimage = new BitmapImage();\n                bitmapimage.BeginInit();\n                bitmapimage.StreamSource = memory;\n                bitmapimage.CacheOption = BitmapCacheOption.OnLoad;\n                bitmapimage.EndInit();\n\n                return bitmapimage;\n            }\n        }\n        #endregion\n\n        Settings settings;\n        List<Bitmap> images = new List<Bitmap>();\n        int selectedIndex = 0;\n        public long lastSetTime = 0;\n\n        string aurasFolder = \"Plugins\\\\Assets\\\\MIDITrail\\\\Aura\";\n\n        public string SelectedImageName => (string)((ListBoxItem)imagesList.SelectedItem).Content;\n        public Bitmap SelectedImage\n        {\n            get\n            {\n                return images[selectedIndex];\n            }\n        }\n\n        public void LoadSettings()\n        {\n            auraStrength.Value = (decimal)settings.auraStrength;\n            auraEnabled.IsChecked = settings.auraEnabled;\n            bool set = false;\n            foreach (var i in imagesList.Items)\n            {\n                if ((string)((ListBoxItem)i).Content == settings.selectedAuraImage)\n                {\n                    imagesList.SelectedItem = i;\n                    set = true;\n                    break;\n                }\n            }\n            if (!set)\n            {\n                imagesList.SelectedIndex = 0;\n            }\n        }\n\n        void ReloadImages()\n        {\n            foreach (var i in images) i.Dispose();\n            images.Clear();\n            imagesList.Items.Clear();\n            if (!Directory.Exists(aurasFolder)) Directory.CreateDirectory(aurasFolder);\n            try\n            {\n                Properties.Resources.aura_ring.Save(Path.Combine(aurasFolder, \"ring.png\"));\n            }\n            catch { }\n            var imagePaths = Directory.GetFiles(aurasFolder).Where((p) => p.EndsWith(\".png\"));\n            foreach (var i in imagePaths)\n            {\n                try\n                {\n                    using (var fs = new System.IO.FileStream(i, System.IO.FileMode.Open))\n                    {\n                        Bitmap img = new Bitmap(fs);\n                        if (img.Width != img.Height) continue;\n                        if (((int)img.PixelFormat & (int)System.Drawing.Imaging.PixelFormat.Alpha) > 0)\n                        {\n                            images.Add(img);\n                            var item = new ListBoxItem() { Content = Path.GetFileNameWithoutExtension(i) };\n                            imagesList.Items.Add(item);\n                        }\n                    }\n                }\n                catch\n                {\n\n                }\n            }\n            bool set = false;\n            foreach (var i in imagesList.Items)\n            {\n                if ((string)((ListBoxItem)i).Content == settings.selectedAuraImage)\n                {\n                    imagesList.SelectedItem = i;\n                    set = true;\n                    break;\n                }\n            }\n            if (!set)\n            {\n                imagesList.SelectedIndex = 0;\n            }\n        }\n\n        public AuraSelect(Settings settings) : base()\n        {\n            this.settings = settings;\n            InitializeComponent();\n            Resources.MergedDictionaries.Clear();\n            ReloadImages();\n        }\n\n        private void Reload_Click(object sender, RoutedEventArgs e)\n        {\n            ReloadImages();\n        }\n\n        private void ImagesList_SelectionChanged(object sender, SelectionChangedEventArgs e)\n        {\n            if (imagesList.SelectedIndex != -1)\n            {\n                selectedIndex = imagesList.SelectedIndex;\n                settings.selectedAuraImage = SelectedImageName;\n                lastSetTime = DateTime.Now.Ticks;\n                var img = new Bitmap(SelectedImage);\n                for (int i = 0; i < img.Width; i++)\n                for(int j = 0; j < img.Height; j++)\n                    {\n                        var col = img.GetPixel(i, j);\n                        img.SetPixel(i, j, Color.FromArgb(1, col.A, col.A, col.A));\n                    }\n                        imagePreview.Source = BitmapToImageSource(img);\n            }\n        }\n\n        private void AuraStrength_ValueChanged(object sender, RoutedPropertyChangedEventArgs<decimal> e)\n        {\n            try\n            {\n                settings.auraStrength = (double)auraStrength.Value;\n            }\n            catch { }\n        }\n\n        private void AuraEnabled_Checked(object sender, RoutedEventArgs e)\n        {\n            try\n            {\n                settings.auraEnabled = (bool)auraEnabled.IsChecked;\n            }\n            catch { }\n        }\n\n        private void openFolder_Click(object sender, RoutedEventArgs e)\n        {\n            if (!aurasFolder.Contains(\":\\\\\") && !aurasFolder.Contains(\":/\"))\n                Process.Start(\"explorer.exe\", System.IO.Path.Combine(System.IO.Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), aurasFolder));\n            else\n                Process.Start(\"explorer.exe\", aurasFolder);\n        }\n    }\n}\n"
  },
  {
    "path": "MidiTrailRender/Languages/en/miditrail.xaml",
    "content": "﻿<ResourceDictionary\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" \n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n    xmlns:local=\"using:MSDNSample\"\n    xmlns:system=\"clr-namespace:System;assembly=mscorlib\">\n\n    <!--Shared-->\n    <system:String x:Key=\"delete\">Delete</system:String>\n    <system:String x:Key=\"saveNew\">Save New</system:String>\n    <system:String x:Key=\"loadDefault\">Defaults</system:String>\n\n    <!--Tab Names-->\n    <system:String x:Key=\"visualsTab\">Visuals</system:String>\n    <system:String x:Key=\"cameraTab\">Camera</system:String>\n    <system:String x:Key=\"auraTab\">Aura</system:String>\n\n\n    <!--Visuals-->\n    <system:String x:Key=\"firstNote\">First Note</system:String>\n    <system:String x:Key=\"lastNote\">Last Note</system:String>\n    <system:String x:Key=\"keyDownSpeed\">Key Press Speed</system:String>\n    <system:String x:Key=\"keyUpSpeed\">Key Unpress Speed</system:String>\n    <system:String x:Key=\"tiltKeys\">Tilt Keys</system:String>\n    <system:String x:Key=\"3dNotes\">3D Notes (SLOWER!)</system:String>\n    <system:String x:Key=\"lightShade\">Light Shade</system:String>\n    <system:String x:Key=\"noteSpeed\">Note Speed</system:String>\n    <system:String x:Key=\"velocityStrength\">Strength from Note Velocity</system:String>\n    <system:String x:Key=\"keyboardClip\">Keyboard Clips Notes</system:String>\n    <system:String x:Key=\"sameWidthNotes\">Same Width Notes</system:String>\n    <system:String x:Key=\"onNoteHit\">On note hit:</system:String>\n    <system:String x:Key=\"changeSize\">Change Size</system:String>\n    <system:String x:Key=\"changeTint\">Change Tint</system:String>\n    <system:String x:Key=\"showKeyboard\">Show Keyboard</system:String>\n\n    <!--Camera-->\n    <system:String x:Key=\"speedWarning\">WARNING: This plug-in can use much more ram and CPU due to the higher note time on screen. The more notes are visible, the slower the render!</system:String>\n\n    <system:String x:Key=\"cameraPreset\">Presets:</system:String>\n    <system:String x:Key=\"farPreset\">Far</system:String>\n    <system:String x:Key=\"mediumPreset\">Medium</system:String>\n    <system:String x:Key=\"closerPreset\">Closer</system:String>\n    <system:String x:Key=\"closePreset\">Close</system:String>\n\n    <system:String x:Key=\"verticalNotes\">Vertical Notes</system:String>\n    <system:String x:Key=\"FOV\">FOV</system:String>\n    <system:String x:Key=\"viewAngle\">View Angle</system:String>\n    <system:String x:Key=\"renderDistF\">Note Render Dist. Forwards</system:String>\n    <system:String x:Key=\"renderDistB\">Note Render Dist. Backwards</system:String>\n    <system:String x:Key=\"camOffsets\">Camera Offsets</system:String>\n    <system:String x:Key=\"horizontal\">Horizontal</system:String>\n    <system:String x:Key=\"vertical\">Vertical</system:String>\n\n    <system:String x:Key=\"viewTilt\">Camera Tilt</system:String>\n    <system:String x:Key=\"viewTurn\">Camera Turn</system:String>\n    <system:String x:Key=\"viewSpin\">Camera Spin</system:String>\n    <system:String x:Key=\"offsetX\">X</system:String>\n    <system:String x:Key=\"offsetY\">Y</system:String>\n    <system:String x:Key=\"offsetZ\">Z</system:String>\n    <system:String x:Key=\"topPreset\">Top</system:String>\n    <system:String x:Key=\"perspectivePreset\">Perspective</system:String> \n\n    <!--Aura-->\n    <system:String x:Key=\"imageHint\">Image needs to be square png and only alpha is taken into account</system:String>\n    <system:String x:Key=\"auraEnabled\">Note Aura Enabled</system:String>\n    <system:String x:Key=\"reload\">Reload</system:String>\n    <system:String x:Key=\"auraStrength\">Strength:</system:String>\n    <system:String x:Key=\"openAuraFolder\">Open Aura Folder</system:String>\n</ResourceDictionary>"
  },
  {
    "path": "MidiTrailRender/MidiTrailRender.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProjectGuid>{991E132A-A56D-49AB-BBEE-9828359BB307}</ProjectGuid>\n    <OutputType>Library</OutputType>\n    <AppDesignerFolder>Properties</AppDesignerFolder>\n    <RootNamespace>MIDITrailRender</RootNamespace>\n    <AssemblyName>MIDITrailRender</AssemblyName>\n    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>\n    <FileAlignment>512</FileAlignment>\n    <Deterministic>true</Deterministic>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>..\\bin\\Any cpu\\Debug\\Plugins\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>..\\bin\\Any cpu\\Release\\Plugins\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <PlatformTarget>x64</PlatformTarget>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Debug|x86'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>..\\bin\\x86\\Debug\\Plugins\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <DebugType>full</DebugType>\n    <PlatformTarget>x86</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Release|x86'\">\n    <OutputPath>..\\bin\\x86\\Release\\Plugins\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <Optimize>true</Optimize>\n    <DebugType>pdbonly</DebugType>\n    <PlatformTarget>x86</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Debug|x64'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>..\\bin\\x64\\Debug\\Plugins\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <DebugType>full</DebugType>\n    <PlatformTarget>x64</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Release|x64'\">\n    <OutputPath>..\\bin\\x64\\Release\\Plugins\\</OutputPath>\n    <DefineConstants>\n    </DefineConstants>\n    <Optimize>true</Optimize>\n    <DebugType>pdbonly</DebugType>\n    <PlatformTarget>x64</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Newtonsoft.Json.12.0.1\\lib\\net45\\Newtonsoft.Json.dll</HintPath>\n    </Reference>\n    <Reference Include=\"OpenTK, Version=3.1.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\OpenTK.3.1.0\\lib\\net20\\OpenTK.dll</HintPath>\n    </Reference>\n    <Reference Include=\"PresentationCore\" />\n    <Reference Include=\"PresentationFramework\" />\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Core\" />\n    <Reference Include=\"System.Drawing\" />\n    <Reference Include=\"System.Xaml\" />\n    <Reference Include=\"System.Xml.Linq\" />\n    <Reference Include=\"System.Data.DataSetExtensions\" />\n    <Reference Include=\"Microsoft.CSharp\" />\n    <Reference Include=\"System.Data\" />\n    <Reference Include=\"System.Net.Http\" />\n    <Reference Include=\"System.Xml\" />\n    <Reference Include=\"WindowsBase\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"AuraSelect.xaml.cs\">\n      <DependentUpon>AuraSelect.xaml</DependentUpon>\n    </Compile>\n    <Compile Include=\"ProfileManager.cs\" />\n    <Compile Include=\"Properties\\AssemblyInfo.cs\" />\n    <Compile Include=\"Properties\\Resources.Designer.cs\">\n      <AutoGen>True</AutoGen>\n      <DesignTime>True</DesignTime>\n      <DependentUpon>Resources.resx</DependentUpon>\n    </Compile>\n    <Compile Include=\"Render.cs\" />\n    <Compile Include=\"Settings.cs\" />\n    <Compile Include=\"SettingsCtrl.xaml.cs\">\n      <DependentUpon>SettingsCtrl.xaml</DependentUpon>\n    </Compile>\n    <Compile Include=\"Util.cs\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\BMEngine\\ZenithEngine.csproj\">\n      <Project>{60f2212a-d56c-4776-836f-6a49453cdbc4}</Project>\n      <Name>ZenithEngine</Name>\n    </ProjectReference>\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"packages.config\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Page Include=\"AuraSelect.xaml\">\n      <SubType>Designer</SubType>\n      <Generator>MSBuild:Compile</Generator>\n    </Page>\n    <EmbeddedResource Include=\"Languages\\en\\miditrail.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </EmbeddedResource>\n    <Page Include=\"SettingsCtrl.xaml\">\n      <SubType>Designer</SubType>\n      <Generator>MSBuild:Compile</Generator>\n    </Page>\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Properties\\Resources.resx\">\n      <Generator>ResXFileCodeGenerator</Generator>\n      <LastGenOutput>Resources.Designer.cs</LastGenOutput>\n    </EmbeddedResource>\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"aura_ring.png\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Content Include=\"preview.png\" />\n  </ItemGroup>\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n</Project>"
  },
  {
    "path": "MidiTrailRender/OpenTK.dll.config",
    "content": "<configuration>\n  <dllmap os=\"linux\" dll=\"opengl32.dll\" target=\"libGL.so.1\"/>\n  <dllmap os=\"linux\" dll=\"glu32.dll\" target=\"libGLU.so.1\"/>\n  <dllmap os=\"linux\" dll=\"openal32.dll\" target=\"libopenal.so.1\"/>\n  <dllmap os=\"linux\" dll=\"alut.dll\" target=\"libalut.so.0\"/>\n  <dllmap os=\"linux\" dll=\"opencl.dll\" target=\"libOpenCL.so\"/>\n  <dllmap os=\"linux\" dll=\"libX11\" target=\"libX11.so.6\"/>\n  <dllmap os=\"linux\" dll=\"libXi\" target=\"libXi.so.6\"/>\n  <dllmap os=\"linux\" dll=\"SDL2.dll\" target=\"libSDL2-2.0.so.0\"/>\n  <dllmap os=\"osx\" dll=\"opengl32.dll\" target=\"/System/Library/Frameworks/OpenGL.framework/OpenGL\"/>\n  <dllmap os=\"osx\" dll=\"openal32.dll\" target=\"/System/Library/Frameworks/OpenAL.framework/OpenAL\" />\n  <dllmap os=\"osx\" dll=\"alut.dll\" target=\"/System/Library/Frameworks/OpenAL.framework/OpenAL\" />\n  <dllmap os=\"osx\" dll=\"libGLES.dll\" target=\"/System/Library/Frameworks/OpenGLES.framework/OpenGLES\" />\n  <dllmap os=\"osx\" dll=\"libGLESv1_CM.dll\" target=\"/System/Library/Frameworks/OpenGLES.framework/OpenGLES\" />\n  <dllmap os=\"osx\" dll=\"libGLESv2.dll\" target=\"/System/Library/Frameworks/OpenGLES.framework/OpenGLES\" />\n  <dllmap os=\"osx\" dll=\"opencl.dll\" target=\"/System/Library/Frameworks/OpenCL.framework/OpenCL\"/>\n  <dllmap os=\"osx\" dll=\"SDL2.dll\" target=\"libSDL2.dylib\"/>\n  <!-- XQuartz compatibility (X11 on Mac) -->\n  <dllmap os=\"osx\" dll=\"libGL.so.1\" target=\"/usr/X11/lib/libGL.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libX11\" target=\"/usr/X11/lib/libX11.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXcursor.so.1\" target=\"/usr/X11/lib/libXcursor.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXi\" target=\"/usr/X11/lib/libXi.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXinerama\" target=\"/usr/X11/lib/libXinerama.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXrandr.so.2\" target=\"/usr/X11/lib/libXrandr.dylib\"/>\n</configuration>\n"
  },
  {
    "path": "MidiTrailRender/ProfileManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing Newtonsoft.Json;\n\nnamespace MIDITrailRender\n{\n    class ProfileManager\n    {\n        string jsonPath;\n        Dictionary<string, Settings> settings = new Dictionary<string, Settings>();\n\n        public string[] Profiles => settings.Keys.ToArray();\n\n        public ProfileManager(string savePath)\n        {\n            jsonPath = savePath;\n            Load();\n        }\n\n        void injectSettings(Settings insett, Settings outsett)\n        {\n            var sourceProps = typeof(Settings).GetFields().ToList();\n            var destProps = typeof(Settings).GetFields().ToList();\n\n            foreach (var sourceProp in sourceProps)\n            {\n                if (destProps.Any(x => x.Name == sourceProp.Name))\n                {\n                    var p = destProps.First(x => x.Name == sourceProp.Name);\n                    p.SetValue(outsett, sourceProp.GetValue(insett));\n                }\n            }\n        }\n\n        public void Add(Settings sett, string name)\n        {\n            if (settings.ContainsKey(name))\n            {\n                MessageBox.Show(\"A profile with this name already exists\");\n                return;\n            }\n            var s = new Settings();\n            injectSettings(sett, s);\n            settings.Add(name, s);\n            Save();\n        }\n\n        public void Save()\n        {\n            try\n            {\n                var s = JsonConvert.SerializeObject(settings, Formatting.Indented);\n                File.WriteAllText(jsonPath, s);\n            }\n            catch\n            {\n                MessageBox.Show(\"Could not save settings. Is the file open in another program?\");\n            }\n        }\n\n        public void Load()\n        {\n            if (!File.Exists(jsonPath))\n            {\n                settings = new Dictionary<string, Settings>();\n            }\n            else\n            {\n                try\n                {\n                    var json = File.ReadAllText(jsonPath);\n                    settings = JsonConvert.DeserializeObject<Dictionary<string, Settings>>(json);\n                }\n                catch\n                {\n                    MessageBox.Show(\"Could not decode JSON settings file, loading defaults\");\n                    settings = new Dictionary<string, Settings> { { \"Default\", new Settings() } };\n                }\n            }\n        }\n\n        public void LoadProfile(string name, Settings dest)\n        {\n            if (!settings.ContainsKey(name))\n            {\n                MessageBox.Show(\"Could not poad profile\");\n                return;\n            }\n            injectSettings(settings[name], dest);\n        }\n\n        public void DeleteProfile(string name)\n        {\n            try\n            {\n                settings.Remove(name);\n                Save();\n            }\n            catch { }\n        }\n    }\n}\n"
  },
  {
    "path": "MidiTrailRender/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// General Information about an assembly is controlled through the following\n// set of attributes. Change these attribute values to modify the information\n// associated with an assembly.\n[assembly: AssemblyTitle(\"MIDITrailRender\")]\n[assembly: AssemblyDescription(\"\")]\n[assembly: AssemblyConfiguration(\"\")]\n[assembly: AssemblyCompany(\"\")]\n[assembly: AssemblyProduct(\"MIDITrailRender\")]\n[assembly: AssemblyCopyright(\"Copyright ©  2019\")]\n[assembly: AssemblyTrademark(\"\")]\n[assembly: AssemblyCulture(\"\")]\n\n// Setting ComVisible to false makes the types in this assembly not visible\n// to COM components.  If you need to access a type in this assembly from\n// COM, set the ComVisible attribute to true on that type.\n[assembly: ComVisible(false)]\n\n// The following GUID is for the ID of the typelib if this project is exposed to COM\n[assembly: Guid(\"991e132a-a56d-49ab-bbee-9828359bb307\")]\n\n// Version information for an assembly consists of the following four values:\n//\n//      Major Version\n//      Minor Version\n//      Build Number\n//      Revision\n//\n// You can specify all the values or you can default the Build and Revision Numbers\n// by using the '*' as shown below:\n// [assembly: AssemblyVersion(\"1.0.*\")]\n[assembly: AssemblyVersion(\"1.0.0.0\")]\n[assembly: AssemblyFileVersion(\"1.0.0.0\")]\n"
  },
  {
    "path": "MidiTrailRender/Properties/Resources.Designer.cs",
    "content": "﻿//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace MIDITrailRender.Properties {\n    using System;\n    \n    \n    /// <summary>\n    ///   A strongly-typed resource class, for looking up localized strings, etc.\n    /// </summary>\n    // This class was auto-generated by the StronglyTypedResourceBuilder\n    // class via a tool like ResGen or Visual Studio.\n    // To add or remove a member, edit your .ResX file then rerun ResGen\n    // with the /str option, or rebuild your VS project.\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"System.Resources.Tools.StronglyTypedResourceBuilder\", \"15.0.0.0\")]\n    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]\n    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]\n    internal class Resources {\n        \n        private static global::System.Resources.ResourceManager resourceMan;\n        \n        private static global::System.Globalization.CultureInfo resourceCulture;\n        \n        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute(\"Microsoft.Performance\", \"CA1811:AvoidUncalledPrivateCode\")]\n        internal Resources() {\n        }\n        \n        /// <summary>\n        ///   Returns the cached ResourceManager instance used by this class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Resources.ResourceManager ResourceManager {\n            get {\n                if (object.ReferenceEquals(resourceMan, null)) {\n                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager(\"MIDITrailRender.Properties.Resources\", typeof(Resources).Assembly);\n                    resourceMan = temp;\n                }\n                return resourceMan;\n            }\n        }\n        \n        /// <summary>\n        ///   Overrides the current thread's CurrentUICulture property for all\n        ///   resource lookups using this strongly typed resource class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Globalization.CultureInfo Culture {\n            get {\n                return resourceCulture;\n            }\n            set {\n                resourceCulture = value;\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized resource of type System.Drawing.Bitmap.\n        /// </summary>\n        internal static System.Drawing.Bitmap aura_ring {\n            get {\n                object obj = ResourceManager.GetObject(\"aura_ring\", resourceCulture);\n                return ((System.Drawing.Bitmap)(obj));\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized resource of type System.Drawing.Bitmap.\n        /// </summary>\n        internal static System.Drawing.Bitmap preview {\n            get {\n                object obj = ResourceManager.GetObject(\"preview\", resourceCulture);\n                return ((System.Drawing.Bitmap)(obj));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "MidiTrailRender/Properties/Resources.resx",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The primary goals of this format is to allow a simple XML format \n    that is mostly human readable. The generation and parsing of the \n    various data types are done through the TypeConverter classes \n    associated with the data types.\n    \n    Example:\n    \n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n                \n    There are any number of \"resheader\" rows that contain simple \n    name/value pairs.\n    \n    Each data row contains a name, and value. The row also contains a \n    type or mimetype. Type corresponds to a .NET class that support \n    text/value conversion through the TypeConverter architecture. \n    Classes that don't support this are serialized and stored with the \n    mimetype set.\n    \n    The mimetype is used for serialized objects, and tells the \n    ResXResourceReader how to depersist the object. This is currently not \n    extensible. For a given mimetype the value must be set accordingly:\n    \n    Note - application/x-microsoft.net.object.binary.base64 is the format \n    that the ResXResourceWriter will generate, however the reader can \n    read any of the formats listed below.\n    \n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n    \n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array \n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <assembly alias=\"System.Windows.Forms\" name=\"System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" />\n  <data name=\"aura_ring\" type=\"System.Resources.ResXFileRef, System.Windows.Forms\">\n    <value>..\\aura_ring.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>\n  </data>\n  <data name=\"preview\" type=\"System.Resources.ResXFileRef, System.Windows.Forms\">\n    <value>..\\preview.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>\n  </data>\n</root>"
  },
  {
    "path": "MidiTrailRender/README.md",
    "content": "# Zenith-MidiTrail-Render"
  },
  {
    "path": "MidiTrailRender/Render.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Drawing;\nusing System.Drawing.Imaging;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows.Controls;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing ZenithEngine;\nusing OpenTK;\nusing OpenTK.Graphics;\nusing OpenTK.Graphics.OpenGL;\n\nnamespace MIDITrailRender\n{\n    public class Render : IPluginRender\n    {\n        #region PreviewConvert\n        BitmapImage BitmapToImageSource(Bitmap bitmap)\n        {\n            using (MemoryStream memory = new MemoryStream())\n            {\n                bitmap.Save(memory, System.Drawing.Imaging.ImageFormat.Bmp);\n                memory.Position = 0;\n                BitmapImage bitmapimage = new BitmapImage();\n                bitmapimage.BeginInit();\n                bitmapimage.StreamSource = memory;\n                bitmapimage.CacheOption = BitmapCacheOption.OnLoad;\n                bitmapimage.EndInit();\n\n                return bitmapimage;\n            }\n        }\n        #endregion\n\n        public string Name => \"MIDITrail+\";\n        public string Description => \"Clone of the popular tool MIDITrail for black midi rendering. Added exclusive bonus features, and less buggy. Extremely customisable.\";\n        public string LanguageDictName { get; } = \"miditrail\";\n\n        public bool Initialized { get; set; } = false;\n\n        public ImageSource PreviewImage { get; private set; }\n\n        #region Shaders\n        string whiteKeyShaderVert = @\"#version 330 core\n\nlayout(location=0) in vec3 in_position;\nlayout(location=1) in float in_brightness;\nlayout(location=2) in float blend_fac;\n\nout vec4 v2f_color;\n\nuniform mat4 MVP;\nuniform vec4 coll;\nuniform vec4 colr;\n\nvoid main()\n{\n    gl_Position = MVP * vec4(in_position, 1.0);\n    v2f_color = vec4((coll.xyz * blend_fac + colr.xyz * (1 - blend_fac)) * in_brightness, 1);\n}\n\";\n        string whiteKeyShaderFrag = @\"#version 330 core\n\nin vec4 v2f_color;\nlayout (location=0) out vec4 out_color;\n\nvoid main()\n{\n    out_color = v2f_color;\n}\n\";\n\n        string blackKeyShaderVert = @\"#version 330 core\n\nlayout(location=0) in vec3 in_position;\nlayout(location=1) in float in_brightness;\nlayout(location=2) in float blend_fac;\n\nout vec4 v2f_color;\n\nuniform mat4 MVP;\nuniform vec4 coll;\nuniform vec4 colr;\n\nvoid main()\n{\n    gl_Position = MVP * vec4(in_position, 1.0);\n    v2f_color = vec4(1 - in_brightness + (coll.xyz * blend_fac + colr.xyz * (1 - blend_fac)) * in_brightness, 1);\n}\n\";\n        string blackKeyShaderFrag = @\"#version 330 core\n\nin vec4 v2f_color;\nlayout (location=0) out vec4 out_color;\n\nvoid main()\n{\n    out_color = v2f_color;\n}\n\";\n        string noteShaderVert = @\"#version 330 core\n\nlayout(location=0) in vec3 in_position;\nlayout(location=1) in vec4 in_color;\nlayout(location=2) in float in_shade;\n\nout vec4 v2f_color;\n\nuniform mat4 MVP;\n\nvoid main()\n{\n    gl_Position = MVP * vec4(in_position, 1.0);\n    v2f_color = vec4(in_color.xyz + in_shade, in_color.w);\n}\n\";\n        string noteShaderFrag = @\"#version 330 core\n\nin vec4 v2f_color;\nlayout (location=0) out vec4 out_color;\n\nvoid main()\n{\n    out_color = v2f_color;\n}\n\";\n        string circleShaderVert = @\"#version 330 core\n\nlayout(location=0) in vec3 in_position;\nlayout(location=1) in vec4 in_color;\nlayout(location=2) in vec2 in_uv;\n\nout vec4 v2f_color;\nout vec2 uv;\n\nuniform mat4 MVP;\n\nvoid main()\n{\n    gl_Position = MVP * vec4(in_position, 1.0);\n    v2f_color = in_color;\n    uv = in_uv;\n}\n\";\n        string circleShaderFrag = @\"#version 330 core\n\nin vec4 v2f_color;\nin vec2 uv;\n\nuniform sampler2D textureSampler;\n\nlayout (location=0) out vec4 out_color;\n\nvoid main()\n{\n    out_color = v2f_color;\n    out_color.w *=  texture2D( textureSampler, uv ).w;\n}\n\";\n\n        int MakeShader(string vert, string frag)\n        {\n            int _vertexObj = GL.CreateShader(ShaderType.VertexShader);\n            int _fragObj = GL.CreateShader(ShaderType.FragmentShader);\n            int statusCode;\n            string info;\n\n            GL.ShaderSource(_vertexObj, vert);\n            GL.CompileShader(_vertexObj);\n            info = GL.GetShaderInfoLog(_vertexObj);\n            GL.GetShader(_vertexObj, ShaderParameter.CompileStatus, out statusCode);\n            if (statusCode != 1) throw new ApplicationException(info);\n\n            GL.ShaderSource(_fragObj, frag);\n            GL.CompileShader(_fragObj);\n            info = GL.GetShaderInfoLog(_fragObj);\n            GL.GetShader(_fragObj, ShaderParameter.CompileStatus, out statusCode);\n            if (statusCode != 1) throw new ApplicationException(info);\n\n            int shader = GL.CreateProgram();\n            GL.AttachShader(shader, _fragObj);\n            GL.AttachShader(shader, _vertexObj);\n            GL.LinkProgram(shader);\n            return shader;\n        }\n        #endregion\n\n        int whiteKeyShader;\n        int blackKeyShader;\n        int noteShader;\n        int circleShader;\n\n        int uWhiteKeyMVP;\n        int uWhiteKeycoll;\n        int uWhiteKeycolr;\n\n        int uBlackKeyMVP;\n        int uBlackKeycoll;\n        int uBlackKeycolr;\n\n        int uNoteMVP;\n\n        int uCircleMVP;\n\n        public bool ManualNoteDelete => false;\n\n        public double Tempo { get; set; }\n\n        public MidiInfo CurrentMidi { get; set; }\n\n        public double NoteScreenTime => settings.viewdist * settings.deltaTimeOnScreen;\n\n        public NoteColor[][] NoteColors { get; set; }\n\n        public long LastNoteCount { get; private set; }\n\n        SettingsCtrl settingsCtrl;\n        public Control SettingsControl => settingsCtrl;\n\n        public double NoteCollectorOffset\n        {\n            get\n            {\n                if (settings.eatNotes) return 0;\n                return -settings.deltaTimeOnScreen * settings.viewback;\n            }\n        }\n\n        bool[] blackKeys = new bool[257];\n        int[] keynum = new int[257];\n\n        RenderSettings renderSettings;\n        Settings settings;\n\n        int buffer3dtex;\n        int buffer3dbuf;\n        int buffer3dbufdepth;\n\n        int[] whiteKeyVert = new int[7 * 3];\n        int whiteKeyCol;\n        int whiteKeyIndx;\n        int whiteKeyBlend;\n\n        int blackKeyVert;\n        int blackKeyCol;\n        int blackKeyIndx;\n        int blackKeyBlend;\n\n        int noteVert;\n        int noteCol;\n        int noteIndx;\n        int noteShade;\n\n        int noteBuffLen = 2048 * 256;\n\n        double[] noteVertBuff;\n        float[] noteColBuff;\n        float[] noteShadeBuff;\n        int[] noteIndxBuff;\n\n        int noteBuffPos = 0;\n\n        int circleVert;\n        int circleColor;\n        int circleUV;\n        int circleIndx;\n\n        double[] circleVertBuff;\n        float[] circleColorBuff;\n        double[] circleUVBuff;\n        int[] circleIndxBuff;\n\n        int circleBuffPos = 0;\n\n        long lastAuraTexChange;\n        int auraTex;\n\n        void loadImage(Bitmap image, int texID)\n        {\n            GL.BindTexture(TextureTarget.Texture2D, texID);\n            BitmapData data = image.LockBits(new System.Drawing.Rectangle(0, 0, image.Width, image.Height),\n                ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);\n\n            GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, data.Width, data.Height, 0,\n                OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);\n\n            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);\n            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);\n            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge);\n            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge);\n\n            image.UnlockBits(data);\n        }\n\n        public void Dispose()\n        {\n            lastAuraTexChange = 0;\n            GL.DeleteTexture(auraTex);\n\n            GL.DeleteFramebuffer(buffer3dbuf);\n            GL.DeleteTexture(buffer3dtex);\n            GL.DeleteRenderbuffer(buffer3dbufdepth);\n\n            GL.DeleteBuffers(7 * 3, whiteKeyVert);\n            GL.DeleteBuffers(11, new int[] {\n                whiteKeyCol, blackKeyVert, blackKeyCol,\n                whiteKeyIndx, blackKeyIndx, whiteKeyBlend, blackKeyBlend,\n                noteVert, noteCol, noteIndx, noteShade\n            });\n            GL.DeleteProgram(whiteKeyShader);\n            GL.DeleteProgram(blackKeyShader);\n            GL.DeleteProgram(noteShader);\n            GL.DeleteProgram(circleShader);\n\n            noteVertBuff = null;\n            noteColBuff = null;\n            noteShadeBuff = null;\n            noteIndxBuff = null;\n\n            circleVertBuff = null;\n            circleColorBuff = null;\n            circleUVBuff = null;\n            circleIndxBuff = null;\n\n            util.Dispose();\n            Initialized = false;\n            Console.WriteLine(\"Disposed of MIDITrailRender\");\n        }\n\n        Util util;\n        public Render(RenderSettings settings)\n        {\n            this.settings = new Settings();\n            this.renderSettings = settings;\n            settingsCtrl = new SettingsCtrl(this.settings);\n            ((SettingsCtrl)SettingsControl).PaletteChanged += () => { ReloadTrackColors(); };\n            PreviewImage = BitmapToImageSource(Properties.Resources.preview);\n            for (int i = 0; i < blackKeys.Length; i++) blackKeys[i] = isBlackNote(i);\n            int b = 0;\n            int w = 0;\n            for (int i = 0; i < keynum.Length; i++)\n            {\n                if (blackKeys[i]) keynum[i] = b++;\n                else keynum[i] = w++;\n            }\n        }\n\n        void ReloadAuraTexture()\n        {\n            loadImage(settingsCtrl.auraselect.SelectedImage, auraTex);\n            lastAuraTexChange = settingsCtrl.auraselect.lastSetTime;\n        }\n\n        int whiteKeyBufferLen = 0;\n        int blackKeyBufferLen = 0;\n        public void Init()\n        {\n            whiteKeyShader = MakeShader(whiteKeyShaderVert, whiteKeyShaderFrag);\n            blackKeyShader = MakeShader(blackKeyShaderVert, blackKeyShaderFrag);\n            noteShader = MakeShader(noteShaderVert, noteShaderFrag);\n            circleShader = MakeShader(circleShaderVert, circleShaderFrag);\n\n            uWhiteKeyMVP = GL.GetUniformLocation(whiteKeyShader, \"MVP\");\n            uWhiteKeycoll = GL.GetUniformLocation(whiteKeyShader, \"coll\");\n            uWhiteKeycolr = GL.GetUniformLocation(whiteKeyShader, \"colr\");\n\n            uBlackKeyMVP = GL.GetUniformLocation(blackKeyShader, \"MVP\");\n            uBlackKeycoll = GL.GetUniformLocation(blackKeyShader, \"coll\");\n            uBlackKeycolr = GL.GetUniformLocation(blackKeyShader, \"colr\");\n\n            uNoteMVP = GL.GetUniformLocation(noteShader, \"MVP\");\n            uCircleMVP = GL.GetUniformLocation(circleShader, \"MVP\");\n\n            GLUtils.GenFrameBufferTexture3d(renderSettings.width, renderSettings.height, out buffer3dbuf, out buffer3dtex, out buffer3dbufdepth);\n\n            util = new Util();\n            Initialized = true;\n            Console.WriteLine(\"Initialised MIDITrailRender\");\n\n            GL.GenBuffers(7 * 3, whiteKeyVert);\n            whiteKeyCol = GL.GenBuffer();\n            whiteKeyIndx = GL.GenBuffer();\n            whiteKeyBlend = GL.GenBuffer();\n\n            blackKeyVert = GL.GenBuffer();\n            blackKeyCol = GL.GenBuffer();\n            blackKeyIndx = GL.GenBuffer();\n            blackKeyBlend = GL.GenBuffer();\n\n            noteVert = GL.GenBuffer();\n            noteCol = GL.GenBuffer();\n            noteIndx = GL.GenBuffer();\n            noteShade = GL.GenBuffer();\n\n            circleVert = GL.GenBuffer();\n            circleColor = GL.GenBuffer();\n            circleUV = GL.GenBuffer();\n            circleIndx = GL.GenBuffer();\n\n            noteVertBuff = new double[noteBuffLen * 4 * 3];\n            noteColBuff = new float[noteBuffLen * 4 * 4];\n            noteShadeBuff = new float[noteBuffLen * 4];\n\n            noteIndxBuff = new int[noteBuffLen * 4];\n\n            circleVertBuff = new double[256 * 4 * 3];\n            circleColorBuff = new float[256 * 4 * 4];\n            circleUVBuff = new double[256 * 4 * 2];\n            circleIndxBuff = new int[256 * 4];\n\n            for (int i = 0; i < noteIndxBuff.Length; i++) noteIndxBuff[i] = i;\n            GL.BindBuffer(BufferTarget.ElementArrayBuffer, noteIndx);\n            GL.BufferData(\n                BufferTarget.ElementArrayBuffer,\n                (IntPtr)(noteIndxBuff.Length * 4),\n                noteIndxBuff,\n                BufferUsageHint.StaticDraw);\n\n            for (int i = 0; i < circleIndxBuff.Length; i++) circleIndxBuff[i] = i;\n            GL.BindBuffer(BufferTarget.ElementArrayBuffer, circleIndx);\n            GL.BufferData(\n                BufferTarget.ElementArrayBuffer,\n                (IntPtr)(circleIndxBuff.Length * 4),\n                circleIndxBuff,\n                BufferUsageHint.StaticDraw);\n\n            auraTex = GL.GenTexture();\n\n            ReloadAuraTexture();\n\n            float whitekeylen = 5.0f;\n            float blackkeylen = 6.9f;\n            float lenfac = 0.69f;\n            float blackKeyEnd = whitekeylen * lenfac;\n            float left = 0.3f;\n            float right = 0.7f;\n\n            #region White Key Model\n\n            double[] verts = new double[] {\n                //front\n                0, 0, -whitekeylen,//0\n                0, 0.7, -whitekeylen,\n                1, 0.7, -whitekeylen,\n                1, 0, -whitekeylen,\n                //front dark\n                0, 0.7, -whitekeylen,//12\n                0, 1, -whitekeylen,\n                1, 1, -whitekeylen,\n                1, 0.7, -whitekeylen,\n                //top\n                1, 1, -blackKeyEnd,//24\n                1, 1, -whitekeylen,\n                0, 1, -whitekeylen,\n                0, 1, -blackKeyEnd,\n                //notch\n                0, 1, -whitekeylen,\n                0.03, 0.95, -whitekeylen - 0.1,//36\n                0.97, 0.95, -whitekeylen - 0.1,\n                1, 1, -whitekeylen,\n\n                0, 0.90, -whitekeylen - 0.07,//48\n                0.03, 0.95, -whitekeylen - 0.1,\n                0.97, 0.95, -whitekeylen - 0.1,\n                1, 0.90, -whitekeylen - 0.07,\n                //left\n                0, 1, -blackKeyEnd,//60\n                0, 1, -whitekeylen,\n                0, 0, -whitekeylen,\n                0, 0, -blackKeyEnd,\n                //right\n                1, 1, -blackKeyEnd,//72\n                1, 1, -whitekeylen,\n                1, 0, -whitekeylen,\n                1, 0, -blackKeyEnd,\n                //back\n                left, 0, 0,//84\n                left, 1, 0,\n                right, 1, 0,\n                right, 0, 0,\n\n                //left2\n                left, 1, 0,//96\n                left, 1, -blackKeyEnd,\n                left, 0, -blackKeyEnd,\n                left, 0, 0,\n                //right2\n                right, 1, 0,//108\n                right, 1, -blackKeyEnd,\n                right, 0, -blackKeyEnd,\n                right, 0, 0,\n                //top\n                left, 1, 0,//120\n                left, 1, -blackKeyEnd,\n                right, 1, -blackKeyEnd,\n                right, 1, 0,\n                //left inner\n                0, 1, -blackKeyEnd,//96\n                left, 1, -blackKeyEnd,\n                left, 0, -blackKeyEnd,\n                0, 0, -blackKeyEnd,\n                //right inner\n                1, 1, -blackKeyEnd,//108\n                right, 1, -blackKeyEnd,\n                right, 0, -blackKeyEnd,\n                1, 0, -blackKeyEnd,\n            };\n            int[] rightIndxs = new int[] {\n                7 * 12 + 6, 7 * 12 + 9,\n                9 * 12, 9 * 12 + 3, 9 * 12 + 6, 9 * 12 + 9,\n                10 * 12 + 6, 10 * 12 + 9,\n                12 * 12 + 3, 12 * 12 + 6,\n            };\n            int[] leftIndxs = new int[] {\n                7 * 12, 7 * 12 + 3,\n                8 * 12, 8 * 12 + 3, 8 * 12 + 6, 8 * 12 + 9,\n                10 * 12, 10 * 12 + 3,\n                11 * 12 + 3, 11 * 12 + 6,\n            };\n\n            float[] cols = new float[] {\n                //front\n                0.7f,\n                0.8f,\n                0.8f,\n                0.7f,\n                //front dark\n                0.6f,\n                0.6f,\n                0.6f,\n                0.6f,\n                //top\n                1,\n                1,\n                1,\n                1,\n                //notch\n                1f,\n                0.9f,\n                0.9f,\n                1f,\n\n                0.9f,\n                0.9f,\n                0.9f,\n                0.9f,\n                //left\n                0.6f,\n                0.6f,\n                0.6f,\n                0.6f,\n                //right\n                0.6f,\n                0.6f,\n                0.6f,\n                0.6f,\n                //back\n                0.8f,\n                0.8f,\n                0.8f,\n                0.8f,\n                //left2\n                0.6f,\n                0.6f,\n                0.6f,\n                0.6f,\n                //right2\n                0.6f,\n                0.6f,\n                0.6f,\n                0.6f,\n                //top2\n                1,\n                1,\n                1,\n                1,\n                //left inner\n                0.6f,\n                0.6f,\n                0.6f,\n                0.6f,\n                //right inner\n                0.6f,\n                0.6f,\n                0.6f,\n                0.6f,\n            };\n            float[] blend = new float[] {\n                //front\n                1, 1, 1, 1,\n                //front dark\n                1, 1, 1, 1,\n                //top\n                lenfac, 1, 1, lenfac,\n                //notch\n                1, 1, 1, 1,\n\n                1, 1, 1, 1,\n                //left\n                lenfac, 1, 1, lenfac,\n                //right\n                lenfac, 1, 1, lenfac,\n                \n                //back\n                0, 0, 0, 0,\n                \n                //left2\n                0, lenfac, lenfac, 0,\n                //right2\n                0, lenfac, lenfac, 0,\n                //top2\n                0, lenfac, lenfac, 0,\n\n                //left inner\n                lenfac, lenfac, lenfac, lenfac,\n                //right inner\n                lenfac, lenfac, lenfac, lenfac,\n            };\n            int[] indexes = new int[52];\n            for (int i = 0; i < indexes.Length; i++) indexes[i] = i;\n            whiteKeyBufferLen = indexes.Length;\n\n            GL.BindBuffer(BufferTarget.ArrayBuffer, whiteKeyCol);\n            GL.BufferData(\n                BufferTarget.ArrayBuffer,\n                (IntPtr)(cols.Length * 4),\n                cols,\n                BufferUsageHint.StaticDraw);\n            GL.BindBuffer(BufferTarget.ArrayBuffer, whiteKeyBlend);\n            GL.BufferData(\n                BufferTarget.ArrayBuffer,\n                (IntPtr)(blend.Length * 4),\n                blend,\n                BufferUsageHint.StaticDraw);\n            GL.BindBuffer(BufferTarget.ElementArrayBuffer, whiteKeyIndx);\n            GL.BufferData(\n                BufferTarget.ElementArrayBuffer,\n                (IntPtr)(indexes.Length * 4),\n                indexes,\n                BufferUsageHint.StaticDraw);\n\n            float[] offsets = new float[] {\n                0, 0.6f,\n                0.2f, 0.8f,\n                0.4f, 1,\n                0, 0.55f,\n                0.15f, 0.7f,\n                0.3f, 0.85f,\n                0.45f, 1,\n            };\n            for (int i = 0; i < 7; i++)\n            {\n                foreach (var j in leftIndxs) verts[j] = offsets[i * 2];\n                foreach (var j in rightIndxs) verts[j] = offsets[i * 2 + 1];\n                GL.BindBuffer(BufferTarget.ArrayBuffer, whiteKeyVert[i]);\n                GL.BufferData(\n                    BufferTarget.ArrayBuffer,\n                    (IntPtr)(verts.Length * 8),\n                    verts,\n                    BufferUsageHint.StaticDraw);\n            }\n            for (int i = 0; i < 7; i++)\n            {\n                foreach (var j in leftIndxs) verts[j] = offsets[i * 2];\n                foreach (var j in rightIndxs) verts[j] = 1;\n                GL.BindBuffer(BufferTarget.ArrayBuffer, whiteKeyVert[i + 7]);\n                GL.BufferData(\n                    BufferTarget.ArrayBuffer,\n                    (IntPtr)(verts.Length * 8),\n                    verts,\n                    BufferUsageHint.StaticDraw);\n            }\n            for (int i = 0; i < 7; i++)\n            {\n                foreach (var j in leftIndxs) verts[j] = 0;\n                foreach (var j in rightIndxs) verts[j] = offsets[i * 2 + 1];\n                GL.BindBuffer(BufferTarget.ArrayBuffer, whiteKeyVert[i + 14]);\n                GL.BufferData(\n                    BufferTarget.ArrayBuffer,\n                    (IntPtr)(verts.Length * 8),\n                    verts,\n                    BufferUsageHint.StaticDraw);\n            }\n            #endregion\n\n            #region Black Key Model\n            verts = new double[] {\n                //front\n                0, 0, -blackkeylen,\n                0, 1, -blackkeylen + 1,\n                1, 1, -blackkeylen + 1,\n                1, 0, -blackkeylen,\n                //top\n                0, 1, -blackkeylen + 1,\n                0, 1, -0,\n                1, 1, -0,\n                1, 1, -blackkeylen + 1,\n                //left\n                0, 0, 0,\n                0, 0, -blackkeylen,\n                0, 1, -blackkeylen + 1,\n                0, 1, 0,\n                //right\n                1, 0, 0,\n                1, 0, -blackkeylen,\n                1, 1, -blackkeylen + 1,\n                1, 1, 0,\n                //back\n                0, -1, 0,\n                0, 1, 0,\n                1, 1, 0,\n                1, -1, 0,\n                //left2\n                0, 0, 0,\n                0, 0, -blackkeylen,\n                0, -1, -blackkeylen,\n                0, -1, 0,\n                //right2\n                1, 0, 0,\n                1, 0, -blackkeylen,\n                1, -1, -blackkeylen,\n                1, -1, 0,\n                //front2\n                0, 0, -blackkeylen,\n                0, -1, -blackkeylen,\n                1, -1, -blackkeylen,\n                1, 0, -blackkeylen,\n            };\n\n            cols = new float[] {\n                //front\n                0.9f,\n                0.95f,\n                0.95f,\n                0.9f,                \n                //top\n                1f,\n                0.94f,\n                0.94f,\n                1f,              \n                //left\n                0.8f,\n                0.8f,\n                0.9f,\n                0.8f,        \n                //right\n                0.8f,\n                0.8f,\n                0.9f,\n                0.8f,        \n                //back\n                0.9f,\n                0.9f,\n                0.9f,\n                0.9f,       \n                //left2\n                0.8f,\n                0.8f,\n                0.8f,\n                0.8f,        \n                //right2\n                0.8f,\n                0.8f,\n                0.8f,\n                0.8f,\n                //front2\n                0.9f,\n                1f,\n                1f,\n                0.9f,\n            };\n            blend = new float[] {\n                //front\n                1, 1, 1, 1,\n                //top\n                1, 0, 0, 1,\n                //left\n                0, 1, 1, 0,\n                //right\n                0, 1, 1, 0,\n                //back\n                0, 0, 0, 0,\n                //left2\n                0, 1, 1, 0,\n                //right2\n                0, 1, 1, 0,\n                //front2\n                1, 1, 1, 1,\n            };\n\n            indexes = new int[32];\n            for (int i = 0; i < indexes.Length; i++) indexes[i] = i;\n            blackKeyBufferLen = indexes.Length;\n\n            GL.BindBuffer(BufferTarget.ArrayBuffer, blackKeyVert);\n            GL.BufferData(\n                BufferTarget.ArrayBuffer,\n                (IntPtr)(verts.Length * 8),\n                verts,\n                BufferUsageHint.StaticDraw);\n            GL.BindBuffer(BufferTarget.ArrayBuffer, blackKeyCol);\n            GL.BufferData(\n                BufferTarget.ArrayBuffer,\n                (IntPtr)(cols.Length * 4),\n                cols,\n                BufferUsageHint.StaticDraw);\n            GL.BindBuffer(BufferTarget.ArrayBuffer, blackKeyBlend);\n            GL.BufferData(\n                BufferTarget.ArrayBuffer,\n                (IntPtr)(blend.Length * 4),\n                blend,\n                BufferUsageHint.StaticDraw);\n            GL.BindBuffer(BufferTarget.ElementArrayBuffer, blackKeyIndx);\n            GL.BufferData(\n                BufferTarget.ElementArrayBuffer,\n                (IntPtr)(indexes.Length * 4),\n                indexes,\n                BufferUsageHint.StaticDraw);\n            #endregion\n\n            keyPressFactor = new double[257];\n        }\n\n        #region Vars\n        int firstNote;\n        int lastNote;\n        bool sameWidth;\n        double deltaTimeOnScreen;\n        double noteDownSpeed;\n        double noteUpSpeed;\n        bool blockNotes;\n        bool useVel;\n        bool changeSize;\n        bool changeTint;\n        double tempoFrameStep;\n        bool eatNotes;\n        float auraStrength;\n        bool auraEnabled;\n        bool lightShade;\n        bool tiltKeys;\n\n        double fov;\n        double aspect;\n        double viewdist;\n        double viewback;\n        double viewheight;\n        double viewpan;\n        double viewoffset;\n        double camAng;\n        double camRot;\n        double camSpin;\n\n        double circleRadius;\n        #endregion\n\n        Color4[] keyColors = new Color4[514];\n        double[] x1array = new double[257];\n        double[] wdtharray = new double[257];\n        double[] keyPressFactor = new double[257];\n        double[] auraSize = new double[256];\n        public void RenderFrame(FastList<Note> notes, double midiTime, int finalCompositeBuff)\n        {\n            if (lastAuraTexChange != settingsCtrl.auraselect.lastSetTime) ReloadAuraTexture();\n            GL.Enable(EnableCap.Blend);\n            GL.EnableClientState(ArrayCap.VertexArray);\n            GL.EnableClientState(ArrayCap.ColorArray);\n            GL.Enable(EnableCap.Texture2D);\n            GL.Enable(EnableCap.DepthTest);\n            GL.DepthFunc(DepthFunction.Always);\n\n            GL.EnableVertexAttribArray(0);\n            GL.EnableVertexAttribArray(1);\n            GL.EnableVertexAttribArray(2);\n\n            GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);\n\n            GL.BindFramebuffer(FramebufferTarget.Framebuffer, buffer3dbuf);\n            GL.Viewport(0, 0, renderSettings.width, renderSettings.height);\n            GL.Clear(ClearBufferMask.ColorBufferBit);\n            GL.Clear(ClearBufferMask.DepthBufferBit);\n\n            long nc = 0;\n            firstNote = settings.firstNote;\n            lastNote = settings.lastNote;\n            sameWidth = settings.sameWidthNotes;\n            deltaTimeOnScreen = NoteScreenTime;\n            noteDownSpeed = settings.noteDownSpeed;\n            noteUpSpeed = settings.noteUpSpeed;\n            blockNotes = settings.boxNotes;\n            useVel = settings.useVel;\n            changeSize = settings.notesChangeSize;\n            changeTint = settings.notesChangeTint;\n            tempoFrameStep = 1 / (60000000 / Tempo / CurrentMidi.division) * (1000000.0 / renderSettings.fps);\n            eatNotes = settings.eatNotes;\n            auraStrength = (float)settings.auraStrength;\n            auraEnabled = settings.auraEnabled;\n            lightShade = settings.lightShade;\n            tiltKeys = settings.tiltKeys;\n\n            fov = settings.FOV;\n            aspect = (double)renderSettings.width / renderSettings.height;\n            viewdist = settings.viewdist;\n            viewback = settings.viewback;\n            viewheight = settings.viewHeight;\n            viewpan = settings.viewPan;\n            viewoffset = -settings.viewOffset;\n            camAng = settings.camAng;\n            camRot = settings.camRot;\n            camSpin = settings.camSpin;\n            fov /= 1;\n            for (int i = 0; i < 514; i++) keyColors[i] = Color4.Transparent;\n            for (int i = 0; i < 256; i++) auraSize[i] = 0;\n            for (int i = 0; i < keyPressFactor.Length; i++) keyPressFactor[i] = Math.Max(keyPressFactor[i] / 1.05 - noteUpSpeed, 0);\n            float wdth;\n            double wdthd;\n            float r, g, b, a, r2, g2, b2, a2;\n            double x1d;\n            double x2d;\n            double y1;\n            double y2;\n            Matrix4 mvp;\n            if (settings.sameWidthNotes)\n            {\n                for (int i = 0; i < 257; i++)\n                {\n                    x1array[i] = (float)(i - firstNote) / (lastNote - firstNote);\n                    wdtharray[i] = 1.0f / (lastNote - firstNote);\n                }\n                circleRadius = 1.0f / (lastNote - firstNote);\n            }\n            else\n            {\n                double knmfn = keynum[firstNote];\n                double knmln = keynum[lastNote - 1];\n                if (blackKeys[firstNote]) knmfn = keynum[firstNote - 1] + 0.5;\n                if (blackKeys[lastNote - 1]) knmln = keynum[lastNote] - 0.5;\n                for (int i = 0; i < 257; i++)\n                {\n                    if (!blackKeys[i])\n                    {\n                        x1array[i] = (float)(keynum[i] - knmfn) / (knmln - knmfn + 1);\n                        wdtharray[i] = 1.0f / (knmln - knmfn + 1);\n                    }\n                    else\n                    {\n                        int _i = i + 1;\n                        wdth = (float)(0.6f / (knmln - knmfn + 1));\n                        int bknum = keynum[i] % 5;\n                        double offset = wdth / 2;\n                        if (bknum == 0)\n                        {\n                            offset *= 1.4;\n                        }\n                        else if (bknum == 1)\n                        {\n                            offset *= 1 / 1.4;\n                        }\n                        if (bknum == 2)\n                        {\n                            offset *= 1.5;\n                        }\n                        else if (bknum == 4)\n                        {\n                            offset *= 1 / 1.5;\n                        }\n                        x1array[i] = (float)(keynum[_i] - knmfn) / (knmln - knmfn + 1) - offset;\n                        wdtharray[i] = wdth;\n                    }\n                }\n                circleRadius = (float)(0.6f / (knmln - knmfn + 1));\n            }\n\n\n            #region Notes\n            noteBuffPos = 0;\n            GL.UseProgram(noteShader);\n\n            mvp = Matrix4.Identity;\n            if (settings.verticalNotes) mvp *= Matrix4.CreateRotationX(-(float)Math.PI / 2);\n            mvp = mvp *\n                Matrix4.CreateTranslation((float)viewpan, -(float)viewheight, -(float)viewoffset) *\n                Matrix4.CreateScale(1, 1, -1) *\n                Matrix4.CreateRotationZ((float)camSpin) *\n                Matrix4.CreateRotationY((float)camRot) *\n                Matrix4.CreateRotationX((float)camAng) *\n                Matrix4.CreatePerspectiveFieldOfView((float)fov, (float)aspect, 0.01f, 400)\n                ;\n            GL.UniformMatrix4(uNoteMVP, false, ref mvp);\n\n            double renderCutoff = midiTime + deltaTimeOnScreen;\n            double renderStart = midiTime + NoteCollectorOffset;\n            double maxAuraLen = tempoFrameStep * renderSettings.fps;\n\n            if (blockNotes)\n            {\n                foreach (Note n in notes)\n                {\n                    if (n.end >= renderStart || !n.hasEnded)\n                    {\n                        if (n.start < renderCutoff)\n                        {\n                            nc++;\n                            int k = n.key;\n                            if (!(k >= firstNote && k < lastNote)) continue;\n                            Color4 coll = n.color.left;\n                            Color4 colr = n.color.right;\n                            float shade = 0;\n                            x1d = x1array[k] - 0.5;\n                            wdthd = wdtharray[k];\n                            y1 = n.end - midiTime;\n                            y2 = n.start - midiTime;\n                            if (eatNotes && y1 < 0) y1 = 0;\n                            if (eatNotes && y2 < 0) y2 = 0;\n                            if (!n.hasEnded)\n                                y1 = viewdist * deltaTimeOnScreen;\n                            y1 /= deltaTimeOnScreen / viewdist;\n                            y2 /= deltaTimeOnScreen / viewdist;\n\n                            if (x1d < -viewpan) x1d += wdthd;\n                            if (n.start < midiTime && (n.end > midiTime || !n.hasEnded))\n                            {\n                                double factor = 0.5;\n                                if (n.hasEnded)\n                                {\n                                    double len = n.end - n.start;\n                                    double offset = n.end - midiTime;\n                                    if (offset > maxAuraLen) offset = maxAuraLen;\n                                    if (len > maxAuraLen) len = maxAuraLen;\n                                    factor = Math.Pow(offset / len, 0.3);\n                                    factor /= 2;\n                                }\n                                else\n                                {\n                                    factor = 0.5;\n                                }\n                                if (changeTint)\n                                    shade = (float)(factor * 0.7);\n                                if (changeSize)\n                                {\n                                    if (x1d > 0)\n                                        x1d -= wdthd * 0.3 * factor;\n                                    else\n                                        x1d += wdthd * 0.3 * factor;\n                                }\n                            }\n                            if (lightShade)\n                                shade += 0.2f;\n                            else\n                                shade -= 0.3f;\n\n                            r = coll.R;\n                            g = coll.G;\n                            b = coll.B;\n                            a = coll.A;\n                            r2 = colr.R;\n                            g2 = colr.G;\n                            b2 = colr.B;\n                            a2 = colr.A;\n\n                            int pos = noteBuffPos * 12;\n                            noteVertBuff[pos++] = x1d;\n                            noteVertBuff[pos++] = 0;\n                            noteVertBuff[pos++] = y2;\n                            noteVertBuff[pos++] = x1d;\n                            noteVertBuff[pos++] = 0;\n                            noteVertBuff[pos++] = y1;\n                            noteVertBuff[pos++] = x1d;\n                            noteVertBuff[pos++] = -wdthd;\n                            noteVertBuff[pos++] = y1;\n                            noteVertBuff[pos++] = x1d;\n                            noteVertBuff[pos++] = -wdthd;\n                            noteVertBuff[pos++] = y2;\n\n                            pos = noteBuffPos * 16;\n                            noteColBuff[pos++] = r;\n                            noteColBuff[pos++] = g;\n                            noteColBuff[pos++] = b;\n                            noteColBuff[pos++] = a;\n                            noteColBuff[pos++] = r;\n                            noteColBuff[pos++] = g;\n                            noteColBuff[pos++] = b;\n                            noteColBuff[pos++] = a;\n                            noteColBuff[pos++] = r2;\n                            noteColBuff[pos++] = g2;\n                            noteColBuff[pos++] = b2;\n                            noteColBuff[pos++] = a2;\n                            noteColBuff[pos++] = r2;\n                            noteColBuff[pos++] = g2;\n                            noteColBuff[pos++] = b2;\n                            noteColBuff[pos++] = a2;\n\n                            pos = noteBuffPos * 4;\n                            noteShadeBuff[pos++] = shade;\n                            noteShadeBuff[pos++] = shade;\n                            noteShadeBuff[pos++] = shade;\n                            noteShadeBuff[pos++] = shade;\n\n                            noteBuffPos++;\n                            FlushNoteBuffer();\n\n                        }\n                        else break;\n                    }\n                }\n\n                FlushNoteBuffer(false);\n\n                foreach (Note n in notes)\n                {\n                    if (n.end >= renderStart || !n.hasEnded)\n                    {\n                        if (n.start < renderCutoff)\n                        {\n                            nc++;\n                            int k = n.key;\n                            if (!(k >= firstNote && k < lastNote)) continue;\n                            Color4 coll = n.color.left;\n                            Color4 colr = n.color.right;\n                            float shade = 0;\n                            x1d = x1array[k] - 0.5;\n                            wdthd = wdtharray[k];\n                            x2d = x1d + wdthd;\n                            y1 = n.end - midiTime;\n                            y2 = n.start - midiTime;\n                            if (eatNotes && y1 < 0) y1 = 0;\n                            if (eatNotes && y2 < 0) y2 = 0;\n                            if (!n.hasEnded)\n                                y1 = viewdist * deltaTimeOnScreen;\n                            y1 /= deltaTimeOnScreen / viewdist;\n                            y2 /= deltaTimeOnScreen / viewdist;\n                            if ((settings.verticalNotes && y2 < viewheight) || (!settings.verticalNotes && y2 < viewoffset)) y2 = y1;\n                            if (n.start < midiTime && (n.end > midiTime || !n.hasEnded))\n                            {\n                                double factor = 0.5;\n                                if (n.hasEnded)\n                                {\n                                    double len = n.end - n.start;\n                                    double offset = n.end - midiTime;\n                                    if (offset > maxAuraLen) offset = maxAuraLen;\n                                    if (len > maxAuraLen) len = maxAuraLen;\n                                    factor = Math.Pow(offset / len, 0.3);\n                                    factor /= 2;\n                                }\n                                else\n                                {\n                                    factor = 0.5;\n                                }\n                                if (changeTint)\n                                    shade = (float)(factor * 0.7);\n                                if (changeSize)\n                                {\n                                    x1d -= wdthd * 0.3 * factor;\n                                    x2d += wdthd * 0.3 * factor;\n                                }\n                            }\n                            shade -= 0.2f;\n\n                            r = coll.R;\n                            g = coll.G;\n                            b = coll.B;\n                            a = coll.A;\n                            r2 = colr.R;\n                            g2 = colr.G;\n                            b2 = colr.B;\n                            a2 = colr.A;\n\n                            int pos = noteBuffPos * 12;\n                            noteVertBuff[pos++] = x2d;\n                            noteVertBuff[pos++] = -wdthd;\n                            noteVertBuff[pos++] = y2;\n                            noteVertBuff[pos++] = x2d;\n                            noteVertBuff[pos++] = 0;\n                            noteVertBuff[pos++] = y2;\n                            noteVertBuff[pos++] = x1d;\n                            noteVertBuff[pos++] = 0;\n                            noteVertBuff[pos++] = y2;\n                            noteVertBuff[pos++] = x1d;\n                            noteVertBuff[pos++] = -wdthd;\n                            noteVertBuff[pos++] = y2;\n\n                            pos = noteBuffPos * 16;\n                            noteColBuff[pos++] = r;\n                            noteColBuff[pos++] = g;\n                            noteColBuff[pos++] = b;\n                            noteColBuff[pos++] = a;\n                            noteColBuff[pos++] = r;\n                            noteColBuff[pos++] = g;\n                            noteColBuff[pos++] = b;\n                            noteColBuff[pos++] = a;\n                            noteColBuff[pos++] = r2;\n                            noteColBuff[pos++] = g2;\n                            noteColBuff[pos++] = b2;\n                            noteColBuff[pos++] = a2;\n                            noteColBuff[pos++] = r2;\n                            noteColBuff[pos++] = g2;\n                            noteColBuff[pos++] = b2;\n                            noteColBuff[pos++] = a2;\n\n                            pos = noteBuffPos * 4;\n                            noteShadeBuff[pos++] = shade;\n                            noteShadeBuff[pos++] = shade;\n                            noteShadeBuff[pos++] = shade;\n                            noteShadeBuff[pos++] = shade;\n\n                            noteBuffPos++;\n                            FlushNoteBuffer();\n\n                        }\n                        else break;\n                    }\n                }\n            }\n\n            foreach (Note n in notes)\n            {\n                if (n.end >= renderStart || !n.hasEnded)\n                {\n                    if (n.start < renderCutoff)\n                    {\n                        unsafe\n                        {\n                            nc++;\n                            int k = n.key;\n                            if (!(k >= firstNote && k < lastNote)) continue;\n                            Color4 coll = n.color.left;\n                            Color4 colr = n.color.right;\n                            float shade = 0;\n\n                            x1d = x1array[k] - 0.5;\n                            wdthd = wdtharray[k];\n                            x2d = x1d + wdthd;\n                            y1 = n.end - midiTime;\n                            y2 = n.start - midiTime;\n                            if (eatNotes && y1 < 0) y1 = 0;\n                            if (eatNotes && y2 < 0) y2 = 0;\n                            if (!n.hasEnded)\n                                y1 = viewdist * deltaTimeOnScreen;\n                            y1 /= deltaTimeOnScreen / viewdist;\n                            y2 /= deltaTimeOnScreen / viewdist;\n\n                            if (n.start < midiTime && (n.end > midiTime || !n.hasEnded))\n                            {\n                                Color4 origcoll = keyColors[k * 2];\n                                Color4 origcolr = keyColors[k * 2 + 1];\n                                float blendfac = coll.A;\n                                float revblendfac = 1 - blendfac;\n                                keyColors[k * 2] = new Color4(\n                                    coll.R * blendfac + origcoll.R * revblendfac,\n                                    coll.G * blendfac + origcoll.G * revblendfac,\n                                    coll.B * blendfac + origcoll.B * revblendfac,\n                                    1);\n                                blendfac = colr.A * 0.8f;\n                                revblendfac = 1 - blendfac;\n                                keyColors[k * 2 + 1] = new Color4(\n                                    colr.R * blendfac + origcolr.R * revblendfac,\n                                    colr.G * blendfac + origcolr.G * revblendfac,\n                                    colr.B * blendfac + origcolr.B * revblendfac,\n                                    1);\n                                if (useVel)\n                                    keyPressFactor[k] = Math.Min(1, keyPressFactor[k] + noteDownSpeed * n.vel / 127.0);\n                                else\n                                    keyPressFactor[k] = Math.Min(1, keyPressFactor[k] + noteDownSpeed);\n                                double factor = 0;\n                                double factor2 = Math.Pow(Math.Max(10 - (midiTime - n.start) / tempoFrameStep, 0), 2) / 600;\n                                if (n.hasEnded)\n                                {\n                                    double len = n.end - n.start;\n                                    double offset = n.end - midiTime;\n                                    if (offset > maxAuraLen) offset = maxAuraLen;\n                                    if (len > maxAuraLen) len = maxAuraLen;\n                                    factor = Math.Pow(offset / len, 0.3);\n                                    factor /= 2;\n                                }\n                                else\n                                {\n                                    factor = 0.5;\n                                }\n\n                                if (auraSize[k] < factor + factor2) auraSize[k] = factor + factor2;\n\n                                if (changeTint)\n                                    shade = (float)(factor * 0.7);\n                                if (changeSize)\n                                {\n                                    x1d -= wdthd * 0.3 * factor;\n                                    x2d += wdthd * 0.3 * factor;\n                                }\n                            }\n\n                            r = coll.R;\n                            g = coll.G;\n                            b = coll.B;\n                            a = coll.A;\n                            r2 = colr.R;\n                            g2 = colr.G;\n                            b2 = colr.B;\n                            a2 = colr.A;\n\n                            int pos = noteBuffPos * 12;\n                            noteVertBuff[pos++] = x2d;\n                            noteVertBuff[pos++] = 0;\n                            noteVertBuff[pos++] = y2;\n                            noteVertBuff[pos++] = x2d;\n                            noteVertBuff[pos++] = 0;\n                            noteVertBuff[pos++] = y1;\n                            noteVertBuff[pos++] = x1d;\n                            noteVertBuff[pos++] = 0;\n                            noteVertBuff[pos++] = y1;\n                            noteVertBuff[pos++] = x1d;\n                            noteVertBuff[pos++] = 0;\n                            noteVertBuff[pos++] = y2;\n\n                            pos = noteBuffPos * 16;\n                            noteColBuff[pos++] = r;\n                            noteColBuff[pos++] = g;\n                            noteColBuff[pos++] = b;\n                            noteColBuff[pos++] = a;\n                            noteColBuff[pos++] = r;\n                            noteColBuff[pos++] = g;\n                            noteColBuff[pos++] = b;\n                            noteColBuff[pos++] = a;\n                            noteColBuff[pos++] = r2;\n                            noteColBuff[pos++] = g2;\n                            noteColBuff[pos++] = b2;\n                            noteColBuff[pos++] = a2;\n                            noteColBuff[pos++] = r2;\n                            noteColBuff[pos++] = g2;\n                            noteColBuff[pos++] = b2;\n                            noteColBuff[pos++] = a2;\n\n                            pos = noteBuffPos * 4;\n                            noteShadeBuff[pos++] = shade;\n                            noteShadeBuff[pos++] = shade;\n                            noteShadeBuff[pos++] = shade;\n                            noteShadeBuff[pos++] = shade;\n\n                            noteBuffPos++;\n                        }\n                        FlushNoteBuffer();\n\n                    }\n                    else break;\n                }\n            }\n\n            FlushNoteBuffer(false);\n            noteBuffPos = 0;\n\n            LastNoteCount = nc;\n            #endregion\n\n\n            if ((!settings.verticalNotes && viewoffset < 0) || (settings.verticalNotes && viewheight < 0.025))\n            {\n                if (auraEnabled)\n                {\n                    GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.One);\n                    RenderAura();\n                    GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);\n                }\n                GL.DepthFunc(DepthFunction.Less);\n\n                RenderKeyboard();\n            }\n            else\n            {\n                GL.DepthFunc(DepthFunction.Less);\n                RenderKeyboard();\n                GL.DepthFunc(DepthFunction.Always);\n\n                if (auraEnabled)\n                {\n                    GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.One);\n                    RenderAura();\n                    GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);\n                }\n                GL.DepthFunc(DepthFunction.Less);\n            }\n\n\n\n            GL.Disable(EnableCap.Blend);\n            GL.DisableClientState(ArrayCap.VertexArray);\n            GL.DisableClientState(ArrayCap.ColorArray);\n            GL.Disable(EnableCap.Texture2D);\n            GL.Disable(EnableCap.DepthTest);\n\n            GL.DisableVertexAttribArray(0);\n            GL.DisableVertexAttribArray(1);\n            GL.DisableVertexAttribArray(2);\n\n            GL.BindFramebuffer(FramebufferTarget.Framebuffer, finalCompositeBuff);\n            GL.BindTexture(TextureTarget.Texture2D, buffer3dtex);\n            GL.Clear(ClearBufferMask.ColorBufferBit);\n            GL.Clear(ClearBufferMask.DepthBufferBit);\n            util.DrawScreenQuad();\n        }\n\n        void RenderAura()\n        {\n            #region Aura\n            double wdthd;\n            float r, g, b, a, r2, g2, b2, a2;\n            double x1d;\n            double x2d;\n            double y1;\n            double y2;\n            Matrix4 mvp;\n\n            GL.UseProgram(circleShader);\n\n            GL.BindTexture(TextureTarget.Texture2D, auraTex);\n\n            mvp = Matrix4.Identity;\n            if (settings.verticalNotes) mvp *= \n                    Matrix4.CreateRotationX(-(float)Math.PI / 2) *\n                    Matrix4.CreateTranslation(0, 0.025f, 0);\n            mvp *= \n                Matrix4.CreateTranslation((float)viewpan, -(float)viewheight, -(float)viewoffset) *\n                Matrix4.CreateScale(1, 1, -1) *\n                Matrix4.CreateRotationZ((float)camSpin) *\n                Matrix4.CreateRotationY((float)camRot) *\n                Matrix4.CreateRotationX((float)camAng) *\n                Matrix4.CreatePerspectiveFieldOfView((float)fov, (float)aspect, 0.01f, 400)\n                ;\n            GL.UniformMatrix4(uCircleMVP, false, ref mvp);\n\n            circleBuffPos = 0;\n            for (int n = firstNote; n < lastNote; n++)\n            {\n                x1d = x1array[n] - 0.5;\n                wdthd = wdtharray[n];\n                x2d = x1d + wdthd;\n                double size = circleRadius * 12 * auraSize[n];\n                if (!blackKeys[n])\n                {\n                    y2 = 0;\n                    if (settings.sameWidthNotes)\n                    {\n                        int _n = n % 12;\n                        if (_n == 0)\n                            x2d += wdthd * 0.666f;\n                        else if (_n == 2)\n                        {\n                            x1d -= wdthd / 3;\n                            x2d += wdthd / 3;\n                        }\n                        else if (_n == 4)\n                            x1d -= wdthd / 3 * 2;\n                        else if (_n == 5)\n                            x2d += wdthd * 0.75f;\n                        else if (_n == 7)\n                        {\n                            x1d -= wdthd / 4;\n                            x2d += wdthd / 2;\n                        }\n                        else if (_n == 9)\n                        {\n                            x1d -= wdthd / 2;\n                            x2d += wdthd / 4;\n                        }\n                        else if (_n == 11)\n                            x1d -= wdthd * 0.75f;\n                    }\n                }\n\n                Color4 coll = keyColors[n * 2];\n                Color4 colr = keyColors[n * 2 + 1];\n\n                r = coll.R * auraStrength;\n                g = coll.G * auraStrength;\n                b = coll.B * auraStrength;\n                a = coll.A * auraStrength;\n                r2 = colr.R * auraStrength;\n                g2 = colr.G * auraStrength;\n                b2 = colr.B * auraStrength;\n                a2 = colr.A * auraStrength;\n\n                double middle = (x1d + x2d) / 2;\n                x1d = middle - size;\n                x2d = middle + size;\n                y1 = size;\n                y2 = -size;\n\n\n                int pos = circleBuffPos * 12;\n                circleVertBuff[pos++] = x1d;\n                circleVertBuff[pos++] = y1;\n                circleVertBuff[pos++] = 0;\n                circleVertBuff[pos++] = x1d;\n                circleVertBuff[pos++] = y2;\n                circleVertBuff[pos++] = 0;\n                circleVertBuff[pos++] = x2d;\n                circleVertBuff[pos++] = y2;\n                circleVertBuff[pos++] = 0;\n                circleVertBuff[pos++] = x2d;\n                circleVertBuff[pos++] = y1;\n                circleVertBuff[pos++] = 0;\n\n                pos = circleBuffPos * 16;\n                circleColorBuff[pos++] = r;\n                circleColorBuff[pos++] = g;\n                circleColorBuff[pos++] = b;\n                circleColorBuff[pos++] = a;\n                circleColorBuff[pos++] = r;\n                circleColorBuff[pos++] = g;\n                circleColorBuff[pos++] = b;\n                circleColorBuff[pos++] = a;\n                circleColorBuff[pos++] = r2;\n                circleColorBuff[pos++] = g2;\n                circleColorBuff[pos++] = b2;\n                circleColorBuff[pos++] = a2;\n                circleColorBuff[pos++] = r2;\n                circleColorBuff[pos++] = g2;\n                circleColorBuff[pos++] = b2;\n                circleColorBuff[pos++] = a2;\n\n                pos = circleBuffPos * 8;\n                circleUVBuff[pos++] = 0;\n                circleUVBuff[pos++] = 0;\n                circleUVBuff[pos++] = 1;\n                circleUVBuff[pos++] = 0;\n                circleUVBuff[pos++] = 1;\n                circleUVBuff[pos++] = 1;\n                circleUVBuff[pos++] = 0;\n                circleUVBuff[pos++] = 1;\n\n                circleBuffPos++;\n            }\n            GL.BindBuffer(BufferTarget.ArrayBuffer, circleVert);\n            GL.BufferData(\n                BufferTarget.ArrayBuffer,\n                (IntPtr)(circleVertBuff.Length * 8),\n                circleVertBuff,\n                BufferUsageHint.StaticDraw);\n            GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Double, false, 24, 0);\n            GL.BindBuffer(BufferTarget.ArrayBuffer, circleColor);\n            GL.BufferData(\n                BufferTarget.ArrayBuffer,\n                (IntPtr)(circleColorBuff.Length * 4),\n                circleColorBuff,\n                BufferUsageHint.StaticDraw);\n            GL.VertexAttribPointer(1, 4, VertexAttribPointerType.Float, false, 16, 0);\n            GL.BindBuffer(BufferTarget.ArrayBuffer, circleUV);\n            GL.BufferData(\n                BufferTarget.ArrayBuffer,\n                (IntPtr)(circleUVBuff.Length * 8),\n                circleUVBuff,\n                BufferUsageHint.StaticDraw);\n            GL.VertexAttribPointer(2, 2, VertexAttribPointerType.Double, false, 16, 0);\n            GL.BindBuffer(BufferTarget.ElementArrayBuffer, circleIndx);\n            GL.IndexPointer(IndexPointerType.Int, 1, 0);\n            GL.DrawElements(PrimitiveType.Quads, circleBuffPos * 4, DrawElementsType.UnsignedInt, IntPtr.Zero);\n\n            GL.BindTexture(TextureTarget.Texture2D, 0);\n            #endregion\n        }\n\n        void RenderKeyboard()\n        {\n            if (settings.showKeyboard)\n            {\n                #region Keyboard\n                float wdth;\n                float wdth2;\n                float x1;\n                float x2;\n                double y2;\n                Matrix4 mvp;\n                Color4[] origColors = new Color4[257];\n                for (int k = firstNote; k < lastNote; k++)\n                {\n                    if (isBlackNote(k))\n                        origColors[k] = Color4.Black;\n                    else\n                        origColors[k] = Color4.White;\n                }\n\n                GL.UseProgram(whiteKeyShader);\n\n                GL.BindBuffer(BufferTarget.ArrayBuffer, whiteKeyCol);\n                GL.VertexAttribPointer(1, 1, VertexAttribPointerType.Float, false, 4, 0);\n                GL.BindBuffer(BufferTarget.ArrayBuffer, whiteKeyBlend);\n                GL.VertexAttribPointer(2, 1, VertexAttribPointerType.Float, false, 4, 0);\n                GL.BindBuffer(BufferTarget.ElementArrayBuffer, whiteKeyIndx);\n                GL.IndexPointer(IndexPointerType.Int, 1, 0);\n\n                for (int n = firstNote; n < lastNote; n++)\n                {\n                    x1 = (float)x1array[n];\n                    wdth = (float)wdtharray[n];\n                    x2 = x1 + wdth;\n\n                    if (!blackKeys[n])\n                    {\n                        y2 = 0;\n                        if (settings.sameWidthNotes)\n                        {\n                            int _n = n % 12;\n                            if (_n == 0)\n                                x2 += wdth * 0.666f;\n                            else if (_n == 2)\n                            {\n                                x1 -= wdth / 3;\n                                x2 += wdth / 3;\n                            }\n                            else if (_n == 4)\n                                x1 -= wdth / 3 * 2;\n                            else if (_n == 5)\n                                x2 += wdth * 0.75f;\n                            else if (_n == 7)\n                            {\n                                x1 -= wdth / 4;\n                                x2 += wdth / 2;\n                            }\n                            else if (_n == 9)\n                            {\n                                x1 -= wdth / 2;\n                                x2 += wdth / 4;\n                            }\n                            else if (_n == 11)\n                                x1 -= wdth * 0.75f;\n                            wdth2 = wdth * 2;\n                        }\n                        else\n                        {\n                            wdth2 = wdth;\n                        }\n                    }\n                    else continue;\n                    wdth = x2 - x1;\n                    x1 -= 0.5f;\n\n                    var coll = keyColors[n * 2];\n                    var colr = keyColors[n * 2 + 1];\n                    var origcol = origColors[n];\n                    float blendfac = coll.A * 0.8f;\n                    float revblendfac = 1 - blendfac;\n                    coll = new Color4(\n                        coll.R * blendfac + origcol.R * revblendfac,\n                        coll.G * blendfac + origcol.G * revblendfac,\n                        coll.B * blendfac + origcol.B * revblendfac,\n                        1);\n                    blendfac = colr.A * 0.8f;\n                    revblendfac = 1 - blendfac;\n                    colr = new Color4(\n                        colr.R * blendfac + origcol.R * revblendfac,\n                        colr.G * blendfac + origcol.G * revblendfac,\n                        colr.B * blendfac + origcol.B * revblendfac,\n                        1);\n\n                    GL.Uniform4(uWhiteKeycoll, coll);\n                    GL.Uniform4(uWhiteKeycolr, colr);\n                    float scale = 1;\n                    if (!sameWidth)\n                    {\n                        scale = 1.17f;\n                        wdth2 = wdth;\n                    }\n                    else wdth2 = (float)wdtharray[firstNote] * 2;\n                    mvp = Matrix4.Identity *\n                        Matrix4.CreateScale(0.95f, 1, scale);\n                    if (tiltKeys)\n                        mvp *=\n                            Matrix4.CreateTranslation(0, 0, -4) *\n                            Matrix4.CreateRotationX((float)-keyPressFactor[n] / 20) *\n                            Matrix4.CreateTranslation(0, 0, 4);\n                    else\n                        mvp *= Matrix4.CreateTranslation(0, (float)-keyPressFactor[n] / 2, 0);\n                    mvp *=\n                        Matrix4.CreateTranslation(0, -0.3f, 0) *\n                        (sameWidth ? Matrix4.CreateScale(wdth, wdth2 * 0.9f, wdth2 * 1.01f) : Matrix4.CreateScale(wdth2, wdth2, wdth2)) *\n                        Matrix4.CreateTranslation(x1, 0, 0) *\n                        (settings.verticalNotes ? Matrix4.CreateTranslation(0, 0, 0.05f) : Matrix4.Identity) *\n                        Matrix4.CreateTranslation((float)viewpan, -(float)viewheight, -(float)viewoffset) *\n                        Matrix4.CreateScale(1, 1, -1) *\n                        Matrix4.CreateRotationZ((float)camSpin) *\n                        Matrix4.CreateRotationY((float)camRot) *\n                        Matrix4.CreateRotationX((float)camAng) *\n                        Matrix4.CreatePerspectiveFieldOfView((float)fov, (float)aspect, 0.01f, 400)\n                    ;\n\n                    if (n == firstNote)\n                        GL.BindBuffer(BufferTarget.ArrayBuffer, whiteKeyVert[keynum[n] % 7 + 14]);\n                    else if (n == lastNote - 1)\n                        GL.BindBuffer(BufferTarget.ArrayBuffer, whiteKeyVert[keynum[n] % 7 + 7]);\n                    else\n                        GL.BindBuffer(BufferTarget.ArrayBuffer, whiteKeyVert[keynum[n] % 7]);\n                    GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Double, false, 24, 0);\n                    GL.UniformMatrix4(uWhiteKeyMVP, false, ref mvp);\n                    GL.DrawElements(PrimitiveType.Quads, whiteKeyBufferLen, DrawElementsType.UnsignedInt, IntPtr.Zero);\n                }\n\n                GL.UseProgram(blackKeyShader);\n\n                GL.BindBuffer(BufferTarget.ArrayBuffer, blackKeyVert);\n                GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Double, false, 24, 0);\n                GL.BindBuffer(BufferTarget.ArrayBuffer, blackKeyCol);\n                GL.VertexAttribPointer(1, 1, VertexAttribPointerType.Float, false, 4, 0);\n                GL.BindBuffer(BufferTarget.ArrayBuffer, blackKeyBlend);\n                GL.VertexAttribPointer(2, 1, VertexAttribPointerType.Float, false, 4, 0);\n                GL.BindBuffer(BufferTarget.ElementArrayBuffer, blackKeyIndx);\n                GL.IndexPointer(IndexPointerType.Int, 1, 0);\n\n                for (int n = firstNote; n < lastNote; n++)\n                {\n                    x1 = (float)x1array[n];\n                    wdth = (float)wdtharray[n];\n                    x1 -= 0.5f;\n\n                    if (!blackKeys[n]) continue;\n\n                    var coll = keyColors[n * 2];\n                    var colr = keyColors[n * 2 + 1];\n                    var origcol = origColors[n];\n                    float blendfac = coll.A * 0.8f;\n                    float revblendfac = 1 - blendfac;\n                    coll = new Color4(\n                        coll.R * blendfac + origcol.R * revblendfac,\n                        coll.G * blendfac + origcol.G * revblendfac,\n                        coll.B * blendfac + origcol.B * revblendfac,\n                        1);\n                    blendfac = colr.A * 0.8f;\n                    revblendfac = 1 - blendfac;\n                    colr = new Color4(\n                        colr.R * blendfac + origcol.R * revblendfac,\n                        colr.G * blendfac + origcol.G * revblendfac,\n                        colr.B * blendfac + origcol.B * revblendfac,\n                        1);\n\n                    GL.Uniform4(uBlackKeycoll, coll);\n                    GL.Uniform4(uBlackKeycolr, colr);\n\n                    float vertOffset = 1.2f;\n                    if (!sameWidth) vertOffset = 1.1f;\n                    float scale = 1;\n                    if (!sameWidth) scale = 0.97f;\n                    mvp = Matrix4.Identity *\n                        Matrix4.CreateScale(0.95f, 1, scale);\n                    if (tiltKeys)\n                        mvp *=\n                            Matrix4.CreateTranslation(0, 0, -4) *\n                            Matrix4.CreateRotationX((float)-keyPressFactor[n] / 20) *\n                            Matrix4.CreateTranslation(0, 0, 4);\n                    else\n                        mvp *= Matrix4.CreateTranslation(0, (float)-keyPressFactor[n] / 1.2f, 0);\n                    mvp *=\n                    Matrix4.CreateTranslation(0, vertOffset, 0) *\n                    Matrix4.CreateScale(wdth, wdth / scale, wdth) *\n                    Matrix4.CreateTranslation(x1, 0, 0) *\n                    (settings.verticalNotes ? Matrix4.CreateTranslation(0, 0, 0.05f) : Matrix4.Identity) *\n                    Matrix4.CreateTranslation((float)viewpan, -(float)viewheight, -(float)viewoffset) *\n                    Matrix4.CreateScale(1, 1, -1) *\n                    Matrix4.CreateRotationZ((float)camSpin) *\n                    Matrix4.CreateRotationY((float)camRot) *\n                    Matrix4.CreateRotationX((float)camAng) *\n                    Matrix4.CreatePerspectiveFieldOfView((float)fov, (float)aspect, 0.01f, 400)\n                    ;\n\n                    GL.UniformMatrix4(uWhiteKeyMVP, false, ref mvp);\n                    GL.DrawElements(PrimitiveType.Quads, blackKeyBufferLen, DrawElementsType.UnsignedInt, IntPtr.Zero);\n                }\n                #endregion\n            }\n        }\n\n        public void ReloadTrackColors()\n        {\n            if (NoteColors == null) return;\n            var cols = ((SettingsCtrl)SettingsControl).paletteList.GetColors(NoteColors.Length);\n\n            for (int i = 0; i < NoteColors.Length; i++)\n            {\n                for (int j = 0; j < NoteColors[i].Length; j++)\n                {\n                    if (NoteColors[i][j].isDefault)\n                    {\n                        NoteColors[i][j].left = cols[i * 32 + j * 2];\n                        NoteColors[i][j].right = cols[i * 32 + j * 2 + 1];\n                    }\n                }\n            }\n        }\n\n        void FlushNoteBuffer(bool check = true)\n        {\n            if (noteBuffPos < noteBuffLen && check) return;\n            if (noteBuffPos == 0) return;\n            GL.BindBuffer(BufferTarget.ArrayBuffer, noteVert);\n            GL.BufferData(\n                BufferTarget.ArrayBuffer,\n                (IntPtr)(noteBuffPos * 12 * 8),\n                noteVertBuff,\n                BufferUsageHint.StaticDraw);\n            GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Double, false, 24, 0);\n            GL.BindBuffer(BufferTarget.ArrayBuffer, noteCol);\n            GL.BufferData(\n                BufferTarget.ArrayBuffer,\n                (IntPtr)(noteBuffPos * 16 * 4),\n                noteColBuff,\n                BufferUsageHint.StaticDraw);\n            GL.VertexAttribPointer(1, 4, VertexAttribPointerType.Float, false, 16, 0);\n            GL.BindBuffer(BufferTarget.ArrayBuffer, noteShade);\n            GL.BufferData(\n                BufferTarget.ArrayBuffer,\n                (IntPtr)(noteBuffPos * 4 * 4),\n                noteShadeBuff,\n                BufferUsageHint.StaticDraw);\n            GL.VertexAttribPointer(2, 1, VertexAttribPointerType.Float, false, 4, 0);\n            GL.BindBuffer(BufferTarget.ElementArrayBuffer, noteIndx);\n            GL.IndexPointer(IndexPointerType.Int, 1, 0);\n            GL.DrawElements(PrimitiveType.Quads, noteBuffPos * 4, DrawElementsType.UnsignedInt, IntPtr.Zero);\n            noteBuffPos = 0;\n        }\n\n        bool isBlackNote(int n)\n        {\n            n = n % 12;\n            return n == 1 || n == 3 || n == 6 || n == 8 || n == 10;\n        }\n    }\n}\n"
  },
  {
    "path": "MidiTrailRender/Settings.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace MIDITrailRender\n{\n    public class Settings\n    {\n        public int firstNote = 0;\n        public int lastNote = 128;\n        public double deltaTimeOnScreen = 400;\n        public bool sameWidthNotes = true;\n\n        public double FOV = 3.1415 / 3;\n        public double viewHeight = 0.5;\n        public double viewOffset = 0.4;\n        public double viewPan = 0.0;\n        public double camAng = 0.56;\n        public double camRot = 0;\n        public double camSpin = 0;\n        public double viewdist = 14;\n        public double viewback = 0.2;\n\n        public bool verticalNotes = false;\n\n        public double noteDownSpeed = 0.6;\n        public double noteUpSpeed = 0.2;\n        public bool boxNotes = false;\n        public bool lightShade = false;\n        public bool showKeyboard = true;\n\n        public bool tiltKeys = true;\n\n        public bool eatNotes = false;\n\n        public string palette = \"Random\";\n\n        public string selectedAuraImage = \"ring\";\n        public double auraStrength = 2;\n        public bool auraEnabled = true;\n\n        public bool useVel = false;\n\n        public bool notesChangeSize = false;\n        public bool notesChangeTint = true;\n\n\n        public float noteBrightness = 1;\n\n        public bool tickBased = true;\n    }\n}\n"
  },
  {
    "path": "MidiTrailRender/SettingsCtrl.xaml",
    "content": "﻿<UserControl\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" \n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\" \n             xmlns:local=\"clr-namespace:MIDITrailRender\"\n             xmlns:ui=\"clr-namespace:ZenithEngine.UI;assembly=ZenithEngine\"\n             xmlns:xctk=\"http://schemas.xceed.com/wpf/xaml/toolkit\"\n             xmlns:bme=\"clr-namespace:ZenithEngine;assembly=ZenithEngine\" x:Class=\"MIDITrailRender.SettingsCtrl\"\n             mc:Ignorable=\"d\" Height=\"500\" Width=\"688.373\">\n    <UserControl.Resources>\n        <ResourceDictionary>\n            <ResourceDictionary.MergedDictionaries>\n                <ResourceDictionary>\n                    <ResourceDictionary.MergedDictionaries>\n                        <ResourceDictionary Source=\"pack://siteoforigin:,,,/Languages/en/miditrail.xaml\" />\n                    </ResourceDictionary.MergedDictionaries>\n                </ResourceDictionary>\n                <ResourceDictionary Source=\"pack://application:,,,/ZenithEngine;component/UI/Material.xaml\"/>\n            </ResourceDictionary.MergedDictionaries>\n            <Style TargetType=\"TabControl\" BasedOn=\"{StaticResource SubTabs}\"/>\n            <Style TargetType=\"TabItem\" BasedOn=\"{StaticResource SubTabItems}\"/>\n        </ResourceDictionary>\n    </UserControl.Resources>\n    <DockPanel LastChildFill=\"True\" Margin=\"10\">\n        <DockPanel DockPanel.Dock=\"Bottom\" Height=\"26\" LastChildFill=\"False\" Margin=\"0,0,0,0\">\n            <Button x:Name=\"deleteProfile\" Content=\"{DynamicResource delete}\" HorizontalAlignment=\"Left\" Margin=\"0\" Padding=\"20,0,20,0\" Click=\"DeleteProfile_Click\" DockPanel.Dock=\"Left\"/>\n            <ComboBox DropDownOpened=\"profileSelect_DropDownOpened\" DockPanel.Dock=\"Left\" x:Name=\"profileSelect\" HorizontalAlignment=\"Left\" Margin=\"5,0,0,0\" Width=\"110\" VerticalAlignment=\"Bottom\" SelectionChanged=\"ProfileSelect_SelectionChanged\"/>\n            <Button x:Name=\"newProfile\" Content=\"{DynamicResource saveNew}\" HorizontalAlignment=\"Left\" Margin=\"5,0,0,0\" Padding=\"20,0,20,0\" Click=\"NewProfile_Click\" DockPanel.Dock=\"Left\"/>\n            <TextBox DockPanel.Dock=\"Left\" x:Name=\"profileName\" HorizontalAlignment=\"Left\" Margin=\"5,0,0,0\" TextWrapping=\"Wrap\" Text=\"\" Width=\"120\"/>\n            <Button x:Name=\"defaultsButton\" Content=\"{DynamicResource loadDefault}\" Margin=\"0\" Click=\"DefaultsButton_Click\" HorizontalAlignment=\"Right\" Padding=\"20,0,20,0\" DockPanel.Dock=\"Right\"/>\n        </DockPanel>\n        <TabControl Margin=\"0,0,0,0\">\n            <TabItem Header=\"{DynamicResource visualsTab}\">\n                <DockPanel>\n                    <StackPanel Margin=\"10\">\n                        <DockPanel HorizontalAlignment=\"Left\" LastChildFill=\"False\" Margin=\"0,0,0,0\" VerticalAlignment=\"Top\">\n                            <Label Content=\"{DynamicResource firstNote}\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Top\" DockPanel.Dock=\"Left\"/>\n                            <ui:NumberSelect x:Name=\"firstNote\" Value=\"1\" Maximum=\"254\" Minimum=\"0\" Margin=\"5,0,0,2\" HorizontalAlignment=\"Left\" Width=\"80\" ValueChanged=\"Nud_ValueChanged\"  />\n                            <Label Content=\"{DynamicResource lastNote}\" HorizontalAlignment=\"Left\" Margin=\"10,0,0,0\" VerticalAlignment=\"Top\"/>\n                            <ui:NumberSelect x:Name=\"lastNote\" Value=\"1\" Maximum=\"255\" Minimum=\"1\" Margin=\"5,0,0,2\" HorizontalAlignment=\"Left\" Width=\"80\" ValueChanged=\"Nud_ValueChanged\"  />\n                        </DockPanel>\n                        <DockPanel HorizontalAlignment=\"Left\" LastChildFill=\"False\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\">\n                            <Label Content=\"{DynamicResource keyDownSpeed}\" HorizontalAlignment=\"Left\" Margin=\"0\" VerticalAlignment=\"Top\"/>\n                            <ui:NumberSelect x:Name=\"noteDownSpeed\" DecimalPoints=\"2\" Step=\"0.1\" Value=\"0.2\" Maximum=\"1\" Minimum=\"0\" Margin=\"5,0,0,2\" HorizontalAlignment=\"Left\" Width=\"65\" Height=\"26\" VerticalAlignment=\"Top\" ValueChanged=\"Nud_ValueChanged\"  />\n                            <Label Content=\"{DynamicResource keyUpSpeed}\" HorizontalAlignment=\"Left\" Margin=\"10,0,0,0\" VerticalAlignment=\"Top\"/>\n                            <ui:NumberSelect x:Name=\"noteUpSpeed\" DecimalPoints=\"2\" Step=\"0.1\" Value=\"0.1\" Maximum=\"1\" Minimum=\"0\" Margin=\"5,0,0,2\" HorizontalAlignment=\"Left\" Width=\"65\" Height=\"26\" VerticalAlignment=\"Top\" ValueChanged=\"Nud_ValueChanged\"  />\n                        </DockPanel>\n                        <ui:BetterCheckbox x:Name=\"tiltKeys\" Text =\"{DynamicResource tiltKeys}\" HorizontalAlignment=\"Left\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\"  CheckToggled=\"CheckboxChecked\"/>\n                        <ui:BetterCheckbox x:Name=\"boxNotes\" Text=\"{DynamicResource 3dNotes}\" HorizontalAlignment=\"Left\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\" CheckToggled=\"BoxNotes_Checked\"/>\n                        <ui:BetterCheckbox IsEnabled=\"{Binding IsChecked, ElementName=boxNotes }\" x:Name=\"lightShade\" Text=\"{DynamicResource lightShade}\" HorizontalAlignment=\"Left\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\" CheckToggled=\"CheckboxChecked\"/>\n                        <DockPanel HorizontalAlignment=\"Left\" LastChildFill=\"False\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\" >\n                            <Label Content=\"{DynamicResource noteSpeed}\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Top\" DockPanel.Dock=\"Left\"/>\n                            <ui:ValueSlider x:Name=\"noteDeltaScreenTime\" DecimalPoints=\"1\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Top\" Width=\"275\" Maximum=\"11\" TrueMax=\"100000\" Minimum=\"2\" TrueMin=\"1\" Value=\"1\" ValueChanged=\"NoteDeltaScreenTime_ValueChanged\" DockPanel.Dock=\"Left\" Margin=\"0,0,0,0\"/>\n                        </DockPanel>\n                        <ui:BetterCheckbox x:Name=\"useVel\" Text=\"{DynamicResource velocityStrength}\" HorizontalAlignment=\"Left\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\" CheckToggled=\"CheckboxChecked\"/>\n                        <ui:BetterCheckbox x:Name=\"eatNotes\" Text=\"{DynamicResource keyboardClip}\" HorizontalAlignment=\"Left\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\" CheckToggled=\"CheckboxChecked\"/>\n                        <ui:BetterCheckbox x:Name=\"sameWidthNotes\" Text=\"{DynamicResource sameWidthNotes}\" HorizontalAlignment=\"Left\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\" CheckToggled=\"CheckboxChecked\"/>\n                        <ui:BetterCheckbox x:Name=\"showKeyboard\" Text=\"{DynamicResource showKeyboard}\" HorizontalAlignment=\"Left\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\" CheckToggled=\"CheckboxChecked\" IsChecked=\"True\"/>\n                        <Label Content=\"{DynamicResource onNoteHit}\" HorizontalAlignment=\"Left\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\"/>\n                        <ui:BetterCheckbox x:Name=\"notesChangeSize\" Text=\"{DynamicResource changeSize}\" HorizontalAlignment=\"Left\" Margin=\"0,0,0,0\" VerticalAlignment=\"Top\" CheckToggled=\"CheckboxChecked\"/>\n                        <ui:BetterCheckbox x:Name=\"notesChangeTint\" Text=\"{DynamicResource changeTint}\" HorizontalAlignment=\"Left\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\" CheckToggled=\"CheckboxChecked\"/>\n                    </StackPanel>\n                    <bme:NoteColorPalettePick x:FieldModifier=\"public\" x:Name=\"paletteList\" Margin=\"0,10,10,10\" HorizontalAlignment=\"Right\" Width=\"184\"/>\n                </DockPanel>\n            </TabItem>\n            <TabItem Header=\"{DynamicResource cameraTab}\">\n                <StackPanel Margin=\"10\">\n                    <TextBlock Text=\"{DynamicResource speedWarning}\" HorizontalAlignment=\"Left\" Margin=\"0,0,0,0\" TextWrapping=\"Wrap\" VerticalAlignment=\"Top\" />\n                    <DockPanel HorizontalAlignment=\"Left\" LastChildFill=\"False\" Margin=\"0,10,0,0\" Height=\"26\" VerticalAlignment=\"Top\" >\n                        <Label Content=\"{DynamicResource cameraPreset}\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Top\" DockPanel.Dock=\"Left\"/>\n                        <Button x:Name=\"farPreset\" Content=\"{DynamicResource farPreset}\" HorizontalAlignment=\"Left\" Width=\"75\" Click=\"FarPreset_Click\" DockPanel.Dock=\"Left\" Margin=\"5,0,0,0\"/>\n                        <Button x:Name=\"mediumPreset\" Content=\"{DynamicResource mediumPreset}\" HorizontalAlignment=\"Left\" Width=\"75\" Click=\"MediumPreset_Click\" DockPanel.Dock=\"Left\" Margin=\"5,0,0,0\"/>\n                        <Button x:Name=\"closePreset\" Content=\"{DynamicResource closePreset}\" HorizontalAlignment=\"Left\" Width=\"75\" Click=\"ClosePreset_Click\" DockPanel.Dock=\"Left\" Margin=\"5,0,0,0\"/>\n                        <Button x:Name=\"topPreset\" Content=\"{DynamicResource topPreset}\" HorizontalAlignment=\"Left\" Width=\"75\" Click=\"TopPreset_Click\" DockPanel.Dock=\"Left\" Margin=\"5,0,0,0\"/>\n                        <Button x:Name=\"perspectivePreset\" Content=\"{DynamicResource perspectivePreset}\" HorizontalAlignment=\"Left\" Width=\"75\" Click=\"PerspectivePreset_Click\" DockPanel.Dock=\"Left\" Margin=\"5,0,0,0\"/>\n                    </DockPanel>\n                    <ui:BetterCheckbox Name=\"verticalNotes\" Text=\"{DynamicResource verticalNotes}\" Margin=\"0,10,0,0\" CheckToggled=\"verticalNotes_CheckToggled\"/>\n                    <DockPanel HorizontalAlignment=\"Left\" LastChildFill=\"False\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\">\n                        <Label Content=\"{DynamicResource FOV}\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Top\" DockPanel.Dock=\"Left\"/>\n                        <ui:ValueSlider x:Name=\"FOVSlider\" HorizontalAlignment=\"Left\" DecimalPoints=\"2\" Width=\"285\" Maximum=\"150\" TrueMax=\"150\" Minimum=\"20\" TrueMin=\"5\" Value=\"60\" VerticalAlignment=\"Top\" ValueChanged=\"FOVSlider_ValueChanged\" DockPanel.Dock=\"Left\" Margin=\"0,0,0,0\"/>\n                    </DockPanel>\n                    <DockPanel HorizontalAlignment=\"Left\" LastChildFill=\"False\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\" >\n                        <Label Content=\"{DynamicResource renderDistF}\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Top\" DockPanel.Dock=\"Left\"/>\n                        <ui:ValueSlider x:Name=\"renderDistSlider\" HorizontalAlignment=\"Left\" DecimalPoints=\"2\" VerticalAlignment=\"Top\" Width=\"467\" Maximum=\"20\" TrueMax=\"200\" Minimum=\"0\" TrueMin=\"0\" Value=\"60\" Height=\"26\" ValueChanged=\"RenderDistSlider_ValueChanged\" DockPanel.Dock=\"Left\" Margin=\"0,0,0,0\"/>\n                    </DockPanel>\n                    <DockPanel HorizontalAlignment=\"Left\" LastChildFill=\"False\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\" >\n                        <Label Content=\"{DynamicResource renderDistB}\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Top\" DockPanel.Dock=\"Left\"/>\n                        <ui:ValueSlider x:Name=\"renderDistBackSlider\" HorizontalAlignment=\"Left\" DecimalPoints=\"2\" VerticalAlignment=\"Top\" Width=\"461\" Maximum=\"20\" TrueMax=\"200\" Minimum=\"0\" TrueMin=\"0\" Value=\"60\" Height=\"26\" ValueChanged=\"RenderDistBackSlider_ValueChanged\" DockPanel.Dock=\"Left\" Margin=\"0,0,0,0\"/>\n                    </DockPanel>\n                    <DockPanel HorizontalAlignment=\"Left\" LastChildFill=\"False\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\" >\n                        <Label Content=\"{DynamicResource camOffsets}\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Top\" DockPanel.Dock=\"Left\"/>\n                        <Label Content=\"{DynamicResource offsetX}\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Top\" DockPanel.Dock=\"Left\" Margin=\"10,0,0,0\"/>\n                        <ui:NumberSelect x:Name=\"camOffsetX\" Value=\"0.0\" DecimalPoints=\"2\" Step=\"0.1\" Maximum=\"15\" Minimum=\"-20\" HorizontalAlignment=\"Left\" MinWidth=\"65\" Height=\"26\" VerticalAlignment=\"Top\" ValueChanged=\"Nud_ValueChanged\"  DockPanel.Dock=\"Left\" Margin=\"5,0,0,0\"  />\n                        <Label Content=\"{DynamicResource offsetY}\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Top\" DockPanel.Dock=\"Left\" Margin=\"10,0,0,0\"/>\n                        <ui:NumberSelect x:Name=\"camOffsetY\" Value=\"0.0\" DecimalPoints=\"2\" Step=\"0.1\" Maximum=\"10\" Minimum=\"0\" HorizontalAlignment=\"Left\" MinWidth=\"64\" Height=\"26\" VerticalAlignment=\"Top\" ValueChanged=\"Nud_ValueChanged\"  DockPanel.Dock=\"Left\" Margin=\"5,0,0,0\"  />\n                        <Label Content=\"{DynamicResource offsetZ}\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Top\" DockPanel.Dock=\"Left\" Margin=\"10,0,0,0\"/>\n                        <ui:NumberSelect x:Name=\"camOffsetZ\" Value=\"0.0\" DecimalPoints=\"2\" Step=\"0.1\" Maximum=\"20\" Minimum=\"-20\" HorizontalAlignment=\"Left\" MinWidth=\"64\" Height=\"26\" VerticalAlignment=\"Top\" ValueChanged=\"Nud_ValueChanged\"  DockPanel.Dock=\"Left\" Margin=\"5,0,0,0\"  />\n                    </DockPanel>\n                    <DockPanel HorizontalAlignment=\"Left\" LastChildFill=\"False\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\" >\n                        <Label Content=\"{DynamicResource viewTilt}\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Top\" DockPanel.Dock=\"Left\"/>\n                        <ui:ValueSlider x:Name=\"viewAngSlider\" HorizontalAlignment=\"Left\" DecimalPoints=\"2\" VerticalAlignment=\"Top\" Width=\"324\" Maximum=\"90\" TrueMax=\"180\" Minimum=\"0\" TrueMin=\"-90\" Value=\"0\" Height=\"26\" ValueChanged=\"ViewAngSlider_ValueChanged\" DockPanel.Dock=\"Left\" Margin=\"0,0,0,0\"/>\n                    </DockPanel>\n                    <DockPanel HorizontalAlignment=\"Left\" LastChildFill=\"False\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\" >\n                        <Label Content=\"{DynamicResource viewTurn}\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Top\" DockPanel.Dock=\"Left\"/>\n                        <ui:ValueSlider x:Name=\"viewTurnSlider\" HorizontalAlignment=\"Left\" DecimalPoints=\"2\" VerticalAlignment=\"Top\" Width=\"370\" Maximum=\"180\" TrueMax=\"180\" Minimum=\"-180\" TrueMin=\"-180\" Value=\"0\" Height=\"26\" ValueChanged=\"ViewTurnSlider_ValueChanged\" DockPanel.Dock=\"Left\" Margin=\"0,0,0,0\"/>\n                    </DockPanel>\n                    <DockPanel HorizontalAlignment=\"Left\" LastChildFill=\"False\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\" >\n                        <Label Content=\"{DynamicResource viewSpin}\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Top\" DockPanel.Dock=\"Left\"/>\n                        <ui:ValueSlider x:Name=\"viewSpinSlider\" HorizontalAlignment=\"Left\" DecimalPoints=\"2\" VerticalAlignment=\"Top\" Width=\"370\" Maximum=\"180\" TrueMax=\"180\" Minimum=\"-180\" TrueMin=\"-180\" Value=\"0\" Height=\"26\" ValueChanged=\"viewSpinSlider_ValueChanged\" DockPanel.Dock=\"Left\" Margin=\"0,0,0,0\"/>\n                    </DockPanel>\n                </StackPanel>\n            </TabItem>\n            <TabItem Header=\"{DynamicResource auraTab}\">\n                <Grid x:Name=\"auraSubControlGrid\">\n                </Grid>\n            </TabItem>\n        </TabControl>\n    </DockPanel>\n</UserControl>\n"
  },
  {
    "path": "MidiTrailRender/SettingsCtrl.xaml.cs",
    "content": "﻿using Newtonsoft.Json;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\n\nnamespace MIDITrailRender\n{\n    /// <summary>\n    /// Interaction logic for SettingsCtrl.xaml\n    /// </summary>\n    public partial class SettingsCtrl : UserControl\n    {\n        Settings settings;\n        public AuraSelect auraselect;\n\n        public event Action PaletteChanged\n        {\n            add { paletteList.PaletteChanged += value; }\n            remove { paletteList.PaletteChanged -= value; }\n        }\n\n        public void SetValues()\n        {\n            firstNote.Value = settings.firstNote;\n            lastNote.Value = settings.lastNote - 1;\n            noteDownSpeed.Value = (decimal)settings.noteDownSpeed;\n            noteUpSpeed.Value = (decimal)settings.noteUpSpeed;\n            boxNotes.IsChecked = settings.boxNotes;\n            useVel.IsChecked = settings.useVel;\n            notesChangeSize.IsChecked = settings.notesChangeSize;\n            notesChangeTint.IsChecked = settings.notesChangeTint;\n            sameWidthNotes.IsChecked = settings.sameWidthNotes;\n            lightShade.IsChecked = settings.lightShade;\n            tiltKeys.IsChecked = settings.tiltKeys;\n            showKeyboard.IsChecked = settings.showKeyboard;\n            noteDeltaScreenTime.Value = settings.deltaTimeOnScreen;\n            camOffsetX.Value = (decimal)settings.viewOffset;\n            camOffsetY.Value = (decimal)settings.viewHeight;\n            camOffsetZ.Value = (decimal)settings.viewPan;\n            FOVSlider.Value = settings.FOV / Math.PI * 180;\n            viewAngSlider.Value = settings.camAng / Math.PI * 180;\n            viewTurnSlider.Value = settings.camRot / Math.PI * 180;\n            viewSpinSlider.Value = settings.camSpin / Math.PI * 180;\n            renderDistSlider.Value = settings.viewdist;\n            renderDistBackSlider.Value = settings.viewback;\n            verticalNotes.IsChecked = settings.verticalNotes;\n            paletteList.SelectImage(settings.palette);\n            auraselect.LoadSettings();\n        }\n\n        ProfileManager profiles = new ProfileManager(\"Plugins/Assets/MIDITrail/Profiles.json\");\n        public SettingsCtrl(Settings settings) : base()\n        {\n            InitializeComponent();\n            noteDeltaScreenTime.nudToSlider = v => Math.Log(v, 2);\n            noteDeltaScreenTime.sliderToNud = v => Math.Pow(2, v);\n            this.settings = settings;\n            paletteList.SetPath(\"Plugins\\\\Assets\\\\Palettes\");\n            auraselect = new AuraSelect(settings);\n            auraSubControlGrid.Children.Add(auraselect);\n            auraselect.Margin = new Thickness(0);\n            auraselect.HorizontalAlignment = HorizontalAlignment.Stretch;\n            auraselect.VerticalAlignment = VerticalAlignment.Stretch;\n            auraselect.Width = double.NaN;\n            auraselect.Height = double.NaN;\n            SetValues();\n            ReloadProfiles();\n        }\n\n        private void Nud_ValueChanged(object sender, RoutedPropertyChangedEventArgs<decimal> e)\n        {\n            if (settings == null) return;\n            if (sender == firstNote) settings.firstNote = (int)firstNote.Value;\n            if (sender == lastNote) settings.lastNote = (int)lastNote.Value + 1;\n            if (sender == noteDownSpeed) settings.noteDownSpeed = (double)noteDownSpeed.Value;\n            if (sender == noteUpSpeed) settings.noteUpSpeed = (double)noteUpSpeed.Value;\n            if (sender == camOffsetX) settings.viewOffset = (double)camOffsetX.Value;\n            if (sender == camOffsetY) settings.viewHeight = (double)camOffsetY.Value;\n            if (sender == camOffsetZ) settings.viewPan = (double)camOffsetZ.Value;\n        }\n\n        void injectSettings(Settings sett)\n        {\n            var sourceProps = typeof(Settings).GetFields().ToList();\n            var destProps = typeof(Settings).GetFields().ToList();\n\n            foreach (var sourceProp in sourceProps)\n            {\n                if (destProps.Any(x => x.Name == sourceProp.Name))\n                {\n                    var p = destProps.First(x => x.Name == sourceProp.Name);\n                    p.SetValue(settings, sourceProp.GetValue(sett));\n                }\n            }\n            SetValues();\n        }\n\n        private void BoxNotes_Checked(object sender, RoutedEventArgs e)\n        {\n            if (settings == null) return;\n            settings.boxNotes = (bool)boxNotes.IsChecked;\n        }\n\n        private void FOVSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)\n        {\n            if (settings == null) return;\n            settings.FOV = (double)FOVSlider.Value / 180 * Math.PI;\n        }\n\n        private void ViewAngSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)\n        {\n            if (settings == null) return;\n            settings.camAng = (double)viewAngSlider.Value / 180 * Math.PI;\n        }\n\n        private void ViewTurnSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)\n        {\n            if (settings == null) return;\n            settings.camRot = (double)viewTurnSlider.Value / 180 * Math.PI;\n        }\n\n        private void NoteDeltaScreenTime_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)\n        {\n            if (settings == null) return;\n            settings.deltaTimeOnScreen = noteDeltaScreenTime.Value;\n        }\n\n        private void RenderDistSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)\n        {\n            if (settings == null) return;\n            settings.viewdist = (double)renderDistSlider.Value;\n        }\n\n        private void RenderDistBackSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)\n        {\n            if (settings == null) return;\n            settings.viewback = (double)renderDistBackSlider.Value;\n        }\n\n        private void FarPreset_Click(object sender, RoutedEventArgs e)\n        {\n            camOffsetY.Value = 0.5M;\n            camOffsetX.Value = 0.4M;\n            camOffsetZ.Value = 0;\n            FOVSlider.Value = 60;\n            viewAngSlider.Value = 32.08;\n            viewTurnSlider.Value = 0;\n            viewSpinSlider.Value = 0;\n            renderDistSlider.Value = 14;\n            renderDistBackSlider.Value = 0.2;\n        }\n\n        private void MediumPreset_Click(object sender, RoutedEventArgs e)\n        {\n            camOffsetY.Value = 0.52M;\n            camOffsetX.Value = 0.37M;\n            camOffsetZ.Value = 0;\n            FOVSlider.Value = 60;\n            viewAngSlider.Value = 34.98;\n            viewTurnSlider.Value = 0;\n            viewSpinSlider.Value = 0;\n            renderDistSlider.Value = 5.52;\n            renderDistBackSlider.Value = 0.2;\n        }\n\n        private void ClosePreset_Click(object sender, RoutedEventArgs e)\n        {\n            camOffsetY.Value = 0.55M;\n            camOffsetX.Value = 0.33M;\n            camOffsetZ.Value = 0;\n            FOVSlider.Value = 60;\n            viewAngSlider.Value = 39.62;\n            viewTurnSlider.Value = 0;\n            viewSpinSlider.Value = 0;\n            renderDistSlider.Value = 3.06;\n            renderDistBackSlider.Value = 0.2;\n        }\n\n        private void TopPreset_Click(object sender, RoutedEventArgs e)\n        {\n            camOffsetY.Value = 10M;\n            camOffsetX.Value = -3.77M;\n            camOffsetZ.Value = -1.53M;\n            FOVSlider.Value = 26;\n            viewAngSlider.Value = 90;\n            viewTurnSlider.Value = -90;\n            viewSpinSlider.Value = 0;\n            renderDistSlider.Value = 7.93;\n            renderDistBackSlider.Value = 0.64;\n        }\n\n        private void PerspectivePreset_Click(object sender, RoutedEventArgs e)\n        {\n            camOffsetY.Value = 0.67M;\n            camOffsetX.Value = 1.07M;\n            camOffsetZ.Value = -0.32M;\n            FOVSlider.Value = 60;\n            viewAngSlider.Value = 33.24;\n            viewTurnSlider.Value = -13.84;\n            viewSpinSlider.Value = 0;\n            renderDistSlider.Value = 14;\n            renderDistBackSlider.Value = 0.98;\n        }\n\n        private void SaveButton_Click(object sender, RoutedEventArgs e)\n        {\n            settings.palette = paletteList.SelectedImage;\n            try\n            {\n                string s = JsonConvert.SerializeObject(settings, Formatting.Indented);\n                File.WriteAllText(\"Plugins/MIDITrailRender.json\", s);\n                Console.WriteLine(\"Saved settings to MIDITrailRender.json\");\n            }\n            catch\n            {\n                Console.WriteLine(\"Could not save settings\");\n            }\n        }\n\n        private void DefaultsButton_Click(object sender, RoutedEventArgs e)\n        {\n            injectSettings(new Settings());\n        }\n\n        private void UseVel_Checked(object sender, RoutedEventArgs e)\n        {\n            try\n            {\n                settings.useVel = (bool)useVel.IsChecked;\n            }\n            catch { }\n        }\n\n        private void CheckboxChecked(object sender, RoutedEventArgs e)\n        {\n            if (settings == null) return;\n                if (sender == notesChangeSize) settings.notesChangeSize = (bool)notesChangeSize.IsChecked;\n                if (sender == notesChangeTint) settings.notesChangeTint = (bool)notesChangeTint.IsChecked;\n                if (sender == eatNotes) settings.eatNotes = (bool)eatNotes.IsChecked;\n                if (sender == sameWidthNotes) settings.sameWidthNotes = (bool)sameWidthNotes.IsChecked;\n                if (sender == lightShade) settings.lightShade = (bool)lightShade.IsChecked;\n                if (sender == tiltKeys) settings.tiltKeys = (bool)tiltKeys.IsChecked;\n                if (sender == showKeyboard) settings.showKeyboard = (bool)showKeyboard.IsChecked;\n        }\n\n        private void NewProfile_Click(object sender, RoutedEventArgs e)\n        {\n            settings.palette = paletteList.SelectedImage;\n            if (profileName.Text == \"\")\n            {\n                MessageBox.Show(\"Please write a name for the profile\");\n                return;\n            }\n            if (profiles.Profiles.Contains(profileName.Text))\n            {\n                if (MessageBox.Show(\"Are you sure you want to override the profile \\\"\" + profileName.Text + \"\\\"?\", \"Override Profile\", MessageBoxButton.YesNo) == MessageBoxResult.No)\n                {\n                    return;\n                }\n            }\n            profiles.Add(settings, profileName.Text);\n            ReloadProfiles();\n            foreach (var i in profileSelect.Items)\n            {\n                if ((string)((ComboBoxItem)i).Content == profileName.Text)\n                {\n                    profileSelect.SelectedItem = i;\n                    break;\n                }\n            }\n            SetValues();\n        }\n\n        private void ProfileSelect_SelectionChanged(object sender, SelectionChangedEventArgs e)\n        {\n            try\n            {\n                profiles.LoadProfile((string)((ComboBoxItem)profileSelect.SelectedItem).Content, settings);\n                SetValues();\n            }\n            catch { }\n        }\n\n        private void DeleteProfile_Click(object sender, RoutedEventArgs e)\n        {\n            if (profileSelect.SelectedItem == null) return;\n            profiles.DeleteProfile((string)((ComboBoxItem)profileSelect.SelectedItem).Content);\n            ReloadProfiles();\n            SetValues();\n        }\n\n        void ReloadProfiles()\n        {\n            var ps = profiles.Profiles;\n            profileSelect.Items.Clear();\n            foreach (var p in ps)\n            {\n                var item = new ComboBoxItem()\n                {\n                    Content = p\n                };\n                profileSelect.Items.Add(item);\n            }\n        }\n\n        private void verticalNotes_CheckToggled(object sender, RoutedPropertyChangedEventArgs<bool> e)\n        {\n            try\n            {\n                settings.verticalNotes = verticalNotes.IsChecked;\n            }\n            catch { }\n        }\n\n        private void viewSpinSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)\n        {\n            try\n            {\n                settings.camSpin = (double)viewSpinSlider.Value / 180 * Math.PI;\n            }\n            catch { }\n        }\n\n        private void profileSelect_DropDownOpened(object sender, EventArgs e)\n        {\n            profileSelect.SelectedIndex = -1;\n        }\n    }\n}\n"
  },
  {
    "path": "MidiTrailRender/Util.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing OpenTK.Graphics.OpenGL;\n\nnamespace MIDITrailRender\n{\n    class Util : IDisposable\n    {\n        #region Shaders\n        string postShaderVert = @\"#version 330 compatibility\n\nin vec3 position;\nout vec2 UV;\n\nvoid main()\n{\n    gl_Position = vec4(position.x * 2 - 1, position.y * 2 - 1, position.z * 2 - 1, 1.0f);\n\t//color = glColor;\n    UV = vec2(position.x, position.y);\n}\n\";\n        string postShaderFrag = @\"#version 330 compatibility\n\nin vec2 UV;\n\nout vec4 color;\n\nuniform sampler2D myTextureSampler;\n\nvoid main()\n{\n    color = texture2D( myTextureSampler, UV );\n}\n\";\n\n        int MakeShader(string vert, string frag)\n        {\n            int _vertexObj = GL.CreateShader(ShaderType.VertexShader);\n            int _fragObj = GL.CreateShader(ShaderType.FragmentShader);\n            int statusCode;\n            string info;\n\n            GL.ShaderSource(_vertexObj, vert);\n            GL.CompileShader(_vertexObj);\n            info = GL.GetShaderInfoLog(_vertexObj);\n            GL.GetShader(_vertexObj, ShaderParameter.CompileStatus, out statusCode);\n            if (statusCode != 1) throw new ApplicationException(info);\n\n            GL.ShaderSource(_fragObj, frag);\n            GL.CompileShader(_fragObj);\n            info = GL.GetShaderInfoLog(_fragObj);\n            GL.GetShader(_fragObj, ShaderParameter.CompileStatus, out statusCode);\n            if (statusCode != 1) throw new ApplicationException(info);\n\n            int shader = GL.CreateProgram();\n            GL.AttachShader(shader, _fragObj);\n            GL.AttachShader(shader, _vertexObj);\n            GL.LinkProgram(shader);\n            return shader;\n        }\n        #endregion\n        \n        int postShader;\n\n        int screenQuadBuffer;\n        int screenQuadIndexBuffer;\n        double[] screenQuadArray = new double[] { 0, 0, 0, 1, 1, 1, 1, 0 };\n        int[] screenQuadArrayIndex = new int[] { 0, 1, 2, 3 };\n\n        public Util()\n        {\n            GL.GenBuffers(1, out screenQuadBuffer);\n            GL.GenBuffers(1, out screenQuadIndexBuffer);\n\n            GL.BindBuffer(BufferTarget.ArrayBuffer, screenQuadBuffer);\n            GL.BufferData(\n                BufferTarget.ArrayBuffer,\n                (IntPtr)(screenQuadArray.Length * 8),\n                screenQuadArray,\n                BufferUsageHint.StaticDraw);\n\n            GL.BindBuffer(BufferTarget.ElementArrayBuffer, screenQuadIndexBuffer);\n            GL.BufferData(\n                BufferTarget.ElementArrayBuffer,\n                (IntPtr)(screenQuadArrayIndex.Length * 4),\n                screenQuadArrayIndex,\n                BufferUsageHint.StaticDraw);\n\n            postShader = MakeShader(postShaderVert, postShaderFrag);\n        }\n\n        public void DrawScreenQuad()\n        {\n            GL.UseProgram(postShader);\n            GL.Enable(EnableCap.Blend);\n            GL.EnableClientState(ArrayCap.VertexArray);\n            GL.Enable(EnableCap.Texture2D);\n            GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);\n\n            GL.BindBuffer(BufferTarget.ArrayBuffer, screenQuadBuffer);\n            GL.VertexPointer(2, VertexPointerType.Double, 16, 0);\n\n            GL.BindBuffer(BufferTarget.ElementArrayBuffer, screenQuadIndexBuffer);\n            GL.IndexPointer(IndexPointerType.Int, 1, 0);\n            GL.DrawElements(PrimitiveType.Quads, 4, DrawElementsType.UnsignedInt, IntPtr.Zero);\n\n            GL.Disable(EnableCap.Blend);\n            GL.DisableClientState(ArrayCap.VertexArray);\n            GL.Disable(EnableCap.Texture2D);\n        }\n\n        public void Dispose()\n        {\n            GL.DeleteBuffers(2, new int[] { screenQuadBuffer, screenQuadIndexBuffer });\n        }\n    }\n}\n"
  },
  {
    "path": "MidiTrailRender/packages.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n  <package id=\"Newtonsoft.Json\" version=\"12.0.1\" targetFramework=\"net461\" />\n  <package id=\"OpenTK\" version=\"3.1.0\" targetFramework=\"net461\" />\n</packages>"
  },
  {
    "path": "NoteCountRender/Languages/en/notecounter.xaml",
    "content": "﻿<ResourceDictionary\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" \n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n    xmlns:local=\"using:MSDNSample\"\n    xmlns:system=\"clr-namespace:System;assembly=mscorlib\">\n\n    <!-- Tab Names -->\n    <system:String x:Key=\"Render\">Render</system:String>\n    <system:String x:Key=\"SaveCSV\">Save Data</system:String>\n    <system:String x:Key=\"Padding\">Padding</system:String>\n\n    <!-- Render Tab -->\n    <system:String x:Key=\"alignments\">Alignments</system:String>\n    <system:String x:Key=\"alTopLeft\">Top Left</system:String>\n    <system:String x:Key=\"alTopRight\">Top Right</system:String>\n    <system:String x:Key=\"alBottomLeft\">Bottom Left</system:String>\n    <system:String x:Key=\"alBottomRight\">Bottom Right</system:String>\n    <system:String x:Key=\"alTopSpread\">Top Spread</system:String>\n    <system:String x:Key=\"alBottomSpread\">Bottom Spread</system:String>\n\n    <system:String x:Key=\"fontSize\">Font Size</system:String>\n    <system:String x:Key=\"font\">Font</system:String>\n\n    <system:String x:Key=\"reload\">Reload</system:String>\n    <system:String x:Key=\"openFolder\">Open Folder</system:String>\n    <system:String x:Key=\"saveNew\">Save New</system:String>\n\n    <system:String x:Key=\"thousandsSeparator\">Thousands separator</system:String>\n    <system:String x:Key=\"commas\">Commas</system:String>\n    <system:String x:Key=\"nothing\">Nothing</system:String>\n    <system:String x:Key=\"ZeroPadding\">Zero Padding</system:String>\n\n    <!-- Save Data Tab -->\n    <system:String x:Key=\"savePath\">Save Location</system:String>\n    <system:String x:Key=\"browse\">Browse</system:String>\n    <system:String x:Key=\"saveToFile\">Save frame data to .csv file</system:String>\n    <system:String x:Key=\"formatString\">CSV line format</system:String>\n\n    <!-- Padding Tab -->\n    <system:String x:Key=\"BPMint\">BPM</system:String>\n    <system:String x:Key=\"BPMfrac\">(Decimal Part)</system:String>\n    <system:String x:Key=\"NoteCount\">Notes</system:String>\n    <system:String x:Key=\"Polyphony\">Polyphony</system:String>\n    <system:String x:Key=\"NPS\">NPS</system:String>\n    <system:String x:Key=\"Ticks\">Tick</system:String>\n    <system:String x:Key=\"Bars\">Bar</system:String>\n    <system:String x:Key=\"Frames\">Frame</system:String>\n    <system:String x:Key=\"SetDefault\">Defaults</system:String>\n</ResourceDictionary>"
  },
  {
    "path": "NoteCountRender/NoteCountRender.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProjectGuid>{5F30BA79-D86B-4FC6-BA76-7CBB9EBBA721}</ProjectGuid>\n    <OutputType>Library</OutputType>\n    <AppDesignerFolder>Properties</AppDesignerFolder>\n    <RootNamespace>NoteCountRender</RootNamespace>\n    <AssemblyName>NoteCountRender</AssemblyName>\n    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>\n    <FileAlignment>512</FileAlignment>\n    <Deterministic>true</Deterministic>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>..\\bin\\Any cpu\\Debug\\Plugins\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>..\\bin\\Any cpu\\Release\\Plugins\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <PlatformTarget>x64</PlatformTarget>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Debug|x86'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>..\\bin\\x86\\Debug\\Plugins\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <DebugType>full</DebugType>\n    <PlatformTarget>x86</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Release|x86'\">\n    <OutputPath>..\\bin\\x86\\Release\\Plugins\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <Optimize>true</Optimize>\n    <DebugType>pdbonly</DebugType>\n    <PlatformTarget>x86</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Debug|x64'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>..\\bin\\x64\\Debug\\Plugins\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <DebugType>full</DebugType>\n    <PlatformTarget>x64</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Release|x64'\">\n    <OutputPath>..\\bin\\x64\\Release\\Plugins\\</OutputPath>\n    <DefineConstants>\n    </DefineConstants>\n    <Optimize>true</Optimize>\n    <DebugType>pdbonly</DebugType>\n    <PlatformTarget>x64</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Newtonsoft.Json.12.0.1\\lib\\net45\\Newtonsoft.Json.dll</HintPath>\n    </Reference>\n    <Reference Include=\"OpenTK, Version=3.1.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\OpenTK.3.1.0\\lib\\net20\\OpenTK.dll</HintPath>\n    </Reference>\n    <Reference Include=\"PresentationCore\" />\n    <Reference Include=\"PresentationFramework\" />\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Core\" />\n    <Reference Include=\"System.Drawing\" />\n    <Reference Include=\"System.Xaml\" />\n    <Reference Include=\"System.Xml.Linq\" />\n    <Reference Include=\"System.Data.DataSetExtensions\" />\n    <Reference Include=\"Microsoft.CSharp\" />\n    <Reference Include=\"System.Data\" />\n    <Reference Include=\"System.Net.Http\" />\n    <Reference Include=\"System.Xml\" />\n    <Reference Include=\"WindowsBase\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"Properties\\AssemblyInfo.cs\" />\n    <Compile Include=\"Properties\\Resources.Designer.cs\">\n      <AutoGen>True</AutoGen>\n      <DesignTime>True</DesignTime>\n      <DependentUpon>Resources.resx</DependentUpon>\n    </Compile>\n    <Compile Include=\"Render.cs\" />\n    <Compile Include=\"Settings.cs\" />\n    <Compile Include=\"SettingsCtrl.xaml.cs\">\n      <DependentUpon>SettingsCtrl.xaml</DependentUpon>\n    </Compile>\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\BMEngine\\ZenithEngine.csproj\">\n      <Project>{60f2212a-d56c-4776-836f-6a49453cdbc4}</Project>\n      <Name>ZenithEngine</Name>\n    </ProjectReference>\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"OpenTK.dll.config\" />\n    <None Include=\"packages.config\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Page Include=\"SettingsCtrl.xaml\">\n      <SubType>Designer</SubType>\n      <Generator>MSBuild:Compile</Generator>\n    </Page>\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Languages\\en\\notecounter.xaml\">\n      <SubType>Designer</SubType>\n      <Generator>MSBuild:Compile</Generator>\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </EmbeddedResource>\n    <EmbeddedResource Include=\"Properties\\Resources.resx\">\n      <Generator>ResXFileCodeGenerator</Generator>\n      <LastGenOutput>Resources.Designer.cs</LastGenOutput>\n    </EmbeddedResource>\n  </ItemGroup>\n  <ItemGroup>\n    <Content Include=\"preview.png\" />\n  </ItemGroup>\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n</Project>"
  },
  {
    "path": "NoteCountRender/OpenTK.dll.config",
    "content": "<configuration>\n  <dllmap os=\"linux\" dll=\"opengl32.dll\" target=\"libGL.so.1\"/>\n  <dllmap os=\"linux\" dll=\"glu32.dll\" target=\"libGLU.so.1\"/>\n  <dllmap os=\"linux\" dll=\"openal32.dll\" target=\"libopenal.so.1\"/>\n  <dllmap os=\"linux\" dll=\"alut.dll\" target=\"libalut.so.0\"/>\n  <dllmap os=\"linux\" dll=\"opencl.dll\" target=\"libOpenCL.so\"/>\n  <dllmap os=\"linux\" dll=\"libX11\" target=\"libX11.so.6\"/>\n  <dllmap os=\"linux\" dll=\"libXi\" target=\"libXi.so.6\"/>\n  <dllmap os=\"linux\" dll=\"SDL2.dll\" target=\"libSDL2-2.0.so.0\"/>\n  <dllmap os=\"osx\" dll=\"opengl32.dll\" target=\"/System/Library/Frameworks/OpenGL.framework/OpenGL\"/>\n  <dllmap os=\"osx\" dll=\"openal32.dll\" target=\"/System/Library/Frameworks/OpenAL.framework/OpenAL\" />\n  <dllmap os=\"osx\" dll=\"alut.dll\" target=\"/System/Library/Frameworks/OpenAL.framework/OpenAL\" />\n  <dllmap os=\"osx\" dll=\"libGLES.dll\" target=\"/System/Library/Frameworks/OpenGLES.framework/OpenGLES\" />\n  <dllmap os=\"osx\" dll=\"libGLESv1_CM.dll\" target=\"/System/Library/Frameworks/OpenGLES.framework/OpenGLES\" />\n  <dllmap os=\"osx\" dll=\"libGLESv2.dll\" target=\"/System/Library/Frameworks/OpenGLES.framework/OpenGLES\" />\n  <dllmap os=\"osx\" dll=\"opencl.dll\" target=\"/System/Library/Frameworks/OpenCL.framework/OpenCL\"/>\n  <dllmap os=\"osx\" dll=\"SDL2.dll\" target=\"libSDL2.dylib\"/>\n  <!-- XQuartz compatibility (X11 on Mac) -->\n  <dllmap os=\"osx\" dll=\"libGL.so.1\" target=\"/usr/X11/lib/libGL.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libX11\" target=\"/usr/X11/lib/libX11.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXcursor.so.1\" target=\"/usr/X11/lib/libXcursor.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXi\" target=\"/usr/X11/lib/libXi.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXinerama\" target=\"/usr/X11/lib/libXinerama.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXrandr.so.2\" target=\"/usr/X11/lib/libXrandr.dylib\"/>\n</configuration>\n"
  },
  {
    "path": "NoteCountRender/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// General Information about an assembly is controlled through the following\n// set of attributes. Change these attribute values to modify the information\n// associated with an assembly.\n[assembly: AssemblyTitle(\"NoteCountRender\")]\n[assembly: AssemblyDescription(\"\")]\n[assembly: AssemblyConfiguration(\"\")]\n[assembly: AssemblyCompany(\"\")]\n[assembly: AssemblyProduct(\"NoteCountRender\")]\n[assembly: AssemblyCopyright(\"Copyright ©  2019\")]\n[assembly: AssemblyTrademark(\"\")]\n[assembly: AssemblyCulture(\"\")]\n\n// Setting ComVisible to false makes the types in this assembly not visible\n// to COM components.  If you need to access a type in this assembly from\n// COM, set the ComVisible attribute to true on that type.\n[assembly: ComVisible(false)]\n\n// The following GUID is for the ID of the typelib if this project is exposed to COM\n[assembly: Guid(\"5f30ba79-d86b-4fc6-ba76-7cbb9ebba721\")]\n\n// Version information for an assembly consists of the following four values:\n//\n//      Major Version\n//      Minor Version\n//      Build Number\n//      Revision\n//\n// You can specify all the values or you can default the Build and Revision Numbers\n// by using the '*' as shown below:\n// [assembly: AssemblyVersion(\"1.0.*\")]\n[assembly: AssemblyVersion(\"1.0.0.0\")]\n[assembly: AssemblyFileVersion(\"1.0.0.0\")]\n"
  },
  {
    "path": "NoteCountRender/Properties/Resources.Designer.cs",
    "content": "﻿//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace NoteCountRender.Properties {\n    using System;\n    \n    \n    /// <summary>\n    ///   A strongly-typed resource class, for looking up localized strings, etc.\n    /// </summary>\n    // This class was auto-generated by the StronglyTypedResourceBuilder\n    // class via a tool like ResGen or Visual Studio.\n    // To add or remove a member, edit your .ResX file then rerun ResGen\n    // with the /str option, or rebuild your VS project.\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"System.Resources.Tools.StronglyTypedResourceBuilder\", \"15.0.0.0\")]\n    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]\n    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]\n    internal class Resources {\n        \n        private static global::System.Resources.ResourceManager resourceMan;\n        \n        private static global::System.Globalization.CultureInfo resourceCulture;\n        \n        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute(\"Microsoft.Performance\", \"CA1811:AvoidUncalledPrivateCode\")]\n        internal Resources() {\n        }\n        \n        /// <summary>\n        ///   Returns the cached ResourceManager instance used by this class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Resources.ResourceManager ResourceManager {\n            get {\n                if (object.ReferenceEquals(resourceMan, null)) {\n                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager(\"NoteCountRender.Properties.Resources\", typeof(Resources).Assembly);\n                    resourceMan = temp;\n                }\n                return resourceMan;\n            }\n        }\n        \n        /// <summary>\n        ///   Overrides the current thread's CurrentUICulture property for all\n        ///   resource lookups using this strongly typed resource class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Globalization.CultureInfo Culture {\n            get {\n                return resourceCulture;\n            }\n            set {\n                resourceCulture = value;\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized resource of type System.Drawing.Bitmap.\n        /// </summary>\n        internal static System.Drawing.Bitmap preview {\n            get {\n                object obj = ResourceManager.GetObject(\"preview\", resourceCulture);\n                return ((System.Drawing.Bitmap)(obj));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "NoteCountRender/Properties/Resources.resx",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The primary goals of this format is to allow a simple XML format \n    that is mostly human readable. The generation and parsing of the \n    various data types are done through the TypeConverter classes \n    associated with the data types.\n    \n    Example:\n    \n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n                \n    There are any number of \"resheader\" rows that contain simple \n    name/value pairs.\n    \n    Each data row contains a name, and value. The row also contains a \n    type or mimetype. Type corresponds to a .NET class that support \n    text/value conversion through the TypeConverter architecture. \n    Classes that don't support this are serialized and stored with the \n    mimetype set.\n    \n    The mimetype is used for serialized objects, and tells the \n    ResXResourceReader how to depersist the object. This is currently not \n    extensible. For a given mimetype the value must be set accordingly:\n    \n    Note - application/x-microsoft.net.object.binary.base64 is the format \n    that the ResXResourceWriter will generate, however the reader can \n    read any of the formats listed below.\n    \n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n    \n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array \n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <assembly alias=\"System.Windows.Forms\" name=\"System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" />\n  <data name=\"preview\" type=\"System.Resources.ResXFileRef, System.Windows.Forms\">\n    <value>..\\preview.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>\n  </data>\n</root>"
  },
  {
    "path": "NoteCountRender/README.md",
    "content": "# Zenith-Note-Count-Render\nNote counter for Zenith\n"
  },
  {
    "path": "NoteCountRender/Render.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Drawing;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows.Media.Imaging;\nusing ZenithEngine;\nusing OpenTK;\nusing OpenTK.Graphics;\nusing OpenTK.Graphics.OpenGL;\nusing System.Windows.Controls;\n\nnamespace NoteCountRender\n{\n    public class Render : IPluginRender\n    {\n        #region PreviewConvert\n        BitmapImage BitmapToImageSource(Bitmap bitmap)\n        {\n            using (MemoryStream memory = new MemoryStream())\n            {\n                bitmap.Save(memory, System.Drawing.Imaging.ImageFormat.Bmp);\n                memory.Position = 0;\n                BitmapImage bitmapimage = new BitmapImage();\n                bitmapimage.BeginInit();\n                bitmapimage.StreamSource = memory;\n                bitmapimage.CacheOption = BitmapCacheOption.OnLoad;\n                bitmapimage.EndInit();\n\n                return bitmapimage;\n            }\n        }\n        #endregion\n\n        public string Name => \"Note Counter\";\n\n        public string Description => \"Generate note counts and other midi statistics\";\n\n        public string LanguageDictName { get; } = \"notecounter\";\n\n        public bool Initialized { get; set; } = false;\n\n        public System.Windows.Media.ImageSource PreviewImage { get; set; }\n\n        public bool ManualNoteDelete => true;\n\n        public double NoteCollectorOffset => 0;\n\n        public double Tempo { get; set; }\n\n        public NoteColor[][] NoteColors { get; set; }\n\n        public MidiInfo CurrentMidi { get; set; }\n\n        public double NoteScreenTime => 0;\n\n        public long LastNoteCount { get; set; } = 0;\n\n        public System.Windows.Controls.Control SettingsControl { get; set; }\n\n        RenderSettings renderSettings;\n        Settings settings;\n\n        GLTextEngine textEngine;\n        public void Dispose()\n        {\n            textEngine.Dispose();\n            Initialized = false;\n            if (outputCsv != null) outputCsv.Close();\n            Console.WriteLine(\"Disposed of NoteCountRender\");\n        }\n\n        int fontSize = 40;\n        string font = \"Arial\";\n        public System.Drawing.FontStyle fontStyle = System.Drawing.FontStyle.Regular;\n\n        StreamWriter outputCsv = null;\n\n        public void Init()\n        {\n            textEngine = new GLTextEngine();\n            if (settings.fontName != font || settings.fontSize != fontSize || settings.fontStyle != fontStyle)\n            {\n                font = settings.fontName;\n                fontSize = settings.fontSize;\n                fontStyle = settings.fontStyle;\n            }\n            textEngine.SetFont(font, fontStyle, fontSize);\n            noteCount = 0;\n            nps = 0;\n            Mnps = 0;\n            frames = 0;\n            Mplph = 0;\n            notesHit = new LinkedList<long>();\n            Initialized = true;\n\n            if (settings.saveCsv && settings.csvOutput != \"\")\n            {\n                outputCsv = new StreamWriter(settings.csvOutput);\n            }\n\n            Console.WriteLine(\"Initialised NoteCountRender\");\n        }\n\n        public Render(RenderSettings settings)\n        {\n            this.renderSettings = settings;\n            this.settings = new Settings();\n            SettingsControl = new SettingsCtrl(this.settings);\n            PreviewImage = BitmapToImageSource(Properties.Resources.preview);\n        }\n\n        long noteCount = 0;\n        long nps = 0;\n        long Mnps = 0;\n        int frames = 0;\n        long currentNotes = 0;\n        long polyphony = 0;\n        long Mplph = 0;\n        \n        LinkedList<long> notesHit = new LinkedList<long>();\n\n        public void RenderFrame(FastList<Note> notes, double midiTime, int finalCompositeBuff)\n        {\n            GL.BindFramebuffer(FramebufferTarget.Framebuffer, finalCompositeBuff);\n\n            GL.Viewport(0, 0, renderSettings.width, renderSettings.height);\n            GL.Clear(ClearBufferMask.ColorBufferBit);\n            GL.Clear(ClearBufferMask.DepthBufferBit);\n\n            GL.Enable(EnableCap.Blend);\n            GL.EnableClientState(ArrayCap.VertexArray);\n            GL.EnableClientState(ArrayCap.ColorArray);\n            GL.EnableClientState(ArrayCap.TextureCoordArray);\n            GL.Enable(EnableCap.Texture2D);\n\n            GL.EnableVertexAttribArray(0);\n            GL.EnableVertexAttribArray(1);\n\n            GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);\n\n            if (settings.fontName != font || settings.fontSize != fontSize || settings.fontStyle != fontStyle)\n            {\n                font = settings.fontName;\n                fontSize = settings.fontSize;\n                fontStyle = settings.fontStyle;\n                textEngine.SetFont(font, fontStyle, fontSize);\n            }\n            if (!renderSettings.Paused)\n            {\n                polyphony = 0;\n                currentNotes = 0;\n                long nc = 0;\n                lock (notes)\n                    foreach (Note n in notes)\n                    {\n                        nc++;\n                        if (n.start < midiTime)\n                        {\n                            if (n.end > midiTime || !n.hasEnded)\n                            {\n                                polyphony++;\n                            }\n                            else if (n.meta != null)\n                            {\n                                n.delete = true;\n                            }\n                            if (n.meta == null)\n                            {\n                                currentNotes++;\n                                noteCount++;\n                                n.meta = true;\n                            }\n                        }\n                        if (n.start > midiTime) break;\n                    }\n                LastNoteCount = nc;\n                notesHit.AddLast(currentNotes);\n                while (notesHit.Count > renderSettings.fps) notesHit.RemoveFirst();\n                nps = notesHit.Sum();\n                if (Mnps < nps) Mnps = nps;\n                if (Mplph < polyphony) Mplph = polyphony;\n                frames++;\n            }\n\n            double tempo = Tempo;\n            \n            int seconds = (int)Math.Floor((double)frames * 1000 / renderSettings.fps);\n            int totalsec = (int)Math.Floor(CurrentMidi.secondsLength * 1000);\n            int totalframes = (int)Math.Ceiling(CurrentMidi.secondsLength * renderSettings.fps);\n            if (seconds > totalsec) seconds = totalsec;\n            TimeSpan time = new TimeSpan(0, 0, 0, 0, seconds);\n            TimeSpan totaltime = new TimeSpan(0, 0, 0, 0, totalsec);\n            if (frames > totalframes) frames = totalframes;\n\n            double barDivide = (double)CurrentMidi.division * CurrentMidi.timeSig.numerator / CurrentMidi.timeSig.denominator * 4;\n\n            long limMidiTime = (long)midiTime;\n            if (limMidiTime > CurrentMidi.tickLength) limMidiTime = CurrentMidi.tickLength;\n\n            long bar = (long)Math.Floor(limMidiTime / barDivide) + 1;\n            long maxbar = (long)Math.Floor(CurrentMidi.tickLength / barDivide);\n            if (bar > maxbar) bar = maxbar;\n            string fzp = new string('0', renderSettings.fps.ToString().Length);\n            \n            Func<string, Commas, string> replace = (text, separator) =>\n            {\n                Zeroes zeroes = new Zeroes();\n                string sep = \"\";\n                if (separator == Commas.Comma) sep = \"#,\";\n                if (settings.PaddingZeroes)\n                {\n                    zeroes.bpm = new string('0', settings.BPMintPad) +\".\" + new string('0', settings.BPMDecPtPad);\n                    zeroes.nc = new string('0', settings.NoteCountPad);\n                    zeroes.plph = new string('0', settings.PolyphonyPad);\n                    zeroes.nps = new string('0', settings.NPSPad);\n                    zeroes.tick = new string('0', settings.TicksPad);\n                    zeroes.bars = new string('0', settings.BarCountPad);\n                    zeroes.frms = new string('0', settings.FrCountPad);\n                }\n\n                text = text.Replace(\"{bpm}\", tempo.ToString(zeroes.bpm));\n\n                text = text.Replace(\"{nc}\", noteCount.ToString(sep + zeroes.nc));\n                text = text.Replace(\"{nr}\", (CurrentMidi.noteCount - noteCount).ToString(sep + zeroes.nc));\n                text = text.Replace(\"{tn}\", CurrentMidi.noteCount.ToString(sep + zeroes.nc));\n\n                text = text.Replace(\"{nps}\", nps.ToString(sep + zeroes.nps));\n                text = text.Replace(\"{mnps}\", Mnps.ToString(sep + zeroes.nps));\n                text = text.Replace(\"{plph}\", polyphony.ToString(sep + zeroes.plph));\n                text = text.Replace(\"{mplph}\", Mplph.ToString(sep + zeroes.plph));\n\n                text = text.Replace(\"{currsec}\", ((double)(seconds / 100) / 10).ToString(sep + \"0.0\"));\n                text = text.Replace(\"{currtime}\", time.ToString(\"mm\\\\:ss\"));\n                text = text.Replace(\"{cmiltime}\", time.ToString(\"mm\\\\:ss\\\\.fff\"));\n                text = text.Replace(\"{cfrtime}\", time.ToString(\"mm\\\\:ss\") + \";\" + (frames % renderSettings.fps).ToString(fzp));\n\n                text = text.Replace(\"{totalsec}\", ((double)(totalsec / 100) / 10).ToString(sep + \"0.0\"));\n                text = text.Replace(\"{totaltime}\", totaltime.ToString(\"mm\\\\:ss\"));\n                text = text.Replace(\"{tmiltime}\", totaltime.ToString(\"mm\\\\:ss\\\\.fff\"));\n                text = text.Replace(\"{tfrtime}\", totaltime.ToString(\"mm\\\\:ss\") + \";\" + (totalframes % renderSettings.fps).ToString(fzp));\n\n                text = text.Replace(\"{remsec}\", ((double)((totalsec - seconds) / 100) / 10).ToString(sep + \"0.0\"));\n                text = text.Replace(\"{remtime}\", (totaltime - time).ToString(\"mm\\\\:ss\"));\n                text = text.Replace(\"{rmiltime}\", (totaltime - time).ToString(\"mm\\\\:ss\\\\.fff\"));\n                text = text.Replace(\"{rfrtime}\", (totaltime - time).ToString(\"mm\\\\:ss\") + \";\" + ((totalframes - frames + renderSettings.fps) % renderSettings.fps).ToString(fzp));\n\n                text = text.Replace(\"{currticks}\", (limMidiTime).ToString(sep + zeroes.tick));\n                text = text.Replace(\"{totalticks}\", (CurrentMidi.tickLength).ToString(sep + zeroes.tick));\n                text = text.Replace(\"{remticks}\", (CurrentMidi.tickLength - limMidiTime).ToString(sep + zeroes.tick));\n\n                text = text.Replace(\"{currbars}\", bar.ToString(sep + zeroes.bars));\n                text = text.Replace(\"{totalbars}\", maxbar.ToString(sep + zeroes.bars));\n                text = text.Replace(\"{rembars}\", (maxbar - bar).ToString(sep + zeroes.bars));\n\n                text = text.Replace(\"{ppq}\", CurrentMidi.division.ToString());\n                text = text.Replace(\"{tsn}\", CurrentMidi.timeSig.numerator.ToString());\n                text = text.Replace(\"{tsd}\", CurrentMidi.timeSig.denominator.ToString());\n                text = text.Replace(\"{avgnps}\", ((double)CurrentMidi.noteCount / (double)CurrentMidi.secondsLength).ToString(sep + \"0.00\"));\n\n                text = text.Replace(\"{currframes}\", frames.ToString(sep + zeroes.frms));\n                text = text.Replace(\"{totalframes}\", totalframes.ToString(sep + zeroes.frms));\n                text = text.Replace(\"{remframes}\", (totalframes - frames).ToString(sep + zeroes.frms));\n\n                text = text.Replace(\"{notep}\", (((decimal)noteCount * 1000000 / (decimal)CurrentMidi.noteCount) / 10000).ToString(\"00.0000\"));\n                text = text.Replace(\"{tickp}\", (((decimal)limMidiTime * 1000000 / (decimal)CurrentMidi.tickLength) / 10000).ToString(\"00.0000\"));\n                text = text.Replace(\"{timep}\", (((decimal)seconds * 1000000 / (decimal)totalsec) / 10000).ToString(\"00.0000\"));\n                return text;\n            };\n\n\n            string renderText = settings.text;\n            renderText = replace(renderText, settings.thousandSeparator);\n\n            if (settings.textAlignment == Alignments.TopLeft)\n            {\n                var size = textEngine.GetBoundBox(renderText);\n                Matrix4 transform = Matrix4.Identity;\n                transform = Matrix4.Mult(transform, Matrix4.CreateScale(1.0f / renderSettings.width, -1.0f / renderSettings.height, 1.0f));\n                transform = Matrix4.Mult(transform, Matrix4.CreateTranslation(-1, 1, 0));\n                transform = Matrix4.Mult(transform, Matrix4.CreateRotationZ(0));\n\n                textEngine.Render(renderText, transform, Color4.White);\n            }\n            else if (settings.textAlignment == Alignments.TopRight)\n            {\n                float offset = 0;\n                string[] lines = renderText.Split('\\n');\n                foreach (var line in lines)\n                {\n                    var size = textEngine.GetBoundBox(line);\n                    Matrix4 transform = Matrix4.Identity;\n                    transform = Matrix4.Mult(transform, Matrix4.CreateTranslation(-size.Width, offset, 0));\n                    transform = Matrix4.Mult(transform, Matrix4.CreateScale(1.0f / renderSettings.width, -1.0f / renderSettings.height, 1.0f));\n                    transform = Matrix4.Mult(transform, Matrix4.CreateTranslation(1, 1, 0));\n                    transform = Matrix4.Mult(transform, Matrix4.CreateRotationZ(0));\n                    offset += size.Height;\n                    textEngine.Render(line, transform, Color4.White);\n                }\n            }\n            else if (settings.textAlignment == Alignments.BottomLeft)\n            {\n                float offset = 0;\n                string[] lines = renderText.Split('\\n');\n                foreach (var line in lines.Reverse())\n                {\n                    var size = textEngine.GetBoundBox(line);\n                    Matrix4 transform = Matrix4.Identity;\n                    transform = Matrix4.Mult(transform, Matrix4.CreateTranslation(0, offset - size.Height, 0));\n                    transform = Matrix4.Mult(transform, Matrix4.CreateScale(1.0f / renderSettings.width, -1.0f / renderSettings.height, 1.0f));\n                    transform = Matrix4.Mult(transform, Matrix4.CreateTranslation(-1, -1, 0));\n                    transform = Matrix4.Mult(transform, Matrix4.CreateRotationZ(0));\n                    offset -= size.Height;\n                    textEngine.Render(line, transform, Color4.White);\n                }\n            }\n            else if (settings.textAlignment == Alignments.BottomRight)\n            {\n                float offset = 0;\n                string[] lines = renderText.Split('\\n');\n                foreach (var line in lines.Reverse())\n                {\n                    var size = textEngine.GetBoundBox(line);\n                    Matrix4 transform = Matrix4.Identity;\n                    transform = Matrix4.Mult(transform, Matrix4.CreateTranslation(-size.Width, offset - size.Height, 0));\n                    transform = Matrix4.Mult(transform, Matrix4.CreateScale(1.0f / renderSettings.width, -1.0f / renderSettings.height, 1.0f));\n                    transform = Matrix4.Mult(transform, Matrix4.CreateTranslation(1, -1, 0));\n                    transform = Matrix4.Mult(transform, Matrix4.CreateRotationZ(0));\n                    offset -= size.Height;\n                    textEngine.Render(line, transform, Color4.White);\n                }\n            }\n            else if (settings.textAlignment == Alignments.TopSpread)\n            {\n                float offset = 0;\n                string[] lines = renderText.Split('\\n');\n                float dist = 1.0f / (lines.Length + 1);\n                int p = 1;\n                foreach (var line in lines.Reverse())\n                {\n                    var size = textEngine.GetBoundBox(line);\n                    Matrix4 transform = Matrix4.Identity;\n                    transform = Matrix4.Mult(transform, Matrix4.CreateTranslation(-size.Width / 2, 0, 0));\n                    transform = Matrix4.Mult(transform, Matrix4.CreateScale(1.0f / renderSettings.width, -1.0f / renderSettings.height, 1.0f));\n                    transform = Matrix4.Mult(transform, Matrix4.CreateTranslation((dist * p++) * 2 - 1, 1, 0));\n                    transform = Matrix4.Mult(transform, Matrix4.CreateRotationZ(0));\n                    offset -= size.Height;\n                    textEngine.Render(line, transform, Color4.White);\n                }\n            }\n            else if (settings.textAlignment == Alignments.BottomSpread)\n            {\n                float offset = 0;\n                string[] lines = renderText.Split('\\n');\n                float dist = 1.0f / (lines.Length + 1);\n                int p = 1;\n                foreach (var line in lines.Reverse())\n                {\n                    var size = textEngine.GetBoundBox(line);\n                    Matrix4 transform = Matrix4.Identity;\n                    transform = Matrix4.Mult(transform, Matrix4.CreateTranslation(-size.Width / 2, -size.Height, 0));\n                    transform = Matrix4.Mult(transform, Matrix4.CreateScale(1.0f / renderSettings.width, -1.0f / renderSettings.height, 1.0f));\n                    transform = Matrix4.Mult(transform, Matrix4.CreateTranslation((dist * p++) * 2 - 1, -1, 0));\n                    transform = Matrix4.Mult(transform, Matrix4.CreateRotationZ(0));\n                    offset -= size.Height;\n                    textEngine.Render(line, transform, Color4.White);\n                }\n            }\n\n            if(outputCsv != null)\n            {\n                outputCsv.WriteLine(replace(settings.csvFormat, Commas.Nothing));\n            }\n\n            GL.Disable(EnableCap.Blend);\n            GL.Disable(EnableCap.Texture2D);\n            GL.DisableClientState(ArrayCap.VertexArray);\n            GL.DisableClientState(ArrayCap.ColorArray);\n            GL.DisableClientState(ArrayCap.TextureCoordArray);\n\n            GL.DisableVertexAttribArray(0);\n            GL.DisableVertexAttribArray(1);\n        }\n\n        public void ReloadTrackColors()\n        {\n\n        }\n    }\n}\n"
  },
  {
    "path": "NoteCountRender/Settings.cs",
    "content": "﻿using OpenTK.Graphics.OpenGL;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows.Media.Effects;\n\nnamespace NoteCountRender\n{\n    public enum Alignments\n    {\n        TopLeft,\n        TopRight,\n        BottomLeft,\n        BottomRight,\n        TopSpread,\n        BottomSpread,\n    }\n    public enum Commas\n    {\n        Comma,\n        Dot,\n        Nothing,\n    }\n    public class Settings\n    {\n        public string text = \"Notes: {nc} / {tn}\\nBPM: {bpm}\\nNPS: {nps}\\nPPQ: {ppq}\\nPolyphony: {plph}\\nSeconds: {seconds}\\nTime: {time}\\nTicks: {ticks}\";\n        public Alignments textAlignment = Alignments.TopLeft;\n\n        public int fontSize = 40;\n        public string fontName = \"Arial\";\n        public System.Drawing.FontStyle fontStyle = System.Drawing.FontStyle.Regular;\n\n        public Commas thousandSeparator = Commas.Comma;\n\n        public bool saveCsv = false;\n        public bool PaddingZeroes = false;\n        public string csvOutput = \"\";\n        public string csvFormat = \"{nps},{plph},{bpm},{nc}\";\n\n        public int BPMintPad = 3;\n        public int BPMDecPtPad = 2;\n        public int NoteCountPad = 5;\n        public int PolyphonyPad = 3;\n        public int NPSPad = 3;\n        public int TicksPad = 5;\n        public int BarCountPad = 3;\n        public int FrCountPad = 5;\n    }\n    public class Zeroes\n    {\n        public string bpm = \"0.00\";\n        public string nc = \"0\";\n        public string plph = \"0\";\n        public string nps = \"0\";\n        public string tick = \"0\";\n        public string bars = \"0\";\n        public string frms = \"0\";\n    }\n}\n"
  },
  {
    "path": "NoteCountRender/SettingsCtrl.xaml",
    "content": "﻿<UserControl x:Class=\"NoteCountRender.SettingsCtrl\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:ui=\"clr-namespace:ZenithEngine.UI;assembly=ZenithEngine\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" \n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\" \n             xmlns:local=\"clr-namespace:NoteCountRender\"\n             xmlns:xctk=\"http://schemas.xceed.com/wpf/xaml/toolkit\"\n             mc:Ignorable=\"d\" \n             d:DesignHeight=\"450\" d:DesignWidth=\"800\">\n    <UserControl.Resources>\n        <ResourceDictionary>\n            <ResourceDictionary.MergedDictionaries>\n                <ResourceDictionary>\n                    <ResourceDictionary.MergedDictionaries>\n                        <ResourceDictionary Source=\"pack://siteoforigin:,,,/Languages/en/notecounter.xaml\" />\n                        <ResourceDictionary Source=\"pack://application:,,,/ZenithEngine;component/UI/Material.xaml\"/>\n                    </ResourceDictionary.MergedDictionaries>\n                </ResourceDictionary>\n            </ResourceDictionary.MergedDictionaries>\n            <Style TargetType=\"TabControl\" BasedOn=\"{StaticResource SubTabs}\"/>\n            <Style TargetType=\"TabItem\" BasedOn=\"{StaticResource SubTabItems}\"/>\n        </ResourceDictionary>\n    </UserControl.Resources>\n    <Grid>\n        <TabControl Margin=\"10,10,10,10\">\n            <TabItem Header=\"{DynamicResource Render}\">\n                <DockPanel Margin=\"10\">\n                    <DockPanel DockPanel.Dock=\"Top\">\n                        <StackPanel Width=\"530\">\n                            <DockPanel HorizontalAlignment=\"Left\" LastChildFill=\"False\" Margin=\"0,0,0,0\" VerticalAlignment=\"Top\">\n                                <Label DockPanel.Dock=\"Left\" Content=\"{DynamicResource fontSize}\" VerticalAlignment=\"Top\"/>\n                                <ui:NumberSelect x:Name=\"fontSize\" Value=\"1\"  Maximum=\"15360\" Minimum=\"1\" HorizontalAlignment=\"Left\" Width=\"99\" IsEnabled=\"{Binding IsEnabled, ElementName=notPreviewingOrRendering}\"  DockPanel.Dock=\"Left\" Margin=\"5,0,0,0\" ValueChanged=\"FontSize_ValueChanged\"  />\n                                <Label DockPanel.Dock=\"Left\" Content=\"{DynamicResource font}\" VerticalAlignment=\"Top\"/>\n                                <ComboBox x:Name=\"fontSelect\" DockPanel.Dock=\"Left\" Width=\"120\" Margin=\"5,0,0,0\" SelectionChanged=\"FontSelect_SelectionChanged\"/>\n                                <ComboBox x:Name=\"fontStyle\" HorizontalAlignment=\"Left\" Margin=\"10,0,0,0\" Width=\"120\" SelectionChanged=\"FontStyle_SelectionChanged\"/>\n                            </DockPanel>\n                            <DockPanel HorizontalAlignment=\"Left\" LastChildFill=\"False\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\" >\n                                <Label Content=\"{DynamicResource alignments}\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Top\" DockPanel.Dock=\"Left\"/>\n                                <ComboBox x:Name=\"alignSelect\" DockPanel.Dock=\"Left\" HorizontalAlignment=\"Left\" Margin=\"5,0,0,0\" VerticalAlignment=\"Top\" Height=\"26\" SelectedIndex=\"0\" SelectionChanged=\"AlignSelect_SelectionChanged\">\n                                    <ComboBoxItem Content=\"{DynamicResource alTopLeft}\"/>\n                                    <ComboBoxItem Content=\"{DynamicResource alTopRight}\"/>\n                                    <ComboBoxItem Content=\"{DynamicResource alBottomLeft}\"/>\n                                    <ComboBoxItem Content=\"{DynamicResource alBottomRight}\"/>\n                                    <ComboBoxItem Content=\"{DynamicResource alTopSpread}\"/>\n                                    <ComboBoxItem Content=\"{DynamicResource alBottomSpread}\"/>\n                                </ComboBox>\n                            </DockPanel>\n                            <DockPanel HorizontalAlignment=\"Left\" LastChildFill=\"False\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\">\n                                <ComboBox x:Name=\"templates\" DockPanel.Dock=\"Left\" Width=\"120\" SelectionChanged=\"Templates_SelectionChanged\"/>\n                                <Button x:Name=\"reload\" Content=\"{DynamicResource reload}\" HorizontalAlignment=\"Left\" Padding=\"10,0,10,0\" DockPanel.Dock=\"Left\" Margin=\"5,0,0,0\" Click=\"Reload_Click\"/>\n                                <Button x:Name=\"openFolder\" Content=\"{DynamicResource openFolder}\" HorizontalAlignment=\"Left\" Padding=\"10,0,10,0\" DockPanel.Dock=\"Left\" Margin=\"5,0,0,0\" Click=\"openFolder_Click\"/>\n                                <Button x:Name=\"newProfile\" Content=\"{DynamicResource saveNew}\" HorizontalAlignment=\"Left\" Margin=\"15,0,0,0\" Padding=\"10,0,10,0\" Click=\"NewProfile_Click\" DockPanel.Dock=\"Left\"/>\n                                <TextBox DockPanel.Dock=\"Left\" x:Name=\"profileName\" HorizontalAlignment=\"Left\" Margin=\"5,0,0,0\" TextWrapping=\"Wrap\" Text=\"\" Width=\"110\"/>\n                                <Label DockPanel.Dock=\"Right\" HorizontalAlignment=\"Right\" Content=\".txt\" />\n                            </DockPanel>\n                        </StackPanel>\n                        <StackPanel Margin=\"30,0,0,0\">\n                            <Label Content=\"{DynamicResource thousandsSeparator}\"/>\n                            <ui:BetterRadio x:Name=\"useCommas\" IsChecked=\"True\" Text=\"{DynamicResource commas}\" Margin=\"0,0,0,5\" RadioChecked=\"useCommas_RadioChecked\"/>\n                            <!--<ui:BetterRadio x:Name=\"useDots\" Text=\"Dots\" Margin=\"0,0,0,5\" RadioChecked=\"useCommas_RadioChecked\"/>-->\n                            <ui:BetterRadio x:Name=\"useNothing\" Text=\"{DynamicResource nothing}\" Margin=\"0,0,0,5\" RadioChecked=\"useCommas_RadioChecked\"/>\n                            <ui:BetterCheckbox Margin=\"-4,2,0,0\" x:Name=\"ZeroPadding\" Text=\"{DynamicResource ZeroPadding}\" CheckToggled=\"ZP\" />\n                        </StackPanel>\n                    </DockPanel>\n                    <TextBox AcceptsReturn=\"True\" VerticalContentAlignment=\"Top\" x:Name=\"textTemplate\" Margin=\"0,10,0,0\" TextWrapping=\"Wrap\" Text=\"TextBox\" TextChanged=\"TextTemplate_TextChanged\"/>\n                </DockPanel>\n            </TabItem>\n            <TabItem Header=\"{DynamicResource SaveCSV}\">\n                <StackPanel Margin=\"10\">\n                    <ui:BetterCheckbox Name=\"saveCsv\" CheckToggled=\"saveCsv_Checked\" Text=\"{DynamicResource saveToFile}\" Margin=\"0,0,0,0\"/>\n                    <DockPanel Height=\"26\" LastChildFill=\"True\" Margin=\"0,10,0,0\">\n                        <Label HorizontalAlignment=\"Left\" Margin=\"0,0,0,0\" Content=\"{DynamicResource savePath}\"/>\n                        <Button x:Name=\"browseOutputSaveButton\" Content=\"{DynamicResource browse}\" HorizontalAlignment=\"Left\" Margin=\"10,0,0,0\" Padding=\"20,0,20,0\" Click=\"browseOutputSaveButton_Click\"/>\n                        <TextBox x:Name=\"csvPath\" Margin=\"10,0,0,0\" IsEnabled=\"False\" TextWrapping=\"Wrap\" Text=\"\"/>\n                    </DockPanel>\n                    <DockPanel Height=\"26\" LastChildFill=\"True\" Margin=\"0,10,0,0\">\n                        <Label HorizontalAlignment=\"Left\" Margin=\"0,0,0,0\" Content=\"{DynamicResource formatString}\"/>\n                        <TextBox x:Name=\"csvFormat\" Margin=\"10,0,0,0\" TextWrapping=\"Wrap\" Text=\"\" TextChanged=\"csvFormat_TextChanged\"/>\n                    </DockPanel>\n                </StackPanel>\n            </TabItem>\n            <TabItem Header=\"{DynamicResource Padding}\">\n                <StackPanel Margin=\"10\">\n                    <Grid>\n                        <Grid.ColumnDefinitions>\n                            <ColumnDefinition Width=\"2*\" />\n                            <ColumnDefinition Width=\"2*\" />\n                            <ColumnDefinition Width=\"3*\" />\n                            <ColumnDefinition Width=\"2*\" />\n                            <ColumnDefinition Width=\"9*\" />\n                        </Grid.ColumnDefinitions>\n                        <Grid.RowDefinitions>\n                            <RowDefinition Height=\"30\" />\n                            <RowDefinition Height=\"30\" />\n                            <RowDefinition Height=\"30\" />\n                            <RowDefinition Height=\"30\" />\n                            <RowDefinition Height=\"30\" />\n                            <RowDefinition Height=\"30\" />\n                            <RowDefinition Height=\"30\" />\n                            <RowDefinition Height=\"15\" />\n                            <RowDefinition Height=\"30\" />\n                        </Grid.RowDefinitions>\n                        <Label HorizontalAlignment=\"Right\" Margin=\"0,1,0,0\" Content=\"{DynamicResource BPMint}\" />\n                        <ui:NumberSelect Grid.Column=\"1\" Grid.Row=\"0\" x:Name=\"BPMint\" Value=\"3\" Maximum=\"5\" Minimum=\"1\" Width=\"64\" IsEnabled=\"{Binding IsEnabled, ElementName=notPreviewingOrRendering}\"  HorizontalAlignment=\"Left\" Margin=\"10,-2,0,0\" ValueChanged=\"Paddings_ValueChanged\" Height=\"32\" VerticalAlignment=\"Top\"/>\n                        <Label Grid.Column=\"2\" Grid.Row=\"0\" HorizontalAlignment=\"Right\" Margin=\"10,1,0,0\" Content=\"{DynamicResource BPMfrac}\"/>\n                        <ui:NumberSelect Grid.Column=\"3\" Grid.Row=\"0\" x:Name=\"BPMDecPt\" Value=\"2\" Maximum=\"12\" Minimum=\"0\" Width=\"64\" IsEnabled=\"{Binding IsEnabled, ElementName=notPreviewingOrRendering}\"  HorizontalAlignment=\"Left\" Margin=\"10,-2,0,0\" ValueChanged=\"Paddings_ValueChanged\"/>\n                        <Label Grid.Column=\"0\" Grid.Row=\"1\" HorizontalAlignment=\"Right\" Margin=\"0,0,0,0\" Content=\"{DynamicResource NoteCount}\" />\n                        <ui:NumberSelect Grid.Column=\"1\" Grid.Row=\"1\" x:Name=\"NoteCount\" Value=\"5\" Maximum=\"12\" Minimum=\"1\" Width=\"64\" IsEnabled=\"{Binding IsEnabled, ElementName=notPreviewingOrRendering}\"  HorizontalAlignment=\"Left\" Margin=\"10,-2,0,0\" ValueChanged=\"Paddings_ValueChanged\"/>\n                        <Label Grid.Column=\"0\" Grid.Row=\"2\" HorizontalAlignment=\"Right\" Margin=\"0,0,0,0\" Content=\"{DynamicResource Polyphony}\" />\n                        <ui:NumberSelect Grid.Column=\"1\" Grid.Row=\"2\" x:Name=\"Polyphony\" Value=\"3\" Maximum=\"9\" Minimum=\"1\" Width=\"64\" IsEnabled=\"{Binding IsEnabled, ElementName=notPreviewingOrRendering}\"  HorizontalAlignment=\"Left\" Margin=\"10,-2,0,0\" ValueChanged=\"Paddings_ValueChanged\"/>\n                        <Label Grid.Column=\"0\" Grid.Row=\"3\" HorizontalAlignment=\"Right\" Margin=\"0,0,0,0\" Content=\"{DynamicResource NPS}\" />\n                        <ui:NumberSelect Grid.Column=\"1\" Grid.Row=\"3\" x:Name=\"NPS\" Value=\"3\" Maximum=\"9\" Minimum=\"1\" Width=\"64\" IsEnabled=\"{Binding IsEnabled, ElementName=notPreviewingOrRendering}\"  HorizontalAlignment=\"Left\" Margin=\"10,-2,0,0\" ValueChanged=\"Paddings_ValueChanged\"/>\n                        <Label Grid.Column=\"0\" Grid.Row=\"4\" HorizontalAlignment=\"Right\" Margin=\"0,0,0,0\" Content=\"{DynamicResource Ticks}\" />\n                        <ui:NumberSelect Grid.Column=\"1\" Grid.Row=\"4\" x:Name=\"Ticks\" Value=\"5\" Maximum=\"9\" Minimum=\"1\" Width=\"64\" IsEnabled=\"{Binding IsEnabled, ElementName=notPreviewingOrRendering}\"  HorizontalAlignment=\"Left\" Margin=\"10,-2,0,0\" ValueChanged=\"Paddings_ValueChanged\"/>\n                        <Label Grid.Column=\"0\" Grid.Row=\"5\" HorizontalAlignment=\"Right\" Margin=\"0,0,0,0\" Content=\"{DynamicResource Bars}\" />\n                        <ui:NumberSelect Grid.Column=\"1\" Grid.Row=\"5\" x:Name=\"Bars\" Value=\"3\" Maximum=\"5\" Minimum=\"1\" Width=\"64\" IsEnabled=\"{Binding IsEnabled, ElementName=notPreviewingOrRendering}\"  HorizontalAlignment=\"Left\" Margin=\"10,-2,0,0\" ValueChanged=\"Paddings_ValueChanged\"/>\n                        <Label Grid.Column=\"0\" Grid.Row=\"6\" HorizontalAlignment=\"Right\" Margin=\"0,0,0,0\" Content=\"{DynamicResource Frames}\" />\n                        <ui:NumberSelect Grid.Column=\"1\" Grid.Row=\"6\" x:Name=\"Frames\" Value=\"5\" Maximum=\"5\" Minimum=\"1\" Width=\"64\" IsEnabled=\"{Binding IsEnabled, ElementName=notPreviewingOrRendering}\"  HorizontalAlignment=\"Left\" Margin=\"10,-2,0,0\" ValueChanged=\"Paddings_ValueChanged\"/>\n                        <Button Grid.ColumnSpan=\"2\" Grid.Row=\"8\" HorizontalAlignment=\"Center\" x:Name=\"SetDefault\" Content=\"{DynamicResource SetDefault}\" Height =\"25\" Padding=\"20,2,20,2\" Click=\"SetDefault_Click\" />\n                    </Grid>\n                </StackPanel>\n            </TabItem>\n        </TabControl>\n\n    </Grid>\n</UserControl>\n"
  },
  {
    "path": "NoteCountRender/SettingsCtrl.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\nusing Path = System.IO.Path;\nusing FontStyle = System.Drawing.FontStyle;\nusing Microsoft.Win32;\nusing System.Diagnostics;\nusing System.Reflection;\n\nnamespace NoteCountRender\n{\n    /// <summary>\n    /// Interaction logic for SettingsCtrl.xaml\n    /// </summary>\n    public partial class SettingsCtrl : UserControl\n    {\n        Settings settings;\n\n        string defText = @\"Notes: {nc} / {tn}\nBPM: {bpm}\nNPS: {nps}\nPPQ: {ppq}\nPolyphony: {plph}\nTime: {currtime}\";\n        string fullText = @\"Notes: {nc} / {tn} / {nr}\nBPM: {bpm}\nNPS: {nps} (Max: {mnps})\nPolyphony: {plph} (Max: {mplph})\nSeconds: {currsec} / {totalsec} / {remsec}\nTime: {currtime} / {totaltime} / {remtime}\nTime(ms): {cmiltime} / {tmiltime} / {rmiltime}\nTime(frame): {cfrtime} / {tfrtime} / {rfrtime}\nTicks: {currticks} / {totalticks} / {remticks}\nBars: {currbars} / {totalbars} / {rembars}\nFrames: {currframes} / {totalframes} / {remframes}\nPPQ: {ppq}\nTime Signature: {tsn}/{tsd}\nAverage NPS: {avgnps}\n\n-----Progress-----\nNotes: {notep}%\nTicks: {tickp}%\nTime: {timep}%\";\n        string MIDITrail = @\"TIME:{cmiltime}/{tmiltime}  BPM:{bpm}  BEAT:{tsn}/{tsd}  BAR:{currbars}/{totalbars}  NOTES:{nc}/{tn}\";\n\n        bool initialised = false;\n        bool Reloading = false;\n\n        string templateFolder = \"Plugins\\\\Assets\\\\NoteCounter\\\\Templates\";\n\n        Dictionary<FontStyle, string> fontStyles = new Dictionary<FontStyle, string>() {\n            { System.Drawing.FontStyle.Regular, \"Regular\" },\n            { System.Drawing.FontStyle.Bold, \"Bold\" },\n            { System.Drawing.FontStyle.Italic, \"Italic\" },\n            { System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic, \"Bold Italic\" },\n        };\n\n        public SettingsCtrl(Settings settings) : base()\n        {\n            this.settings = settings;\n            InitializeComponent();\n            foreach (var font in System.Drawing.FontFamily.Families)\n            {\n                try\n                {\n                    using (var f = new System.Drawing.Font(font.Name, 12)) { }\n                }\n                catch { continue; }\n                var dock = new DockPanel();\n                dock.Children.Add(new Label()\n                {\n                    Content = font.Name,\n                    Padding = new Thickness(2)\n                });\n                dock.Children.Add(new Label()\n                {\n                    Content = \"AaBbCc123\",\n                    FontFamily = new FontFamily(font.Name),\n                    Padding = new Thickness(2),\n                    VerticalContentAlignment = VerticalAlignment.Center\n                });\n                var item = new ComboBoxItem()\n                {\n                    Content = dock\n                };\n                item.Tag = font.Name;\n                fontSelect.Items.Add(item);\n            }\n            foreach (var i in fontSelect.Items)\n            {\n                if ((string)((ComboBoxItem)i).Tag == settings.fontName)\n                {\n                    fontSelect.SelectedItem = i;\n                    break;\n                }\n            }\n            fontSize.Value = settings.fontSize;\n            textTemplate.Text = settings.text;\n            saveCsv.IsChecked = settings.saveCsv;\n            csvFormat.Text = settings.csvFormat;\n            initialised = true;\n            UpdateFontStyles();\n            Reload();\n        }\n\n        private void AlignSelect_SelectionChanged(object sender, SelectionChangedEventArgs e)\n        {\n            if (!initialised) return;\n            settings.textAlignment = (Alignments)alignSelect.SelectedIndex;\n        }\n\n        private void FontSize_ValueChanged(object sender, RoutedPropertyChangedEventArgs<decimal> e)\n        {\n            if (!initialised) return;\n            settings.fontSize = (int)fontSize.Value;\n        }\n\n        private void FontSelect_SelectionChanged(object sender, SelectionChangedEventArgs e)\n        {\n            if (!initialised) return;\n            settings.fontName = (string)((ComboBoxItem)fontSelect.SelectedItem).Tag;\n            UpdateFontStyles();\n        }\n\n        void UpdateFontStyles()\n        {\n            fontStyle.Items.Clear();\n            using (var font = new System.Drawing.FontFamily(settings.fontName))\n            {\n                foreach (var k in fontStyles.Keys)\n                {\n                    if (font.IsStyleAvailable(k))\n                    {\n                        fontStyle.Items.Add(new ComboBoxItem() { Content = fontStyles[k] });\n                    }\n                }\n            }\n            fontStyle.SelectedIndex = 0;\n        }\n\n        private void FontStyle_SelectionChanged(object sender, SelectionChangedEventArgs e)\n        {\n            if (!initialised || fontStyle.SelectedItem == null) return;\n            settings.fontStyle = fontStyles.ToDictionary(kp => kp.Value, kp => kp.Key)[(string)((ComboBoxItem)fontStyle.SelectedItem).Content];\n        }\n\n        private void TextTemplate_TextChanged(object sender, TextChangedEventArgs e)\n        {\n            if (!initialised) return;\n            settings.text = textTemplate.Text;\n        }\n\n        List<string> templateStrings = new List<string>();\n        void Reload()\n        {\n            if (!Directory.Exists(templateFolder))\n            {\n                Directory.CreateDirectory(templateFolder);\n            }\n            try\n            {\n                File.WriteAllText(Path.Combine(templateFolder, \"default.txt\"), defText);\n            }\n            catch { }\n            try\n            {\n                File.WriteAllText(Path.Combine(templateFolder, \"full.txt\"), fullText);\n            }\n            catch { }\n            try\n            {\n                File.WriteAllText(Path.Combine(templateFolder, \"MIDITrail.txt\"), MIDITrail);\n            }\n            catch { }\n            var files = Directory.GetFiles(templateFolder).Where(f => f.EndsWith(\".txt\"));\n            templateStrings.Clear();\n            templates.Items.Clear();\n            foreach (var f in files)\n            {\n                string text = File.ReadAllText(f);\n                templateStrings.Add(text);\n                templates.Items.Add(new ComboBoxItem() { Content = Path.GetFileNameWithoutExtension(f) });\n            }\n            foreach (var i in templates.Items)\n            {\n                if (!Reloading && (string)((ComboBoxItem)i).Content == \"default\")\n                {\n                    templates.SelectedItem = i;\n                    break;\n                }\n            }\n        }\n\n        private void Templates_SelectionChanged(object sender, SelectionChangedEventArgs e)\n        {\n            if (!initialised || templates.SelectedIndex == -1) return;\n            textTemplate.Text = templateStrings[templates.SelectedIndex];\n        }\n\n        private void Reload_Click(object sender, RoutedEventArgs e)\n        {\n            Reloading = true;\n            Reload();\n        }\n\n        private void saveCsv_Checked(object sender, RoutedEventArgs e)\n        {\n            if (!initialised) return;\n            settings.saveCsv = (bool)saveCsv.IsChecked;\n        }\n\n        private void browseOutputSaveButton_Click(object sender, RoutedEventArgs e)\n        {\n            var save = new SaveFileDialog();\n            save.OverwritePrompt = true;\n            save.Filter = \"CSV date file (*.csv)|*.csv\";\n            if ((bool)save.ShowDialog())\n            {\n                csvPath.Text = save.FileName;\n                settings.csvOutput = save.FileName;\n            }\n        }\n\n        private void csvFormat_TextChanged(object sender, TextChangedEventArgs e)\n        {\n            if (!initialised) return;\n            settings.csvFormat = csvFormat.Text;\n        }\n\n        private void openFolder_Click(object sender, RoutedEventArgs e)\n        {\n            if (!templateFolder.Contains(\":\\\\\") && !templateFolder.Contains(\":/\"))\n                Process.Start(\"explorer.exe\", System.IO.Path.Combine(System.IO.Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), templateFolder));\n            else\n                Process.Start(\"explorer.exe\", templateFolder);\n        }\n\n        private void useCommas_RadioChecked(object sender, RoutedEventArgs e)\n        {\n            if (!initialised) return;\n            if (useCommas.IsChecked) settings.thousandSeparator = Commas.Comma;\n            //if (useDots.IsChecked) settings.thousandSeparator = Commas.Dot;\n            if (useNothing.IsChecked) settings.thousandSeparator = Commas.Nothing;\n        }\n\n        private void ZP(object sender, RoutedPropertyChangedEventArgs<bool> e)\n        {\n            settings.PaddingZeroes = (bool)ZeroPadding.IsChecked;\n        }\n        private void Paddings_ValueChanged(object sender, RoutedPropertyChangedEventArgs<decimal> e)\n        {\n            settings.BPMintPad = (int)BPMint.Value;\n            settings.BPMDecPtPad = (int)BPMDecPt.Value;\n            settings.NoteCountPad = (int)NoteCount.Value;\n            settings.PolyphonyPad = (int)Polyphony.Value;\n            settings.NPSPad = (int)NPS.Value;\n            settings.TicksPad = (int)Ticks.Value;\n            settings.BarCountPad = (int)Bars.Value;\n            settings.FrCountPad = (int)Frames.Value;\n        }\n\n        private void SetDefault_Click(object sender, RoutedEventArgs e)\n        {\n            settings.BPMintPad = 3;\n            settings.BPMDecPtPad = 2;\n            settings.NoteCountPad = 5;\n            settings.PolyphonyPad = 3;\n            settings.NPSPad = 3;\n            settings.TicksPad = 5;\n            settings.BarCountPad = 3;\n            settings.FrCountPad = 5;\n        }\n\n        private void NewProfile_Click(object sender, RoutedEventArgs e)\n        {\n            if (profileName.Text == \"\")\n            {\n                MessageBox.Show(\"Please write a name for the profile\");\n                return;\n            }\n            profileName.Text = profileName.Text.Replace(\".txt\", \"\");\n            if (String.Compare(profileName.Text, \"default\", true) == 0 ||\n                String.Compare(profileName.Text, \"full\", true) == 0 ||\n                String.Compare(profileName.Text, \"miditrail\", true) == 0)\n            {\n                MessageBox.Show(\"You can't override default profiles.\");\n                return;\n            }\n            if (File.Exists(templateFolder + \"\\\\\" + profileName.Text + \".txt\"))\n            {\n                if (MessageBox.Show(\"Are you sure you want to override the profile \\\"\" + profileName.Text + \"\\\"?\", \"Override Profile\", MessageBoxButton.YesNo) == MessageBoxResult.No)\n                {\n                    return;\n                }\n            }\n            try\n            {\n                File.WriteAllText(Path.Combine(templateFolder, profileName.Text + \".txt\"), textTemplate.Text);\n            }\n            catch { }\n            Reloading = true;\n            Reload();\n        }\n    }\n}\n"
  },
  {
    "path": "NoteCountRender/packages.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n  <package id=\"Newtonsoft.Json\" version=\"12.0.1\" targetFramework=\"net461\" />\n  <package id=\"OpenTK\" version=\"3.1.0\" targetFramework=\"net461\" />\n</packages>"
  },
  {
    "path": "PFARender/.gitignore",
    "content": "\nobj/*"
  },
  {
    "path": "PFARender/Languages/en/pfa.xaml",
    "content": "﻿<ResourceDictionary\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" \n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n    xmlns:local=\"using:MSDNSample\"\n    xmlns:system=\"clr-namespace:System;assembly=mscorlib\">\n\n    <!--Other settings-->\n    <system:String x:Key=\"firstNote\">First Note</system:String>\n    <system:String x:Key=\"lastNote\">Last Note</system:String>\n    <system:String x:Key=\"pianoHeight\">Piano Height %</system:String>\n    <system:String x:Key=\"noteScreenTime\">Note screen time</system:String>\n    <system:String x:Key=\"sameWidthNotes\">Same Width Notes</system:String>\n    <system:String x:Key=\"save\">Save</system:String>\n    <system:String x:Key=\"loadSaved\">Load Saved</system:String>\n    <system:String x:Key=\"setDefaults\">Defaults</system:String>\n\n    <system:String x:Key=\"middleCSquare\">Middle C square</system:String>\n    <system:String x:Key=\"blackNotesAbove\">Draw black notes above (Warning: SLOWER!!)</system:String>\n    <system:String x:Key=\"topColor\">Top Color</system:String>\n    <system:String x:Key=\"colorRed\">Red</system:String>\n    <system:String x:Key=\"colorGreen\">Green</system:String>\n    <system:String x:Key=\"colorBlue\">Blue</system:String>\n\n    <system:String x:Key=\"borderWidth\">Border Width</system:String> <!--NEW-->\n</ResourceDictionary>"
  },
  {
    "path": "PFARender/OpenTK.dll.config",
    "content": "<configuration>\n  <dllmap os=\"linux\" dll=\"opengl32.dll\" target=\"libGL.so.1\"/>\n  <dllmap os=\"linux\" dll=\"glu32.dll\" target=\"libGLU.so.1\"/>\n  <dllmap os=\"linux\" dll=\"openal32.dll\" target=\"libopenal.so.1\"/>\n  <dllmap os=\"linux\" dll=\"alut.dll\" target=\"libalut.so.0\"/>\n  <dllmap os=\"linux\" dll=\"opencl.dll\" target=\"libOpenCL.so\"/>\n  <dllmap os=\"linux\" dll=\"libX11\" target=\"libX11.so.6\"/>\n  <dllmap os=\"linux\" dll=\"libXi\" target=\"libXi.so.6\"/>\n  <dllmap os=\"linux\" dll=\"SDL2.dll\" target=\"libSDL2-2.0.so.0\"/>\n  <dllmap os=\"osx\" dll=\"opengl32.dll\" target=\"/System/Library/Frameworks/OpenGL.framework/OpenGL\"/>\n  <dllmap os=\"osx\" dll=\"openal32.dll\" target=\"/System/Library/Frameworks/OpenAL.framework/OpenAL\" />\n  <dllmap os=\"osx\" dll=\"alut.dll\" target=\"/System/Library/Frameworks/OpenAL.framework/OpenAL\" />\n  <dllmap os=\"osx\" dll=\"libGLES.dll\" target=\"/System/Library/Frameworks/OpenGLES.framework/OpenGLES\" />\n  <dllmap os=\"osx\" dll=\"libGLESv1_CM.dll\" target=\"/System/Library/Frameworks/OpenGLES.framework/OpenGLES\" />\n  <dllmap os=\"osx\" dll=\"libGLESv2.dll\" target=\"/System/Library/Frameworks/OpenGLES.framework/OpenGLES\" />\n  <dllmap os=\"osx\" dll=\"opencl.dll\" target=\"/System/Library/Frameworks/OpenCL.framework/OpenCL\"/>\n  <dllmap os=\"osx\" dll=\"SDL2.dll\" target=\"libSDL2.dylib\"/>\n  <!-- XQuartz compatibility (X11 on Mac) -->\n  <dllmap os=\"osx\" dll=\"libGL.so.1\" target=\"/usr/X11/lib/libGL.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libX11\" target=\"/usr/X11/lib/libX11.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXcursor.so.1\" target=\"/usr/X11/lib/libXcursor.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXi\" target=\"/usr/X11/lib/libXi.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXinerama\" target=\"/usr/X11/lib/libXinerama.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXrandr.so.2\" target=\"/usr/X11/lib/libXrandr.dylib\"/>\n</configuration>\n"
  },
  {
    "path": "PFARender/PFARender.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProjectGuid>{84E13869-0D33-4A56-A1A8-0B6F12C76081}</ProjectGuid>\n    <OutputType>Library</OutputType>\n    <AppDesignerFolder>Properties</AppDesignerFolder>\n    <RootNamespace>PFARender</RootNamespace>\n    <AssemblyName>PFARender</AssemblyName>\n    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>\n    <FileAlignment>512</FileAlignment>\n    <Deterministic>true</Deterministic>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>..\\bin\\Any cpu\\Debug\\Plugins\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>..\\bin\\Any cpu\\Release\\Plugins\\</OutputPath>\n    <DefineConstants>\n    </DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <PlatformTarget>x64</PlatformTarget>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Debug|x86'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>..\\bin\\x86\\Debug\\Plugins\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <DebugType>full</DebugType>\n    <PlatformTarget>x86</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Release|x86'\">\n    <OutputPath>..\\bin\\x86\\Release\\Plugins\\</OutputPath>\n    <DefineConstants>\n    </DefineConstants>\n    <Optimize>true</Optimize>\n    <DebugType>pdbonly</DebugType>\n    <PlatformTarget>x86</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Debug|x64'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>..\\bin\\x64\\Debug\\Plugins\\</OutputPath>\n    <DefineConstants>TRACE;DEBUG</DefineConstants>\n    <DebugType>full</DebugType>\n    <PlatformTarget>x64</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Release|x64'\">\n    <OutputPath>..\\bin\\x64\\Release\\Plugins\\</OutputPath>\n    <DefineConstants>\n    </DefineConstants>\n    <Optimize>true</Optimize>\n    <DebugType>pdbonly</DebugType>\n    <PlatformTarget>x64</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Newtonsoft.Json.12.0.1\\lib\\net45\\Newtonsoft.Json.dll</HintPath>\n    </Reference>\n    <Reference Include=\"OpenTK, Version=3.1.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\OpenTK.3.1.0\\lib\\net20\\OpenTK.dll</HintPath>\n    </Reference>\n    <Reference Include=\"PresentationCore\" />\n    <Reference Include=\"PresentationFramework\" />\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Core\" />\n    <Reference Include=\"System.Drawing\" />\n    <Reference Include=\"System.Xaml\" />\n    <Reference Include=\"System.Xml.Linq\" />\n    <Reference Include=\"System.Data.DataSetExtensions\" />\n    <Reference Include=\"Microsoft.CSharp\" />\n    <Reference Include=\"System.Data\" />\n    <Reference Include=\"System.Net.Http\" />\n    <Reference Include=\"System.Xml\" />\n    <Reference Include=\"WindowsBase\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"Properties\\AssemblyInfo.cs\" />\n    <Compile Include=\"Properties\\Resources.Designer.cs\">\n      <AutoGen>True</AutoGen>\n      <DesignTime>True</DesignTime>\n      <DependentUpon>Resources.resx</DependentUpon>\n    </Compile>\n    <Compile Include=\"Render.cs\" />\n    <Compile Include=\"Settings.cs\" />\n    <Compile Include=\"SettingsCtrl.xaml.cs\">\n      <DependentUpon>SettingsCtrl.xaml</DependentUpon>\n    </Compile>\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\BMEngine\\ZenithEngine.csproj\">\n      <Project>{60f2212a-d56c-4776-836f-6a49453cdbc4}</Project>\n      <Name>ZenithEngine</Name>\n    </ProjectReference>\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"OpenTK.dll.config\" />\n    <None Include=\"packages.config\" />\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Languages\\en\\pfa.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </EmbeddedResource>\n    <Page Include=\"SettingsCtrl.xaml\">\n      <SubType>Designer</SubType>\n      <Generator>MSBuild:Compile</Generator>\n    </Page>\n  </ItemGroup>\n  <ItemGroup>\n    <Content Include=\"preview.png\" />\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Properties\\Resources.resx\">\n      <Generator>ResXFileCodeGenerator</Generator>\n      <LastGenOutput>Resources.Designer.cs</LastGenOutput>\n    </EmbeddedResource>\n  </ItemGroup>\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n  <PropertyGroup>\n    <PostBuildEvent>\n    </PostBuildEvent>\n  </PropertyGroup>\n</Project>"
  },
  {
    "path": "PFARender/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// General Information about an assembly is controlled through the following\n// set of attributes. Change these attribute values to modify the information\n// associated with an assembly.\n[assembly: AssemblyTitle(\"PFARender\")]\n[assembly: AssemblyDescription(\"\")]\n[assembly: AssemblyConfiguration(\"\")]\n[assembly: AssemblyCompany(\"\")]\n[assembly: AssemblyProduct(\"PFARender\")]\n[assembly: AssemblyCopyright(\"Copyright ©  2019\")]\n[assembly: AssemblyTrademark(\"\")]\n[assembly: AssemblyCulture(\"\")]\n\n// Setting ComVisible to false makes the types in this assembly not visible\n// to COM components.  If you need to access a type in this assembly from\n// COM, set the ComVisible attribute to true on that type.\n[assembly: ComVisible(false)]\n\n// The following GUID is for the ID of the typelib if this project is exposed to COM\n[assembly: Guid(\"84e13869-0d33-4a56-a1a8-0b6f12c76081\")]\n\n// Version information for an assembly consists of the following four values:\n//\n//      Major Version\n//      Minor Version\n//      Build Number\n//      Revision\n//\n// You can specify all the values or you can default the Build and Revision Numbers\n// by using the '*' as shown below:\n// [assembly: AssemblyVersion(\"1.0.*\")]\n[assembly: AssemblyVersion(\"1.0.0.0\")]\n[assembly: AssemblyFileVersion(\"1.0.0.0\")]\n"
  },
  {
    "path": "PFARender/Properties/Resources.Designer.cs",
    "content": "﻿//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace PFARender.Properties {\n    using System;\n    \n    \n    /// <summary>\n    ///   A strongly-typed resource class, for looking up localized strings, etc.\n    /// </summary>\n    // This class was auto-generated by the StronglyTypedResourceBuilder\n    // class via a tool like ResGen or Visual Studio.\n    // To add or remove a member, edit your .ResX file then rerun ResGen\n    // with the /str option, or rebuild your VS project.\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"System.Resources.Tools.StronglyTypedResourceBuilder\", \"15.0.0.0\")]\n    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]\n    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]\n    internal class Resources {\n        \n        private static global::System.Resources.ResourceManager resourceMan;\n        \n        private static global::System.Globalization.CultureInfo resourceCulture;\n        \n        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute(\"Microsoft.Performance\", \"CA1811:AvoidUncalledPrivateCode\")]\n        internal Resources() {\n        }\n        \n        /// <summary>\n        ///   Returns the cached ResourceManager instance used by this class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Resources.ResourceManager ResourceManager {\n            get {\n                if (object.ReferenceEquals(resourceMan, null)) {\n                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager(\"PFARender.Properties.Resources\", typeof(Resources).Assembly);\n                    resourceMan = temp;\n                }\n                return resourceMan;\n            }\n        }\n        \n        /// <summary>\n        ///   Overrides the current thread's CurrentUICulture property for all\n        ///   resource lookups using this strongly typed resource class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Globalization.CultureInfo Culture {\n            get {\n                return resourceCulture;\n            }\n            set {\n                resourceCulture = value;\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized resource of type System.Drawing.Bitmap.\n        /// </summary>\n        internal static System.Drawing.Bitmap preview {\n            get {\n                object obj = ResourceManager.GetObject(\"preview\", resourceCulture);\n                return ((System.Drawing.Bitmap)(obj));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "PFARender/Properties/Resources.resx",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The primary goals of this format is to allow a simple XML format \n    that is mostly human readable. The generation and parsing of the \n    various data types are done through the TypeConverter classes \n    associated with the data types.\n    \n    Example:\n    \n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n                \n    There are any number of \"resheader\" rows that contain simple \n    name/value pairs.\n    \n    Each data row contains a name, and value. The row also contains a \n    type or mimetype. Type corresponds to a .NET class that support \n    text/value conversion through the TypeConverter architecture. \n    Classes that don't support this are serialized and stored with the \n    mimetype set.\n    \n    The mimetype is used for serialized objects, and tells the \n    ResXResourceReader how to depersist the object. This is currently not \n    extensible. For a given mimetype the value must be set accordingly:\n    \n    Note - application/x-microsoft.net.object.binary.base64 is the format \n    that the ResXResourceWriter will generate, however the reader can \n    read any of the formats listed below.\n    \n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n    \n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array \n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <assembly alias=\"System.Windows.Forms\" name=\"System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" />\n  <data name=\"preview\" type=\"System.Resources.ResXFileRef, System.Windows.Forms\">\n    <value>..\\preview.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>\n  </data>\n</root>"
  },
  {
    "path": "PFARender/README.md",
    "content": "# Zenith-PFA-Render"
  },
  {
    "path": "PFARender/Render.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Drawing;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing ZenithEngine;\nusing OpenTK;\nusing OpenTK.Graphics;\nusing OpenTK.Graphics.OpenGL;\n\nnamespace PFARender\n{\n    public class Render : IPluginRender\n    {\n        #region PreviewConvert\n        BitmapImage BitmapToImageSource(Bitmap bitmap)\n        {\n            using (MemoryStream memory = new MemoryStream())\n            {\n                bitmap.Save(memory, System.Drawing.Imaging.ImageFormat.Bmp);\n                memory.Position = 0;\n                BitmapImage bitmapimage = new BitmapImage();\n                bitmapimage.BeginInit();\n                bitmapimage.StreamSource = memory;\n                bitmapimage.CacheOption = BitmapCacheOption.OnLoad;\n                bitmapimage.EndInit();\n\n                return bitmapimage;\n            }\n        }\n        #endregion\n\n#if !NORMAL_SHADE\n        public string Name => \"PFA+\";\n#else\n        public string Name => \"PFA+ Original\";\n#endif\n        public string Description => \"A replica of PFA with some special extra features\";\n        public ImageSource PreviewImage { get; private set; }\n        public string LanguageDictName { get; } = \"pfa\";\n\n        #region Shaders\n        string noteShaderVert = @\"#version 330 compatibility\n\nlayout(location = 0) in vec3 position;\nlayout(location = 1) in vec4 glColor;\nlayout(location = 2) in vec2 attrib;\n\nout vec4 color;\n\nvoid main()\n{\n    gl_Position = vec4(position.x * 2 - 1, position.y * 2 - 1, 1.0f, 1.0f);\n    color = vec4(glColor.xyz * attrib.y + attrib.x, glColor.w);\n}\n\";\n        string noteShaderFrag = @\"#version 330 compatibility\n \nin vec4 color;\n \nout vec4 outputF;\nlayout(location = 0) out vec4 texOut;\n\nvoid main()\n{\n    outputF = color;\n\ttexOut = outputF;\n}\n\";\n        #endregion\n\n        public double NoteCollectorOffset => 0;\n        public bool ManualNoteDelete => false;\n\n        RenderSettings renderSettings;\n        Settings settings;\n\n        SettingsCtrl settingsControl;\n\n        int noteShader;\n\n        int vertexBufferID;\n        int colorBufferID;\n        int attribBufferID;\n\n        int quadBufferLength = 2048 * 64;\n        double[] quadVertexbuff;\n        float[] quadColorbuff;\n        double[] quadAttribbuff;\n        int quadBufferPos = 0;\n\n        int indexBufferId;\n        uint[] indexes = new uint[2048 * 128 * 6];\n\n        bool[] blackKeys = new bool[257];\n        int[] keynum = new int[257];\n\n        public bool Initialized { get; private set; } = false;\n\n        public long LastNoteCount { get; private set; }\n\n        public MidiInfo CurrentMidi { get; set; }\n\n        public NoteColor[][] NoteColors { get; set; }\n\n        public double NoteScreenTime => settings.deltaTimeOnScreen;\n\n        public System.Windows.Controls.Control SettingsControl => settingsControl;\n\n        public double Tempo { get; set; }\n\n        public void Dispose()\n        {\n            GL.DeleteBuffers(3, new int[] { vertexBufferID, colorBufferID, attribBufferID });\n            GL.DeleteProgram(noteShader);\n            quadVertexbuff = null;\n            quadColorbuff = null;\n            quadAttribbuff = null;\n            Initialized = false;\n            Console.WriteLine(\"Disposed of PFARender\");\n        }\n\n        public Render(RenderSettings settings)\n        {\n            this.settings = new Settings();\n            this.renderSettings = settings;\n            settingsControl = new SettingsCtrl(this.settings);\n            ((SettingsCtrl)SettingsControl).PaletteChanged += () => { ReloadTrackColors(); };\n            PreviewImage = BitmapToImageSource(Properties.Resources.preview);\n            for (int i = 0; i < blackKeys.Length; i++) blackKeys[i] = isBlackNote(i);\n            int b = 0;\n            int w = 0;\n            for (int i = 0; i < keynum.Length; i++)\n            {\n                if (blackKeys[i]) keynum[i] = b++;\n                else keynum[i] = w++;\n            }\n        }\n\n        public void Init()\n        {\n            int _vertexObj = GL.CreateShader(ShaderType.VertexShader);\n            int _fragObj = GL.CreateShader(ShaderType.FragmentShader);\n            int statusCode;\n            string info;\n\n            GL.ShaderSource(_vertexObj, noteShaderVert);\n            GL.CompileShader(_vertexObj);\n            info = GL.GetShaderInfoLog(_vertexObj);\n            GL.GetShader(_vertexObj, ShaderParameter.CompileStatus, out statusCode);\n            if (statusCode != 1) throw new ApplicationException(info);\n\n            GL.ShaderSource(_fragObj, noteShaderFrag);\n            GL.CompileShader(_fragObj);\n            info = GL.GetShaderInfoLog(_fragObj);\n            GL.GetShader(_fragObj, ShaderParameter.CompileStatus, out statusCode);\n            if (statusCode != 1) throw new ApplicationException(info);\n\n            noteShader = GL.CreateProgram();\n            GL.AttachShader(noteShader, _fragObj);\n            GL.AttachShader(noteShader, _vertexObj);\n            GL.LinkProgram(noteShader);\n\n            quadVertexbuff = new double[quadBufferLength * 8];\n            quadColorbuff = new float[quadBufferLength * 16];\n            quadAttribbuff = new double[quadBufferLength * 8];\n\n            GL.GenBuffers(1, out vertexBufferID);\n            GL.GenBuffers(1, out colorBufferID);\n            GL.GenBuffers(1, out attribBufferID);\n            for (uint i = 0; i < indexes.Length / 6; i++)\n            {\n                indexes[i * 6 + 0] = i * 4 + 0;\n                indexes[i * 6 + 1] = i * 4 + 1;\n                indexes[i * 6 + 2] = i * 4 + 3;\n                indexes[i * 6 + 3] = i * 4 + 1;\n                indexes[i * 6 + 4] = i * 4 + 3;\n                indexes[i * 6 + 5] = i * 4 + 2;\n            }\n            for (int i = 0; i < quadAttribbuff.Length;)\n            {\n                quadAttribbuff[i++] = -0.1;\n                quadAttribbuff[i++] = 0;\n                quadAttribbuff[i++] = 0.3;\n                quadAttribbuff[i++] = 0;\n                quadAttribbuff[i++] = -0.3;\n                quadAttribbuff[i++] = 0;\n                quadAttribbuff[i++] = 0.3;\n                quadAttribbuff[i++] = 0;\n            }\n            GL.BindBuffer(BufferTarget.ArrayBuffer, attribBufferID);\n            GL.BufferData(\n                BufferTarget.ArrayBuffer,\n                (IntPtr)(quadAttribbuff.Length * 8),\n                quadAttribbuff,\n                BufferUsageHint.StaticDraw);\n\n            GL.GenBuffers(1, out indexBufferId);\n            GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexBufferId);\n            GL.BufferData(\n                BufferTarget.ElementArrayBuffer,\n                (IntPtr)(indexes.Length * 4),\n                indexes,\n                BufferUsageHint.StaticDraw);\n            Initialized = true;\n            Console.WriteLine(\"Initialised PFARender\");\n        }\n\n        public void RenderFrame(FastList<Note> notes, double midiTime, int finalCompositeBuff)\n        {\n            GL.Enable(EnableCap.Blend);\n            GL.EnableClientState(ArrayCap.VertexArray);\n            GL.EnableClientState(ArrayCap.ColorArray);\n            GL.Enable(EnableCap.Texture2D);\n\n            GL.EnableVertexAttribArray(0);\n            GL.EnableVertexAttribArray(1);\n            GL.EnableVertexAttribArray(2);\n\n            GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);\n\n            GL.BindFramebuffer(FramebufferTarget.Framebuffer, finalCompositeBuff);\n            GL.Viewport(0, 0, renderSettings.width, renderSettings.height);\n            GL.Clear(ClearBufferMask.ColorBufferBit);\n\n            GL.UseProgram(noteShader);\n\n            #region Vars\n            long nc = 0;\n            int firstNote = settings.firstNote;\n            int lastNote = settings.lastNote;\n            int kbfirstNote = settings.firstNote;\n            int kblastNote = settings.lastNote;\n            if (blackKeys[firstNote]) kbfirstNote--;\n            if (blackKeys[lastNote - 1]) kblastNote++;\n\n            double deltaTimeOnScreen = NoteScreenTime;\n            double pianoHeight = settings.pianoHeight;\n            bool sameWidth = settings.sameWidthNotes;\n            bool blackNotesAbove = settings.blackNotesAbove;\n            double scwidth = renderSettings.width;\n            Color4[] keyColors = new Color4[514];\n            bool[] keyPressed = new bool[257];\n            for (int i = 0; i < 514; i++) keyColors[i] = Color4.Transparent;\n            for (int i = 0; i < 257; i++) keyPressed[i] = false;\n            quadBufferPos = 0;\n\n            int pos;\n\n            double x1, x2, y1, y2;\n            double wdth;\n            float r, g, b, a, r2, g2, b2, a2, r3, g3, b3, a3;\n            double paddingx = 0.001 * settings.borderWidth;\n            double paddingy = paddingx * renderSettings.width / renderSettings.height;\n\n            double[] x1array = new double[257];\n            double[] wdtharray = new double[257];\n            if (settings.sameWidthNotes)\n            {\n                for (int i = 0; i < 257; i++)\n                {\n                    x1array[i] = (float)(i - firstNote) / (lastNote - firstNote);\n                    wdtharray[i] = 1.0f / (lastNote - firstNote);\n                }\n            }\n            else\n            {\n                double knmfn = keynum[firstNote];\n                double knmln = keynum[lastNote - 1];\n                if (blackKeys[firstNote]) knmfn = keynum[firstNote - 1] + 0.5;\n                if (blackKeys[lastNote - 1]) knmln = keynum[lastNote] - 0.5;\n                for (int i = 0; i < 257; i++)\n                {\n                    if (!blackKeys[i])\n                    {\n                        x1array[i] = (float)(keynum[i] - knmfn) / (knmln - knmfn + 1);\n                        wdtharray[i] = 1.0f / (knmln - knmfn + 1);\n                    }\n                    else\n                    {\n                        int _i = i + 1;\n                        wdth = 0.64f / (knmln - knmfn + 1);\n                        int bknum = keynum[i] % 5;\n                        double offset = wdth / 2;\n                        if (bknum == 0) offset += offset * 0.4;\n                        if (bknum == 2) offset += offset * 0.4;\n                        if (bknum == 1) offset -= offset * 0.4;\n                        if (bknum == 4) offset -= offset * 0.4;\n\n                        //if (bknum == 0 || bknum == 2)\n                        //{\n                        //    offset *= 1.3;\n                        //}\n                        //else if (bknum == 1 || bknum == 4)\n                        //{\n                        //    offset *= 0.7;\n                        //}\n                        x1array[i] = (float)(keynum[_i] - knmfn) / (knmln - knmfn + 1) - offset;\n                        wdtharray[i] = wdth;\n                    }\n                }\n            }\n            double sepwdth = Math.Round(wdtharray[0] * scwidth / 20);\n            if (sepwdth == 0) sepwdth = 1;\n            #endregion\n\n            #region Notes\n            quadBufferPos = 0;\n            double notePosFactor = 1 / deltaTimeOnScreen * (1 - pianoHeight);\n            foreach (Note n in notes)\n            {\n                double renderCutoff = midiTime + deltaTimeOnScreen;\n                if (n.end >= midiTime || !n.hasEnded)\n                {\n                    if (n.start < renderCutoff)\n                    {\n                        unsafe\n                        {\n                            int k = n.key;\n                            if (!(k >= firstNote && k < lastNote) || (blackKeys[k] && blackNotesAbove)) continue;\n                            nc++;\n                            Color4 coll = n.color.left;\n                            Color4 colr = n.color.right;\n                            if (n.start < midiTime)\n                            {\n                                Color4 origcoll = keyColors[k * 2];\n                                Color4 origcolr = keyColors[k * 2 + 1];\n                                float blendfac = coll.A;\n                                float revblendfac = 1 - blendfac;\n                                keyColors[k * 2] = new Color4(\n                                    coll.R * blendfac + origcoll.R * revblendfac,\n                                    coll.G * blendfac + origcoll.G * revblendfac,\n                                    coll.B * blendfac + origcoll.B * revblendfac,\n                                    1);\n                                blendfac = colr.A;\n                                revblendfac = 1 - blendfac;\n                                keyColors[k * 2 + 1] = new Color4(\n                                    colr.R * blendfac + origcolr.R * revblendfac,\n                                    colr.G * blendfac + origcolr.G * revblendfac,\n                                    colr.B * blendfac + origcolr.B * revblendfac,\n                                    1);\n                                keyPressed[k] = true;\n                            }\n                            x1 = x1array[k];\n                            wdth = wdtharray[k];\n                            x2 = x1 + wdth;\n                            y1 = 1 - (renderCutoff - n.end) * notePosFactor;\n                            y2 = 1 - (renderCutoff - n.start) * notePosFactor;\n                            if (!n.hasEnded)\n                                y1 = 1;\n\n                            r = coll.R;\n                            g = coll.G;\n                            b = coll.B;\n                            a = coll.A;\n                            r2 = colr.R;\n                            g2 = colr.G;\n                            b2 = colr.B;\n                            a2 = colr.A;\n\n                            //Outside\n                            pos = quadBufferPos * 8;\n                            quadVertexbuff[pos++] = x2;\n                            quadVertexbuff[pos++] = y2;\n                            quadVertexbuff[pos++] = x2;\n                            quadVertexbuff[pos++] = y1;\n                            quadVertexbuff[pos++] = x1;\n                            quadVertexbuff[pos++] = y1;\n                            quadVertexbuff[pos++] = x1;\n                            quadVertexbuff[pos++] = y2;\n\n                            pos = quadBufferPos * 16;\n                            quadColorbuff[pos++] = r;\n                            quadColorbuff[pos++] = g;\n                            quadColorbuff[pos++] = b;\n                            quadColorbuff[pos++] = a;\n                            quadColorbuff[pos++] = r;\n                            quadColorbuff[pos++] = g;\n                            quadColorbuff[pos++] = b;\n                            quadColorbuff[pos++] = a;\n                            quadColorbuff[pos++] = r2;\n                            quadColorbuff[pos++] = g2;\n                            quadColorbuff[pos++] = b2;\n                            quadColorbuff[pos++] = a2;\n                            quadColorbuff[pos++] = r2;\n                            quadColorbuff[pos++] = g2;\n                            quadColorbuff[pos++] = b2;\n                            quadColorbuff[pos++] = a2;\n\n                            pos = quadBufferPos * 8;\n                            quadAttribbuff[pos++] = 0;\n                            quadAttribbuff[pos++] = 0.2;\n                            quadAttribbuff[pos++] = 0;\n                            quadAttribbuff[pos++] = 0.2;\n                            quadAttribbuff[pos++] = 0;\n                            quadAttribbuff[pos++] = 0.2;\n                            quadAttribbuff[pos++] = 0;\n                            quadAttribbuff[pos++] = 0.2;\n\n                            quadBufferPos++;\n                            FlushQuadBuffer();\n                        }\n                        //Inside\n                        unsafe\n                        {\n                            if (y1 - y2 > paddingy * 2)\n                            {\n                                x1 += paddingx;\n                                x2 -= paddingx;\n                                y1 -= paddingy;\n                                y2 += paddingy;\n                                pos = quadBufferPos * 8;\n                                quadVertexbuff[pos++] = x2;\n                                quadVertexbuff[pos++] = y2;\n                                quadVertexbuff[pos++] = x2;\n                                quadVertexbuff[pos++] = y1;\n                                quadVertexbuff[pos++] = x1;\n                                quadVertexbuff[pos++] = y1;\n                                quadVertexbuff[pos++] = x1;\n                                quadVertexbuff[pos++] = y2;\n\n                                pos = quadBufferPos * 16;\n                                quadColorbuff[pos++] = r;\n                                quadColorbuff[pos++] = g;\n                                quadColorbuff[pos++] = b;\n                                quadColorbuff[pos++] = a;\n                                quadColorbuff[pos++] = r;\n                                quadColorbuff[pos++] = g;\n                                quadColorbuff[pos++] = b;\n                                quadColorbuff[pos++] = a;\n                                quadColorbuff[pos++] = r2;\n                                quadColorbuff[pos++] = g2;\n                                quadColorbuff[pos++] = b2;\n                                quadColorbuff[pos++] = a2;\n                                quadColorbuff[pos++] = r2;\n                                quadColorbuff[pos++] = g2;\n                                quadColorbuff[pos++] = b2;\n                                quadColorbuff[pos++] = a2;\n\n                                pos = quadBufferPos * 8;\n                                quadAttribbuff[pos++] = 0;\n                                quadAttribbuff[pos++] = 0.5;\n                                quadAttribbuff[pos++] = 0;\n                                quadAttribbuff[pos++] = 0.5;\n                                quadAttribbuff[pos++] = 0;\n                                quadAttribbuff[pos++] = 1;\n                                quadAttribbuff[pos++] = 0;\n                                quadAttribbuff[pos++] = 1;\n\n                                quadBufferPos++;\n                            }\n                            FlushQuadBuffer();\n                        }\n                    }\n                    else break;\n                }\n            }\n            if (blackNotesAbove)\n            {\n                FlushQuadBuffer(false);\n                foreach (Note n in notes)\n                {\n                    double renderCutoff = midiTime + deltaTimeOnScreen;\n                    if (n.end >= midiTime || !n.hasEnded)\n                        if (n.start < renderCutoff)\n                        {\n                            unsafe\n                            {\n                                int k = n.key;\n                                if (!(k >= firstNote && k < lastNote) || !blackKeys[k]) continue;\n                                nc++;\n                                Color4 coll = n.color.left;\n                                Color4 colr = n.color.right;\n                                if (n.start < midiTime)\n                                {\n                                    keyColors[k * 2] = coll;\n                                    keyColors[k * 2 + 1] = colr;\n                                    keyPressed[k] = true;\n                                }\n                                x1 = x1array[k];\n                                wdth = wdtharray[k];\n                                x2 = x1 + wdth;\n                                y1 = 1 - (renderCutoff - n.end) * notePosFactor;\n                                y2 = 1 - (renderCutoff - n.start) * notePosFactor;\n                                if (!n.hasEnded)\n                                    y1 = 1;\n\n                                r = coll.R;\n                                g = coll.G;\n                                b = coll.B;\n                                a = coll.A;\n                                r2 = colr.R;\n                                g2 = colr.G;\n                                b2 = colr.B;\n                                a2 = colr.A;\n\n                                //Outside\n                                pos = quadBufferPos * 8;\n                                quadVertexbuff[pos++] = x2;\n                                quadVertexbuff[pos++] = y2;\n                                quadVertexbuff[pos++] = x2;\n                                quadVertexbuff[pos++] = y1;\n                                quadVertexbuff[pos++] = x1;\n                                quadVertexbuff[pos++] = y1;\n                                quadVertexbuff[pos++] = x1;\n                                quadVertexbuff[pos++] = y2;\n\n                                pos = quadBufferPos * 16;\n                                quadColorbuff[pos++] = r;\n                                quadColorbuff[pos++] = g;\n                                quadColorbuff[pos++] = b;\n                                quadColorbuff[pos++] = a;\n                                quadColorbuff[pos++] = r;\n                                quadColorbuff[pos++] = g;\n                                quadColorbuff[pos++] = b;\n                                quadColorbuff[pos++] = a;\n                                quadColorbuff[pos++] = r2;\n                                quadColorbuff[pos++] = g2;\n                                quadColorbuff[pos++] = b2;\n                                quadColorbuff[pos++] = a2;\n                                quadColorbuff[pos++] = r2;\n                                quadColorbuff[pos++] = g2;\n                                quadColorbuff[pos++] = b2;\n                                quadColorbuff[pos++] = a2;\n\n                                pos = quadBufferPos * 8;\n                                quadAttribbuff[pos++] = 0;\n                                quadAttribbuff[pos++] = 0.2;\n                                quadAttribbuff[pos++] = 0;\n                                quadAttribbuff[pos++] = 0.2;\n                                quadAttribbuff[pos++] = 0;\n                                quadAttribbuff[pos++] = 0.2;\n                                quadAttribbuff[pos++] = 0;\n                                quadAttribbuff[pos++] = 0.2;\n\n                                quadBufferPos++;\n                            }\n                            FlushQuadBuffer();\n\n                            //Inside\n                            unsafe\n                            {\n                                if (y1 - y2 > paddingy * 2)\n                                {\n                                    x1 += paddingx;\n                                    x2 -= paddingx;\n                                    y1 -= paddingy;\n                                    y2 += paddingy;\n                                    pos = quadBufferPos * 8;\n                                    quadVertexbuff[pos++] = x2;\n                                    quadVertexbuff[pos++] = y2;\n                                    quadVertexbuff[pos++] = x2;\n                                    quadVertexbuff[pos++] = y1;\n                                    quadVertexbuff[pos++] = x1;\n                                    quadVertexbuff[pos++] = y1;\n                                    quadVertexbuff[pos++] = x1;\n                                    quadVertexbuff[pos++] = y2;\n\n                                    pos = quadBufferPos * 16;\n                                    quadColorbuff[pos++] = r;\n                                    quadColorbuff[pos++] = g;\n                                    quadColorbuff[pos++] = b;\n                                    quadColorbuff[pos++] = a;\n                                    quadColorbuff[pos++] = r;\n                                    quadColorbuff[pos++] = g;\n                                    quadColorbuff[pos++] = b;\n                                    quadColorbuff[pos++] = a;\n                                    quadColorbuff[pos++] = r2;\n                                    quadColorbuff[pos++] = g2;\n                                    quadColorbuff[pos++] = b2;\n                                    quadColorbuff[pos++] = a2;\n                                    quadColorbuff[pos++] = r2;\n                                    quadColorbuff[pos++] = g2;\n                                    quadColorbuff[pos++] = b2;\n                                    quadColorbuff[pos++] = a2;\n\n                                    pos = quadBufferPos * 8;\n                                    quadAttribbuff[pos++] = 0;\n                                    quadAttribbuff[pos++] = 0.5;\n                                    quadAttribbuff[pos++] = 0;\n                                    quadAttribbuff[pos++] = 0.5;\n                                    quadAttribbuff[pos++] = 0;\n                                    quadAttribbuff[pos++] = 1;\n                                    quadAttribbuff[pos++] = 0;\n                                    quadAttribbuff[pos++] = 1;\n\n                                    quadBufferPos++;\n                                }\n                                FlushQuadBuffer();\n                            }\n                        }\n                        else break;\n                }\n            }\n            FlushQuadBuffer(false);\n            LastNoteCount = nc;\n            #endregion\n\n            #region Keyboard\n            quadBufferPos = 0;\n\n            double topRedStart = pianoHeight * .99;\n            double topRedEnd = pianoHeight * .94;\n            double topBarEnd = pianoHeight * .927;\n\n            double wEndUpT = pianoHeight * 0.03 + pianoHeight * 0.020;\n            double wEndUpB = pianoHeight * 0.03 + pianoHeight * 0.005;\n            double wEndDownT = pianoHeight * 0.01;\n            double bKeyEnd = pianoHeight * .345;\n            double bKeyDownT = topBarEnd + pianoHeight * 0.015;\n            double bKeyDownB = bKeyEnd + pianoHeight * 0.015;\n            double bKeyUpT = topBarEnd + pianoHeight * 0.045;\n            double bKeyUpB = bKeyEnd + pianoHeight * 0.045;\n\n            double bKeyUSplitLT = pianoHeight * 0.78;\n            double bKeyUSplitRT = pianoHeight * 0.71;\n            double bKeyUSplitLB = pianoHeight * 0.65;\n            double bKeyUSplitRB = pianoHeight * 0.58;\n            double keySpacing = 0;\n\n            double ox1, ox2, oy1, oy2, ix1, ix2, iy1, iy2;\n\n            #region Decorations\n            //Grey thing\n            pos = quadBufferPos * 8;\n            quadVertexbuff[pos++] = 0;\n            quadVertexbuff[pos++] = pianoHeight;\n            quadVertexbuff[pos++] = 1;\n            quadVertexbuff[pos++] = pianoHeight;\n            quadVertexbuff[pos++] = 1;\n            quadVertexbuff[pos++] = topRedStart;\n            quadVertexbuff[pos++] = 0;\n            quadVertexbuff[pos++] = topRedStart;\n\n            pos = quadBufferPos * 8;\n            quadAttribbuff[pos++] = 0;\n            quadAttribbuff[pos++] = 1;\n            quadAttribbuff[pos++] = 0;\n            quadAttribbuff[pos++] = 1;\n            quadAttribbuff[pos++] = 0;\n            quadAttribbuff[pos++] = 1;\n            quadAttribbuff[pos++] = 0;\n            quadAttribbuff[pos++] = 1;\n\n            r = .086f;\n            g = .086f;\n            b = .086f;\n            a = 1f;\n            r2 = .0196f;\n            g2 = .0196f;\n            b2 = .0196f;\n            a2 = 1f;\n\n            pos = quadBufferPos * 16;\n            quadColorbuff[pos++] = r;\n            quadColorbuff[pos++] = g;\n            quadColorbuff[pos++] = b;\n            quadColorbuff[pos++] = a;\n            quadColorbuff[pos++] = r;\n            quadColorbuff[pos++] = g;\n            quadColorbuff[pos++] = b;\n            quadColorbuff[pos++] = a;\n            quadColorbuff[pos++] = r2;\n            quadColorbuff[pos++] = g2;\n            quadColorbuff[pos++] = b2;\n            quadColorbuff[pos++] = a2;\n            quadColorbuff[pos++] = r2;\n            quadColorbuff[pos++] = g2;\n            quadColorbuff[pos++] = b2;\n            quadColorbuff[pos++] = a2;\n            quadBufferPos++;\n            FlushQuadBuffer();\n\n            //Red thing\n            pos = quadBufferPos * 8;\n            quadVertexbuff[pos++] = 0;\n            quadVertexbuff[pos++] = topRedStart;\n            quadVertexbuff[pos++] = 1;\n            quadVertexbuff[pos++] = topRedStart;\n            quadVertexbuff[pos++] = 1;\n            quadVertexbuff[pos++] = topRedEnd;\n            quadVertexbuff[pos++] = 0;\n            quadVertexbuff[pos++] = topRedEnd;\n\n            pos = quadBufferPos * 8;\n            quadAttribbuff[pos++] = 0;\n            quadAttribbuff[pos++] = 1;\n            quadAttribbuff[pos++] = 0;\n            quadAttribbuff[pos++] = 1;\n            quadAttribbuff[pos++] = 0;\n            quadAttribbuff[pos++] = 1;\n            quadAttribbuff[pos++] = 0;\n            quadAttribbuff[pos++] = 1;\n\n            if (settings.topColor == TopColor.Red)\n            {\n                r2 = settings.topBarR;\n                g2 = settings.topBarG;\n                b2 = settings.topBarB;\n                a2 = 1f;\n                r = r2 * 0.5f;\n                g = g2 * 0.5f;\n                b = b2 * 0.5f;\n                a = 1f;\n            }\n            else if (settings.topColor == TopColor.Blue)\n            {\n                b = .313f;\n                r = .0196f;\n                g = .0274f;\n                a = 1f;\n                b2 = .585f;\n                r2 = .0392f;\n                g2 = .0249f;\n                a2 = 1f;\n            }\n            else if (settings.topColor == TopColor.Green)\n            {\n                g = .313f;\n                b = .0196f;\n                r = .0274f;\n                a = 1f;\n                g2 = .585f;\n                b2 = .0392f;\n                r2 = .0249f;\n                a2 = 1f;\n            }\n\n            pos = quadBufferPos * 16;\n            quadColorbuff[pos++] = r;\n            quadColorbuff[pos++] = g;\n            quadColorbuff[pos++] = b;\n            quadColorbuff[pos++] = a;\n            quadColorbuff[pos++] = r;\n            quadColorbuff[pos++] = g;\n            quadColorbuff[pos++] = b;\n            quadColorbuff[pos++] = a;\n            quadColorbuff[pos++] = r2;\n            quadColorbuff[pos++] = g2;\n            quadColorbuff[pos++] = b2;\n            quadColorbuff[pos++] = a2;\n            quadColorbuff[pos++] = r2;\n            quadColorbuff[pos++] = g2;\n            quadColorbuff[pos++] = b2;\n            quadColorbuff[pos++] = a2;\n            quadBufferPos++;\n            FlushQuadBuffer();\n\n            //Small grey thing\n            pos = quadBufferPos * 8;\n            quadVertexbuff[pos++] = 0;\n            quadVertexbuff[pos++] = topRedEnd;\n            quadVertexbuff[pos++] = 1;\n            quadVertexbuff[pos++] = topRedEnd;\n            quadVertexbuff[pos++] = 1;\n            quadVertexbuff[pos++] = topBarEnd;\n            quadVertexbuff[pos++] = 0;\n            quadVertexbuff[pos++] = topBarEnd;\n\n            pos = quadBufferPos * 8;\n            quadAttribbuff[pos++] = 0;\n            quadAttribbuff[pos++] = 1;\n            quadAttribbuff[pos++] = 0;\n            quadAttribbuff[pos++] = 1;\n            quadAttribbuff[pos++] = 0;\n            quadAttribbuff[pos++] = 1;\n            quadAttribbuff[pos++] = 0;\n            quadAttribbuff[pos++] = 1;\n\n            r = .239f;\n            g = .239f;\n            b = .239f;\n            a = 1f;\n            r2 = .239f;\n            g2 = .239f;\n            b2 = .239f;\n            a2 = 1f;\n\n            pos = quadBufferPos * 16;\n            quadColorbuff[pos++] = r;\n            quadColorbuff[pos++] = g;\n            quadColorbuff[pos++] = b;\n            quadColorbuff[pos++] = a;\n            quadColorbuff[pos++] = r;\n            quadColorbuff[pos++] = g;\n            quadColorbuff[pos++] = b;\n            quadColorbuff[pos++] = a;\n            quadColorbuff[pos++] = r2;\n            quadColorbuff[pos++] = g2;\n            quadColorbuff[pos++] = b2;\n            quadColorbuff[pos++] = a2;\n            quadColorbuff[pos++] = r2;\n            quadColorbuff[pos++] = g2;\n            quadColorbuff[pos++] = b2;\n            quadColorbuff[pos++] = a2;\n            quadBufferPos++;\n            FlushQuadBuffer();\n            #endregion\n\n            y2 = 0;\n            y1 = topBarEnd;\n            Color4[] origColors = new Color4[257];\n            for (int k = kbfirstNote; k < kblastNote; k++)\n            {\n                if (isBlackNote(k))\n                    origColors[k] = Color4.Black;\n                else\n                    origColors[k] = Color4.White;\n            }\n\n            #region White\n            for (int n = kbfirstNote; n < kblastNote; n++)\n            {\n                x1 = x1array[n];\n                wdth = wdtharray[n];\n                x2 = x1 + wdth;\n\n                if (!blackKeys[n])\n                {\n                    y2 = 0;\n                    if (settings.sameWidthNotes)\n                    {\n                        int _n = n % 12;\n                        if (_n == 0)\n                            x2 += wdth * 0.666;\n                        else if (_n == 2)\n                        {\n                            x1 -= wdth / 3;\n                            x2 += wdth / 3;\n                        }\n                        else if (_n == 4)\n                            x1 -= wdth / 3 * 2;\n                        else if (_n == 5)\n                            x2 += wdth * 0.75;\n                        else if (_n == 7)\n                        {\n                            x1 -= wdth / 4;\n                            x2 += wdth / 2;\n                        }\n                        else if (_n == 9)\n                        {\n                            x1 -= wdth / 2;\n                            x2 += wdth / 4;\n                        }\n                        else if (_n == 11)\n                            x1 -= wdth * 0.75;\n                    }\n                }\n                else continue;\n\n                var coll = keyColors[n * 2];\n                var colr = keyColors[n * 2 + 1];\n                var origcol = origColors[n];\n                float blendfac = coll.A;\n                float revblendfac = 1 - blendfac;\n                coll = new Color4(\n                    coll.R * blendfac + origcol.R * revblendfac,\n                    coll.G * blendfac + origcol.G * revblendfac,\n                    coll.B * blendfac + origcol.B * revblendfac,\n                    1);\n                r = coll.R;\n                g = coll.G;\n                b = coll.B;\n                a = coll.A;\n                blendfac = colr.A;\n                revblendfac = 1 - blendfac;\n                colr = new Color4(\n                    colr.R * blendfac + origcol.R * revblendfac,\n                    colr.G * blendfac + origcol.G * revblendfac,\n                    colr.B * blendfac + origcol.B * revblendfac,\n                    1);\n                r2 = colr.R;\n                g2 = colr.G;\n                b2 = colr.B;\n                a2 = colr.A;\n\n                if (keyPressed[n])\n                {\n                    //White key panel\n                    pos = quadBufferPos * 8;\n                    quadVertexbuff[pos++] = x1;\n                    quadVertexbuff[pos++] = wEndDownT;\n                    quadVertexbuff[pos++] = x2;\n                    quadVertexbuff[pos++] = wEndDownT;\n                    quadVertexbuff[pos++] = x2;\n                    quadVertexbuff[pos++] = y1;\n                    quadVertexbuff[pos++] = x1;\n                    quadVertexbuff[pos++] = y1;\n\n                    pos = quadBufferPos * 8;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 1;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 1;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 0.5;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 0.5;\n\n                    pos = quadBufferPos * 16;\n                    quadColorbuff[pos++] = r;\n                    quadColorbuff[pos++] = g;\n                    quadColorbuff[pos++] = b;\n                    quadColorbuff[pos++] = a;\n                    quadColorbuff[pos++] = r;\n                    quadColorbuff[pos++] = g;\n                    quadColorbuff[pos++] = b;\n                    quadColorbuff[pos++] = a;\n                    quadColorbuff[pos++] = r2;\n                    quadColorbuff[pos++] = g2;\n                    quadColorbuff[pos++] = b2;\n                    quadColorbuff[pos++] = a2;\n                    quadColorbuff[pos++] = r2;\n                    quadColorbuff[pos++] = g2;\n                    quadColorbuff[pos++] = b2;\n                    quadColorbuff[pos++] = a2;\n                    quadBufferPos++;\n                    FlushQuadBuffer();\n\n                    //Key End Notch \n                    pos = quadBufferPos * 8;\n                    quadVertexbuff[pos++] = x1;\n                    quadVertexbuff[pos++] = y2;\n                    quadVertexbuff[pos++] = x2;\n                    quadVertexbuff[pos++] = y2;\n                    quadVertexbuff[pos++] = x2;\n                    quadVertexbuff[pos++] = wEndDownT;\n                    quadVertexbuff[pos++] = x1;\n                    quadVertexbuff[pos++] = wEndDownT;\n\n                    pos = quadBufferPos * 8;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 0.6;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 0.6;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 0.6;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 0.6;\n\n                    pos = quadBufferPos * 16;\n                    quadColorbuff[pos++] = r;\n                    quadColorbuff[pos++] = g;\n                    quadColorbuff[pos++] = b;\n                    quadColorbuff[pos++] = a;\n                    quadColorbuff[pos++] = r;\n                    quadColorbuff[pos++] = g;\n                    quadColorbuff[pos++] = b;\n                    quadColorbuff[pos++] = a;\n                    quadColorbuff[pos++] = r2;\n                    quadColorbuff[pos++] = g2;\n                    quadColorbuff[pos++] = b2;\n                    quadColorbuff[pos++] = a2;\n                    quadColorbuff[pos++] = r2;\n                    quadColorbuff[pos++] = g2;\n                    quadColorbuff[pos++] = b2;\n                    quadColorbuff[pos++] = a2;\n                    quadBufferPos++;\n                    FlushQuadBuffer();\n                }\n                else\n                {\n                    //White key panel\n                    pos = quadBufferPos * 8;\n                    quadVertexbuff[pos++] = x1;\n                    quadVertexbuff[pos++] = wEndUpT;\n                    quadVertexbuff[pos++] = x2;\n                    quadVertexbuff[pos++] = wEndUpT;\n                    quadVertexbuff[pos++] = x2;\n                    quadVertexbuff[pos++] = y1;\n                    quadVertexbuff[pos++] = x1;\n                    quadVertexbuff[pos++] = y1;\n\n                    pos = quadBufferPos * 8;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 1;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 1;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 0.8;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 0.8;\n\n                    pos = quadBufferPos * 16;\n                    quadColorbuff[pos++] = r;\n                    quadColorbuff[pos++] = g;\n                    quadColorbuff[pos++] = b;\n                    quadColorbuff[pos++] = a;\n                    quadColorbuff[pos++] = r;\n                    quadColorbuff[pos++] = g;\n                    quadColorbuff[pos++] = b;\n                    quadColorbuff[pos++] = a;\n                    quadColorbuff[pos++] = r2;\n                    quadColorbuff[pos++] = g2;\n                    quadColorbuff[pos++] = b2;\n                    quadColorbuff[pos++] = a2;\n                    quadColorbuff[pos++] = r2;\n                    quadColorbuff[pos++] = g2;\n                    quadColorbuff[pos++] = b2;\n                    quadColorbuff[pos++] = a2;\n                    quadBufferPos++;\n                    FlushQuadBuffer();\n\n                    //Key End Notch \n                    r = .529f;\n                    g = .529f;\n                    b = .529f;\n                    a = 1f;\n                    r2 = .329f;\n                    g2 = .329f;\n                    b2 = .329f;\n                    a2 = 1f;\n                    pos = quadBufferPos * 8;\n                    quadVertexbuff[pos++] = x1;\n                    quadVertexbuff[pos++] = wEndUpB;\n                    quadVertexbuff[pos++] = x2;\n                    quadVertexbuff[pos++] = wEndUpB;\n                    quadVertexbuff[pos++] = x2;\n                    quadVertexbuff[pos++] = wEndUpT;\n                    quadVertexbuff[pos++] = x1;\n                    quadVertexbuff[pos++] = wEndUpT;\n\n                    pos = quadBufferPos * 8;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 1;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 1;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 1;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 1;\n\n                    pos = quadBufferPos * 16;\n                    quadColorbuff[pos++] = r;\n                    quadColorbuff[pos++] = g;\n                    quadColorbuff[pos++] = b;\n                    quadColorbuff[pos++] = a;\n                    quadColorbuff[pos++] = r;\n                    quadColorbuff[pos++] = g;\n                    quadColorbuff[pos++] = b;\n                    quadColorbuff[pos++] = a;\n                    quadColorbuff[pos++] = r2;\n                    quadColorbuff[pos++] = g2;\n                    quadColorbuff[pos++] = b2;\n                    quadColorbuff[pos++] = a2;\n                    quadColorbuff[pos++] = r2;\n                    quadColorbuff[pos++] = g2;\n                    quadColorbuff[pos++] = b2;\n                    quadColorbuff[pos++] = a2;\n                    quadBufferPos++;\n                    FlushQuadBuffer();\n\n                    //Key bottom side \n                    pos = quadBufferPos * 8;\n                    quadVertexbuff[pos++] = x1;\n                    quadVertexbuff[pos++] = y2;\n                    quadVertexbuff[pos++] = x2;\n                    quadVertexbuff[pos++] = y2;\n                    quadVertexbuff[pos++] = x2;\n                    quadVertexbuff[pos++] = wEndUpB;\n                    quadVertexbuff[pos++] = x1;\n                    quadVertexbuff[pos++] = wEndUpB;\n\n                    pos = quadBufferPos * 8;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 1;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 1;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 1;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 1;\n\n                    r = .615f;\n                    g = .615f;\n                    b = .615f;\n                    a = 1f;\n                    r2 = .729f;\n                    g2 = .729f;\n                    b2 = .729f;\n                    a2 = 1f;\n                    pos = quadBufferPos * 16;\n                    quadColorbuff[pos++] = r;\n                    quadColorbuff[pos++] = g;\n                    quadColorbuff[pos++] = b;\n                    quadColorbuff[pos++] = a;\n                    quadColorbuff[pos++] = r;\n                    quadColorbuff[pos++] = g;\n                    quadColorbuff[pos++] = b;\n                    quadColorbuff[pos++] = a;\n                    quadColorbuff[pos++] = r2;\n                    quadColorbuff[pos++] = g2;\n                    quadColorbuff[pos++] = b2;\n                    quadColorbuff[pos++] = a2;\n                    quadColorbuff[pos++] = r2;\n                    quadColorbuff[pos++] = g2;\n                    quadColorbuff[pos++] = b2;\n                    quadColorbuff[pos++] = a2;\n                    quadBufferPos++;\n                    FlushQuadBuffer();\n                }\n\n                //Middle C\n                if (n == 60 && settings.middleC)\n                {\n                    r = coll.R;\n                    g = coll.G;\n                    b = coll.B;\n                    a = coll.A;\n                    double _wdth = x2 - x1;\n                    double _x1 = x1 + _wdth / 4;\n                    double _x2 = x2 - _wdth / 4;\n                    double _y1, _y2;\n                    if (keyPressed[n])\n                    {\n                        _y2 = wEndDownT + wdth / 4;\n                        _y1 = _y2 + _wdth / 4.0 * 2.0 / renderSettings.height * renderSettings.width;\n                    }\n                    else\n                    {\n                        _y2 = wEndUpT + _wdth / 4;\n                        _y1 = _y2 + wdth / 4.0 * 2.0 / renderSettings.height * renderSettings.width;\n                    }\n                    //Key End Notch \n                    pos = quadBufferPos * 8;\n                    quadVertexbuff[pos++] = _x1;\n                    quadVertexbuff[pos++] = _y2;\n                    quadVertexbuff[pos++] = _x2;\n                    quadVertexbuff[pos++] = _y2;\n                    quadVertexbuff[pos++] = _x2;\n                    quadVertexbuff[pos++] = _y1;\n                    quadVertexbuff[pos++] = _x1;\n                    quadVertexbuff[pos++] = _y1;\n\n                    pos = quadBufferPos * 8;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 0.8;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 0.8;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 0.8;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 0.8;\n\n                    pos = quadBufferPos * 16;\n                    quadColorbuff[pos++] = r;\n                    quadColorbuff[pos++] = g;\n                    quadColorbuff[pos++] = b;\n                    quadColorbuff[pos++] = a;\n                    quadColorbuff[pos++] = r;\n                    quadColorbuff[pos++] = g;\n                    quadColorbuff[pos++] = b;\n                    quadColorbuff[pos++] = a;\n                    quadColorbuff[pos++] = r2;\n                    quadColorbuff[pos++] = g2;\n                    quadColorbuff[pos++] = b2;\n                    quadColorbuff[pos++] = a2;\n                    quadColorbuff[pos++] = r2;\n                    quadColorbuff[pos++] = g2;\n                    quadColorbuff[pos++] = b2;\n                    quadColorbuff[pos++] = a2;\n                    quadBufferPos++;\n                    FlushQuadBuffer();\n                }\n\n                //Separator\n                pos = quadBufferPos * 8;\n                x2 = x1;\n\n                x1 = Math.Floor(x1 * scwidth - sepwdth / 2);\n                x2 = Math.Floor(x2 * scwidth + sepwdth / 2);\n                if (x1 == x2) x2++;\n                x1 /= scwidth;\n                x2 /= scwidth;\n\n                r = .0431f;\n                g = .0431f;\n                b = .0431f;\n                a = 1f;\n                r2 = .556f;\n                g2 = .556f;\n                b2 = .556f;\n                a2 = 1f;\n                quadVertexbuff[pos++] = x1;\n                quadVertexbuff[pos++] = y2;\n                quadVertexbuff[pos++] = x2;\n                quadVertexbuff[pos++] = y2;\n                quadVertexbuff[pos++] = x2;\n                quadVertexbuff[pos++] = y1;\n                quadVertexbuff[pos++] = x1;\n                quadVertexbuff[pos++] = y1;\n\n                pos = quadBufferPos * 8;\n                quadAttribbuff[pos++] = 0;\n                quadAttribbuff[pos++] = 1;\n                quadAttribbuff[pos++] = 0;\n                quadAttribbuff[pos++] = 1;\n                quadAttribbuff[pos++] = 0;\n                quadAttribbuff[pos++] = 1;\n                quadAttribbuff[pos++] = 0;\n                quadAttribbuff[pos++] = 1;\n\n                pos = quadBufferPos * 16;\n                quadColorbuff[pos++] = r;\n                quadColorbuff[pos++] = g;\n                quadColorbuff[pos++] = b;\n                quadColorbuff[pos++] = a;\n                quadColorbuff[pos++] = r2;\n                quadColorbuff[pos++] = g2;\n                quadColorbuff[pos++] = b2;\n                quadColorbuff[pos++] = a2;\n                quadColorbuff[pos++] = r2;\n                quadColorbuff[pos++] = g2;\n                quadColorbuff[pos++] = b2;\n                quadColorbuff[pos++] = a2;\n                quadColorbuff[pos++] = r;\n                quadColorbuff[pos++] = g;\n                quadColorbuff[pos++] = b;\n                quadColorbuff[pos++] = a;\n                quadBufferPos++;\n                FlushQuadBuffer();\n            }\n            #endregion\n\n            #region Black\n            for (int n = kbfirstNote; n < kblastNote; n++)\n            {\n                if (!blackKeys[n])\n                {\n                    continue;\n                }\n\n                ox1 = x1array[n];\n                wdth = wdtharray[n];\n                ox2 = ox1 + wdth;\n                ix1 = ox1 + wdth / 8;\n                ix2 = ox2 - wdth / 8;\n\n                var coll = keyColors[n * 2];\n                var colr = keyColors[n * 2 + 1];\n                var origcol = origColors[n];\n                float blendfac = coll.A;\n                float revblendfac = 1 - blendfac;\n                coll = new Color4(\n                    coll.R * blendfac + origcol.R * revblendfac,\n                    coll.G * blendfac + origcol.G * revblendfac,\n                    coll.B * blendfac + origcol.B * revblendfac,\n                    1);\n                r = coll.R;\n                g = coll.G;\n                b = coll.B;\n                a = coll.A;\n                blendfac = colr.A;\n                revblendfac = 1 - blendfac;\n                colr = new Color4(\n                    colr.R * blendfac + origcol.R * revblendfac,\n                    colr.G * blendfac + origcol.G * revblendfac,\n                    colr.B * blendfac + origcol.B * revblendfac,\n                    1);\n                r2 = colr.R;\n                g2 = colr.G;\n                b2 = colr.B;\n                a2 = colr.A;\n                var colm = new Color4(\n                    (coll.R + colr.R) / 2,\n                    (coll.G + colr.G) / 2,\n                    (coll.B + colr.B) / 2,\n                    (coll.A + colr.A) / 2\n                    );\n                r3 = colm.R;\n                g3 = colm.G;\n                b3 = colm.B;\n                a3 = colm.A;\n\n                if (!keyPressed[n])\n                {\n                    #region Unpressed\n                    //Middle Top\n                    pos = quadBufferPos * 8;\n                    quadVertexbuff[pos++] = ix1;\n                    quadVertexbuff[pos++] = bKeyUSplitLT;\n                    quadVertexbuff[pos++] = ix2;\n                    quadVertexbuff[pos++] = bKeyUSplitRT;\n                    quadVertexbuff[pos++] = ix2;\n                    quadVertexbuff[pos++] = bKeyUpT;\n                    quadVertexbuff[pos++] = ix1;\n                    quadVertexbuff[pos++] = bKeyUpT;\n\n                    pos = quadBufferPos * 8;\n                    quadAttribbuff[pos++] = 0.25f;\n                    quadAttribbuff[pos++] = 1;\n                    quadAttribbuff[pos++] = 0.25f;\n                    quadAttribbuff[pos++] = 1;\n                    quadAttribbuff[pos++] = 0.15f;\n                    quadAttribbuff[pos++] = 1;\n                    quadAttribbuff[pos++] = 0.15f;\n                    quadAttribbuff[pos++] = 1;\n\n                    pos = quadBufferPos * 16;\n                    quadColorbuff[pos++] = r;\n                    quadColorbuff[pos++] = g;\n                    quadColorbuff[pos++] = b;\n                    quadColorbuff[pos++] = a;\n                    quadColorbuff[pos++] = r;\n                    quadColorbuff[pos++] = g;\n                    quadColorbuff[pos++] = b;\n                    quadColorbuff[pos++] = a;\n                    quadColorbuff[pos++] = r2;\n                    quadColorbuff[pos++] = g2;\n                    quadColorbuff[pos++] = b2;\n                    quadColorbuff[pos++] = a2;\n                    quadColorbuff[pos++] = r2;\n                    quadColorbuff[pos++] = g2;\n                    quadColorbuff[pos++] = b2;\n                    quadColorbuff[pos++] = a2;\n                    quadBufferPos++;\n                    FlushQuadBuffer();\n\n                    //Middle Middle\n                    pos = quadBufferPos * 8;\n                    quadVertexbuff[pos++] = ix1;\n                    quadVertexbuff[pos++] = bKeyUSplitLB;\n                    quadVertexbuff[pos++] = ix2;\n                    quadVertexbuff[pos++] = bKeyUSplitRB;\n                    quadVertexbuff[pos++] = ix2;\n                    quadVertexbuff[pos++] = bKeyUSplitRT;\n                    quadVertexbuff[pos++] = ix1;\n                    quadVertexbuff[pos++] = bKeyUSplitLT;\n\n                    pos = quadBufferPos * 8;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 1;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 1;\n                    quadAttribbuff[pos++] = 0.25f;\n                    quadAttribbuff[pos++] = 1;\n                    quadAttribbuff[pos++] = 0.25f;\n                    quadAttribbuff[pos++] = 1;\n\n                    pos = quadBufferPos * 16;\n                    quadColorbuff[pos++] = r;\n                    quadColorbuff[pos++] = g;\n                    quadColorbuff[pos++] = b;\n                    quadColorbuff[pos++] = a;\n                    quadColorbuff[pos++] = r;\n                    quadColorbuff[pos++] = g;\n                    quadColorbuff[pos++] = b;\n                    quadColorbuff[pos++] = a;\n                    quadColorbuff[pos++] = r2;\n                    quadColorbuff[pos++] = g2;\n                    quadColorbuff[pos++] = b2;\n                    quadColorbuff[pos++] = a2;\n                    quadColorbuff[pos++] = r2;\n                    quadColorbuff[pos++] = g2;\n                    quadColorbuff[pos++] = b2;\n                    quadColorbuff[pos++] = a2;\n                    quadBufferPos++;\n                    FlushQuadBuffer();\n\n                    //Middle Bottom\n                    pos = quadBufferPos * 8;\n                    quadVertexbuff[pos++] = ix1;\n                    quadVertexbuff[pos++] = bKeyUpB;\n                    quadVertexbuff[pos++] = ix2;\n                    quadVertexbuff[pos++] = bKeyUpB;\n                    quadVertexbuff[pos++] = ix2;\n                    quadVertexbuff[pos++] = bKeyUSplitRB;\n                    quadVertexbuff[pos++] = ix1;\n                    quadVertexbuff[pos++] = bKeyUSplitLB;\n\n                    pos = quadBufferPos * 8;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 1;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 1;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 1;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 1;\n\n                    pos = quadBufferPos * 16;\n                    quadColorbuff[pos++] = r;\n                    quadColorbuff[pos++] = g;\n                    quadColorbuff[pos++] = b;\n                    quadColorbuff[pos++] = a;\n                    quadColorbuff[pos++] = r;\n                    quadColorbuff[pos++] = g;\n                    quadColorbuff[pos++] = b;\n                    quadColorbuff[pos++] = a;\n                    quadColorbuff[pos++] = r2;\n                    quadColorbuff[pos++] = g2;\n                    quadColorbuff[pos++] = b2;\n                    quadColorbuff[pos++] = a2;\n                    quadColorbuff[pos++] = r2;\n                    quadColorbuff[pos++] = g2;\n                    quadColorbuff[pos++] = b2;\n                    quadColorbuff[pos++] = a2;\n                    quadBufferPos++;\n                    FlushQuadBuffer();\n\n                    //Left\n                    pos = quadBufferPos * 8;\n                    quadVertexbuff[pos++] = ox1;\n                    quadVertexbuff[pos++] = bKeyEnd;\n                    quadVertexbuff[pos++] = ix1;\n                    quadVertexbuff[pos++] = bKeyUpB;\n                    quadVertexbuff[pos++] = ix1;\n                    quadVertexbuff[pos++] = bKeyUpT;\n                    quadVertexbuff[pos++] = ox1;\n                    quadVertexbuff[pos++] = topBarEnd;\n\n                    pos = quadBufferPos * 8;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 1;\n                    quadAttribbuff[pos++] = 0.3f;\n                    quadAttribbuff[pos++] = 1;\n                    quadAttribbuff[pos++] = 0.3f;\n                    quadAttribbuff[pos++] = 1;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 1;\n\n                    pos = quadBufferPos * 16;\n                    quadColorbuff[pos++] = r;\n                    quadColorbuff[pos++] = g;\n                    quadColorbuff[pos++] = b;\n                    quadColorbuff[pos++] = a;\n                    quadColorbuff[pos++] = r;\n                    quadColorbuff[pos++] = g;\n                    quadColorbuff[pos++] = b;\n                    quadColorbuff[pos++] = a;\n                    quadColorbuff[pos++] = r2;\n                    quadColorbuff[pos++] = g2;\n                    quadColorbuff[pos++] = b2;\n                    quadColorbuff[pos++] = a2;\n                    quadColorbuff[pos++] = r2;\n                    quadColorbuff[pos++] = g2;\n                    quadColorbuff[pos++] = b2;\n                    quadColorbuff[pos++] = a2;\n                    quadBufferPos++;\n                    FlushQuadBuffer();\n\n                    //Right\n                    pos = quadBufferPos * 8;\n                    quadVertexbuff[pos++] = ox2;\n                    quadVertexbuff[pos++] = bKeyEnd;\n                    quadVertexbuff[pos++] = ix2;\n                    quadVertexbuff[pos++] = bKeyUpB;\n                    quadVertexbuff[pos++] = ix2;\n                    quadVertexbuff[pos++] = bKeyUpT;\n                    quadVertexbuff[pos++] = ox2;\n                    quadVertexbuff[pos++] = topBarEnd;\n\n                    pos = quadBufferPos * 8;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 1;\n                    quadAttribbuff[pos++] = 0.3f;\n                    quadAttribbuff[pos++] = 1;\n                    quadAttribbuff[pos++] = 0.3f;\n                    quadAttribbuff[pos++] = 1;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 1;\n\n                    pos = quadBufferPos * 16;\n                    quadColorbuff[pos++] = r;\n                    quadColorbuff[pos++] = g;\n                    quadColorbuff[pos++] = b;\n                    quadColorbuff[pos++] = a;\n                    quadColorbuff[pos++] = r;\n                    quadColorbuff[pos++] = g;\n                    quadColorbuff[pos++] = b;\n                    quadColorbuff[pos++] = a;\n                    quadColorbuff[pos++] = r2;\n                    quadColorbuff[pos++] = g2;\n                    quadColorbuff[pos++] = b2;\n                    quadColorbuff[pos++] = a2;\n                    quadColorbuff[pos++] = r2;\n                    quadColorbuff[pos++] = g2;\n                    quadColorbuff[pos++] = b2;\n                    quadColorbuff[pos++] = a2;\n                    quadBufferPos++;\n                    FlushQuadBuffer();\n\n                    //Bottom\n                    pos = quadBufferPos * 8;\n                    quadVertexbuff[pos++] = ox1;\n                    quadVertexbuff[pos++] = bKeyEnd;\n                    quadVertexbuff[pos++] = ox2;\n                    quadVertexbuff[pos++] = bKeyEnd;\n                    quadVertexbuff[pos++] = ix2;\n                    quadVertexbuff[pos++] = bKeyUpB;\n                    quadVertexbuff[pos++] = ix1;\n                    quadVertexbuff[pos++] = bKeyUpB;\n\n                    pos = quadBufferPos * 8;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 1;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 1;\n                    quadAttribbuff[pos++] = 0.3f;\n                    quadAttribbuff[pos++] = 1;\n                    quadAttribbuff[pos++] = 0.3f;\n                    quadAttribbuff[pos++] = 1;\n\n                    pos = quadBufferPos * 16;\n                    quadColorbuff[pos++] = r;\n                    quadColorbuff[pos++] = g;\n                    quadColorbuff[pos++] = b;\n                    quadColorbuff[pos++] = a;\n                    quadColorbuff[pos++] = r;\n                    quadColorbuff[pos++] = g;\n                    quadColorbuff[pos++] = b;\n                    quadColorbuff[pos++] = a;\n                    quadColorbuff[pos++] = r2;\n                    quadColorbuff[pos++] = g2;\n                    quadColorbuff[pos++] = b2;\n                    quadColorbuff[pos++] = a2;\n                    quadColorbuff[pos++] = r2;\n                    quadColorbuff[pos++] = g2;\n                    quadColorbuff[pos++] = b2;\n                    quadColorbuff[pos++] = a2;\n                    quadBufferPos++;\n                    FlushQuadBuffer();\n                    #endregion\n                }\n                else\n                {\n                    #region Pressed\n                    //Middle Top\n                    pos = quadBufferPos * 8;\n                    quadVertexbuff[pos++] = ix1;\n                    quadVertexbuff[pos++] = bKeyUSplitLT;\n                    quadVertexbuff[pos++] = ix2;\n                    quadVertexbuff[pos++] = bKeyUSplitRT;\n                    quadVertexbuff[pos++] = ix2;\n                    quadVertexbuff[pos++] = bKeyDownT;\n                    quadVertexbuff[pos++] = ix1;\n                    quadVertexbuff[pos++] = bKeyDownT;\n\n                    pos = quadBufferPos * 8;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 0.85f;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 0.85f;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 0.85f;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 0.85f;\n\n                    pos = quadBufferPos * 16;\n                    quadColorbuff[pos++] = r3;\n                    quadColorbuff[pos++] = g3;\n                    quadColorbuff[pos++] = b3;\n                    quadColorbuff[pos++] = a3;\n                    quadColorbuff[pos++] = r3;\n                    quadColorbuff[pos++] = g3;\n                    quadColorbuff[pos++] = b3;\n                    quadColorbuff[pos++] = a3;\n                    quadColorbuff[pos++] = r2;\n                    quadColorbuff[pos++] = g2;\n                    quadColorbuff[pos++] = b2;\n                    quadColorbuff[pos++] = a2;\n                    quadColorbuff[pos++] = r2;\n                    quadColorbuff[pos++] = g2;\n                    quadColorbuff[pos++] = b2;\n                    quadColorbuff[pos++] = a2;\n                    quadBufferPos++;\n                    FlushQuadBuffer();\n\n                    //Middle Middle\n                    pos = quadBufferPos * 8;\n                    quadVertexbuff[pos++] = ix1;\n                    quadVertexbuff[pos++] = bKeyUSplitLB;\n                    quadVertexbuff[pos++] = ix2;\n                    quadVertexbuff[pos++] = bKeyUSplitRB;\n                    quadVertexbuff[pos++] = ix2;\n                    quadVertexbuff[pos++] = bKeyUSplitRT;\n                    quadVertexbuff[pos++] = ix1;\n                    quadVertexbuff[pos++] = bKeyUSplitLT;\n\n                    pos = quadBufferPos * 8;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 0.7f;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 0.7f;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 0.85f;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 0.85f;\n\n                    pos = quadBufferPos * 16;\n                    quadColorbuff[pos++] = r3;\n                    quadColorbuff[pos++] = g3;\n                    quadColorbuff[pos++] = b3;\n                    quadColorbuff[pos++] = a3;\n                    quadColorbuff[pos++] = r3;\n                    quadColorbuff[pos++] = g3;\n                    quadColorbuff[pos++] = b3;\n                    quadColorbuff[pos++] = a3;\n                    quadColorbuff[pos++] = r3;\n                    quadColorbuff[pos++] = g3;\n                    quadColorbuff[pos++] = b3;\n                    quadColorbuff[pos++] = a3;\n                    quadColorbuff[pos++] = r3;\n                    quadColorbuff[pos++] = g3;\n                    quadColorbuff[pos++] = b3;\n                    quadColorbuff[pos++] = a3;\n                    quadBufferPos++;\n                    FlushQuadBuffer();\n\n                    //Middle Bottom\n                    pos = quadBufferPos * 8;\n                    quadVertexbuff[pos++] = ix1;\n                    quadVertexbuff[pos++] = bKeyDownB;\n                    quadVertexbuff[pos++] = ix2;\n                    quadVertexbuff[pos++] = bKeyDownB;\n                    quadVertexbuff[pos++] = ix2;\n                    quadVertexbuff[pos++] = bKeyUSplitRB;\n                    quadVertexbuff[pos++] = ix1;\n                    quadVertexbuff[pos++] = bKeyUSplitLB;\n\n                    pos = quadBufferPos * 8;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 0.7f;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 0.7f;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 0.7f;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 0.7f;\n\n                    pos = quadBufferPos * 16;\n                    quadColorbuff[pos++] = r;\n                    quadColorbuff[pos++] = g;\n                    quadColorbuff[pos++] = b;\n                    quadColorbuff[pos++] = a;\n                    quadColorbuff[pos++] = r;\n                    quadColorbuff[pos++] = g;\n                    quadColorbuff[pos++] = b;\n                    quadColorbuff[pos++] = a;\n                    quadColorbuff[pos++] = r3;\n                    quadColorbuff[pos++] = g3;\n                    quadColorbuff[pos++] = b3;\n                    quadColorbuff[pos++] = a3;\n                    quadColorbuff[pos++] = r3;\n                    quadColorbuff[pos++] = g3;\n                    quadColorbuff[pos++] = b3;\n                    quadColorbuff[pos++] = a3;\n                    quadBufferPos++;\n                    FlushQuadBuffer();\n\n                    //Left\n                    pos = quadBufferPos * 8;\n                    quadVertexbuff[pos++] = ox1;\n                    quadVertexbuff[pos++] = bKeyEnd;\n                    quadVertexbuff[pos++] = ix1;\n                    quadVertexbuff[pos++] = bKeyDownB;\n                    quadVertexbuff[pos++] = ix1;\n                    quadVertexbuff[pos++] = bKeyDownT;\n                    quadVertexbuff[pos++] = ox1;\n                    quadVertexbuff[pos++] = topBarEnd;\n\n                    pos = quadBufferPos * 8;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 0.7f;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 1;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 1;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 0.7f;\n\n                    pos = quadBufferPos * 16;\n                    quadColorbuff[pos++] = r;\n                    quadColorbuff[pos++] = g;\n                    quadColorbuff[pos++] = b;\n                    quadColorbuff[pos++] = a;\n                    quadColorbuff[pos++] = r;\n                    quadColorbuff[pos++] = g;\n                    quadColorbuff[pos++] = b;\n                    quadColorbuff[pos++] = a;\n                    quadColorbuff[pos++] = r2;\n                    quadColorbuff[pos++] = g2;\n                    quadColorbuff[pos++] = b2;\n                    quadColorbuff[pos++] = a2;\n                    quadColorbuff[pos++] = r2;\n                    quadColorbuff[pos++] = g2;\n                    quadColorbuff[pos++] = b2;\n                    quadColorbuff[pos++] = a2;\n                    quadBufferPos++;\n                    FlushQuadBuffer();\n\n                    //Right\n                    pos = quadBufferPos * 8;\n                    quadVertexbuff[pos++] = ox2;\n                    quadVertexbuff[pos++] = bKeyEnd;\n                    quadVertexbuff[pos++] = ix2;\n                    quadVertexbuff[pos++] = bKeyDownB;\n                    quadVertexbuff[pos++] = ix2;\n                    quadVertexbuff[pos++] = bKeyDownT;\n                    quadVertexbuff[pos++] = ox2;\n                    quadVertexbuff[pos++] = topBarEnd;\n\n                    pos = quadBufferPos * 8;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 0.7f;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 1;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 1;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 0.7f;\n\n                    pos = quadBufferPos * 16;\n                    quadColorbuff[pos++] = r;\n                    quadColorbuff[pos++] = g;\n                    quadColorbuff[pos++] = b;\n                    quadColorbuff[pos++] = a;\n                    quadColorbuff[pos++] = r;\n                    quadColorbuff[pos++] = g;\n                    quadColorbuff[pos++] = b;\n                    quadColorbuff[pos++] = a;\n                    quadColorbuff[pos++] = r2;\n                    quadColorbuff[pos++] = g2;\n                    quadColorbuff[pos++] = b2;\n                    quadColorbuff[pos++] = a2;\n                    quadColorbuff[pos++] = r2;\n                    quadColorbuff[pos++] = g2;\n                    quadColorbuff[pos++] = b2;\n                    quadColorbuff[pos++] = a2;\n                    quadBufferPos++;\n                    FlushQuadBuffer();\n\n                    //Bottom\n                    pos = quadBufferPos * 8;\n                    quadVertexbuff[pos++] = ox1;\n                    quadVertexbuff[pos++] = bKeyEnd;\n                    quadVertexbuff[pos++] = ox2;\n                    quadVertexbuff[pos++] = bKeyEnd;\n                    quadVertexbuff[pos++] = ix2;\n                    quadVertexbuff[pos++] = bKeyDownB;\n                    quadVertexbuff[pos++] = ix1;\n                    quadVertexbuff[pos++] = bKeyDownB;\n\n                    pos = quadBufferPos * 8;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 0.7f;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 0.7f;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 1;\n                    quadAttribbuff[pos++] = 0;\n                    quadAttribbuff[pos++] = 1;\n\n                    pos = quadBufferPos * 16;\n                    quadColorbuff[pos++] = r;\n                    quadColorbuff[pos++] = g;\n                    quadColorbuff[pos++] = b;\n                    quadColorbuff[pos++] = a;\n                    quadColorbuff[pos++] = r;\n                    quadColorbuff[pos++] = g;\n                    quadColorbuff[pos++] = b;\n                    quadColorbuff[pos++] = a;\n                    quadColorbuff[pos++] = r;\n                    quadColorbuff[pos++] = g;\n                    quadColorbuff[pos++] = b;\n                    quadColorbuff[pos++] = a;\n                    quadColorbuff[pos++] = r;\n                    quadColorbuff[pos++] = g;\n                    quadColorbuff[pos++] = b;\n                    quadColorbuff[pos++] = a;\n                    quadBufferPos++;\n                    FlushQuadBuffer();\n                    #endregion\n                }\n            }\n            #endregion\n\n            FlushQuadBuffer(false);\n            #endregion\n\n\n            GL.Disable(EnableCap.Blend);\n            GL.DisableClientState(ArrayCap.VertexArray);\n            GL.DisableClientState(ArrayCap.ColorArray);\n            GL.Disable(EnableCap.Texture2D);\n\n            GL.DisableVertexAttribArray(0);\n            GL.DisableVertexAttribArray(1);\n            GL.DisableVertexAttribArray(2);\n        }\n\n        void FlushQuadBuffer(bool check = true)\n        {\n            if (quadBufferPos < quadBufferLength && check) return;\n            if (quadBufferPos == 0) return;\n            GL.BindBuffer(BufferTarget.ArrayBuffer, vertexBufferID);\n            GL.BufferData(\n                BufferTarget.ArrayBuffer,\n                (IntPtr)(quadBufferPos * 8 * 8),\n                quadVertexbuff,\n                BufferUsageHint.StaticDraw);\n            GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Double, false, 16, 0);\n            GL.BindBuffer(BufferTarget.ArrayBuffer, colorBufferID);\n            GL.BufferData(\n                BufferTarget.ArrayBuffer,\n                (IntPtr)(quadBufferPos * 16 * 4),\n                quadColorbuff,\n                BufferUsageHint.StaticDraw);\n            GL.VertexAttribPointer(1, 4, VertexAttribPointerType.Float, false, 16, 0);\n            GL.BindBuffer(BufferTarget.ArrayBuffer, attribBufferID);\n            GL.BufferData(\n                BufferTarget.ArrayBuffer,\n                (IntPtr)(quadBufferPos * 8 * 8),\n                quadAttribbuff,\n                BufferUsageHint.StaticDraw);\n            GL.VertexAttribPointer(2, 2, VertexAttribPointerType.Double, false, 16, 0);\n            GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexBufferId);\n            GL.IndexPointer(IndexPointerType.Int, 1, 0);\n            GL.DrawElements(PrimitiveType.Triangles, quadBufferPos * 6, DrawElementsType.UnsignedInt, IntPtr.Zero);\n            quadBufferPos = 0;\n        }\n\n        public void ReloadTrackColors()\n        {\n            if (NoteColors == null) return;\n            var cols = ((SettingsCtrl)SettingsControl).paletteList.GetColors(NoteColors.Length);\n\n            for (int i = 0; i < NoteColors.Length; i++)\n            {\n                for (int j = 0; j < NoteColors[i].Length; j++)\n                {\n                    if (NoteColors[i][j].isDefault)\n                    {\n                        NoteColors[i][j].left = cols[i * 32 + j * 2];\n                        NoteColors[i][j].right = cols[i * 32 + j * 2 + 1];\n                    }\n                }\n            }\n        }\n\n        bool isBlackNote(int n)\n        {\n            n = n % 12;\n            return n == 1 || n == 3 || n == 6 || n == 8 || n == 10;\n        }\n    }\n}\n"
  },
  {
    "path": "PFARender/Settings.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace PFARender\n{\n    public enum TopColor\n    {\n        Red,\n        Blue,\n        Green\n    }\n\n    public class Settings\n    {\n        public int firstNote = 0;\n        public int lastNote = 128;\n        public double pianoHeight = 0.151;\n        public double deltaTimeOnScreen = 300;\n        public double borderWidth = 1;\n        public bool sameWidthNotes = false;\n        public TopColor topColor = TopColor.Red;\n        public bool middleC = false;\n        public bool blackNotesAbove = true;\n\n        public float topBarR = .585f;\n        public float topBarG = .0392f;\n        public float topBarB = .0249f;\n\n        public string palette = \"Random\";\n    }\n}\n"
  },
  {
    "path": "PFARender/SettingsCtrl.xaml",
    "content": "﻿<UserControl\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" \n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\" \n             xmlns:ui=\"clr-namespace:ZenithEngine.UI;assembly=ZenithEngine\"\n             xmlns:local=\"clr-namespace:PFARender\"\n             xmlns:xctk=\"http://schemas.xceed.com/wpf/xaml/toolkit\"\n             xmlns:bme=\"clr-namespace:ZenithEngine;assembly=ZenithEngine\" x:Class=\"PFARender.SettingsCtrl\"\n             mc:Ignorable=\"d\" \n             d:DesignHeight=\"450\" d:DesignWidth=\"800\">\n    <UserControl.Resources>\n        <ResourceDictionary>\n            <ResourceDictionary.MergedDictionaries>\n                <ResourceDictionary>\n                    <ResourceDictionary.MergedDictionaries>\n                        <ResourceDictionary Source=\"pack://siteoforigin:,,,/Languages/en/pfa.xaml\" />\n                    </ResourceDictionary.MergedDictionaries>\n                </ResourceDictionary>\n                <ResourceDictionary Source=\"pack://application:,,,/ZenithEngine;component/UI/Material.xaml\"/>\n            </ResourceDictionary.MergedDictionaries>\n        </ResourceDictionary>\n    </UserControl.Resources>\n    <DockPanel>\n        <StackPanel Margin=\"10\">\n            <DockPanel HorizontalAlignment=\"Left\" LastChildFill=\"False\" Margin=\"0,0,0,0\">\n                <Label Content=\"{DynamicResource firstNote}\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Top\" DockPanel.Dock=\"Left\"/>\n                <ui:NumberSelect x:Name=\"firstNote\" Value=\"1\" Maximum=\"254\" Minimum=\"0\" Margin=\"5,0,0,2\" HorizontalAlignment=\"Left\" Width=\"80\" ValueChanged=\"Nud_ValueChanged\"  />\n                <Label Content=\"{DynamicResource lastNote}\" HorizontalAlignment=\"Left\" Margin=\"10,0,0,0\" VerticalAlignment=\"Top\"/>\n                <ui:NumberSelect x:Name=\"lastNote\" Value=\"1\" Maximum=\"255\" Minimum=\"1\" Margin=\"5,0,0,2\" HorizontalAlignment=\"Left\" Width=\"80\" ValueChanged=\"Nud_ValueChanged\"  />\n            </DockPanel>\n            <DockPanel HorizontalAlignment=\"Left\" LastChildFill=\"False\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\">\n                <Label Content=\"{DynamicResource noteScreenTime}\" HorizontalAlignment=\"Left\" Margin=\"0,0,0,0\" VerticalAlignment=\"Top\"/>\n                <ui:ValueSlider x:Name=\"noteDeltaScreenTime\" Maximum=\"12\" DecimalPoints=\"2\" Minimum=\"2\" TrueMin=\"1\" TrueMax=\"100000\" ValueChanged=\"NoteDeltaScreenTime_ValueChanged\" Width=\"305\" VerticalAlignment=\"Top\"/>\n            </DockPanel>\n            <DockPanel HorizontalAlignment=\"Left\" LastChildFill=\"False\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\">\n                <Label Content=\"{DynamicResource pianoHeight}\" HorizontalAlignment=\"Left\" Margin=\"0\" VerticalAlignment=\"Top\"/>\n                <ui:NumberSelect x:Name=\"pianoHeight\" Value=\"1\" Maximum=\"100\" Minimum=\"1\" Margin=\"6,0,0,2\" HorizontalAlignment=\"Left\" Width=\"80\" ValueChanged=\"Nud_ValueChanged\"  />\n            </DockPanel>\n            <ui:BetterCheckbox x:Name=\"sameWidth\" Text=\"{DynamicResource sameWidthNotes}\" HorizontalAlignment=\"Left\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\" IsChecked=\"True\" CheckToggled=\"SameWidth_Checked\"/>\n            <ui:BetterCheckbox Name=\"middleCSquare\" Text=\"{DynamicResource middleCSquare}\" HorizontalAlignment=\"Left\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\" CheckToggled=\"MiddleCSquare_Checked\"/>\n            <ui:BetterCheckbox x:Name=\"blackNotesAbove\" Text=\"{DynamicResource blackNotesAbove}\" HorizontalAlignment=\"Left\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\" CheckToggled=\"BlackNotesAbove_Checked\"/>\n            <DockPanel HorizontalAlignment=\"Left\" LastChildFill=\"False\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\">\n                <Label x:Name=\"topCol\" Content=\"{DynamicResource topColor}\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Top\" DockPanel.Dock=\"Left\"/>\n                <TextBlock HorizontalAlignment=\"Left\" Margin=\"0,6,2,0\" TextWrapping=\"Wrap\" Text=\"#\" VerticalAlignment=\"Top\" FontSize=\"14\"/>\n                <TextBox x:Name=\"barColorHex\" HorizontalAlignment=\"Left\" FontSize=\"14\" MaxLength=\"6\" Height=\"24\" Margin=\"0,2,0,0\" TextWrapping=\"Wrap\" Text=\"950A06\" VerticalAlignment=\"Top\" Width=\"58\" TextChanged=\"BarColorHex_TextChanged\"/>\n            </DockPanel>\n            <DockPanel HorizontalAlignment=\"Left\" LastChildFill=\"False\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\" >\n                <Label Content=\"{DynamicResource borderWidth}\" DockPanel.Dock=\"Left\" VerticalAlignment=\"Top\"/>\n                <ui:NumberSelect x:Name=\"borderWidth\" Value=\"1\" DecimalPoints=\"2\" Step=\"0.1\" Maximum=\"2\" Minimum=\"0\" Margin=\"5,0,0,0\" HorizontalAlignment=\"Left\" Width=\"80\" Height=\"26\" VerticalAlignment=\"Top\" ValueChanged=\"Nud_ValueChanged\"  />\n            </DockPanel>\n        </StackPanel>\n        <bme:NoteColorPalettePick x:FieldModifier=\"public\" x:Name=\"paletteList\" Margin=\"0,10,10,10\" HorizontalAlignment=\"Right\" Width=\"184\"/>\n    </DockPanel>\n</UserControl>\n"
  },
  {
    "path": "PFARender/SettingsCtrl.xaml.cs",
    "content": "﻿using Newtonsoft.Json;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\n\nnamespace PFARender\n{\n    /// <summary>\n    /// Interaction logic for SettingsCtrl.xaml\n    /// </summary>\n    public partial class SettingsCtrl : UserControl\n    {\n        Settings settings;\n\n        public event Action PaletteChanged\n        {\n            add { paletteList.PaletteChanged += value; }\n            remove { paletteList.PaletteChanged -= value; }\n        }\n\n        public void SetValues()\n        {\n            firstNote.Value = settings.firstNote;\n            lastNote.Value = settings.lastNote - 1;\n            pianoHeight.Value = (int)(settings.pianoHeight * 100);\n            noteDeltaScreenTime.Value = settings.deltaTimeOnScreen;\n            borderWidth.Value = (decimal)settings.borderWidth;\n            sameWidth.IsChecked = settings.sameWidthNotes;\n            //topColorSelect.SelectedIndex = (int)settings.topColor;\n            middleCSquare.IsChecked = settings.middleC;\n            blackNotesAbove.IsChecked = settings.blackNotesAbove;\n            paletteList.SelectImage(settings.palette);\n        }\n\n        public SettingsCtrl(Settings settings) : base()\n        {\n            InitializeComponent();\n            noteDeltaScreenTime.nudToSlider = v => Math.Log(v, 2);\n            noteDeltaScreenTime.sliderToNud = v => Math.Pow(2, v);\n            this.settings = settings;\n            paletteList.SetPath(\"Plugins\\\\Assets\\\\Palettes\", 0.8f);\n            SetValues();\n        }\n\n        private void Nud_ValueChanged(object sender, RoutedPropertyChangedEventArgs<decimal> e)\n        {\n            if (settings == null) return;\n            if (sender == firstNote) settings.firstNote = (int)firstNote.Value;\n            if (sender == lastNote) settings.lastNote = (int)lastNote.Value + 1;\n            if (sender == pianoHeight) settings.pianoHeight = (double)pianoHeight.Value / 100;\n            if (sender == noteDeltaScreenTime) settings.deltaTimeOnScreen = (int)noteDeltaScreenTime.Value;\n            if (sender == borderWidth) settings.borderWidth = (double)borderWidth.Value;\n        }\n\n        private void NoteDeltaScreenTime_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)\n        {\n            if (settings == null) return;\n            settings.deltaTimeOnScreen = noteDeltaScreenTime.Value;\n        }\n\n        void injectSettings(Settings sett)\n        {\n            var sourceProps = typeof(Settings).GetFields().ToList();\n            var destProps = typeof(Settings).GetFields().ToList();\n\n            foreach (var sourceProp in sourceProps)\n            {\n                if (destProps.Any(x => x.Name == sourceProp.Name))\n                {\n                    var p = destProps.First(x => x.Name == sourceProp.Name);\n                    p.SetValue(settings, sourceProp.GetValue(sett));\n                }\n            }\n            SetValues();\n        }\n\n        private void SameWidth_Checked(object sender, RoutedEventArgs e)\n        {\n            if (settings == null) return;\n            settings.sameWidthNotes = (bool)sameWidth.IsChecked;\n        }\n\n        //private void TopColorSelect_SelectionChanged(object sender, SelectionChangedEventArgs e)\n        //{\n        //    try\n        //    {\n        //        settings.topColor = (TopColor)topColorSelect.SelectedIndex;\n        //    }\n        //    catch (NullReferenceException) { }\n        //}\n\n        private void MiddleCSquare_Checked(object sender, RoutedEventArgs e)\n        {\n            if (settings == null) return;\n            settings.middleC = (bool)middleCSquare.IsChecked;\n        }\n\n        private void BlackNotesAbove_Checked(object sender, RoutedEventArgs e)\n        {\n            if (settings == null) return;\n            settings.blackNotesAbove = (bool)blackNotesAbove.IsChecked;\n        }\n\n        private void BarColorHex_TextChanged(object sender, TextChangedEventArgs e)\n        {\n            if (settings == null) return;\n            var hex = barColorHex.Text;\n            if (hex.Length != 6) return;\n            try\n            {\n                int col = int.Parse(hex, System.Globalization.NumberStyles.HexNumber);\n                settings.topBarR = ((col >> 16) & 0xFF) / 255.0f;\n                settings.topBarG = ((col >> 8) & 0xFF) / 255.0f;\n                settings.topBarB = ((col >> 0) & 0xFF) / 255.0f;\n            }\n            catch { return; }\n        }\n    }\n}\n"
  },
  {
    "path": "PFARender/packages.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n  <package id=\"Newtonsoft.Json\" version=\"12.0.1\" targetFramework=\"net461\" />\n  <package id=\"OpenTK\" version=\"3.1.0\" targetFramework=\"net461\" />\n</packages>"
  },
  {
    "path": "README-plugin.md",
    "content": "# Black-Midi-Render\n\n## Creating a plugin project:\n- Create a new class library project\n- Add references to: BMEngine project, PresentationCore, PresentationFramework, OpenTK (through nuget)\n- Optionally, add references (though nuget) to Newtonsoft.Json, Extended.Wpf.Toolkit\n- Add a **public** \"Render\" class to the project that implements the IPluginRender interface from BMEngine\n\n- Name, Description and PreviewImage are static fields that show the information on the plugins tab\n- Initialized needs to be set to true when Init() is called, and to false when Dispose is called\n- NoteScreenTime is the time the window of time (in ticks) that the notes can spend on the screen before getting deleted\n- LastNoteCount is the last number of notes rendered. \n- SettingsControl is a WPF control that will be visible on the Plugin Settings page\n\n- Init() can be used to initialise frame buffers and vertex buffers\n- Dispose() removed them\n- SetTrackColors() sets the initial colors of the midi tracks/channels (for custom note color schemes)\n- RenderFrame() renders a frame, the final image has to be loaded into the framebuffer \"finalCompositeBuff\"\n- For those new to framebuffers, this is done by: `GL.BindFramebuffer(FramebufferTarget.Framebuffer, finalCompositeBuff);`"
  },
  {
    "path": "README.md",
    "content": "<h1 align=\"center\">Zenith</h1>\n\n<p align=\"center\">\n    <img src=\"https://i.imgur.com/jshhiL3.png\" width=\"256\" />\n    <br />\n    <strong>The world's most optimised MIDI renderer!</strong>\n</p>\n\n<p align=\"center\">\n    <a href=\"https://github.com/arduano/Zenith-MIDI/releases/\"><img src=\"https://img.shields.io/github/release/arduano/Zenith-MIDI.svg?style=flat-square\" alt=\"GitHub release\"></a>\n    <a href=\"https://github.com/arduano/Zenith-MIDI/releases/\"><img src=\"https://img.shields.io/github/downloads/arduano/Zenith-MIDI/total.svg?style=flat-square\" alt=\"GitHub release\"></a>\n    <a href=\"https://github.com/arduano/Zenith-MIDI/blob/master/LICENSE\"><img src=\"https://img.shields.io/badge/license-DBAD-blue.svg?style=flat-square\" alt=\"DBAD license\"></a>\n    <a href=\"http://makeapullrequest.com\"><img src=\"https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square\" alt=\"PRs Welcome\"></a>\n    <a href=\"https://discord.gg/Aj4cb5\"><img src=\"https://img.shields.io/discord/549344616210628609.svg?color=7289DA&style=flat-square\" alt=\"Discord\"></a>\n    <a href=\"https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=M9XRCSPYSMBCA&source=url\"><img src=\"https://img.shields.io/badge/Donate-PayPal-green.svg?style=flat-square\" alt=\"Donate\"></a>\n</p>\n\n<strong>NOTE: I no longer support this project, I don't even have the means to compile new versions anymore as I'm on Linux and this project is Windows-only. I may answer GitHub issues, but don't expect further updates unless you're willing to take on maintenance yourself.</strong>\n\n## Features\n- Much better performance than any other midi renderer currently out there, and extremely RAM optimised!\n- Custom render plugins! Anyone can make a render plugin and use it with my Zenith, just by dropping it into the Plugins folder!\n- About the plugins again, the included plugins are extremely customisable and are designed with high quality video production in mind!\n- Customisable render settings, with the ability to change them during preview mode to see real time changes\n- Option to \"Include Audio\" with render, and to render a separate \"Transparency Mask\" video for advanced video editing!\n- Customisable render settings, with the ability to change almost all plugin settings, and even switch plugins themselves during preview mode to see real time changes! \n\n## Plugins\nAs a general rule, all custom and original plugins are completely free and open for anyone to use. However exact clones (PFA, miditrail, etc.) will be slightly modified to make them distinct from the original. I might release the exact looking copies, however BMT members are highly against them spreading anywhere.\n\nIt is possible for anyone else who knows C# to make a plugin themselves as well. Feel free to contact me on my dev server! \n\n- **Classic:** The original render engine for Zenith. Any new render features in Zenith will always be supported first by this plugin. *Included with the program*\n- **Flat:** Very similar to the classic plugin, and more of a proof of concept than anything. Basically the classic version except without shading. *Included with the program*\n- **PFA+:** An almost identical visual clone of the original program, with support for some exclusive features, including: same width notes, tick based rendering, and Zenith's classic gradient and transparency support. Original options like same width notes and changing the bar colour are also present. Some extra customisations were also included! *Included with the program*\n- **MidiTrail+:**  My most ambitious plugin yet. Taking inspiration from the original MidiTrail program, and going far above and beyond, adding support for many requested features, uncluding: Custom ripple (aura) images, 3d box note support and different width notes. This all comes with Zenith's classic support of transparent notes and note gradients. *Included with the program*\n- **Note Count Render:** Renders a highly customisable text label for the midi statistics, including properties such as note count, polyphony, tempo, time, ticks, bars and MANY more. Font and font size are also easily customsiable. *Included with the program*\n- **Textured:** If the other plugins aren't enough for you, you can make your own look by using the Textured plugin. Extremely customisable, allowing features such as custom note caps for rounded notes and custom keyboard overlays. *Included with the program*\n\n## Installation\n[![](https://img.shields.io/github/v/release/arduano/Zenith-MIDI?label=download%2032-bit&style=flat-square)](https://github.com/arduano/Zenith-MIDI/releases/latest/download/Zenithx86.zip) [![](https://img.shields.io/github/v/release/arduano/Zenith-MIDI?label=download%2064-bit&style=flat-square&color=green)](https://github.com/arduano/Zenith-MIDI/releases/latest/download/Zenithx64.zip)\n\nYou can download the latest version of Zenith for Windows 32-bit [here](https://github.com/arduano/Zenith-MIDI/releases/latest/download/Zenithx86.zip) and Windows 64-bit [here](https://github.com/arduano/Zenith-MIDI/releases/latest/download/Zenithx64.zip).\n\nFFmpeg is required for video rendering, which you can get it [here](https://ffmpeg.zeranoe.com/builds/).\n\n## Usage\nAfter downloading the app, extract the .zip archive and run the program. If FFmpeg is downloaded, put it next to the Zenith .exe.\n\n## License\nZenith is licensed under the terms of the [Don't Be a Dick Public License](https://github.com/arduano/Zenith-MIDI/blob/master/LICENSE).\n\n## Screenshots\n| ![](https://arduano.github.io/Zenith-MIDI/dist/bmr/assets/plugins/classic.png) |   ![](https://arduano.github.io/Zenith-MIDI/dist/bmr/assets/plugins/flat.png)    |\n| :----------------------------------------------------------------------------: | :------------------------------------------------------------------------------: |\n|                                 Classic plugin                                 |                                   Flat plugin                                    |\n|   ![](https://arduano.github.io/Zenith-MIDI/dist/bmr/assets/plugins/pfa.png)   | ![](https://arduano.github.io/Zenith-MIDI/dist/bmr/assets/plugins/miditrail.png) |\n|                                  PFA+ plugin                                   |                                MidiTrail+ plugin                                 |\n"
  },
  {
    "path": "ScriptedRenderer/Languages/en/scripted.xaml",
    "content": "﻿<ResourceDictionary\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" \n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n    xmlns:local=\"using:MSDNSample\"\n    xmlns:system=\"clr-namespace:System;assembly=mscorlib\">\n\n    <!--Tab Names-->\n    <system:String x:Key=\"resourcesTab\">Resources</system:String>\n    <system:String x:Key=\"miscTab\">Misc</system:String>\n    <system:String x:Key=\"settingsTab\">Script Settings</system:String>\n\n    <!--Resources-->\n    <system:String x:Key=\"resourcePath\">Resource packs path:</system:String>\n    <system:String x:Key=\"resourcePathDir\">Plugins\\Assets\\Textured\\Resources</system:String>\n    <system:String x:Key=\"openFolder\">Open Folder</system:String>\n    <system:String x:Key=\"reloadList\">Reload List</system:String>\n    <system:String x:Key=\"reloadScript\">Reload Script</system:String>\n\n    <!--Other settings-->\n    <system:String x:Key=\"firstNote\">First Note</system:String>\n    <system:String x:Key=\"lastNote\">Last Note</system:String>\n    <system:String x:Key=\"noteScreenTime\">Note screen time</system:String>\n\n    <system:String x:Key=\"profile\">Profile</system:String>\n    <system:String x:Key=\"save\">Save</system:String>\n    <system:String x:Key=\"delete\">Delete</system:String>\n    <system:String x:Key=\"defaults\">Defaults</system:String>\n\n</ResourceDictionary>"
  },
  {
    "path": "ScriptedRenderer/OpenTK.dll.config",
    "content": "<configuration>\n  <dllmap os=\"linux\" dll=\"opengl32.dll\" target=\"libGL.so.1\"/>\n  <dllmap os=\"linux\" dll=\"glu32.dll\" target=\"libGLU.so.1\"/>\n  <dllmap os=\"linux\" dll=\"openal32.dll\" target=\"libopenal.so.1\"/>\n  <dllmap os=\"linux\" dll=\"alut.dll\" target=\"libalut.so.0\"/>\n  <dllmap os=\"linux\" dll=\"opencl.dll\" target=\"libOpenCL.so\"/>\n  <dllmap os=\"linux\" dll=\"libX11\" target=\"libX11.so.6\"/>\n  <dllmap os=\"linux\" dll=\"libXi\" target=\"libXi.so.6\"/>\n  <dllmap os=\"linux\" dll=\"SDL2.dll\" target=\"libSDL2-2.0.so.0\"/>\n  <dllmap os=\"osx\" dll=\"opengl32.dll\" target=\"/System/Library/Frameworks/OpenGL.framework/OpenGL\"/>\n  <dllmap os=\"osx\" dll=\"openal32.dll\" target=\"/System/Library/Frameworks/OpenAL.framework/OpenAL\" />\n  <dllmap os=\"osx\" dll=\"alut.dll\" target=\"/System/Library/Frameworks/OpenAL.framework/OpenAL\" />\n  <dllmap os=\"osx\" dll=\"libGLES.dll\" target=\"/System/Library/Frameworks/OpenGLES.framework/OpenGLES\" />\n  <dllmap os=\"osx\" dll=\"libGLESv1_CM.dll\" target=\"/System/Library/Frameworks/OpenGLES.framework/OpenGLES\" />\n  <dllmap os=\"osx\" dll=\"libGLESv2.dll\" target=\"/System/Library/Frameworks/OpenGLES.framework/OpenGLES\" />\n  <dllmap os=\"osx\" dll=\"opencl.dll\" target=\"/System/Library/Frameworks/OpenCL.framework/OpenCL\"/>\n  <dllmap os=\"osx\" dll=\"SDL2.dll\" target=\"libSDL2.dylib\"/>\n  <!-- XQuartz compatibility (X11 on Mac) -->\n  <dllmap os=\"osx\" dll=\"libGL.so.1\" target=\"/usr/X11/lib/libGL.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libX11\" target=\"/usr/X11/lib/libX11.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXcursor.so.1\" target=\"/usr/X11/lib/libXcursor.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXi\" target=\"/usr/X11/lib/libXi.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXinerama\" target=\"/usr/X11/lib/libXinerama.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXrandr.so.2\" target=\"/usr/X11/lib/libXrandr.dylib\"/>\n</configuration>\n"
  },
  {
    "path": "ScriptedRenderer/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// General Information about an assembly is controlled through the following\n// set of attributes. Change these attribute values to modify the information\n// associated with an assembly.\n[assembly: AssemblyTitle(\"ScriptedRender\")]\n[assembly: AssemblyDescription(\"\")]\n[assembly: AssemblyConfiguration(\"\")]\n[assembly: AssemblyCompany(\"\")]\n[assembly: AssemblyProduct(\"ScriptedRender\")]\n[assembly: AssemblyCopyright(\"Copyright ©  2020\")]\n[assembly: AssemblyTrademark(\"\")]\n[assembly: AssemblyCulture(\"\")]\n\n// Setting ComVisible to false makes the types in this assembly not visible\n// to COM components.  If you need to access a type in this assembly from\n// COM, set the ComVisible attribute to true on that type.\n[assembly: ComVisible(false)]\n\n// The following GUID is for the ID of the typelib if this project is exposed to COM\n[assembly: Guid(\"95f774eb-47a2-40b3-a0ce-e616bbd4d62b\")]\n\n// Version information for an assembly consists of the following four values:\n//\n//      Major Version\n//      Minor Version\n//      Build Number\n//      Revision\n//\n// You can specify all the values or you can default the Build and Revision Numbers\n// by using the '*' as shown below:\n// [assembly: AssemblyVersion(\"1.0.*\")]\n[assembly: AssemblyVersion(\"1.0.0.0\")]\n[assembly: AssemblyFileVersion(\"1.0.0.0\")]\n"
  },
  {
    "path": "ScriptedRenderer/Properties/Resources.Designer.cs",
    "content": "﻿//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace ScriptedRender.Properties {\n    using System;\n    \n    \n    /// <summary>\n    ///   A strongly-typed resource class, for looking up localized strings, etc.\n    /// </summary>\n    // This class was auto-generated by the StronglyTypedResourceBuilder\n    // class via a tool like ResGen or Visual Studio.\n    // To add or remove a member, edit your .ResX file then rerun ResGen\n    // with the /str option, or rebuild your VS project.\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"System.Resources.Tools.StronglyTypedResourceBuilder\", \"16.0.0.0\")]\n    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]\n    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]\n    internal class Resources {\n        \n        private static global::System.Resources.ResourceManager resourceMan;\n        \n        private static global::System.Globalization.CultureInfo resourceCulture;\n        \n        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute(\"Microsoft.Performance\", \"CA1811:AvoidUncalledPrivateCode\")]\n        internal Resources() {\n        }\n        \n        /// <summary>\n        ///   Returns the cached ResourceManager instance used by this class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Resources.ResourceManager ResourceManager {\n            get {\n                if (object.ReferenceEquals(resourceMan, null)) {\n                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager(\"ScriptedRender.Properties.Resources\", typeof(Resources).Assembly);\n                    resourceMan = temp;\n                }\n                return resourceMan;\n            }\n        }\n        \n        /// <summary>\n        ///   Overrides the current thread's CurrentUICulture property for all\n        ///   resource lookups using this strongly typed resource class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Globalization.CultureInfo Culture {\n            get {\n                return resourceCulture;\n            }\n            set {\n                resourceCulture = value;\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized resource of type System.Byte[].\n        /// </summary>\n        internal static byte[] Examples {\n            get {\n                object obj = ResourceManager.GetObject(\"Examples\", resourceCulture);\n                return ((byte[])(obj));\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized resource of type System.Drawing.Bitmap.\n        /// </summary>\n        internal static System.Drawing.Bitmap preview {\n            get {\n                object obj = ResourceManager.GetObject(\"preview\", resourceCulture);\n                return ((System.Drawing.Bitmap)(obj));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "ScriptedRenderer/Properties/Resources.resx",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The primary goals of this format is to allow a simple XML format \n    that is mostly human readable. The generation and parsing of the \n    various data types are done through the TypeConverter classes \n    associated with the data types.\n    \n    Example:\n    \n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n                \n    There are any number of \"resheader\" rows that contain simple \n    name/value pairs.\n    \n    Each data row contains a name, and value. The row also contains a \n    type or mimetype. Type corresponds to a .NET class that support \n    text/value conversion through the TypeConverter architecture. \n    Classes that don't support this are serialized and stored with the \n    mimetype set.\n    \n    The mimetype is used for serialized objects, and tells the \n    ResXResourceReader how to depersist the object. This is currently not \n    extensible. For a given mimetype the value must be set accordingly:\n    \n    Note - application/x-microsoft.net.object.binary.base64 is the format \n    that the ResXResourceWriter will generate, however the reader can \n    read any of the formats listed below.\n    \n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n    \n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array \n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <assembly alias=\"System.Windows.Forms\" name=\"System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" />\n  <data name=\"Examples\" type=\"System.Resources.ResXFileRef, System.Windows.Forms\">\n    <value>..\\Resources\\Examples.zip;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </data>\n  <data name=\"preview\" type=\"System.Resources.ResXFileRef, System.Windows.Forms\">\n    <value>..\\Resources\\preview.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>\n  </data>\n</root>"
  },
  {
    "path": "ScriptedRenderer/Render.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing ZenithEngine;\nusing OpenTK.Graphics.OpenGL;\nusing OpenTK.Graphics;\nusing OpenTK;\nusing System.Drawing;\nusing System.Drawing.Imaging;\nusing ScriptedEngine;\nusing Font = ScriptedEngine.Font;\n\nnamespace ScriptedRender\n{\n    class jsIterator<T>\n    {\n        IEnumerator<T> en;\n        IEnumerable<T> iter;\n\n        public jsIterator(IEnumerable<T> iter)\n        {\n            this.iter = iter;\n            reset();\n        }\n\n        public T current => en.Current;\n        public bool next() => en.MoveNext();\n        public void reset() => en = iter.GetEnumerator();\n    }\n\n    public class Render : IPluginRender\n    {\n        public string Name => \"Scripted\";\n        public string Description => \"Uses user-made C# scripts (compiled in runtime) to give almost full control over rendering.\\nExtremely customizable, relatively easy to use.\";\n        public string LanguageDictName => \"scripted\";\n\n        public bool ManualNoteDelete { get; private set; } = false;\n        public double NoteCollectorOffset { get; private set; } = 0;\n\n        public System.Windows.Media.ImageSource PreviewImage { get; private set; }\n        public System.Windows.Controls.Control SettingsControl => settingsControl;\n        public bool Initialized { get; private set; }\n        public double NoteScreenTime { get; private set; }\n        public long LastNoteCount { get; private set; }\n\n        public NoteColor[][] NoteColors { get; set; }\n        public double Tempo { get; set; }\n        public MidiInfo CurrentMidi { get; set; }\n\n        #region Shaders\n        string quadShaderVert = @\"#version 330 core\n\nlayout(location=0) in vec2 in_position;\nlayout(location=1) in vec4 in_color;\nlayout(location=2) in vec2 in_uv;\nlayout(location=3) in float in_texid;\n\nout vec4 v2f_color;\nout vec2 uv;\nout float texid;\n\nvoid main()\n{\n    gl_Position = vec4(in_position.x * 2 - 1, in_position.y * 2 - 1, 1.0f, 1.0f);\n    v2f_color = in_color;\n    uv = in_uv;\n    texid = in_texid;\n}\n\";\n        string quadShaderFrag = @\"#version 330 core\n\nin vec4 v2f_color;\nin vec2 uv;\nin float texid;\n\nuniform sampler2D textureSampler1;\nuniform sampler2D textureSampler2;\nuniform sampler2D textureSampler3;\nuniform sampler2D textureSampler4;\nuniform sampler2D textureSampler5;\nuniform sampler2D textureSampler6;\nuniform sampler2D textureSampler7;\nuniform sampler2D textureSampler8;\nuniform sampler2D textureSampler9;\nuniform sampler2D textureSampler10;\nuniform sampler2D textureSampler11;\nuniform sampler2D textureSampler12;\n\nout vec4 out_color;\n\nvoid main()\n{\n    vec4 col;\n    if(texid < -0.5) col = vec4(1, 1, 1, 1);\n    else if(texid < 0.5) col = texture2D( textureSampler1, uv );\n    else if(texid < 1.5) col = texture2D( textureSampler2, uv );\n    else if(texid < 2.5) col = texture2D( textureSampler3, uv );\n    else if(texid < 3.5) col = texture2D( textureSampler4, uv );\n    else if(texid < 4.5) col = texture2D( textureSampler5, uv );\n    else if(texid < 5.5) col = texture2D( textureSampler6, uv );\n    else if(texid < 6.5) col = texture2D( textureSampler7, uv );\n    else if(texid < 7.5) col = texture2D( textureSampler8, uv );\n    else if(texid < 8.5) col = texture2D( textureSampler9, uv );\n    else if(texid < 9.5) col = texture2D( textureSampler10, uv );\n    else if(texid < 10.5) col = texture2D( textureSampler11, uv );\n    else if(texid < 11.5) col = texture2D( textureSampler12, uv );\n    out_color = col * v2f_color;\n}\n\";\n        string invertQuadShaderFrag = @\"#version 330 core\n\nin vec4 v2f_color;\nin vec2 uv;\nin float texid;\n\nuniform sampler2D textureSampler1;\nuniform sampler2D textureSampler2;\nuniform sampler2D textureSampler3;\nuniform sampler2D textureSampler4;\nuniform sampler2D textureSampler5;\nuniform sampler2D textureSampler6;\nuniform sampler2D textureSampler7;\nuniform sampler2D textureSampler8;\nuniform sampler2D textureSampler9;\nuniform sampler2D textureSampler10;\nuniform sampler2D textureSampler11;\nuniform sampler2D textureSampler12;\n\nout vec4 out_color;\n\nvoid main()\n{\n    vec4 col;\n    if(texid < -0.5) col = vec4(0, 0, 0, 1);\n    else if(texid < 0.5) col = texture2D( textureSampler1, uv );\n    else if(texid < 1.5) col = texture2D( textureSampler2, uv );\n    else if(texid < 2.5) col = texture2D( textureSampler3, uv );\n    else if(texid < 3.5) col = texture2D( textureSampler4, uv );\n    else if(texid < 4.5) col = texture2D( textureSampler5, uv );\n    else if(texid < 5.5) col = texture2D( textureSampler6, uv );\n    else if(texid < 6.5) col = texture2D( textureSampler7, uv );\n    else if(texid < 7.5) col = texture2D( textureSampler8, uv );\n    else if(texid < 8.5) col = texture2D( textureSampler9, uv );\n    else if(texid < 9.5) col = texture2D( textureSampler10, uv );\n    else if(texid < 10.5) col = texture2D( textureSampler11, uv );\n    else if(texid < 11.5) col = texture2D( textureSampler12, uv );\n    col = 1 - col;\n    col.w = 1 - col.w;\n    vec4 col2 = 1 - v2f_color;\n    col2.w = 1 - col2.w;\n    out_color = 1 - col * col2;\n    out_color.w = 1 - out_color.w;\n}\n\";\n        string evenQuadShaderFrag = @\"#version 330 core\n\nin vec4 v2f_color;\nin vec2 uv;\nin float texid;\n\nuniform sampler2D textureSampler1;\nuniform sampler2D textureSampler2;\nuniform sampler2D textureSampler3;\nuniform sampler2D textureSampler4;\nuniform sampler2D textureSampler5;\nuniform sampler2D textureSampler6;\nuniform sampler2D textureSampler7;\nuniform sampler2D textureSampler8;\nuniform sampler2D textureSampler9;\nuniform sampler2D textureSampler10;\nuniform sampler2D textureSampler11;\nuniform sampler2D textureSampler12;\n\nout vec4 out_color;\n\nvoid main()\n{\n    vec4 col;\n    if(texid < -0.5) col = vec4(0.5, 0.5, 0.5, 1);\n    else if(texid < 0.5) col = texture2D( textureSampler1, uv );\n    else if(texid < 1.5) col = texture2D( textureSampler2, uv );\n    else if(texid < 2.5) col = texture2D( textureSampler3, uv );\n    else if(texid < 3.5) col = texture2D( textureSampler4, uv );\n    else if(texid < 4.5) col = texture2D( textureSampler5, uv );\n    else if(texid < 5.5) col = texture2D( textureSampler6, uv );\n    else if(texid < 6.5) col = texture2D( textureSampler7, uv );\n    else if(texid < 7.5) col = texture2D( textureSampler8, uv );\n    else if(texid < 8.5) col = texture2D( textureSampler9, uv );\n    else if(texid < 9.5) col = texture2D( textureSampler10, uv );\n    else if(texid < 10.5) col = texture2D( textureSampler11, uv );\n    else if(texid < 11.5) col = texture2D( textureSampler12, uv );\n    col = col * 2;\n    if(col.x > 1){\n        out_color.x = 1 - (2 - col.x) * (1 - v2f_color.x);\n    }\n    else out_color.x = col.x * v2f_color.x;\n    if(col.y > 1){\n        out_color.y = 1 - (2 - col.y) * (1 - v2f_color.y);\n    }\n    else out_color.y = col.y * v2f_color.y;\n    if(col.z > 1){\n        out_color.z = 1 - (2 - col.z) * (1 - v2f_color.z);\n    }\n    else out_color.z = col.z * v2f_color.z;\n    out_color.w = col.w * v2f_color.w;\n}\n\";\n\n        int MakeShader(string vert, string frag)\n        {\n            int _vertexObj = GL.CreateShader(ShaderType.VertexShader);\n            int _fragObj = GL.CreateShader(ShaderType.FragmentShader);\n            int statusCode;\n            string info;\n\n            GL.ShaderSource(_vertexObj, vert);\n            GL.CompileShader(_vertexObj);\n            info = GL.GetShaderInfoLog(_vertexObj);\n            GL.GetShader(_vertexObj, ShaderParameter.CompileStatus, out statusCode);\n            if (statusCode != 1) throw new ApplicationException(info);\n\n            GL.ShaderSource(_fragObj, frag);\n            GL.CompileShader(_fragObj);\n            info = GL.GetShaderInfoLog(_fragObj);\n            GL.GetShader(_fragObj, ShaderParameter.CompileStatus, out statusCode);\n            if (statusCode != 1) throw new ApplicationException(info);\n\n            int shader = GL.CreateProgram();\n            GL.AttachShader(shader, _fragObj);\n            GL.AttachShader(shader, _vertexObj);\n            GL.LinkProgram(shader);\n            return shader;\n        }\n        #endregion\n\n        #region Vars\n        int quadShader;\n        int evenquadShader;\n        int inverseQuadShader;\n\n        int vertexBufferID;\n        int colorBufferID;\n        int uvBufferID;\n        int texIDBufferID;\n\n        int quadBufferLength = 2048 * 64;\n        double[] quadVertexbuff;\n        float[] quadColorbuff;\n        double[] quadUVbuff;\n        float[] quadTexIDbuff;\n        int quadBufferPos = 0;\n\n        int indexBufferId;\n        uint[] indexes = new uint[2048 * 128 * 6];\n\n        double[] x1arrayKeys = new double[257];\n        double[] x1arrayNotes = new double[257];\n        double[] wdtharrayKeys = new double[257];\n        double[] wdtharrayNotes = new double[257];\n\n        int[] activeTexIds = new int[12];\n\n        public long lastScriptChangeTime = 0;\n\n        RenderSettings renderSettings;\n        Settings settings;\n\n        SettingsCtrl settingsControl;\n\n        Script currScript;\n\n        TextureShaders currentShader;\n        BlendFunc currentBlendFunc;\n        #endregion\n\n        public Render(RenderSettings settings)\n        {\n            renderSettings = settings;\n\n            this.settings = new Settings();\n            settingsControl = new SettingsCtrl(this.settings);\n            ((SettingsCtrl)SettingsControl).PaletteChanged += () => { ReloadTrackColors(); };\n            PreviewImage = PluginUtils.BitmapToImageSource(Properties.Resources.preview);\n        }\n\n        RenderOptions GetRenderOptions(double midiTime = 0)\n        {\n            return new RenderOptions()\n            {\n                firstKey = settings.firstNote,\n                lastKey = settings.lastNote,\n                midiTime = midiTime,\n                noteScreenTime = settings.deltaTimeOnScreen,\n\n                renderFPS = renderSettings.fps,\n                renderWidth = renderSettings.width,\n                renderHeight = renderSettings.height,\n                renderSSAA = renderSettings.downscale,\n                renderAspectRatio = renderSettings.width / (double)renderSettings.height,\n\n                midiPPQ = CurrentMidi.division,\n                midiTimeBased = renderSettings.timeBasedNotes,\n                midiTimeSignature = CurrentMidi.timeSig,\n                midiBarLength = CurrentMidi.division * CurrentMidi.timeSig.numerator / CurrentMidi.timeSig.denominator * 4\n            };\n        }\n\n        void UnloadScript(Script s)\n        {\n            foreach (var lt in s.textures)\n            {\n                GL.DeleteTexture(lt.texId);\n                lt.texId = -1;\n            }\n            foreach (var lt in s.fonts)\n            {\n                lt.engine.Dispose();\n                lt.engine = null;\n            }\n            if (s.hasPostRender) s.instance.RenderDispose();\n        }\n\n        void LoadScript(Script s)\n        {\n            CopyScriptValues();\n            foreach (var lt in s.textures)\n            {\n                lt.texId = GL.GenTexture();\n                loadImage(lt.bitmap, lt.texId, lt.looped, lt.linear);\n            }\n            foreach (var lt in s.fonts)\n            {\n                lt.engine = new GLTextEngine();\n                if(lt.charMap == null)\n                {\n                    lt.engine.SetFont(lt.fontName, (System.Drawing.FontStyle)lt.fontStyle, lt.fontPixelSize);\n                }\n                else\n                {\n                    lt.engine.SetFont(lt.fontName, (System.Drawing.FontStyle)lt.fontStyle, lt.fontPixelSize, lt.charMap);\n                }\n            }\n            if (s.hasPreRender) s.instance.RenderInit(GetRenderOptions());\n            CopyScriptValues();\n        }\n\n        void CheckScript(IEnumerable<Note> notes = null)\n        {\n            if (settings.lastScriptChangeTime > lastScriptChangeTime)\n            {\n                if (currScript != null)\n                {\n                    if (notes != null) foreach (var n in notes) n.meta = null;\n                    UnloadScript(currScript);\n                }\n                currScript = settings.currScript;\n                lastScriptChangeTime = settings.lastScriptChangeTime;\n                if (currScript != null) LoadScript(currScript);\n            }\n        }\n\n        public void Dispose()\n        {\n            GL.DeleteBuffers(3, new int[] { vertexBufferID, colorBufferID, uvBufferID, indexBufferId, texIDBufferID });\n\n            GL.DeleteProgram(quadShader);\n            GL.DeleteProgram(inverseQuadShader);\n            GL.DeleteProgram(evenquadShader);\n            quadVertexbuff = null;\n            quadColorbuff = null;\n            quadUVbuff = null;\n            Initialized = false;\n\n            UnloadScript(currScript);\n\n            Console.WriteLine(\"Disposed of ScriptedRender\");\n        }\n\n        public void Init()\n        {\n            quadShader = MakeShader(quadShaderVert, quadShaderFrag);\n            inverseQuadShader = MakeShader(quadShaderVert, invertQuadShaderFrag);\n            evenquadShader = MakeShader(quadShaderVert, evenQuadShaderFrag);\n\n            int loc;\n            int[] samplers = new int[12];\n            for (int i = 0; i < 12; i++)\n            {\n                samplers[i] = i;\n            }\n\n            GL.UseProgram(quadShader);\n            for (int i = 0; i < 12; i++)\n            {\n                loc = GL.GetUniformLocation(quadShader, \"textureSampler\" + (i + 1));\n                GL.Uniform1(loc, i);\n            }\n            GL.UseProgram(inverseQuadShader);\n            for (int i = 0; i < 12; i++)\n            {\n                loc = GL.GetUniformLocation(inverseQuadShader, \"textureSampler\" + (i + 1));\n                GL.Uniform1(loc, i);\n            }\n            GL.UseProgram(evenquadShader);\n            for (int i = 0; i < 12; i++)\n            {\n                loc = GL.GetUniformLocation(evenquadShader, \"textureSampler\" + (i + 1));\n                GL.Uniform1(loc, i);\n            }\n\n            quadVertexbuff = new double[quadBufferLength * 8];\n            quadColorbuff = new float[quadBufferLength * 16];\n            quadUVbuff = new double[quadBufferLength * 8];\n            quadTexIDbuff = new float[quadBufferLength * 4];\n\n            GL.GenBuffers(1, out vertexBufferID);\n            GL.GenBuffers(1, out colorBufferID);\n            GL.GenBuffers(1, out uvBufferID);\n            GL.GenBuffers(1, out texIDBufferID);\n            for (uint i = 0; i < indexes.Length / 6; i++)\n            {\n                indexes[i * 6 + 0] = i * 4 + 0;\n                indexes[i * 6 + 1] = i * 4 + 1;\n                indexes[i * 6 + 2] = i * 4 + 3;\n                indexes[i * 6 + 3] = i * 4 + 1;\n                indexes[i * 6 + 4] = i * 4 + 3;\n                indexes[i * 6 + 5] = i * 4 + 2;\n            }\n\n            GL.GenBuffers(1, out indexBufferId);\n            GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexBufferId);\n            GL.BufferData(\n                BufferTarget.ElementArrayBuffer,\n                (IntPtr)(indexes.Length * 4),\n                indexes,\n                BufferUsageHint.StaticDraw);\n\n            if (currScript != null)\n            {\n                try\n                {\n                    LoadScript(currScript);\n                }\n                catch\n                {\n                    currScript = null;\n                    lastScriptChangeTime = 0;\n                }\n            }\n            CheckScript();\n\n            IO.renderQuad = RenderQuad;\n            IO.renderShape = RenderShape;\n            IO.forceFlush = () => FlushQuadBuffer(false);\n            IO.selectTexShader = (s) =>\n            {\n                if (currentShader != s) FlushQuadBuffer(false);\n                if (s == TextureShaders.Normal) GL.UseProgram(quadShader);\n                if (s == TextureShaders.Inverted) GL.UseProgram(inverseQuadShader);\n                if (s == TextureShaders.Hybrid) GL.UseProgram(evenquadShader);\n                currentShader = s;\n            };\n            IO.setBlendFunc = (f) =>\n            {\n                if (currentBlendFunc != f) FlushQuadBuffer(false);\n                if (f == BlendFunc.Mix) OpenTK.Graphics.OpenGL4.GL.BlendFuncSeparate(OpenTK.Graphics.OpenGL4.BlendingFactorSrc.SrcAlpha, OpenTK.Graphics.OpenGL4.BlendingFactorDest.OneMinusSrcAlpha, OpenTK.Graphics.OpenGL4.BlendingFactorSrc.One, OpenTK.Graphics.OpenGL4.BlendingFactorDest.One);\n                if (f == BlendFunc.Add) OpenTK.Graphics.OpenGL4.GL.BlendFuncSeparate(OpenTK.Graphics.OpenGL4.BlendingFactorSrc.SrcAlpha, OpenTK.Graphics.OpenGL4.BlendingFactorDest.One, OpenTK.Graphics.OpenGL4.BlendingFactorSrc.One, OpenTK.Graphics.OpenGL4.BlendingFactorDest.One);\n                currentBlendFunc = f;\n            };\n            IO.getTextSize = (Font f, string t) =>\n            {\n                var bb = f.engine.GetBoundBox(t);\n                return bb.Width / (double)bb.Height / renderSettings.width * renderSettings.height;\n            };\n            IO.renderText = (double left, double bottom, double height, Color4 color, Font f, string text) =>\n            {\n                if (text.Contains(\"\\n\")) throw new Exception(\"New line characters not allowed when rendering text (yet)\");\n\n                FlushQuadBuffer(false);\n\n                var size = f.engine.GetBoundBox(text);\n                Matrix4 transform = Matrix4.Identity;\n                transform = Matrix4.Mult(transform, Matrix4.CreateScale(1.0f / renderSettings.width * renderSettings.height / f.fontPixelSize * (float)height, -1.0f / f.fontPixelSize * (float)height, 1.0f));\n                transform = Matrix4.Mult(transform, Matrix4.CreateTranslation((float)left * 2 - 1, (float)(bottom + height * 0.7) * 2 - 1, 0));\n                \n                f.engine.Render(text, transform, color);\n\n                if (currentShader == TextureShaders.Normal) GL.UseProgram(quadShader);\n                if (currentShader == TextureShaders.Inverted) GL.UseProgram(inverseQuadShader);\n                if (currentShader == TextureShaders.Hybrid) GL.UseProgram(evenquadShader);\n            };\n\n            Initialized = true;\n            Console.WriteLine(\"Initialised ScriptedRender\");\n        }\n\n        void CopyScriptValues()\n        {\n            if (currScript == null) return;\n            if (currScript.hasManualNoteDelete) ManualNoteDelete = (bool)currScript.instance.ManualNoteDelete;\n            else ManualNoteDelete = false;\n            if (currScript.hasCollectorOffset) NoteCollectorOffset = (double)currScript.instance.NoteCollectorOffset;\n            else NoteCollectorOffset = 0;\n            if (currScript.hasNoteCount) LastNoteCount = (long)currScript.instance.LastNoteCount;\n            else LastNoteCount = 0;\n            if (currScript.hasNoteScreenTime) NoteScreenTime = (double)currScript.instance.NoteScreenTime;\n            else NoteScreenTime = settings.deltaTimeOnScreen;\n        }\n\n        public void RenderFrame(FastList<Note> notes, double midiTime, int finalCompositeBuff)\n        {\n            CheckScript(notes);\n\n            if (currScript == null || currScript.error) return;\n\n\n            GL.Enable(EnableCap.Blend);\n            GL.EnableClientState(ArrayCap.VertexArray);\n            GL.EnableClientState(ArrayCap.ColorArray);\n            GL.EnableClientState(ArrayCap.TextureCoordArray);\n            GL.Enable(EnableCap.Texture2D);\n\n            GL.EnableVertexAttribArray(0);\n            GL.EnableVertexAttribArray(1);\n            GL.EnableVertexAttribArray(2);\n            GL.EnableVertexAttribArray(3);\n\n            GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);\n            GL.BlendEquationSeparate(BlendEquationMode.FuncAdd, BlendEquationMode.Max);\n            OpenTK.Graphics.OpenGL4.GL.BlendEquationSeparate(OpenTK.Graphics.OpenGL4.BlendEquationMode.FuncAdd, OpenTK.Graphics.OpenGL4.BlendEquationMode.FuncAdd);\n            OpenTK.Graphics.OpenGL4.GL.BlendFuncSeparate(OpenTK.Graphics.OpenGL4.BlendingFactorSrc.SrcAlpha, OpenTK.Graphics.OpenGL4.BlendingFactorDest.OneMinusSrcAlpha, OpenTK.Graphics.OpenGL4.BlendingFactorSrc.One, OpenTK.Graphics.OpenGL4.BlendingFactorDest.One);\n            GL.BindFramebuffer(FramebufferTarget.Framebuffer, finalCompositeBuff);\n            GL.Viewport(0, 0, renderSettings.width, renderSettings.height);\n            GL.Clear(ClearBufferMask.ColorBufferBit);\n\n            #region Vars\n            for (int i = 0; i < 12; i++) activeTexIds[i] = -1;\n            currentShader = TextureShaders.Normal;\n            currentBlendFunc = BlendFunc.Mix;\n\n            #endregion\n\n            GL.UseProgram(quadShader);\n            currentShader = TextureShaders.Normal;\n\n            var options = GetRenderOptions(midiTime);\n\n            CopyScriptValues();\n            currScript.instance.Render((IEnumerable<Note>)notes, options);\n            CopyScriptValues();\n\n            FlushQuadBuffer(false);\n\n            OpenTK.Graphics.OpenGL4.GL.BlendEquationSeparate(OpenTK.Graphics.OpenGL4.BlendEquationMode.FuncAdd, OpenTK.Graphics.OpenGL4.BlendEquationMode.FuncAdd);\n\n            GL.ActiveTexture(TextureUnit.Texture0);\n            GL.Disable(EnableCap.Blend);\n            GL.DisableClientState(ArrayCap.VertexArray);\n            GL.DisableClientState(ArrayCap.ColorArray);\n            GL.DisableClientState(ArrayCap.TextureCoordArray);\n            GL.Disable(EnableCap.Texture2D);\n\n            GL.DisableVertexAttribArray(0);\n            GL.DisableVertexAttribArray(1);\n            GL.DisableVertexAttribArray(2);\n            GL.DisableVertexAttribArray(3);\n        }\n\n        void RenderQuad(double left, double top, double right, double bottom, Color4 topLeft, Color4 topRight, Color4 bottomRight, Color4 bottomLeft, Texture tex, double uvLeft, double uvTop, double uvRight, double uvBottom)\n        {\n            int texSlot = -1;\n            if (tex != null) texSlot = FindTexSlot(tex.texId);\n\n            int pos = quadBufferPos * 8;\n            quadVertexbuff[pos++] = left;\n            quadVertexbuff[pos++] = top;\n            quadVertexbuff[pos++] = right;\n            quadVertexbuff[pos++] = top;\n            quadVertexbuff[pos++] = right;\n            quadVertexbuff[pos++] = bottom;\n            quadVertexbuff[pos++] = left;\n            quadVertexbuff[pos++] = bottom;\n\n            pos = quadBufferPos * 16;\n            quadColorbuff[pos++] = topLeft.R;\n            quadColorbuff[pos++] = topLeft.G;\n            quadColorbuff[pos++] = topLeft.B;\n            quadColorbuff[pos++] = topLeft.A;\n            quadColorbuff[pos++] = topRight.R;\n            quadColorbuff[pos++] = topRight.G;\n            quadColorbuff[pos++] = topRight.B;\n            quadColorbuff[pos++] = topRight.A;\n            quadColorbuff[pos++] = bottomRight.R;\n            quadColorbuff[pos++] = bottomRight.G;\n            quadColorbuff[pos++] = bottomRight.B;\n            quadColorbuff[pos++] = bottomRight.A;\n            quadColorbuff[pos++] = bottomLeft.R;\n            quadColorbuff[pos++] = bottomLeft.G;\n            quadColorbuff[pos++] = bottomLeft.B;\n            quadColorbuff[pos++] = bottomLeft.A;\n\n            pos = quadBufferPos * 8;\n            quadUVbuff[pos++] = uvLeft;\n            quadUVbuff[pos++] = uvTop;\n            quadUVbuff[pos++] = uvRight;\n            quadUVbuff[pos++] = uvTop;\n            quadUVbuff[pos++] = uvRight;\n            quadUVbuff[pos++] = uvBottom;\n            quadUVbuff[pos++] = uvLeft;\n            quadUVbuff[pos++] = uvBottom;\n\n            pos = quadBufferPos * 4;\n            quadTexIDbuff[pos++] = texSlot;\n            quadTexIDbuff[pos++] = texSlot;\n            quadTexIDbuff[pos++] = texSlot;\n            quadTexIDbuff[pos++] = texSlot;\n            quadBufferPos++;\n            FlushQuadBuffer(true);\n        }\n\n        void RenderShape(Vector2d v1, Vector2d v2, Vector2d v3, Vector2d v4, Color4 c1, Color4 c2, Color4 c3, Color4 c4, Texture tex, Vector2d uv1, Vector2d uv2, Vector2d uv3, Vector2d uv4)\n        {\n            int texSlot = -1;\n            if (tex != null) texSlot = FindTexSlot(tex.texId);\n\n            int pos = quadBufferPos * 8;\n            quadVertexbuff[pos++] = v1.X;\n            quadVertexbuff[pos++] = v1.Y;\n            quadVertexbuff[pos++] = v2.X;\n            quadVertexbuff[pos++] = v2.Y;\n            quadVertexbuff[pos++] = v3.X;\n            quadVertexbuff[pos++] = v3.Y;\n            quadVertexbuff[pos++] = v4.X;\n            quadVertexbuff[pos++] = v4.Y;\n\n            pos = quadBufferPos * 16;\n            quadColorbuff[pos++] = c1.R;\n            quadColorbuff[pos++] = c1.G;\n            quadColorbuff[pos++] = c1.B;\n            quadColorbuff[pos++] = c1.A;\n            quadColorbuff[pos++] = c2.R;\n            quadColorbuff[pos++] = c2.G;\n            quadColorbuff[pos++] = c2.B;\n            quadColorbuff[pos++] = c2.A;\n            quadColorbuff[pos++] = c3.R;\n            quadColorbuff[pos++] = c3.G;\n            quadColorbuff[pos++] = c3.B;\n            quadColorbuff[pos++] = c3.A;\n            quadColorbuff[pos++] = c4.R;\n            quadColorbuff[pos++] = c4.G;\n            quadColorbuff[pos++] = c4.B;\n            quadColorbuff[pos++] = c4.A;\n\n            pos = quadBufferPos * 8;\n            quadUVbuff[pos++] = uv1.X;\n            quadUVbuff[pos++] = uv1.Y;\n            quadUVbuff[pos++] = uv2.X;\n            quadUVbuff[pos++] = uv2.Y;\n            quadUVbuff[pos++] = uv3.X;\n            quadUVbuff[pos++] = uv3.Y;\n            quadUVbuff[pos++] = uv4.X;\n            quadUVbuff[pos++] = uv4.Y;\n\n            pos = quadBufferPos * 4;\n            quadTexIDbuff[pos++] = texSlot;\n            quadTexIDbuff[pos++] = texSlot;\n            quadTexIDbuff[pos++] = texSlot;\n            quadTexIDbuff[pos++] = texSlot;\n            quadBufferPos++;\n            FlushQuadBuffer(true);\n        }\n\n        int FindTexSlot(int id)\n        {\n            for (int i = 0; i < 12; i++)\n            {\n                if (activeTexIds[i] == id) return i;\n                if (activeTexIds[i] == -1)\n                {\n                    activeTexIds[i] = id;\n                    return i;\n                }\n            }\n            FlushQuadBuffer(false);\n            activeTexIds[0] = id;\n            return 0;\n        }\n\n        void FlushQuadBuffer(bool check = true, bool forceShader = true)\n        {\n            if (quadBufferPos < quadBufferLength && check) return;\n            if (quadBufferPos == 0) return;\n\n            if (forceShader)\n            {\n                if (currentShader == TextureShaders.Normal) GL.UseProgram(quadShader);\n                if (currentShader == TextureShaders.Inverted) GL.UseProgram(inverseQuadShader);\n                if (currentShader == TextureShaders.Hybrid) GL.UseProgram(evenquadShader);\n            }\n\n            for (int i = 0; i < 12 && activeTexIds[i] != -1; i++)\n            {\n                GL.ActiveTexture(TextureUnit.Texture0 + i);\n                GL.BindTexture(TextureTarget.Texture2D, activeTexIds[i]);\n                activeTexIds[i] = -1;\n            }\n\n            GL.BindBuffer(BufferTarget.ArrayBuffer, vertexBufferID);\n            GL.BufferData(\n                BufferTarget.ArrayBuffer,\n                (IntPtr)(quadBufferPos * 8 * 8),\n                quadVertexbuff,\n                BufferUsageHint.StaticDraw);\n            GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Double, false, 16, 0);\n            GL.BindBuffer(BufferTarget.ArrayBuffer, colorBufferID);\n            GL.BufferData(\n                BufferTarget.ArrayBuffer,\n                (IntPtr)(quadBufferPos * 16 * 4),\n                quadColorbuff,\n                BufferUsageHint.StaticDraw);\n            GL.VertexAttribPointer(1, 4, VertexAttribPointerType.Float, false, 16, 0);\n            GL.BindBuffer(BufferTarget.ArrayBuffer, uvBufferID);\n            GL.BufferData(\n                BufferTarget.ArrayBuffer,\n                (IntPtr)(quadBufferPos * 8 * 8),\n                quadUVbuff,\n                BufferUsageHint.StaticDraw);\n            GL.VertexAttribPointer(2, 2, VertexAttribPointerType.Double, false, 16, 0);\n            GL.BindBuffer(BufferTarget.ArrayBuffer, texIDBufferID);\n            GL.BufferData(\n                BufferTarget.ArrayBuffer,\n                (IntPtr)(quadBufferPos * 1 * 4 * 4),\n                quadTexIDbuff,\n                BufferUsageHint.StaticDraw);\n            GL.VertexAttribPointer(3, 1, VertexAttribPointerType.Float, false, 4, 0);\n            GL.VertexAttribPointer(3, 1, VertexAttribPointerType.Float, false, 4, 0);\n            GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexBufferId);\n            GL.IndexPointer(IndexPointerType.Int, 1, 0);\n            GL.DrawElements(PrimitiveType.Triangles, quadBufferPos * 6, DrawElementsType.UnsignedInt, IntPtr.Zero);\n            quadBufferPos = 0;\n            for (int i = 0; i < 12; i++) activeTexIds[i] = -1;\n        }\n\n        void loadImage(Bitmap image, int texID, bool loop, bool linear = false)\n        {\n            GL.BindTexture(TextureTarget.Texture2D, texID);\n            BitmapData data = image.LockBits(new System.Drawing.Rectangle(0, 0, image.Width, image.Height),\n                ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);\n\n            GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, data.Width, data.Height, 0,\n                OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);\n\n            if (linear)\n            {\n                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);\n                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);\n            }\n            else\n            {\n                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest);\n                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest);\n            }\n            if (loop)\n            {\n                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);\n                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);\n            }\n            else\n            {\n                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge);\n                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge);\n            }\n\n            image.UnlockBits(data);\n        }\n\n        public void ReloadTrackColors()\n        {\n            if (NoteColors == null) return;\n            var cols = ((SettingsCtrl)SettingsControl).paletteList.GetColors(NoteColors.Length);\n\n            for (int i = 0; i < NoteColors.Length; i++)\n            {\n                for (int j = 0; j < NoteColors[i].Length; j++)\n                {\n                    if (NoteColors[i][j].isDefault)\n                    {\n                        NoteColors[i][j].left = cols[i * 32 + j * 2];\n                        NoteColors[i][j].right = cols[i * 32 + j * 2 + 1];\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "ScriptedRenderer/Script.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Drawing;\nusing System.Linq;\nusing System.Reflection;\nusing System.Text;\nusing System.Threading.Tasks;\nusing ScriptedEngine;\nusing Font = ScriptedEngine.Font;\n\nnamespace ScriptedRender\n{\n    public enum ScriptType\n    {\n        Folder, Zip, Zrp, Rar, SevenZip, Tar\n    }\n\n    public class Script\n    {\n        public string filepath;\n        public ScriptType filetype;\n\n        public string name;\n        public string description = \"\";\n        public bool error = false;\n        public Bitmap preview = null;\n\n        public List<Texture> textures = new List<Texture>();\n        public List<Font> fonts = new List<Font>();\n\n        public bool hasPreRender = false;\n        public bool hasPostRender = false;\n\n        public bool hasCollectorOffset = false;\n        public bool hasManualNoteDelete = false;\n        public bool hasNoteScreenTime = false;\n        public bool hasNoteCount = false;\n\n        public bool hasProfiles = false;\n\n        public dynamic instance;\n        public Type renderType;\n\n        public IEnumerable<UISetting> uiSettings = null;\n    }\n}\n"
  },
  {
    "path": "ScriptedRenderer/ScriptedRender.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProjectGuid>{95F774EB-47A2-40B3-A0CE-E616BBD4D62B}</ProjectGuid>\n    <OutputType>Library</OutputType>\n    <AppDesignerFolder>Properties</AppDesignerFolder>\n    <RootNamespace>ScriptedRender</RootNamespace>\n    <AssemblyName>ScriptedRender</AssemblyName>\n    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>\n    <FileAlignment>512</FileAlignment>\n    <Deterministic>true</Deterministic>\n    <TargetFrameworkProfile />\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>..\\bin\\Any cpu\\Debug\\Plugins\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>..\\bin\\Any cpu\\Release\\Plugins\\</OutputPath>\n    <DefineConstants>\n    </DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <PlatformTarget>x64</PlatformTarget>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Debug|x86'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>..\\bin\\x86\\Debug\\Plugins\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <DebugType>full</DebugType>\n    <PlatformTarget>x86</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Release|x86'\">\n    <OutputPath>..\\bin\\x86\\Release\\Plugins\\</OutputPath>\n    <DefineConstants>\n    </DefineConstants>\n    <Optimize>true</Optimize>\n    <DebugType>pdbonly</DebugType>\n    <PlatformTarget>x86</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Debug|x64'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>..\\bin\\x64\\Debug\\Plugins\\</OutputPath>\n    <DefineConstants>TRACE;DEBUG</DefineConstants>\n    <DebugType>full</DebugType>\n    <PlatformTarget>x64</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Release|x64'\">\n    <OutputPath>..\\bin\\x64\\Release\\Plugins\\</OutputPath>\n    <DefineConstants>\n    </DefineConstants>\n    <Optimize>true</Optimize>\n    <DebugType>pdbonly</DebugType>\n    <PlatformTarget>x64</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Newtonsoft.Json.12.0.3\\lib\\net45\\Newtonsoft.Json.dll</HintPath>\n    </Reference>\n    <Reference Include=\"OpenTK, Version=3.1.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\OpenTK.3.1.0\\lib\\net20\\OpenTK.dll</HintPath>\n    </Reference>\n    <Reference Include=\"PresentationCore\" />\n    <Reference Include=\"PresentationFramework\" />\n    <Reference Include=\"SharpCompress, Version=0.24.0.0, Culture=neutral, PublicKeyToken=afb0a02973931d96, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\SharpCompress.0.24.0\\lib\\net45\\SharpCompress.dll</HintPath>\n    </Reference>\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Core\" />\n    <Reference Include=\"System.Drawing\" />\n    <Reference Include=\"System.IO.Compression\" />\n    <Reference Include=\"System.IO.Compression.FileSystem\" />\n    <Reference Include=\"System.Xaml\" />\n    <Reference Include=\"System.Xml.Linq\" />\n    <Reference Include=\"System.Data.DataSetExtensions\" />\n    <Reference Include=\"Microsoft.CSharp\" />\n    <Reference Include=\"System.Data\" />\n    <Reference Include=\"System.Net.Http\" />\n    <Reference Include=\"System.Xml\" />\n    <Reference Include=\"WindowsBase\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"Properties\\AssemblyInfo.cs\" />\n    <Compile Include=\"Properties\\Resources.Designer.cs\">\n      <AutoGen>True</AutoGen>\n      <DesignTime>True</DesignTime>\n      <DependentUpon>Resources.resx</DependentUpon>\n    </Compile>\n    <Compile Include=\"Render.cs\" />\n    <Compile Include=\"Script.cs\" />\n    <Compile Include=\"Settings.cs\" />\n    <Compile Include=\"SettingsCtrl.xaml.cs\">\n      <DependentUpon>SettingsCtrl.xaml</DependentUpon>\n    </Compile>\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\BMEngine\\ZenithEngine.csproj\">\n      <Project>{60f2212a-d56c-4776-836f-6a49453cdbc4}</Project>\n      <Name>ZenithEngine</Name>\n    </ProjectReference>\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"OpenTK.dll.config\" />\n    <None Include=\"packages.config\" />\n    <None Include=\"Resources\\Examples.zip\" />\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Languages\\en\\scripted.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </EmbeddedResource>\n    <EmbeddedResource Include=\"Properties\\Resources.resx\">\n      <Generator>ResXFileCodeGenerator</Generator>\n      <LastGenOutput>Resources.Designer.cs</LastGenOutput>\n    </EmbeddedResource>\n    <Page Include=\"SettingsCtrl.xaml\">\n      <SubType>Designer</SubType>\n      <Generator>MSBuild:Compile</Generator>\n    </Page>\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"Resources\\preview.png\" />\n  </ItemGroup>\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n</Project>"
  },
  {
    "path": "ScriptedRenderer/Settings.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace ScriptedRender\n{\n    public class Settings\n    {\n        public int firstNote = 0;\n        public int lastNote = 128;\n        public double deltaTimeOnScreen = 300;\n\n        public string palette = \"Random\";\n\n        public Script currScript = null;\n        public long lastScriptChangeTime = 0;\n    }\n}\n"
  },
  {
    "path": "ScriptedRenderer/SettingsCtrl.xaml",
    "content": "﻿<UserControl x:Class=\"ScriptedRender.SettingsCtrl\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" \n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\" \n             xmlns:ui=\"clr-namespace:ZenithEngine.UI;assembly=ZenithEngine\"\n             xmlns:local=\"clr-namespace:ScriptedRender\"\n             xmlns:bme=\"clr-namespace:ZenithEngine;assembly=ZenithEngine\"\n             mc:Ignorable=\"d\" \n             d:DesignHeight=\"450\" d:DesignWidth=\"800\">\n    <UserControl.Resources>\n        <ResourceDictionary>\n            <ResourceDictionary.MergedDictionaries>\n                <ResourceDictionary>\n                    <ResourceDictionary.MergedDictionaries>\n                        <ResourceDictionary Source=\"pack://siteoforigin:,,,/Languages/en/scripted.xaml\" />\n                    </ResourceDictionary.MergedDictionaries>\n                </ResourceDictionary>\n                <ResourceDictionary Source=\"pack://application:,,,/ZenithEngine;component/UI/Material.xaml\"/>\n            </ResourceDictionary.MergedDictionaries>\n            <Style TargetType=\"TabControl\" BasedOn=\"{StaticResource SubTabs}\"/>\n            <Style TargetType=\"TabItem\" BasedOn=\"{StaticResource SubTabItems}\"/>\n        </ResourceDictionary>\n    </UserControl.Resources>\n    <Grid>\n        <TabControl Margin=\"10\">\n            <TabItem Header=\"{DynamicResource resourcesTab}\">\n                <Grid Margin=\"10\">\n                    <Grid.ColumnDefinitions>\n                        <ColumnDefinition Width=\"204*\"/>\n                        <ColumnDefinition Width=\"371*\"/>\n                        <ColumnDefinition Width=\"199*\"/>\n                    </Grid.ColumnDefinitions>\n                    <bme:NoteColorPalettePick x:FieldModifier=\"public\" x:Name=\"paletteList\" Margin=\"0\" Grid.Column=\"2\" Grid.RowSpan=\"2\" />\n                    <DockPanel LastChildFill=\"True\" Grid.RowSpan=\"2\">\n                        <Button x:Name=\"reloadListButton\" Content=\"{DynamicResource reloadList}\" Margin=\"0,0,0,0\" Height=\"26\" DockPanel.Dock=\"Top\" Click=\"ReloadButton_Click\"/>\n                        <Button x:Name=\"reloadPackButton\" Content=\"{DynamicResource reloadScript}\" Margin=\"0,10,0,0\" Height=\"26\" DockPanel.Dock=\"Top\" Click=\"ReloadPackButton_Click\"/>\n                        <Button x:Name=\"openFolderButton\" Content=\"{DynamicResource openFolder}\" Margin=\"0,10,0,0\" Height=\"26\" DockPanel.Dock=\"Bottom\" Click=\"openFolderButton_Click\"/>\n                        <ListBox x:Name=\"pluginList\" Margin=\"0,10,0,0\" SelectionChanged=\"PluginList_SelectionChanged\"/>\n                    </DockPanel>\n                    <DockPanel Grid.Column=\"1\" LastChildFill=\"True\">\n                        <TextBox MinHeight=\"100\" DockPanel.Dock=\"Bottom\" TextAlignment=\"Center\" x:Name=\"pluginDesc\" Grid.Column=\"1\" Margin=\"10,10,10,0\" TextWrapping=\"Wrap\" IsEnabled=\"False\" Grid.Row=\"1\"/>\n                        <Image x:Name=\"previewImg\" Grid.Column=\"1\" Margin=\"10,0,10,0\"/>\n                    </DockPanel>\n                </Grid>\n            </TabItem>\n            <TabItem Header=\"{DynamicResource settingsTab}\" Name=\"switchTab\">\n                <DockPanel Margin=\"10,10,10,0\">\n                    <DockPanel Visibility=\"Collapsed\" Name=\"profileDock\" DockPanel.Dock=\"Top\" LastChildFill=\"False\" Margin=\"0,0,0,10\">\n                        <Label Content=\"{DynamicResource profile}\"/>\n                        <ComboBox DropDownOpened=\"profileSelect_DropDownOpened\" Name=\"profileSelect\" Width=\"120\" Margin=\"5,0,0,0\" SelectionChanged=\"profileSelect_SelectionChanged\"/>\n                        <TextBox Name=\"profileName\" Margin=\"5,0,0,0\" Width=\"120\" />\n                        <Button Name=\"saveProfile\" Margin=\"5,0,0,0\" Padding=\"20,0\" Content=\"{DynamicResource save}\" Click=\"saveProfile_Click\"/>\n                        <Button Name=\"deleteProfile\" Margin=\"5,0,0,0\" Padding=\"20,0\" Content=\"{DynamicResource delete}\" Click=\"deleteProfile_Click\" />\n                        <Button Name=\"profileDefaults\" DockPanel.Dock=\"Right\" Padding=\"20,0\" Content=\"{DynamicResource defaults}\" Click=\"profileDefaults_Click\"/>\n                    </DockPanel>\n                    <DockPanel Name=\"settingsPanel\" LastChildFill=\"False\">\n\n                    </DockPanel>\n                </DockPanel>\n            </TabItem>\n            <TabItem Header=\"{DynamicResource miscTab}\">\n                <StackPanel Margin=\"10\">\n                    <DockPanel HorizontalAlignment=\"Left\" LastChildFill=\"False\" Margin=\"0,0,0,0\" VerticalAlignment=\"Top\">\n                        <Label Content=\"{DynamicResource firstNote}\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Top\" DockPanel.Dock=\"Left\"/>\n                        <ui:NumberSelect x:Name=\"firstNote\" Value=\"0\" Maximum=\"254\" Minimum=\"0\" Margin=\"5,0,0,0\" HorizontalAlignment=\"Left\" Width=\"80\" Height=\"26\" VerticalAlignment=\"Top\" ValueChanged=\"Nud_ValueChanged\"  />\n                        <Label Content=\"{DynamicResource lastNote}\" HorizontalAlignment=\"Left\" Margin=\"10,0,0,0\" VerticalAlignment=\"Top\"/>\n                        <ui:NumberSelect x:Name=\"lastNote\" Value=\"127\" Maximum=\"255\" Minimum=\"1\" Margin=\"5,0,0,0\" HorizontalAlignment=\"Left\" Width=\"80\" Height=\"26\" VerticalAlignment=\"Top\" ValueChanged=\"Nud_ValueChanged\"  />\n                    </DockPanel>\n                    <DockPanel HorizontalAlignment=\"Left\" LastChildFill=\"False\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\" Width=\"528\" >\n                        <Label Content=\"{DynamicResource noteScreenTime}\" HorizontalAlignment=\"Left\" Margin=\"0,0,0,0\" VerticalAlignment=\"Top\"/>\n                        <ui:ValueSlider x:Name=\"noteDeltaScreenTime\" Maximum=\"12\" DecimalPoints=\"2\" Minimum=\"2\" TrueMin=\"1\" TrueMax=\"100000\" ValueChanged=\"NoteDeltaScreenTime_ValueChanged\" Width=\"305\" VerticalAlignment=\"Top\"/>\n                    </DockPanel>\n                </StackPanel>\n            </TabItem>\n        </TabControl>\n    </Grid>\n</UserControl>\n"
  },
  {
    "path": "ScriptedRenderer/SettingsCtrl.xaml.cs",
    "content": "﻿using Microsoft.CSharp;\nusing Microsoft.CSharp.RuntimeBinder;\nusing Newtonsoft.Json.Linq;\nusing SharpCompress;\nusing SharpCompress.Archives;\nusing SharpCompress.Archives.Rar;\nusing SharpCompress.Archives.SevenZip;\nusing SharpCompress.Archives.Tar;\nusing System;\nusing System.CodeDom.Compiler;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Drawing;\nusing System.Dynamic;\nusing System.IO;\nusing System.IO.Compression;\nusing System.Linq;\nusing System.Reflection;\nusing System.Security.Cryptography;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\nusing ScriptedEngine;\nusing Brushes = System.Windows.Media.Brushes;\nusing Path = System.IO.Path;\nusing ZenithEngine.UI;\nusing System.Threading;\nusing Newtonsoft.Json;\nusing Font = ScriptedEngine.Font;\n\nnamespace ScriptedRender\n{\n    /// <summary>\n    /// Interaction logic for SettingsCtrl.xaml\n    /// </summary>\n\n    class ScriptLocation\n    {\n        public string filename;\n        public ScriptType type;\n    }\n\n    public partial class SettingsCtrl : UserControl\n    {\n        List<ScriptLocation> resourcePacks = new List<ScriptLocation>();\n        Settings settings;\n\n        public event Action PaletteChanged\n        {\n            add { paletteList.PaletteChanged += value; }\n            remove { paletteList.PaletteChanged -= value; }\n        }\n\n        JObject packProfiles = null;\n        string profilesDir;\n        List<Control> settingsControls = new List<Control>();\n        List<UISetting> settingsMeta = new List<UISetting>();\n        List<object[]> profiles = new List<object[]>();\n\n        string packPath = \"Plugins\\\\Assets\\\\Scripted\\\\Resources\";\n\n        CSharpCodeProvider provider = new CSharpCodeProvider();\n\n        BitmapImage BitmapToImageSource(Bitmap bitmap)\n        {\n            using (MemoryStream memory = new MemoryStream())\n            {\n                bitmap.Save(memory, System.Drawing.Imaging.ImageFormat.Bmp);\n                memory.Position = 0;\n                BitmapImage bitmapimage = new BitmapImage();\n                bitmapimage.BeginInit();\n                bitmapimage.StreamSource = memory;\n                bitmapimage.CacheOption = BitmapCacheOption.OnLoad;\n                bitmapimage.EndInit();\n\n                return bitmapimage;\n            }\n        }\n\n        bool inited = false;\n        public SettingsCtrl(Settings settings)\n        {\n            this.settings = settings;\n            InitializeComponent();\n            noteDeltaScreenTime.nudToSlider = v => Math.Log(v, 2);\n            noteDeltaScreenTime.sliderToNud = v => Math.Pow(2, v);\n            inited = true;\n            paletteList.SetPath(\"Plugins\\\\Assets\\\\Palettes\", 1f);\n            ReloadPacks();\n            SetValues();\n        }\n\n        void WriteDefaultPack()\n        {\n            var stream = new MemoryStream(Properties.Resources.Examples);\n            var archive = new ZipArchive(stream, ZipArchiveMode.Read);\n            foreach (var e in archive.Entries)\n            {\n                var path = Path.Combine(\"Plugins/Assets/Scripted/Resources\", e.FullName);\n                if (!Directory.Exists(Path.GetDirectoryName(path))) Directory.CreateDirectory(Path.GetDirectoryName(path));\n                if (e.FullName.EndsWith(\"/\")) continue;\n                if (File.Exists(path)) continue;\n                var f = File.OpenWrite(path);\n                var es = e.Open();\n                es.CopyTo(f);\n                f.Dispose();\n                es.Dispose();\n            }\n            archive.Dispose();\n            stream.Dispose();\n        }\n\n        void ReloadPacks()\n        {\n            int lastSelected = pluginList.SelectedIndex;\n            string lastSelectedName;\n            if (lastSelected == -1)\n            {\n                lastSelectedName = \"Example Flat\";\n                lastSelected = 0;\n            }\n            else\n            {\n                lastSelectedName = (string)((ListBoxItem)pluginList.SelectedItem).Content;\n            }\n\n            string dir = packPath;\n\n            resourcePacks.Clear();\n            WriteDefaultPack();\n            pluginList.Items.Clear();\n            foreach (var p in Directory.GetDirectories(dir))\n            {\n                resourcePacks.Add(new ScriptLocation() { filename = p, type = ScriptType.Folder });\n            }\n            foreach (var p in Directory.GetFiles(dir, \"*.zip\"))\n            {\n                resourcePacks.Add(new ScriptLocation() { filename = p, type = ScriptType.Zip });\n            }\n            foreach (var p in Directory.GetFiles(dir, \"*.zrp\"))\n            {\n                resourcePacks.Add(new ScriptLocation() { filename = p, type = ScriptType.Zrp });\n            }\n            foreach (var p in Directory.GetFiles(dir, \"*.rar\"))\n            {\n                resourcePacks.Add(new ScriptLocation() { filename = p, type = ScriptType.Rar });\n            }\n            foreach (var p in Directory.GetFiles(dir, \"*.7z\"))\n            {\n                resourcePacks.Add(new ScriptLocation() { filename = p, type = ScriptType.SevenZip });\n            }\n            foreach (var p in Directory.GetFiles(dir, \"*.tar\"))\n            {\n                resourcePacks.Add(new ScriptLocation() { filename = p, type = ScriptType.Tar });\n            }\n            foreach (var p in Directory.GetFiles(dir, \"*.tar.bz\"))\n            {\n                resourcePacks.Add(new ScriptLocation() { filename = p, type = ScriptType.Tar });\n            }\n            foreach (var p in Directory.GetFiles(dir, \"*.tar.gz\"))\n            {\n                resourcePacks.Add(new ScriptLocation() { filename = p, type = ScriptType.Tar });\n            }\n            foreach (var p in Directory.GetFiles(dir, \"*.tar.xz\"))\n            {\n                resourcePacks.Add(new ScriptLocation() { filename = p, type = ScriptType.Tar });\n            }\n\n            resourcePacks.Sort((a, b) =>\n            {\n                return a.filename.CompareTo(b.filename);\n            });\n\n            foreach (var p in resourcePacks)\n            {\n                if (p.type == ScriptType.Folder)\n                    pluginList.Items.Add(new ListBoxItem()\n                    {\n                        Content = p.filename.Split('\\\\').Last(),\n                        Foreground = Brushes.White\n                    });\n                else\n                    pluginList.Items.Add(new ListBoxItem()\n                    {\n                        Content = p.filename.Split('\\\\').Last(),\n                        Foreground = Brushes.Green\n                    });\n            }\n\n            if ((string)((ListBoxItem)pluginList.Items[lastSelected]).Content == lastSelectedName)\n            {\n                pluginList.SelectedIndex = lastSelected;\n            }\n            else\n            {\n                foreach (ListBoxItem p in pluginList.Items)\n                {\n                    if ((string)p.Content == lastSelectedName)\n                    {\n                        pluginList.SelectedItem = p;\n                        break;\n                    }\n                }\n            }\n        }\n\n        void UnloadScript(Script r)\n        {\n            foreach (var lt in r.textures) lt.bitmap.Dispose();\n        }\n\n        Script LoadScript(string p, ScriptType type, Dictionary<string, string> switches = null, Dictionary<string, string[]> assertSwitches = null)\n        {\n            var script = new Script() { name = p.Split('\\\\').Last() };\n            string pbase = \"\";\n\n            ZipArchive archive = null;\n            IArchive compress = null;\n            archive = null;\n            MemoryStream zrpstream = null;\n\n            Bitmap GetBitmap(string path)\n            {\n                if (type == ScriptType.Folder)\n                {\n                    if (path == null)\n                    { }\n                    path = Path.Combine(p, pbase, path);\n                    FileStream s;\n                    try\n                    {\n                        s = File.OpenRead(path);\n                    }\n                    catch { throw new Exception(\"Could not open \" + path); }\n                    Bitmap b;\n                    try\n                    {\n                        b = new Bitmap(s);\n                    }\n                    catch { throw new Exception(\"Corrupt image: \" + path); }\n                    s.Close();\n                    return b;\n                }\n                else if (type == ScriptType.Zip || type == ScriptType.Zrp)\n                {\n                    path = Path.Combine(pbase, path);\n                    Stream s;\n                    try\n                    {\n                        s = archive.GetEntry(path).Open();\n                    }\n                    catch { throw new Exception(\"Could not open \" + path); }\n                    Bitmap b;\n                    try\n                    {\n                        b = new Bitmap(s);\n                    }\n                    catch { throw new Exception(\"Corrupt image: \" + path); }\n                    s.Close();\n                    return b;\n                }\n                else\n                {\n                    path = Path.Combine(pbase, path);\n                    Stream s;\n                    var e = compress.Entries.Where(a => a.Key == path).ToArray();\n                    if (e.Length == 0) throw new Exception(\"Could not open \" + path);\n                    s = e[0].OpenEntryStream();\n                    Bitmap b;\n                    try\n                    {\n                        b = new Bitmap(s);\n                    }\n                    catch { throw new Exception(\"Corrupt image: \" + path); }\n                    s.Close();\n                    return b;\n                }\n            }\n            try\n            {\n                string code = \"\";\n                if (type == ScriptType.Folder)\n                {\n                    var files = Directory.GetFiles(p, \"*script.cs\", SearchOption.AllDirectories)\n                        .Where(s => s.EndsWith(\"\\\\script.cs\"))\n                        .Select(s => s.Substring(p.Length + 1))\n                        .ToArray();\n                    Array.Sort(files.Select(s => s.Length).ToArray(), files);\n                    if (files.Length == 0) throw new Exception(\"Could not find script.cs file\");\n                    var jsonpath = files[0];\n                    pbase = jsonpath.Substring(0, jsonpath.Length - \"script.cs\".Length);\n                    if (files.Length == 0) throw new Exception(\"Could not find script.cs file\");\n                    try\n                    {\n                        code = File.ReadAllText(Path.Combine(p, jsonpath));\n                    }\n                    catch { throw new Exception(\"Could not read script.cs file\"); }\n                }\n                else if (type == ScriptType.Zip || type == ScriptType.Zrp)\n                {\n                    if (type == ScriptType.Zrp)\n                    {\n                        var encoded = File.OpenRead(p);\n                        var key = new byte[16];\n                        var iv = new byte[16];\n                        encoded.Read(key, 0, 16);\n                        encoded.Read(iv, 0, 16);\n\n                        zrpstream = new MemoryStream();\n\n                        using (AesManaged aes = new AesManaged())\n                        {\n                            ICryptoTransform decryptor = aes.CreateDecryptor(key, iv);\n                            using (CryptoStream cs = new CryptoStream(encoded, decryptor, CryptoStreamMode.Read))\n                            {\n                                cs.CopyTo(zrpstream);\n                            }\n                            zrpstream.Position = 0;\n                            archive = new ZipArchive(zrpstream);\n                        }\n                    }\n                    else\n                    {\n                        archive = ZipFile.OpenRead(p);\n                    }\n                    var files = archive.Entries.Where(e => e.Name == \"script.cs\").ToArray();\n                    Array.Sort(files.Select(s => s.FullName.Length).ToArray(), files);\n                    if (files.Length == 0) throw new Exception(\"Could not find script.cs file\");\n                    var jsonfile = files[0];\n                    pbase = jsonfile.FullName.Substring(0, jsonfile.FullName.Length - \"script.cs\".Length);\n                    using (var jfile = new StreamReader(jsonfile.Open()))\n                    {\n                        code = jfile.ReadToEnd();\n                    }\n                }\n                else\n                {\n                    if (type == ScriptType.Rar)\n                        compress = RarArchive.Open(p);\n                    if (type == ScriptType.SevenZip)\n                        compress = SevenZipArchive.Open(p);\n                    if (type == ScriptType.Tar)\n                        compress = TarArchive.Open(p);\n\n                    var files = compress.Entries.Where(e => e.Key.EndsWith(\"/script.cs\") || e.Key == \"script.cs\").ToArray();\n                    Array.Sort(files.Select(s => s.Key.Length).ToArray(), files);\n                    if (files.Length == 0) throw new Exception(\"Could not find script.cs file\");\n                    var jsonfile = files[0];\n                    pbase = jsonfile.Key.Substring(0, jsonfile.Key.Length - \"script.cs\".Length);\n                    using (var jfile = new StreamReader(jsonfile.OpenEntryStream()))\n                    {\n                        code = jfile.ReadToEnd();\n                    }\n                }\n\n                var fc = code.Replace(\" \", \"\").Replace(\"\\t\", \"\").Replace(\"\\n\", \"\").Replace(\"\\r\", \"\");\n\n                Dictionary<string, string> notallowed = new Dictionary<string, string>()\n                {\n                    {\"System.IO\", null},\n                    {\"System.Reflection\", null},\n                    {\"Microsoft.CSharp\", null},\n                    {\"System.Net\", null},\n                    {\"Microsoft.VisualBasic\", null},\n                    {\"System.Drawing\", null},\n                    {\"System.AttributeUsage\", null},\n                    {\"System.EnterpriseServices\", null},\n                    {\"System.Media\", null},\n                    {\"System.Messaging\", null},\n                    {\"System.Printing\", null},\n                    {\"System.Security\", null},\n                    {\"System.ServiceModel\", null},\n                    {\"System.ServiceProcess\", null},\n                    {\"System.Speech\", null},\n                    {\"System.Web\", null},\n                    {\"System.Windows\", null},\n                    {\"System.Xml\", null},\n                    {\"Microsoft.Windows\", null},\n                    {\"Microsoft.Win32\", null},\n                    {\"Microsoft.SqlServer\", null},\n                    {\"Microsoft.JScript\", null},\n                    {\"Microsoft.Build\", null},\n                    {\"Accessibility\", null},\n                    {\"Microsoft.Activities\", null},\n                    {\"System.Diagnostics\", \"random user\"},\n                    {\"System.Runtime\", \"random user\"},\n                    {\"System.Management\", \"random user\"}\n                };\n\n                foreach (var n in notallowed)\n                {\n                    if (fc.Contains(n.Key))\n                    {\n                        if (n.Value == null)\n                            throw new Exception(n.Key + \" is not allowed\");\n                        else\n                            if (fc.Contains(n.Key)) throw new Exception(n.Key + \" is not allowed (found by \" + n.Value + \")\");\n                    }\n                }\n\n                CompilerParameters compiler_parameters = new CompilerParameters();\n\n                compiler_parameters.GenerateInMemory = true;\n\n                compiler_parameters.GenerateExecutable = false;\n\n                compiler_parameters.ReferencedAssemblies.Add(typeof(object).Assembly.Location);\n                compiler_parameters.ReferencedAssemblies.Add(typeof(OpenTK.Vector2).Assembly.Location);\n                compiler_parameters.ReferencedAssemblies.Add(typeof(IEnumerable<object>).Assembly.Location);\n                compiler_parameters.ReferencedAssemblies.Add(typeof(LinkedList<object>).Assembly.Location);\n                compiler_parameters.ReferencedAssemblies.Add(typeof(System.Drawing.Color).Assembly.Location);\n                compiler_parameters.ReferencedAssemblies.Add(typeof(IO).Assembly.Location);\n                compiler_parameters.ReferencedAssemblies.Add(typeof(System.Linq.Enumerable).Assembly.Location);\n\n                compiler_parameters.CompilerOptions = \"/optimize /unsafe /nostdlib\";\n\n                CompilerResults results = provider.CompileAssemblyFromSource(compiler_parameters, code);\n\n                if (results.Errors.HasErrors)\n                {\n                    StringBuilder builder = new StringBuilder();\n                    foreach (CompilerError error in results.Errors)\n                    {\n                        builder.AppendLine(String.Format(\"Error ({0}): {1}\", error.ErrorNumber, error.ErrorText));\n                    }\n                    throw new Exception(String.Format(\"Error on line {0}:\\n{1}\", results.Errors[0].Line, results.Errors[0].ErrorText) + \"\\n\" + builder.ToString());\n                }\n\n                IO.loadTexture = (path, loop, linear) =>\n                {\n                    var bmp = GetBitmap(path);\n                    var loaded = new Texture()\n                    {\n                        bitmap = bmp,\n                        path = path,\n                        width = bmp.Width,\n                        height = bmp.Height,\n                        aspectRatio = bmp.Width / (double)bmp.Height,\n                        looped = loop,\n                        linear = linear\n                    };\n                    script.textures.Add(loaded);\n                    return loaded;\n                };\n\n                IO.loadFont = (size, name, style, chars) =>\n                {\n                    var font = new Font()\n                    {\n                        charMap = chars,\n                        fontName = name,\n                        fontPixelSize = size,\n                        fontStyle = style\n                    };\n                    script.fonts.Add(font);\n                    return font;\n                };\n\n                Assembly assembly = results.CompiledAssembly;\n                var renderType = assembly.GetType(\"Script\");\n                var instance = (dynamic)Activator.CreateInstance(renderType);\n                script.instance = instance;\n                script.renderType = renderType;\n\n                IO.callLoadFunction(script.instance);\n\n                if (renderType.GetField(\"Description\") != null) script.description = instance.Description;\n                if (renderType.GetField(\"Preview\") != null) script.preview = GetBitmap(instance.Preview);\n\n                if (renderType.GetMethod(\"Load\") == null) throw new Exception(\"Load method required\");\n                if (renderType.GetMethod(\"Render\") == null) throw new Exception(\"Render method required\");\n\n                if (renderType.GetMethod(\"RenderInit\") != null) script.hasPreRender = true;\n                if (renderType.GetMethod(\"RenderDispose\") != null) script.hasPostRender = true;\n\n                bool hasVar(string name, Type ftype)\n                {\n                    if (renderType.GetField(name) != null)\n                        if (ftype.IsAssignableFrom(renderType.GetField(name).FieldType))\n                            return true;\n                    if (renderType.GetProperty(name) != null)\n                        if (ftype.IsAssignableFrom(renderType.GetProperty(name).PropertyType))\n                            return true;\n                    return false;\n                }\n\n                if (hasVar(\"ManualNoteDelete\", typeof(bool))) script.hasManualNoteDelete = true;\n                if (hasVar(\"NoteCollectorOffset\", typeof(double))) script.hasCollectorOffset = true;\n                if (hasVar(\"NoteScreenTime\", typeof(double))) script.hasNoteScreenTime = true;\n                if (hasVar(\"LastNoteCount\", typeof(long))) script.hasNoteCount = true;\n                if (hasVar(\"UseProfiles\", typeof(bool))) script.hasProfiles = script.instance.UseProfiles;\n\n                if (hasVar(\"SettingsUI\", typeof(IEnumerable<UISetting>)))\n                {\n                    script.uiSettings = instance.SettingsUI;\n                }\n            }\n            catch (Exception e)\n            {\n                if (e is TargetInvocationException) e = e.InnerException;\n                script.error = true;\n                script.description = e.Message;\n            }\n            finally\n            {\n                if (archive != null)\n                    archive.Dispose();\n                if (zrpstream != null)\n                    zrpstream.Dispose();\n                if (compress != null)\n                    compress.Dispose();\n            }\n            return script;\n        }\n\n        private void PluginList_SelectionChanged(object sender, SelectionChangedEventArgs e)\n        {\n            LoadSelectedPack();\n        }\n\n        private void ReloadPackButton_Click(object sender, RoutedEventArgs e)\n        {\n            LoadSelectedPack();\n        }\n\n        void PopulateSettingsDock(IEnumerable<UISetting> settings, DockPanel dock)\n        {\n            void Dispatch(Action action)\n            {\n                if (Thread.CurrentThread.ManagedThreadId == Dispatcher.Thread.ManagedThreadId)\n                {\n                    action();\n                }\n                else\n                {\n                    Dispatcher.InvokeAsync(action).Task.GetAwaiter().GetResult();\n                }\n            }\n\n            dock.LastChildFill = false;\n            foreach (var sett in settings)\n            {\n                if (sett is UILabel)\n                {\n                    var s = sett as UILabel;\n                    var label = new Label() { Content = s.Text, FontSize = s.FontSize, Margin = new Thickness(0, 0, 0, s.Padding) };\n                    DockPanel.SetDock(label, Dock.Top);\n\n                    dock.Children.Add(label);\n                }\n                if (sett is UINumber)\n                {\n                    var s = sett as UINumber;\n                    var number = new NumberSelect() { Minimum = (decimal)s.Minimum, Maximum = (decimal)s.Maximum, DecimalPoints = s.DecialPoints, Step = (decimal)s.Step, Value = (decimal)s.Value };\n                    number.MinWidth = 100;\n                    var label = new Label() { Content = s.Text, FontSize = 16 };\n                    var d = new DockPanel() { HorizontalAlignment = HorizontalAlignment.Left, Margin = new Thickness(0, 0, 0, s.Padding) };\n                    if (!(s.Text == null || s.Text == \"\"))\n                    {\n                        number.Margin = new Thickness(5, 0, 0, 1);\n                        d.Children.Add(label);\n                    }\n                    d.Children.Add(number);\n                    DockPanel.SetDock(d, Dock.Top);\n                    number.ValueChanged += (_, e) => { s.Value = (double)e.NewValue; };\n                    dock.Children.Add(d);\n\n                    s.EnableToggled += (enable) =>\n                    {\n                        Dispatch(() => number.IsEnabled = enable);\n                    };\n\n                    s.ValueChanged += (v) => Dispatch(() =>\n                    {\n                        if (v < (double)number.Minimum) v = (double)number.Minimum;\n                        if (v > (double)number.Maximum) v = (double)number.Maximum;\n                        if (number.Value != (decimal)v)\n                            number.Value = (decimal)v;\n                    });\n\n                    settingsControls.Add(number);\n                    settingsMeta.Add(s);\n                }\n                if (sett is UINumberSlider)\n                {\n                    var s = sett as UINumberSlider;\n                    double min = s.Minimum;\n                    double max = s.Maximum;\n                    if (s.Logarithmic)\n                    {\n                        min = Math.Log(min, 2);\n                        max = Math.Log(max, 2);\n                    }\n                    var number = new ValueSlider() { Minimum = min, Maximum = max, TrueMin = (decimal)s.TrueMinimum, TrueMax = (decimal)s.TrueMaximum, DecimalPoints = s.DecialPoints, Step = (decimal)s.Step };\n                    if (s.Logarithmic)\n                    {\n                        number.nudToSlider = v => Math.Log(v, 2);\n                        number.sliderToNud = v => Math.Pow(2, v);\n                    }\n                    number.Value = s.Value;\n                    number.MinWidth = 400;\n                    var label = new Label() { Content = s.Text, FontSize = 16 };\n                    var d = new DockPanel() { HorizontalAlignment = HorizontalAlignment.Left, Margin = new Thickness(0, 0, 0, s.Padding) };\n                    if (!(s.Text == null || s.Text == \"\"))\n                    {\n                        number.Margin = new Thickness(5, 0, 0, 1);\n                        d.Children.Add(label);\n                    }\n                    d.Children.Add(number);\n                    DockPanel.SetDock(d, Dock.Top);\n                    number.ValueChanged += (_, e) => { s.Value = e.NewValue; };\n                    dock.Children.Add(d);\n\n                    s.EnableToggled += (enable) =>\n                    {\n                        Dispatch(() => number.IsEnabled = enable);\n                    };\n\n                    s.ValueChanged += (v) => Dispatch(() =>\n                    {\n                        v = Math.Round(v, s.DecialPoints);\n                        if (v < (double)number.TrueMin) v = (double)number.TrueMin;\n                        if (v > (double)number.TrueMax) v = (double)number.TrueMax;\n                        if (number.Value != v)\n                            number.Value = v;\n                    });\n\n                    settingsControls.Add(number);\n                    settingsMeta.Add(s);\n                }\n                if (sett is UIDropdown)\n                {\n                    var s = sett as UIDropdown;\n                    var drop = new ComboBox() { FontSize = 16 };\n                    var label = new Label() { Content = s.Text, FontSize = 16 };\n                    foreach (var o in s.Options) drop.Items.Add(new ComboBoxItem() { Content = o, FontSize = 16 });\n                    var d = new DockPanel() { HorizontalAlignment = HorizontalAlignment.Left, Margin = new Thickness(0, 0, 0, s.Padding) };\n                    if (!(s.Text == null || s.Text == \"\"))\n                    {\n                        drop.Margin = new Thickness(5, 0, 0, 0);\n                        d.Children.Add(label);\n                    }\n                    d.Children.Add(drop);\n                    DockPanel.SetDock(d, Dock.Top);\n                    drop.SelectionChanged += (_, e) =>\n                    {\n                        var combo = (ComboBox)e.Source;\n                        s.Index = combo.SelectedIndex;\n                        s.Value = s.Options[combo.SelectedIndex];\n                    };\n                    drop.SelectedIndex = s.Index;\n                    dock.Children.Add(d);\n\n                    s.EnableToggled += (enable) =>\n                    {\n                        Dispatch(() => drop.IsEnabled = enable);\n                    };\n\n                    s.IndexChanged += (i) => Dispatch(() =>\n                    {\n                        if (drop.SelectedIndex != i)\n                            drop.SelectedIndex = i;\n                    });\n\n                    settingsControls.Add(drop);\n                    settingsMeta.Add(s);\n                }\n                if (sett is UICheckbox)\n                {\n                    var s = sett as UICheckbox;\n                    var check = new BetterCheckbox() { FontSize = 16, Text = s.Text };\n                    DockPanel.SetDock(check, Dock.Top);\n                    check.CheckToggled += (_, e) =>\n                    {\n                        s.Checked = e.NewValue;\n                    };\n                    check.Margin = new Thickness(0, 0, 0, s.Padding);\n                    check.IsChecked = s.Checked;\n                    dock.Children.Add(check);\n\n                    s.EnableToggled += (enable) =>\n                    {\n                        Dispatch(() => check.IsEnabled = enable);\n                    };\n\n                    s.ValueChanged += (c) => Dispatch(() =>\n                    {\n                        if (check.IsChecked != c)\n                            check.IsChecked = c;\n                    });\n\n                    settingsControls.Add(check);\n                    settingsMeta.Add(s);\n                }\n                if (sett is UITabs)\n                {\n                    var s = sett as UITabs;\n                    var tabs = new TabControl();\n                    foreach (var k in s.Tabs.Keys)\n                    {\n                        var item = new TabItem() { Header = k };\n                        var d = new DockPanel() { Margin = new Thickness(10, 10, 10, 0) };\n                        d.LastChildFill = false;\n                        var c = new Grid();\n                        c.Children.Add(d);\n                        item.Content = c;\n                        PopulateSettingsDock(s.Tabs[k], d);\n                        tabs.Items.Add(item);\n                    }\n                    dock.Children.Add(tabs);\n                    dock.LastChildFill = true;\n                    break;\n                }\n            }\n        }\n\n        void LoadSelectedPack()\n        {\n            if (pluginList.SelectedIndex == -1) return;\n            try\n            {\n                settingsControls.Clear();\n                settingsMeta.Clear();\n                profiles.Clear();\n                profileSelect.Items.Clear();\n                var p = resourcePacks[pluginList.SelectedIndex];\n                if (settings.currScript != null) UnloadScript(settings.currScript);\n                var script = LoadScript(p.filename, p.type);\n\n                if (!script.error)\n                {\n                    pluginDesc.Foreground = Brushes.White;\n                    settings.currScript = script;\n                    settings.lastScriptChangeTime = DateTime.Now.Ticks;\n                }\n                else\n                {\n                    pluginDesc.Foreground = Brushes.Red;\n                    settings.currScript = null;\n                    settings.lastScriptChangeTime = DateTime.Now.Ticks;\n                }\n                if (script.preview == null)\n                    previewImg.Source = null;\n                else\n                    previewImg.Source = BitmapToImageSource(script.preview);\n                switchTab.Visibility = Visibility.Collapsed;\n                settingsPanel.Children.Clear();\n\n                if (script.uiSettings != null)\n                {\n                    switchTab.Visibility = Visibility.Visible;\n                    PopulateSettingsDock(script.uiSettings, settingsPanel);\n                }\n\n                if (script.hasProfiles)\n                {\n                    profilesDir = p.filename + \".profiles.json\";\n                    if (File.Exists(profilesDir))\n                    {\n                        try\n                        {\n                            var text = File.ReadAllText(profilesDir);\n                            packProfiles = (JObject)JsonConvert.DeserializeObject(text);\n                        }\n                        catch\n                        {\n                            packProfiles = new JObject();\n                        }\n                    }\n                    else\n                    {\n                        packProfiles = new JObject();\n                    }\n                    profileDock.Visibility = Visibility.Visible;\n                    foreach (var v in packProfiles)\n                    {\n                        if (!(v.Value is JArray)) continue;\n                        var data = (v.Value as JArray).Select<JToken, object>(val =>\n                        {\n                            if (val.Type == JTokenType.Boolean) return (bool)val;\n                            if (val.Type == JTokenType.Integer) return (int)val;\n                            if (val.Type == JTokenType.Float) return (double)val;\n                            return null;\n                        }).ToArray();\n                        if (!CheckProfileValidity(data)) continue;\n                        profiles.Add(data);\n                        profileSelect.Items.Add(new ComboBoxItem()\n                        {\n                            Content = v.Key,\n                            Tag = v.Key\n                        });\n                    }\n                }\n                else\n                {\n                    profilesDir = null;\n                    packProfiles = null;\n                    profileDock.Visibility = Visibility.Collapsed;\n                }\n\n                pluginDesc.Text = script.description;\n            }\n            catch { }\n        }\n\n        public void SetValues()\n        {\n            firstNote.Value = settings.firstNote;\n            lastNote.Value = settings.lastNote - 1;\n            noteDeltaScreenTime.Value = settings.deltaTimeOnScreen;\n            paletteList.SelectImage(settings.palette);\n        }\n\n        private void ReloadButton_Click(object sender, RoutedEventArgs e)\n        {\n            ReloadPacks();\n        }\n\n        bool screenTimeLock = false;\n\n        private void NoteDeltaScreenTime_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)\n        {\n            if (!inited) return;\n            try\n            {\n                if (screenTimeLock) return;\n                screenTimeLock = true;\n                settings.deltaTimeOnScreen = noteDeltaScreenTime.Value;\n                screenTimeLock = false;\n            }\n            catch (NullReferenceException)\n            {\n                screenTimeLock = false;\n            }\n        }\n\n        private void Nud_ValueChanged(object sender, RoutedPropertyChangedEventArgs<decimal> e)\n        {\n            if (!inited) return;\n            try\n            {\n                if (sender == firstNote) settings.firstNote = (int)firstNote.Value;\n                if (sender == lastNote) settings.lastNote = (int)lastNote.Value + 1;\n            }\n            catch (NullReferenceException) { }\n            catch (InvalidOperationException) { }\n        }\n\n        private void openFolderButton_Click(object sender, RoutedEventArgs e)\n        {\n            if (!packPath.Contains(\":\\\\\") && !packPath.Contains(\":/\"))\n                Process.Start(\"explorer.exe\", System.IO.Path.Combine(System.IO.Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), packPath));\n            else\n                Process.Start(\"explorer.exe\", packPath);\n        }\n\n        void SaveProfilesFile()\n        {\n            File.WriteAllText(profilesDir, JsonConvert.SerializeObject(packProfiles));\n        }\n\n        private void saveProfile_Click(object sender, RoutedEventArgs e)\n        {\n            var name = profileName.Text.Trim();\n            if (name == \"\") MessageBox.Show(\"Please write a name for the profile\", \"Invalid name\");\n            else\n            {\n                if (packProfiles.ContainsKey(name))\n                {\n                    if (MessageBox.Show(\"Are you sure you want to override profile \" + name + \"?\", \"Override\", MessageBoxButton.YesNo) == MessageBoxResult.No) return;\n                    packProfiles.Remove(name);\n                    ComboBoxItem item = null;\n                    foreach (var i in profileSelect.Items)\n                    {\n                        if ((string)((ComboBoxItem)i).Tag == name)\n                        {\n                            item = (ComboBoxItem)i;\n                            break;\n                        }\n                    }\n                    profiles.RemoveAt(profileSelect.Items.IndexOf(item));\n                    profileSelect.Items.Remove(item);\n                }\n                var values = settingsMeta.Select(s =>\n                {\n                    if (s is UINumber)\n                        return (object)(s as UINumber).Value;\n                    if (s is UINumberSlider)\n                        return (object)(s as UINumberSlider).Value;\n                    if (s is UICheckbox)\n                        return (object)(s as UICheckbox).Checked;\n                    if (s is UIDropdown)\n                        return (object)(s as UIDropdown).Index;\n                    return (object)null;\n                }).ToArray();\n                packProfiles.Add(name, new JArray(values));\n                SaveProfilesFile();\n                profiles.Add(values);\n                profileSelect.Items.Add(new ComboBoxItem()\n                {\n                    Content = name,\n                    Tag = name\n                });\n                profileSelect.SelectedIndex = profileSelect.Items.Count - 1;\n            }\n        }\n\n        private bool CheckProfileValidity(IEnumerable<object> data)\n        {\n            var i = 0;\n            foreach (var d in data)\n            {\n                if (settingsMeta[i] is UINumber)\n                    if (!typeof(double).IsAssignableFrom(d.GetType()) && !typeof(int).IsAssignableFrom(d.GetType()))\n                        return false;\n                if (settingsMeta[i] is UINumberSlider)\n                    if (!typeof(double).IsAssignableFrom(d.GetType()) && !typeof(int).IsAssignableFrom(d.GetType()))\n                        return false;\n                if (settingsMeta[i] is UICheckbox)\n                    if (!typeof(bool).IsAssignableFrom(d.GetType()))\n                        return false;\n                if (settingsMeta[i] is UIDropdown)\n                {\n                    if (!typeof(int).IsAssignableFrom(d.GetType()))\n                        return false;\n                    var index = (int)d;\n                    if (index < 0 || index >= ((UIDropdown)settingsMeta[i]).Options.Length)\n                        return false;\n                }\n                i++;\n            }\n            return true;\n        }\n\n        private void deleteProfile_Click(object sender, RoutedEventArgs e)\n        {\n            if (profileSelect.SelectedIndex == -1) return;\n            var i = profileSelect.SelectedIndex;\n            profiles.RemoveAt(i);\n            var name = (string)((ComboBoxItem)profileSelect.SelectedItem).Tag;\n            var selected = profileSelect.SelectedItem;\n            profileSelect.SelectedIndex = -1;\n            profileSelect.Items.Remove(selected);\n            packProfiles.Remove(name);\n            SaveProfilesFile();\n        }\n\n        private void profileDefaults_Click(object sender, RoutedEventArgs e)\n        {\n            for (int i = 0; i < settingsMeta.Count; i++)\n            {\n                var m = settingsMeta[i];\n                if (m is UINumber)\n                {\n                    var me = m as UINumber;\n                    me.Value = me.Default;\n                }\n                if (m is UINumberSlider)\n                {\n                    var me = m as UINumberSlider;\n                    me.Value = me.Default;\n                }\n                if (m is UICheckbox)\n                {\n                    var me = m as UICheckbox;\n                    me.Checked = me.Default;\n                }\n                if (m is UIDropdown)\n                {\n                    var me = m as UIDropdown;\n                    me.Index = me.Default;\n                }\n            }\n        }\n\n        private void profileSelect_SelectionChanged(object sender, SelectionChangedEventArgs e)\n        {\n            if (profileSelect.SelectedIndex == -1) return;\n            var profile = profiles[profileSelect.SelectedIndex];\n            profileName.Text = (string)(((ComboBoxItem)profileSelect.SelectedItem).Tag);\n\n            for (int i = 0; i < profile.Length; i++)\n            {\n                var d = profile[i];\n                if (settingsMeta[i] is UINumber)\n                {\n                    if (!typeof(double).IsAssignableFrom(d.GetType()) && !typeof(int).IsAssignableFrom(d.GetType())) return;\n                    (settingsMeta[i] as UINumber).Value = (double)d;\n                }\n                if (settingsMeta[i] is UINumberSlider)\n                {\n                    if (!typeof(double).IsAssignableFrom(d.GetType()) && !typeof(int).IsAssignableFrom(d.GetType())) return;\n                    (settingsMeta[i] as UINumberSlider).Value = (double)d;\n                }\n                if (settingsMeta[i] is UICheckbox)\n                {\n                    if (!typeof(bool).IsAssignableFrom(d.GetType())) return;\n                    (settingsMeta[i] as UICheckbox).Checked = (bool)d;\n                }\n                if (settingsMeta[i] is UIDropdown)\n                {\n                    if (!typeof(int).IsAssignableFrom(d.GetType())) return;\n                    var index = (int)d;\n                    if (index < 0 || index >= ((UIDropdown)settingsMeta[i]).Options.Length) return;\n                    (settingsMeta[i] as UIDropdown).Index = (int)d;\n                }\n            }\n        }\n\n        private void profileSelect_DropDownOpened(object sender, EventArgs e)\n        {\n            profileSelect.SelectedIndex = -1;\n        }\n    }\n}\n"
  },
  {
    "path": "ScriptedRenderer/packages.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n  <package id=\"Newtonsoft.Json\" version=\"12.0.3\" targetFramework=\"net461\" />\n  <package id=\"OpenTK\" version=\"3.1.0\" targetFramework=\"net461\" />\n  <package id=\"SharpCompress\" version=\"0.24.0\" targetFramework=\"net461\" />\n</packages>"
  },
  {
    "path": "TexturedRender/Languages/en/textured.xaml",
    "content": "﻿<ResourceDictionary\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" \n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n    xmlns:local=\"using:MSDNSample\"\n    xmlns:system=\"clr-namespace:System;assembly=mscorlib\">\n\n    <!--Tab Names-->\n    <system:String x:Key=\"resourcesTab\">Resources</system:String>\n    <system:String x:Key=\"miscTab\">Misc</system:String>\n    <system:String x:Key=\"switchesTab\">Switches</system:String>\n\n    <!--Resources-->\n    <system:String x:Key=\"resourcePath\">Resource packs path:</system:String>\n    <system:String x:Key=\"resourcePathDir\">Plugins\\Assets\\Textured\\Resources</system:String>\n    <system:String x:Key=\"openFolder\">Open Folder</system:String>\n    <system:String x:Key=\"reloadList\">Reload List</system:String>\n    <system:String x:Key=\"reloadPack\">Reload Pack</system:String>\n\n    <!--Other settings-->\n    <system:String x:Key=\"firstNote\">First Note</system:String>\n    <system:String x:Key=\"lastNote\">Last Note</system:String>\n    <system:String x:Key=\"noteScreenTime\">Note screen time</system:String>\n    <system:String x:Key=\"blackNotesAbove\">Draw black notes above (Warning: SLOWER!!)</system:String>\n</ResourceDictionary>"
  },
  {
    "path": "TexturedRender/OpenTK.dll.config",
    "content": "<configuration>\n  <dllmap os=\"linux\" dll=\"opengl32.dll\" target=\"libGL.so.1\"/>\n  <dllmap os=\"linux\" dll=\"glu32.dll\" target=\"libGLU.so.1\"/>\n  <dllmap os=\"linux\" dll=\"openal32.dll\" target=\"libopenal.so.1\"/>\n  <dllmap os=\"linux\" dll=\"alut.dll\" target=\"libalut.so.0\"/>\n  <dllmap os=\"linux\" dll=\"opencl.dll\" target=\"libOpenCL.so\"/>\n  <dllmap os=\"linux\" dll=\"libX11\" target=\"libX11.so.6\"/>\n  <dllmap os=\"linux\" dll=\"libXi\" target=\"libXi.so.6\"/>\n  <dllmap os=\"linux\" dll=\"SDL2.dll\" target=\"libSDL2-2.0.so.0\"/>\n  <dllmap os=\"osx\" dll=\"opengl32.dll\" target=\"/System/Library/Frameworks/OpenGL.framework/OpenGL\"/>\n  <dllmap os=\"osx\" dll=\"openal32.dll\" target=\"/System/Library/Frameworks/OpenAL.framework/OpenAL\" />\n  <dllmap os=\"osx\" dll=\"alut.dll\" target=\"/System/Library/Frameworks/OpenAL.framework/OpenAL\" />\n  <dllmap os=\"osx\" dll=\"libGLES.dll\" target=\"/System/Library/Frameworks/OpenGLES.framework/OpenGLES\" />\n  <dllmap os=\"osx\" dll=\"libGLESv1_CM.dll\" target=\"/System/Library/Frameworks/OpenGLES.framework/OpenGLES\" />\n  <dllmap os=\"osx\" dll=\"libGLESv2.dll\" target=\"/System/Library/Frameworks/OpenGLES.framework/OpenGLES\" />\n  <dllmap os=\"osx\" dll=\"opencl.dll\" target=\"/System/Library/Frameworks/OpenCL.framework/OpenCL\"/>\n  <dllmap os=\"osx\" dll=\"SDL2.dll\" target=\"libSDL2.dylib\"/>\n  <!-- XQuartz compatibility (X11 on Mac) -->\n  <dllmap os=\"osx\" dll=\"libGL.so.1\" target=\"/usr/X11/lib/libGL.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libX11\" target=\"/usr/X11/lib/libX11.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXcursor.so.1\" target=\"/usr/X11/lib/libXcursor.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXi\" target=\"/usr/X11/lib/libXi.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXinerama\" target=\"/usr/X11/lib/libXinerama.dylib\"/>\n  <dllmap os=\"osx\" dll=\"libXrandr.so.2\" target=\"/usr/X11/lib/libXrandr.dylib\"/>\n</configuration>\n"
  },
  {
    "path": "TexturedRender/Pack.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Drawing;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace TexturedRender\n{\n    public enum TextureShaderType\n    {\n        Normal,\n        Inverted,\n        Hybrid\n    }\n\n    public enum KeyType\n    {\n        White,\n        Black,\n        Both\n    }\n\n    public enum PackType\n    {\n        Folder, Zip, Zrp, Rar, SevenZip, Tar\n    }\n\n    public class KeyboardOverlay\n    {\n        public int firstKey;\n        public int lastKey;\n        public double alpha = 1;\n        public bool overlayBelow = false;\n        public Bitmap tex;\n        public int texID;\n        public double texAspect;\n    }\n\n    public class NoteTexture\n    {\n        public double maxSize;\n        public bool useCaps;\n        public bool stretch;\n        public bool squeezeEndCaps = true;\n\n        public double darkenBlackNotes = 1;\n        public double highlightHitNotes = 0;\n        public Color highlightHitNotesColor = Color.FromArgb(255, 255, 255, 255);\n\n        public double noteMiddleAspect;\n        public Bitmap noteMiddleTex;\n        public int noteMiddleTexID;\n        public double noteTopOversize;\n        public double noteTopAspect;\n        public Bitmap noteTopTex;\n        public int noteTopTexID;\n        public double noteBottomOversize;\n        public double noteBottomAspect;\n        public Bitmap noteBottomTex;\n        public int noteBottomTexID;\n\n        public KeyType keyType = KeyType.Both;\n    }\n\n    public class Pack\n    {\n        public string filepath;\n        public PackType filetype;\n\n        public List<string> switchOrder = new List<string>();\n        public Dictionary<string, string> switchValues = new Dictionary<string, string>();\n        public Dictionary<string, string[]> switchChoices = new Dictionary<string, string[]>();\n\n        public string name;\n        public bool error = false;\n        public Bitmap preview = null;\n\n        public bool sameWidthNotes = false;\n        public double keyboardHeight = 0.15;\n        public double blackKeyHeight = 0.4;\n\n        public string description = \"\";\n\n        public TextureShaderType noteShader = TextureShaderType.Normal;\n        public TextureShaderType whiteKeyShader = TextureShaderType.Normal;\n        public TextureShaderType blackKeyShader = TextureShaderType.Normal;\n        public bool blackKeyDefaultWhite = false;\n\n        public double blackKey2setOffset = 0.3;\n        public double blackKey3setOffset = 0.5;\n        public double blackKeyScale = 0.6;\n\n        public double blackNote2setOffset = 0;\n        public double blackNote3setOffset = 0;\n        public double blackNoteScale = 1;\n\n        public bool linearScaling = true;\n\n        public double[] advancedBlackKeyOffsets = new double[] { 0, 0, 0, 0, 0 };\n        public double[] advancedBlackKeySizes = new double[] { 1, 1, 1, 1, 1 };\n\n        public Bitmap whiteKeyTex;\n        public int whiteKeyTexID;\n        public double whiteKeyOversize = 0;\n\n        public Bitmap blackKeyTex;\n        public int blackKeyTexID;\n        public double blackKeyOversize = 0;\n\n        public Bitmap whiteKeyPressedTex;\n        public int whiteKeyPressedTexID;\n        public double whiteKeyPressedOversize = 0;\n\n        public Bitmap blackKeyPressedTex;\n        public int blackKeyPressedTexID;\n        public double blackKeyPressedOversize = 0;\n\n        public bool useBar = false;\n        public Bitmap barTex;\n        public int barTexID;\n        public double barHeight = 0.05;\n\n        public Bitmap whiteKeyLeftTex = null;\n        public int whiteKeyLeftTexID;\n        public Bitmap whiteKeyPressedLeftTex = null;\n        public int whiteKeyPressedLeftTexID;\n\n        public Bitmap whiteKeyRightTex = null;\n        public int whiteKeyRightTexID;\n        public Bitmap whiteKeyPressedRightTex = null;\n        public int whiteKeyPressedRightTexID;\n\n        public float interpolateUnendedNotes = 0;\n\n        public bool whiteKeysFullOctave = false;\n        public bool blackKeysFullOctave = false;\n\n        public NoteTexture[] NoteTextures;\n        public KeyboardOverlay[] OverlayTextures;\n\n        public bool disposed = false;\n    }\n}\n"
  },
  {
    "path": "TexturedRender/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// General Information about an assembly is controlled through the following\n// set of attributes. Change these attribute values to modify the information\n// associated with an assembly.\n[assembly: AssemblyTitle(\"TexturedRender\")]\n[assembly: AssemblyDescription(\"\")]\n[assembly: AssemblyConfiguration(\"\")]\n[assembly: AssemblyCompany(\"\")]\n[assembly: AssemblyProduct(\"TexturedRender\")]\n[assembly: AssemblyCopyright(\"Copyright ©  2019\")]\n[assembly: AssemblyTrademark(\"\")]\n[assembly: AssemblyCulture(\"\")]\n\n// Setting ComVisible to false makes the types in this assembly not visible\n// to COM components.  If you need to access a type in this assembly from\n// COM, set the ComVisible attribute to true on that type.\n[assembly: ComVisible(false)]\n\n// The following GUID is for the ID of the typelib if this project is exposed to COM\n[assembly: Guid(\"ecf72523-79ae-4dd1-9421-60b1d8bccb9e\")]\n\n// Version information for an assembly consists of the following four values:\n//\n//      Major Version\n//      Minor Version\n//      Build Number\n//      Revision\n//\n// You can specify all the values or you can default the Build and Revision Numbers\n// by using the '*' as shown below:\n// [assembly: AssemblyVersion(\"1.0.*\")]\n[assembly: AssemblyVersion(\"1.0.0.0\")]\n[assembly: AssemblyFileVersion(\"1.0.0.0\")]\n"
  },
  {
    "path": "TexturedRender/Properties/Resources.Designer.cs",
    "content": "﻿//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace TexturedRender.Properties {\n    using System;\n    \n    \n    /// <summary>\n    ///   A strongly-typed resource class, for looking up localized strings, etc.\n    /// </summary>\n    // This class was auto-generated by the StronglyTypedResourceBuilder\n    // class via a tool like ResGen or Visual Studio.\n    // To add or remove a member, edit your .ResX file then rerun ResGen\n    // with the /str option, or rebuild your VS project.\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"System.Resources.Tools.StronglyTypedResourceBuilder\", \"15.0.0.0\")]\n    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]\n    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]\n    internal class Resources {\n        \n        private static global::System.Resources.ResourceManager resourceMan;\n        \n        private static global::System.Globalization.CultureInfo resourceCulture;\n        \n        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute(\"Microsoft.Performance\", \"CA1811:AvoidUncalledPrivateCode\")]\n        internal Resources() {\n        }\n        \n        /// <summary>\n        ///   Returns the cached ResourceManager instance used by this class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Resources.ResourceManager ResourceManager {\n            get {\n                if (object.ReferenceEquals(resourceMan, null)) {\n                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager(\"TexturedRender.Properties.Resources\", typeof(Resources).Assembly);\n                    resourceMan = temp;\n                }\n                return resourceMan;\n            }\n        }\n        \n        /// <summary>\n        ///   Overrides the current thread's CurrentUICulture property for all\n        ///   resource lookups using this strongly typed resource class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Globalization.CultureInfo Culture {\n            get {\n                return resourceCulture;\n            }\n            set {\n                resourceCulture = value;\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized resource of type System.Drawing.Bitmap.\n        /// </summary>\n        internal static System.Drawing.Bitmap bar {\n            get {\n                object obj = ResourceManager.GetObject(\"bar\", resourceCulture);\n                return ((System.Drawing.Bitmap)(obj));\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized resource of type System.Drawing.Bitmap.\n        /// </summary>\n        internal static System.Drawing.Bitmap keyBlack {\n            get {\n                object obj = ResourceManager.GetObject(\"keyBlack\", resourceCulture);\n                return ((System.Drawing.Bitmap)(obj));\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized resource of type System.Drawing.Bitmap.\n        /// </summary>\n        internal static System.Drawing.Bitmap keyBlackPressed {\n            get {\n                object obj = ResourceManager.GetObject(\"keyBlackPressed\", resourceCulture);\n                return ((System.Drawing.Bitmap)(obj));\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized resource of type System.Drawing.Bitmap.\n        /// </summary>\n        internal static System.Drawing.Bitmap keyWhite {\n            get {\n                object obj = ResourceManager.GetObject(\"keyWhite\", resourceCulture);\n                return ((System.Drawing.Bitmap)(obj));\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized resource of type System.Drawing.Bitmap.\n        /// </summary>\n        internal static System.Drawing.Bitmap keyWhitePressed {\n            get {\n                object obj = ResourceManager.GetObject(\"keyWhitePressed\", resourceCulture);\n                return ((System.Drawing.Bitmap)(obj));\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized resource of type System.Drawing.Bitmap.\n        /// </summary>\n        internal static System.Drawing.Bitmap note {\n            get {\n                object obj = ResourceManager.GetObject(\"note\", resourceCulture);\n                return ((System.Drawing.Bitmap)(obj));\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized resource of type System.Drawing.Bitmap.\n        /// </summary>\n        internal static System.Drawing.Bitmap noteEdge {\n            get {\n                object obj = ResourceManager.GetObject(\"noteEdge\", resourceCulture);\n                return ((System.Drawing.Bitmap)(obj));\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized resource of type System.Byte[].\n        /// </summary>\n        internal static byte[] pack {\n            get {\n                object obj = ResourceManager.GetObject(\"pack\", resourceCulture);\n                return ((byte[])(obj));\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized resource of type System.Byte[].\n        /// </summary>\n        internal static byte[] pack1 {\n            get {\n                object obj = ResourceManager.GetObject(\"pack1\", resourceCulture);\n                return ((byte[])(obj));\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized resource of type System.Drawing.Bitmap.\n        /// </summary>\n        internal static System.Drawing.Bitmap pluginPreview {\n            get {\n                object obj = ResourceManager.GetObject(\"pluginPreview\", resourceCulture);\n                return ((System.Drawing.Bitmap)(obj));\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized resource of type System.Drawing.Bitmap.\n        /// </summary>\n        internal static System.Drawing.Bitmap preview {\n            get {\n                object obj = ResourceManager.GetObject(\"preview\", resourceCulture);\n                return ((System.Drawing.Bitmap)(obj));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "TexturedRender/Properties/Resources.resx",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The primary goals of this format is to allow a simple XML format \n    that is mostly human readable. The generation and parsing of the \n    various data types are done through the TypeConverter classes \n    associated with the data types.\n    \n    Example:\n    \n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n                \n    There are any number of \"resheader\" rows that contain simple \n    name/value pairs.\n    \n    Each data row contains a name, and value. The row also contains a \n    type or mimetype. Type corresponds to a .NET class that support \n    text/value conversion through the TypeConverter architecture. \n    Classes that don't support this are serialized and stored with the \n    mimetype set.\n    \n    The mimetype is used for serialized objects, and tells the \n    ResXResourceReader how to depersist the object. This is currently not \n    extensible. For a given mimetype the value must be set accordingly:\n    \n    Note - application/x-microsoft.net.object.binary.base64 is the format \n    that the ResXResourceWriter will generate, however the reader can \n    read any of the formats listed below.\n    \n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n    \n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array \n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <assembly alias=\"System.Windows.Forms\" name=\"System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" />\n  <data name=\"bar\" type=\"System.Resources.ResXFileRef, System.Windows.Forms\">\n    <value>..\\Resources\\bar.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>\n  </data>\n  <data name=\"keyBlack\" type=\"System.Resources.ResXFileRef, System.Windows.Forms\">\n    <value>..\\Resources\\keyBlack.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>\n  </data>\n  <data name=\"keyBlackPressed\" type=\"System.Resources.ResXFileRef, System.Windows.Forms\">\n    <value>..\\Resources\\keyBlackPressed.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>\n  </data>\n  <data name=\"keyWhite\" type=\"System.Resources.ResXFileRef, System.Windows.Forms\">\n    <value>..\\Resources\\keyWhite.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>\n  </data>\n  <data name=\"keyWhitePressed\" type=\"System.Resources.ResXFileRef, System.Windows.Forms\">\n    <value>..\\Resources\\keyWhitePressed.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>\n  </data>\n  <data name=\"note\" type=\"System.Resources.ResXFileRef, System.Windows.Forms\">\n    <value>..\\Resources\\note.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>\n  </data>\n  <data name=\"noteEdge\" type=\"System.Resources.ResXFileRef, System.Windows.Forms\">\n    <value>..\\Resources\\noteEdge.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>\n  </data>\n  <data name=\"pack\" type=\"System.Resources.ResXFileRef, System.Windows.Forms\">\n    <value>..\\resources\\pack.json;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </data>\n  <data name=\"pack1\" type=\"System.Resources.ResXFileRef, System.Windows.Forms\">\n    <value>..\\Resources\\pack.json;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </data>\n  <data name=\"pluginPreview\" type=\"System.Resources.ResXFileRef, System.Windows.Forms\">\n    <value>..\\Resources\\pluginPreview.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>\n  </data>\n  <data name=\"preview\" type=\"System.Resources.ResXFileRef, System.Windows.Forms\">\n    <value>..\\Resources\\preview.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>\n  </data>\n</root>"
  },
  {
    "path": "TexturedRender/README.md",
    "content": "# Zenith-Texture-Render\nTexture/Resource pack renderer for Zenith.\n"
  },
  {
    "path": "TexturedRender/Render.cs",
    "content": "﻿using ZenithEngine;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing OpenTK.Graphics.OpenGL;\nusing OpenTK.Graphics;\nusing OpenTK;\nusing System.Drawing;\nusing System.Drawing.Imaging;\nusing System.Windows.Media.Imaging;\nusing System.IO;\n\nnamespace TexturedRender\n{\n    public class Render : IPluginRender\n    {\n        public string Name => \"Textured\";\n\n        public string Description => \"Plugin for loading and rendering custom resource packs, \" +\n            \"with settings defined in a .json file\";\n\n        public string LanguageDictName { get; } = \"textured\";\n\n        #region Shaders\n        string quadShaderVert = @\"#version 330 core\n\nlayout(location=0) in vec2 in_position;\nlayout(location=1) in vec4 in_color;\nlayout(location=2) in vec2 in_uv;\nlayout(location=3) in float in_texid;\n\nout vec4 v2f_color;\nout vec2 uv;\nout float texid;\n\nvoid main()\n{\n    gl_Position = vec4(in_position.x * 2 - 1, in_position.y * 2 - 1, 1.0f, 1.0f);\n    v2f_color = in_color;\n    uv = in_uv;\n    texid = in_texid;\n}\n\";\n        string quadShaderFrag = @\"#version 330 core\n\nin vec4 v2f_color;\nin vec2 uv;\nin float texid;\n\nuniform sampler2D textureSampler1;\nuniform sampler2D textureSampler2;\nuniform sampler2D textureSampler3;\nuniform sampler2D textureSampler4;\nuniform sampler2D textureSampler5;\nuniform sampler2D textureSampler6;\nuniform sampler2D textureSampler7;\nuniform sampler2D textureSampler8;\nuniform sampler2D textureSampler9;\nuniform sampler2D textureSampler10;\nuniform sampler2D textureSampler11;\nuniform sampler2D textureSampler12;\n\nout vec4 out_color;\n\nvoid main()\n{\n    vec4 col;\n    if(texid < 0.5) col = texture2D( textureSampler1, uv );\n    else if(texid < 1.5) col = texture2D( textureSampler2, uv );\n    else if(texid < 2.5) col = texture2D( textureSampler3, uv );\n    else if(texid < 3.5) col = texture2D( textureSampler4, uv );\n    else if(texid < 4.5) col = texture2D( textureSampler5, uv );\n    else if(texid < 5.5) col = texture2D( textureSampler6, uv );\n    else if(texid < 6.5) col = texture2D( textureSampler7, uv );\n    else if(texid < 7.5) col = texture2D( textureSampler8, uv );\n    else if(texid < 8.5) col = texture2D( textureSampler9, uv );\n    else if(texid < 9.5) col = texture2D( textureSampler10, uv );\n    else if(texid < 10.5) col = texture2D( textureSampler11, uv );\n    else if(texid < 11.5) col = texture2D( textureSampler12, uv );\n    out_color = col * v2f_color;\n}\n\";\n        string invertQuadShaderFrag = @\"#version 330 core\n\nin vec4 v2f_color;\nin vec2 uv;\nin float texid;\n\nuniform sampler2D textureSampler1;\nuniform sampler2D textureSampler2;\nuniform sampler2D textureSampler3;\nuniform sampler2D textureSampler4;\nuniform sampler2D textureSampler5;\nuniform sampler2D textureSampler6;\nuniform sampler2D textureSampler7;\nuniform sampler2D textureSampler8;\nuniform sampler2D textureSampler9;\nuniform sampler2D textureSampler10;\nuniform sampler2D textureSampler11;\nuniform sampler2D textureSampler12;\n\nout vec4 out_color;\n\nvoid main()\n{\n    vec4 col;\n    if(texid < 0.5) col = texture2D( textureSampler1, uv );\n    else if(texid < 1.5) col = texture2D( textureSampler2, uv );\n    else if(texid < 2.5) col = texture2D( textureSampler3, uv );\n    else if(texid < 3.5) col = texture2D( textureSampler4, uv );\n    else if(texid < 4.5) col = texture2D( textureSampler5, uv );\n    else if(texid < 5.5) col = texture2D( textureSampler6, uv );\n    else if(texid < 6.5) col = texture2D( textureSampler7, uv );\n    else if(texid < 7.5) col = texture2D( textureSampler8, uv );\n    else if(texid < 8.5) col = texture2D( textureSampler9, uv );\n    else if(texid < 9.5) col = texture2D( textureSampler10, uv );\n    else if(texid < 10.5) col = texture2D( textureSampler11, uv );\n    else if(texid < 11.5) col = texture2D( textureSampler12, uv );\n    col = 1 - col;\n    col.w = 1 - col.w;\n    vec4 col2 = 1 - v2f_color;\n    col2.w = 1 - col2.w;\n    out_color = 1 - col * col2;\n    out_color.w = 1 - out_color.w;\n}\n\";\n        string evenQuadShaderFrag = @\"#version 330 core\n\nin vec4 v2f_color;\nin vec2 uv;\nin float texid;\n\nuniform sampler2D textureSampler1;\nuniform sampler2D textureSampler2;\nuniform sampler2D textureSampler3;\nuniform sampler2D textureSampler4;\nuniform sampler2D textureSampler5;\nuniform sampler2D textureSampler6;\nuniform sampler2D textureSampler7;\nuniform sampler2D textureSampler8;\nuniform sampler2D textureSampler9;\nuniform sampler2D textureSampler10;\nuniform sampler2D textureSampler11;\nuniform sampler2D textureSampler12;\n\nout vec4 out_color;\n\nvoid main()\n{\n    vec4 col;\n    if(texid < 0.5) col = texture2D( textureSampler1, uv );\n    else if(texid < 1.5) col = texture2D( textureSampler2, uv );\n    else if(texid < 2.5) col = texture2D( textureSampler3, uv );\n    else if(texid < 3.5) col = texture2D( textureSampler4, uv );\n    else if(texid < 4.5) col = texture2D( textureSampler5, uv );\n    else if(texid < 5.5) col = texture2D( textureSampler6, uv );\n    else if(texid < 6.5) col = texture2D( textureSampler7, uv );\n    else if(texid < 7.5) col = texture2D( textureSampler8, uv );\n    else if(texid < 8.5) col = texture2D( textureSampler9, uv );\n    else if(texid < 9.5) col = texture2D( textureSampler10, uv );\n    else if(texid < 10.5) col = texture2D( textureSampler11, uv );\n    else if(texid < 11.5) col = texture2D( textureSampler12, uv );\n    col = col * 2;\n    if(col.x > 1){\n        out_color.x = 1 - (2 - col.x) * (1 - v2f_color.x);\n    }\n    else out_color.x = col.x * v2f_color.x;\n    if(col.y > 1){\n        out_color.y = 1 - (2 - col.y) * (1 - v2f_color.y);\n    }\n    else out_color.y = col.y * v2f_color.y;\n    if(col.z > 1){\n        out_color.z = 1 - (2 - col.z) * (1 - v2f_color.z);\n    }\n    else out_color.z = col.z * v2f_color.z;\n    out_color.w = col.w * v2f_color.w;\n}\n\";\n\n        int MakeShader(string vert, string frag)\n        {\n            int _vertexObj = GL.CreateShader(ShaderType.VertexShader);\n            int _fragObj = GL.CreateShader(ShaderType.FragmentShader);\n            int statusCode;\n            string info;\n\n            GL.ShaderSource(_vertexObj, vert);\n            GL.CompileShader(_vertexObj);\n            info = GL.GetShaderInfoLog(_vertexObj);\n            GL.GetShader(_vertexObj, ShaderParameter.CompileStatus, out statusCode);\n            if (statusCode != 1) throw new ApplicationException(info);\n\n            GL.ShaderSource(_fragObj, frag);\n            GL.CompileShader(_fragObj);\n            info = GL.GetShaderInfoLog(_fragObj);\n            GL.GetShader(_fragObj, ShaderParameter.CompileStatus, out statusCode);\n            if (statusCode != 1) throw new ApplicationException(info);\n\n            int shader = GL.CreateProgram();\n            GL.AttachShader(shader, _fragObj);\n            GL.AttachShader(shader, _vertexObj);\n            GL.LinkProgram(shader);\n            return shader;\n        }\n        #endregion\n\n        void loadImage(Bitmap image, int texID, bool loop, bool linear = false)\n        {\n            GL.BindTexture(TextureTarget.Texture2D, texID);\n            BitmapData data = image.LockBits(new System.Drawing.Rectangle(0, 0, image.Width, image.Height),\n                ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);\n\n            GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, data.Width, data.Height, 0,\n                OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);\n\n            if (linear)\n            {\n                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);\n                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);\n            }\n            else\n            {\n                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest);\n                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest);\n            }\n            if (loop)\n            {\n                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);\n                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);\n            }\n            else\n            {\n                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge);\n                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge);\n            }\n\n            image.UnlockBits(data);\n        }\n\n        public bool Initialized { get; set; }\n\n        public System.Windows.Media.ImageSource PreviewImage { get; set; } = null;\n\n        public bool ManualNoteDelete => false;\n\n        public double NoteCollectorOffset => -maxBottomCapSize;\n\n        public NoteColor[][] NoteColors { get; set; }\n\n        public double Tempo { get; set; }\n        public MidiInfo CurrentMidi { get; set; }\n\n        public double NoteScreenTime => settings.deltaTimeOnScreen + maxTopCapSize;\n\n        public long LastNoteCount { get; set; }\n\n        public System.Windows.Controls.Control SettingsControl { get; set; } = null;\n\n        int quadShader;\n        int evenquadShader;\n        int inverseQuadShader;\n\n        int vertexBufferID;\n        int colorBufferID;\n        int uvBufferID;\n        int texIDBufferID;\n\n        int quadBufferLength = 2048 * 64;\n        double[] quadVertexbuff;\n        float[] quadColorbuff;\n        double[] quadUVbuff;\n        float[] quadTexIDbuff;\n        int quadBufferPos = 0;\n\n        RenderSettings renderSettings;\n        Settings settings;\n\n        int indexBufferId;\n        uint[] indexes = new uint[2048 * 128 * 6];\n\n        bool[] blackKeys = new bool[257];\n        int[] keynum = new int[257];\n\n        Pack currPack = null;\n        public long lastPackChangeTime = 0;\n\n        public void UnloadPack()\n        {\n            if (currPack == null) return;\n            GL.DeleteTextures(4, new int[] {\n                currPack.whiteKeyTexID, currPack.whiteKeyPressedTexID,\n                currPack.blackKeyTexID, currPack.blackKeyPressedTexID\n            });\n\n            if (currPack.whiteKeyLeftTex != null)\n                GL.DeleteTextures(2, new int[] {\n                    currPack.whiteKeyLeftTexID, currPack.whiteKeyPressedLeftTexID\n                });\n            if (currPack.whiteKeyRightTex != null)\n                GL.DeleteTextures(2, new int[] {\n                    currPack.whiteKeyRightTexID, currPack.whiteKeyPressedRightTexID\n                });\n\n            if (currPack.useBar) GL.DeleteTexture(currPack.barTexID);\n            foreach (var n in currPack.NoteTextures)\n            {\n                GL.DeleteTexture(n.noteMiddleTexID);\n                if (n.useCaps)\n                {\n                    GL.DeleteTexture(n.noteBottomTexID);\n                    GL.DeleteTexture(n.noteTopTexID);\n                }\n            }\n            foreach (var o in currPack.OverlayTextures)\n            {\n                GL.DeleteTexture(o.texID);\n            }\n        }\n\n        public void LoadPack()\n        {\n            if (currPack == null) return;\n            if (currPack.disposed) return;\n            lock (currPack)\n            {\n                currPack.whiteKeyTexID = GL.GenTexture();\n                currPack.whiteKeyPressedTexID = GL.GenTexture();\n                currPack.blackKeyTexID = GL.GenTexture();\n                currPack.blackKeyPressedTexID = GL.GenTexture();\n                if (currPack.useBar) currPack.barTexID = GL.GenTexture();\n\n                loadImage(currPack.whiteKeyTex, currPack.whiteKeyTexID, false, currPack.linearScaling);\n                loadImage(currPack.whiteKeyPressedTex, currPack.whiteKeyPressedTexID, false, currPack.linearScaling);\n                loadImage(currPack.blackKeyTex, currPack.blackKeyTexID, false);\n                loadImage(currPack.blackKeyPressedTex, currPack.blackKeyPressedTexID, false);\n\n                if (currPack.whiteKeyLeftTex != null)\n                {\n                    currPack.whiteKeyLeftTexID = GL.GenTexture();\n                    currPack.whiteKeyPressedLeftTexID = GL.GenTexture();\n                    loadImage(currPack.whiteKeyLeftTex, currPack.whiteKeyLeftTexID, false, currPack.linearScaling);\n                    loadImage(currPack.whiteKeyPressedLeftTex, currPack.whiteKeyPressedLeftTexID, false, currPack.linearScaling);\n                }\n                if (currPack.whiteKeyRightTex != null)\n                {\n                    currPack.whiteKeyRightTexID = GL.GenTexture();\n                    currPack.whiteKeyPressedRightTexID = GL.GenTexture();\n                    loadImage(currPack.whiteKeyRightTex, currPack.whiteKeyRightTexID, false, currPack.linearScaling);\n                    loadImage(currPack.whiteKeyPressedRightTex, currPack.whiteKeyPressedRightTexID, false, currPack.linearScaling);\n                }\n\n                if (currPack.useBar) loadImage(currPack.barTex, currPack.barTexID, false);\n\n                foreach (var n in currPack.NoteTextures)\n                {\n                    n.noteMiddleTexID = GL.GenTexture();\n                    loadImage(n.noteMiddleTex, n.noteMiddleTexID, true);\n                    if (n.useCaps)\n                    {\n                        n.noteTopTexID = GL.GenTexture();\n                        loadImage(n.noteTopTex, n.noteTopTexID, false);\n                        n.noteBottomTexID = GL.GenTexture();\n                        loadImage(n.noteBottomTex, n.noteBottomTexID, false);\n                    }\n                }\n                foreach (var o in currPack.OverlayTextures)\n                {\n                    o.texID = GL.GenTexture();\n                    loadImage(o.tex, o.texID, false);\n                }\n            }\n        }\n\n        public void CheckPack()\n        {\n            if (settings.lastPackChangeTime != lastPackChangeTime)\n            {\n                UnloadPack();\n                currPack = settings.currPack;\n                lastPackChangeTime = settings.lastPackChangeTime;\n                LoadPack();\n            }\n        }\n\n        public void Dispose()\n        {\n            GL.DeleteBuffers(3, new int[] { vertexBufferID, colorBufferID, uvBufferID, indexBufferId, texIDBufferID });\n\n            GL.DeleteProgram(quadShader);\n            GL.DeleteProgram(inverseQuadShader);\n            GL.DeleteProgram(evenquadShader);\n            quadVertexbuff = null;\n            quadColorbuff = null;\n            quadUVbuff = null;\n            Initialized = false;\n            UnloadPack();\n            Console.WriteLine(\"Disposed of TextureRender\");\n        }\n\n        public Render(RenderSettings settings)\n        {\n            this.settings = new Settings();\n            this.renderSettings = settings;\n            SettingsControl = new SettingsCtrl(this.settings);\n            ((SettingsCtrl)SettingsControl).PaletteChanged += () => { ReloadTrackColors(); };\n            PreviewImage = PluginUtils.BitmapToImageSource(Properties.Resources.pluginPreview);\n\n            for (int i = 0; i < blackKeys.Length; i++) blackKeys[i] = isBlackNote(i);\n            int b = 0;\n            int w = 0;\n            for (int i = 0; i < keynum.Length; i++)\n            {\n                if (blackKeys[i]) keynum[i] = b++;\n                else keynum[i] = w++;\n            }\n        }\n\n        public void Init()\n        {\n            quadShader = MakeShader(quadShaderVert, quadShaderFrag);\n            inverseQuadShader = MakeShader(quadShaderVert, invertQuadShaderFrag);\n            evenquadShader = MakeShader(quadShaderVert, evenQuadShaderFrag);\n\n            int loc;\n            int[] samplers = new int[12];\n            for (int i = 0; i < 12; i++)\n            {\n                samplers[i] = i;\n            }\n\n            GL.UseProgram(quadShader);\n            for (int i = 0; i < 12; i++)\n            {\n                loc = GL.GetUniformLocation(quadShader, \"textureSampler\" + (i + 1));\n                GL.Uniform1(loc, i);\n            }\n            GL.UseProgram(inverseQuadShader);\n            for (int i = 0; i < 12; i++)\n            {\n                loc = GL.GetUniformLocation(inverseQuadShader, \"textureSampler\" + (i + 1));\n                GL.Uniform1(loc, i);\n            }\n            GL.UseProgram(evenquadShader);\n            for (int i = 0; i < 12; i++)\n            {\n                loc = GL.GetUniformLocation(evenquadShader, \"textureSampler\" + (i + 1));\n                GL.Uniform1(loc, i);\n            }\n\n            quadVertexbuff = new double[quadBufferLength * 8];\n            quadColorbuff = new float[quadBufferLength * 16];\n            quadUVbuff = new double[quadBufferLength * 8];\n            quadTexIDbuff = new float[quadBufferLength * 4];\n\n            LoadPack();\n\n            GL.GenBuffers(1, out vertexBufferID);\n            GL.GenBuffers(1, out colorBufferID);\n            GL.GenBuffers(1, out uvBufferID);\n            GL.GenBuffers(1, out texIDBufferID);\n            for (uint i = 0; i < indexes.Length / 6; i++)\n            {\n                indexes[i * 6 + 0] = i * 4 + 0;\n                indexes[i * 6 + 1] = i * 4 + 1;\n                indexes[i * 6 + 2] = i * 4 + 3;\n                indexes[i * 6 + 3] = i * 4 + 1;\n                indexes[i * 6 + 4] = i * 4 + 3;\n                indexes[i * 6 + 5] = i * 4 + 2;\n            }\n\n            GL.GenBuffers(1, out indexBufferId);\n            GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexBufferId);\n            GL.BufferData(\n                BufferTarget.ElementArrayBuffer,\n                (IntPtr)(indexes.Length * 4),\n                indexes,\n                BufferUsageHint.StaticDraw);\n            Initialized = true;\n            Console.WriteLine(\"Initialised TextureRender\");\n        }\n\n        Color4[] keyColors = new Color4[514];\n        double[] x1arrayKeys = new double[257];\n        double[] x1arrayNotes = new double[257];\n        double[] wdtharrayKeys = new double[257];\n        double[] wdtharrayNotes = new double[257];\n        double maxTopCapSize = 0;\n        double maxBottomCapSize = 0;\n\n        void SwitchShader(TextureShaderType shader)\n        {\n            if (shader == TextureShaderType.Normal) GL.UseProgram(quadShader);\n            if (shader == TextureShaderType.Inverted) GL.UseProgram(inverseQuadShader);\n            if (shader == TextureShaderType.Hybrid) GL.UseProgram(evenquadShader);\n        }\n\n        public void RenderFrame(FastList<Note> notes, double midiTime, int finalCompositeBuff)\n        {\n            CheckPack();\n            if (currPack == null) return;\n            GL.Enable(EnableCap.Blend);\n            GL.EnableClientState(ArrayCap.VertexArray);\n            GL.EnableClientState(ArrayCap.ColorArray);\n            GL.Enable(EnableCap.Texture2D);\n\n            GL.EnableVertexAttribArray(0);\n            GL.EnableVertexAttribArray(1);\n            GL.EnableVertexAttribArray(2);\n            GL.EnableVertexAttribArray(3);\n\n            GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);\n            GL.BlendEquationSeparate(BlendEquationMode.FuncAdd, BlendEquationMode.Max);\n\n            GL.BindFramebuffer(FramebufferTarget.Framebuffer, finalCompositeBuff);\n            GL.Viewport(0, 0, renderSettings.width, renderSettings.height);\n            GL.Clear(ClearBufferMask.ColorBufferBit);\n\n            #region Vars\n            long nc = 0;\n            int firstNote = settings.firstNote;\n            int lastNote = settings.lastNote;\n            int kbfirstNote = settings.firstNote;\n            int kblastNote = settings.lastNote;\n            if (blackKeys[firstNote] || (currPack.whiteKeysFullOctave && currPack.whiteKeyLeftTex == null && firstNote != 0)) kbfirstNote--;\n            if (blackKeys[lastNote - 1] || (currPack.whiteKeysFullOctave && currPack.whiteKeyRightTex == null)) kblastNote++;\n\n            double deltaTimeOnScreen = settings.deltaTimeOnScreen;\n            double viewAspect = (double)renderSettings.width / renderSettings.height;\n            double keyboardHeightFull = currPack.keyboardHeight / (lastNote - firstNote) * 128 / (1920.0 / 1080.0) * viewAspect;\n            double keyboardHeight = keyboardHeightFull;\n            double barHeight = keyboardHeightFull * currPack.barHeight;\n            if (currPack.useBar) keyboardHeight -= barHeight;\n            bool sameWidth = currPack.sameWidthNotes;\n            for (int i = 0; i < 514; i++) keyColors[i] = Color4.Transparent;\n            double wdth;\n            float r, g, b, a, r2, g2, b2, a2;\n            double x1;\n            double x2;\n            double y1;\n            double y2;\n            int pos;\n            quadBufferPos = 0;\n            bool interpolateUnendedNotes = currPack.interpolateUnendedNotes != 0;\n            float interpolateUnendedNotesVal = 1.0f / renderSettings.fps / currPack.interpolateUnendedNotes;\n\n            if (sameWidth)\n            {\n                for (int i = 0; i < 257; i++)\n                {\n                    x1arrayKeys[i] = (float)(i - firstNote) / (lastNote - firstNote);\n                    wdtharrayKeys[i] = 1.0f / (lastNote - firstNote);\n                    x1arrayNotes[i] = (float)(i - firstNote) / (lastNote - firstNote);\n                    wdtharrayNotes[i] = 1.0f / (lastNote - firstNote);\n                }\n            }\n            else\n            {\n                for (int i = 0; i < 257; i++)\n                {\n                    if (!blackKeys[i])\n                    {\n                        x1arrayKeys[i] = keynum[i];\n                        x1arrayNotes[i] = keynum[i];\n                        wdtharrayKeys[i] = 1.0f;\n                        wdtharrayNotes[i] = 1.0f;\n                    }\n                    else\n                    {\n                        int _i = i + 1;\n                        wdth = currPack.blackKeyScale * currPack.advancedBlackKeySizes[keynum[i] % 5];\n                        int bknum = keynum[i] % 5;\n                        double offset = wdth / 2;\n                        if (bknum == 0) offset += wdth / 2 * currPack.blackKey2setOffset;\n                        if (bknum == 2) offset += wdth / 2 * currPack.blackKey3setOffset;\n                        if (bknum == 1) offset -= wdth / 2 * currPack.blackKey2setOffset;\n                        if (bknum == 4) offset -= wdth / 2 * currPack.blackKey3setOffset;\n\n                        offset -= currPack.advancedBlackKeyOffsets[keynum[i] % 5] * wdth / 2;\n\n                        x1arrayKeys[i] = keynum[_i] - offset;\n                        wdtharrayKeys[i] = wdth;\n\n                        offset -= wdth / 2 * (1 - currPack.blackNoteScale);\n                        if (bknum == 0) offset += wdth / 2 * currPack.blackNote2setOffset;\n                        if (bknum == 2) offset += wdth / 2 * currPack.blackNote3setOffset;\n                        if (bknum == 1) offset -= wdth / 2 * currPack.blackNote2setOffset;\n                        if (bknum == 4) offset -= wdth / 2 * currPack.blackNote3setOffset;\n                        wdth *= currPack.blackNoteScale;\n\n                        x1arrayNotes[i] = (float)keynum[_i] - offset;\n                        wdtharrayNotes[i] = wdth;\n                    }\n                }\n                double knmfn = x1arrayKeys[firstNote];\n                double knmln = x1arrayKeys[lastNote - 1] + wdtharrayKeys[lastNote - 1];\n                double width = knmln - knmfn;\n                for (int i = 0; i < 257; i++)\n                {\n                    x1arrayKeys[i] = (x1arrayKeys[i] - knmfn) / width;\n                    x1arrayNotes[i] = (x1arrayNotes[i] - knmfn) / width;\n                    wdtharrayKeys[i] /= width;\n                    wdtharrayNotes[i] /= width;\n                }\n            }\n\n            #endregion\n\n            #region Notes\n            quadBufferPos = 0;\n            double notePosFactor = 1 / deltaTimeOnScreen * (1 - keyboardHeightFull);\n\n            maxTopCapSize = 0;\n            maxBottomCapSize = 0;\n            foreach (var tex in currPack.NoteTextures)\n            {\n                var topSize = tex.noteTopAspect * viewAspect * wdtharrayNotes[5] / notePosFactor;\n                if (tex.squeezeEndCaps) topSize *= tex.noteTopOversize;\n                var bottomSize = tex.noteBottomAspect * viewAspect * wdtharrayNotes[5] / notePosFactor;\n                if (tex.squeezeEndCaps) bottomSize *= tex.noteBottomOversize;\n                if (maxTopCapSize < topSize) maxTopCapSize = topSize;\n                if (maxBottomCapSize < bottomSize) maxBottomCapSize = bottomSize;\n            }\n\n            double renderCutoff = midiTime + deltaTimeOnScreen;\n\n            var currNoteTex = currPack.NoteTextures[0];\n            var noteTextures = currPack.NoteTextures;\n            GL.BindTexture(TextureTarget.Texture2D, currNoteTex.noteMiddleTexID);\n            for (int i = 0; i < noteTextures.Length; i++)\n            {\n                GL.ActiveTexture(TextureUnit.Texture0 + (i * 3));\n                GL.BindTexture(TextureTarget.Texture2D, noteTextures[i].noteMiddleTexID);\n                if (noteTextures[i].useCaps)\n                {\n                    GL.ActiveTexture(TextureUnit.Texture0 + (i * 3) + 1);\n                    GL.BindTexture(TextureTarget.Texture2D, noteTextures[i].noteBottomTexID);\n                    GL.ActiveTexture(TextureUnit.Texture0 + (i * 3) + 2);\n                    GL.BindTexture(TextureTarget.Texture2D, noteTextures[i].noteTopTexID);\n                }\n            }\n            SwitchShader(currPack.noteShader);\n            for (int i = 0; i < 2; i++)\n            {\n                bool black = false;\n                bool blackabove = settings.blackNotesAbove;\n                if (blackabove && i == 1) black = true;\n                if (!blackabove && i == 1) break;\n                foreach (Note n in notes)\n                {\n                    if (n.end >= midiTime || !n.hasEnded)\n                    {\n                        if (n.start < renderCutoff + maxBottomCapSize)\n                        {\n                            nc++;\n                            int k = n.key;\n                            if (blackabove && !black && blackKeys[k]) continue;\n                            if (blackabove && black && !blackKeys[k]) continue;\n                            if (!(k >= firstNote && k < lastNote)) continue;\n                            Color4 coll = n.color.left;\n                            Color4 colr = n.color.right;\n                            if (n.start <= midiTime)\n                            {\n                                Color4 origcoll = keyColors[k * 2];\n                                Color4 origcolr = keyColors[k * 2 + 1];\n                                float blendfac = coll.A;\n                                float revblendfac = 1 - blendfac;\n                                keyColors[k * 2] = new Color4(\n                                    coll.R * blendfac + origcoll.R * revblendfac,\n                                    coll.G * blendfac + origcoll.G * revblendfac,\n                                    coll.B * blendfac + origcoll.B * revblendfac,\n                                    1);\n                                blendfac = colr.A;\n                                revblendfac = 1 - blendfac;\n                                keyColors[k * 2 + 1] = new Color4(\n                                    colr.R * blendfac + origcolr.R * revblendfac,\n                                    colr.G * blendfac + origcolr.G * revblendfac,\n                                    colr.B * blendfac + origcolr.B * revblendfac,\n                                    1);\n                            }\n                            x1 = x1arrayNotes[k];\n                            wdth = wdtharrayNotes[k];\n                            x2 = x1 + wdth;\n                            y1 = 1 - (renderCutoff - n.end) * notePosFactor;\n                            y2 = 1 - (renderCutoff - n.start) * notePosFactor;\n                            if (!n.hasEnded)\n                            {\n                                y1 = 1.0;\n                                if (interpolateUnendedNotes)\n                                    n.meta = 1.0f;\n                            }\n                            else if (interpolateUnendedNotes)\n                            {\n                                if (n.meta == null) n.meta = 0f;\n                                n.meta = (float)n.meta - interpolateUnendedNotesVal;\n                                if ((float)n.meta < 0f) n.meta = 0f;\n                                y1 = 1.0 * (float)n.meta + y1 * (1 - (float)n.meta);\n                            }\n                            double texSize = (y1 - y2) / wdth / viewAspect;\n                            NoteTexture ntex = null;\n                            int tex = 0;\n                            foreach (var t in noteTextures)\n                            {\n                                if (t.maxSize > texSize)\n                                {\n                                    if (t.keyType != KeyType.Both)\n                                    {\n                                        if (blackKeys[k] ^ (t.keyType == KeyType.Black))\n                                        {\n                                            tex++;\n                                            continue;\n                                        }\n                                    }\n                                    ntex = t;\n                                    break;\n                                }\n                                tex++;\n                            }\n                            tex *= 3;\n\n                            if (ntex.highlightHitNotes > 0 && n.start <= midiTime)\n                            {\n                                var col = ntex.highlightHitNotesColor;\n                                float blendfac = (float)ntex.highlightHitNotes;\n                                float iblendfac = 1 - blendfac;\n                                coll = new Color4(\n                                    coll.R * iblendfac + col.R / 255.0f * blendfac,\n                                    coll.G * iblendfac + col.G / 255.0f * blendfac,\n                                    coll.B * iblendfac + col.B / 255.0f * blendfac,\n                                    coll.A\n                                );\n                                colr = new Color4(\n                                    colr.R * iblendfac + col.R / 255.0f * blendfac,\n                                    colr.G * iblendfac + col.G / 255.0f * blendfac,\n                                    colr.B * iblendfac + col.B / 255.0f * blendfac,\n                                    colr.A\n                                );\n                            }\n\n                            double topHeight;\n                            double bottomHeight;\n                            double yy1 = 0, yy2 = 0;\n                            if (ntex.useCaps)\n                            {\n                                topHeight = wdth * ntex.noteTopAspect * viewAspect;\n                                bottomHeight = wdth * ntex.noteBottomAspect * viewAspect;\n                                yy1 = y1 + topHeight * ntex.noteTopOversize;\n                                yy2 = y2 - bottomHeight * ntex.noteBottomOversize;\n                                if (n.hasEnded)\n                                    y1 -= topHeight * (1 - ntex.noteTopOversize);\n                                y2 += bottomHeight * (1 - ntex.noteBottomOversize);\n                                if (y2 > y1)\n                                {\n                                    double middley = (y2 + y1) / 2;\n                                    y1 = middley;\n                                    y2 = middley;\n                                    if (!ntex.squeezeEndCaps)\n                                    {\n                                        yy1 = y1 + topHeight;\n                                        yy2 = y2 - bottomHeight;\n                                    }\n                                }\n                                texSize = (y1 - y2) / wdth / viewAspect;\n                            }\n\n                            if (ntex.stretch)\n                                texSize = 1;\n                            else\n                                texSize /= currNoteTex.noteMiddleAspect;\n                            if (n.hasEnded)\n                                texSize = -Math.Round(texSize);\n                            if (texSize == 0) texSize = -1;\n                            pos = quadBufferPos * 8;\n                            quadVertexbuff[pos++] = x2;\n                            quadVertexbuff[pos++] = y2;\n                            quadVertexbuff[pos++] = x2;\n                            quadVertexbuff[pos++] = y1;\n                            quadVertexbuff[pos++] = x1;\n                            quadVertexbuff[pos++] = y1;\n                            quadVertexbuff[pos++] = x1;\n                            quadVertexbuff[pos++] = y2;\n\n                            pos = quadBufferPos * 16;\n                            if (blackKeys[n.key])\n                            {\n                                float multiply = (float)currNoteTex.darkenBlackNotes;\n                                r = coll.R * multiply;\n                                g = coll.G * multiply;\n                                b = coll.B * multiply;\n                                a = coll.A;\n                                r2 = colr.R * multiply;\n                                g2 = colr.G * multiply;\n                                b2 = colr.B * multiply;\n                                a2 = colr.A;\n                            }\n                            else\n                            {\n                                r = coll.R;\n                                g = coll.G;\n                                b = coll.B;\n                                a = coll.A;\n                                r2 = colr.R;\n                                g2 = colr.G;\n                                b2 = colr.B;\n                                a2 = colr.A;\n                            }\n                            quadColorbuff[pos++] = r;\n                            quadColorbuff[pos++] = g;\n                            quadColorbuff[pos++] = b;\n                            quadColorbuff[pos++] = a;\n                            quadColorbuff[pos++] = r;\n                            quadColorbuff[pos++] = g;\n                            quadColorbuff[pos++] = b;\n                            quadColorbuff[pos++] = a;\n                            quadColorbuff[pos++] = r2;\n                            quadColorbuff[pos++] = g2;\n                            quadColorbuff[pos++] = b2;\n                            quadColorbuff[pos++] = a2;\n                            quadColorbuff[pos++] = r2;\n                            quadColorbuff[pos++] = g2;\n                            quadColorbuff[pos++] = b2;\n                            quadColorbuff[pos++] = a2;\n\n                            pos = quadBufferPos * 8;\n                            quadUVbuff[pos++] = 1;\n                            quadUVbuff[pos++] = 0;\n                            quadUVbuff[pos++] = 1;\n                            quadUVbuff[pos++] = texSize;\n                            quadUVbuff[pos++] = 0;\n                            quadUVbuff[pos++] = texSize;\n                            quadUVbuff[pos++] = 0;\n                            quadUVbuff[pos++] = 0;\n\n                            pos = quadBufferPos * 4;\n                            quadTexIDbuff[pos++] = tex;\n                            quadTexIDbuff[pos++] = tex;\n                            quadTexIDbuff[pos++] = tex;\n                            quadTexIDbuff[pos++] = tex;\n\n                            quadBufferPos++;\n                            FlushQuadBuffer();\n\n                            if (ntex.useCaps)\n                            {\n                                pos = quadBufferPos * 8;\n                                quadVertexbuff[pos++] = x2;\n                                quadVertexbuff[pos++] = yy2;\n                                quadVertexbuff[pos++] = x2;\n                                quadVertexbuff[pos++] = y2;\n                                quadVertexbuff[pos++] = x1;\n                                quadVertexbuff[pos++] = y2;\n                                quadVertexbuff[pos++] = x1;\n                                quadVertexbuff[pos++] = yy2;\n\n                                pos = quadBufferPos * 16;\n                                quadColorbuff[pos++] = r;\n                                quadColorbuff[pos++] = g;\n                                quadColorbuff[pos++] = b;\n                                quadColorbuff[pos++] = a;\n                                quadColorbuff[pos++] = r;\n                                quadColorbuff[pos++] = g;\n                                quadColorbuff[pos++] = b;\n                                quadColorbuff[pos++] = a;\n                                quadColorbuff[pos++] = r2;\n                                quadColorbuff[pos++] = g2;\n                                quadColorbuff[pos++] = b2;\n                                quadColorbuff[pos++] = a2;\n                                quadColorbuff[pos++] = r2;\n                                quadColorbuff[pos++] = g2;\n                                quadColorbuff[pos++] = b2;\n                                quadColorbuff[pos++] = a2;\n\n                                pos = quadBufferPos * 8;\n                                quadUVbuff[pos++] = 1;\n                                quadUVbuff[pos++] = 1;\n                                quadUVbuff[pos++] = 1;\n                                quadUVbuff[pos++] = 0;\n                                quadUVbuff[pos++] = 0;\n                                quadUVbuff[pos++] = 0;\n                                quadUVbuff[pos++] = 0;\n                                quadUVbuff[pos++] = 1;\n\n                                pos = quadBufferPos * 4;\n                                tex++;\n                                quadTexIDbuff[pos++] = tex;\n                                quadTexIDbuff[pos++] = tex;\n                                quadTexIDbuff[pos++] = tex;\n                                quadTexIDbuff[pos++] = tex;\n\n                                quadBufferPos++;\n                                FlushQuadBuffer();\n\n                                pos = quadBufferPos * 8;\n                                quadVertexbuff[pos++] = x2;\n                                quadVertexbuff[pos++] = y1;\n                                quadVertexbuff[pos++] = x2;\n                                quadVertexbuff[pos++] = yy1;\n                                quadVertexbuff[pos++] = x1;\n                                quadVertexbuff[pos++] = yy1;\n                                quadVertexbuff[pos++] = x1;\n                                quadVertexbuff[pos++] = y1;\n\n                                pos = quadBufferPos * 16;\n                                quadColorbuff[pos++] = r;\n                                quadColorbuff[pos++] = g;\n                                quadColorbuff[pos++] = b;\n                                quadColorbuff[pos++] = a;\n                                quadColorbuff[pos++] = r;\n                                quadColorbuff[pos++] = g;\n                                quadColorbuff[pos++] = b;\n                                quadColorbuff[pos++] = a;\n                                quadColorbuff[pos++] = r2;\n                                quadColorbuff[pos++] = g2;\n                                quadColorbuff[pos++] = b2;\n                                quadColorbuff[pos++] = a2;\n                                quadColorbuff[pos++] = r2;\n                                quadColorbuff[pos++] = g2;\n                                quadColorbuff[pos++] = b2;\n                                quadColorbuff[pos++] = a2;\n\n                                pos = quadBufferPos * 8;\n                                quadUVbuff[pos++] = 1;\n                                quadUVbuff[pos++] = 1;\n                                quadUVbuff[pos++] = 1;\n                                quadUVbuff[pos++] = 0;\n                                quadUVbuff[pos++] = 0;\n                                quadUVbuff[pos++] = 0;\n                                quadUVbuff[pos++] = 0;\n                                quadUVbuff[pos++] = 1;\n\n                                pos = quadBufferPos * 4;\n                                tex++;\n                                quadTexIDbuff[pos++] = tex;\n                                quadTexIDbuff[pos++] = tex;\n                                quadTexIDbuff[pos++] = tex;\n                                quadTexIDbuff[pos++] = tex;\n\n                                quadBufferPos++;\n                                FlushQuadBuffer();\n                            }\n\n                        }\n                        else break;\n                    }\n                    else\n                    { }\n                }\n            }\n            FlushQuadBuffer(false);\n            quadBufferPos = 0;\n\n            LastNoteCount = nc;\n            #endregion\n\n            RenderOverlays(true);\n\n            #region Keyboard\n\n            GL.UseProgram(quadShader);\n            GL.ActiveTexture(TextureUnit.Texture0);\n            GL.BindTexture(TextureTarget.Texture2D, currPack.barTexID);\n            pos = quadBufferPos * 8;\n            quadVertexbuff[pos++] = 0;\n            quadVertexbuff[pos++] = keyboardHeightFull;\n            quadVertexbuff[pos++] = 1;\n            quadVertexbuff[pos++] = keyboardHeightFull;\n            quadVertexbuff[pos++] = 1;\n            quadVertexbuff[pos++] = keyboardHeight;\n            quadVertexbuff[pos++] = 0;\n            quadVertexbuff[pos++] = keyboardHeight;\n\n            pos = quadBufferPos * 16;\n            quadColorbuff[pos++] = 1;\n            quadColorbuff[pos++] = 1;\n            quadColorbuff[pos++] = 1;\n            quadColorbuff[pos++] = 1;\n            quadColorbuff[pos++] = 1;\n            quadColorbuff[pos++] = 1;\n            quadColorbuff[pos++] = 1;\n            quadColorbuff[pos++] = 1;\n            quadColorbuff[pos++] = 1;\n            quadColorbuff[pos++] = 1;\n            quadColorbuff[pos++] = 1;\n            quadColorbuff[pos++] = 1;\n            quadColorbuff[pos++] = 1;\n            quadColorbuff[pos++] = 1;\n            quadColorbuff[pos++] = 1;\n            quadColorbuff[pos++] = 1;\n\n            pos = quadBufferPos * 8;\n            quadUVbuff[pos++] = 0;\n            quadUVbuff[pos++] = 0;\n            quadUVbuff[pos++] = 1;\n            quadUVbuff[pos++] = 0;\n            quadUVbuff[pos++] = 1;\n            quadUVbuff[pos++] = 1;\n            quadUVbuff[pos++] = 0;\n            quadUVbuff[pos++] = 1;\n\n            pos = quadBufferPos * 4;\n            quadTexIDbuff[pos++] = 0;\n            quadTexIDbuff[pos++] = 0;\n            quadTexIDbuff[pos++] = 0;\n            quadTexIDbuff[pos++] = 0;\n            quadBufferPos++;\n            FlushQuadBuffer(false);\n\n            y1 = keyboardHeight;\n            y2 = 0;\n            Color4[] origColors = new Color4[257];\n            SwitchShader(currPack.whiteKeyShader);\n            GL.ActiveTexture(TextureUnit.Texture1);\n            GL.BindTexture(TextureTarget.Texture2D, currPack.whiteKeyPressedTexID);\n            GL.ActiveTexture(TextureUnit.Texture0);\n            GL.BindTexture(TextureTarget.Texture2D, currPack.whiteKeyTexID);\n            if (currPack.whiteKeyLeftTex != null)\n            {\n                GL.ActiveTexture(TextureUnit.Texture3);\n                GL.BindTexture(TextureTarget.Texture2D, currPack.whiteKeyPressedLeftTexID);\n                GL.ActiveTexture(TextureUnit.Texture2);\n                GL.BindTexture(TextureTarget.Texture2D, currPack.whiteKeyLeftTexID);\n            }\n            if (currPack.whiteKeyRightTex != null)\n            {\n                GL.ActiveTexture(TextureUnit.Texture5);\n                GL.BindTexture(TextureTarget.Texture2D, currPack.whiteKeyPressedRightTexID);\n                GL.ActiveTexture(TextureUnit.Texture4);\n                GL.BindTexture(TextureTarget.Texture2D, currPack.whiteKeyRightTexID);\n            }\n            for (int k = kbfirstNote; k < kblastNote; k++)\n            {\n                if (isBlackNote(k))\n                    origColors[k] = Color4.Black;\n                else\n                    origColors[k] = Color4.White;\n            }\n\n            float pressed;\n            for (int n = kbfirstNote; n < kblastNote; n++)\n            {\n                x1 = x1arrayKeys[n];\n                wdth = wdtharrayKeys[n];\n                x2 = x1 + wdth;\n\n                if (!blackKeys[n])\n                {\n                    y2 = 0;\n                    if (sameWidth)\n                    {\n                        int _n = n % 12;\n                        if (_n == 0)\n                            x2 += wdth * 0.666;\n                        else if (_n == 2)\n                        {\n                            x1 -= wdth / 3;\n                            x2 += wdth / 3;\n                        }\n                        else if (_n == 4)\n                            x1 -= wdth / 3 * 2;\n                        else if (_n == 5)\n                            x2 += wdth * 0.75;\n                        else if (_n == 7)\n                        {\n                            x1 -= wdth / 4;\n                            x2 += wdth / 2;\n                        }\n                        else if (_n == 9)\n                        {\n                            x1 -= wdth / 2;\n                            x2 += wdth / 4;\n                        }\n                        else if (_n == 11)\n                            x1 -= wdth * 0.75;\n                    }\n                }\n                else continue;\n\n                var coll = keyColors[n * 2];\n                var colr = keyColors[n * 2 + 1];\n                var origcol = origColors[n];\n                float blendfac1 = coll.A;\n                float revblendfac = 1 - blendfac1;\n                coll = new Color4(\n                    coll.R * blendfac1 + origcol.R * revblendfac,\n                    coll.G * blendfac1 + origcol.G * revblendfac,\n                    coll.B * blendfac1 + origcol.B * revblendfac,\n                    1);\n                r = coll.R;\n                g = coll.G;\n                b = coll.B;\n                a = coll.A;\n                float blendfac2 = coll.A;\n                blendfac2 = colr.A;\n                revblendfac = 1 - blendfac2;\n                colr = new Color4(\n                    colr.R * blendfac2 + origcol.R * revblendfac,\n                    colr.G * blendfac2 + origcol.G * revblendfac,\n                    colr.B * blendfac2 + origcol.B * revblendfac,\n                    1);\n                r2 = colr.R;\n                g2 = colr.G;\n                b2 = colr.B;\n                a2 = colr.A;\n                if (blendfac1 + blendfac2 != 0) pressed = 1;\n                else pressed = 0;\n\n                if (currPack.whiteKeyLeftTex != null && n == kbfirstNote && !blackKeys[n])\n                    pressed += 2;\n                else if (currPack.whiteKeyRightTex != null && n == kblastNote - 1 && !blackKeys[n])\n                    pressed += 4;\n\n                double yy1 = y1;\n                if (pressed == 1) yy1 += keyboardHeightFull * currPack.whiteKeyPressedOversize;\n                else yy1 += keyboardHeightFull * currPack.whiteKeyOversize;\n\n                pos = quadBufferPos * 8;\n                quadVertexbuff[pos++] = x1;\n                quadVertexbuff[pos++] = y2;\n                quadVertexbuff[pos++] = x2;\n                quadVertexbuff[pos++] = y2;\n                quadVertexbuff[pos++] = x2;\n                quadVertexbuff[pos++] = yy1;\n                quadVertexbuff[pos++] = x1;\n                quadVertexbuff[pos++] = yy1;\n\n                pos = quadBufferPos * 16;\n                quadColorbuff[pos++] = r;\n                quadColorbuff[pos++] = g;\n                quadColorbuff[pos++] = b;\n                quadColorbuff[pos++] = a;\n                quadColorbuff[pos++] = r;\n                quadColorbuff[pos++] = g;\n                quadColorbuff[pos++] = b;\n                quadColorbuff[pos++] = a;\n                quadColorbuff[pos++] = r2;\n                quadColorbuff[pos++] = g2;\n                quadColorbuff[pos++] = b2;\n                quadColorbuff[pos++] = a2;\n                quadColorbuff[pos++] = r2;\n                quadColorbuff[pos++] = g2;\n                quadColorbuff[pos++] = b2;\n                quadColorbuff[pos++] = a2;\n\n                if (!currPack.whiteKeysFullOctave)\n                {\n                    pos = quadBufferPos * 8;\n                    quadUVbuff[pos++] = 0;\n                    quadUVbuff[pos++] = 1;\n                    quadUVbuff[pos++] = 1;\n                    quadUVbuff[pos++] = 1;\n                    quadUVbuff[pos++] = 1;\n                    quadUVbuff[pos++] = 0;\n                    quadUVbuff[pos++] = 0;\n                    quadUVbuff[pos++] = 0;\n                }\n                else\n                {\n                    var k = keynum[n] % 7;\n                    double uvl = k / 7.0;\n                    double uvr = (k + 1) / 7.0;\n                    pos = quadBufferPos * 8;\n                    quadUVbuff[pos++] = uvl;\n                    quadUVbuff[pos++] = 1;\n                    quadUVbuff[pos++] = uvr;\n                    quadUVbuff[pos++] = 1;\n                    quadUVbuff[pos++] = uvr;\n                    quadUVbuff[pos++] = 0;\n                    quadUVbuff[pos++] = uvl;\n                    quadUVbuff[pos++] = 0;\n                }\n\n                pos = quadBufferPos * 4;\n                quadTexIDbuff[pos++] = pressed;\n                quadTexIDbuff[pos++] = pressed;\n                quadTexIDbuff[pos++] = pressed;\n                quadTexIDbuff[pos++] = pressed;\n                quadBufferPos++;\n                FlushQuadBuffer();\n            }\n            FlushQuadBuffer(false);\n            SwitchShader(currPack.blackKeyShader);\n            GL.ActiveTexture(TextureUnit.Texture1);\n            GL.BindTexture(TextureTarget.Texture2D, currPack.blackKeyPressedTexID);\n            GL.ActiveTexture(TextureUnit.Texture0);\n            GL.BindTexture(TextureTarget.Texture2D, currPack.blackKeyTexID);\n            for (int n = kbfirstNote; n < kblastNote; n++)\n            {\n                x1 = x1arrayKeys[n];\n                wdth = wdtharrayKeys[n];\n                x2 = x1 + wdth;\n\n                if (blackKeys[n])\n                {\n                    y2 = keyboardHeight * currPack.blackKeyHeight;\n                }\n                else continue;\n\n                var coll = keyColors[n * 2];\n                var colr = keyColors[n * 2 + 1];\n                var origcol = origColors[n];\n                float blendfac1 = coll.A;\n                float revblendfac = 1 - blendfac1;\n                coll = new Color4(\n                    coll.R * blendfac1 + origcol.R * revblendfac,\n                    coll.G * blendfac1 + origcol.G * revblendfac,\n                    coll.B * blendfac1 + origcol.B * revblendfac,\n                    1);\n                r = coll.R;\n                g = coll.G;\n                b = coll.B;\n                a = coll.A;\n                float blendfac2 = coll.A;\n                blendfac2 = colr.A;\n                revblendfac = 1 - blendfac2;\n                colr = new Color4(\n                    colr.R * blendfac2 + origcol.R * revblendfac,\n                    colr.G * blendfac2 + origcol.G * revblendfac,\n                    colr.B * blendfac2 + origcol.B * revblendfac,\n                    1);\n                r2 = colr.R;\n                g2 = colr.G;\n                b2 = colr.B;\n                a2 = colr.A;\n                if (blendfac1 + blendfac2 != 0) pressed = 1;\n                else pressed = 0;\n\n                double yy1 = y1;\n                if (pressed == 1) yy1 += keyboardHeightFull * currPack.blackKeyPressedOversize;\n                else yy1 += keyboardHeightFull * currPack.blackKeyOversize;\n                if (pressed == 0 && currPack.blackKeyDefaultWhite)\n                {\n                    r = g = b = a = 1;\n                    r2 = g2 = b2 = a2 = 1;\n                }\n\n                pos = quadBufferPos * 8;\n                quadVertexbuff[pos++] = x1;\n                quadVertexbuff[pos++] = y2;\n                quadVertexbuff[pos++] = x2;\n                quadVertexbuff[pos++] = y2;\n                quadVertexbuff[pos++] = x2;\n                quadVertexbuff[pos++] = yy1;\n                quadVertexbuff[pos++] = x1;\n                quadVertexbuff[pos++] = yy1;\n\n                pos = quadBufferPos * 16;\n                quadColorbuff[pos++] = r;\n                quadColorbuff[pos++] = g;\n                quadColorbuff[pos++] = b;\n                quadColorbuff[pos++] = a;\n                quadColorbuff[pos++] = r;\n                quadColorbuff[pos++] = g;\n                quadColorbuff[pos++] = b;\n                quadColorbuff[pos++] = a;\n                quadColorbuff[pos++] = r2;\n                quadColorbuff[pos++] = g2;\n                quadColorbuff[pos++] = b2;\n                quadColorbuff[pos++] = a2;\n                quadColorbuff[pos++] = r2;\n                quadColorbuff[pos++] = g2;\n                quadColorbuff[pos++] = b2;\n                quadColorbuff[pos++] = a2;\n\n                if (!currPack.blackKeysFullOctave)\n                {\n                    pos = quadBufferPos * 8;\n                    quadUVbuff[pos++] = 0;\n                    quadUVbuff[pos++] = 1;\n                    quadUVbuff[pos++] = 1;\n                    quadUVbuff[pos++] = 1;\n                    quadUVbuff[pos++] = 1;\n                    quadUVbuff[pos++] = 0;\n                    quadUVbuff[pos++] = 0;\n                    quadUVbuff[pos++] = 0;\n                }\n                else\n                {\n                    var k = keynum[n] % 5;\n                    double uvl = k / 5.0;\n                    double uvr = (k + 1) / 5.0;\n                    pos = quadBufferPos * 8;\n                    quadUVbuff[pos++] = uvl;\n                    quadUVbuff[pos++] = 1;\n                    quadUVbuff[pos++] = uvr;\n                    quadUVbuff[pos++] = 1;\n                    quadUVbuff[pos++] = uvr;\n                    quadUVbuff[pos++] = 0;\n                    quadUVbuff[pos++] = uvl;\n                    quadUVbuff[pos++] = 0;\n                }\n\n                pos = quadBufferPos * 4;\n                quadTexIDbuff[pos++] = pressed;\n                quadTexIDbuff[pos++] = pressed;\n                quadTexIDbuff[pos++] = pressed;\n                quadTexIDbuff[pos++] = pressed;\n\n                quadBufferPos++;\n                FlushQuadBuffer(true);\n            }\n            FlushQuadBuffer(false);\n            #endregion\n\n            RenderOverlays(false);\n\n            GL.ActiveTexture(TextureUnit.Texture0);\n            GL.Disable(EnableCap.Blend);\n            GL.DisableClientState(ArrayCap.VertexArray);\n            GL.DisableClientState(ArrayCap.ColorArray);\n            GL.Disable(EnableCap.Texture2D);\n\n            GL.DisableVertexAttribArray(0);\n            GL.DisableVertexAttribArray(1);\n            GL.DisableVertexAttribArray(2);\n            GL.DisableVertexAttribArray(3);\n        }\n\n        void RenderOverlays(bool below)\n        {\n            double getx1(int key)\n            {\n                int k = key % 12;\n                if (k < 0) k += 12;\n                int o = (key - k) / 12;\n\n                return x1arrayKeys[k] + (x1arrayKeys[12] - x1arrayKeys[0]) * o;\n            }\n\n            double getwdth(int key)\n            {\n                int k = key % 12;\n                if (k < 0) k += 12;\n                if (isBlackNote(k)) return wdtharrayKeys[1];\n                else return wdtharrayKeys[0];\n            }\n\n            double viewAspect = (double)renderSettings.width / renderSettings.height;\n            foreach (var o in currPack.OverlayTextures)\n            {\n                if (o.overlayBelow != below) continue;\n                int pos;\n                GL.UseProgram(quadShader);\n                GL.ActiveTexture(TextureUnit.Texture0);\n                GL.BindTexture(TextureTarget.Texture2D, o.texID);\n                pos = quadBufferPos * 8;\n                double start = getx1(o.firstKey);\n                double end = getx1(o.lastKey) + getwdth(o.lastKey);\n                double height = Math.Abs(start - end) / o.texAspect * viewAspect;\n                quadVertexbuff[pos++] = start;\n                quadVertexbuff[pos++] = height;\n                quadVertexbuff[pos++] = end;\n                quadVertexbuff[pos++] = height;\n                quadVertexbuff[pos++] = end;\n                quadVertexbuff[pos++] = 0;\n                quadVertexbuff[pos++] = start;\n                quadVertexbuff[pos++] = 0;\n\n                pos = quadBufferPos * 16;\n                quadColorbuff[pos++] = 1;\n                quadColorbuff[pos++] = 1;\n                quadColorbuff[pos++] = 1;\n                quadColorbuff[pos++] = (float)o.alpha;\n                quadColorbuff[pos++] = 1;\n                quadColorbuff[pos++] = 1;\n                quadColorbuff[pos++] = 1;\n                quadColorbuff[pos++] = (float)o.alpha;\n                quadColorbuff[pos++] = 1;\n                quadColorbuff[pos++] = 1;\n                quadColorbuff[pos++] = 1;\n                quadColorbuff[pos++] = (float)o.alpha;\n                quadColorbuff[pos++] = 1;\n                quadColorbuff[pos++] = 1;\n                quadColorbuff[pos++] = 1;\n                quadColorbuff[pos++] = (float)o.alpha;\n\n                pos = quadBufferPos * 8;\n                quadUVbuff[pos++] = 0;\n                quadUVbuff[pos++] = 0;\n                quadUVbuff[pos++] = 1;\n                quadUVbuff[pos++] = 0;\n                quadUVbuff[pos++] = 1;\n                quadUVbuff[pos++] = 1;\n                quadUVbuff[pos++] = 0;\n                quadUVbuff[pos++] = 1;\n\n                pos = quadBufferPos * 4;\n                quadTexIDbuff[pos++] = 0;\n                quadTexIDbuff[pos++] = 0;\n                quadTexIDbuff[pos++] = 0;\n                quadTexIDbuff[pos++] = 0;\n                quadBufferPos++;\n                FlushQuadBuffer(false);\n            }\n        }\n\n        void FlushQuadBuffer(bool check = true)\n        {\n            if (quadBufferPos < quadBufferLength && check) return;\n            if (quadBufferPos == 0) return;\n            GL.BindBuffer(BufferTarget.ArrayBuffer, vertexBufferID);\n            GL.BufferData(\n                BufferTarget.ArrayBuffer,\n                (IntPtr)(quadBufferPos * 8 * 8),\n                quadVertexbuff,\n                BufferUsageHint.StaticDraw);\n            GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Double, false, 16, 0);\n            GL.BindBuffer(BufferTarget.ArrayBuffer, colorBufferID);\n            GL.BufferData(\n                BufferTarget.ArrayBuffer,\n                (IntPtr)(quadBufferPos * 16 * 4),\n                quadColorbuff,\n                BufferUsageHint.StaticDraw);\n            GL.VertexAttribPointer(1, 4, VertexAttribPointerType.Float, false, 16, 0);\n            GL.BindBuffer(BufferTarget.ArrayBuffer, uvBufferID);\n            GL.BufferData(\n                BufferTarget.ArrayBuffer,\n                (IntPtr)(quadBufferPos * 8 * 8),\n                quadUVbuff,\n                BufferUsageHint.StaticDraw);\n            GL.VertexAttribPointer(2, 2, VertexAttribPointerType.Double, false, 16, 0);\n            GL.BindBuffer(BufferTarget.ArrayBuffer, texIDBufferID);\n            GL.BufferData(\n                BufferTarget.ArrayBuffer,\n                (IntPtr)(quadBufferPos * 1 * 4 * 4),\n                quadTexIDbuff,\n                BufferUsageHint.StaticDraw);\n            GL.VertexAttribPointer(3, 1, VertexAttribPointerType.Float, false, 4, 0);\n            GL.VertexAttribPointer(3, 1, VertexAttribPointerType.Float, false, 4, 0);\n            GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexBufferId);\n            GL.IndexPointer(IndexPointerType.Int, 1, 0);\n            GL.DrawElements(PrimitiveType.Triangles, quadBufferPos * 6, DrawElementsType.UnsignedInt, IntPtr.Zero);\n            quadBufferPos = 0;\n        }\n\n        bool isBlackNote(int n)\n        {\n            n = n % 12;\n            return n == 1 || n == 3 || n == 6 || n == 8 || n == 10;\n        }\n\n        public void ReloadTrackColors()\n        {\n            if (NoteColors == null) return;\n            var cols = ((SettingsCtrl)SettingsControl).paletteList.GetColors(NoteColors.Length);\n\n            for (int i = 0; i < NoteColors.Length; i++)\n            {\n                for (int j = 0; j < NoteColors[i].Length; j++)\n                {\n                    if (NoteColors[i][j].isDefault)\n                    {\n                        NoteColors[i][j].left = cols[i * 32 + j * 2];\n                        NoteColors[i][j].right = cols[i * 32 + j * 2 + 1];\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "TexturedRender/Resources/pack.json",
    "content": "﻿{\n  //Any line that starts with \"//\" is a comment and can be removed\n  \"description\": \"Default resource pack for Zenith's textured plugin.\",\n  \"sameWidthNotes\": \"false\",\n\n  \"previewImage\": \"preview.png\",\n\n  \"blackKey\": \"keyBlack.png\",\n  \"blackKeyPressed\": \"keyBlackPressed.png\",\n  \"whiteKey\": \"keyWhite.png\",\n  \"whiteKeyPressed\": \"keyWhitePressed.png\",\n  \"bar\": \"bar.png\",\n\n  \"blackKeyOversize\": 3,\n  \"blackKeyPressedOversize\": 1,\n\n  \"blackKeysWhiteShade\": \"true\",\n  \"blackKeyHeight\": 40,\n  //Note type maximum sizes (aspect ratios)\n  //Warning: using more than one note height texture is much slower\n  \"notes\": [\n    {\n      //Size is relative to width. Size = note_height / note_width\n      \"maxSize\": 4,\n      //alwaysStretch means that it will never tile the texture\n      \"alwaysStretch\": \"true\",\n      \"useEndCaps\": \"true\",\n      \"middleTexture\": \"note.png\",\n      \"topTexture\": \"noteEdge.png\",\n      \"bottomTexture\": \"noteEdge.png\",\n      \"topOversize\": 0,\n      \"bottomOversize\": 0\n    }\n  ]\n}"
  },
  {
    "path": "TexturedRender/Settings.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace TexturedRender\n{\n    public class Settings\n    {\n        public int firstNote = 0;\n        public int lastNote = 128;\n        public double deltaTimeOnScreen = 300;\n        public bool sameWidthNotes = true;\n        public bool blackNotesAbove = true;\n\n        public string palette = \"Random\";\n\n        public Pack currPack = null;\n        public long lastPackChangeTime = 0;\n    }\n}\n"
  },
  {
    "path": "TexturedRender/SettingsCtrl.xaml",
    "content": "﻿<UserControl\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" \n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\" \n             xmlns:ui=\"clr-namespace:ZenithEngine.UI;assembly=ZenithEngine\"\n             xmlns:local=\"clr-namespace:TexturedRender\"\n             xmlns:bme=\"clr-namespace:ZenithEngine;assembly=ZenithEngine\"\n             xmlns:xctk=\"http://schemas.xceed.com/wpf/xaml/toolkit\" x:Class=\"TexturedRender.SettingsCtrl\"\n             mc:Ignorable=\"d\" \n             d:DesignHeight=\"450\" d:DesignWidth=\"800\">\n    <UserControl.Resources>\n        <ResourceDictionary>\n            <ResourceDictionary.MergedDictionaries>\n                <ResourceDictionary>\n                    <ResourceDictionary.MergedDictionaries>\n                        <ResourceDictionary Source=\"pack://siteoforigin:,,,/Languages/en/textured.xaml\" />\n                    </ResourceDictionary.MergedDictionaries>\n                </ResourceDictionary>\n                <ResourceDictionary Source=\"pack://application:,,,/ZenithEngine;component/UI/Material.xaml\"/>\n            </ResourceDictionary.MergedDictionaries>\n            <Style TargetType=\"TabControl\" BasedOn=\"{StaticResource SubTabs}\"/>\n            <Style TargetType=\"TabItem\" BasedOn=\"{StaticResource SubTabItems}\"/>\n        </ResourceDictionary>\n    </UserControl.Resources>\n    <Grid>\n        <TabControl Margin=\"10\">\n            <TabItem Header=\"{DynamicResource resourcesTab}\">\n                <Grid Margin=\"10\">\n                    <Grid.ColumnDefinitions>\n                        <ColumnDefinition Width=\"204*\"/>\n                        <ColumnDefinition Width=\"371*\"/>\n                        <ColumnDefinition Width=\"199*\"/>\n                    </Grid.ColumnDefinitions>\n                    <bme:NoteColorPalettePick x:FieldModifier=\"public\" x:Name=\"paletteList\" Margin=\"0\" Grid.Column=\"2\" Grid.RowSpan=\"2\" />\n                    <DockPanel LastChildFill=\"True\" Grid.RowSpan=\"2\">\n                        <Button x:Name=\"reloadListButton\" Content=\"{DynamicResource reloadList}\" Margin=\"0,0,0,0\" Height=\"26\" DockPanel.Dock=\"Top\" Click=\"ReloadButton_Click\"/>\n                        <Button x:Name=\"reloadPackButton\" Content=\"{DynamicResource reloadPack}\" Margin=\"0,10,0,0\" Height=\"26\" DockPanel.Dock=\"Top\" Click=\"ReloadPackButton_Click\"/>\n                        <Button x:Name=\"openFolderButton\" Content=\"{DynamicResource openFolder}\" Margin=\"0,10,0,0\" Height=\"26\" DockPanel.Dock=\"Bottom\" Click=\"openFolderButton_Click\"/>\n                        <ListBox x:Name=\"pluginList\" Margin=\"0,10,0,0\" SelectionChanged=\"PluginList_SelectionChanged\"/>\n                    </DockPanel>\n                    <DockPanel Grid.Column=\"1\" LastChildFill=\"True\">\n                    <TextBox MinHeight=\"100\" DockPanel.Dock=\"Bottom\" TextAlignment=\"Center\" x:Name=\"pluginDesc\" Grid.Column=\"1\" Margin=\"10,10,10,0\" TextWrapping=\"Wrap\" IsEnabled=\"False\" Grid.Row=\"1\"/>\n                    <Image x:Name=\"previewImg\" Grid.Column=\"1\" Margin=\"10,0,10,0\"/>\n                    </DockPanel>\n                </Grid>\n            </TabItem>\n            <TabItem Header=\"{DynamicResource switchesTab}\" Name=\"switchTab\">\n                <Grid>\n                    <StackPanel Name=\"switchPanel\" Margin=\"10\">\n                    </StackPanel>\n                </Grid>\n            </TabItem>\n            <TabItem Header=\"{DynamicResource miscTab}\">\n                <StackPanel Margin=\"10\">\n                    <DockPanel HorizontalAlignment=\"Left\" LastChildFill=\"False\" Margin=\"0,0,0,0\" VerticalAlignment=\"Top\">\n                        <Label Content=\"{DynamicResource firstNote}\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Top\" DockPanel.Dock=\"Left\"/>\n                        <ui:NumberSelect x:Name=\"firstNote\" Value=\"0\" Maximum=\"254\" Minimum=\"0\" Margin=\"5,0,0,0\" HorizontalAlignment=\"Left\" Width=\"80\" Height=\"26\" VerticalAlignment=\"Top\" ValueChanged=\"Nud_ValueChanged\"  />\n                        <Label Content=\"{DynamicResource lastNote}\" HorizontalAlignment=\"Left\" Margin=\"10,0,0,0\" VerticalAlignment=\"Top\"/>\n                        <ui:NumberSelect x:Name=\"lastNote\" Value=\"127\" Maximum=\"255\" Minimum=\"1\" Margin=\"5,0,0,0\" HorizontalAlignment=\"Left\" Width=\"80\" Height=\"26\" VerticalAlignment=\"Top\" ValueChanged=\"Nud_ValueChanged\"  />\n                    </DockPanel>\n                    <DockPanel HorizontalAlignment=\"Left\" LastChildFill=\"False\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\" Width=\"528\" >\n                        <Label Content=\"{DynamicResource noteScreenTime}\" HorizontalAlignment=\"Left\" Margin=\"0,0,0,0\" VerticalAlignment=\"Top\"/>\n                        <ui:ValueSlider x:Name=\"noteDeltaScreenTime\" Maximum=\"12\" DecimalPoints=\"2\" Minimum=\"2\" TrueMin=\"1\" TrueMax=\"100000\" ValueChanged=\"NoteDeltaScreenTime_ValueChanged\" Width=\"305\" VerticalAlignment=\"Top\"/>\n                    </DockPanel>\n                    <ui:BetterCheckbox x:Name=\"blackNotesAbove\" Text=\"{DynamicResource blackNotesAbove}\" HorizontalAlignment=\"Left\" Margin=\"0,10,0,0\" VerticalAlignment=\"Top\" CheckToggled=\"BlackNotesAbove_Checked\" />\n                </StackPanel>\n            </TabItem>\n        </TabControl>\n    </Grid>\n</UserControl>\n"
  },
  {
    "path": "TexturedRender/SettingsCtrl.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Drawing;\nusing System.IO;\nusing System.IO.Compression;\nusing System.Linq;\nusing System.Reflection;\nusing System.Runtime.Remoting;\nusing System.Security.Cryptography;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\nusing Microsoft.CSharp.RuntimeBinder;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing SharpCompress;\nusing SharpCompress.Archives;\nusing SharpCompress.Archives.Rar;\nusing SharpCompress.Archives.SevenZip;\nusing SharpCompress.Archives.Tar;\nusing Brushes = System.Windows.Media.Brushes;\nusing Path = System.IO.Path;\n\nnamespace TexturedRender\n{\n    /// <summary>\n    /// Interaction logic for SettingsCtrl.xaml\n    /// </summary>\n\n    class PackLocation\n    {\n        public string filename;\n        public PackType type;\n    }\n\n    public partial class SettingsCtrl : UserControl\n    {\n        List<PackLocation> resourcePacks = new List<PackLocation>();\n        Settings settings;\n\n        public event Action PaletteChanged\n        {\n            add { paletteList.PaletteChanged += value; }\n            remove { paletteList.PaletteChanged -= value; }\n        }\n\n        string packPath = \"Plugins\\\\Assets\\\\Textured\\\\Resources\";\n\n        BitmapImage BitmapToImageSource(Bitmap bitmap)\n        {\n            using (MemoryStream memory = new MemoryStream())\n            {\n                bitmap.Save(memory, System.Drawing.Imaging.ImageFormat.Bmp);\n                memory.Position = 0;\n                BitmapImage bitmapimage = new BitmapImage();\n                bitmapimage.BeginInit();\n                bitmapimage.StreamSource = memory;\n                bitmapimage.CacheOption = BitmapCacheOption.OnLoad;\n                bitmapimage.EndInit();\n\n                return bitmapimage;\n            }\n        }\n\n        bool inited = false;\n        public SettingsCtrl(Settings settings)\n        {\n            this.settings = settings;\n            InitializeComponent();\n            noteDeltaScreenTime.nudToSlider = v => Math.Log(v, 2);\n            noteDeltaScreenTime.sliderToNud = v => Math.Pow(2, v);\n            inited = true;\n            paletteList.SetPath(\"Plugins\\\\Assets\\\\Palettes\", 1f);\n            ReloadPacks();\n            SetValues();\n        }\n\n        void WriteDefaultPack()\n        {\n            string dir = Path.Combine(packPath, \"Default\");\n            if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);\n            Properties.Resources.keyBlack.Save(dir + \"\\\\keyBlack.png\");\n            Properties.Resources.keyBlackPressed.Save(dir + \"\\\\keyBlackPressed.png\");\n            Properties.Resources.keyWhite.Save(dir + \"\\\\keyWhite.png\");\n            Properties.Resources.keyWhitePressed.Save(dir + \"\\\\keyWhitePressed.png\");\n            Properties.Resources.note.Save(dir + \"\\\\note.png\");\n            Properties.Resources.bar.Save(dir + \"\\\\bar.png\");\n            Properties.Resources.noteEdge.Save(dir + \"\\\\noteEdge.png\");\n            Properties.Resources.preview.Save(dir + \"\\\\preview.png\");\n            File.WriteAllBytes(dir + \"\\\\pack.json\", Properties.Resources.pack);\n        }\n\n        void ReloadPacks()\n        {\n            int lastSelected = pluginList.SelectedIndex;\n            string lastSelectedName;\n            if (lastSelected == -1)\n            {\n                lastSelectedName = \"Default\";\n                lastSelected = 0;\n            }\n            else\n            {\n                lastSelectedName = (string)((ListBoxItem)pluginList.SelectedItem).Content;\n            }\n\n            string dir = packPath;\n\n            resourcePacks.Clear();\n            WriteDefaultPack();\n            pluginList.Items.Clear();\n            foreach (var p in Directory.GetDirectories(dir))\n            {\n                resourcePacks.Add(new PackLocation() { filename = p, type = PackType.Folder });\n            }\n            foreach (var p in Directory.GetFiles(dir, \"*.zip\"))\n            {\n                resourcePacks.Add(new PackLocation() { filename = p, type = PackType.Zip });\n            }\n            foreach (var p in Directory.GetFiles(dir, \"*.zrp\"))\n            {\n                resourcePacks.Add(new PackLocation() { filename = p, type = PackType.Zrp });\n            }\n            foreach (var p in Directory.GetFiles(dir, \"*.rar\"))\n            {\n                resourcePacks.Add(new PackLocation() { filename = p, type = PackType.Rar });\n            }\n            foreach (var p in Directory.GetFiles(dir, \"*.7z\"))\n            {\n                resourcePacks.Add(new PackLocation() { filename = p, type = PackType.SevenZip });\n            }\n            foreach (var p in Directory.GetFiles(dir, \"*.tar\"))\n            {\n                resourcePacks.Add(new PackLocation() { filename = p, type = PackType.Tar });\n            }\n            foreach (var p in Directory.GetFiles(dir, \"*.tar.bz\"))\n            {\n                resourcePacks.Add(new PackLocation() { filename = p, type = PackType.Tar });\n            }\n            foreach (var p in Directory.GetFiles(dir, \"*.tar.gz\"))\n            {\n                resourcePacks.Add(new PackLocation() { filename = p, type = PackType.Tar });\n            }\n            foreach (var p in Directory.GetFiles(dir, \"*.tar.xz\"))\n            {\n                resourcePacks.Add(new PackLocation() { filename = p, type = PackType.Tar });\n            }\n\n            resourcePacks.Sort((a, b) =>\n            {\n                return a.filename.CompareTo(b.filename);\n            });\n\n            foreach (var p in resourcePacks)\n            {\n                if (p.type == PackType.Folder)\n                    pluginList.Items.Add(new ListBoxItem()\n                    {\n                        Content = p.filename.Split('\\\\').Last(),\n                        Foreground = Brushes.White\n                    });\n                else\n                    pluginList.Items.Add(new ListBoxItem()\n                    {\n                        Content = p.filename.Split('\\\\').Last(),\n                        Foreground = Brushes.Green\n                    });\n            }\n\n            if ((string)((ListBoxItem)pluginList.Items[lastSelected]).Content == lastSelectedName)\n            {\n                pluginList.SelectedIndex = lastSelected;\n            }\n            else\n            {\n                foreach (ListBoxItem p in pluginList.Items)\n                {\n                    if ((string)p.Content == lastSelectedName)\n                    {\n                        pluginList.SelectedItem = p;\n                        break;\n                    }\n                }\n            }\n        }\n\n        void UnloadPack(Pack r)\n        {\n            lock (r)\n            {\n                if (r.whiteKeyTex != null)\n                    r.whiteKeyTex.Dispose();\n                if (r.blackKeyTex != null)\n                    r.blackKeyTex.Dispose();\n                if (r.whiteKeyPressedTex != null)\n                    r.whiteKeyPressedTex.Dispose();\n                if (r.blackKeyPressedTex != null)\n                    r.blackKeyPressedTex.Dispose();\n                if (r.preview != null)\n                    r.preview.Dispose();\n                if (r.NoteTextures != null)\n                    foreach (var n in r.NoteTextures)\n                    {\n                        if (n.noteMiddleTex != null)\n                            n.noteMiddleTex.Dispose();\n                        if (n.noteBottomTex != null)\n                            n.noteBottomTex.Dispose();\n                        if (n.noteTopTex != null)\n                            n.noteTopTex.Dispose();\n                    }\n                if (r.OverlayTextures != null)\n                    foreach (var o in r.OverlayTextures)\n                    {\n                        if (o.tex != null)\n                            o.tex.Dispose();\n                    }\n                r.disposed = true;\n            }\n        }\n\n        T parseType<T>(Pack pack, dynamic o)\n        {\n            if (o == null) throw new RuntimeBinderException();\n            string switchName = null;\n            try\n            {\n                switchName = (string)((JObject)o).GetValue(\"_switch\");\n                if (switchName == null) throw new RuntimeBinderException();\n            }\n            catch\n            {\n                try\n                {\n                    return (T)o;\n                }\n                catch\n                {\n                    throw new Exception(\"value \" + o.ToString() + \" can't be converted to type \" + typeof(T).ToString());\n                }\n            }\n\n            if (!pack.switchValues.ContainsKey(switchName))\n            {\n                throw new Exception(\"switch name not found: \" + switchName);\n            }\n\n            dynamic _o;\n            try\n            {\n                _o = ((JObject)o).GetValue(pack.switchValues[switchName]);\n            }\n            catch\n            {\n                throw new Exception(\"value \" + pack.switchValues[switchName] + \" not found on a switch\");\n            }\n\n            try\n            {\n                return parseType<T>(pack, _o);\n            }\n            catch\n            {\n                throw new Exception(\"value \" + _o.ToString() + \" can't be converted to type \" + typeof(T).ToString());\n            }\n        }\n\n        Pack LoadPack(string p, PackType type, Dictionary<string, string> switches = null, Dictionary<string, string[]> assertSwitches = null)\n        {\n            var pack = new Pack() { name = p.Split('\\\\').Last() };\n            string pbase = \"\";\n\n            ZipArchive archive = null;\n            IArchive compress = null;\n            archive = null;\n            MemoryStream zrpstream = null;\n\n            TextureShaderType strToShader(string s)\n            {\n                if (s == \"normal\") return TextureShaderType.Normal;\n                if (s == \"inverse\") return TextureShaderType.Inverted;\n                if (s == \"hybrid\") return TextureShaderType.Hybrid;\n                throw new Exception(\"Unknown shader type \\\"\" + s + \"\\\"\");\n            }\n\n            KeyType strToKeyType(string s)\n            {\n                if (s == \"black\") return KeyType.Black;\n                if (s == \"white\") return KeyType.White;\n                if (s == \"both\") return KeyType.Both;\n                throw new Exception(\"Unknown key type \\\"\" + s + \"\\\"\");\n            }\n\n            Bitmap GetBitmap(string path)\n            {\n                if (type == PackType.Folder)\n                {\n                    if (path == null)\n                    { }\n                    path = Path.Combine(p, pbase, path);\n                    FileStream s;\n                    try\n                    {\n                        s = File.OpenRead(path);\n                    }\n                    catch { throw new Exception(\"Could not open \" + path); }\n                    Bitmap b;\n                    try\n                    {\n                        b = new Bitmap(s);\n                    }\n                    catch { throw new Exception(\"Corrupt image: \" + path); }\n                    s.Close();\n                    return b;\n                }\n                else if (type == PackType.Zip || type == PackType.Zrp)\n                {\n                    path = Path.Combine(pbase, path);\n                    Stream s;\n                    try\n                    {\n                        s = archive.GetEntry(path).Open();\n                    }\n                    catch { throw new Exception(\"Could not open \" + path); }\n                    Bitmap b;\n                    try\n                    {\n                        b = new Bitmap(s);\n                    }\n                    catch { throw new Exception(\"Corrupt image: \" + path); }\n                    s.Close();\n                    return b;\n                }\n                else\n                {\n                    path = Path.Combine(pbase, path).Replace(\"/\", \"\\\\\");\n                    Stream s;\n                    var e = compress.Entries.Where(a => a.Key == path).ToArray();\n                    if (e.Length == 0) throw new Exception(\"Could not open \" + path);\n                    s = e[0].OpenEntryStream();\n                    Bitmap b;\n                    try\n                    {\n                        b = new Bitmap(s);\n                    }\n                    catch { throw new Exception(\"Corrupt image: \" + path); }\n                    s.Close();\n                    return b;\n                }\n            }\n            try\n            {\n                string json = \"\";\n                if (type == PackType.Folder)\n                {\n                    var files = Directory.GetFiles(p, \"*pack.json\", SearchOption.AllDirectories)\n                        .Where(s => s.EndsWith(\"\\\\pack.json\"))\n                        .Select(s => s.Substring(p.Length + 1))\n                        .ToArray();\n                    Array.Sort(files.Select(s => s.Length).ToArray(), files);\n                    var jsonpath = files[0];\n                    pbase = jsonpath.Substring(0, jsonpath.Length - \"pack.json\".Length);\n                    if (files.Length == 0) throw new Exception(\"Could not find pack.json file\");\n                    try\n                    {\n                        json = File.ReadAllText(Path.Combine(p, jsonpath));\n                    }\n                    catch { throw new Exception(\"Could not read pack.json file\"); }\n                }\n                else if (type == PackType.Zip || type == PackType.Zrp)\n                {\n                    if (type == PackType.Zrp)\n                    {\n                        var encoded = File.OpenRead(p);\n                        var key = new byte[16];\n                        var iv = new byte[16];\n                        encoded.Read(key, 0, 16);\n                        encoded.Read(iv, 0, 16);\n\n                        zrpstream = new MemoryStream();\n\n                        using (AesManaged aes = new AesManaged())\n                        {\n                            ICryptoTransform decryptor = aes.CreateDecryptor(key, iv);\n                            using (CryptoStream cs = new CryptoStream(encoded, decryptor, CryptoStreamMode.Read))\n                            {\n                                cs.CopyTo(zrpstream);\n                            }\n                            zrpstream.Position = 0;\n                            archive = new ZipArchive(zrpstream);\n                        }\n                    }\n                    else\n                    {\n                        archive = ZipFile.OpenRead(p);\n                    }\n                    var files = archive.Entries.Where(e => e.Name == \"pack.json\").ToArray();\n                    Array.Sort(files.Select(s => s.FullName.Length).ToArray(), files);\n                    if (files.Length == 0) throw new Exception(\"Could not find pack.json file\");\n                    var jsonfile = files[0];\n                    pbase = jsonfile.FullName.Substring(0, jsonfile.FullName.Length - \"pack.json\".Length);\n                    using (var jfile = new StreamReader(jsonfile.Open()))\n                    {\n                        json = jfile.ReadToEnd();\n                    }\n                }\n                else\n                {\n                    if (type == PackType.Rar)\n                        compress = RarArchive.Open(p);\n                    if (type == PackType.SevenZip)\n                        compress = SevenZipArchive.Open(p);\n                    if (type == PackType.Tar)\n                        compress = TarArchive.Open(p);\n\n                    var files = compress.Entries.Where(e => e.Key.EndsWith(\"\\\\pack.json\") || e.Key == \"pack.json\").ToArray();\n                    Array.Sort(files.Select(s => s.Key.Length).ToArray(), files);\n                    if (files.Length == 0) throw new Exception(\"Could not find pack.json file\");\n                    var jsonfile = files[0];\n                    pbase = jsonfile.Key.Substring(0, jsonfile.Key.Length - \"pack.json\".Length);\n                    using (var jfile = new StreamReader(jsonfile.OpenEntryStream()))\n                    {\n                        json = jfile.ReadToEnd();\n                    }\n                }\n                dynamic data;\n                try\n                {\n                    data = (dynamic)JsonConvert.DeserializeObject(json);\n                }\n                catch { throw new Exception(\"Corrupt json in pack.json\"); }\n                try\n                {\n                    pack.description = data.description;\n                }\n                catch (RuntimeBinderException) { pack.description = \"[no description]\"; }\n\n\n                #region Switches\n                JArray sw = null;\n                bool swIsArray = false;\n                try\n                {\n                    var s = data.switches;\n                    if (s.GetType() == typeof(JArray)) swIsArray = true;\n                    sw = s;\n                }\n                catch (RuntimeBinderException) { }\n                if (sw != null)\n                {\n                    if (!swIsArray) throw new Exception(\"switches must be an array\");\n                    foreach (dynamic s in sw)\n                    {\n                        string swName = null;\n                        List<string> swVals = new List<string>();\n                        dynamic swValArr;\n                        swName = s.name;\n                        if (swName == null)\n                        {\n                            string titleText = s.text;\n                            if (titleText == null)\n                                throw new Exception(\"missing property 'name' or 'text' on switch\");\n                            else\n                            {\n                                pack.switchOrder.Add(titleText);\n                                continue;\n                            }\n                        }\n                        pack.switchOrder.Add(swName);\n                        swValArr = s.values;\n                        if (swValArr == null) throw new Exception(\"missing property 'values' on switch\");\n                        if (swValArr.GetType() != typeof(JArray)) throw new Exception(\"'values' must be a string array\");\n                        if (((JArray)swValArr).Count == 0) throw new Exception(\"'values' array must have at least 1 item\");\n                        foreach (dynamic v in (JArray)swValArr)\n                        {\n                            swVals.Add((string)v);\n                        }\n                        pack.switchChoices.Add(swName, swVals.ToArray());\n                        if (assertSwitches != null)\n                            if (!assertSwitches.ContainsKey(swName) || !swVals.SequenceEqual(assertSwitches[swName]))\n                            {\n                                throw new Exception(\"switches have been changed in pack.json, please reload pack\");\n                            }\n                        pack.switchValues.Add(swName, swVals[0]);\n                    }\n                    if (assertSwitches != null)\n                        if (pack.switchValues.Count != assertSwitches.Count)\n                            throw new Exception(\"switches have been changed in pack.json, please reload pack\");\n                        else\n                            pack.switchValues = switches;\n                }\n                #endregion\n\n\n                string pname;\n                try\n                {\n                    pname = parseType<string>(pack, data.previewImage);\n                    if (pname != null)\n                        pack.preview = GetBitmap(pname);\n                }\n                catch (RuntimeBinderException) { }\n\n                #region Misc\n                try\n                {\n                    pack.keyboardHeight = parseType<double>(pack, data.keyboardHeight) / 100;\n                }\n                catch (RuntimeBinderException) { }\n                try\n                {\n                    pack.sameWidthNotes = parseType<bool>(pack, data.sameWidthNotes);\n                }\n                catch (RuntimeBinderException) { }\n                try\n                {\n                    pack.blackKeysFullOctave = parseType<bool>(pack, data.blackKeysFullOctave);\n                }\n                catch (RuntimeBinderException) { }\n                try\n                {\n                    pack.whiteKeysFullOctave = parseType<bool>(pack, data.whiteKeysFullOctave);\n                }\n                catch (RuntimeBinderException) { }\n                try\n                {\n                    pack.blackKeyHeight = parseType<double>(pack, data.blackKeyHeight) / 100;\n                }\n                catch (RuntimeBinderException) { }\n                try\n                {\n                    pack.blackKeyDefaultWhite = parseType<bool>(pack, data.blackKeysWhiteShade);\n                }\n                catch (RuntimeBinderException) { }\n                try\n                {\n                    pack.linearScaling = parseType<bool>(pack, data.linearTextureScaling);\n                }\n                catch (RuntimeBinderException) { }\n                try\n                {\n                    pack.interpolateUnendedNotes = parseType<float>(pack, data.interpolateUnendedNotes);\n                }\n                catch (RuntimeBinderException) { }\n                #endregion\n\n                #region Shaders\n                string shader = null;\n                try\n                {\n                    shader = parseType<string>(pack, data.noteShader);\n                }\n                catch (RuntimeBinderException) { }\n                if (shader != null) pack.noteShader = strToShader(shader);\n                shader = null;\n                try\n                {\n                    shader = parseType<string>(pack, data.whiteKeyShader);\n                }\n                catch (RuntimeBinderException) { }\n                if (shader != null) pack.whiteKeyShader = strToShader(shader);\n                shader = null;\n                try\n                {\n                    shader = parseType<string>(pack, data.blackKeyShader);\n                }\n                catch (RuntimeBinderException) { }\n                if (shader != null) pack.blackKeyShader = strToShader(shader);\n                #endregion\n\n                #region Get Keys\n                try\n                {\n                    pname = parseType<string>(pack, data.blackKey);\n                }\n                catch (RuntimeBinderException) { throw new Exception(\"Missing property \\\"blackKey\\\"\"); }\n                pack.blackKeyTex = GetBitmap(pname);\n                try\n                {\n                    pname = parseType<string>(pack, data.blackKeyPressed);\n                }\n                catch (RuntimeBinderException) { throw new Exception(\"Missing property \\\"blackKeyPressed\\\"\"); }\n                pack.blackKeyPressedTex = GetBitmap(pname);\n                try\n                {\n                    pname = parseType<string>(pack, data.whiteKey);\n                }\n                catch (RuntimeBinderException) { throw new Exception(\"Missing property \\\"whiteKey\\\"\"); }\n                pack.whiteKeyTex = GetBitmap(pname);\n                try\n                {\n                    pname = parseType<string>(pack, data.whiteKeyPressed);\n                }\n                catch (RuntimeBinderException) { throw new Exception(\"Missing property \\\"whiteKeyPressed\\\"\"); }\n                pack.whiteKeyPressedTex = GetBitmap(pname);\n\n                try\n                {\n                    pname = parseType<string>(pack, data.whiteKeyLeft);\n                    if (pname != null)\n                        pack.whiteKeyLeftTex = GetBitmap(pname);\n                }\n                catch (RuntimeBinderException) { pack.whiteKeyLeftTex = null; }\n                try\n                {\n                    pname = parseType<string>(pack, data.whiteKeyLeftPressed);\n                    if (pname != null)\n                        pack.whiteKeyPressedLeftTex = GetBitmap(pname);\n                }\n                catch (RuntimeBinderException) { pack.whiteKeyPressedLeftTex = null; }\n\n                try\n                {\n                    pname = parseType<string>(pack, data.whiteKeyRight);\n                    if (pname != null)\n                        pack.whiteKeyRightTex = GetBitmap(pname);\n                }\n                catch (RuntimeBinderException) { pack.whiteKeyRightTex = null; }\n                try\n                {\n                    pname = parseType<string>(pack, data.whiteKeyRightPressed);\n                    if (pname != null)\n                        pack.whiteKeyPressedRightTex = GetBitmap(pname);\n                }\n                catch (RuntimeBinderException) { pack.whiteKeyPressedRightTex = null; }\n\n                if ((pack.whiteKeyLeftTex == null) ^ (pack.whiteKeyPressedLeftTex == null))\n                    if (pack.whiteKeyLeftTex == null)\n                        throw new Exception(\"whiteKeyLeft is incliuded while whiteKeyLeftPressed is missing. Include or remove both.\");\n                    else\n                        throw new Exception(\"whiteKeyLeftPressed is incliuded while whiteKeyLeft is missing. Include or remove both.\");\n\n                if ((pack.whiteKeyRightTex == null) ^ (pack.whiteKeyPressedRightTex == null))\n                    if (pack.whiteKeyRightTex == null)\n                        throw new Exception(\"whiteKeyRight is incliuded while whiteKeyRightPressed is missing. Include or remove both.\");\n                    else\n                        throw new Exception(\"whiteKeyRightPressed is incliuded while whiteKeyRight is missing. Include or remove both.\");\n\n                #endregion\n\n                #region Oversizes\n                try\n                {\n                    pack.whiteKeyOversize = parseType<double>(pack, data.whiteKeyOversize) / 100;\n                }\n                catch (RuntimeBinderException) { }\n                try\n                {\n                    pack.blackKeyOversize = parseType<double>(pack, data.blackKeyOversize) / 100;\n                }\n                catch (RuntimeBinderException) { }\n                try\n                {\n                    pack.whiteKeyPressedOversize = parseType<double>(pack, data.whiteKeyPressedOversize) / 100;\n                }\n                catch (RuntimeBinderException) { }\n                try\n                {\n                    pack.blackKeyPressedOversize = parseType<double>(pack, data.blackKeyPressedOversize) / 100;\n                }\n                catch (RuntimeBinderException) { }\n                #endregion\n\n                #region Black Key Sizes\n                try\n                {\n                    pack.blackKey2setOffset = parseType<double>(pack, data.blackKey2setOffset);\n                }\n                catch (RuntimeBinderException) { }\n                try\n                {\n                    pack.blackKey3setOffset = parseType<double>(pack, data.blackKey3setOffset);\n                }\n                catch (RuntimeBinderException) { }\n                try\n                {\n                    pack.blackKeyScale = parseType<double>(pack, data.blackKeyScale);\n                }\n                catch (RuntimeBinderException) { }\n                try\n                {\n                    pack.blackNote2setOffset = parseType<double>(pack, data.blackNote2setOffset);\n                }\n                catch (RuntimeBinderException) { }\n                try\n                {\n                    pack.blackNote3setOffset = parseType<double>(pack, data.blackNote3setOffset);\n                }\n                catch (RuntimeBinderException) { }\n                try\n                {\n                    pack.blackNoteScale = parseType<double>(pack, data.blackNoteScale);\n                }\n                catch (RuntimeBinderException) { }\n                try\n                {\n                    JArray offsets = parseType<JArray>(pack, data.advancedBlackKeyOffsets);\n                    if (offsets.Count != 5) throw new Exception(\"advancedBlackKeyOffsets must have 5 elements\");\n                    pack.advancedBlackKeyOffsets = offsets.Select(s => (double)s).ToArray();\n                }\n                catch (RuntimeBinderException) { }\n                try\n                {\n                    JArray offsets = parseType<JArray>(pack, data.advancedBlackKeySizes);\n                    if (offsets.Count != 5) throw new Exception(\"advancedBlackKeySizes must have 5 elements\");\n                    pack.advancedBlackKeySizes = offsets.Select(s => (double)s).ToArray();\n                }\n                catch (RuntimeBinderException) { }\n                #endregion\n\n                #region Bar\n                try\n                {\n                    pname = parseType<string>(pack, data.bar);\n                    pack.useBar = true;\n                }\n                catch (RuntimeBinderException) { }\n\n                if (pack.useBar)\n                {\n                    pack.barTex = GetBitmap(pname);\n                    try\n                    {\n                        pack.barHeight = parseType<double>(pack, data.barHeight) / 100;\n                    }\n                    catch (RuntimeBinderException) { }\n                }\n                #endregion\n\n                #region Overlays\n                JArray overlaysArray = null;\n                bool notArray = false;\n                try\n                {\n                    var _array = parseType<JArray>(pack, data.overlays);\n                    if (_array != null)\n                    {\n                        notArray = _array.GetType() != typeof(JArray);\n                        if (!notArray) overlaysArray = _array;\n                    }\n                }\n                catch (RuntimeBinderException) { }\n                if (overlaysArray != null)\n                {\n                    if (notArray) throw new Exception(\"overlays must be an array\");\n                    pack.OverlayTextures = overlaysArray.Select((dynamic o) =>\n                    {\n                        o = parseType<JObject>(pack, o);\n                        var overlay = new KeyboardOverlay();\n                        try\n                        {\n                            overlay.firstKey = parseType<int>(pack, o.firstKey);\n                        }\n                        catch (RuntimeBinderException) { throw new Exception(\"firstKey missing on one of the overlay textures\"); }\n                        try\n                        {\n                            overlay.lastKey = parseType<int>(pack, o.lastKey);\n                        }\n                        catch (RuntimeBinderException) { throw new Exception(\"lastKey missing on one of the overlay textures\"); }\n\n                        try\n                        {\n                            overlay.overlayBelow = parseType<bool>(pack, o.belowKeyboard);\n                        }\n                        catch (RuntimeBinderException) { }\n\n                        try\n                        {\n                            pname = parseType<string>(pack, o.texture);\n                        }\n                        catch (RuntimeBinderException) { throw new Exception(\"\\\"texture\\\" missing on one of the overlay textures\"); }\n\n                        overlay.tex = GetBitmap(pname);\n\n                        try\n                        {\n                            overlay.alpha = parseType<double>(pack, o.alpha);\n                        }\n                        catch (RuntimeBinderException) { }\n\n                        overlay.texAspect = overlay.tex.Width / (double)overlay.tex.Height;\n\n                        return overlay;\n                    }).ToArray();\n                }\n                else\n                {\n                    pack.OverlayTextures = new KeyboardOverlay[0];\n                }\n                #endregion\n\n                #region Notes\n                JArray noteSizes;\n                try\n                {\n                    noteSizes = parseType<JArray>(pack, data.notes);\n                }\n                catch (RuntimeBinderException) { throw new Exception(\"Missing Array Property \\\"notes\\\"\"); }\n                if (noteSizes.Count == 0) throw new Exception(\"Note textures array can't be 0\");\n                if (noteSizes.Count > 4) throw new Exception(\"Only up to 4 note textures are supported\");\n\n                List<NoteTexture> noteTex = new List<NoteTexture>();\n                bool hasBothKeyType = false;\n                foreach (dynamic _s in noteSizes)\n                {\n                    dynamic s = parseType<JObject>(pack, _s);\n                    NoteTexture tex = new NoteTexture();\n                    try\n                    {\n                        tex.useCaps = parseType<bool>(pack, s.useEndCaps);\n                    }\n                    catch (RuntimeBinderException) { throw new Exception(\"Missing property \\\"useEndCaps\\\" in note size textures\"); }\n                    try\n                    {\n                        tex.stretch = parseType<bool>(pack, s.alwaysStretch);\n                    }\n                    catch (RuntimeBinderException) { throw new Exception(\"Missing property \\\"alwaysStretch\\\" in note size textures\"); }\n                    try\n                    {\n                        tex.maxSize = parseType<double>(pack, s.maxSize);\n                    }\n                    catch (RuntimeBinderException) { throw new Exception(\"Missing property \\\"maxSize\\\" in note size textures\"); }\n\n                    try\n                    {\n                        pname = parseType<string>(pack, s.middleTexture);\n                    }\n                    catch (RuntimeBinderException) { throw new Exception(\"Missing property \\\"middleTexture\\\"\"); }\n                    tex.noteMiddleTex = GetBitmap(pname);\n                    tex.noteMiddleAspect = (double)tex.noteMiddleTex.Height / tex.noteMiddleTex.Width;\n\n                    try\n                    {\n                        tex.darkenBlackNotes = parseType<double>(pack, s.darkenBlackNotes);\n                    }\n                    catch (RuntimeBinderException) { }\n\n                    try\n                    {\n                        tex.highlightHitNotes = parseType<double>(pack, s.highlightHitNotes);\n                    }\n                    catch (RuntimeBinderException) { }\n                    if (tex.highlightHitNotes > 1 || tex.highlightHitNotes < 0) throw new Exception(\"highlightHitNotes must be between 0 and 1\");\n\n                    JArray array = null;\n                    try\n                    {\n                        var _array = parseType<JArray>(pack, s.highlightHitNotesColor);\n                        if (_array != null)\n                        {\n                            notArray = _array.GetType() != typeof(JArray);\n                            if (!notArray) array = _array;\n                        }\n                    }\n                    catch (RuntimeBinderException) { }\n                    if (array != null)\n                    {\n                        if (notArray) throw new Exception(\"highlightHitNotes must be an array of 3 numbers (RGB or RGBA, e.g. [255, 255, 255])\");\n                        if (!(array.Count == 3)) throw new Exception(\"highlightHitNotes must be an array of 3 numbers (RGB or RGBA, e.g. [255, 255, 255])\");\n                        else\n                        {\n                            tex.highlightHitNotesColor = System.Drawing.Color.FromArgb(255, (int)array[0], (int)array[1], (int)array[2]);\n                        }\n                    }\n\n                    try\n                    {\n                        tex.squeezeEndCaps = parseType<bool>(pack, s.squeezeEndCaps);\n                    }\n                    catch (RuntimeBinderException) { }\n\n                    string keyType = null;\n                    try\n                    {\n                        keyType = parseType<string>(pack, s.keyType);\n                    }\n                    catch (RuntimeBinderException) { }\n                    if (keyType != null) tex.keyType = strToKeyType(keyType);\n                    if (tex.keyType == KeyType.Both) hasBothKeyType = true;\n\n                    if (tex.useCaps)\n                    {\n                        try\n                        {\n                            pname = parseType<string>(pack, s.topTexture);\n                        }\n                        catch (RuntimeBinderException) { throw new Exception(\"Missing property \\\"topTexture\\\"\"); }\n                        tex.noteTopTex = GetBitmap(pname);\n                        try\n                        {\n                            pname = parseType<string>(pack, s.bottomTexture);\n                        }\n                        catch (RuntimeBinderException) { throw new Exception(\"Missing property \\\"bottomTexture\\\"\"); }\n                        tex.noteBottomTex = GetBitmap(pname);\n                        tex.noteTopAspect = (double)tex.noteTopTex.Height / tex.noteTopTex.Width;\n                        tex.noteBottomAspect = (double)tex.noteBottomTex.Height / tex.noteBottomTex.Width;\n\n                        try\n                        {\n                            tex.noteTopOversize = parseType<double>(pack, s.topOversize);\n                        }\n                        catch (RuntimeBinderException) { throw new Exception(\"Missing property \\\"topOversize\\\" in note size textures\"); }\n                        try\n                        {\n                            tex.noteBottomOversize = parseType<double>(pack, s.bottomOversize);\n                        }\n                        catch (RuntimeBinderException) { throw new Exception(\"Missing property \\\"bottomOversize\\\" in note size textures\"); }\n                    }\n                    noteTex.Add(tex);\n                }\n\n                if (!hasBothKeyType) throw new Exception(\"At least one note texture required with key type of \\\"both\\\"\");\n\n                noteTex.Sort((c1, c2) =>\n                {\n                    if (c1.maxSize < c2.maxSize) return -1;\n                    if (c1.maxSize > c2.maxSize) return 1;\n                    if (c2.keyType == KeyType.Both && c1.keyType != KeyType.Both) return -1;\n                    if (c1.keyType == KeyType.Both && c2.keyType != KeyType.Both) return 1;\n                    return 0;\n                });\n                bool firstBoth = false;\n                for (int i = noteTex.Count - 1; i >= 0; i--)\n                {\n                    if (noteTex[i].keyType == KeyType.Both)\n                    {\n                        if (firstBoth) break;\n                        else firstBoth = true;\n                    }\n                    noteTex[i].maxSize = double.PositiveInfinity;\n                }\n                pack.NoteTextures = noteTex.ToArray();\n                #endregion\n            }\n            catch (Exception e)\n            {\n                pack.error = true;\n                pack.description = e.Message;\n            }\n            finally\n            {\n                if (archive != null)\n                    archive.Dispose();\n                if (zrpstream != null)\n                    zrpstream.Dispose();\n                if (compress != null)\n                    compress.Dispose();\n            }\n            return pack;\n        }\n\n        private void PluginList_SelectionChanged(object sender, SelectionChangedEventArgs e)\n        {\n            LoadSelectedPack();\n        }\n\n        private void ReloadPackButton_Click(object sender, RoutedEventArgs e)\n        {\n            LoadSelectedPack();\n        }\n\n        void LoadSelectedPack()\n        {\n            try\n            {\n                var p = resourcePacks[pluginList.SelectedIndex];\n                if (settings.currPack != null) UnloadPack(settings.currPack);\n                var pack = LoadPack(p.filename, p.type);\n                if (!pack.error)\n                {\n                    pluginDesc.Foreground = Brushes.White;\n                    settings.currPack = pack;\n                    settings.lastPackChangeTime = DateTime.Now.Ticks;\n                }\n                else\n                {\n                    pluginDesc.Foreground = Brushes.Red;\n                    settings.currPack = null;\n                    settings.lastPackChangeTime = DateTime.Now.Ticks;\n                }\n                if (pack.preview == null)\n                    previewImg.Source = null;\n                else\n                    previewImg.Source = BitmapToImageSource(pack.preview);\n                switchTab.Visibility = Visibility.Collapsed;\n                switchPanel.Children.Clear();\n                if (pack.switchChoices != null && pack.switchChoices.Count != 0)\n                {\n                    switchTab.Visibility = Visibility.Visible;\n                    bool first = true;\n                    foreach (var s in pack.switchOrder)\n                    {\n                        if (pack.switchChoices.ContainsKey(s))\n                        {\n                            var menu = new ComboBox();\n                            menu.Tag = s;\n                            foreach (var v in pack.switchChoices[s])\n                            {\n                                menu.Items.Add(new ComboBoxItem() { Content = v });\n                            }\n                            var dock = new DockPanel();\n                            dock.HorizontalAlignment = HorizontalAlignment.Left;\n                            dock.Children.Add(new Label() { Content = s });\n                            dock.Children.Add(menu);\n                            switchPanel.Children.Add(dock);\n                            menu.SelectedIndex = 0;\n                            pack.switchValues[s] = pack.switchChoices[s][0];\n                            menu.SelectionChanged += Menu_SelectionChanged;\n                        }\n                        else\n                        {\n                            switchPanel.Children.Add(new Label() { Content = s, FontSize = 16, Margin = first ? new Thickness(0) : new Thickness(0, 10, 0, 0) });\n                        }\n                        first = false;\n                    }\n                }\n                pluginDesc.Text = pack.description;\n            }\n            catch { }\n        }\n\n        private void Menu_SelectionChanged(object sender, SelectionChangedEventArgs e)\n        {\n            if (settings.currPack != null)\n            {\n                var p = resourcePacks[pluginList.SelectedIndex];\n                var choices = settings.currPack.switchChoices;\n                var vals = settings.currPack.switchValues;\n\n                var box = (ComboBox)sender;\n                var tag = (string)box.Tag;\n                vals[tag] = choices[tag][box.SelectedIndex];\n\n                UnloadPack(settings.currPack);\n                var pack = LoadPack(p.filename, p.type, vals, choices);\n                if (!pack.error)\n                {\n                    pluginDesc.Text = pack.description;\n                    pluginDesc.Foreground = Brushes.White;\n                    settings.currPack = pack;\n                    settings.lastPackChangeTime = DateTime.Now.Ticks;\n                }\n                else\n                {\n                    pluginDesc.Text = pack.description;\n                    pluginDesc.Foreground = Brushes.Red;\n                    settings.currPack = null;\n                    settings.lastPackChangeTime = DateTime.Now.Ticks;\n                }\n                if (pack.preview == null)\n                    previewImg.Source = null;\n                else\n                    previewImg.Source = BitmapToImageSource(pack.preview);\n            }\n        }\n\n        public void SetValues()\n        {\n            firstNote.Value = settings.firstNote;\n            lastNote.Value = settings.lastNote - 1;\n            noteDeltaScreenTime.Value = settings.deltaTimeOnScreen;\n            blackNotesAbove.IsChecked = settings.blackNotesAbove;\n            paletteList.SelectImage(settings.palette);\n        }\n\n        private void ReloadButton_Click(object sender, RoutedEventArgs e)\n        {\n            ReloadPacks();\n        }\n\n        bool screenTimeLock = false;\n\n        private void BlackNotesAbove_Checked(object sender, RoutedEventArgs e)\n        {\n            if (!inited) return;\n            try\n            {\n                settings.blackNotesAbove = (bool)blackNotesAbove.IsChecked;\n            }\n            catch (NullReferenceException) { }\n        }\n\n        private void NoteDeltaScreenTime_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)\n        {\n            if (!inited) return;\n            try\n            {\n                if (screenTimeLock) return;\n                screenTimeLock = true;\n                settings.deltaTimeOnScreen = noteDeltaScreenTime.Value;\n                screenTimeLock = false;\n            }\n            catch (NullReferenceException)\n            {\n                screenTimeLock = false;\n            }\n        }\n\n        private void Nud_ValueChanged(object sender, RoutedPropertyChangedEventArgs<decimal> e)\n        {\n            if (!inited) return;\n            try\n            {\n                if (sender == firstNote) settings.firstNote = (int)firstNote.Value;\n                if (sender == lastNote) settings.lastNote = (int)lastNote.Value + 1;\n            }\n            catch (NullReferenceException) { }\n            catch (InvalidOperationException) { }\n        }\n\n        private void openFolderButton_Click(object sender, RoutedEventArgs e)\n        {\n            if (!packPath.Contains(\":\\\\\") && !packPath.Contains(\":/\"))\n                Process.Start(\"explorer.exe\", System.IO.Path.Combine(System.IO.Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), packPath));\n            else\n                Process.Start(\"explorer.exe\", packPath);\n        }\n    }\n}\n"
  },
  {
    "path": "TexturedRender/TexturedRender.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProjectGuid>{ECF72523-79AE-4DD1-9421-60B1D8BCCB9E}</ProjectGuid>\n    <OutputType>Library</OutputType>\n    <AppDesignerFolder>Properties</AppDesignerFolder>\n    <RootNamespace>TexturedRender</RootNamespace>\n    <AssemblyName>TexturedRender</AssemblyName>\n    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>\n    <FileAlignment>512</FileAlignment>\n    <Deterministic>true</Deterministic>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>..\\bin\\Any cpu\\Debug\\Plugins\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>..\\bin\\Any cpu\\Release\\Plugins\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <PlatformTarget>x64</PlatformTarget>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Debug|x86'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>..\\bin\\x86\\Debug\\Plugins\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <DebugType>full</DebugType>\n    <PlatformTarget>x86</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Release|x86'\">\n    <OutputPath>..\\bin\\x86\\Release\\Plugins\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <Optimize>true</Optimize>\n    <DebugType>pdbonly</DebugType>\n    <PlatformTarget>x86</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Debug|x64'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>..\\bin\\x64\\Debug\\Plugins\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <DebugType>full</DebugType>\n    <PlatformTarget>x64</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Release|x64'\">\n    <OutputPath>..\\bin\\x64\\Release\\Plugins\\</OutputPath>\n    <DefineConstants>\n    </DefineConstants>\n    <Optimize>true</Optimize>\n    <DebugType>pdbonly</DebugType>\n    <PlatformTarget>x64</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Newtonsoft.Json.12.0.1\\lib\\net45\\Newtonsoft.Json.dll</HintPath>\n    </Reference>\n    <Reference Include=\"OpenTK, Version=3.1.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\OpenTK.3.1.0\\lib\\net20\\OpenTK.dll</HintPath>\n    </Reference>\n    <Reference Include=\"PresentationCore\" />\n    <Reference Include=\"PresentationFramework\" />\n    <Reference Include=\"SharpCompress, Version=0.24.0.0, Culture=neutral, PublicKeyToken=afb0a02973931d96, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\SharpCompress.0.24.0\\lib\\net45\\SharpCompress.dll</HintPath>\n    </Reference>\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Core\" />\n    <Reference Include=\"System.Drawing\" />\n    <Reference Include=\"System.IO.Compression\" />\n    <Reference Include=\"System.IO.Compression.FileSystem\" />\n    <Reference Include=\"System.Xaml\" />\n    <Reference Include=\"System.Xml.Linq\" />\n    <Reference Include=\"System.Data.DataSetExtensions\" />\n    <Reference Include=\"Microsoft.CSharp\" />\n    <Reference Include=\"System.Data\" />\n    <Reference Include=\"System.Net.Http\" />\n    <Reference Include=\"System.Xml\" />\n    <Reference Include=\"WindowsBase\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"Pack.cs\" />\n    <Compile Include=\"Properties\\AssemblyInfo.cs\" />\n    <Compile Include=\"Properties\\Resources.Designer.cs\">\n      <AutoGen>True</AutoGen>\n      <DesignTime>True</DesignTime>\n      <DependentUpon>Resources.resx</DependentUpon>\n    </Compile>\n    <Compile Include=\"Render.cs\" />\n    <Compile Include=\"Settings.cs\" />\n    <Compile Include=\"SettingsCtrl.xaml.cs\">\n      <DependentUpon>SettingsCtrl.xaml</DependentUpon>\n    </Compile>\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\BMEngine\\ZenithEngine.csproj\">\n      <Project>{60f2212a-d56c-4776-836f-6a49453cdbc4}</Project>\n      <Name>ZenithEngine</Name>\n    </ProjectReference>\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"OpenTK.dll.config\" />\n    <None Include=\"packages.config\" />\n    <None Include=\"Resources\\pack.json\" />\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Properties\\Resources.resx\">\n      <Generator>ResXFileCodeGenerator</Generator>\n      <LastGenOutput>Resources.Designer.cs</LastGenOutput>\n    </EmbeddedResource>\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Languages\\en\\textured.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </EmbeddedResource>\n    <Page Include=\"SettingsCtrl.xaml\">\n      <SubType>Designer</SubType>\n      <Generator>MSBuild:Compile</Generator>\n    </Page>\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"Resources\\note.png\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"Resources\\noteEdge.png\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"Resources\\bar.png\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"Resources\\keyBlack.png\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"Resources\\keyBlackPressed.png\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"Resources\\keyWhite.png\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"Resources\\keyWhitePressed.png\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"Resources\\preview.png\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"Resources\\pluginPreview.png\" />\n  </ItemGroup>\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n</Project>"
  },
  {
    "path": "TexturedRender/packages.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n  <package id=\"Newtonsoft.Json\" version=\"12.0.1\" targetFramework=\"net461\" />\n  <package id=\"OpenTK\" version=\"3.1.0\" targetFramework=\"net461\" />\n  <package id=\"SharpCompress\" version=\"0.24.0\" targetFramework=\"net461\" />\n</packages>"
  },
  {
    "path": "Zenith.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 16\nVisualStudioVersion = 16.0.29001.49\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"Zenith\", \"Black-Midi-Render\\Zenith.csproj\", \"{273DA7BD-C89A-4574-97DE-AF425529FAAE}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"ZenithEngine\", \"BMEngine\\ZenithEngine.csproj\", \"{60F2212A-D56C-4776-836F-6A49453CDBC4}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Plugins\", \"Plugins\", \"{D60FA663-FD18-4342-A8C7-CD072DA1E4A0}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"ClassicRender\", \"ClassicRender\\ClassicRender.csproj\", \"{B733D6F1-78A8-43CA-91A3-28FA3E7025D4}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"PFARender\", \"PFARender\\PFARender.csproj\", \"{84E13869-0D33-4A56-A1A8-0B6F12C76081}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"FlatRender\", \"FlatRender\\FlatRender.csproj\", \"{AE59479C-9D0D-464C-A4D3-8FDD7E1F3786}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"MidiTrailRender\", \"MidiTrailRender\\MidiTrailRender.csproj\", \"{991E132A-A56D-49AB-BBEE-9828359BB307}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"NoteCountRender\", \"NoteCountRender\\NoteCountRender.csproj\", \"{5F30BA79-D86B-4FC6-BA76-7CBB9EBBA721}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"TexturedRender\", \"TexturedRender\\TexturedRender.csproj\", \"{ECF72523-79AE-4DD1-9421-60B1D8BCCB9E}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"ScriptedRender\", \"ScriptedRenderer\\ScriptedRender.csproj\", \"{95F774EB-47A2-40B3-A0CE-E616BBD4D62B}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"ZenithShared\", \"ZenithShared\\ZenithShared.csproj\", \"{019E4CE1-547F-4A32-B02C-F190943713EB}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"ZenithInstaller\", \"ZenithInstaller\\ZenithInstaller.csproj\", \"{F43DEA70-CB97-43FA-9CE9-172D85F010F3}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|Any CPU = Debug|Any CPU\n\t\tDebug|x64 = Debug|x64\n\t\tDebug|x86 = Debug|x86\n\t\tRelease|Any CPU = Release|Any CPU\n\t\tRelease|x64 = Release|x64\n\t\tRelease|x86 = Release|x86\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{273DA7BD-C89A-4574-97DE-AF425529FAAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{273DA7BD-C89A-4574-97DE-AF425529FAAE}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{273DA7BD-C89A-4574-97DE-AF425529FAAE}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{273DA7BD-C89A-4574-97DE-AF425529FAAE}.Debug|x64.Build.0 = Debug|x64\n\t\t{273DA7BD-C89A-4574-97DE-AF425529FAAE}.Debug|x86.ActiveCfg = Debug|x86\n\t\t{273DA7BD-C89A-4574-97DE-AF425529FAAE}.Debug|x86.Build.0 = Debug|x86\n\t\t{273DA7BD-C89A-4574-97DE-AF425529FAAE}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{273DA7BD-C89A-4574-97DE-AF425529FAAE}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{273DA7BD-C89A-4574-97DE-AF425529FAAE}.Release|x64.ActiveCfg = Release|x64\n\t\t{273DA7BD-C89A-4574-97DE-AF425529FAAE}.Release|x64.Build.0 = Release|x64\n\t\t{273DA7BD-C89A-4574-97DE-AF425529FAAE}.Release|x86.ActiveCfg = Release|x86\n\t\t{273DA7BD-C89A-4574-97DE-AF425529FAAE}.Release|x86.Build.0 = Release|x86\n\t\t{60F2212A-D56C-4776-836F-6A49453CDBC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{60F2212A-D56C-4776-836F-6A49453CDBC4}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{60F2212A-D56C-4776-836F-6A49453CDBC4}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{60F2212A-D56C-4776-836F-6A49453CDBC4}.Debug|x64.Build.0 = Debug|x64\n\t\t{60F2212A-D56C-4776-836F-6A49453CDBC4}.Debug|x86.ActiveCfg = Debug|x86\n\t\t{60F2212A-D56C-4776-836F-6A49453CDBC4}.Debug|x86.Build.0 = Debug|x86\n\t\t{60F2212A-D56C-4776-836F-6A49453CDBC4}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{60F2212A-D56C-4776-836F-6A49453CDBC4}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{60F2212A-D56C-4776-836F-6A49453CDBC4}.Release|x64.ActiveCfg = Release|x64\n\t\t{60F2212A-D56C-4776-836F-6A49453CDBC4}.Release|x64.Build.0 = Release|x64\n\t\t{60F2212A-D56C-4776-836F-6A49453CDBC4}.Release|x86.ActiveCfg = Release|x86\n\t\t{60F2212A-D56C-4776-836F-6A49453CDBC4}.Release|x86.Build.0 = Release|x86\n\t\t{B733D6F1-78A8-43CA-91A3-28FA3E7025D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{B733D6F1-78A8-43CA-91A3-28FA3E7025D4}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{B733D6F1-78A8-43CA-91A3-28FA3E7025D4}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{B733D6F1-78A8-43CA-91A3-28FA3E7025D4}.Debug|x64.Build.0 = Debug|x64\n\t\t{B733D6F1-78A8-43CA-91A3-28FA3E7025D4}.Debug|x86.ActiveCfg = Debug|x86\n\t\t{B733D6F1-78A8-43CA-91A3-28FA3E7025D4}.Debug|x86.Build.0 = Debug|x86\n\t\t{B733D6F1-78A8-43CA-91A3-28FA3E7025D4}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{B733D6F1-78A8-43CA-91A3-28FA3E7025D4}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{B733D6F1-78A8-43CA-91A3-28FA3E7025D4}.Release|x64.ActiveCfg = Release|x64\n\t\t{B733D6F1-78A8-43CA-91A3-28FA3E7025D4}.Release|x64.Build.0 = Release|x64\n\t\t{B733D6F1-78A8-43CA-91A3-28FA3E7025D4}.Release|x86.ActiveCfg = Release|x86\n\t\t{B733D6F1-78A8-43CA-91A3-28FA3E7025D4}.Release|x86.Build.0 = Release|x86\n\t\t{84E13869-0D33-4A56-A1A8-0B6F12C76081}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{84E13869-0D33-4A56-A1A8-0B6F12C76081}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{84E13869-0D33-4A56-A1A8-0B6F12C76081}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{84E13869-0D33-4A56-A1A8-0B6F12C76081}.Debug|x64.Build.0 = Debug|x64\n\t\t{84E13869-0D33-4A56-A1A8-0B6F12C76081}.Debug|x86.ActiveCfg = Debug|x86\n\t\t{84E13869-0D33-4A56-A1A8-0B6F12C76081}.Debug|x86.Build.0 = Debug|x86\n\t\t{84E13869-0D33-4A56-A1A8-0B6F12C76081}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{84E13869-0D33-4A56-A1A8-0B6F12C76081}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{84E13869-0D33-4A56-A1A8-0B6F12C76081}.Release|x64.ActiveCfg = Release|x64\n\t\t{84E13869-0D33-4A56-A1A8-0B6F12C76081}.Release|x64.Build.0 = Release|x64\n\t\t{84E13869-0D33-4A56-A1A8-0B6F12C76081}.Release|x86.ActiveCfg = Release|x86\n\t\t{84E13869-0D33-4A56-A1A8-0B6F12C76081}.Release|x86.Build.0 = Release|x86\n\t\t{AE59479C-9D0D-464C-A4D3-8FDD7E1F3786}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{AE59479C-9D0D-464C-A4D3-8FDD7E1F3786}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{AE59479C-9D0D-464C-A4D3-8FDD7E1F3786}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{AE59479C-9D0D-464C-A4D3-8FDD7E1F3786}.Debug|x64.Build.0 = Debug|x64\n\t\t{AE59479C-9D0D-464C-A4D3-8FDD7E1F3786}.Debug|x86.ActiveCfg = Debug|x86\n\t\t{AE59479C-9D0D-464C-A4D3-8FDD7E1F3786}.Debug|x86.Build.0 = Debug|x86\n\t\t{AE59479C-9D0D-464C-A4D3-8FDD7E1F3786}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{AE59479C-9D0D-464C-A4D3-8FDD7E1F3786}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{AE59479C-9D0D-464C-A4D3-8FDD7E1F3786}.Release|x64.ActiveCfg = Release|x64\n\t\t{AE59479C-9D0D-464C-A4D3-8FDD7E1F3786}.Release|x64.Build.0 = Release|x64\n\t\t{AE59479C-9D0D-464C-A4D3-8FDD7E1F3786}.Release|x86.ActiveCfg = Release|x86\n\t\t{AE59479C-9D0D-464C-A4D3-8FDD7E1F3786}.Release|x86.Build.0 = Release|x86\n\t\t{991E132A-A56D-49AB-BBEE-9828359BB307}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{991E132A-A56D-49AB-BBEE-9828359BB307}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{991E132A-A56D-49AB-BBEE-9828359BB307}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{991E132A-A56D-49AB-BBEE-9828359BB307}.Debug|x64.Build.0 = Debug|x64\n\t\t{991E132A-A56D-49AB-BBEE-9828359BB307}.Debug|x86.ActiveCfg = Debug|x86\n\t\t{991E132A-A56D-49AB-BBEE-9828359BB307}.Debug|x86.Build.0 = Debug|x86\n\t\t{991E132A-A56D-49AB-BBEE-9828359BB307}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{991E132A-A56D-49AB-BBEE-9828359BB307}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{991E132A-A56D-49AB-BBEE-9828359BB307}.Release|x64.ActiveCfg = Release|x64\n\t\t{991E132A-A56D-49AB-BBEE-9828359BB307}.Release|x64.Build.0 = Release|x64\n\t\t{991E132A-A56D-49AB-BBEE-9828359BB307}.Release|x86.ActiveCfg = Release|x86\n\t\t{991E132A-A56D-49AB-BBEE-9828359BB307}.Release|x86.Build.0 = Release|x86\n\t\t{5F30BA79-D86B-4FC6-BA76-7CBB9EBBA721}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{5F30BA79-D86B-4FC6-BA76-7CBB9EBBA721}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{5F30BA79-D86B-4FC6-BA76-7CBB9EBBA721}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{5F30BA79-D86B-4FC6-BA76-7CBB9EBBA721}.Debug|x64.Build.0 = Debug|x64\n\t\t{5F30BA79-D86B-4FC6-BA76-7CBB9EBBA721}.Debug|x86.ActiveCfg = Debug|x86\n\t\t{5F30BA79-D86B-4FC6-BA76-7CBB9EBBA721}.Debug|x86.Build.0 = Debug|x86\n\t\t{5F30BA79-D86B-4FC6-BA76-7CBB9EBBA721}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{5F30BA79-D86B-4FC6-BA76-7CBB9EBBA721}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{5F30BA79-D86B-4FC6-BA76-7CBB9EBBA721}.Release|x64.ActiveCfg = Release|x64\n\t\t{5F30BA79-D86B-4FC6-BA76-7CBB9EBBA721}.Release|x64.Build.0 = Release|x64\n\t\t{5F30BA79-D86B-4FC6-BA76-7CBB9EBBA721}.Release|x86.ActiveCfg = Release|x86\n\t\t{5F30BA79-D86B-4FC6-BA76-7CBB9EBBA721}.Release|x86.Build.0 = Release|x86\n\t\t{ECF72523-79AE-4DD1-9421-60B1D8BCCB9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{ECF72523-79AE-4DD1-9421-60B1D8BCCB9E}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{ECF72523-79AE-4DD1-9421-60B1D8BCCB9E}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{ECF72523-79AE-4DD1-9421-60B1D8BCCB9E}.Debug|x64.Build.0 = Debug|x64\n\t\t{ECF72523-79AE-4DD1-9421-60B1D8BCCB9E}.Debug|x86.ActiveCfg = Debug|x86\n\t\t{ECF72523-79AE-4DD1-9421-60B1D8BCCB9E}.Debug|x86.Build.0 = Debug|x86\n\t\t{ECF72523-79AE-4DD1-9421-60B1D8BCCB9E}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{ECF72523-79AE-4DD1-9421-60B1D8BCCB9E}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{ECF72523-79AE-4DD1-9421-60B1D8BCCB9E}.Release|x64.ActiveCfg = Release|x64\n\t\t{ECF72523-79AE-4DD1-9421-60B1D8BCCB9E}.Release|x64.Build.0 = Release|x64\n\t\t{ECF72523-79AE-4DD1-9421-60B1D8BCCB9E}.Release|x86.ActiveCfg = Release|x86\n\t\t{ECF72523-79AE-4DD1-9421-60B1D8BCCB9E}.Release|x86.Build.0 = Release|x86\n\t\t{95F774EB-47A2-40B3-A0CE-E616BBD4D62B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{95F774EB-47A2-40B3-A0CE-E616BBD4D62B}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{95F774EB-47A2-40B3-A0CE-E616BBD4D62B}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{95F774EB-47A2-40B3-A0CE-E616BBD4D62B}.Debug|x64.Build.0 = Debug|x64\n\t\t{95F774EB-47A2-40B3-A0CE-E616BBD4D62B}.Debug|x86.ActiveCfg = Debug|x86\n\t\t{95F774EB-47A2-40B3-A0CE-E616BBD4D62B}.Debug|x86.Build.0 = Debug|x86\n\t\t{95F774EB-47A2-40B3-A0CE-E616BBD4D62B}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{95F774EB-47A2-40B3-A0CE-E616BBD4D62B}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{95F774EB-47A2-40B3-A0CE-E616BBD4D62B}.Release|x64.ActiveCfg = Release|x64\n\t\t{95F774EB-47A2-40B3-A0CE-E616BBD4D62B}.Release|x64.Build.0 = Release|x64\n\t\t{95F774EB-47A2-40B3-A0CE-E616BBD4D62B}.Release|x86.ActiveCfg = Release|x86\n\t\t{95F774EB-47A2-40B3-A0CE-E616BBD4D62B}.Release|x86.Build.0 = Release|x86\n\t\t{019E4CE1-547F-4A32-B02C-F190943713EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{019E4CE1-547F-4A32-B02C-F190943713EB}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{019E4CE1-547F-4A32-B02C-F190943713EB}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{019E4CE1-547F-4A32-B02C-F190943713EB}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{019E4CE1-547F-4A32-B02C-F190943713EB}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{019E4CE1-547F-4A32-B02C-F190943713EB}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{019E4CE1-547F-4A32-B02C-F190943713EB}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{019E4CE1-547F-4A32-B02C-F190943713EB}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{019E4CE1-547F-4A32-B02C-F190943713EB}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{019E4CE1-547F-4A32-B02C-F190943713EB}.Release|x64.Build.0 = Release|Any CPU\n\t\t{019E4CE1-547F-4A32-B02C-F190943713EB}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{019E4CE1-547F-4A32-B02C-F190943713EB}.Release|x86.Build.0 = Release|Any CPU\n\t\t{F43DEA70-CB97-43FA-9CE9-172D85F010F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{F43DEA70-CB97-43FA-9CE9-172D85F010F3}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{F43DEA70-CB97-43FA-9CE9-172D85F010F3}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{F43DEA70-CB97-43FA-9CE9-172D85F010F3}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{F43DEA70-CB97-43FA-9CE9-172D85F010F3}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{F43DEA70-CB97-43FA-9CE9-172D85F010F3}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{F43DEA70-CB97-43FA-9CE9-172D85F010F3}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{F43DEA70-CB97-43FA-9CE9-172D85F010F3}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{F43DEA70-CB97-43FA-9CE9-172D85F010F3}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{F43DEA70-CB97-43FA-9CE9-172D85F010F3}.Release|x64.Build.0 = Release|Any CPU\n\t\t{F43DEA70-CB97-43FA-9CE9-172D85F010F3}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{F43DEA70-CB97-43FA-9CE9-172D85F010F3}.Release|x86.Build.0 = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\n\tGlobalSection(NestedProjects) = preSolution\n\t\t{B733D6F1-78A8-43CA-91A3-28FA3E7025D4} = {D60FA663-FD18-4342-A8C7-CD072DA1E4A0}\n\t\t{84E13869-0D33-4A56-A1A8-0B6F12C76081} = {D60FA663-FD18-4342-A8C7-CD072DA1E4A0}\n\t\t{AE59479C-9D0D-464C-A4D3-8FDD7E1F3786} = {D60FA663-FD18-4342-A8C7-CD072DA1E4A0}\n\t\t{991E132A-A56D-49AB-BBEE-9828359BB307} = {D60FA663-FD18-4342-A8C7-CD072DA1E4A0}\n\t\t{5F30BA79-D86B-4FC6-BA76-7CBB9EBBA721} = {D60FA663-FD18-4342-A8C7-CD072DA1E4A0}\n\t\t{ECF72523-79AE-4DD1-9421-60B1D8BCCB9E} = {D60FA663-FD18-4342-A8C7-CD072DA1E4A0}\n\t\t{95F774EB-47A2-40B3-A0CE-E616BBD4D62B} = {D60FA663-FD18-4342-A8C7-CD072DA1E4A0}\n\tEndGlobalSection\n\tGlobalSection(ExtensibilityGlobals) = postSolution\n\t\tSolutionGuid = {8FECB4F7-95FB-4E36-BDEE-C79BB233B885}\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "ZenithInstaller/App.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<configuration>\n    <startup> \n        <supportedRuntime version=\"v4.0\" sku=\".NETFramework,Version=v4.6.1\" />\n    </startup>\n</configuration>"
  },
  {
    "path": "ZenithInstaller/App.xaml",
    "content": "﻿<Application x:Class=\"ZenithInstaller.App\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:local=\"clr-namespace:ZenithInstaller\"\n             StartupUri=\"MainWindow.xaml\">\n    <Application.Resources>\n         \n    </Application.Resources>\n</Application>\n"
  },
  {
    "path": "ZenithInstaller/App.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Configuration;\nusing System.Data;\nusing System.Linq;\nusing System.Threading.Tasks;\nusing System.Windows;\n\nnamespace ZenithInstaller\n{\n    /// <summary>\n    /// Interaction logic for App.xaml\n    /// </summary>\n    public partial class App : Application\n    {\n    }\n}\n"
  },
  {
    "path": "ZenithInstaller/FodyWeavers.xml",
    "content": "﻿<Weavers xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"FodyWeavers.xsd\">\n  <Costura />\n</Weavers>"
  },
  {
    "path": "ZenithInstaller/FodyWeavers.xsd",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">\n  <!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->\n  <xs:element name=\"Weavers\">\n    <xs:complexType>\n      <xs:all>\n        <xs:element name=\"Costura\" minOccurs=\"0\" maxOccurs=\"1\">\n          <xs:complexType>\n            <xs:all>\n              <xs:element minOccurs=\"0\" maxOccurs=\"1\" name=\"ExcludeAssemblies\" type=\"xs:string\">\n                <xs:annotation>\n                  <xs:documentation>A list of assembly names to exclude from the default action of \"embed all Copy Local references\", delimited with line breaks</xs:documentation>\n                </xs:annotation>\n              </xs:element>\n              <xs:element minOccurs=\"0\" maxOccurs=\"1\" name=\"IncludeAssemblies\" type=\"xs:string\">\n                <xs:annotation>\n                  <xs:documentation>A list of assembly names to include from the default action of \"embed all Copy Local references\", delimited with line breaks.</xs:documentation>\n                </xs:annotation>\n              </xs:element>\n              <xs:element minOccurs=\"0\" maxOccurs=\"1\" name=\"Unmanaged32Assemblies\" type=\"xs:string\">\n                <xs:annotation>\n                  <xs:documentation>A list of unmanaged 32 bit assembly names to include, delimited with line breaks.</xs:documentation>\n                </xs:annotation>\n              </xs:element>\n              <xs:element minOccurs=\"0\" maxOccurs=\"1\" name=\"Unmanaged64Assemblies\" type=\"xs:string\">\n                <xs:annotation>\n                  <xs:documentation>A list of unmanaged 64 bit assembly names to include, delimited with line breaks.</xs:documentation>\n                </xs:annotation>\n              </xs:element>\n              <xs:element minOccurs=\"0\" maxOccurs=\"1\" name=\"PreloadOrder\" type=\"xs:string\">\n                <xs:annotation>\n                  <xs:documentation>The order of preloaded assemblies, delimited with line breaks.</xs:documentation>\n                </xs:annotation>\n              </xs:element>\n            </xs:all>\n            <xs:attribute name=\"CreateTemporaryAssemblies\" type=\"xs:boolean\">\n              <xs:annotation>\n                <xs:documentation>This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"IncludeDebugSymbols\" type=\"xs:boolean\">\n              <xs:annotation>\n                <xs:documentation>Controls if .pdbs for reference assemblies are also embedded.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"DisableCompression\" type=\"xs:boolean\">\n              <xs:annotation>\n                <xs:documentation>Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"DisableCleanup\" type=\"xs:boolean\">\n              <xs:annotation>\n                <xs:documentation>As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"LoadAtModuleInit\" type=\"xs:boolean\">\n              <xs:annotation>\n                <xs:documentation>Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"IgnoreSatelliteAssemblies\" type=\"xs:boolean\">\n              <xs:annotation>\n                <xs:documentation>Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"ExcludeAssemblies\" type=\"xs:string\">\n              <xs:annotation>\n                <xs:documentation>A list of assembly names to exclude from the default action of \"embed all Copy Local references\", delimited with |</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"IncludeAssemblies\" type=\"xs:string\">\n              <xs:annotation>\n                <xs:documentation>A list of assembly names to include from the default action of \"embed all Copy Local references\", delimited with |.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"Unmanaged32Assemblies\" type=\"xs:string\">\n              <xs:annotation>\n                <xs:documentation>A list of unmanaged 32 bit assembly names to include, delimited with |.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"Unmanaged64Assemblies\" type=\"xs:string\">\n              <xs:annotation>\n                <xs:documentation>A list of unmanaged 64 bit assembly names to include, delimited with |.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"PreloadOrder\" type=\"xs:string\">\n              <xs:annotation>\n                <xs:documentation>The order of preloaded assemblies, delimited with |.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n          </xs:complexType>\n        </xs:element>\n      </xs:all>\n      <xs:attribute name=\"VerifyAssembly\" type=\"xs:boolean\">\n        <xs:annotation>\n          <xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"VerifyIgnoreCodes\" type=\"xs:string\">\n        <xs:annotation>\n          <xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"GenerateXsd\" type=\"xs:boolean\">\n        <xs:annotation>\n          <xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n    </xs:complexType>\n  </xs:element>\n</xs:schema>"
  },
  {
    "path": "ZenithInstaller/MainWindow.xaml",
    "content": "﻿<Window x:Class=\"ZenithInstaller.MainWindow\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:local=\"clr-namespace:ZenithInstaller\"\n        mc:Ignorable=\"d\"\n        Title=\"LoadingMidiForm\" WindowStyle=\"None\" Height=\"72.729\" Width=\"266.373\" Loaded=\"Window_Loaded\" WindowStartupLocation=\"CenterScreen\">\n    <WindowChrome.WindowChrome>\n        <WindowChrome CaptionHeight=\"{Binding ActualHeight,ElementName=content}\"/>\n    </WindowChrome.WindowChrome>\n    <Grid Name=\"content\" Background=\"#009245\">\n        <StackPanel VerticalAlignment=\"Center\" HorizontalAlignment=\"Center\">\n            <Label Name=\"progressText\" FontSize=\"16\" Foreground=\"White\" HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\">Downloading Zenith...</Label>\n            <Label Name=\"dlProgress\" Visibility=\"Collapsed\" FontSize=\"12\" HorizontalAlignment=\"Center\" Padding=\"0\" Foreground=\"White\">0%</Label>\n        </StackPanel>\n    </Grid>\n</Window>"
  },
  {
    "path": "ZenithInstaller/MainWindow.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\nusing ZenithShared;\n\nnamespace ZenithInstaller\n{\n    /// <summary>\n    /// Interaction logic for MainWindow.xaml\n    /// </summary>\n    public partial class MainWindow : Window\n    {\n        public MainWindow()\n        {\n            InitializeComponent();\n        }\n\n        public Exception exception = null;\n\n        void ProgressCallbac(long dl, long total)\n        {\n            Dispatcher.Invoke(() =>\n            {\n                dlProgress.Visibility = Visibility.Visible;\n                dlProgress.Content = (Math.Round(((double)dl / total * 1000.0)) / 10) + \"% of \" + (Math.Round(total / 100000.0) / 10) + \"mb\";\n            });\n        }\n\n        private void Window_Loaded(object sender, RoutedEventArgs e)\n        {\n            Task.Run(() =>\n            {\n#if !DEBUG\n                try\n                {\n#endif\n                    Stream data;\n                    if(Environment.Is64BitOperatingSystem) data = ZenithUpdates.DownloadAssetDataProgressive(ZenithUpdates.DataAssetName64, ProgressCallbac);\n                    else data = ZenithUpdates.DownloadAssetDataProgressive(ZenithUpdates.DataAssetName32, ProgressCallbac);\n                    Dispatcher.Invoke(() =>\n                    {\n                        dlProgress.Visibility = Visibility.Collapsed;\n                    });\n                    Dispatcher.Invoke(() => progressText.Content = \"Installing...\");\n                    ZenithUpdates.InstallFromStream(data);\n                    data.Close();\n                    Program.FinalizeInstall();\n                    Dispatcher.Invoke(() => Close());\n#if !DEBUG\n                }\n                catch (Exception ex)\n                {\n                    exception = ex;\n                    Dispatcher.Invoke(() => Close());\n                }\n#endif\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "ZenithInstaller/Program.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing ZenithShared;\n\nnamespace ZenithInstaller\n{\n    class Program\n    {\n        public static bool Silent = false;\n\n        [STAThread]\n        static void Main(string[] args)\n        {\n#if !DEBUG\n            try\n            {\n#endif\n                ZenithUpdates.KillAllProcesses();\n\n                bool reopen = false;\n                string reopenArg = \"\";\n\n                if (args.Length == 0)\n                {\n                    reopen = true;\n                    NormalInstall();\n                }\n                else\n                {\n                    string command = args[0];\n\n                    if (!new[] { \"install\", \"update\", \"uninstall\" }.Contains(command))\n                    {\n                        Console.WriteLine(\"Invalid command \" + command);\n                        return;\n                    }\n\n                    string packagePath = ZenithUpdates.DefaultUpdatePackagePath;\n\n                    for (int i = 1; i < args.Length; i++)\n                    {\n                        if (args[i] == \"-Silent\") Silent = true;\n                        if (args[i] == \"-PackagePath\")\n                        {\n                            if (command != \"update\")\n                            {\n                                Console.WriteLine(\"-PackagePath flag only allowed on update command\");\n                                return;\n                            }\n                            i++;\n                            if (i == args.Length)\n                            {\n                                Console.WriteLine(\"path expected after -PackagePath\");\n                                return;\n                            }\n                            packagePath = args[i];\n                        }\n                        if (args[i] == \"-ReopenArg\")\n                        {\n                            if (command == \"uninstall\")\n                            {\n                                Console.WriteLine(\"-ReopenArg flag not allowed on uninstall command\");\n                                return;\n                            }\n                            i++;\n                            if (i == args.Length)\n                            {\n                                Console.WriteLine(\"argument expected after -ReopenArg\");\n                                return;\n                            }\n                            reopenArg = args[i];\n                        }\n                        if (args[i] == \"-Reopen\")\n                        {\n                            if (command == \"uninstall\")\n                            {\n                                Console.WriteLine(\"-Reopen flag not allowed on uninstall command\");\n                                return;\n                            }\n                            reopen = true;\n                        }\n                    }\n\n                    if (command == \"install\")\n                    {\n                        if (Silent) SilentInstall();\n                        else NormalInstall();\n                    }\n                    if (command == \"update\")\n                    {\n                        UpdateFromPackage(packagePath);\n                        ZenithUpdates.WriteVersionSettings(ZenithUpdates.GetLatestVersion(), true, true);\n                    }\n                    if (command == \"uninstall\")\n                    {\n                        ZenithUpdates.DeleteStartShortcut();\n                        ZenithUpdates.DeleteDesktopShortcut();\n                        ZenithUpdates.DeleteProgramFolder();\n                        if (!Silent) MessageBox.Show(\"Successfully uninstalled \" + ZenithUpdates.ProgramName + \"!\");\n                    }\n                }\n\n                if (reopen)\n                {\n                    string exePath = Path.Combine(ZenithUpdates.InstallPath, ZenithUpdates.ExeName);\n                    if (reopenArg == \"\") Process.Start(new ProcessStartInfo()\n                    {\n                        FileName = exePath,\n                        WorkingDirectory = Path.GetDirectoryName(exePath)\n                    });\n                    else Process.Start(new ProcessStartInfo()\n                    {\n                        FileName = exePath,\n                        WorkingDirectory = Path.GetDirectoryName(exePath),\n                        Arguments = \"\\\"\" + reopenArg + \"\\\"\"\n                    });\n                }\n#if !DEBUG\n            }\n            catch (Exception e)\n            {\n                if (!Silent)\n                {\n                    string msg = e.Message + \"\\n\" + e.Data + \"\\n\";\n                    msg += e.StackTrace;\n                    MessageBox.Show(msg, ZenithUpdates.ProgramName + \" installer has crashed!\");\n                }\n            }\n#endif\n        }\n\n        static void NormalInstall()\n        {\n            var window = new MainWindow();\n            window.ShowDialog();\n            if (window.exception != null) throw window.exception;\n        }\n\n        static void SilentInstall()\n        {\n            Stream data;\n            if (Environment.Is64BitOperatingSystem) data = ZenithUpdates.DownloadAssetData(ZenithUpdates.DataAssetName64);\n            else data = ZenithUpdates.DownloadAssetData(ZenithUpdates.DataAssetName32);\n            ZenithUpdates.InstallFromStream(data);\n            data.Close();\n            FinalizeInstall();\n        }\n\n        static void UpdateFromPackage(string path)\n        {\n            if (!File.Exists(path))\n            {\n                if (!Silent) MessageBox.Show(\"Could not install update, update package file missing\", \"Update failed\");\n                return;\n            }\n            var f = File.OpenRead(path);\n            ZenithUpdates.KillAllProcesses();\n            ZenithUpdates.InstallFromStream(f);\n            f.Close();\n            File.Delete(path);\n        }\n\n        public static void FinalizeInstall()\n        {\n            ZenithUpdates.WriteVersionSettings(ZenithUpdates.GetLatestVersion(), true, true);\n            ZenithUpdates.CopySelfInside(ZenithUpdates.InstallerPath);\n            ZenithUpdates.CreateStartShortcut();\n            ZenithUpdates.CreateUninstallScript();\n            ZenithUpdates.CreateDesktopShortcut();\n        }\n    }\n}\n"
  },
  {
    "path": "ZenithInstaller/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\nusing System.Resources;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\nusing System.Windows;\n\n// General Information about an assembly is controlled through the following\n// set of attributes. Change these attribute values to modify the information\n// associated with an assembly.\n[assembly: AssemblyTitle(\"ZenithInstaller\")]\n[assembly: AssemblyDescription(\"\")]\n[assembly: AssemblyConfiguration(\"\")]\n[assembly: AssemblyCompany(\"\")]\n[assembly: AssemblyProduct(\"ZenithInstaller\")]\n[assembly: AssemblyCopyright(\"Copyright ©  2020\")]\n[assembly: AssemblyTrademark(\"\")]\n[assembly: AssemblyCulture(\"\")]\n\n// Setting ComVisible to false makes the types in this assembly not visible\n// to COM components.  If you need to access a type in this assembly from\n// COM, set the ComVisible attribute to true on that type.\n[assembly: ComVisible(false)]\n\n//In order to begin building localizable applications, set\n//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file\n//inside a <PropertyGroup>.  For example, if you are using US english\n//in your source files, set the <UICulture> to en-US.  Then uncomment\n//the NeutralResourceLanguage attribute below.  Update the \"en-US\" in\n//the line below to match the UICulture setting in the project file.\n\n//[assembly: NeutralResourcesLanguage(\"en-US\", UltimateResourceFallbackLocation.Satellite)]\n\n\n[assembly: ThemeInfo(\n    ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located\n                                     //(used if a resource is not found in the page,\n                                     // or application resource dictionaries)\n    ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located\n                                              //(used if a resource is not found in the page,\n                                              // app, or any theme specific resource dictionaries)\n)]\n\n\n// Version information for an assembly consists of the following four values:\n//\n//      Major Version\n//      Minor Version\n//      Build Number\n//      Revision\n//\n// You can specify all the values or you can default the Build and Revision Numbers\n// by using the '*' as shown below:\n// [assembly: AssemblyVersion(\"1.0.*\")]\n[assembly: AssemblyVersion(\"1.0.0.0\")]\n[assembly: AssemblyFileVersion(\"1.0.0.0\")]\n"
  },
  {
    "path": "ZenithInstaller/Properties/Resources.Designer.cs",
    "content": "﻿//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace ZenithInstaller.Properties\n{\n\n\n    /// <summary>\n    ///   A strongly-typed resource class, for looking up localized strings, etc.\n    /// </summary>\n    // This class was auto-generated by the StronglyTypedResourceBuilder\n    // class via a tool like ResGen or Visual Studio.\n    // To add or remove a member, edit your .ResX file then rerun ResGen\n    // with the /str option, or rebuild your VS project.\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"System.Resources.Tools.StronglyTypedResourceBuilder\", \"4.0.0.0\")]\n    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]\n    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]\n    internal class Resources\n    {\n\n        private static global::System.Resources.ResourceManager resourceMan;\n\n        private static global::System.Globalization.CultureInfo resourceCulture;\n\n        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute(\"Microsoft.Performance\", \"CA1811:AvoidUncalledPrivateCode\")]\n        internal Resources()\n        {\n        }\n\n        /// <summary>\n        ///   Returns the cached ResourceManager instance used by this class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Resources.ResourceManager ResourceManager\n        {\n            get\n            {\n                if ((resourceMan == null))\n                {\n                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager(\"ZenithInstaller.Properties.Resources\", typeof(Resources).Assembly);\n                    resourceMan = temp;\n                }\n                return resourceMan;\n            }\n        }\n\n        /// <summary>\n        ///   Overrides the current thread's CurrentUICulture property for all\n        ///   resource lookups using this strongly typed resource class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Globalization.CultureInfo Culture\n        {\n            get\n            {\n                return resourceCulture;\n            }\n            set\n            {\n                resourceCulture = value;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "ZenithInstaller/Properties/Resources.resx",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The primary goals of this format is to allow a simple XML format \n    that is mostly human readable. The generation and parsing of the \n    various data types are done through the TypeConverter classes \n    associated with the data types.\n    \n    Example:\n    \n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n                \n    There are any number of \"resheader\" rows that contain simple \n    name/value pairs.\n    \n    Each data row contains a name, and value. The row also contains a \n    type or mimetype. Type corresponds to a .NET class that support \n    text/value conversion through the TypeConverter architecture. \n    Classes that don't support this are serialized and stored with the \n    mimetype set.\n    \n    The mimetype is used for serialized objects, and tells the \n    ResXResourceReader how to depersist the object. This is currently not \n    extensible. For a given mimetype the value must be set accordingly:\n    \n    Note - application/x-microsoft.net.object.binary.base64 is the format \n    that the ResXResourceWriter will generate, however the reader can \n    read any of the formats listed below.\n    \n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with \n            : System.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n    \n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array \n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n</root>"
  },
  {
    "path": "ZenithInstaller/Properties/Settings.Designer.cs",
    "content": "﻿//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace ZenithInstaller.Properties\n{\n\n\n    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator\", \"11.0.0.0\")]\n    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase\n    {\n\n        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));\n\n        public static Settings Default\n        {\n            get\n            {\n                return defaultInstance;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "ZenithInstaller/Properties/Settings.settings",
    "content": "﻿<?xml version='1.0' encoding='utf-8'?>\n<SettingsFile xmlns=\"uri:settings\" CurrentProfile=\"(Default)\">\n  <Profiles>\n    <Profile Name=\"(Default)\" />\n  </Profiles>\n  <Settings />\n</SettingsFile>"
  },
  {
    "path": "ZenithInstaller/ZenithInstaller.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"..\\packages\\Costura.Fody.4.1.0\\build\\Costura.Fody.props\" Condition=\"Exists('..\\packages\\Costura.Fody.4.1.0\\build\\Costura.Fody.props')\" />\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProjectGuid>{F43DEA70-CB97-43FA-9CE9-172D85F010F3}</ProjectGuid>\n    <OutputType>WinExe</OutputType>\n    <RootNamespace>ZenithInstaller</RootNamespace>\n    <AssemblyName>ZenithInstaller</AssemblyName>\n    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>\n    <FileAlignment>512</FileAlignment>\n    <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>\n    <WarningLevel>4</WarningLevel>\n    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>\n    <Deterministic>true</Deterministic>\n    <NuGetPackageImportStamp>\n    </NuGetPackageImportStamp>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>bin\\Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>bin\\Release\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n  </PropertyGroup>\n  <PropertyGroup>\n    <StartupObject>ZenithInstaller.Program</StartupObject>\n  </PropertyGroup>\n  <PropertyGroup>\n    <ApplicationIcon>icon.ico</ApplicationIcon>\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"Costura, Version=4.1.0.0, Culture=neutral, PublicKeyToken=9919ef960d84173d, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Costura.Fody.4.1.0\\lib\\net40\\Costura.dll</HintPath>\n    </Reference>\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Data\" />\n    <Reference Include=\"System.Xml\" />\n    <Reference Include=\"Microsoft.CSharp\" />\n    <Reference Include=\"System.Core\" />\n    <Reference Include=\"System.Xml.Linq\" />\n    <Reference Include=\"System.Data.DataSetExtensions\" />\n    <Reference Include=\"System.Net.Http\" />\n    <Reference Include=\"System.Xaml\">\n      <RequiredTargetFramework>4.0</RequiredTargetFramework>\n    </Reference>\n    <Reference Include=\"WindowsBase\" />\n    <Reference Include=\"PresentationCore\" />\n    <Reference Include=\"PresentationFramework\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ApplicationDefinition Include=\"App.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </ApplicationDefinition>\n    <Page Include=\"MainWindow.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </Page>\n    <Compile Include=\"App.xaml.cs\">\n      <DependentUpon>App.xaml</DependentUpon>\n      <SubType>Code</SubType>\n    </Compile>\n    <Compile Include=\"MainWindow.xaml.cs\">\n      <DependentUpon>MainWindow.xaml</DependentUpon>\n      <SubType>Code</SubType>\n    </Compile>\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"Program.cs\" />\n    <Compile Include=\"Properties\\AssemblyInfo.cs\">\n      <SubType>Code</SubType>\n    </Compile>\n    <Compile Include=\"Properties\\Resources.Designer.cs\">\n      <AutoGen>True</AutoGen>\n      <DesignTime>True</DesignTime>\n      <DependentUpon>Resources.resx</DependentUpon>\n    </Compile>\n    <Compile Include=\"Properties\\Settings.Designer.cs\">\n      <AutoGen>True</AutoGen>\n      <DependentUpon>Settings.settings</DependentUpon>\n      <DesignTimeSharedInput>True</DesignTimeSharedInput>\n    </Compile>\n    <EmbeddedResource Include=\"Properties\\Resources.resx\">\n      <Generator>ResXFileCodeGenerator</Generator>\n      <LastGenOutput>Resources.Designer.cs</LastGenOutput>\n    </EmbeddedResource>\n    <None Include=\"packages.config\" />\n    <None Include=\"Properties\\Settings.settings\">\n      <Generator>SettingsSingleFileGenerator</Generator>\n      <LastGenOutput>Settings.Designer.cs</LastGenOutput>\n    </None>\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"App.config\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\ZenithShared\\ZenithShared.csproj\">\n      <Project>{019e4ce1-547f-4a32-b02c-f190943713eb}</Project>\n      <Name>ZenithShared</Name>\n    </ProjectReference>\n  </ItemGroup>\n  <ItemGroup>\n    <Resource Include=\"icon.ico\" />\n  </ItemGroup>\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n  <Import Project=\"..\\packages\\Fody.6.0.0\\build\\Fody.targets\" Condition=\"Exists('..\\packages\\Fody.6.0.0\\build\\Fody.targets')\" />\n  <Target Name=\"EnsureNuGetPackageBuildImports\" BeforeTargets=\"PrepareForBuild\">\n    <PropertyGroup>\n      <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>\n    </PropertyGroup>\n    <Error Condition=\"!Exists('..\\packages\\Fody.6.0.0\\build\\Fody.targets')\" Text=\"$([System.String]::Format('$(ErrorText)', '..\\packages\\Fody.6.0.0\\build\\Fody.targets'))\" />\n    <Error Condition=\"!Exists('..\\packages\\Costura.Fody.4.1.0\\build\\Costura.Fody.props')\" Text=\"$([System.String]::Format('$(ErrorText)', '..\\packages\\Costura.Fody.4.1.0\\build\\Costura.Fody.props'))\" />\n  </Target>\n</Project>"
  },
  {
    "path": "ZenithInstaller/ZenithInstaller_nf5vy1mn_wpftmp.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"..\\packages\\Costura.Fody.4.1.0\\build\\Costura.Fody.props\" Condition=\"Exists('..\\packages\\Costura.Fody.4.1.0\\build\\Costura.Fody.props')\" />\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProjectGuid>{F43DEA70-CB97-43FA-9CE9-172D85F010F3}</ProjectGuid>\n    <OutputType>WinExe</OutputType>\n    <RootNamespace>ZenithInstaller</RootNamespace>\n    <AssemblyName>ZenithInstaller</AssemblyName>\n    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>\n    <FileAlignment>512</FileAlignment>\n    <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>\n    <WarningLevel>4</WarningLevel>\n    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>\n    <Deterministic>true</Deterministic>\n    <NuGetPackageImportStamp>\n    </NuGetPackageImportStamp>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>bin\\Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>bin\\Release\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n  </PropertyGroup>\n  <PropertyGroup>\n    <StartupObject>ZenithInstaller.Program</StartupObject>\n  </PropertyGroup>\n  <PropertyGroup>\n    <ApplicationIcon>icon.ico</ApplicationIcon>\n  </PropertyGroup>\n  <ItemGroup>\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"App.xaml.cs\">\n      <DependentUpon>App.xaml</DependentUpon>\n      <SubType>Code</SubType>\n    </Compile>\n    <Compile Include=\"MainWindow.xaml.cs\">\n      <DependentUpon>MainWindow.xaml</DependentUpon>\n      <SubType>Code</SubType>\n    </Compile>\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"Program.cs\" />\n    <Compile Include=\"Properties\\AssemblyInfo.cs\">\n      <SubType>Code</SubType>\n    </Compile>\n    <Compile Include=\"Properties\\Resources.Designer.cs\">\n      <AutoGen>True</AutoGen>\n      <DesignTime>True</DesignTime>\n      <DependentUpon>Resources.resx</DependentUpon>\n    </Compile>\n    <Compile Include=\"Properties\\Settings.Designer.cs\">\n      <AutoGen>True</AutoGen>\n      <DependentUpon>Settings.settings</DependentUpon>\n      <DesignTimeSharedInput>True</DesignTimeSharedInput>\n    </Compile>\n    <EmbeddedResource Include=\"Properties\\Resources.resx\">\n      <Generator>ResXFileCodeGenerator</Generator>\n      <LastGenOutput>Resources.Designer.cs</LastGenOutput>\n    </EmbeddedResource>\n    <None Include=\"packages.config\" />\n    <None Include=\"Properties\\Settings.settings\">\n      <Generator>SettingsSingleFileGenerator</Generator>\n      <LastGenOutput>Settings.Designer.cs</LastGenOutput>\n    </None>\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"App.config\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\ZenithShared\\ZenithShared.csproj\">\n      <Project>{019e4ce1-547f-4a32-b02c-f190943713eb}</Project>\n      <Name>ZenithShared</Name>\n    </ProjectReference>\n  </ItemGroup>\n  <ItemGroup>\n  </ItemGroup>\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n  <Import Project=\"..\\packages\\Fody.6.0.0\\build\\Fody.targets\" Condition=\"Exists('..\\packages\\Fody.6.0.0\\build\\Fody.targets')\" />\n  <Target Name=\"EnsureNuGetPackageBuildImports\" BeforeTargets=\"PrepareForBuild\">\n    <PropertyGroup>\n      <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>\n    </PropertyGroup>\n    <Error Condition=\"!Exists('..\\packages\\Fody.6.0.0\\build\\Fody.targets')\" Text=\"$([System.String]::Format('$(ErrorText)', '..\\packages\\Fody.6.0.0\\build\\Fody.targets'))\" />\n    <Error Condition=\"!Exists('..\\packages\\Costura.Fody.4.1.0\\build\\Costura.Fody.props')\" Text=\"$([System.String]::Format('$(ErrorText)', '..\\packages\\Costura.Fody.4.1.0\\build\\Costura.Fody.props'))\" />\n  </Target>\n  <ItemGroup>\n    <ReferencePath Include=\"E:\\Programming\\Personal\\2019\\Zenith-MIDI\\packages\\Costura.Fody.4.1.0\\lib\\net40\\Costura.dll\" />\n    <ReferencePath Include=\"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.6.1\\Microsoft.CSharp.dll\" />\n    <ReferencePath Include=\"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.6.1\\mscorlib.dll\" />\n    <ReferencePath Include=\"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.6.1\\PresentationCore.dll\" />\n    <ReferencePath Include=\"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.6.1\\PresentationFramework.dll\" />\n    <ReferencePath Include=\"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.6.1\\System.Core.dll\" />\n    <ReferencePath Include=\"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.6.1\\System.Data.DataSetExtensions.dll\" />\n    <ReferencePath Include=\"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.6.1\\System.Data.dll\" />\n    <ReferencePath Include=\"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.6.1\\System.dll\" />\n    <ReferencePath Include=\"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.6.1\\System.Net.Http.dll\" />\n    <ReferencePath Include=\"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.6.1\\System.Xaml.dll\" />\n    <ReferencePath Include=\"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.6.1\\System.Xml.dll\" />\n    <ReferencePath Include=\"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.6.1\\System.Xml.Linq.dll\" />\n    <ReferencePath Include=\"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.6.1\\WindowsBase.dll\" />\n    <ReferencePath Include=\"E:\\Programming\\Personal\\2019\\Zenith-MIDI\\ZenithShared\\bin\\Release\\ZenithShared.dll\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"E:\\Programming\\Personal\\2019\\Zenith-MIDI\\ZenithInstaller\\obj\\Release\\MainWindow.g.cs\" />\n    <Compile Include=\"E:\\Programming\\Personal\\2019\\Zenith-MIDI\\ZenithInstaller\\obj\\Release\\App.g.cs\" />\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "ZenithInstaller/packages.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n  <package id=\"Costura.Fody\" version=\"4.1.0\" targetFramework=\"net461\" />\n  <package id=\"Fody\" version=\"6.0.0\" targetFramework=\"net461\" developmentDependency=\"true\" />\n</packages>"
  },
  {
    "path": "ZenithShared/InstallFailedException.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace ZenithShared\n{\n    class InstallFailedException : Exception\n    {\n        public InstallFailedException(string message) : base(message) { }\n    }\n}\n"
  },
  {
    "path": "ZenithShared/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// General Information about an assembly is controlled through the following\n// set of attributes. Change these attribute values to modify the information\n// associated with an assembly.\n[assembly: AssemblyTitle(\"ZenithShared\")]\n[assembly: AssemblyDescription(\"\")]\n[assembly: AssemblyConfiguration(\"\")]\n[assembly: AssemblyCompany(\"\")]\n[assembly: AssemblyProduct(\"ZenithShared\")]\n[assembly: AssemblyCopyright(\"Copyright ©  2020\")]\n[assembly: AssemblyTrademark(\"\")]\n[assembly: AssemblyCulture(\"\")]\n\n// Setting ComVisible to false makes the types in this assembly not visible\n// to COM components.  If you need to access a type in this assembly from\n// COM, set the ComVisible attribute to true on that type.\n[assembly: ComVisible(false)]\n\n// The following GUID is for the ID of the typelib if this project is exposed to COM\n[assembly: Guid(\"019e4ce1-547f-4a32-b02c-f190943713eb\")]\n\n// Version information for an assembly consists of the following four values:\n//\n//      Major Version\n//      Minor Version\n//      Build Number\n//      Revision\n//\n// You can specify all the values or you can default the Build and Revision Numbers\n// by using the '*' as shown below:\n// [assembly: AssemblyVersion(\"1.0.*\")]\n[assembly: AssemblyVersion(\"1.0.0.0\")]\n[assembly: AssemblyFileVersion(\"1.0.0.0\")]\n"
  },
  {
    "path": "ZenithShared/ZenithLanguages.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.IO.Compression;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace ZenithShared\n{\n    public static class ZenithLanguages\n    {\n        public static readonly string ApiURL = \"https://api.github.com/repos/Hans5958/Zenith-MIDI-i18n/releases/latest\";\n        public static readonly string DataAssetName = \"pack.zip\";\n\n        public static string GetLatestVersion() => ZenithUpdates.GetLatestVersion(ApiURL);\n        public static Stream DownloadLatestVersion() => ZenithUpdates.DownloadAssetData(DataAssetName, ApiURL);\n\n        public static void UnpackFromStream(Stream s) => UnpackFromStream(s, \"Languages\");\n\n        public static void UnpackFromStream(Stream s, string dir)\n        {\n            foreach(var f in Directory.GetDirectories(dir))\n            {\n                if (!f.EndsWith(\"\\\\en\")) Directory.Delete(f, true);\n            }\n\n            using (ZipArchive archive = new ZipArchive(s))\n            {\n                foreach (var e in archive.Entries)\n                {\n                    if (e.FullName.StartsWith(\"en/\")) continue;\n                    if (e.FullName.EndsWith(\"\\\\\") || e.FullName.EndsWith(\"/\")) continue;\n                    if (!Directory.Exists(Path.Combine(dir, Path.GetDirectoryName(e.FullName))))\n                        Directory.CreateDirectory(Path.Combine(dir, Path.GetDirectoryName(e.FullName)));\n                    try\n                    {\n                        e.ExtractToFile(Path.Combine(dir, e.FullName), true);\n                    }\n                    catch (IOException ex)\n                    {\n                        throw new InstallFailedException(\"Could not overwrite file \" + Path.Combine(dir, e.FullName));\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "ZenithShared/ZenithShared.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProjectGuid>{019E4CE1-547F-4A32-B02C-F190943713EB}</ProjectGuid>\n    <OutputType>Library</OutputType>\n    <AppDesignerFolder>Properties</AppDesignerFolder>\n    <RootNamespace>ZenithShared</RootNamespace>\n    <AssemblyName>ZenithShared</AssemblyName>\n    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>\n    <FileAlignment>512</FileAlignment>\n    <Deterministic>true</Deterministic>\n    <TargetFrameworkProfile />\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>bin\\Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>bin\\Release\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Newtonsoft.Json.12.0.3\\lib\\net45\\Newtonsoft.Json.dll</HintPath>\n    </Reference>\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Core\" />\n    <Reference Include=\"System.IO.Compression\" />\n    <Reference Include=\"System.IO.Compression.FileSystem\" />\n    <Reference Include=\"System.Xml.Linq\" />\n    <Reference Include=\"System.Data.DataSetExtensions\" />\n    <Reference Include=\"Microsoft.CSharp\" />\n    <Reference Include=\"System.Data\" />\n    <Reference Include=\"System.Net.Http\" />\n    <Reference Include=\"System.Xml\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"InstallFailedException.cs\" />\n    <Compile Include=\"ZenithLanguages.cs\" />\n    <Compile Include=\"ZenithUpdates.cs\" />\n    <Compile Include=\"Properties\\AssemblyInfo.cs\" />\n  </ItemGroup>\n  <ItemGroup>\n    <COMReference Include=\"IWshRuntimeLibrary\">\n      <Guid>{F935DC20-1CF0-11D0-ADB9-00C04FD58A0B}</Guid>\n      <VersionMajor>1</VersionMajor>\n      <VersionMinor>0</VersionMinor>\n      <Lcid>0</Lcid>\n      <WrapperTool>tlbimp</WrapperTool>\n      <Isolated>False</Isolated>\n      <EmbedInteropTypes>True</EmbedInteropTypes>\n    </COMReference>\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"packages.config\" />\n  </ItemGroup>\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n</Project>"
  },
  {
    "path": "ZenithShared/ZenithUpdates.cs",
    "content": "﻿using IWshRuntimeLibrary;\nusing Microsoft.Win32;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.IO;\nusing System.IO.Compression;\nusing System.Linq;\nusing System.Net;\nusing System.Net.Http;\nusing System.Net.Http.Headers;\nusing System.Text;\nusing System.Threading.Tasks;\nusing File = System.IO.File;\n\nnamespace ZenithShared\n{\n    public static class ZenithUpdates\n    {\n        public static readonly string DefaultUpdatePackagePath = \"Updates\\\\pkg.zip\";\n        public static readonly string DataAssetName32 = \"Zenithx86.zip\";\n        public static readonly string DataAssetName64 = \"Zenithx64.zip\";\n        public static readonly string InstallerPath = \"Updates\\\\ins.exe\";\n        public static readonly string SettingsPath = \"Settings\\\\meta.kvs\";\n        public static readonly string ProgramName = \"Zenith\";\n        public static readonly string ExeName = \"Zenith.exe\";\n        public static readonly string[] ProcessNames = new[] { \"Zenith\", \"Zenith-MIDI\" };\n        public static readonly string InstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), \"Zenith\");\n\n        public static readonly string InstallerVer = \"4\";\n\n        public static readonly string ApiURL = \"https://api.github.com/repos/arduano/Zenith-MIDI/releases/latest\";\n\n        public static dynamic GetHTTPJSON(string uri)\n        {\n            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);\n            request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;\n            request.UserAgent = ProgramName;\n\n            using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())\n            {\n                using (Stream stream = response.GetResponseStream())\n                {\n                    using (StreamReader reader = new StreamReader(stream))\n                    {\n                        return JsonConvert.DeserializeObject(reader.ReadToEnd());\n                    }\n                }\n            }\n        }\n\n        public static Stream GetHTTPData(string uri)\n        {\n            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);\n            request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;\n            request.UserAgent = ProgramName;\n\n            using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())\n            {\n                using (Stream stream = response.GetResponseStream())\n                {\n                    MemoryStream data = new MemoryStream();\n                    stream.CopyTo(data);\n                    data.Position = 0;\n                    return data;\n                }\n            }\n        }\n\n        public static Stream GetHTTPDataProgressive(string uri, Action<long, long> progressCallback)\n        {\n            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);\n            request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;\n            request.UserAgent = ProgramName;\n\n            var res = request.BeginGetResponse((o) => { }, \"\");\n\n            using (HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(res))\n            {\n                using (Stream stream = response.GetResponseStream())\n                {\n                    MemoryStream data = new MemoryStream();\n                    var buffer = new byte[2048];\n                    var copied = 0;\n                    while (copied < response.ContentLength)\n                    {\n                        var r = stream.Read(buffer, 0, buffer.Length);\n                        data.Write(buffer, 0, r);\n                        copied += r;\n                        progressCallback(copied, response.ContentLength);\n                    }\n                    data.Position = 0;\n                    return data;\n                }\n            }\n        }\n\n        public static string GetLatestVersion() => GetLatestVersion(ApiURL);\n        public static string GetLatestVersion(string repo)\n        {\n            var data = GetHTTPJSON(repo);\n            return (string)data.tag_name;\n        }\n\n        public static Stream DownloadAssetData(string filename) => DownloadAssetData(filename, ApiURL);\n        public static Stream DownloadAssetData(string filename, string repo)\n        {\n            var data = GetHTTPJSON(repo);\n            var assets = (JArray)data.assets;\n            var asset = (dynamic)assets.Where(a => ((dynamic)a).name == filename).First();\n            var url = (string)asset.browser_download_url;\n            return GetHTTPData(url);\n        }\n\n        public static Stream DownloadAssetDataProgressive(string filename, Action<long, long> progressCallback) => DownloadAssetDataProgressive(filename, ApiURL, progressCallback);\n        public static Stream DownloadAssetDataProgressive(string filename, string repo, Action<long, long> progressCallback)\n        {\n            var data = GetHTTPJSON(repo);\n            var assets = (JArray)data.assets;\n            var asset = (dynamic)assets.Where(a => ((dynamic)a).name == filename).First();\n            var url = (string)asset.browser_download_url;\n            return GetHTTPDataProgressive(url, progressCallback);\n        }\n\n        public static bool IsAnotherProcessRunning()\n        {\n            var kivas = Process.GetProcessesByName(Path.GetFileNameWithoutExtension(System.Reflection.Assembly.GetEntryAssembly().Location)).ToArray();\n            if (kivas.Length > 1) return true;\n            return false;\n        }\n\n        public static void KillAllProcesses()\n        {\n            var kivas = Process.GetProcesses().Where(p => ProcessNames.Contains(p.ProcessName));\n            var current = Process.GetCurrentProcess();\n            foreach (var k in kivas)\n            {\n                if (k.Id == current.Id) continue;\n                if (!k.HasExited) k.Kill();\n                try\n                {\n                    k.WaitForExit(10000);\n                }\n                catch { continue; }\n                if (!k.HasExited) throw new InstallFailedException(\"Could not kill process \\\"\" + k.ProcessName + \"\\\" with pid \" + k.Id);\n            }\n        }\n\n        public static void InstallFromStream(Stream s)\n        {\n            var basePath = InstallPath;\n            using (ZipArchive archive = new ZipArchive(s))\n            {\n                foreach (var e in archive.Entries)\n                {\n                    if (e.FullName.EndsWith(\"\\\\\") || e.FullName.EndsWith(\"/\")) continue;\n                    if (!Directory.Exists(Path.Combine(basePath, Path.GetDirectoryName(e.FullName))))\n                        Directory.CreateDirectory(Path.Combine(basePath, Path.GetDirectoryName(e.FullName)));\n\n                    var fs = File.Open(Path.Combine(basePath, e.FullName), FileMode.OpenOrCreate);\n                    e.Open().CopyTo(fs);\n                    fs.Close();\n                }\n            }\n        }\n\n        public static void WriteVersionSettings(string version, bool autoUpdate = true, bool installed = true)\n        {\n            var path = Path.Combine(InstallPath, SettingsPath);\n            if (!Directory.Exists(Path.GetDirectoryName(path))) Directory.CreateDirectory(Path.GetDirectoryName(path));\n\n            JObject jobj;\n            if (File.Exists(path))\n            {\n                var s = new StreamReader(new GZipStream(File.Open(path, FileMode.Open), CompressionMode.Decompress));\n                var text = s.ReadToEnd();\n                jobj = (JObject)JsonConvert.DeserializeObject(text);\n                s.Close();\n            }\n            else\n            {\n                jobj = new JObject();\n            }\n\n            if (jobj.ContainsKey(\"version\")) jobj.Remove(\"version\");\n            if (jobj.ContainsKey(\"autoUpdate\")) jobj.Remove(\"autoUpdate\");\n            if (jobj.ContainsKey(\"installed\")) jobj.Remove(\"installed\");\n            if (jobj.ContainsKey(\"installerVer\")) jobj.Remove(\"installerVer\");\n            jobj.Add(\"version\", version);\n            jobj.Add(\"autoUpdate\", autoUpdate);\n            jobj.Add(\"installed\", installed);\n            jobj.Add(\"installerVer\", InstallerVer);\n\n            var stream = new StreamWriter(new GZipStream(File.Open(path, FileMode.Create), CompressionMode.Compress));\n            stream.Write(JsonConvert.SerializeObject(jobj));\n            stream.Close();\n        }\n\n        public static void CopySelfInside(string path)\n        {\n            var p = Path.Combine(InstallPath, path);\n            if (!Directory.Exists(Path.GetDirectoryName(p))) Directory.CreateDirectory(Path.GetDirectoryName(p));\n            File.Copy(System.Reflection.Assembly.GetEntryAssembly().Location, p, true);\n        }\n\n        public static void UpdateInstaller()\n        {\n            var data = DownloadAssetData(\"ZenithInstaller.exe\");\n            var s = File.Open(InstallerPath, FileMode.Create);\n            data.Position = 0;\n            data.CopyTo(s);\n            s.Close();\n        }\n\n        public static void CreateStartShortcut()\n        {\n            string shortcutLocation = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.StartMenu), \"Programs\\\\\" + ProgramName + \".lnk\");\n            if (File.Exists(shortcutLocation)) File.Delete(shortcutLocation);\n            WshShell shell = new WshShell();\n            IWshShortcut shortcut = (IWshShortcut)shell.CreateShortcut(shortcutLocation);\n\n            shortcut.Description = \"Kiva\";\n            shortcut.TargetPath = Path.Combine(InstallPath, ExeName);\n            shortcut.WorkingDirectory = Path.GetDirectoryName(shortcut.TargetPath);\n            shortcut.Save();\n        }\n\n        public static void DeleteStartShortcut()\n        {\n            string shortcutLocation = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.StartMenu), \"Programs\\\\\" + ProgramName + \".lnk\");\n            if (File.Exists(shortcutLocation)) File.Delete(shortcutLocation);\n        }\n\n        public static void CreateDesktopShortcut()\n        {\n            string shortcutLocation = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), ProgramName + \".lnk\");\n            if (File.Exists(shortcutLocation)) File.Delete(shortcutLocation);\n            WshShell shell = new WshShell();\n            IWshShortcut shortcut = (IWshShortcut)shell.CreateShortcut(shortcutLocation);\n\n            shortcut.Description = \"Kiva\";\n            shortcut.TargetPath = Path.Combine(InstallPath, ExeName);\n            shortcut.WorkingDirectory = Path.GetDirectoryName(shortcut.TargetPath);\n            shortcut.Save();\n        }\n\n        public static void DeleteDesktopShortcut()\n        {\n            string shortcutLocation = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), ProgramName + \".lnk\");\n            if (File.Exists(shortcutLocation)) File.Delete(shortcutLocation);\n        }\n\n        public static void DeleteProgramFolder()\n        {\n            Directory.Delete(InstallPath, true);\n        }\n\n        public static void CreateUninstallScript()\n        {\n            File.WriteAllText(\n                Path.Combine(InstallPath, \"uninstall.bat\"),\n                @\"\ncd ..\ncopy {ins} %temp%\\{tmp}\n%temp%\\{tmp} uninstall\ndel %temp%\\{tmp}\n\"\n            .Replace(\"{ins}\", Path.Combine(InstallPath, InstallerPath))\n            .Replace(\"{tmp}\", ProgramName + \"Ins.exe\")\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "ZenithShared/packages.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n  <package id=\"Newtonsoft.Json\" version=\"12.0.3\" targetFramework=\"net461\" />\n</packages>"
  },
  {
    "path": "build.bat",
    "content": "del /S /F /Q build\\*\nrmdir /s /q build\nmkdir build\n\ncall \"C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\Common7\\Tools\\VsDevCmd.bat\" \nrmdir /s /q x64\\Release\nMSBuild.exe Zenith.sln /t:Zenith /p:Configuration=Release /p:Platform=x64\nrmdir /s /q x86\\Release\nMSBuild.exe Zenith.sln /t:Zenith /p:Configuration=Release /p:Platform=x86\nMSBuild.exe Zenith.sln /t:ZenithInstaller /p:Configuration=Release\n\nrmdir \"bin\\x64\\Release\\debug\" /s /q\nrmdir \"bin\\x64\\Release\\Plugins\\Assets\" /s /q\nforfiles /p \"bin\\x64\\Release\\Plugins\" /c \"cmd /c del ..\\lib\\@file\" \ndel bin\\x64\\Release\\Plugins\\PianoFallRender.dll\" \nrmdir \"bin\\x86\\Release\\debug\" /s /q\nrmdir \"bin\\x86\\Release\\Plugins\\Assets\" /s /q\nforfiles /p \"bin\\x86\\Release\\Plugins\" /c \"cmd /c del ..\\lib\\@file\" \ndel x86\\Release\\Plugins\\PianoFallRender.dll\" \npowershell -c Compress-Archive -Path \"bin\\x64\\Release\\*\" -CompressionLevel Optimal -Force -DestinationPath build\\Zenithx64.zip\npowershell -c Compress-Archive -Path \"bin\\x86\\Release\\*\" -CompressionLevel Optimal -Force -DestinationPath build\\Zenithx86.zip\ncopy ZenithInstaller\\bin\\Release\\ZenithInstaller.exe build\\ZenithInstaller.exe"
  }
]